-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSDRAM_Controller_v.v
517 lines (476 loc) · 23.7 KB
/
SDRAM_Controller_v.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
`timescale 1ns / 1ps
////////////////////////////////////////////////////////////////////////////////////
// SDRAM_controller_v.v
//
// Copyright (C) 2014 Mike Field
//
// 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 3 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, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////////////////
//
// Description: Simple SDRAM controller for a Micron 48LC16M16A2-7E
// or Micron 48LC4M16A2-7E @ 100MHz
// Revision:
// Revision 0.1 - Initial verilog version
//
// Worst case performance (single accesses to different rows or banks) is:
// Writes 16 cycles = 6,250,000 writes/sec = 25.0MB/s (excluding refresh overhead)
// Reads 17 cycles = 5,882,352 reads/sec = 23.5MB/s (excluding refresh overhead)
//
// For 1:1 mixed reads and writes into the same row it is around 88MB/s
// For reads or wries to the same it is can be as high as 184MB/s
////////////////////////////////////////////////////////////////////////////////////
module SDRAM_Controller_v (
clk, reset,
// command and write port
cmd_ready, cmd_enable, cmd_wr, cmd_byte_enable, cmd_address, cmd_data_in,
// Read data port
data_out, data_out_ready,
// SDRAM signals
SDRAM_CLK, SDRAM_CKE, SDRAM_CS, SDRAM_RAS, SDRAM_CAS,
SDRAM_WE, SDRAM_DQM, SDRAM_ADDR, SDRAM_BA, SDRAM_DATA
);
//////////////////////////////////
/// These need to be checked out
//////////////////////////////////
parameter sdram_column_bits = 8; //
parameter sdram_address_width = 22; // zzz
parameter sdram_startup_cycles = 10100; // -- 100us, plus a little more, @ 100MHz
parameter cycles_per_refresh = 1524; // (64000*100)/4196-1 Cacled as (64ms @ 100MHz)/ 4196 rose
input clk;
input reset;
output cmd_ready;
input cmd_enable;
input cmd_wr;
input [sdram_address_width-2:0] cmd_address;
input [3:0] cmd_byte_enable;
input [31:0] cmd_data_in;
//-----------------------------------------------
//--!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//--!! Ensure that all outputs are registered. !!
//--!! Check the pinout report to be sure !!
//--!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//-----------------------------------------------
reg [3:0] iob_command = CMD_NOP;
reg [12:0] iob_address = 13'b0000000000000;
reg [15:0] iob_data = 16'b0000000000000000;
reg [1:0] iob_dqm = 2'b00;
reg iob_cke = 1'b0;
reg [1:0] iob_bank = 2'b00;
reg [31:0] data_out_reg;
//synthesis attribute IOB of iob_command is "TRUE"
//synthesis attribute IOB of iob_address is "TRUE"
//synthesis attribute IOB of iob_data is "TRUE"
//synthesis attribute IOB of iob_dqm is "TRUE"
//synthesis attribute IOB of iob_cke is "TRUE"
//synthesis attribute IOB of iob_bank is "TRUE"
reg [15:0] iob_data_next = 16'b0;
output [31:0] data_out; assign data_out = data_out_reg;
reg data_out_ready_reg;
output data_out_ready; assign data_out_ready = data_out_ready_reg;
output SDRAM_CLK; // Assigned by a primative
output SDRAM_CKE; assign SDRAM_CKE = iob_cke;
output SDRAM_CS; assign SDRAM_CS = iob_command[3];
output SDRAM_RAS; assign SDRAM_RAS = iob_command[2];
output SDRAM_CAS; assign SDRAM_CAS = iob_command[1];
output SDRAM_WE; assign SDRAM_WE = iob_command[0];
output [1:0] SDRAM_DQM; assign SDRAM_DQM = iob_dqm;
output [12:0] SDRAM_ADDR; assign SDRAM_ADDR = iob_address;
output [1:0] SDRAM_BA; assign SDRAM_BA = iob_bank;
inout [15:0] SDRAM_DATA; // Assigned by a primative
// From page 37 of MT48LC16M16A2 datasheet
// Name (Function) CS# RAS# CAS# WE# DQM Addr Data
// COMMAND INHIBIT (NOP) H X X X X X X
// NO OPERATION (NOP) L H H H X X X
// ACTIVE L L H H X Bank/row X
// READ L H L H L/H Bank/col X
// WRITE L H L L L/H Bank/col Valid
// BURST TERMINATE L H H L X X Active
// PRECHARGE L L H L X Code X
// AUTO REFRESH L L L H X X X
// LOAD MODE REGISTER L L L L X Op-code X
// Write enable X X X X L X Active
// Write inhibit X X X X H X High-Z
// -- Here are the commands mapped to constants
parameter CMD_UNSELECTED = 4'b1000;
parameter CMD_NOP = 4'b0111;
parameter CMD_ACTIVE = 4'b0011;
parameter CMD_READ = 4'b0101;
parameter CMD_WRITE = 4'b0100;
parameter CMD_TERMINATE = 4'b0110;
parameter CMD_PRECHARGE = 4'b0010;
parameter CMD_REFRESH = 4'b0001;
parameter CMD_LOAD_MODE_REG = 4'b0000;
wire [12:0] MODE_REG; // Reserved, wr bust, OpMode, CAS Latency (2), Burst Type, Burst Length (2)
assign MODE_REG = {3'b000, 1'b0, 2'b00, 3'b010, 1'b0, 3'b001};
reg [15:0] captured_data;
reg [15:0] captured_data_last;
wire [15:0] sdram_din;
///////////////////////////////
// States for the FSM
///////////////////////////////
parameter s_startup = 5'b00000, s_idle_in_6 = 5'b00001, s_idle_in_5 = 5'b00010, s_idle_in_4 = 5'b00011;
parameter s_idle_in_3 = 5'b00100, s_idle_in_2 = 5'b00101, s_idle_in_1 = 5'b00110, s_idle = 5'b00111;
parameter s_open_in_2 = 5'b01000, s_open_in_1 = 5'b01001, s_write_1 = 5'b01010, s_write_2 = 5'b01011;
parameter s_write_3 = 5'b01100, s_read_1 = 5'b01101, s_read_2 = 5'b01110, s_read_3 = 5'b01111;
parameter s_read_4 = 5'b10000, s_precharge = 5'b10001;
reg [4:0] state = s_startup;
// dual purpose counter, it counts up during the startup phase, then is used to trigger refreshes.
parameter startup_refresh_max = 14'b11111111111111;
reg [13:0] startup_refresh_count = startup_refresh_max-sdram_startup_cycles;
// Indicate the need to refresh when the counter is 2048,
// Force a refresh when the counter is 4096 - (if a refresh is forced,
// multiple refresshes will be forced until the counter is below 2048
wire pending_refresh = startup_refresh_count[11];
wire forcing_refresh = startup_refresh_count[12];
// The incoming address is split into these three values
wire [12:0] addr_row;
wire [12:0] addr_col;
wire [1:0] addr_bank;
// shift register to hold the DQM bits
parameter dqm_sr_high = 3;
reg [dqm_sr_high:0] dqm_sr = 4'b0000; // an extra two bits in case CAS=3
// signals to hold the requested transaction before it is completed
reg save_wr = 1'b0;
reg [12:0] save_row = 13'b0000000000000;
reg [1:0] save_bank = 2'b00;
reg [12:0] save_col = 13'b0000000000000;
reg [31:0] save_data_in = 32'b00000000000000000000000000000000;
reg [3:0] save_byte_enable = 3'b0000;
// control when new transactions are accepted
reg ready_for_new = 1'b0;
reg got_transaction = 1'b0;
reg can_back_to_back = 1'b0;
// signal to control the Hi-Z state of the DQ bus
reg iob_dq_hiz = 1'b0;
// shift register for when to read the data off of the bus
parameter data_ready_delay_high = 4;
reg [data_ready_delay_high:0] data_ready_delay;
// reg [3:0] data_ready_delay; // (for < 60Mhz)
// bit indexes used when splitting the address into row/colum/bank.
parameter start_of_col = 0;
parameter end_of_col = sdram_column_bits-2;
parameter start_of_bank = sdram_column_bits-1;
parameter end_of_bank = sdram_column_bits;
parameter start_of_row = sdram_column_bits+1;
parameter end_of_row = sdram_address_width-2;
parameter prefresh_cmd = 10;
// tell the outside world when we can accept a new transaction;
assign cmd_ready = ready_for_new;
//--------------------------------------------------------------------------
// Seperate the address into row / bank / address
//--------------------------------------------------------------------------
assign addr_row[12] = 1'b0;
assign addr_row[end_of_row-start_of_row:0] = cmd_address[end_of_row:start_of_row];
assign addr_bank = cmd_address[end_of_bank:start_of_bank];
assign addr_col[12:8] = 3'b00;
assign addr_col[sdram_column_bits-1:1] = cmd_address[end_of_col:start_of_col];
assign addr_col[0] = 1'b0;
wire [15:0] sdram_data_wire; assign SDRAM_DATA = sdram_data_wire;
//-----------------------------------------------------------
// Forward the SDRAM clock to the SDRAM chip - 180 degress
// out of phase with the control signals (ensuring setup and holdup times
//-----------------------------------------------------------
ODDR2 #(
.DDR_ALIGNMENT("NONE"), // Sets output alignment to "NONE", "C0" or "C1"
.INIT(1'b0), // Sets initial state of the Q output to 1'b0 or 1'b1
.SRTYPE("SYNC") // Specifies "SYNC" or "ASYNC" set/reset
) ODDR2_inst (
.Q(SDRAM_CLK), .CE(1'b1),
.C0(clk), .C1(!clk),
.D0(1'b0), .D1(1'b1),
.R(1'b0), .S(1'b0)
);
//---------------------------------------------------------------
//-- Explicitly set up the tristate I/O buffers on the DQ signals
//---------------------------------------------------------------
genvar i;
for (i= 0; i < 16; i = i + 1) begin : xx
IOBUF #(
.DRIVE(12),
.IOSTANDARD("LVTTL"),
.SLEW("FAST")
) IOBUF_inst (
.O(sdram_din[i]),
.IO(sdram_data_wire[i]),
.I(iob_data[i]),
.T(iob_dq_hiz)
);
end
always @ (posedge clk ) captured_data <= sdram_din;
always @ (posedge clk )
begin
captured_data_last <= captured_data;
//------------------------------------------------
//-- Default state is to do nothing
//------------------------------------------------
iob_command <= CMD_NOP;
iob_address <= 13'b0000000000000;
iob_bank <= 2'b00;
//------------------------------------------------
//-- countdown for initialisation & refresh
//------------------------------------------------
startup_refresh_count <= startup_refresh_count+1;
//-------------------------------------------------------------------
//-- It we are ready for a new tranasction and one is being presented
//-- then accept it. Also remember what we are reading or writing,
//-- and if it can be back-to-backed with the last transaction
//-------------------------------------------------------------------
if (ready_for_new == 1'b1 && cmd_enable == 1'b1) begin
if(save_bank == addr_bank && save_row == addr_row)
can_back_to_back <= 1'b1;
else
can_back_to_back <= 1'b0;
save_row <= addr_row;
save_bank <= addr_bank;
save_col <= addr_col;
save_wr <= cmd_wr;
save_data_in <= cmd_data_in;
save_byte_enable <= cmd_byte_enable;
got_transaction <= 1'b1;
ready_for_new <= 1'b0;
end
//------------------------------------------------
//-- Handle the data coming back from the
//-- SDRAM for the Read transaction
//------------------------------------------------
data_out_ready_reg <= 1'b0;
if (data_ready_delay[0] == 1'b1) begin
data_out_reg <= {captured_data, captured_data_last};
data_out_ready_reg <= 1'b1;
end
//----------------------------------------------------------------------------
//-- update shift registers used to choose when to present data to/from memory
//----------------------------------------------------------------------------
data_ready_delay <= {1'b0, data_ready_delay[data_ready_delay_high:1]};
iob_dqm <= dqm_sr[1:0];
dqm_sr <= {2'b11, dqm_sr[dqm_sr_high:2]};
case(state)
s_startup: begin
//------------------------------------------------------------------------
//-- This is the initial startup state, where we wait for at least 100us
//-- before starting the start sequence
//--
//-- The initialisation is sequence is
//-- * de-assert SDRAM_CKE
//-- * 100us wait,
//-- * assert SDRAM_CKE
//-- * wait at least one cycle,
//-- * PRECHARGE
//-- * wait 2 cycles
//-- * REFRESH,
//-- * tREF wait
//-- * REFRESH,
//-- * tREF wait
//-- * LOAD_MODE_REG
//-- * 2 cycles wait
//------------------------------------------------------------------------
iob_cke <= 1'b1;
// All the commands during the startup are NOPS, except these
if(startup_refresh_count == startup_refresh_max-31) begin
// ensure all rows are closed
iob_command <= CMD_PRECHARGE;
iob_address[prefresh_cmd] <= 1'b1; // all banks
iob_bank <= 2'b00;
end
else if (startup_refresh_count == startup_refresh_max-23) begin
// these refreshes need to be at least tREF (66ns) apart
iob_command <= CMD_REFRESH;
end else if (startup_refresh_count == startup_refresh_max-15)
iob_command <= CMD_REFRESH;
else if (startup_refresh_count == startup_refresh_max-7) begin
// Now load the mode register
iob_command <= CMD_LOAD_MODE_REG;
iob_address <= MODE_REG;
end
//------------------------------------------------------
//-- if startup is coomplete then go into idle mode,
//-- get prepared to accept a new command, and schedule
//-- the first refresh cycle
//------------------------------------------------------
if (startup_refresh_count == 1'b0) begin
state <= s_idle;
ready_for_new <= 1'b1;
got_transaction <= 1'b0;
startup_refresh_count <= 2048 - cycles_per_refresh+1;
end
end
s_idle_in_6: state <= s_idle_in_5;
s_idle_in_5: state <= s_idle_in_4;
s_idle_in_4: state <= s_idle_in_3;
s_idle_in_3: state <= s_idle_in_2;
s_idle_in_2: state <= s_idle_in_1;
s_idle_in_1: state <= s_idle;
s_idle: begin
// Priority is to issue a refresh if one is outstanding
if (pending_refresh == 1'b1 || forcing_refresh == 1'b1) begin
//------------------------------------------------------------------------
//-- Start the refresh cycle.
//-- This tasks tRFC (66ns), so 6 idle cycles are needed @ 100MHz
//------------------------------------------------------------------------
state <= s_idle_in_6;
iob_command <= CMD_REFRESH;
startup_refresh_count <= startup_refresh_count - cycles_per_refresh+1;
end
else if (got_transaction == 1'b1) begin
//--------------------------------
//-- Start the read or write cycle.
//-- First task is to open the row
//--------------------------------
state <= s_open_in_2;
iob_command <= CMD_ACTIVE;
iob_address <= save_row;
iob_bank <= save_bank;
end
end
//--------------------------------------------
//-- Opening the row ready for reads or writes
//--------------------------------------------
s_open_in_2: state <= s_open_in_1;
s_open_in_1: begin
// still waiting for row to open
if(save_wr == 1'b1) begin
state <= s_write_1;
iob_dq_hiz <= 1'b0;
iob_data <= save_data_in[15:0]; // get the DQ bus out of HiZ early
end else begin
iob_dq_hiz <= 1'b1;
state <= s_read_1;
end
// we will be ready for a new transaction next cycle!
ready_for_new <= 1'b1;
got_transaction <= 1'b0;
end
//----------------------------------
//-- Processing the read transaction
//----------------------------------
s_read_1: begin
state <= s_read_2;
iob_command <= CMD_READ;
iob_address <= save_col;
iob_bank <= save_bank;
iob_address[prefresh_cmd] <= 1'b0; // A10 actually matters - it selects auto precharge
// Schedule reading the data values off the bus
data_ready_delay[data_ready_delay_high] <= 1'b1;
// Set the data masks to read all bytes
iob_dqm <= 2'b00;
dqm_sr[1:0] <= 2'b00;
end
s_read_2: begin
state <= s_read_3;
if (forcing_refresh == 1'b0 && got_transaction == 1'b1 && can_back_to_back == 1'b1) begin
if (save_wr == 1'b0) begin
state <= s_read_1;
ready_for_new <= 1'b1; // we will be ready for a new transaction next cycle!
got_transaction <= 1'b0;
end
end
end
s_read_3: begin
state <= s_read_4;
if (forcing_refresh == 1'b0 && got_transaction == 1'b1 && can_back_to_back == 1'b1) begin
if (save_wr == 1'b0) begin
state <= s_read_1;
ready_for_new <= 1'b1; // we will be ready for a new transaction next cycle!
got_transaction <= 1'b0;
end
end
end
s_read_4: begin
state <= s_precharge;
//-- can we do back-to-back read?
if (forcing_refresh == 1'b0 && got_transaction == 1'b1 && can_back_to_back == 1'b1) begin
if (save_wr == 1'b0) begin
state <= s_read_1;
ready_for_new <= 1'b1; // we will be ready for a new transaction next cycle!
got_transaction <= 1'b0;
end
else
state <= s_open_in_2; // we have to wait for the read data to come back before we swutch the bus into HiZ
end
end
//------------------------------------------------------------------
// -- Processing the write transaction
//-------------------------------------------------------------------
s_write_1: begin
state <= s_write_2;
iob_command <= CMD_WRITE;
iob_address <= save_col;
iob_address[prefresh_cmd] <= 1'b0; // A10 actually matters - it selects auto precharge
iob_bank <= save_bank;
iob_dqm <= ! save_byte_enable[1:0];
dqm_sr[1:0] <= ! save_byte_enable[3:2];
iob_data <= save_data_in[15:0];
iob_data_next <= save_data_in[31:16];
end
s_write_2: begin
state <= s_write_3;
iob_data <= iob_data_next;
// can we do a back-to-back write?
if (forcing_refresh == 1'b0 && got_transaction == 1'b1 && can_back_to_back == 1'b1) begin
if (save_wr == 1'b1) begin
// back-to-back write?
state <= s_write_1;
ready_for_new <= 1'b1;
got_transaction <= 1'b0;
end
// Although it looks right in simulation you can't go write-to-read
// here due to bus contention, as iob_dq_hiz takes a few ns.
end
end
s_write_3: begin
// must wait tRDL, hence the extra idle state
//-- back to back transaction?
if (forcing_refresh == 1'b0 && got_transaction == 1'b1 && can_back_to_back == 1'b1) begin
if (save_wr == 1'b1) begin
// back-to-back write?
state <= s_write_1;
ready_for_new <= 1'b1;
got_transaction <= 1'b0;
end
else begin
// write-to-read switch?
state <= s_read_1;
iob_dq_hiz <= 1'b1;
ready_for_new <= 1'b1; // we will be ready for a new transaction next cycle!
got_transaction <= 1'b0;
end
end else begin
iob_dq_hiz <= 1'b1;
state <= s_precharge;
end
end
//-------------------------------------------------------------------
//-- Closing the row off (this closes all banks)
//-------------------------------------------------------------------
s_precharge: begin
state <= s_idle_in_3;
iob_command <= CMD_PRECHARGE;
iob_address[prefresh_cmd] <= 1'b1; // A10 actually matters - it selects all banks or just one
end
//-------------------------------------------------------------------
//-- We should never get here, but if we do then reset the memory
//-------------------------------------------------------------------
default: begin
state <= s_startup;
ready_for_new <= 1'b0;
startup_refresh_count <= startup_refresh_max-sdram_startup_cycles;
end
endcase
if (reset == 1'b1) begin // Sync reset
state <= s_startup;
ready_for_new <= 1'b0;
startup_refresh_count <= startup_refresh_max-sdram_startup_cycles;
end
end
endmodule