DocJava
Newbie level 4
Hello,
I built a small SSRAM controller for my DE2i-150 board and have noted two things that are odd about memory accesses when I connected up the VGA-LCD core. I have an OpenRISC SoC ported to this board. Under normal circumstances the test program I wrote to test out the SSRAM works as expected (combination of 32-bit reads/writes and then a memcpy of an arbitrary length string followed by some verification). When the core is under a more dynamic load the behavior changes.
When I ran a separate VGA test program at the same time as the VGA core being active I noticed that writes to the frame buffer would set pixels both in a line (as expected) and randomly throughout the frame buffer (buggy behavior). The VGA clock is ~25 MHz for 640x480, the Wishbone bus is operating at 50 MHz, and the SSRAM clock is running at 100 MHz. I think this has to do with a read and a write occurring too close to one another, but cannot be sure. I ran a simulation and had some interesting results where a read (all the appropriate signals are set as if a read occurs) would seem to occur twice for every read/write (however the second read gets cut short).
The question I have for the board is whether or not the state machine I'm using is sufficient to properly handle talking to the SSRAM chip. I've heavily commented the code for the most part, so it may be educational for anyone else encountering similar issues with the SSRAM. Overall I'd like to use the SSRAM as a dedicated framebuffer.
Thanks for taking a look.
-Doc
ssram_controller.v
ssram_controller_tb.v
I built a small SSRAM controller for my DE2i-150 board and have noted two things that are odd about memory accesses when I connected up the VGA-LCD core. I have an OpenRISC SoC ported to this board. Under normal circumstances the test program I wrote to test out the SSRAM works as expected (combination of 32-bit reads/writes and then a memcpy of an arbitrary length string followed by some verification). When the core is under a more dynamic load the behavior changes.
When I ran a separate VGA test program at the same time as the VGA core being active I noticed that writes to the frame buffer would set pixels both in a line (as expected) and randomly throughout the frame buffer (buggy behavior). The VGA clock is ~25 MHz for 640x480, the Wishbone bus is operating at 50 MHz, and the SSRAM clock is running at 100 MHz. I think this has to do with a read and a write occurring too close to one another, but cannot be sure. I ran a simulation and had some interesting results where a read (all the appropriate signals are set as if a read occurs) would seem to occur twice for every read/write (however the second read gets cut short).
The question I have for the board is whether or not the state machine I'm using is sufficient to properly handle talking to the SSRAM chip. I've heavily commented the code for the most part, so it may be educational for anyone else encountering similar issues with the SSRAM. Overall I'd like to use the SSRAM as a dedicated framebuffer.
Thanks for taking a look.
-Doc
ssram_controller.v
Code Verilog - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 /**@file ssram_controller.v * This is a controller for the IS61LPS512 SSRAM chip. */ //============================================================================= // Notes //============================================================================= //Reads and writes are delayed by 1 clock cycles. //============================================================================= // DEFINITIONS //============================================================================= module ssram_controller #( parameter ADDR_WIDTH = 21, parameter DATA_WIDTH = 32 )( //Wishbone // //Important: The wb_clk_i and ssram_clk_i signals must be in phase. // input wb_clk_i, // master clock input input wb_rst_i, // synchronous active high reset input ssram_clk_i, // 100 MHz SSRAM clock //Wishbone Slave input [ADDR_WIDTH-1:0] wb_adr_i, // lower address bits input [DATA_WIDTH-1:0] wb_dat_i, // databus input output wire [DATA_WIDTH-1:0] wb_dat_o, // databus output input wb_we_i, // write enable input input wb_stb_i, // stobe/core select signal input wb_cyc_i, // valid bus cycle input output wire wb_ack_o, // bus cycle acknowledge output //SSRAM Signals inout wire [31:0] FS_DQ, output reg [26:0] FS_ADDR, output SSRAM_ADSC_N, output SSRAM_ADSP_N, output SSRAM_ADV_N, output [3:0] SSRAM_BE, output SSRAM_CLK, output SSRAM_GW_N, output SSRAM_OE_N, output SSRAM_WE_N, output SSRAM0_CE_N, output SSRAM1_CE_N ); //========================================================================= // DEFINITIONS //========================================================================= localparam STATE_IDLE = 4'b0000; localparam STATE_READ_1 = 4'b0001; localparam STATE_READ_2 = 4'b0010; localparam STATE_WRITE_1 = 4'b0100; localparam STATE_WRITE_2 = 4'b1000; //========================================================================= // Wishbone Bus //========================================================================= wire chipselect; wire write; wire read; reg [1:0] cycle_ack; //========================================================================= // INTERFACE //========================================================================= reg output_enable; reg adsp_n; reg adsc_n; reg adv_n; reg [3:0] be; reg gw_n; reg oe_n; reg we_n; //This register is set to whatever FS_DQ will be set to immediately. reg [31:0] write_dq; //This register contains whatever was read in STATE_READ_2. reg [31:0] read_dq; reg [3:0] fsm_state; reg [3:0] next_fsm_state; //========================================================================= // INITIAL //========================================================================= initial begin fsm_state <= STATE_IDLE; next_fsm_state <= STATE_IDLE; adsp_n <= 1'b0; adsc_n <= 1'b0; adv_n <= 1'b0; be <= 4'b0000; gw_n <= 1'b0; oe_n <= 1'b0; we_n <= 1'b0; cycle_ack <= 2'b00; read_dq <= 0; write_dq <= 0; end //========================================================================= // Wishbone Clock to SSRAM Clock Adapter //========================================================================= //This clock adapter is kept synchronized by the phase of both clocks. //They are both created from the same PLL with exactly double the clock rate. always @(posedge wb_clk_i or posedge wb_rst_i) begin if (wb_rst_i) begin write_dq <= 0; end else begin //Set the FS_ADDR using the Wishbone clock since it will use //a Wishbone wb_adr_i and wb_dat_i (for writes). For reads the //FS_ADDR will still be used but the output shall be available //directly from FS_DQ. FS_ADDR[26:0] <= {5'b0_0000, wb_adr_i[19:0], 2'b00}; write_dq <= wb_dat_i; end end //========================================================================= // SSRAM State Machine //========================================================================= //This block changes the state of the finite state machine. //This block executes at 100 MHz. always @(posedge ssram_clk_i or posedge wb_rst_i) begin if (wb_rst_i) begin //Begin in the IDLE state. fsm_state <= STATE_IDLE; read_dq <= 0; end else begin if (fsm_state == STATE_READ_2 || fsm_state == STATE_WRITE_2) begin if (fsm_state == STATE_READ_2) read_dq <= FS_DQ; cycle_ack <= 2'b01; end else begin //This only works because the SDRAM clock is exactly //twice as fast as the Wishbone clock. Also, the cycle_ack //only gets a one shifted onto it if its the end of an operation. //This way there is a guarantee of when a one will be seen. if ((cycle_ack[0] == 1'b0) && (cycle_ack[1] == 1'b1)) begin //Do not extend the ack since its already //been extended once. Allow a zero to be //shifted onto the cycle_ack register. cycle_ack <= {cycle_ack[0], 1'b0}; end else if ((cycle_ack[0] == 1'b1) && (cycle_ack[1] == 1'b0)) begin //Extend the ack by one cycle by shifting cycle_ack <= {cycle_ack[0], 1'b0}; end else begin //Under normal circumstances just shift //a zero onto the cycle_ack register. cycle_ack <= {cycle_ack[0], 1'b0}; end end //Update the state of the state machine. fsm_state <= next_fsm_state; end end //This block executes the finite state machine. //The FSM operates at 100 MHz. //Notes: // cycle_ack is set in the SDRAM clock domain // so it must be valid during the subsequent Wishbone clock // therefore it should be a shift register instead of just // a normal register. The number of bits equals the number of clocks // that the status is kept valid. always @* begin next_fsm_state = fsm_state; //Restore some values adsp_n = 1'b1; adsc_n = 1'b1; adv_n = 1'b0; we_n = 1'b0; oe_n = 1'b0; gw_n = 1'b1; be = 4'b0000; case (fsm_state) STATE_IDLE: begin //Reads take precedence over writes if (read) begin next_fsm_state = STATE_READ_1; end else if (write) begin next_fsm_state = STATE_WRITE_1; end end STATE_READ_1: begin //This should be sufficient to start a read-burst //transaction with the memory chip. adsp_n = 1'b0; oe_n = 1'b0; adsc_n = 1'b0; be = 4'b0000; gw_n = 1'b1; //Finish the read before changing states. next_fsm_state = STATE_READ_2; end STATE_READ_2: begin adsp_n = 1'b1; adv_n = 1'b1; adsc_n = 1'b1; oe_n = 1'b0; we_n = 1'b1; be = 4'b0000; gw_n = 1'b1; next_fsm_state = STATE_IDLE; end STATE_WRITE_1: begin //This should be sufficient to start a write-burst //transaction with the memory chip. be = 4'b1111; adsp_n = 1'b1; adsc_n = 1'b0; oe_n = 1'b1; //Output Enable N High we_n = 1'b0; //BWE Low gw_n = 1'b0; //cycle_ack <= {cycle_ack[0], 1'b0}; //Finish the write before changing states. next_fsm_state = STATE_WRITE_2; end STATE_WRITE_2: begin be = 4'b1111; adsp_n = 1'b1; adsc_n = 1'b0; oe_n = 1'b1; //Output Enable N High we_n = 1'b0; //BWE Low gw_n = 1'b0; next_fsm_state = STATE_IDLE; end default: begin be = 4'b1111; adsp_n = 1'b1; adsc_n = 1'b0; oe_n = 1'b1; //Output Enable N High we_n = 1'b0; //BWE Low gw_n = 1'b1; next_fsm_state = STATE_IDLE; end endcase end //Only assign chip select when core select and cycle are asserted at the same time. assign chipselect = wb_stb_i && wb_cyc_i; //Only assign write if the chip is selected and write enable is enabled. assign write = chipselect && wb_we_i; //Only assign read if the chip is selected and read enable is enabled. assign read = chipselect && ~wb_we_i; //Only assign ACK when the chip is selected and the cycle is complete. //This version of the code triggers either on cycle_ack or a shifted cycle_ack. assign wb_ack_o = chipselect && (cycle_ack[1] || cycle_ack[0]); //Assign clock assign SSRAM_CLK = ssram_clk_i; //This assignment takes care of the contents of ADDR //by automatically switching between read and write addresses //depending on the state of the FSM. //assign FS_ADDR[21:2] = (fsm_state == STATE_READ_1 || fsm_state == STATE_READ_2) ? wb_adr_i[19:0] : wb_adr_i[19:0]; //assign FS_ADDR[26:0] = {5'b0_0000, wb_adr_i[19:0], 2'b00}; //This assignment takes care of the contents of DQ //by automatically switching between Z and wr_dat_i depending //on the state of the FSM. //assign FS_DQ = (fsm_state == STATE_READ_1 || fsm_state == STATE_READ_2) ? 32'bZ : wb_dat_i; //DEBUG: Remove this assignment. Its just to test whether or not this fixes the output not enabled issue during build. assign FS_DQ = write ? write_dq : 32'bZ; //This assignment takes care of the output to the Wishbone bus at all times. //assign wb_dat_o = FS_DQ; assign wb_dat_o = read_dq; //Each SSRAM chip provides approximately 2 MiB of SSRAM. //Chip 0 uses the value of the highest address bit so that it //is not selected at the same time as chip 1. assign SSRAM0_CE_N = (fsm_state == STATE_READ_1 || fsm_state == STATE_READ_2) ? wb_adr_i[20] : ((fsm_state == STATE_WRITE_1 || fsm_state == STATE_WRITE_2) ? wb_adr_i[20] : 1'b1); //Chip 1 negates the highest address bit so that it is //not selected at the same time as chip 0. assign SSRAM1_CE_N = (fsm_state == STATE_READ_1 || fsm_state == STATE_READ_2) ? ~wb_adr_i[20] : ((fsm_state == STATE_WRITE_1 || fsm_state == STATE_WRITE_2) ? ~wb_adr_i[20] : 1'b1); //Assign the byte enable (in the datasheet this is //negated) for an entire word to be written. assign SSRAM_BE[3:0] = be[3:0]; //Synchronous Burst Address Advance assign SSRAM_ADV_N = adv_n; //Synchronous Processor Address Status assign SSRAM_ADSP_N = adsp_n; //Synchronous Controller Address Status assign SSRAM_ADSC_N = adsc_n; //Synchronous Global Write Enable assign SSRAM_GW_N = gw_n; //Output Enable assign SSRAM_OE_N = oe_n; //Synchronous Byte Write Enable assign SSRAM_WE_N = we_n; endmodule
ssram_controller_tb.v
Code Verilog - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 `timescale 1ns/1ns module ssram_controller_tb; reg test_clk; reg test_ssram_clk; reg test_reset; wire [31:0] FS_DQ; wire [26:0] FS_ADDR; wire SSRAM_ADSC_N; wire SSRAM_ADSP_N; wire SSRAM_ADV_N; wire [3:0] SSRAM_BE; wire SSRAM_CLK; wire SSRAM_GW_N; wire SSRAM_OE_N; wire SSRAM_WE_N; wire SSRAM0_CE_N; wire SSRAM1_CE_N; wire wb_clk; wire wb_rst; wire ssram_clk; reg [20:0] wb_adr_i; reg [31:0] wb_dat_i; wire [31:0] wb_dat_o; reg wb_we_i; reg wb_stb_i; reg wb_cyc_i; wire wb_ack_o; initial begin: CLOCK_INITIALIZATION test_clk = 1'b0; test_ssram_clk = 1'b0; end always begin: CLOCK_GENERATOR_SSRAM //For testing make the SSRAM clock twice as fast #25 test_ssram_clk = !test_ssram_clk; end always begin: CLOCK_GENERATOR //For testing make the main clock half speed. #50 test_clk = !test_clk; end assign wb_clk = test_clk; assign ssram_clk = test_ssram_clk; assign wb_rst = test_reset; initial begin //FS_ADDR = 21'b0; //FS_DQ = 32'h0; wb_dat_i = 32'h0; wb_adr_i = 21'b0; wb_we_i = 0; wb_cyc_i = 0; wb_stb_i = 0; test_reset = 0; #50 test_reset = 1; #50 test_reset = 0; // Test a read #50 //FS_DQ = 32'h32EFDCAB; wb_adr_i = 21'b1_0000_1100_0000_1000_1000; wb_we_i = 0; wb_cyc_i = 1; wb_stb_i = 1; #250 wb_we_i = 1; wb_cyc_i = 0; wb_stb_i = 0; #500 //Test a write wb_adr_i = 21'b1_0000_1100_0000_1000_1000; wb_dat_i = 32'hABCDEF12; wb_we_i = 1; wb_cyc_i = 1; wb_stb_i = 1; #250 wb_we_i = 0; wb_cyc_i = 0; wb_stb_i = 0; end ssram_controller dut ( //Wishbone .wb_clk_i (test_clk), .wb_rst_i (test_reset), .ssram_clk_i (test_ssram_clk), //Wishbone Slave .wb_adr_i (wb_adr_i), .wb_dat_i (wb_dat_i), .wb_dat_o (wb_dat_o), .wb_we_i (wb_we_i), .wb_stb_i (wb_stb_i), .wb_cyc_i (wb_cyc_i), .wb_ack_o (wb_ack_o), //SSRAM Signals .FS_DQ (FS_DQ), .FS_ADDR (FS_ADDR), .SSRAM_ADSC_N (SSRAM_ADSC_N), .SSRAM_ADSP_N (SSRAM_ADSP_N), .SSRAM_ADV_N (SSRAM_ADV_N), .SSRAM_BE (SSRAM_BE), .SSRAM_CLK (SSRAM_CLK), .SSRAM_GW_N (SSRAM_GW_N), .SSRAM_OE_N (SSRAM_OE_N), .SSRAM_WE_N (SSRAM_WE_N), .SSRAM0_CE_N (SSRAM0_CE_N), .SSRAM1_CE_N (SSRAM1_CE_N) ); endmodule