Wed, 18 Aug 2010 14:10:48 +0100
parameterise data and addr. buses, tidy up
Parameterised the width of the data and address buses, and the number of
COLUMN, ROW and BANK address bits.
Tidied up code to (hopefully!) work when bus widths are changed.
| philpem@17 | 1 | /**************************************************************************** |
| philpem@0 | 2 | * |
| philpem@0 | 3 | * |
| philpem@0 | 4 | ****************************************************************************/ |
| philpem@0 | 5 | |
| philpem@17 | 6 | module wb_sdram #( |
| philpem@17 | 7 | parameter DATA_BITS = 32, // Width of SDRAM data bus |
| philpem@17 | 8 | parameter COLADDR_BITS = 9, // Number of SDRAM Column Address bits |
| philpem@17 | 9 | parameter BANKADDR_BITS = 2, // Number of SDRAM Bank Address bits |
| philpem@17 | 10 | parameter ROWADDR_BITS = 12 // Number of SDRAM Row Address bits |
| philpem@17 | 11 | ) ( |
| philpem@0 | 12 | // Clocks and resets |
| philpem@17 | 13 | input wb_clk_i, // WISHBONE clock |
| philpem@17 | 14 | input wb_rst_i, // WISHBONE reset |
| philpem@0 | 15 | |
| philpem@0 | 16 | // WISHBONE bus |
| philpem@17 | 17 | input [31:0] wb_adr_i, // WISHBONE address |
| philpem@17 | 18 | input [DATA_BITS-1:0] wb_dat_i, // WISHBONE data in |
| philpem@17 | 19 | output reg [DATA_BITS-1:0] wb_dat_o, // WISHBONE data out |
| philpem@17 | 20 | input [(DATA_BITS/4)-1:0] wb_sel_i, // WISHBONE byte select |
| philpem@17 | 21 | input wb_we_i, // WISHBONE write enable (R/#W) |
| philpem@17 | 22 | input wb_cyc_i, // WISHBONE cycle |
| philpem@17 | 23 | input wb_stb_i, // WISHBONE strobe |
| philpem@17 | 24 | output reg wb_ack_o, // WISHBONE cycle acknowledge (data available, DTACK) |
| philpem@17 | 25 | output wb_err_o, // WISHBONE bus error |
| philpem@17 | 26 | output wb_rty_o, // WISHBONE retry-later |
| philpem@0 | 27 | |
| philpem@0 | 28 | // SDRAM |
| philpem@17 | 29 | output reg sdram_cke, // SDRAM clock enable |
| philpem@17 | 30 | output sdram_cs_n, // SDRAM chip select (active low) |
| philpem@17 | 31 | output sdram_ras_n, // SDRAM row address strobe (active low) |
| philpem@17 | 32 | output sdram_cas_n, // SDRAM column address strobe (active low) |
| philpem@17 | 33 | output sdram_we_n, // SDRAM write enable (active low) |
| philpem@17 | 34 | output [ROWADDR_BITS-1:0] sdram_a, // SDRAM address |
| philpem@17 | 35 | output reg [BANKADDR_BITS-1:0] sdram_ba, // SDRAM bank address |
| philpem@17 | 36 | output reg [(DATA_BITS/4)-1:0] sdram_dqm, // SDRAM data mask (OE#; 0=active, 1=disabled) |
| philpem@17 | 37 | inout [DATA_BITS-1:0] sdram_dq // SDRAM data bus |
| philpem@0 | 38 | ); |
| philpem@0 | 39 | |
| philpem@0 | 40 | |
| philpem@0 | 41 | /**** |
| philpem@10 | 42 | * Timer values |
| philpem@10 | 43 | ****/ |
| philpem@13 | 44 | // CAS latency -- either 2 or 3 [2010-08-10: tested with CL=3, worked fine] |
| philpem@12 | 45 | parameter CAS_LATENCY = 3'd2; |
| philpem@15 | 46 | // System clock frequency |
| philpem@15 | 47 | parameter CLOCK_RATE = 25_000_000; |
| philpem@15 | 48 | |
| philpem@15 | 49 | // SDRAM timings in nanoseconds |
| philpem@16 | 50 | // Precharge to refresh/row activate command (same bank) -- Trp |
| philpem@15 | 51 | parameter TIME_Trp = 20; |
| philpem@16 | 52 | // RAS# to CAS# delay -- Trcd |
| philpem@15 | 53 | parameter TIME_Trcd = 20; |
| philpem@16 | 54 | // Row cycle time -- Trfc, also known as Trc |
| philpem@15 | 55 | parameter TIME_Trfc = 70; |
| philpem@16 | 56 | // Time between refresh cycles -- refresh interval divided by number of rows to refresh (in this case, 64e-3/4096*1e9 --> 15.625us or 15,625ns) |
| philpem@15 | 57 | parameter TIME_Refresh = 15_625; |
| philpem@16 | 58 | // 2ms power-up init period |
| philpem@16 | 59 | parameter TIME_InitDelay = 2_000_000; |
| philpem@16 | 60 | // 2us before the end of the init period, raise CKE |
| philpem@16 | 61 | parameter TIME_InitFinal = 2_000; |
| philpem@15 | 62 | |
| philpem@15 | 63 | // Calculate clock period in nanoseconds |
| philpem@15 | 64 | localparam CLOCK_PERIOD = 1_000_000_000 / CLOCK_RATE; |
| philpem@15 | 65 | |
| philpem@10 | 66 | // T_rp ==> 20ns |
| philpem@15 | 67 | localparam TCY_Trp = (TIME_Trp+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@10 | 68 | // T_rcd ==> 20ns |
| philpem@15 | 69 | localparam TCY_Trcd = (TIME_Trcd+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@10 | 70 | // T_rfc (a.k.a. T_rc) ==> 70ns |
| philpem@15 | 71 | localparam TCY_Trfc = (TIME_Trfc+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@10 | 72 | // T_mrd ==> 2 clock cycles |
| philpem@15 | 73 | localparam TCY_Tmrd = 32'd2; |
| philpem@12 | 74 | // Maximum allowed time between two refresh cycles |
| philpem@15 | 75 | localparam TCY_Refresh = (TIME_Refresh+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@15 | 76 | |
| philpem@15 | 77 | localparam TCY_InitDelay = (TIME_InitDelay+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@15 | 78 | localparam TCY_InitFinal = (TIME_InitFinal+CLOCK_PERIOD-1) / CLOCK_PERIOD - 1; |
| philpem@10 | 79 | |
| philpem@10 | 80 | |
| philpem@10 | 81 | /**** |
| philpem@8 | 82 | * WISHBONE status pins |
| philpem@8 | 83 | ****/ |
| philpem@8 | 84 | // Can't raise bus errors |
| philpem@8 | 85 | assign wb_err_o = 1'b0; |
| philpem@8 | 86 | // Can't request retries |
| philpem@8 | 87 | assign wb_rty_o = 1'b0; |
| philpem@8 | 88 | |
| philpem@14 | 89 | |
| philpem@8 | 90 | /**** |
| philpem@0 | 91 | * SDRAM data output buffer |
| philpem@0 | 92 | ****/ |
| philpem@0 | 93 | // OE=1 for output mode, 0 for input |
| philpem@0 | 94 | reg sdram_dq_oe; |
| philpem@0 | 95 | // SDRAM output register |
| philpem@17 | 96 | reg [DATA_BITS-1:0] sdram_dq_r; |
| philpem@17 | 97 | assign sdram_dq = sdram_dq_oe ? sdram_dq_r : {DATA_BITS{1'bZ}}; |
| philpem@0 | 98 | |
| philpem@0 | 99 | |
| philpem@0 | 100 | /**** |
| philpem@0 | 101 | * State timer |
| philpem@0 | 102 | * This is used to ensure that the state machine abides by RAM timing |
| philpem@0 | 103 | * restrictions. |
| philpem@0 | 104 | ****/ |
| philpem@0 | 105 | reg [31:0] timer; |
| philpem@0 | 106 | |
| philpem@0 | 107 | |
| philpem@0 | 108 | /**** |
| philpem@0 | 109 | * MODE logic |
| philpem@0 | 110 | ****/ |
| philpem@0 | 111 | reg [5:0] sdram_mode; |
| philpem@0 | 112 | reg [11:0] sdram_addr; |
| philpem@0 | 113 | assign sdram_cs_n = sdram_mode[3]; |
| philpem@0 | 114 | assign sdram_ras_n = sdram_mode[2]; |
| philpem@0 | 115 | assign sdram_cas_n = sdram_mode[1]; |
| philpem@0 | 116 | assign sdram_we_n = sdram_mode[0]; |
| philpem@0 | 117 | assign sdram_a = {sdram_addr[11], (sdram_mode[5] ? sdram_mode[4] : sdram_addr[10]), sdram_addr[9:0]}; |
| philpem@0 | 118 | |
| philpem@0 | 119 | // SDRAM chip instructions |
| philpem@0 | 120 | // The bit order is as specified in the ISSI datasheet: A10 Override, A10, CS#, RAS#, CAS#, WE#. |
| philpem@0 | 121 | // If A10 Override is set, then A10 will be overridden to the value specified in the M_ constant. |
| philpem@0 | 122 | localparam M_BankActivate = 6'b0X0011; |
| philpem@0 | 123 | localparam M_PrechargeBank = 6'b100010; |
| philpem@0 | 124 | localparam M_PrechargeAll = 6'b110010; |
| philpem@0 | 125 | localparam M_Write = 6'b100100; |
| philpem@0 | 126 | localparam M_WritePrecharge = 6'b110100; |
| philpem@0 | 127 | localparam M_Read = 6'b100101; |
| philpem@0 | 128 | localparam M_ReadPrecharge = 6'b110101; |
| philpem@2 | 129 | localparam M_LoadModeRegister = 6'b0X0000; |
| philpem@0 | 130 | localparam M_Nop = 6'b0X0111; |
| philpem@0 | 131 | localparam M_BurstStop = 6'b0X0110; |
| philpem@0 | 132 | localparam M_Inhibit = 6'b0X1XXX; // maybe X1111? |
| philpem@0 | 133 | localparam M_AutoRefresh = 6'b0X0001; |
| philpem@0 | 134 | |
| philpem@0 | 135 | |
| philpem@0 | 136 | /**** |
| philpem@3 | 137 | * Refresh Timer |
| philpem@3 | 138 | ****/ |
| philpem@3 | 139 | reg [31:0] refresh_timer; |
| philpem@3 | 140 | reg refresh_req, refresh_ack, refresh_timer_en; |
| philpem@3 | 141 | always @(posedge wb_clk_i) begin |
| philpem@3 | 142 | if (wb_rst_i | !refresh_timer_en) begin |
| philpem@3 | 143 | // Reset; clear timer, unset REFRESH REQUEST |
| philpem@3 | 144 | refresh_req <= 1'b0; |
| philpem@15 | 145 | refresh_timer <= TCY_Refresh - 32'd1; |
| philpem@3 | 146 | end else if (refresh_ack) begin |
| philpem@3 | 147 | // Refresh Ack, clear Refresh Request. |
| philpem@3 | 148 | refresh_req <= 1'b0; |
| philpem@3 | 149 | end else if (refresh_timer == 0) begin |
| philpem@3 | 150 | // Refresh timer timed out, make a Refresh Request and reload the timer |
| philpem@3 | 151 | refresh_req <= 1'b1; |
| philpem@15 | 152 | refresh_timer <= TCY_Refresh - 32'd1; |
| philpem@3 | 153 | end else begin |
| philpem@3 | 154 | // Otherwise just decrement the timer |
| philpem@3 | 155 | refresh_timer <= refresh_timer - 32'd1; |
| philpem@3 | 156 | end |
| philpem@3 | 157 | end |
| philpem@3 | 158 | |
| philpem@4 | 159 | |
| philpem@4 | 160 | /**** |
| philpem@4 | 161 | * Address decoder |
| philpem@4 | 162 | ****/ |
| philpem@17 | 163 | wire [COLADDR_BITS-1:0] column_addr; |
| philpem@17 | 164 | wire [ROWADDR_BITS-1:0] row_addr; |
| philpem@17 | 165 | wire [BANKADDR_BITS-1:0] bank_addr; |
| philpem@4 | 166 | |
| philpem@7 | 167 | // Convert a 23-bit linear address into an SDRAM address |
| philpem@17 | 168 | assign column_addr = wb_adr_i[COLADDR_BITS-1:0]; |
| philpem@17 | 169 | assign bank_addr = wb_adr_i[COLADDR_BITS+BANKADDR_BITS-1:COLADDR_BITS]; |
| philpem@17 | 170 | assign row_addr = wb_adr_i[COLADDR_BITS+BANKADDR_BITS+ROWADDR_BITS-1:COLADDR_BITS+BANKADDR_BITS]; |
| philpem@4 | 171 | |
| philpem@4 | 172 | |
| philpem@3 | 173 | /**** |
| philpem@0 | 174 | * Finite State Machine |
| philpem@0 | 175 | ****/ |
| philpem@2 | 176 | localparam ST_INIT1 = 32'd0; |
| philpem@2 | 177 | localparam ST_INIT2 = 32'd1; |
| philpem@2 | 178 | localparam ST_NOP1 = 32'd2; |
| philpem@2 | 179 | localparam ST_PrechargeAll = 32'd3; |
| philpem@2 | 180 | localparam ST_PrechargeAll_Wait = 32'd4; |
| philpem@2 | 181 | localparam ST_AutoRefresh1 = 32'd5; |
| philpem@2 | 182 | localparam ST_AutoRefresh1_Wait = 32'd6; |
| philpem@2 | 183 | localparam ST_AutoRefresh2 = 32'd7; |
| philpem@2 | 184 | localparam ST_AutoRefresh2_Wait = 32'd8; |
| philpem@2 | 185 | localparam ST_LoadModeRegister = 32'd9; |
| philpem@2 | 186 | localparam ST_LoadModeRegister_Wait = 32'd10; |
| philpem@3 | 187 | localparam ST_Spin = 32'd11; // <<== main 'spin' / 'idle' state |
| philpem@3 | 188 | localparam ST_Refresh = 32'd12; |
| philpem@3 | 189 | localparam ST_Refresh_Wait = 32'd13; |
| philpem@7 | 190 | localparam ST_Activate = 32'd30; |
| philpem@7 | 191 | localparam ST_Activate_Wait = 32'd31; |
| philpem@7 | 192 | localparam ST_Write = 32'd32; |
| philpem@7 | 193 | localparam ST_Read = 32'd33; |
| philpem@7 | 194 | localparam ST_Read_Wait = 32'd34; |
| philpem@7 | 195 | localparam ST_Wait_Trp = 32'd35; |
| philpem@7 | 196 | localparam ST_Ack = 32'd36; |
| philpem@7 | 197 | |
| philpem@0 | 198 | |
| philpem@0 | 199 | reg [31:0] state; |
| philpem@0 | 200 | always @(posedge wb_clk_i) begin |
| philpem@0 | 201 | if (wb_rst_i) begin |
| philpem@0 | 202 | // Initialise state machine and timer |
| philpem@0 | 203 | state <= ST_INIT1; |
| philpem@0 | 204 | timer <= 32'd0; |
| philpem@3 | 205 | |
| philpem@3 | 206 | // Clear REFRESH ACK flag and disable refresh timer |
| philpem@3 | 207 | refresh_ack <= 1'b0; |
| philpem@3 | 208 | refresh_timer_en <= 1'b0; |
| philpem@0 | 209 | |
| philpem@0 | 210 | // Initialisation state for SDRAM |
| philpem@0 | 211 | sdram_cke <= 1'b0; |
| philpem@0 | 212 | sdram_mode <= M_Inhibit; |
| philpem@17 | 213 | sdram_addr <= 0; |
| philpem@17 | 214 | sdram_ba <= 0; |
| philpem@17 | 215 | sdram_dqm <= 0; |
| philpem@17 | 216 | sdram_dq_oe <= 0; // data output disabled |
| philpem@17 | 217 | sdram_dq_r <= 0; |
| philpem@0 | 218 | end else begin |
| philpem@0 | 219 | // timer logic |
| philpem@0 | 220 | if (timer > 32'd0) begin |
| philpem@0 | 221 | timer <= timer - 32'd1; |
| philpem@0 | 222 | end |
| philpem@0 | 223 | |
| philpem@0 | 224 | // state machine logic |
| philpem@0 | 225 | case (state) |
| philpem@0 | 226 | ST_INIT1: begin |
| philpem@0 | 227 | // INIT1: Set up for initial power-up wait |
| philpem@0 | 228 | state <= ST_INIT2; |
| philpem@15 | 229 | timer <= TCY_InitDelay; // Needs to be >= 100us |
| philpem@0 | 230 | |
| philpem@0 | 231 | // SDRAM state |
| philpem@0 | 232 | sdram_cke <= 1'b0; // clock disabled |
| philpem@0 | 233 | sdram_mode <= M_Inhibit; |
| philpem@17 | 234 | sdram_addr <= 0; |
| philpem@17 | 235 | sdram_ba <= 0; |
| philpem@17 | 236 | sdram_dqm <= {(DATA_BITS/4){1'b1}}; |
| philpem@17 | 237 | sdram_dq_oe <= 0; // data output disabled |
| philpem@17 | 238 | sdram_dq_r <= 0; |
| philpem@0 | 239 | end |
| philpem@0 | 240 | |
| philpem@0 | 241 | ST_INIT2: begin |
| philpem@0 | 242 | // INIT2: Power-up wait. Keep CKE low until ~50 cycles before |
| philpem@0 | 243 | // the end of the power-up wait, then bring CKE high. |
| philpem@0 | 244 | if (timer == 32'd0) begin |
| philpem@0 | 245 | // Timer hit zero. Send a NOP. |
| philpem@2 | 246 | state <= ST_NOP1; |
| philpem@15 | 247 | end else if (timer < TCY_InitFinal) begin |
| philpem@0 | 248 | // Timer value is more than zero but less than 50; CKE is on, but |
| philpem@0 | 249 | // keep waiting for the timer to actually expire. |
| philpem@0 | 250 | sdram_cke <= 1'b1; |
| philpem@0 | 251 | state <= ST_INIT2; |
| philpem@0 | 252 | end |
| philpem@0 | 253 | sdram_mode <= M_Inhibit; |
| philpem@0 | 254 | end |
| philpem@0 | 255 | |
| philpem@2 | 256 | ST_NOP1: begin |
| philpem@2 | 257 | // Apply one or more NOP commands to the SDRAM |
| philpem@2 | 258 | sdram_mode <= M_Nop; |
| philpem@2 | 259 | state <= ST_PrechargeAll; |
| philpem@2 | 260 | end |
| philpem@2 | 261 | |
| philpem@2 | 262 | ST_PrechargeAll: begin |
| philpem@2 | 263 | // Precharge All, then wait T_rp (20ns) |
| philpem@2 | 264 | sdram_mode <= M_PrechargeAll; |
| philpem@15 | 265 | timer <= TCY_Trp - 32'd1; |
| philpem@2 | 266 | state <= ST_PrechargeAll_Wait; |
| philpem@2 | 267 | end |
| philpem@2 | 268 | |
| philpem@2 | 269 | ST_PrechargeAll_Wait: begin |
| philpem@2 | 270 | // Wait for T_rp after Precharge All |
| philpem@2 | 271 | sdram_mode <= M_Nop; |
| philpem@2 | 272 | if (timer == 32'd0) begin |
| philpem@2 | 273 | // Timer hit zero. Continue |
| philpem@2 | 274 | state <= ST_AutoRefresh1; |
| philpem@2 | 275 | end |
| philpem@2 | 276 | end |
| philpem@2 | 277 | |
| philpem@2 | 278 | ST_AutoRefresh1: begin |
| philpem@2 | 279 | // Auto Refresh 1 of 2, wait T_rfc (70ns) after each |
| philpem@2 | 280 | sdram_mode <= M_AutoRefresh; |
| philpem@15 | 281 | timer <= TCY_Trfc - 32'd1; |
| philpem@2 | 282 | state <= ST_AutoRefresh1_Wait; |
| philpem@2 | 283 | end |
| philpem@2 | 284 | |
| philpem@2 | 285 | ST_AutoRefresh1_Wait: begin |
| philpem@2 | 286 | // Wait for T_rfc |
| philpem@1 | 287 | sdram_mode <= M_Nop; |
| philpem@2 | 288 | if (timer == 32'd0) begin |
| philpem@2 | 289 | // Timer hit zero. Continue |
| philpem@2 | 290 | state <= ST_AutoRefresh2; |
| philpem@2 | 291 | end |
| philpem@2 | 292 | end |
| philpem@2 | 293 | |
| philpem@2 | 294 | ST_AutoRefresh2: begin |
| philpem@2 | 295 | // Auto Refresh 2 of 2, wait T_rfc (70ns) after each |
| philpem@2 | 296 | sdram_mode <= M_AutoRefresh; |
| philpem@15 | 297 | timer <= TCY_Trfc - 32'd1; |
| philpem@2 | 298 | state <= ST_AutoRefresh2_Wait; |
| philpem@2 | 299 | end |
| philpem@2 | 300 | |
| philpem@2 | 301 | ST_AutoRefresh2_Wait: begin |
| philpem@2 | 302 | // Wait for T_rfc |
| philpem@2 | 303 | sdram_mode <= M_Nop; |
| philpem@2 | 304 | if (timer == 32'd0) begin |
| philpem@2 | 305 | // Timer hit zero. Continue |
| philpem@2 | 306 | state <= ST_LoadModeRegister; |
| philpem@2 | 307 | end |
| philpem@2 | 308 | end |
| philpem@2 | 309 | |
| philpem@2 | 310 | ST_LoadModeRegister: begin |
| philpem@2 | 311 | // Load Mode Register |
| philpem@2 | 312 | /** |
| philpem@2 | 313 | * Mode register: |
| philpem@2 | 314 | * - BS0,1 = 00 [RFU] |
| philpem@2 | 315 | * - A11,10 = 00 [RFU] |
| philpem@2 | 316 | * - A9 = 0 [WBL -- write burst length same as read burst length] |
| philpem@2 | 317 | * - A8,7 = 00 [Test Mode off] |
| philpem@10 | 318 | * - A6..4 = 010 [CAS Latency = 2 or 3 clocks, set above] |
| philpem@2 | 319 | * - A3 = 0 [Burst type = sequential] |
| philpem@2 | 320 | * - A2..0 = 000 [Burst length = 1 word] |
| philpem@2 | 321 | */ |
| philpem@17 | 322 | sdram_ba <= 0; |
| philpem@10 | 323 | sdram_addr <= {5'b00_0_00, CAS_LATENCY[2:0], 3'b000}; |
| philpem@2 | 324 | sdram_mode <= M_LoadModeRegister; |
| philpem@2 | 325 | |
| philpem@2 | 326 | // Wait T_mrd (2 clock cycles) |
| philpem@15 | 327 | timer <= TCY_Tmrd - 32'd1; |
| philpem@2 | 328 | state <= ST_LoadModeRegister_Wait; |
| philpem@2 | 329 | end |
| philpem@2 | 330 | |
| philpem@2 | 331 | ST_LoadModeRegister_Wait: begin |
| philpem@2 | 332 | // Wait for LMR to complete |
| philpem@2 | 333 | sdram_mode <= M_Nop; |
| philpem@17 | 334 | sdram_ba <= 0; |
| philpem@17 | 335 | sdram_addr <= 0; |
| philpem@2 | 336 | if (timer == 32'd0) begin |
| philpem@2 | 337 | // Timer hit zero. Continue |
| philpem@2 | 338 | state <= ST_Spin; |
| philpem@2 | 339 | end |
| philpem@2 | 340 | end |
| philpem@2 | 341 | |
| philpem@2 | 342 | ST_Spin: begin |
| philpem@3 | 343 | // Enable refresh timer |
| philpem@3 | 344 | refresh_timer_en <= 1'b1; |
| philpem@3 | 345 | |
| philpem@7 | 346 | // Idle the SDRAM (Inhibit is lower power than NOP on some SDRAMs) |
| philpem@2 | 347 | sdram_mode <= M_Inhibit; |
| philpem@3 | 348 | |
| philpem@9 | 349 | // Check if a refresh is due (these have highest priority) |
| philpem@3 | 350 | if (refresh_req) begin |
| philpem@3 | 351 | // Refresh request received. Ack it and do a refresh. |
| philpem@3 | 352 | refresh_ack <= 1'b1; |
| philpem@3 | 353 | state <= ST_Refresh; |
| philpem@3 | 354 | end else begin |
| philpem@7 | 355 | if (wb_cyc_i & wb_stb_i) begin |
| philpem@7 | 356 | // CYC and STB high. A Wishbone cycle just started. |
| philpem@7 | 357 | state <= ST_Activate; |
| philpem@7 | 358 | end |
| philpem@3 | 359 | end |
| philpem@3 | 360 | end |
| philpem@7 | 361 | |
| philpem@7 | 362 | ///// |
| philpem@7 | 363 | // Refresh logic |
| philpem@7 | 364 | ///// |
| philpem@3 | 365 | |
| philpem@3 | 366 | ST_Refresh: begin |
| philpem@3 | 367 | // Refresh timer timed out; do a refresh run |
| philpem@3 | 368 | // Start by clearing the ACK flag (which was set by the Spin state) |
| philpem@3 | 369 | refresh_ack <= 1'b0; |
| philpem@3 | 370 | // Tell the SDRAM to do a Refresh |
| philpem@3 | 371 | sdram_mode <= M_AutoRefresh; |
| philpem@3 | 372 | // Wait for T_rfc |
| philpem@15 | 373 | timer <= TCY_Trfc; |
| philpem@3 | 374 | state <= ST_Refresh_Wait; |
| philpem@3 | 375 | end |
| philpem@3 | 376 | |
| philpem@3 | 377 | ST_Refresh_Wait: begin |
| philpem@3 | 378 | // Wait for T_rfc |
| philpem@3 | 379 | sdram_mode <= M_Nop; |
| philpem@3 | 380 | if (timer == 32'd0) begin |
| philpem@3 | 381 | // Timer hit zero. Go back to spin state. |
| philpem@3 | 382 | state <= ST_Spin; |
| philpem@3 | 383 | end |
| philpem@0 | 384 | end |
| philpem@7 | 385 | |
| philpem@7 | 386 | ////// |
| philpem@7 | 387 | // R/W logic |
| philpem@7 | 388 | ////// |
| philpem@7 | 389 | ST_Activate: begin |
| philpem@7 | 390 | // Activate the required bank |
| philpem@4 | 391 | sdram_mode <= M_BankActivate; |
| philpem@4 | 392 | sdram_addr <= row_addr; |
| philpem@7 | 393 | sdram_ba <= bank_addr; |
| philpem@15 | 394 | timer <= TCY_Trcd - 32'd1; |
| philpem@7 | 395 | state <= ST_Activate_Wait; |
| philpem@4 | 396 | end |
| philpem@4 | 397 | |
| philpem@7 | 398 | ST_Activate_Wait: begin |
| philpem@7 | 399 | // Wait for T_rcd |
| philpem@4 | 400 | sdram_mode <= M_Nop; |
| philpem@4 | 401 | if (timer == 32'd0) begin |
| philpem@7 | 402 | if (wb_we_i) begin |
| philpem@7 | 403 | // Write cycle. |
| philpem@7 | 404 | state <= ST_Write; |
| philpem@7 | 405 | end else begin |
| philpem@7 | 406 | // Read cycle |
| philpem@7 | 407 | state <= ST_Read; |
| philpem@7 | 408 | end |
| philpem@4 | 409 | end |
| philpem@4 | 410 | end |
| philpem@4 | 411 | |
| philpem@7 | 412 | ST_Write: begin |
| philpem@7 | 413 | // Write cycle handler |
| philpem@7 | 414 | sdram_mode <= M_WritePrecharge; |
| philpem@7 | 415 | sdram_addr <= column_addr; |
| philpem@17 | 416 | sdram_dq_r <= 0; |
| philpem@17 | 417 | sdram_dq_oe <= 1; // FPGA drives the DQ bus |
| philpem@9 | 418 | sdram_dqm <= ~wb_sel_i; |
| philpem@7 | 419 | |
| philpem@7 | 420 | // Wait T_rp (20ns) |
| philpem@15 | 421 | timer <= TCY_Trp - 32'd1; |
| philpem@7 | 422 | state <= ST_Wait_Trp; |
| philpem@7 | 423 | end |
| philpem@7 | 424 | |
| philpem@7 | 425 | ST_Read: begin |
| philpem@7 | 426 | // Read cycle handler |
| philpem@7 | 427 | sdram_mode <= M_ReadPrecharge; |
| philpem@7 | 428 | sdram_addr <= column_addr; |
| philpem@17 | 429 | sdram_dq_oe <= 0; // SDRAM drives the DQ bus |
| philpem@17 | 430 | sdram_dqm <= 0; // Grab all the data (easier than playing with WB_SEL...) |
| philpem@10 | 431 | timer <= CAS_LATENCY - 32'd1; // CAS# Latency |
| philpem@7 | 432 | state <= ST_Read_Wait; |
| philpem@4 | 433 | end |
| philpem@4 | 434 | |
| philpem@7 | 435 | ST_Read_Wait: begin |
| philpem@7 | 436 | // Wait for CAS# latency |
| philpem@7 | 437 | sdram_mode <= M_Nop; |
| philpem@17 | 438 | sdram_dqm <= {(DATA_BITS/4){1'b1}}; // Make SDRAM DQ bus float |
| philpem@4 | 439 | if (timer == 32'd0) begin |
| philpem@7 | 440 | // Latch data |
| philpem@7 | 441 | wb_dat_o <= sdram_dq; |
| philpem@7 | 442 | // Wait T_rp (20ns) |
| philpem@15 | 443 | timer <= TCY_Trp - 32'd1; |
| philpem@7 | 444 | state <= ST_Wait_Trp; |
| philpem@4 | 445 | end |
| philpem@4 | 446 | end |
| philpem@5 | 447 | |
| philpem@7 | 448 | ST_Wait_Trp: begin |
| philpem@7 | 449 | // Wait for T_rp, then ack |
| philpem@7 | 450 | if (timer == 32'd0) begin |
| philpem@7 | 451 | state <= ST_Ack; |
| philpem@7 | 452 | end |
| philpem@4 | 453 | end |
| philpem@4 | 454 | |
| philpem@7 | 455 | ST_Ack: begin |
| philpem@7 | 456 | // Ack the transfer to the WISHBONE host |
| philpem@7 | 457 | sdram_mode <= M_Nop; |
| philpem@17 | 458 | sdram_addr <= 0; |
| philpem@17 | 459 | sdram_dq_r <= 0; |
| philpem@17 | 460 | sdram_dq_oe <= 0; // SDRAM drives the DQ bus |
| philpem@17 | 461 | sdram_dqm <= {(DATA_BITS/4){1'b1}}; // mask off DQM |
| philpem@7 | 462 | if (wb_cyc_i & wb_stb_i) begin |
| philpem@7 | 463 | // CYC and STB high, ack the transfer |
| philpem@7 | 464 | wb_ack_o <= 1'b1; |
| philpem@7 | 465 | state <= ST_Ack; |
| philpem@7 | 466 | end else begin |
| philpem@9 | 467 | // CYC and STB low, go back and wait for another transaction |
| philpem@9 | 468 | wb_ack_o <= 1'b0; |
| philpem@7 | 469 | state <= ST_Spin; |
| philpem@4 | 470 | end |
| philpem@4 | 471 | end |
| philpem@0 | 472 | endcase |
| philpem@0 | 473 | end |
| philpem@0 | 474 | end |
| philpem@0 | 475 | |
| philpem@0 | 476 | endmodule |