做过 DDR4 SDRAM 控制的同学应该都有印象:FPGA 或者 CPU 上电之后,DDR4 颗粒完全不是拿来就能用,而是要先把一串模式寄存器配置好,跑过写均衡、读训练、写训练,最后才敢真正读写用户数据。这些训练步骤里,MPR Read 往往是第一道真正的“照妖镜”。它不向内存阵列写任何数据,却能直接从 DRAM 内部的 Multi Purpose Register 读回一串已知码型,用来校准 DQS 与 DQ 的相位关系。
这篇笔记就围绕 DDR4 内存初始化里的 MPR Read 展开,重点讲它到底是什么、MR3 怎么配、命令时序怎么给、数据回来之后怎么判断,以及我最常踩的几个坑。适合正在调 FPGA DDR4 控制器、或者想弄懂 DDR4 初始化训练流程的硬件工程师。
1. 先说清楚:DDR4 初始化到底在干什么
1.1 上电后的“半成品”状态
DDR4 颗粒上电之后,内部电路并不是马上就能维持一个可靠的读写接口。电源、参考电压、时钟、DLL(Delay Locked Loop)都需要时间稳定。除了这些模拟部分,还有很多数字控制位还处于复位后的默认值。比如输出驱动强度、ODT 终端电阻、读 DQS 前置码长度、突发长度,这些全靠模式寄存器去告诉颗粒。
如果对 DDR4 还没有整体感觉,可以这么理解:颗粒上电后相当于一个只有电源和时钟、但所有“配置”都还没加载的板卡。你不把模式寄存器写完,DRAM 不知道自己是 x8 还是 x16、不知道读突发是 BC4 还是 BL8、不知道输出驱动是 34Ω 还是 40Ω,自然也没办法做可靠的数据交换。
1.2 初始化顺序里的 MPR 位置
DDR4 的初始化没有统一到只有一条路径,但主流顺序大致是这样:
- 电源和参考电压稳定后,拉低 RESET#,再回到高电平。
- CKE 从低到高,时钟保持稳定,等待 tXPR 等时序。
- 通过 Mode Register Write(MRW)设置 MR0、MR1、MR2、MR3 等。
- 做 ZQ 校准:先是 ZQCL(ZQ Calibration Long),必要时再补 ZQCS。
- 写均衡训练(Write Leveling),主要是对齐 DQS 与 CK 的相位。
- 读训练阶段开始,其中第一步常见的就是 MPR Read。
- 基于 MPR 结果调整读 DQS/DQ,再做实际数据的读写训练。
- 全部训练完成后,再进入正常读写。
这里要特别注意,MPR Read 不是独立于其他步骤的“额外功能”,而是读训练前期的关键一步。写均衡做完,DQS 与 CK 的关系有了基本保证;MPR Read 则从颗粒端回读已知码型,帮控制器确定读数据路径上的延迟到底是多少。没有这一步,控制器连读 DQ 数据也不知道该不该信。
2. MPR Register 机制与 MR3 配置
2.1 MPR 是什么
MPR 的全称是 Multi Purpose Register,翻译过来就是多用途寄存器。名字里带着 Register,说明它不在 DRAM 的存储阵列里,而是颗粒内部逻辑的一部分。正常 READ 命令访问的是 bank 和 cell;当 MPR 被打开时,READ 命令会绕开存储阵列,直接把 MPR 页面里的预置数据送到 DQ 上。
为什么要做这样一个东西?原因很简单:内存控制器上电后的第一次读操作很难做。此时控制器不知道读 DQS 与 DQ 之间的相对相位,不知道每个 bit lane 的走线延迟差多少。如果直接去读内存阵列里的随机数据,万一读错了,根本分不清是颗粒没配好、走线有问题还是控制器时序不对。MPR 模式提供的是固定已知数据,控制器在发出 READ 的瞬间就知道该收到哪个码型。接下来要做的事情只有一件:对比。
我习惯把 MPR 读理解成硬件行业的测试治具。生产线上测一块板子,总要先给一个标准信号,然后看输出是否和预期一致;MPR 就是 DDR4 颗粒自带的“标准信号发生器”。它本身不存用户数据,但能帮控制器把读通路校准到一个可用的状态。
2.2 MR3 怎么配置
MPR 的开关在 MR3 里。DDR4 的模式寄存器写入命令通过 CS#、RAS#、CAS#、WE#、以及地址线共同编码。写入 MR3 时,地址线上的不同位分别对应 MR3 的 A0-A13 等。下面是最常用的几个位:
| MR3 位 | 名称 | 典型配置说明 |
|---|---|---|
| A0 | MPR Enable | 0:关闭 MPR,正常读;1:打开 MPR,READ 命令读取 MPR |
| A2:A1 | MPR Page Select | 00、01、10、11 选择 4 个 MPR 页面 |
| A3 | MPR Read Format | 不同颗粒定义有差异,一般固定按颗粒手册推荐值设置 |
这里必须强调一句:不要只看这张表就马上去写寄存器。JEDEC 标准给出的是大框架,不同品牌、不同容量密度、不同频率等级的颗粒,在 MR3 的个别位上可能会有差异。我遇到过一款颗粒把 A3 设置为 1 时会输出带 DBI 翻转的 MPR 数据,另一款则完全没反应。所以在真正写控制器代码前,把颗粒 datasheet 翻到 Mode Register 章节,逐位核对,这个流程省不了。
当 MR3[0] 被置 1 后,颗粒并不是瞬间就进入 MPR 模式。MRW 命令发出后,需要等待 tMRD 和 tMOD。tMRD 是两次 MRW 之间的最小间隔,tMOD 是模式寄存器更新到允许访问所需的时间。简单记忆:发完 MRW 之后别急着发 READ,等几十个时钟再说,具体数值查所用速度等级的 datasheet。
2.3 为什么 MPR 要分 Page
MPR 有四页而不是一页,因为训练时需要的码型不止一种。只用一种全 0 或者全 1 的数据,只能看出 DQ 通不通,看不出某一根 DQ 和 DQS 之间的相对偏斜,也看不出读突发边缘的毛刺。多几个页面,控制器就可以发多个 READ 命令,分别读回不同的码型,比如交替方波、单 bit 独热码、全高全低等,方便做逐 bit 的相位扫描。
很多控制器在训练时会把 MPR 页面全部扫一遍。Page 0 通常用于最基本的读数据训练,Page 1/2/3 则用来补足边界条件。具体每个页面的布排列,JEDEC 给了大原则,但颗粒厂也留了一些灵活空间。作为控制器侧,你不一定需要背下来每个页面每个地址是什么数据,重要的是建立一套期望码型表,把读回来的结果和表里逐 bit 比较。
3. MPR Read 全流程:从命令序列到判定标准
3.1 最小化命令序列
下面是一段很常见的初始化训练伪代码,只摘录了和 MPR Read 直接相关的部分。实际工程里前后还有 ZQ 校准、写均衡等,这里先不展开。
// 前置条件:电源稳定、RESET 完成、CKE 拉高、时钟稳定、DLL 已经使能并锁定 1. MRW MR0:设置 CL、WR、突发长度、DLL Reset(如果没做过) 2. MRW MR1:设置 DLL Enable、输出驱动、ODT 3. MRW MR2:设置 CWL、动态 ODT 等 4. 等待 DLL 锁定,视规格等待 tDLLK 5. MRW MR3:A0=1,A2:A1=选中的 MPR Page,其余位按需配置 6. 等待 tMRD/tMOD 7. 发送 READ 命令(MPR 模式下,读的已不是 bank 数据) 8. 等待读数据返回,与期望码型逐 bit 对比 9. 如果训练完成,MRW MR3:A0=0,关闭 MPR 10. 再等待 tMRD/tMOD,之后才能进行正常读写训练从软件角度看,这段序列很简单,不过是几条命令加一个判断。但硬件上每一步都有坑:MRW 之前 CKE 必须为高;MRW 命令本身不能和其他命令混在同一个拍;命令地址线上的编码必须严格按颗粒手册来;MRW 之后不能立刻用普通 READ 去读 MPR 数据,否则时序不够,读回来的东西可能是一半新配置一半旧配置。
3.2 读 MPR 时的时序参数
MPR Read 说到底还是 READ 命令,所以 READ 命令本身需要的时序参数一个都不能省。初始化阶段尤其要注意下面几个:
- tRCD、tRP、tRAS 这些命令间距在 MPR 模式下通常可以放宽,因为不真正激活 bank,但控制器如果严格按颗粒规格最小间隔执行,会稳妥很多。
- 读 DQS 前置码(Read Preamble)在训练时往往设成 1 tCK。如果设成 2 tCK,MPR 读回的数据会有明显差异,容易误判。
- READ-to-READ 的不同 bank 间隔、同 bank 间隔,仍要按 spec 满足。
- MRW 到 READ 之间必须满足 tMRD 和 tMOD,这在控制器状态机里最容易漏。
具体到控制器实现,我见过两种风格。一种是“参数全按最坏情况拉大”,MRW 后固定等 32 个周期甚至 64 个周期再发 READ,简单但牺牲效率;另一种是严格按照 datasheet 的 timing 值去编程,速度快但一旦时序配置错,MPR Read 大概率失败。初始化阶段我建议先采用宽松参数把通路打通,再逐步收敛到规格值。训练代码短,跑一次也就几十微秒,稳健比好看更重要。
3.3 拿到数据怎么判定
MPR 数据回来以后,最忌讳只看“对没对上”一个布尔量。好的做法是把每个 bit 的比对结果都记录下来,尤其对 x8/x16 颗粒,每个 byte lane 的 DQ0-DQ7 是否全部一致、有没有固定某一位出错,信息量完全不一样。
我自己的排查顺序是:
- 先看整个 READ 返回的数据是否是期望码型。如果整体全 0 或者全 1,多半不是 MPR 页面配错,而是 DQS gate 的窗口没有对准,数据根本没采到。
- 再看 per-bit 错误。如果只有 DQ3 固定错,先怀疑这一根 DQ 到控制器之间的走线延迟、等长关系,或者是 FPGA 管脚约束、片上延迟不对。
- 最后看错误是否和页面选择有关。如果 Page 0 对、Page 1 不对,那通常是信号完整性问题,而不是 MPR 机制问题。
这里有个容易被忽略的点:MPR 读命令返回的数据不是普通用户数据,可能不带 ECC、不带 CRC,甚至受 DBI 开关影响。如果颗粒配置了 DBI,读训练模式又没把 DBI 关闭,数据会跟你脑子里的期望码型不一致。训练阶段我一般先关掉 DBI,等 MPR 通路稳定后再单独做 DBI 验证。
3.4 关闭 MPR:随手收尾
MPR Read 训练结束之后,一定要记得把 MR3[0] 写回 0。这件事看起来很基础,却是很多初始化状态机最容易忽略的。如果 MPR 没关,后续所有 READ 命令都会去读 MPR 页面,而不是内存阵列。你在调试时会看到一种很诡异的现象:读写训练明明全过,等跑到 DDR4 性能测试时,数据错得完全没有规律。
关闭 MPR 的操作和打开时一样:MRW 写 MR3,A0 置 0,然后等待 tMRD/tMOD。建议在关闭之后顺手做一次普通 READ 验证,读一个全 0 或者全 1 的已知区域,确认颗粒已经回到正常读路径。这一步成本很低,但能省掉后面好几个小时的排查时间。
4. 实操中容易踩的坑
4.1 现象一:MPR 读回来全是同一个值
这是初始化训练里最常见的失败现象。MPR READ 发出去了,数据总线回过来的却是 0x00、0xFF 或者 0x55 这种“看起来很有规律但完全不对”的值。先说结论:这种问题大多不是 MPR 机制的问题,而是读 DQS gate 的采样窗口没对准。
MPR Read 的数据相位和正常的读数据一样,需要读 DQS 先给出 preamble,控制器才能判断从哪个时刻开始采样。如果 DQS gate 打开太早或太晚,采样窗没有覆盖有效数据,控制器采到的要么是总线空闲电平,要么是已经翻过去的旧数据。处理办法不是反复调 MR3,而是先去看读 DQS 前置码和 gate 窗口。FPGA 工程里可以抓 DQS、DQ 的在线逻辑分析仪波形,或者直接用控制器的 per-bit delay 做逐 bit 扫描。
4.2 现象二:特定 bit 固定出错
另一种常见现象是 MPR 码型大部分对,但某一位固定错。这种情况和“全错”不同,更像是物理链路的问题。DDR4 频率很高,PCB 上每一根 DQ 的走线长度、过孔数量、阻抗控制都会引入不同的时延。如果板子上 DQ 组内部等长没做好,或者 FPGA 片内 DQS 到 DQ 的延迟配置没收敛,就会出现某一位对不上。
我调过一块板子,MPR 读回来一直是 DQ5 固定错,查了很久,最后发现原理图上 DQ5 和 DQS 差分对之间多串了一个 0 欧电阻,导致走线末端阻抗不连续。把它去掉后,MPR 立刻全对。碰到 per-bit 固定错误,先查硬件、再查约束、最后才怀疑 MPR 配置,这个顺序基本不会错。
4.3 现象三:FPGA 控制器 cal fail 出现在 MPR 阶段
用 Xilinx、Altera 这类 FPGA 的 DDR4 IP 时,训练一般是黑盒。如果 MIG 或者外部 PHY 报错,错误位置常指向 read calibration。日志里如果明确说挂在 MPR read,先不要怀疑 IP 本身,而应该检查以下几项:
| 检查项 | 常见原因 |
|---|---|
| DDR4 参考电压 VREF | VREF 偏高或偏低,数据眼图偏斜 |
| ODT 配置 | RTT 阻值和控制器端端接不匹配 |
| 电源纹波 | VDD、VDDQ 抖动过大,导致 DQS/DQ 相位不稳 |
| 时钟质量 | DDR4 参考时钟抖动超标 |
| 布线等长 | DQ/DQS 组内等长差异过大 |
尤其要注意电源和参考时钟。很多“偶尔能过、偶尔 cal fail”的训练问题,最后都查到是电源纹波或者时钟抖动在临界值上。MPR Read 对这种模拟量特别敏感,因为训练时看的是眼图中心,电源一抖,眼图就缩小,自然就 fail。
4.4 一个小习惯:训练状态机要留 MPR 现场记录
调初始化代码时,我习惯把每次 MPR Read 的期望值和实际值都打出来,并在状态机上保留一个“MPR 失败现场”寄存器。这个寄存器不需要多大,能记下当前 MR3 配置、MPR Page 编号、期望码型前 8 拍、实际码型前 8 拍就够了。
很多工程师只输出一个 pass/fail,出问题后完全不知道是哪一个页面、哪一个 burst 错了。把现场留下来之后,复现问题、分析根因都会快很多。真实项目里,这个习惯至少帮我省下过一天多的调试时间。尤其是板子从原理图改版回来,或者颗粒供应商换批次时,MPR 现场记录能快速告诉你是硬件变了还是配置没跟上。
5. MPR 在上电训练链路里的位置,以及 DDR2/DDR3/DDR4 的差异
5.1 完整训练链路
MPR 并不是唯一一种读训练手段,但它是最接近“颗粒内部已知数据”的一环。完整的 DDR4 训练链路大致是:
- 上电初始化、模式寄存器配置。
- ZQ 校准:把输出驱动和终端电阻校准到设计值。
- 写均衡:把每个 byte lane 的 DQS 与 CK 对齐。
- MPR Read:读回颗粒内置已知码型,初调读 DQS/DQ。
- Read Deskew:针对 DQ 组内逐 bit 相位做精细调整。
- Write Training:往已知地址写数据,再读回来验证写通路。
- Command/Address Training:对高频率 DDR4 很关键,确保指令采样正确。
MPR 通常在 read deskew 之前做,目的是快速找到一个“能读对数据”的粗窗口,然后才在这个窗口内做更细的 read deskew。如果一上来就直接用真实数据做 read deskew,你连读数据和期望都是错的,根本没法判断 delay 该加还是该减。
5.2 从 DDR2 到 DDR4,训练为什么越来越重要
拿 DDR2、DDR3、DDR4 放在一起看,能更明显感受到训练的重要性变化。
| 项目 | DDR2 | DDR3 | DDR4 |
|---|---|---|---|
| 预取宽度 | 4n | 8n | 8n |
| 工作电压 | 1.8V | 1.5V/1.35V | 1.2V |
| 读 DQS | 差分 DQS 已有 | 差分 DQS | 差分 DQS,preamble 可配置 |
| 训练机制 | OCD 等有限校准 | 写均衡、MPR、读/写训练 | 写均衡、MPR、CA 训练、DBI/CRC 验证更复杂 |
| Bank 组织 | 4/8 bank | 8 bank | 4 bank group,内部 bank group 并发 |
DDR2 时代接口速率没那么高,读数据窗还比较宽,控制器稍微保守一点也能工作。DDR3 开始,速率上来以后,不能再用“固定延迟”来读数据,写均衡和读训练就成了标配。到了 DDR4,工作电压降到 1.2V,信号裕量更小,速率冲到 2400MT/s 以上,训练步骤必须非常精细。MPR 的作用就是给高速读训练提供一个“绝对参考点”。如果没有它,控制器很难在大频率范围内自动适配不同颗粒和不同 PCB。
5.3 原理图与布线的锅,MPR 可不会背
很多初学者看到一个现象:FPGA DDR4 cal fail,第一反应是去改训练寄存器参数。实际上,MPR 训练通过的第一前提是原理图设计和 PCB 布线没大问题。DDR4 原理图里,DQS 差分对、DQ 组、地址命令线、VREF、VTT、去耦电容都有讲究。布线规则里最重要的是组内等长、组间等长、参考平面连续、避免跨越分割区域。
如果板子上 DQ 组内部等长没做到位,单靠 MPR 读训练是补不回来的。训练能做的,只是把 DQS 的采样点移到眼图中心;如果布线把 DQ3 的数据眼图压得根本不存在,训练逻辑再聪明也会 fail。所以遇到 MPR 阶段 cal fail,不要先甩锅给 IP,先拿示波器看 DQS/DQ 的眼图、测量 DQ 与 DQS 的 skew,绝大多数时候答案已经在波形里。
6. 最后想分享的一点训练经验
调了几年 DDR4 之后,我现在的习惯是把 MPR Read 当做一个“仪表盘检查”,而不是一个“功能开关”。每次新板卡回来,第一件事不是急着跑大数据量读写测试,而是先把初始化训练里的 MPR 结果抓出来看,记录每一根 DQ 的 pass/fail。这一小段静态检查,往往比任何压力测试都能更快暴露原理图和布线问题。
另外还有一个小技巧:MPR Read 训练时,尽量把颗粒的 DBI、CRC 这类高级功能先关掉。等 MPR 基本稳定,再逐个把 DBI、CRC、ECC 打开做验证。这样出了问题可以一层一层定位,而不是所有逻辑全打开之后,错得让人无从下手。DDR4 的初始化训练看着复杂,但真正理解了 MPR 这个“已知数据锚点”之后,整条训练链路会清楚很多。