第三讲:DDR初始化流程——上电时序与命令序列

各位同学,今天我们来聊聊DDR初始化。说实话,这块内容看起来简单,但坑特别多。我当年第一次调DDR的时候,就因为在初始化上栽了跟头,板子死活跑不起来。后来才发现,是上电时序没满足要求。

3.1 上电时序要求

DDR芯片不是一上电就能用的。它需要严格的电源上升顺序。我习惯把DDR的电源分成三组:

  • VDD:核心电源,一般是1.8V或1.2V
  • VDDQ:IO电源,与VDD同电压
  • VREF:参考电压,通常是VDDQ的一半

上电顺序有讲究。JEDEC标准要求:VDD和VDDQ必须同时上电,或者VDD先于VDDQ。VREF可以在VDDQ稳定之后才起来。

注意: 我曾经遇到过一款DDR3芯片,因为VDDQ比VDD先上电,导致芯片内部闩锁效应,直接烧了。所以这个顺序千万不能搞反。

上电完成后,还需要等待至少200μs的稳定时间。这段时间里,时钟信号必须稳定提供。嗯,这里要注意,时钟不能在上电过程中就切换频率,否则初始化会失败。

3.2 初始化命令序列

上电稳定后,DDR控制器需要发送一系列命令来初始化芯片。说白了,就是告诉DDR芯片:「嘿,我要开始用你了,你准备好!」

标准的初始化序列如下:

  1. NOP命令:发送至少400个时钟周期的NOP
  2. 预充电所有Bank:PALL命令
  3. 两次自动刷新:REF命令
  4. 模式寄存器设置:MRS命令(MR0、MR1、MR2、MR3)
  5. 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
关键点: 每个状态之间的切换,都需要满足时序要求。比如NOP_WAIT必须持续400个时钟周期,这是JEDEC标准规定的。

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要求更长。我一般会用一个计数器来等待校准完成。

经验之谈: 如果ZQ校准失败,DDR的数据眼图会变得很差。我曾经遇到过因为PCB走线阻抗不匹配,导致ZQ校准一直失败的情况。后来换了参考电阻值才解决。

3.6 初始化流程图

下面我用SVG画了个初始化流程图,方便大家理解整个流程:

DDR初始化状态机流程图 IDLE WAIT_PWR NOP_WAIT PALL REFRESH × 2 MRS (MR0-MR3) ZQCL power_good wait_cnt == 200μs nop_cnt == 400 自动跳转 自动跳转 自动跳转

从流程图可以看出,初始化是一个严格的顺序过程。每个状态都必须等待前一个状态完成才能继续。我建议你在设计状态机时,加上超时保护机制。万一某个状态卡住了,还能自动复位重试。

总结一下: DDR初始化看似简单,但涉及到的时序要求非常严格。上电顺序、等待时间、命令间隔,任何一个环节出问题,DDR都无法正常工作。我建议你在仿真阶段就把这些时序验证充分,不要等到板子调通了才发现问题。

好了,这一讲的内容就到这里。初始化是DDR控制器的第一步,也是最重要的一步。后面的读写操作都建立在初始化成功的基础上。希望大家能把这个状态机吃透,以后调DDR的时候就能少走弯路。

可可精品资料库