The DRAM controller implements a hierarchical, modular architecture that separates concerns into distinct functional units. Each module has a well-defined interface and single responsibility.
┌─────────────────────────────────────────────┐
│ CPU Interface Layer │
│ (External communication with processor) │
└─────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ Control Layer (FSM) │
│ (Operation sequencing and coordination) │
└─────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ Utility Layer │
│ (Address decode, timing, refresh) │
└─────────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────┐
│ DRAM Interface Layer │
│ (Physical signal generation) │
└─────────────────────────────────────────────┘
┌──────────────┐
│ CPU │
└──────┬───────┘
│
┌──────────▼──────────┐
│ dram_controller │
│ _top │
└──────────┬──────────┘
│
┌─────────────────┼─────────────────┐
│ │ │
┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Address │ │ Refresh │ │ Timing │
│ Decoder │ │Controller │ │ Generator │
└─────────┘ └───────────┘ └─────┬─────┘
│
┌─────────────────┘
│
┌────▼────┐
│ DRAM │
│ FSM │
└────┬────┘
│
┌──────▼────────┐
│ Command │
│ Generator │
└───────┬───────┘
│
┌───────▼───────┐
│ DRAM │
└───────────────┘
- CPU asserts
cpu_read_reqwith address - Address Decoder splits address into row/column
- FSM latches address and operation type
- FSM transitions: IDLE → ACTIVATE → RCD_WAIT → READ_CAS → READ_WAIT → READ_DATA
- Command Generator asserts RAS, then CAS signals
- Timing Generator enforces tRCD and tCAS delays
- Data captured from DRAM and presented to CPU
- FSM → PRECHARGE → IDLE
- CPU asserts
cpu_write_reqwith address and data - Address Decoder splits address
- FSM latches address, data, and operation type
- FSM transitions: IDLE → ACTIVATE → RCD_WAIT → WRITE_CAS → WRITE_DATA
- Command Generator asserts RAS, then CAS+WE signals
- Data driven onto DRAM bus
- Timing Generator enforces write hold time
- FSM → PRECHARGE → IDLE
- Refresh Controller generates periodic refresh request
- FSM detects
refresh_pending(higher priority than CPU requests) - FSM transitions: IDLE → REFRESH
- Command Generator asserts RAS+CAS (auto-refresh command)
- Timing Generator enforces tREF delay
- FSM acknowledges refresh completion
- Refresh Controller increments row counter
- FSM → IDLE
CLK ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─
RAS_N ──┐ ┌───────────────────────────
└─────┘
CAS_N ────────────┐ ┌───────────────
└─────┘
ADDR ──<ROW>──────<COL>────────────────
DQ ────────────────────────<DATA>────
State IDLE ACT WAIT R_CAS R_WAT R_DAT PRE
CLK ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐
┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─
RAS_N ──┐ ┌───────────────────────
└─────┘
CAS_N ────────────┐ ┌───────────
└─────┘
WE_N ────────────┐ ┌───────────
└─────┘
ADDR ──<ROW>──────<COL>────────────
DQ ────────────────────<DATA>────
State IDLE ACT WAIT W_CAS W_DAT PRE
| Current State | Condition | Next State |
|---|---|---|
| IDLE | refresh_pending | REFRESH |
| IDLE | cpu_read_req | ACTIVATE |
| IDLE | cpu_write_req | ACTIVATE |
| ACTIVATE | timing_done | RCD_WAIT |
| RCD_WAIT | timing_done & read | READ_CAS |
| RCD_WAIT | timing_done & write | WRITE_CAS |
| READ_CAS | timing_done | READ_WAIT |
| READ_WAIT | timing_done | READ_DATA |
| READ_DATA | always | PRECHARGE |
| WRITE_CAS | timing_done | WRITE_DATA |
| WRITE_DATA | timing_done | PRECHARGE |
| PRECHARGE | timing_done | IDLE |
| REFRESH | timing_done | IDLE |
Inputs:
- cpu_addr[ROW+COL-1:0] // Full address
- latch_enable // Latch signal
Outputs:
- row_addr[ROW-1:0] // Decomposed row
- col_addr[COL-1:0] // Decomposed column
- row_addr_reg[ROW-1:0] // Latched row
- col_addr_reg[COL-1:0] // Latched columnInputs:
- refresh_ack // Completion signal
Outputs:
- refresh_pending // Request flag
- refresh_row[ROW-1:0] // Row to refreshInputs:
- timing_select[2:0] // Which timing to load
- load_timing // Load trigger
- count_enable // Enable counting
Outputs:
- timing_done // Counter reached zero
- counter_value[7:0] // Current countInputs:
- cpu_read_req, cpu_write_req
- refresh_pending
- timing_done
Outputs:
- cpu_ready, cpu_busy, cpu_data_valid
- refresh_active, refresh_ack
- timing_select, load_timing, count_enable
- cmd_activate, cmd_read, cmd_write, etc.
- latch_address, capture_read_dataInputs:
- fsm_state[3:0]
- cmd_activate, cmd_read, cmd_write, etc.
- row_addr, col_addr
- write_data
Outputs:
- dram_addr, dram_ras_n, dram_cas_n, etc.
- dq_output_enable, dq_out- Reason: Address decomposition is a distinct function
- Benefit: Can easily change address mapping scheme
- Trade-off: Additional module complexity vs. clarity
- Reason: Refresh is independent of read/write operations
- Benefit: Easy to modify refresh algorithm
- Trade-off: More modules vs. better separation of concerns
- Reason: Timing is used by multiple operations
- Benefit: Centralized timing management
- Trade-off: Interface complexity vs. code reuse
- Reason: DRAM signal generation is complex
- Benefit: Isolates low-level DRAM protocol
- Trade-off: More inter-module wiring vs. maintainability
-
Read Latency: 1 (ACTIVATE) + tRCD + 1 (READ_CAS) + tCAS + 1 (READ_DATA) + tRP cycles
- With defaults: 1 + 3 + 1 + 2 + 1 + 3 = 11 cycles
-
Write Latency: 1 (ACTIVATE) + tRCD + 1 (WRITE_CAS) + 2 (WRITE_DATA) + tRP cycles
- With defaults: 1 + 3 + 1 + 2 + 3 = 10 cycles
-
Refresh Overhead: tREF cycles every REFRESH_INTERVAL cycles
- With defaults: 8 cycles every 500 cycles = 1.6% overhead
- Maximum sustained read throughput: Clock_Freq / Read_Latency
- Maximum sustained write throughput: Clock_Freq / Write_Latency
Each module can be tested independently:
- address_decoder: Verify address split correctness
- refresh_controller: Verify periodic requests and row cycling
- timing_generator: Verify countdown and timing accuracy
- dram_fsm: Verify state transitions and outputs
- dram_command_generator: Verify signal generation
- Full system test with behavioral DRAM model
- Verify read/write data integrity
- Verify refresh doesn't corrupt operations
- Timing verification with waveform analysis
- Multiple Banks: Add bank selection logic to address decoder
- Burst Mode: Extend FSM with burst states
- DDR Support: Modify command generator for DDR timing
- Power Management: Add power-down states to FSM
All key parameters are exposed at top level:
- Address widths
- Data width
- All timing parameters
- Refresh configuration
Document Version: 1.0
Last Updated: 2025-11-24