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