DRAM/SDRAM
存储密度更大的随机存储器
1. 引言
在 ysyxSoC 项目中,我们先后引入了 SRAM(8KB) 和 PSRAM(4MB),但前者容量太小,后者访问带宽有限。为了运行大型程序(如 RT-Thread、microbench)并满足 CPU 对高带宽主存的需求,我们最终引入了 SDRAM(同步动态随机存取存储器)。
本总结将系统性地阐述 DRAM/SDRAM 的存储原理、颗粒结构、控制器设计、扩展方式及其在 ysyxSoC 中的工程实践,并提供完整的 Verilog 代码示例。
2. DRAM 基本单元与操作原理
2.1 存储单元(1T1C)
- 结构:1 个晶体管(开关) + 1 个电容(电荷存储)。
- 逻辑:电容充电(高电平)表示逻辑“1”,放电(低电平)表示逻辑“0”。
- 漏电问题:电容会自然放电,数据在数毫秒内丢失,因此必须周期性刷新(Refresh)。
- 刷新要求:每个存储单元必须在 64ms 内被刷新一次(标准),否则数据丢失。
2.2 读出放大器(Sense Amplifier)与行缓冲
- 读出放大器:用于检测电容上的微小电压差,并将其放大为稳定的逻辑电平。多个读出放大器组成行缓冲(Row Buffer)。
- 操作流程:
- PRECHARGE:预充电位线,使两端电压相等(VDD/2)。
- ACTIVATE(激活行):将目标行的字线拉高,连接存储单元到位线,电荷共享产生微小电压差。
- 读出放大器使能,检测并放大该电压差,将整行数据锁存到行缓冲中。
- READ/WRITE:对行缓冲中的列进行读写操作。
- PRECHARGE(再次):关闭行,将行缓冲数据写回存储单元,并重新预充电位线。
2.3 行缓冲命中与冲突
- 行命中(Row Hit):待访问地址的行已处于激活状态(在行缓冲中),可直接进行读写,延迟低(~10–15ns)。
- 行冲突(Row Conflict):待访问地址的行不同于当前激活行,必须先 PRECHARGE 关闭当前行,再 ACTIVATE 新行,延迟高(~60–80ns)。
2.4 刷新
- 控制器必须定期向每个 Bank 发送 AUTO REFRESH 命令。
- 刷新操作会占用总线带宽,但可分散在时间上以减少对性能的影响。
3. SDRAM 颗粒结构(以 MT48LC16M16A2 为例)
3.1 逻辑组织
- 4 个 Bank(存储体),每个 Bank 是一个行列矩阵。
- 行数:8192 行,列数:512 列。
- 颗粒字长:16 位(DQ[15:0])。
- 总容量:4 × 8192 × 512 × 16 bit = 256 Mbit = 32 MB。
3.2 引脚定义
| 信号组 | 名称 | 位宽 | 说明 |
|---|---|---|---|
| 时钟 | CLK, CKE | 1+1 | 主时钟和时钟使能 |
| 命令 | CS#, RAS#, CAS#, WE# | 4 | 组合解码出命令 |
| 地址 | A[12:0] | 13 | 行/列复用地址 |
| Bank 地址 | BA[1:0] | 2 | 选择 4 个 Bank 之一 |
| 数据 | DQ[15:0] | 16 | 双向数据总线 |
| 掩码 | DQM[1:0] | 2 | 每个字节的写掩码(低有效) |
3.3 命令解码
命令由 {CS#, RAS#, CAS#, WE#} 组合生成,常用命令如下:
| 命令 | CS# | RAS# | CAS# | WE# | 功能 |
|---|---|---|---|---|---|
| ACTIVE | 0 | 0 | 1 | 1 | 激活指定 Bank 的一行 |
| READ | 0 | 1 | 0 | 1 | 从已激活行读出数据(突发) |
| WRITE | 0 | 1 | 0 | 0 | 向已激活行写入数据(突发) |
| PRECHARGE | 0 | 0 | 1 | 0 | 关闭当前行,预充电 |
| AUTO REFRESH | 0 | 0 | 0 | 1 | 自动刷新一行(内部计数器) |
| LOAD MODE REG | 0 | 0 | 0 | 0 | 设置模式寄存器(CAS 延迟等) |
| NOP | 0 | 1 | 1 | 1 | 无操作 |
3.4 突发传输(Burst)与 CAS 延迟
- 突发传输:一次 READ/WRITE 命令后,可连续传输多个数据(Burst Length = 1, 2, 4, 8 或全页)。
- CAS 延迟(CL):从 READ 命令发出到第一个数据出现在 DQ 引脚上的时钟周期数(如 CL=2)。
- 效率:突发传输避免了每个节拍重新发送列地址,显著提升连续访问带宽。
- 示例:若突发长度=4,CL=2,则总周期 = 1(命令)+ 2(CL)+ 3(后续节拍)= 6 周期;而若分四次发送 READ,则需 12 周期。
3.5 模式寄存器
- 通过 LOAD MODE REGISTER 命令配置:
- CAS 延迟(CAS Latency):a[6:4]。
- 突发长度(Burst Length):a[2:0](1,2,4,8 或全页)。
- 其他字段(突发类型、写模式等可忽略)。
4. SDRAM 控制器(ysyxSoC 集成)
4.1 核心功能
- 将 AXI/APB 总线请求转换为 SDRAM 命令序列。
- 管理行缓冲状态,实现行命中优化(FR-FCFS 调度)。
- 定时发送 AUTO REFRESH(每 7.8μs 刷新一行,保证 64ms 内全部刷新)。
- 支持突发传输,匹配 AXI 突发长度。
4.2 提供的封装
sdram_top_apb:APB 封装,适用于功能调试(速度慢,但握手机制简单)。sdram_top_axi:AXI 封装,用于最终集成(支持突发、乱序、高带宽)。
4.2.1 APB 封装关键代码(sdram_top_apb.v 简化)
wire is_read = ((in_psel && !in_penable) || (state == ST_WAIT_ACCEPT)) && !in_pwrite;
wire is_write = ((in_psel && !in_penable) || (state == ST_WAIT_ACCEPT)) && in_pwrite;
sdram_axi_core #(
.SDRAM_MHZ(100),
.SDRAM_ADDR_W(24),
.SDRAM_COL_W(9),
.SDRAM_READ_LATENCY(2)
) u_sdram_ctrl(
.clk_i(clock),
.rst_i(reset),
.inport_wr_i(is_write ? in_pstrb : 4'b0),
.inport_rd_i(is_read),
.inport_addr_i(in_paddr),
.inport_write_data_i(in_pwdata),
.inport_accept_o(req_accept),
.inport_ack_o(in_pready),
.inport_error_o(in_pslverr),
.inport_read_data_o(in_prdata),
.sdram_* (sdram_*)
);
4.2.2 内部核心 sdram_axi_core 关键参数
SDRAM_ADDR_W = 24(行13 + 列9 + Bank2)。SDRAM_READ_LATENCY = 2(CAS 延迟)。- 自动计算时序参数:
tRCD,tRP,tRFC基于SDRAM_MHZ。
5. 扩展方式:位扩展与字扩展
5.1 位扩展(Bit-Width Expansion)
目的:增大数据总线宽度,提升单次访存带宽。
方法:将多颗颗粒的数据线并联,但共享地址/命令。例如两颗 16 位颗粒组成 32 位总线。
实现(sdram_32mx32.v):
module sdram_32mx32(
input clk, cke, cs, ras, cas, we,
input [12:0] a,
input [1:0] ba,
input [3:0] dqm,
inout [31:0] dq
);
sdram_32mx16 u_low (
.clk(clk), .cke(cke), .cs(cs), .ras(ras), .cas(cas), .we(we),
.a(a), .ba(ba), .dqm(dqm[1:0]), .dq(dq[15:0])
);
sdram_32mx16 u_high (
.clk(clk), .cke(cke), .cs(cs), .ras(ras), .cas(cas), .we(we),
.a(a), .ba(ba), .dqm(dqm[3:2]), .dq(dq[31:16])
);
endmodule
效果:带宽翻倍,容量不变(仍是 32MB),但 DQ 引脚数翻倍。
5.2 字扩展(Word-Depth Expansion)
目的:增加存储容量。
方法:用高位地址线作为片选,选择不同的颗粒组。每组内部可包含位扩展。
实现(sdram.v,实例化两组 sdram_32mx32):
wire [2:0] command = {ras, cas, we};
reg ras_u0, cas_u0, we_u0;
reg ras_u1, cas_u1, we_u1;
always @(*) begin
case(command)
3'b000, 3'b111: begin // NOP and LOAD MODE are broadcast
{ras_u0, cas_u0, we_u0} = command;
{ras_u1, cas_u1, we_u1} = command;
end
default: begin
{ras_u0, cas_u0, we_u0} = !a[13] ? command : 3'b111;
{ras_u1, cas_u1, we_u1} = a[13] ? command : 3'b111;
end
endcase
end
sdram_32mx32 grp0 ( ... .ras(ras_u0), .cas(cas_u0), .we(we_u0), .a(a[12:0]), ... .dq(dq) );
sdram_32mx32 grp1 ( ... .ras(ras_u1), .cas(cas_u1), .we(we_u1), .a(a[12:0]), ... .dq(dq) );
效果:容量从 32MB 扩展至 64MB,地址连续(高位 A[13] 区分组),带宽不变。
5.3 组合扩展
- 最终 ysyxSoC:使用两组
sdram_32mx32(每组位扩展),通过字扩展实现 64MB 容量,32 位数据总线。 - 地址映射:0xa0000000~0xa1ffffff → 组0;0xa2000000~0xa3ffffff → 组1。
6. PSRAM 与 DRAM 对比
| 特性 | PSRAM(伪静态) | SDRAM(同步动态) |
|---|---|---|
| 内部刷新 | 内置自刷新 | 需外部控制器定时刷新 |
| 接口 | SPI/QSPI(串行,引脚少) | 并行(地址/数据/命令,引脚多) |
| 带宽 | 受限(串行) | 高(并行 + 突发) |
| 容量 | 中等(如 4MB) | 大(可达数 GB) |
| 易用性 | 类似 SRAM,控制简单 | 需复杂时序和状态机 |
| 典型应用 | 嵌入式小系统 | 通用计算机主存 |
7. 性能与资源权衡
| 扩展方式 | 优点 | 缺点 |
|---|---|---|
| 位扩展 | 提升带宽,无需修改地址逻辑 | DQ 引脚数增加,PCB 布线复杂 |
| 字扩展 | 提升容量,仅需增加 1 位地址引脚 | 跨组访问可能增加延迟 |
| 组合扩展 | 同时获得容量与带宽提升 | 控制器需管理更多片选和时序 |
在 ysyxSoC 中,我们通过组合扩展,在仿真环境中实现了 64MB、32 位宽 的 SDRAM 子系统,足以运行 RT-Thread 等复杂应用。
8. 实际应用与测试
8.1 测试
- 使用
mem-test程序对 SDRAM 空间进行全地址读写校验。 - APB 封装下,完整测试 64MB 需数小时(受限于 APB 单拍传输)。
- AXI 封装下,突发传输可显著加速。
8.2 引导加载
- Bootloader(SSBL)将程序从 Flash 搬运到 SDRAM 起始地址(0xa0000000)。
- 栈仍放置在 SRAM(8KB)以提升函数调用速度。
8.3 运行大型程序
- 在 SDRAM 上运行
microbench和 RT-Thread,性能和容量均满足要求。
9. 总结
- DRAM/SDRAM 通过 1T1C 单元实现高密度存储,但需刷新和复杂时序控制。
- SDRAM 控制器 是连接总线与颗粒的关键模块,负责命令生成、时序管理和刷新调度。
- 位扩展 和 字扩展 是构建大容量、高带宽主存系统的两种基本方式,可灵活组合。
- ysyxSoC 成功集成了一个 64MB、32 位宽的 SDRAM 子系统,为运行大型软件提供了坚实基础。
通过理解这些原理和实现,你已具备了设计现代 SoC 内存子系统的核心能力。

浙公网安备 33010602011771号