-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspaghetti_sdram_controller.v
More file actions
535 lines (456 loc) · 20.7 KB
/
spaghetti_sdram_controller.v
File metadata and controls
535 lines (456 loc) · 20.7 KB
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
// ============================================================================
// DRAM Controller with Explicit Functional Requirements
// ============================================================================
// This controller implements the following key functions:
// 1. Address Decomposition: Split CPU address into Row and Column
// 2. Operation Control: Determine Read vs Write operations
// 3. Timing Signal Generation: Generate RAS and CAS strobes with proper timing
// 4. Refresh Cycle Management: Handle periodic DRAM refresh
// 5. Status Signaling: Provide busy/ready status to CPU
// ============================================================================
module dram_controller #(
parameter ROW_ADDR_WIDTH = 13, // Row address bits
parameter COL_ADDR_WIDTH = 9, // Column address bits
parameter DATA_WIDTH = 16, // Data bus width
// DRAM Timing Parameters (in clock cycles)
parameter T_RCD = 3, // RAS to CAS delay
parameter T_RP = 3, // Row precharge time
parameter T_RAS = 7, // Row active time
parameter T_CAS = 2, // CAS latency
parameter T_RC = 10, // Row cycle time
parameter T_REF = 8, // Refresh cycle time
// Refresh Configuration
parameter REFRESH_INTERVAL = 500, // Clocks between refresh cycles
parameter REFRESH_ROWS = 8192 // Total rows to refresh
)(
// System Interface
input wire clk,
input wire rst_n,
// ========================================================================
// CPU Interface
// ========================================================================
// Single address from CPU (will be decomposed into row + column)
input wire [ROW_ADDR_WIDTH+COL_ADDR_WIDTH-1:0] cpu_addr,
// Data interface
input wire [DATA_WIDTH-1:0] cpu_data_in,
output reg [DATA_WIDTH-1:0] cpu_data_out,
// Operation control signals
input wire cpu_read_req, // CPU requests read operation
input wire cpu_write_req, // CPU requests write operation
// Status signals back to CPU
output reg cpu_ready, // Controller is ready for new operation
output reg cpu_busy, // Controller is busy (refresh or operation)
output reg cpu_data_valid, // Read data is valid
output reg cpu_refresh_active, // Currently in refresh cycle
// ========================================================================
// DRAM Interface
// ========================================================================
output reg [ROW_ADDR_WIDTH-1:0] dram_addr,
inout wire [DATA_WIDTH-1:0] dram_dq,
// Critical timing signals
output reg dram_ras_n, // Row Address Strobe (active low)
output reg dram_cas_n, // Column Address Strobe (active low)
output reg dram_we_n, // Write Enable (active low)
output reg dram_cs_n, // Chip Select (active low)
output reg dram_cke // Clock Enable
);
// ========================================================================
// 1. ADDRESS DECOMPOSITION
// ========================================================================
// Decompose single CPU address into row and column components
wire [ROW_ADDR_WIDTH-1:0] addr_row;
wire [COL_ADDR_WIDTH-1:0] addr_col;
assign addr_row = cpu_addr[ROW_ADDR_WIDTH+COL_ADDR_WIDTH-1:COL_ADDR_WIDTH];
assign addr_col = cpu_addr[COL_ADDR_WIDTH-1:0];
// Latched addresses for current operation
reg [ROW_ADDR_WIDTH-1:0] current_row;
reg [COL_ADDR_WIDTH-1:0] current_col;
// ========================================================================
// 2. OPERATION CONTROL
// ========================================================================
// Determine and store the type of operation requested
reg operation_is_read;
reg operation_is_write;
// ========================================================================
// 3. TIMING SIGNAL GENERATION
// ========================================================================
// State machine for generating precise timing signals
localparam [3:0]
STATE_IDLE = 4'd0,
STATE_ACTIVATE = 4'd1, // Assert RAS to latch row
STATE_RCD_WAIT = 4'd2, // Wait for tRCD
STATE_READ_CAS = 4'd3, // Assert CAS for read
STATE_READ_WAIT = 4'd4, // Wait for tCAS (data latency)
STATE_READ_DATA = 4'd5, // Capture read data
STATE_WRITE_CAS = 4'd6, // Assert CAS + WE for write
STATE_WRITE_DATA = 4'd7, // Write data to DRAM
STATE_PRECHARGE = 4'd8, // Precharge row
STATE_REFRESH = 4'd9; // Refresh cycle
reg [3:0] current_state;
reg [3:0] next_state;
reg [7:0] timing_counter; // Counter for timing delays
// ========================================================================
// 4. REFRESH CYCLE MANAGEMENT
// ========================================================================
reg [15:0] refresh_timer; // Counts to next refresh
reg [12:0] refresh_row_counter; // Tracks which row to refresh
reg refresh_pending; // Refresh request flag
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
refresh_timer <= 0;
refresh_row_counter <= 0;
refresh_pending <= 0;
end else begin
// Increment refresh timer
if (refresh_timer >= REFRESH_INTERVAL - 1) begin
refresh_timer <= 0;
refresh_pending <= 1; // Request refresh
end else begin
refresh_timer <= refresh_timer + 1;
end
// Clear refresh request when refresh completes
if (current_state == STATE_REFRESH && timing_counter == 1) begin
refresh_pending <= 0;
// Increment row counter for next refresh
if (refresh_row_counter >= REFRESH_ROWS - 1)
refresh_row_counter <= 0;
else
refresh_row_counter <= refresh_row_counter + 1;
end
end
end
// ========================================================================
// Data Bus Control (bidirectional)
// ========================================================================
reg dq_output_enable;
reg [DATA_WIDTH-1:0] dq_out;
assign dram_dq = dq_output_enable ? dq_out : {DATA_WIDTH{1'bz}};
// ========================================================================
// State Machine - Sequential Logic
// ========================================================================
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
current_state <= STATE_IDLE;
timing_counter <= 0;
end else begin
current_state <= next_state;
// Timing counter management
if (next_state != current_state) begin
timing_counter <= 0; // Reset on state change
end else if (timing_counter > 0) begin
timing_counter <= timing_counter - 1;
end
end
end
// ========================================================================
// State Machine - Combinational Logic (Next State)
// ========================================================================
always @(*) begin
next_state = current_state;
case (current_state)
STATE_IDLE: begin
// Priority: Refresh > Read/Write
if (refresh_pending) begin
next_state = STATE_REFRESH;
end else if (cpu_read_req || cpu_write_req) begin
next_state = STATE_ACTIVATE;
end
end
STATE_ACTIVATE: begin
if (timing_counter == 0)
next_state = STATE_RCD_WAIT;
end
STATE_RCD_WAIT: begin
if (timing_counter == 0) begin
if (operation_is_read)
next_state = STATE_READ_CAS;
else if (operation_is_write)
next_state = STATE_WRITE_CAS;
end
end
STATE_READ_CAS: begin
if (timing_counter == 0)
next_state = STATE_READ_WAIT;
end
STATE_READ_WAIT: begin
if (timing_counter == 0)
next_state = STATE_READ_DATA;
end
STATE_READ_DATA: begin
next_state = STATE_PRECHARGE;
end
STATE_WRITE_CAS: begin
if (timing_counter == 0)
next_state = STATE_WRITE_DATA;
end
STATE_WRITE_DATA: begin
if (timing_counter == 0)
next_state = STATE_PRECHARGE;
end
STATE_PRECHARGE: begin
if (timing_counter == 0)
next_state = STATE_IDLE;
end
STATE_REFRESH: begin
if (timing_counter == 0)
next_state = STATE_IDLE;
end
default: next_state = STATE_IDLE;
endcase
end
// ========================================================================
// Output Logic - Generate all control signals
// ========================================================================
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
// DRAM interface defaults
dram_ras_n <= 1'b1;
dram_cas_n <= 1'b1;
dram_we_n <= 1'b1;
dram_cs_n <= 1'b1;
dram_cke <= 1'b0;
dram_addr <= 0;
// CPU status signals
cpu_ready <= 1'b0;
cpu_busy <= 1'b0;
cpu_data_valid <= 1'b0;
cpu_refresh_active <= 1'b0;
cpu_data_out <= 0;
// Internal registers
current_row <= 0;
current_col <= 0;
operation_is_read <= 1'b0;
operation_is_write <= 1'b0;
dq_output_enable <= 1'b0;
dq_out <= 0;
end else begin
// Default signal values (will be overridden in states)
dram_ras_n <= 1'b1;
dram_cas_n <= 1'b1;
dram_we_n <= 1'b1;
dram_cs_n <= 1'b0; // Chip always selected
dram_cke <= 1'b1; // Clock always enabled
cpu_data_valid <= 1'b0;
dq_output_enable <= 1'b0;
case (current_state)
// ============================================================
// IDLE State: Ready to accept new commands
// ============================================================
STATE_IDLE: begin
cpu_ready <= 1'b1;
cpu_busy <= 1'b0;
cpu_refresh_active <= 1'b0;
// Latch address and determine operation type
if (cpu_read_req) begin
current_row <= addr_row;
current_col <= addr_col;
operation_is_read <= 1'b1;
operation_is_write <= 1'b0;
end else if (cpu_write_req) begin
current_row <= addr_row;
current_col <= addr_col;
operation_is_read <= 1'b0;
operation_is_write <= 1'b1;
end
end
// ============================================================
// ACTIVATE: Assert RAS to latch row address
// ============================================================
STATE_ACTIVATE: begin
cpu_ready <= 1'b0;
cpu_busy <= 1'b1;
// RAS signal: latch row address
dram_ras_n <= 1'b0; // Assert RAS
dram_cas_n <= 1'b1;
dram_we_n <= 1'b1;
dram_addr <= current_row;
timing_counter <= 1; // Single cycle for RAS
end
// ============================================================
// RCD_WAIT: Wait for tRCD (RAS to CAS delay)
// ============================================================
STATE_RCD_WAIT: begin
cpu_busy <= 1'b1;
timing_counter <= T_RCD - 1;
end
// ============================================================
// READ_CAS: Assert CAS to latch column for read
// ============================================================
STATE_READ_CAS: begin
cpu_busy <= 1'b1;
// CAS signal: latch column address for read
dram_ras_n <= 1'b1;
dram_cas_n <= 1'b0; // Assert CAS
dram_we_n <= 1'b1; // WE high for read
dram_addr <= {{(ROW_ADDR_WIDTH-COL_ADDR_WIDTH){1'b0}}, current_col};
timing_counter <= 1;
end
// ============================================================
// READ_WAIT: Wait for CAS latency
// ============================================================
STATE_READ_WAIT: begin
cpu_busy <= 1'b1;
timing_counter <= T_CAS - 1;
end
// ============================================================
// READ_DATA: Capture data from DRAM
// ============================================================
STATE_READ_DATA: begin
cpu_busy <= 1'b1;
cpu_data_out <= dram_dq; // Capture data
cpu_data_valid <= 1'b1; // Signal valid data to CPU
end
// ============================================================
// WRITE_CAS: Assert CAS + WE to latch column for write
// ============================================================
STATE_WRITE_CAS: begin
cpu_busy <= 1'b1;
// CAS + WE signals: latch column and write enable
dram_ras_n <= 1'b1;
dram_cas_n <= 1'b0; // Assert CAS
dram_we_n <= 1'b0; // Assert WE for write
dram_addr <= {{(ROW_ADDR_WIDTH-COL_ADDR_WIDTH){1'b0}}, current_col};
timing_counter <= 1;
end
// ============================================================
// WRITE_DATA: Drive data onto bus
// ============================================================
STATE_WRITE_DATA: begin
cpu_busy <= 1'b1;
dq_output_enable <= 1'b1;
dq_out <= cpu_data_in;
timing_counter <= 2;
end
// ============================================================
// PRECHARGE: Deactivate row
// ============================================================
STATE_PRECHARGE: begin
cpu_busy <= 1'b1;
// Precharge command
dram_ras_n <= 1'b0;
dram_cas_n <= 1'b1;
dram_we_n <= 1'b0;
dram_addr <= {1'b1, {(ROW_ADDR_WIDTH-1){1'b0}}}; // A10=1 for all banks
timing_counter <= T_RP - 1;
end
// ============================================================
// REFRESH: Auto-refresh cycle
// ============================================================
STATE_REFRESH: begin
cpu_ready <= 1'b0;
cpu_busy <= 1'b1;
cpu_refresh_active <= 1'b1; // Signal CPU that refresh is active
// Auto-refresh command
dram_ras_n <= 1'b0;
dram_cas_n <= 1'b0;
dram_we_n <= 1'b1;
timing_counter <= T_REF - 1;
end
default: begin
cpu_ready <= 1'b0;
cpu_busy <= 1'b0;
end
endcase
end
end
endmodule
// ============================================================================
// Testbench
// ============================================================================
module dram_controller_tb;
reg clk, rst_n;
reg [21:0] cpu_addr;
reg [15:0] cpu_data_in;
wire [15:0] cpu_data_out;
reg cpu_read_req, cpu_write_req;
wire cpu_ready, cpu_busy, cpu_data_valid, cpu_refresh_active;
wire [12:0] dram_addr;
wire [15:0] dram_dq;
wire dram_ras_n, dram_cas_n, dram_we_n, dram_cs_n, dram_cke;
// Instantiate controller
dram_controller #(
.REFRESH_INTERVAL(100) // Faster refresh for simulation
) dut (
.clk(clk),
.rst_n(rst_n),
.cpu_addr(cpu_addr),
.cpu_data_in(cpu_data_in),
.cpu_data_out(cpu_data_out),
.cpu_read_req(cpu_read_req),
.cpu_write_req(cpu_write_req),
.cpu_ready(cpu_ready),
.cpu_busy(cpu_busy),
.cpu_data_valid(cpu_data_valid),
.cpu_refresh_active(cpu_refresh_active),
.dram_addr(dram_addr),
.dram_dq(dram_dq),
.dram_ras_n(dram_ras_n),
.dram_cas_n(dram_cas_n),
.dram_we_n(dram_we_n),
.dram_cs_n(dram_cs_n),
.dram_cke(dram_cke)
);
// Clock generation: 100MHz (10ns period)
initial clk = 0;
always #5 clk = ~clk;
// Simple DRAM model
reg [15:0] test_read_data = 16'hABCD;
assign dram_dq = (!dram_cas_n && dram_we_n) ? test_read_data : 16'hzzzz;
// Test sequence
initial begin
$display("=== DRAM Controller Test ===");
// Initialize
rst_n = 0;
cpu_read_req = 0;
cpu_write_req = 0;
cpu_addr = 0;
cpu_data_in = 0;
#30 rst_n = 1;
$display("Time %0t: Reset released", $time);
// Wait for ready
wait(cpu_ready);
$display("Time %0t: Controller ready", $time);
// Test Write Operation
@(posedge clk);
cpu_addr = 22'h123456; // Row=0x48D, Col=0x056
cpu_data_in = 16'h5555;
cpu_write_req = 1;
$display("Time %0t: Write request - Addr=%h, Data=%h", $time, cpu_addr, cpu_data_in);
@(posedge clk);
cpu_write_req = 0;
wait(cpu_ready);
$display("Time %0t: Write complete", $time);
// Wait a bit
repeat(10) @(posedge clk);
// Test Read Operation
@(posedge clk);
cpu_addr = 22'h654321; // Row=0x1950, Col=0x121
cpu_read_req = 1;
$display("Time %0t: Read request - Addr=%h", $time, cpu_addr);
@(posedge clk);
cpu_read_req = 0;
wait(cpu_data_valid);
$display("Time %0t: Read complete - Data=%h", $time, cpu_data_out);
// Observe refresh cycle
$display("Time %0t: Waiting for refresh cycle...", $time);
wait(cpu_refresh_active);
$display("Time %0t: Refresh cycle started", $time);
wait(!cpu_refresh_active);
$display("Time %0t: Refresh cycle complete", $time);
#200;
$display("=== Test Complete ===");
$finish;
end
// Monitor key signals
always @(posedge clk) begin
if (!dram_ras_n)
$display("Time %0t: RAS asserted - Addr=%h", $time, dram_addr);
if (!dram_cas_n)
$display("Time %0t: CAS asserted - Addr=%h, WE=%b", $time, dram_addr, dram_we_n);
end
// Waveform dump
initial begin
$dumpfile("dram_controller.vcd");
$dumpvars(0, dram_controller_tb);
end
endmodule