4. DDR初始化流程(Initialization Sequence)

DDR的初始化,说白了就是给颗粒“梳洗打扮”的过程。
我刚开始做DDR调试时,以为只要把时钟打开、发个复位就完事了。结果板子怎么也跑不起来——后来发现CKE拉高的时机早了几百纳秒,颗粒根本没完成内部校准。
这一章我就把标准JEDEC初始化流程拆开讲,顺便聊聊我踩过的坑。

4.1 初始化总览

初始化不是一次性操作,而是一个严格有序的状态机。
你得让电源、时钟、复位、命令通道按部就班地就绪。
下面这张图是我个人习惯画的初始化流程,每个节点都代表一个关键检查点。

DDR初始化流程 ① 电源/时钟稳定 ② RESET#拉低 ③ CKE拉低 ④ 等待tXPR ⑤ CKE拉高 ⑥ tXSR后MRS初始 注意: tXPR = 最大RESET#低电平时间, 一般取500μs以上。 tXSR = 从自刷新退出到第一个MRS。

你想想看,如果哪一步时序没卡好,颗粒可能直接忽略后续命令,或者进入未知状态。
我曾经就在CKE拉高后等了不到100ns就发MRS,结果读回的模式寄存器全是0xFF——

4.2 步骤拆解与关键参数

下面我按实际调试顺序,把每一步的要点讲清楚。

4.2.1 步骤①:电源与参考电压稳定

  • VDD / VDDQ:必须在RESET#拉低前达到标称电压的95%以上。
  • VREF:内部参考电压需要稳定在VDD/2 ± 1%。
  • 我习惯做法:在电源芯片输出加RC延时,保证DDR颗粒“看到”干净的斜坡。
我曾经遇到的坑:Debug一块FPGA板卡时,VREF纹波达到60mV,导致DLL锁定失败。换成低噪声LDO后,初始化一次性通过。
说白了,电源的纹波噪声比电压值更致命。

4.2.2 步骤②:RESET#拉低(保持低电平)

RESET#拉低后,颗粒内部进入复位状态。这里有个容易忽略的点:保持低电平的时间(tXPR)。

参数DDR3典型值DDR4典型值LPDDR4典型值
tXPR(最小)取决于CKE定时的最大值 + 500μs500μs + tRFC最大(500μs, tRFC)
RESET#低电平时间至少100ns至少100ns至少100ns
小技巧:我一般用逻辑分析仪抓RESET#沿和CKE沿,确保低电平宽度不缩水。如果看到CKE在RESET#还没回到高时就被拉高,基本可以断定初始化失败。

4.2.3 步骤③:CKE保持低电平

CKE必须与RESET#拉低动作同步,且在RESET#释放后继续保持低电平一段时间。
你可能会问:“为什么复位释放了还要让CKE低?”
原因:颗粒内部DLL需要稳定的参考时钟,如果CKE过早拉高,DLL可能无法锁定。

实测经验:DDR3需要CKE低至少维持5个时钟周期;DDR4推荐10个周期以上。
我在一个项目里偷懒只设了3个周期,结果高温老化时频繁报ECC错误——后来加长到15个周期就好了。

4.2.4 步骤④:等待tXPR

这个等待是JEDEC规定的“一视同仁”时间——无论DDR频率多高,至少要等500μs。
我个人建议直接设置1ms,给温度、电压余量留点空间。

// 伪代码示例:DDR初始化时序检查
void ddr_init_check() {
    // 1. 检查电源稳定
    while (power_good_ns < 100) {}  // 100ns延迟
    // 2. 拉低RESET#
    set_reset_low();
    // 3. 拉低CKE
    set_cke_low();
    // 4. 等待tXPR(至少500μs)
    delay_us(600);   // 我习惯加20%余量
    // 5. 拉高CKE(同时释放RESET#)
    set_reset_high();
    set_cke_high();
    // 6. 等待tXSR(一般100ns-500ns)
    delay_ns(200);
    // 7. 发送MRS命令
    send_mrs();
}
注意:上面代码中的tXSR是“自刷新退出时间”。如果你用的是不带自刷新功能的DDR(比如某些GDDR),这个值可能不同。一定要看数据手册。

4.2.5 步骤⑤:CKE拉高

CKE拉高标志着颗粒进入工作状态。注意:RESET#释放必须与CKE拉高严格同步(差异小于tIS + tIH)。
我见过一个案例:设计者用两个独立GPIO控制,软件先后发送,结果两个沿差了200ns,颗粒直接卡在复位态。

4.2.6 步骤⑥:等待tXSR后进行MRS配置

MRS(模式寄存器设置)是初始化的最后一环。你需要配置:

  1. MR0:突发长度、CAS延迟
  2. MR1:DLL使能、ODT
  3. MR2:RL、WL等时序参数
  4. MR3:MPR(多用途寄存器)等

配置顺序必须从MR0开始,不能跳跃。我一般在发送每个MRS命令后,等待tMRD(一般4-8个时钟周期)再发送下一个。

避坑指南:
• 写MR0时一定要先使能DLL复位(DLL Reset bit = 1),然后在完成所有MRS后再用DLL复位结束(bit = 0)。
• 我见过有工程师忘了这一步,DLL一直处于复位状态,所有读写操作都乱序。

4.3 初始化完成后的验证

初始化跑完后,我习惯立即回读所有模式寄存器,确认写入值与预期一致。
如果回读失败,多半是时序没到或者命令被颗粒忽略了。
你可以用下面这个表格快速定位:

检查项正常行为异常表现可能原因
MR0 MR1 MR2 MR3回读值与配置一致全F或随机值初始化步骤缺失,CKE时序不对
DLL Lock寄存器指示已锁定锁定失败时钟抖动大,VREF不稳
ODT状态有信号反射正常信号振铃严重ODT阻值配置错误

说到ODT,我有个惨痛教训:一次DDR3调试中,MR1的ODT阻抗设成40Ω,但实际板级特征阻抗是50Ω,结果上升沿过冲超过20%。后来改成60Ω才压住。

4.4 总结:初始化正确,后面的调试事半功倍

DDR初始化看起来只是几个步骤,但每个步骤背后都有具体的时序要求。
我建议你在调试时先从初始化流程入手——电源、复位、CKE、等待、MRS,一步步用示波器测量。
千万不要跳过检查直接跑读/写测试:初始化没走通,后面所有操作都是空中楼阁。