第三讲:DDR初始化流程——上电时序与命令序列
各位同学,今天我们来聊聊DDR初始化。说实话,这块内容看起来简单,但坑特别多。我当年第一次调DDR的时候,就因为在初始化上栽了跟头,板子死活跑不起来。后来才发现,是上电时序没满足要求。
3.1 上电时序要求
DDR芯片不是一上电就能用的。它需要严格的电源上升顺序。我习惯把DDR的电源分成三组:
- VDD:核心电源,一般是1.8V或1.2V
- VDDQ:IO电源,与VDD同电压
- VREF:参考电压,通常是VDDQ的一半
上电顺序有讲究。JEDEC标准要求:VDD和VDDQ必须同时上电,或者VDD先于VDDQ。VREF可以在VDDQ稳定之后才起来。
上电完成后,还需要等待至少200μs的稳定时间。这段时间里,时钟信号必须稳定提供。嗯,这里要注意,时钟不能在上电过程中就切换频率,否则初始化会失败。
3.2 初始化命令序列
上电稳定后,DDR控制器需要发送一系列命令来初始化芯片。说白了,就是告诉DDR芯片:「嘿,我要开始用你了,你准备好!」
标准的初始化序列如下:
- NOP命令:发送至少400个时钟周期的NOP
- 预充电所有Bank:PALL命令
- 两次自动刷新:REF命令
- 模式寄存器设置:MRS命令(MR0、MR1、MR2、MR3)
- ZQ校准:ZQCL命令
你想想看,为什么需要这么多步骤?其实每个步骤都有它的目的。NOP是为了让内部电路稳定,预充电是为了让所有Bank处于空闲状态,刷新是为了初始化DRAM单元。
3.3 状态机设计
我个人习惯用三段式状态机来描述初始化流程。这样代码结构清晰,也容易调试。
先看状态定义:
// 状态编码
typedef enum {
IDLE, // 空闲状态
WAIT_PWR, // 等待上电稳定
NOP_WAIT, // NOP等待
PALL, // 预充电
REFRESH1, // 第一次刷新
REFRESH2, // 第二次刷新
MRS_MR0, // 设置MR0
MRS_MR1, // 设置MR1
MRS_MR2, // 设置MR2
MRS_MR3, // 设置MR3
ZQCL, // ZQ校准
INIT_DONE // 初始化完成
} init_state_t;
状态转移逻辑:
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
end else begin
case (state)
IDLE: begin
if (power_good) state <= WAIT_PWR;
end
WAIT_PWR: begin
if (wait_cnt == 200_000) state <= NOP_WAIT;
end
NOP_WAIT: begin
if (nop_cnt == 400) state <= PALL;
end
PALL: begin
state <= REFRESH1;
end
REFRESH1: begin
state <= REFRESH2;
end
REFRESH2: begin
state <= MRS_MR0;
end
MRS_MR0: state <= MRS_MR1;
MRS_MR1: state <= MRS_MR2;
MRS_MR2: state <= MRS_MR3;
MRS_MR3: state <= ZQCL;
ZQCL: begin
if (zq_done) state <= INIT_DONE;
end
INIT_DONE: state <= INIT_DONE;
default: state <= IDLE;
endcase
end
end
3.4 模式寄存器配置
MRS命令是初始化中最关键的部分。它决定了DDR的工作模式。我给大家列个表格,看看MR0到MR3分别控制什么:
| 寄存器 | 控制内容 | 典型配置 |
|---|---|---|
| MR0 | 突发长度、CAS延迟 | BL=8, CL=11 |
| MR1 | 驱动强度、ODT | RZQ/7, ODT=60Ω |
| MR2 | 写恢复时间、CWL | WR=6, CWL=8 |
| MR3 | MPR、温度补偿 | MPR关闭 |
配置这些寄存器时,地址线A[13:0]用来传递配置数据。比如设置MR0时,A[2:0]控制突发长度,A[6:4]控制CAS延迟。我建议你对照数据手册来配,不同厂家的DDR芯片可能有细微差别。
3.5 ZQ校准
ZQCL命令用于校准DDR的输出驱动强度和ODT电阻值。它通过外部参考电阻(通常是240Ω)来校准内部阻抗。
校准过程需要一定时间。DDR3要求至少512个时钟周期,DDR4要求更长。我一般会用一个计数器来等待校准完成。
3.6 初始化流程图
下面我用SVG画了个初始化流程图,方便大家理解整个流程:
从流程图可以看出,初始化是一个严格的顺序过程。每个状态都必须等待前一个状态完成才能继续。我建议你在设计状态机时,加上超时保护机制。万一某个状态卡住了,还能自动复位重试。
好了,这一讲的内容就到这里。初始化是DDR控制器的第一步,也是最重要的一步。后面的读写操作都建立在初始化成功的基础上。希望大家能把这个状态机吃透,以后调DDR的时候就能少走弯路。