-
Notifications
You must be signed in to change notification settings - Fork 5
/
SdramCtrl.v
698 lines (622 loc) · 37.2 KB
/
SdramCtrl.v
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
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
////////////////////////////////////////////////////////////////////////////////////////////////////
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
//
// ©2013 - Roman Ovseitsev <romovs@gmail.com>
// Based on code ©2011 - X Engineering Software Systems Corp. (www.xess.com)
////////////////////////////////////////////////////////////////////////////////////////////////////
//##################################################################################################
//
// Single Port SDRAM controller for XuLA-200.
//
//##################################################################################################
`timescale 1ns / 1ps
module SdramCtrl (clk_i, lock_i, rst_i, rd_i, wr_i, earlyOpBegun_o, opBegun_o, rdPending_o, done_o,
rdDone_o, addr_i, data_i, data_o, status_o, sdCke_o, sdCe_bo, sdRas_bo, sdCas_bo,
sdWe_bo, sdBs_o, sdAddr_o, sdData_io, sdDqmh_o, sdDqml_o);
`include "Math.v"
parameter real FREQ = 12.0; // Operating frequency in MHz.
parameter PIPE_EN = 0; // If true, enable pipelined read operations.
//`define MULTIPLE_ACTIVE_ROWS_D // If defined allow an active row in each bank.
localparam IN_PHASE = 1; // SDRAM and controller work on same or opposite clock edge.
localparam MAX_NOPS = 10000; // Number of NOPs before entering self-refresh.
localparam ENABLE_REFRESH = 1; // If true, row refreshes are automatically inserted.
`ifdef MULTIPLE_ACTIVE_ROWS_D
localparam MULTIPLE_ACTIVE_ROWS = 1; // If true, allow an active row in each bank.
`else
localparam MULTIPLE_ACTIVE_ROWS = 0;
`endif
localparam DATA_WIDTH = 16; // Host & SDRAM data width.
// Parameters for Winbond W9812G6JH-6 (all times are in nanoseconds).
localparam NROWS = 4096; // Number of rows in SDRAM array.
localparam NCOLS = 512; // Number of columns in SDRAM array.
localparam HADDR_WIDTH = 23; // Host-side address width.
localparam SADDR_WIDTH = 12; // SDRAM-side address width.
localparam BANK_ADDR_WIDTH = 2; // Width of the bank address. Requires additional changes
// of logic related to activeRow_r if modified.
localparam real T_INIT = 200000.0; // min initialization interval (ns).
localparam real T_RAS = 42.0; // min interval between active to precharge commands (ns).
localparam real T_RCD = 15.0; // min interval between active and R/W commands (ns).
localparam real T_REF = 64000000.0; // maximum refresh interval (ns).
localparam real T_RFC = 60.0; // duration of refresh operation (ns).
localparam real T_RP = 15.0; // min precharge command duration (ns).
localparam real T_XSR = 72.0; // exit self-refresh time (ns).
// Host side.
input wire clk_i; // Master clock.
input wire lock_i; // True if clock is stable.
input wire rst_i; // Reset.
input wire rd_i; // Initiate read operation.
input wire wr_i; // Initiate write operation.
output reg earlyOpBegun_o; // Read/write/self-refresh op has begun (async).
output wire opBegun_o; // Read/write/self-refresh op has begun (clocked).
output reg rdPending_o; // True if read operation(s) are still in the pipeline.
output reg done_o; // Read or write operation is done_o.
output reg rdDone_o; // Read operation is done_o and data is available.
input wire [HADDR_WIDTH-1:0] addr_i; // Address from host to SDRAM.
input wire [DATA_WIDTH-1:0] data_i; // Data from host to SDRAM.
output wire [DATA_WIDTH-1:0] data_o; // Data from SDRAM to host.
output reg [3:0] status_o; // Diagnostic status of the FSM .
// SDRAM side.
output sdCke_o; // Clock-enable to SDRAM.
output sdCe_bo; // Chip-select to SDRAM.
output sdRas_bo; // SDRAM row address strobe.
output sdCas_bo; // SDRAM column address strobe.
output sdWe_bo; // SDRAM write enable.
output [BANK_ADDR_WIDTH-1:0] sdBs_o; // SDRAM bank address.
output [SADDR_WIDTH-1:0] sdAddr_o; // SDRAM row/column address.
inout [DATA_WIDTH-1:0] sdData_io; // Data to/from SDRAM.
output sdDqmh_o; // Enable upper-byte of SDRAM databus if true.
output sdDqml_o; // Enable lower-byte of SDRAM databus if true.
localparam [0:0] OUTPUT_C = 1; // direction of dataflow w.r.t. this controller.
localparam [0:0] INPUT_C = 0;
localparam [0:0] NOP_C = 0; // no operation.
localparam [0:0] READ_C = 1; // read operation.
localparam [0:0] WRITE_C = 1; // write operation.
// SDRAM timing parameters converted into clock cycles (based on FREQ).
localparam INIT_CYCLES = ceil(T_INIT*FREQ/1000.0); // SDRAM power-on initialization interval.
localparam RAS_CYCLES = ceil(T_RAS*FREQ/1000.0); // active-to-precharge interval.
localparam RCD_CYCLES = pfx(ceil(T_RCD*FREQ/1000.0)); // active-to-R/W interval.
localparam REF_CYCLES = ceil(T_REF*FREQ/1000.0/NROWS);// interval between row refreshes.
localparam RFC_CYCLES = ceil(T_RFC*FREQ/1000.0); // refresh operation interval.
localparam RP_CYCLES = ceil(T_RP*FREQ/1000.0); // precharge operation interval.
localparam WR_CYCLES = 2; // write recovery time.
localparam XSR_CYCLES = ceil(T_XSR*FREQ/1000.0); // exit self-refresh time.
localparam MODE_CYCLES = 2; // mode register setup time.
localparam CAS_CYCLES = 3; // CAS latency.
localparam RFSH_OPS_C = 8; // number of refresh operations needed to init SDRAM.
// timer registers that count down times for various SDRAM operations.
reg [clog2(INIT_CYCLES):0] timer_r = 0; // current SDRAM op time.
reg [clog2(INIT_CYCLES):0] timer_x = 0;
reg [clog2(RAS_CYCLES):0] rasTimer_r = 0; // active-to-precharge time.
reg [clog2(RAS_CYCLES):0] rasTimer_x = 0;
reg [clog2(WR_CYCLES):0] wrTimer_r = 0; // write-to-precharge time.
reg [clog2(WR_CYCLES):0] wrTimer_x = 0;
reg [clog2(REF_CYCLES):0] refTimer_r = REF_CYCLES;// time between row refreshes.
reg [clog2(REF_CYCLES):0] refTimer_x = REF_CYCLES;
reg [clog2(RFSH_OPS_C):0] rfshCntr_r = 0; // counts refreshes that are needed.
reg [clog2(RFSH_OPS_C):0] rfshCntr_x = 0;
reg [clog2(MAX_NOPS):0] nopCntr_r = 0; // counts consecutive NOP_C operations.
reg [clog2(MAX_NOPS):0] nopCntr_x = 0;
reg doSelfRfsh_s; // active when the NOP counter hits zero and self-refresh can start.
// states of the SDRAM controller state machine.
localparam [2:0] INITWAIT = 3'b000; // initialization - waiting for power-on initialization to complete.
localparam [2:0] INITPCHG = 3'b001; // initialization - initial precharge of SDRAM banks.
localparam [2:0] INITSETMODE = 3'b010; // initialization - set SDRAM mode.
localparam [2:0] INITRFSH = 3'b011; // initialization - do initial refreshes.
localparam [2:0] RW = 3'b100; // read/write/refresh the SDRAM.
localparam [2:0] ACTIVATE = 3'b101; // open a row of the SDRAM for reading/writing.
localparam [2:0] REFRESHROW = 3'b110; // refresh a row of the SDRAM.
localparam [2:0] SELFREFRESH = 3'b111; // keep SDRAM in self-refresh mode with CKE low.
reg [2:0] state_r = INITWAIT; // state register and next state.
reg [2:0] state_x = INITWAIT; // state register and next state.
// commands that are sent to the SDRAM to make it perform certain operations.
// commands use these SDRAM input pins (ce_bo,ras_bo,cas_bo,we_bo,dqmh_o,dqml_o).
localparam [5:0] NOP_CMD_C = 'b011100;
localparam [5:0] ACTIVE_CMD_C = 'b001100;
localparam [5:0] READ_CMD_C = 'b010100;
localparam [5:0] WRITE_CMD_C = 'b010000;
localparam [5:0] PCHG_CMD_C = 'b001000;
localparam [5:0] MODE_CMD_C = 'b000000;
localparam [5:0] RFSH_CMD_C = 'b000100;
// SDRAM mode register.
// the SDRAM is placed in a non-burst mode (burst length = 1) with a 3-cycle CAS.
localparam [11:0] MODE_C = 'b00_0_00_011_0_000;
// the host address is decomposed into these sets of SDRAM address components.
localparam ROW_LEN_C = clog2(NROWS); // number of row address bits.
localparam COL_LEN_C = clog2(NCOLS); // number of column address bits.
reg [BANK_ADDR_WIDTH-1:0] bank_s; // bank address bits.
reg [ROW_LEN_C-1:0] row_s; // row address within bank.
reg [COL_LEN_C-1:0] col_s; // column address within row.
// registers that store the currently active row in each bank of the SDRAM.
localparam NUM_ACTIVE_ROWS = (MULTIPLE_ACTIVE_ROWS == 0 ? 1 : 2**BANK_ADDR_WIDTH);
localparam NUM_ACTIVE_ROWS_WIDTH = (MULTIPLE_ACTIVE_ROWS == 0 ? 1 : BANK_ADDR_WIDTH);
reg [ROW_LEN_C-1:0] activeRow_r [NUM_ACTIVE_ROWS_WIDTH-1:0];
reg [ROW_LEN_C-1:0] activeRow_x [NUM_ACTIVE_ROWS_WIDTH-1:0];
reg [NUM_ACTIVE_ROWS-1:0] activeFlag_r = 0; // indicates that some row in a bank is active.
reg [NUM_ACTIVE_ROWS-1:0] activeFlag_x = 0;
reg [NUM_ACTIVE_ROWS_WIDTH-1:0] bankIndex_s; // bank address bits.
reg [BANK_ADDR_WIDTH-1:0] activeBank_r; // indicates the bank with the active row.
reg [BANK_ADDR_WIDTH-1:0] activeBank_x;
reg doActivate_s; // indicates when a new row in a bank needs to be activated.
// there is a command bit embedded within the SDRAM column address.
localparam CMDBIT_POS_C = 10; // position of command bit.
localparam [0:0] AUTO_PCHG_ON_C = 1; // CMDBIT value to auto-precharge the bank.
localparam [0:0] AUTO_PCHG_OFF_C = 0; // CMDBIT value to disable auto-precharge.
localparam ONE_BANK_C = 0; // CMDBIT value to select one bank.
localparam ALL_BANKS_C = 1; // CMDBIT value to select all banks.
// status signals that indicate when certain operations are in progress.
reg wrInProgress_s; // write operation in progress.
reg rdInProgress_s; // read operation in progress.
reg activateInProgress_s; // row activation is in progress.
// these registers track the progress of read and write operations.
reg [CAS_CYCLES+1:0] rdPipeline_r = 0;
reg [CAS_CYCLES+1:0] rdPipeline_x = 0; // pipeline of read ops in progress.
reg wrPipeline_r = 0;
reg wrPipeline_x = 0; // pipeline of write ops (only need 1 cycle).
// registered outputs to host.
reg opBegun_r = 0;
reg opBegun_x = 0; // true when SDRAM read or write operation is started.
reg [DATA_WIDTH-1:0] sdramData_r = 0;
reg [DATA_WIDTH-1:0] sdramData_x = 0; // holds data read from SDRAM and sent to the host.
reg [DATA_WIDTH-1:0] sdramDataOppPhase_r;
reg [DATA_WIDTH-1:0] sdramDataOppPhase_x; // holds data read from SDRAM on opposite clock edge.
// registered outputs to SDRAM.
reg cke_r = 0;
reg cke_x = 0; // Clock-enable bit.
reg [5:0] cmd_r = NOP_CMD_C;
reg [5:0] cmd_x = NOP_CMD_C; // SDRAM command bits.
reg [BANK_ADDR_WIDTH-1:0] ba_r;
reg [BANK_ADDR_WIDTH-1:0] ba_x; // SDRAM bank address bits.
reg [SADDR_WIDTH-1:0] sAddr_r = 0;
reg [SADDR_WIDTH-1:0] sAddr_x = 0; // SDRAM row/column address.
reg [DATA_WIDTH-1:0] sData_r = 0;
reg [DATA_WIDTH-1:0] sData_x = 0; // SDRAM out databus.
reg sDataDir_r = INPUT_C;
reg sDataDir_x = INPUT_C; // SDRAM databus direction control bit.
// attach registered SDRAM control signals to SDRAM input pins
assign {sdCe_bo, sdRas_bo, sdCas_bo, sdWe_bo, sdDqmh_o, sdDqml_o} = cmd_r; // SDRAM operation control bits
assign sdCke_o = cke_r; // SDRAM clock enable
assign sdBs_o = ba_r; // SDRAM bank address
assign sdAddr_o = sAddr_r; // SDRAM address
assign sdData_io = (sDataDir_r == OUTPUT_C) ? sData_r : 16'bz; // SDRAM output data bus
// attach some port signals
assign data_o = sdramData_r; // data back to host
assign opBegun_o = opBegun_r; // true if requested operation has begun
//*********************************************************************
// compute the next state and outputs
//*********************************************************************
always @(rd_i, wr_i, addr_i, data_i, sdramData_r, sdData_io, state_r, opBegun_x, activeFlag_r,
activeBank_r, rdPipeline_r, wrPipeline_r, sdramDataOppPhase_r, nopCntr_r,
lock_i, rfshCntr_r, timer_r, rasTimer_r, wrTimer_r, refTimer_r, cmd_r, col_s, ba_r, cke_r,
rdInProgress_s, activateInProgress_s, wrInProgress_s, doActivate_s, doSelfRfsh_s,
`ifdef MULTIPLE_ACTIVE_ROWS_D
activeRow_r[1], activeRow_r[0]
`else
activeRow_r[0]
`endif
) begin
//*********************************************************************
// setup default values for signals
//*********************************************************************
opBegun_x = 0; // no operations have begun
earlyOpBegun_o = opBegun_x;
cke_x = 1; // enable SDRAM clock
cmd_x = NOP_CMD_C; // set SDRAM command to no-operation
sDataDir_x = INPUT_C; // accept data from the SDRAM
sData_x = data_i; // output data from host to SDRAM
state_x = state_r; // reload these registers and flags
activeFlag_x = activeFlag_r; // with their existing values
`ifdef MULTIPLE_ACTIVE_ROWS_D
activeRow_x[0] = activeRow_r[0];
activeRow_x[1] = activeRow_r[1];
`else
activeRow_x[0] = activeRow_r[0];
`endif
activeBank_x = activeBank_r;
rfshCntr_x = rfshCntr_r;
//*********************************************************************
// setup default value for the SDRAM address
//*********************************************************************
// extract bank field from host address
ba_x = addr_i[BANK_ADDR_WIDTH + ROW_LEN_C + COL_LEN_C - 1 : ROW_LEN_C + COL_LEN_C];
if (MULTIPLE_ACTIVE_ROWS == 1) begin
bank_s = 0;
bankIndex_s = ba_x;
end else begin
bank_s = ba_x;
bankIndex_s = 0;
end
// extract row, column fields from host address
row_s = addr_i[ROW_LEN_C + COL_LEN_C - 1 : COL_LEN_C];
col_s[COL_LEN_C-1 : 0] = addr_i[COL_LEN_C-1 : 0];
// by default, set SDRAM address to the column address with interspersed
// command bit set to disable auto-precharge
sAddr_x = {1'b0, AUTO_PCHG_OFF_C, col_s[COL_LEN_C-1 : 0]};
//*********************************************************************
// manage the read and write operation pipelines
//*********************************************************************
// determine if read operations are in progress by the presence of
// READ flags in the read pipeline
if (rdPipeline_r[CAS_CYCLES+1 : 1] != 0) begin
rdInProgress_s = 1;
end else begin
rdInProgress_s = 0;
end
rdPending_o = rdInProgress_s; // tell the host if read operations are in progress
// enter NOPs into the read and write pipeline shift registers by default
rdPipeline_x = {NOP_C, rdPipeline_r[CAS_CYCLES+1 : 1]};
wrPipeline_x = NOP_C;
// transfer data from SDRAM to the host data register if a read flag has exited the pipeline
// (the transfer occurs 1 cycle before we tell the host the read operation is done)
if (rdPipeline_r[1] == READ_C) begin
sdramDataOppPhase_x = sdData_io[DATA_WIDTH-1:0]; // gets value on the SDRAM databus on the opposite phase
if (IN_PHASE == 1) begin
// get the SDRAM data for the host directly from the SDRAM if the controller and SDRAM are in-phase
sdramData_x = sdData_io[DATA_WIDTH-1:0];
end else begin
// otherwise get the SDRAM data that was gathered on the previous opposite clock edge
sdramData_x = sdramDataOppPhase_r[DATA_WIDTH-1:0];
end
end else begin
// retain contents of host data registers if no data from the SDRAM has arrived yet
sdramDataOppPhase_x = sdramDataOppPhase_r;
sdramData_x = sdramData_r;
end
done_o = rdPipeline_r[0] | wrPipeline_r; // a read or write operation is done
rdDone_o = rdPipeline_r[0]; // SDRAM data available when a READ flag exits the pipeline
//*********************************************************************
// manage row activation
//*********************************************************************
// request a row activation operation if the row of the current address
// does not match the currently active row in the bank, or if no row
// in the bank is currently active
if ((bank_s != activeBank_r) || (row_s != activeRow_r[bankIndex_s]) || (activeFlag_r[bankIndex_s] == 0)) begin
doActivate_s = 1;
end else begin
doActivate_s = 0;
end
//*********************************************************************
// manage self-refresh
//*********************************************************************
// enter self-refresh if neither a read or write is requested for MAX_NOP consecutive cycles.
if (rd_i == 1 || wr_i == 1) begin
// any read or write resets NOP counter and exits self-refresh state
nopCntr_x = 0;
doSelfRfsh_s = 0;
end else if (nopCntr_r != MAX_NOPS) begin
// increment NOP counter whenever there is no read or write operation
nopCntr_x = nopCntr_r + 1;
doSelfRfsh_s = 0;
end else begin
// start self-refresh when counter hits maximum NOP count and leave counter unchanged
nopCntr_x = nopCntr_r;
doSelfRfsh_s = 1;
end
//*********************************************************************
// update the timers
//*********************************************************************
// row activation timer
if (rasTimer_r != 0) begin
// decrement a non-zero timer and set the flag
// to indicate the row activation is still inprogress
rasTimer_x = rasTimer_r - 1;
activateInProgress_s = 1;
end else begin
// on timeout, keep the timer at zero and reset the flag
// to indicate the row activation operation is done
rasTimer_x = rasTimer_r;
activateInProgress_s = 0;
end
// write operation timer
if (wrTimer_r != 0) begin
// decrement a non-zero timer and set the flag
// to indicate the write operation is still inprogress
wrTimer_x = wrTimer_r - 1;
wrInProgress_s = 1;
end else begin
// on timeout, keep the timer at zero and reset the flag that
// indicates a write operation is in progress
wrTimer_x = wrTimer_r;
wrInProgress_s = 0;
end
// refresh timer
if (refTimer_r != 0) begin
refTimer_x = refTimer_r - 1;
end else begin
// on timeout, reload the timer with the interval between row refreshes
// and increment the counter for the number of row refreshes that are needed
refTimer_x = REF_CYCLES;
if (ENABLE_REFRESH == 1) begin
rfshCntr_x = rfshCntr_r + 1;
end else begin
rfshCntr_x = 0; // refresh never occurs if this counter never gets above zero
end
end
// main timer for sequencing SDRAM operations
if (timer_r != 0) begin
// decrement the timer and do nothing else since the previous operation has not completed yet.
timer_x = timer_r - 1;
status_o = 'b0000;
end else begin
// the previous operation has completed once the timer hits zero
timer_x = timer_r; // by default, leave the timer at zero
//*********************************************************************
// compute the next state and outputs
//*********************************************************************
(* parallel_case *) case (state_r)
//*********************************************************************
// let clock stabilize and then wait for the SDRAM to initialize
//*********************************************************************
INITWAIT: begin
if (lock_i == 1) begin
// wait for SDRAM power-on initialization once the clock is stable
timer_x = INIT_CYCLES; // set timer for initialization duration
state_x = INITPCHG;
end else begin
// disable SDRAM clock and return to this state if the clock is not stable
// this insures the clock is stable before enabling the SDRAM
// it also insures a clean startup if the SDRAM is currently in self-refresh mode
cke_x = 0;
end
status_o = 'b0001;
end
//*********************************************************************
// precharge all SDRAM banks after power-on initialization
//*********************************************************************
INITPCHG: begin
cmd_x = PCHG_CMD_C;
sAddr_x[CMDBIT_POS_C] = ALL_BANKS_C; // precharge all banks
timer_x = RP_CYCLES; // set timer for precharge operation duration
rfshCntr_x = RFSH_OPS_C; // set counter for refresh ops needed after precharge
state_x = INITRFSH;
status_o = 'b0010;
end
//*********************************************************************
// refresh the SDRAM a number of times after initial precharge
//*********************************************************************
INITRFSH: begin
cmd_x = RFSH_CMD_C;
timer_x = RFC_CYCLES; // set timer to refresh operation duration
rfshCntr_x = rfshCntr_r - 1; // decrement refresh operation counter
if (rfshCntr_r == 1) begin
state_x = INITSETMODE; // set the SDRAM mode once all refresh ops are done
end
status_o = 'b0011;
end
//*********************************************************************
// set the mode register of the SDRAM
//*********************************************************************
INITSETMODE: begin
cmd_x = MODE_CMD_C;
sAddr_x[11:0] = MODE_C; // output mode register bits on the SDRAM address bits
timer_x = MODE_CYCLES; // set timer for mode setting operation duration
state_x = RW;
status_o = 'b0100;
end
//*********************************************************************
// process read/write/refresh operations after initialization is done
//*********************************************************************
RW: begin
//*********************************************************************
// highest priority operation: row refresh
// do a refresh operation if the refresh counter is non-zero
//*********************************************************************
if (rfshCntr_r != 0) begin
// wait for any row activations, writes or reads to finish before doing a precharge
if (activateInProgress_s == 0 && wrInProgress_s == 0 && rdInProgress_s == 0) begin
cmd_x = PCHG_CMD_C; // initiate precharge of the SDRAM
sAddr_x[CMDBIT_POS_C] = ALL_BANKS_C; // precharge all banks
timer_x = RP_CYCLES; // set timer for this operation
activeFlag_x = 0; // all rows are inactive after a precharge operation
state_x = REFRESHROW; // refresh the SDRAM after the precharge
end
status_o = 'b0101;
//*********************************************************************
// do a host-initiated read operation
//*********************************************************************
end else if (rd_i == 1) begin
// Wait one clock cycle if the bank address has just changed and each bank has its own active row.
// This gives extra time for the row activation circuitry.
if (ba_x == ba_r || MULTIPLE_ACTIVE_ROWS == 0) begin
// activate a new row if the current read is outside the active row or bank
if (doActivate_s == 1) begin
// activate new row only if all previous activations, writes, reads are done
if (activateInProgress_s == 0 && wrInProgress_s == 0 && rdInProgress_s == 0) begin
cmd_x = PCHG_CMD_C; // initiate precharge of the SDRAM
sAddr_x[CMDBIT_POS_C] = ONE_BANK_C; // precharge this bank
timer_x = RP_CYCLES; // set timer for this operation
activeFlag_x[bankIndex_s] = 0; // rows in this bank are inactive after a precharge operation
state_x = ACTIVATE; // activate the new row after the precharge is done
end
// read from the currently active row if no previous read operation
// is in progress or if pipeline reads are enabled
// we can always initiate a read even if a write is already in progress
end else if (rdInProgress_s == 0 || PIPE_EN == 1) begin
cmd_x = READ_CMD_C; // initiate a read of the SDRAM
// insert a flag into the pipeline shift register that will exit the end
// of the shift register when the data from the SDRAM is available
rdPipeline_x = {READ_C, rdPipeline_r[CAS_CYCLES+1 : 1]};
opBegun_x = 1; // tell the host the requested operation has begun
end
end
status_o = 'b0110;
//*********************************************************************
// do a host-initiated write operation
//*********************************************************************
end else if (wr_i == 1) begin
// Wait one clock cycle if the bank address has just changed and each bank has its own active row.
// This gives extra time for the row activation circuitry.
if ((ba_x == ba_r) || (MULTIPLE_ACTIVE_ROWS == 0)) begin
// activate a new row if the current write is outside the active row or bank
if (doActivate_s == 1) begin
// activate new row only if all previous activations, writes, reads are done
if ((activateInProgress_s == 0) && (wrInProgress_s == 0) && (rdInProgress_s == 0)) begin
cmd_x = PCHG_CMD_C; // initiate precharge of the SDRAM
sAddr_x[CMDBIT_POS_C] = ONE_BANK_C; // precharge this bank
timer_x = RP_CYCLES; // set timer for this operation
activeFlag_x[bankIndex_s] = 0; // rows in this bank are inactive after a precharge operation
state_x = ACTIVATE; // activate the new row after the precharge is done
end
// write to the currently active row if no previous read operations are in progress
end else if (rdInProgress_s == 0) begin
cmd_x = WRITE_CMD_C; // initiate the write operation
sDataDir_x = OUTPUT_C; // turn on drivers to send data to SDRAM
// set timer so precharge doesn't occur too soon after write operation
wrTimer_x = WR_CYCLES;
// insert a flag into the 1-bit pipeline shift register that will exit on the
// next cycle. The write into SDRAM is not actually done by that time, but
// this doesn't matter to the host
wrPipeline_x = WRITE_C;
opBegun_x = 1; // tell the host the requested operation has begun
end
end
status_o = 'b0111;
//*********************************************************************
// do a host-initiated self-refresh operation
//*********************************************************************
end else if (doSelfRfsh_s == 1) begin
// wait until all previous activations, writes, reads are done
if ((activateInProgress_s == 0) && (wrInProgress_s == 0) && (rdInProgress_s == 0)) begin
cmd_x = PCHG_CMD_C; // initiate precharge of the SDRAM
sAddr_x[CMDBIT_POS_C] = ALL_BANKS_C; // precharge all banks
timer_x = RP_CYCLES; // set timer for this operation
activeFlag_x = 0; // all rows are inactive after a precharge operation
state_x = SELFREFRESH; // self-refresh the SDRAM after the precharge
end
status_o = 'b1000;
//*********************************************************************
// no operation
//*********************************************************************
end else begin
state_x = RW; // continue to look for SDRAM operations to execute
status_o = 'b1001;
end
end
//*********************************************************************
// activate a row of the SDRAM
//*********************************************************************
ACTIVATE: begin
cmd_x = ACTIVE_CMD_C;
sAddr_x = 0; // output the address for the row to be activated
sAddr_x[ROW_LEN_C - 1:0] = row_s;
activeBank_x = bank_s;
activeRow_x[bankIndex_s] = row_s; // store the new active SDRAM row address
activeFlag_x[bankIndex_s] = 1; // the SDRAM is now active
rasTimer_x = RAS_CYCLES; // minimum time before another precharge can occur
timer_x = RCD_CYCLES; // minimum time before a read/write operation can occur
state_x = RW; // return to do read/write operation that initiated this activation
status_o = 'b1010;
end
//*********************************************************************
// refresh a row of the SDRAM
//*********************************************************************
REFRESHROW: begin
cmd_x = RFSH_CMD_C;
timer_x = RFC_CYCLES; // refresh operation interval
rfshCntr_x = rfshCntr_r - 1; // decrement the number of needed row refreshes
state_x = RW; // process more SDRAM operations after refresh is done
status_o = 'b1011;
end
//*********************************************************************
// place the SDRAM into self-refresh and keep it there until further notice
//*********************************************************************
SELFREFRESH: begin
if (doSelfRfsh_s == 1 || lock_i == 0) begin
// keep the SDRAM in self-refresh mode as long as requested and until there is a stable clock
cmd_x = RFSH_CMD_C;// output the refresh command; this is only needed on the first clock cycle
cke_x = 0; // disable the SDRAM clock
end else begin
// else exit self-refresh mode and start processing read and write operations
cke_x = 1; // restart the SDRAM clock
rfshCntr_x = 0; // no refreshes are needed immediately after leaving self-refresh
activeFlag_x = 0; // self-refresh deactivates all rows
timer_x = XSR_CYCLES; // wait this long until read and write operations can resume
state_x = RW;
end
status_o = 'b1100;
end
//*********************************************************************
// unknown state
//*********************************************************************
default: begin
state_x = INITWAIT; // reset state if in erroneous state
status_o = 'b1101;
end
endcase
end // else
end // always
//*********************************************************************
// update registers on the appropriate clock edge
//*********************************************************************
always @(posedge clk_i or posedge rst_i) begin
if (rst_i == 1) begin
// asynchronous reset
state_r <= INITWAIT;
activeFlag_r <= 0;
rfshCntr_r <= 0;
timer_r <= 0;
refTimer_r <= REF_CYCLES;
rasTimer_r <= 0;
wrTimer_r <= 0;
nopCntr_r <= 0;
opBegun_r <= 0;
rdPipeline_r <= 0;
wrPipeline_r <= 0;
cke_r <= 0;
cmd_r <= NOP_CMD_C;
ba_r <= 0;
sAddr_r <= 0;
sData_r <= 0;
sDataDir_r <= INPUT_C;
sdramData_r <= 0;
end else begin
state_r <= state_x;
activeBank_r <= activeBank_x;
`ifdef MULTIPLE_ACTIVE_ROWS_D
activeRow_r[0] = activeRow_x[0];
activeRow_r[1] = activeRow_x[1];
`else
activeRow_r[0] = activeRow_x[0];
`endif
activeFlag_r <= activeFlag_x;
rfshCntr_r <= rfshCntr_x;
timer_r <= timer_x;
refTimer_r <= refTimer_x;
rasTimer_r <= rasTimer_x;
wrTimer_r <= wrTimer_x;
nopCntr_r <= nopCntr_x;
opBegun_r <= opBegun_x;
rdPipeline_r <= rdPipeline_x;
wrPipeline_r <= wrPipeline_x;
cke_r <= cke_x;
cmd_r <= cmd_x;
ba_r <= ba_x;
sAddr_r <= sAddr_x;
sData_r <= sData_x;
sDataDir_r <= sDataDir_x;
sdramData_r <= sdramData_x;
end
end
// The register that gets data from the SDRAM and holds it for the host
// is clocked on the opposite edge. We don't use this register if IN_PHASE=TRUE.
always @(negedge clk_i or posedge rst_i) begin
if (rst_i == 1) begin
// asynchronous reset
sdramDataOppPhase_r <= 0;
end else begin
sdramDataOppPhase_r <= sdramDataOppPhase_x;
end
end
endmodule