简介面向嵌入式硬件与存储接口开发者的 DDR2 SDRAM 官方手册聚焦 Micron MT47H64M16HR 及同系列 1Gb x4/x8/x16 器件的选型、设计与调试尤其适合 TI DSP TMS320C6455 开发测试板的硬件工程师查阅。压缩包仅含 1 个 PDF 文件大小约 8.93MB内容为完整英文数据手册便于离线检索电气参数、时序表与封装/订购信息。已有 324 人学习下载。手册系统给出 1.8V 电源与 SSTL_18 兼容 I/O、4n 位预取、8 个内部 bank、可编程 CAS 延迟与附加写入延迟、4/8 突发长度、ODT、64ms/8192 周期刷新及工业级温度选项并列出 DDR2-1066/800/667 等速度等级和 FBGA 封装配置可帮助读者完成原理图设计、时序核对、信号完整性与兼容性评估。1. 上电不亮DDR2 的坑往往藏在官方手册的脚注里同一块板子焊一批颗粒能跑换一批就随机挂——很多人第一反应是焊接虚了、布线没控阻抗。把一份 1Gb DDR2 SDRAM 官方手册从头翻到尾才会发现真正的雷区是两处上电那几十条命令的先后顺序以及一张看着像凑数的时序参数表。DDR2 这一代把「自适应」几乎全砍了CAS 延迟、附加延迟、驱动强度、端接阻抗全靠控制器在上电瞬间用 MRS 命令写死。写错一个位或者早发了一个周期颗粒不会报错、不会复位只会安静地吐错数据。读懂这份手册本质上是拿到一份「控制器和内存之间到底约定了什么」的契约。2. 4n 预取与 8 Bank接口凭什么比内核快2.1 预取把 4n 位宽的一次访问摊成四个节拍DRAM 内核的访问延迟是几十纳秒量级存储阵列本身慢得很可外部接口要跑到 800MT/s 甚至 1066MT/s。这两件事对不上DDR2 的解法是 4n 位预取4n-bit prefetch内核一次从阵列里搬运 4n 位数据到输出寄存器再由接口以 4 倍的速度分批送出去。对 64M×16 这颗器件来说n16内核一次取 64 位接口每个时钟沿送 16 位一个时钟周期两个沿共 32 位正好两个时钟送出 64 位——这就是突发长度 4 对应的 2 个 CK 周期。x8 器件同理内核取 32 位接口每周期 16 位两个周期送完。预取架构决定了几个看起来很怪、但必须遵守的规则。第一突发长度只能是 4 或 8因为内核搬运的粒度就是 4n 位你没法只要一半。第二列命令间隔 tCCD 直接等于 BL/2 个时钟BL4 时下一条 READ/WRITE 要等 2 个 CKBL8 时等 4 个 CK这不是控制器想不想的问题是输出寄存器腾不出位置。第三突发一旦开始就不能中途叫停只能靠 DQM 掩码在输出侧做屏蔽——DQM 掩掉的是已经取出来的数据不是让内核别取。提示想算一颗颗粒的峰值带宽别用内核频率乘位宽。用数据速率乘接口位宽1066MT/s × 2 字节 2133MB/s这是引脚上的上限实际能拿到多少全看 bank 命中率和刷新开销。2.2 8 个 bank并行不是免费的被 tRC 和 tRRD 卡着手册里反复强调的 8 个内部 bank是 DDR2 相对早期 SDRAM 最大的结构性改进。每个 bank 有独立的行缓冲row buffer命中同一行时是列访问只需要 CL跨行就要先 PRECHARGE 再 ACTIVATE付出 tRC 量级的代价。8 个 bank 允许你在一个 bank 预充电的同时去另一个 bank 激活新行把这段空窗填掉。但并行有预算。同一个 bank 内两次激活的最小间隔是 tRC手册表里给出的 5455ns 放在 1066MT/s、tCK1.875ns 下就是近 29 个时钟——这 29 个节拍里那颗 bank 是完全不可用的。不同 bank 之间的激活间隔是 tRRD比 tRC 小得多但仍然存在。所以真实的访存模式决定了性能顺序扫描一大块连续地址时行命中率高跑得飞快地址跳跃、多个主设备乱序访问时bank 冲突会把你打回原形。我一般这么理解这件事8 个 bank 不是「8 倍带宽」而是「8 份可以错峰的时间片」。控制器调度器的全部工作就是在这 8 份时间片里挑一个已经打开行、且数据已经被上层要走的组合把命令塞进去。2.3 地址位怎么映射成行、列、bank算容量和算时序是同一件事手册的寻址表是整份文档里最该先看的一页因为容量、刷新次数、地址线复用全在这里对齐参数256M×4128M×864M×16组织方式32M×4×8 banks16M×8×8 banks8M×16×8 banks行地址A[13:0]16KA[13:0]16KA[12:0]8KBank 地址BA[2:0]8BA[2:0]8BA[2:0]8列地址A[11,9:0]2KA[9:0]1KA[9:0]1K刷新次数8K8K8K拿 x16 那一列验算8K 行 × 1K 列 × 16 位 × 8 bank 8192×1024×2×8 128MB恰好是 1Gb。x4 那颗列地址多一位、行地址多一位、位宽少一半乘出来还是 1Gb。三颗芯片总容量一样地址位分配完全不同这也是为什么换料时寄存器配置不能照抄。x4 那颗的列地址是 A[11, 9:0]中间跳过了 A[10]。这不是笔误而是地址线复用A10 在 DDR2 里被拿来当 APAuto Precharge标志位用列地址里就把它挖掉了。写控制器的时候如果按连续位域去解析列地址x4 器件会在某些地址上莫名其妙读到隔壁列的数据——这类 bug 极难定位因为只在特定地址段出现。下面这段代码把地址拆解显式写出来比让硬件自己去连线的可读性好得多/* 根据器件组织方式拆分物理地址参数取自手册寻址表 */ typedef enum { ORG_X4, ORG_X8, ORG_X16 } ddr2_org_t; static void split_addr(ddr2_org_t org, uint32_t addr, uint32_t *bank, uint32_t *row, uint32_t *col) { switch (org) { case ORG_X16: /* 8K row x 1K col x 8 bank */ *col addr 0x3FF; /* A[9:0]10 位 */ *row (addr 10) 0x1FFF; /* A[12:0]13 位 */ *bank (addr 23) 0x7; /* BA[2:0] */ break; case ORG_X8: /* 16K row x 1K col x 8 bank */ *col addr 0x3FF; *row (addr 10) 0x3FFF; /* A[13:0]14 位 */ *bank (addr 24) 0x7; break; case ORG_X4: /* 16K row x 2K col x 8 bank */ *col addr 0x7FF; /* A[11,9:0] 共 11 位A10 是空位 */ *row (addr 11) 0x3FFF; *bank (addr 25) 0x7; break; } }三个分支的移位量分别对应 10、11、10 位列地址加 13、14、13 位行地址。注意 x4 分支里列地址按 11 位连续处理是因为这里做的是「逻辑地址到 bank/row/col 编号」的映射A10 的物理空位在引脚层处理不在编号层处理。真正上板时如果控制器支持地址镜像或多颗级联别忘了每颗颗粒的 bank 交错粒度要按这颗器件实际的行列大小来切否则跨颗访问会退化成连续 bank 冲突。3. DLL 与 DQS数据眼图是被什么撑开的3.1 DLL为什么 CK 到 DQ 的延迟必须闭环修正DDR2 内部有一个延迟锁定环DLL它的任务只有一个让 DQ 和 DQS 的跳变沿从芯片外部看过去与 CK 的跳变沿对齐。这件事听起来理所当然做起来极其麻烦——从 CK 输入到数据输出中间要经过输入缓冲、命令译码、阵列访问、输出驱动每一段的延迟都随电压、温度、工艺漂移。手册里写的 tACDQ 相对 CK 的输出有效窗口能收敛到几百皮秒量级全靠 DLL 把这个延迟实时补偿掉。DLL 带来的第一个后果是「上电后有一段死区」DLL 复位后需要时间锁定这段时间里读出来的数据不可信。手册里把这段时间写成了明确的时钟数要求通常紧跟在带 DLL Reset 位的 MRS 命令之后。第二个后果是「低功耗模式不是随便进的」DLL 关掉容易再锁回来麻烦所以自刷新Self Refresh和掉电模式对 DLL 的处理有专门规定进之前要把 DLL 状态记清楚。血泪经验是很多人在初始化流程里把 DLL Reset 位当成「可选项」跳过觉得反正也能读。能读是因为运气好温度和电压刚好让延迟落在窗口里换一批批次或者跑上十分钟发热之后就开始出现偶发的单比特错误。3.2 DQS/DQS# 与 RDQSx8 的引脚复用取舍DDR2 的数据选通是差分对 DQS/DQS#读写共用。写的时候控制器发 DQS 给颗粒采样 DQ读的时候颗粒发 DQS 给控制器采样 DQ。差分的好处是共模噪声被抵消同时交叉点位置稳定接收端可以直接拿交叉点当作采样时基不用另外做时钟恢复。x8 器件还有一个额外选项 RDQS用一根额外的输出选通专门给读方向用替代 DQS 在读取时承担的角色。为什么只有 x8 需要因为 x8 的引脚预算紧很多时候一颗颗粒只对应 8 位数据宽度控制器侧的 DQS 数量吃紧RDQS 让读方向可以走一根独立线缓解 x8 多颗粒并联时的扇出问题。手册里 EMR2 寄存器负责打开或关闭这些选项。注意这类「引脚功能重定义」的位必须在上电初始化阶段就定好运行中改会导致引脚驱动方向混乱——DQS 在写的时候是输入、在读的时候是输出方向切换由命令类型隐式驱动控制器如果把 RDQS 使能了却没在读取时准备好接收会看到一整段悬空电平。3.3 三个窗口参数tDQSCK、tDQSQ、tQH读时序能不能做对取决于三个带 DQS 的参数tDQSCKDQS 跳变沿相对 CK 跳变沿的偏移手册给的是正负范围比如 ±0.35ns 这个量级。它告诉你颗粒发出的选通不保证和 CK 对齐所以控制器不能拿 CK 去采样读数据必须用 DQS 自己。tDQSQ同一组数据里DQS 到各根 DQ 之间的最大偏差。它决定了采样窗口的左侧边界。tQH数据保持时间决定窗口的右侧边界。这三个数一摆出来控制器 PHY 的设计目标就清楚了用 DQS 做采样时基在 DQS 交叉点附近取一个相位让所有 DQ 都落在窗口内。因为 tDQSCK 会随 PVT 漂移固定相位靠不住所以实际器件里通常做 DQS 门控DQS gating加相位扫描在初始化时训练一次。3.4 ODT 与驱动强度反射在芯片内部被吃掉频率上到 800MT/s 以后走线不再是「连上就行」。FPGA 或 DSP 到颗粒的这段 PCB 走线有阻抗不连续信号会反射回来叠加在原始波形上把眼图糊掉。DDR2 的解法是在颗粒内部集成了片上端接ODT通过 EMR 里的位选择是否打开、以及端接阻抗的档位实现方式是在 DQ、DQS 上挂一个可切换的电阻网络。ODT 什么时候打开是有讲究的读的时候颗粒是发送方控制器是接收方此时颗粒自己的 ODT 应该关掉否则会把自己发出的信号吃掉一半写的时候反过来颗粒是接收方ODT 必须打开而且要提前打开。手册里把这个提前量写成 tAONDODT 打开到端接生效关闭也有 tAOFD。这两个参数是「读写切换时数据出错的头号原因」——控制器如果按写命令的时刻才拉 ODT前几个 beat 会落在端接还没生效的窗口里采到的就是脏数据。驱动强度Output Drive Strength是另一个旋钮EMR 里有一个位可以在全强度和降强度之间切换。降强度对点对点短走线友好能减少过冲但多颗颗粒并联、负载重的时候必须用全强度否则上升沿变缓建立时间不够。4. 模式寄存器初始化序列为什么一步都不能换4.1 MRBL、CL、WR 和那个容易被忽略的 DLL Reset 位模式寄存器MR是 DDR2 的配置中心位域大致分成几段位 [2:0] 是突发长度位 [3] 是突发类型顺序或交织位 [6:4] 是 CAS 延迟编码位 [8] 是 DLL 复位位 [11:9] 是写恢复时间Write Recovery单位是时钟数。CAS 延迟的编码不是「二进制直接填数字」而是有固定对应关系CL3 对应 010CL4 对应 011CL5 对应 100CL6 对应 101CL7 对应 110。如果按直觉把 5 写成 101实际配出来的是 CL6整个读时序会错开一个周期——现象是读回来的数据永远是「上一笔的尾巴加上这一笔的头」看起来像地址错位其实是延迟错。写恢复时间 WR 也类似它决定了 WRITE 命令之后多久才能发 PRECHARGE。填小了最后一个 beat 还没写进阵列就去预充电数据丢填大了浪费总线周期。4.2 EMR/EMR2/EMR3AL、ODT、驱动强度、OCD扩展模式寄存器负责 MR 装不下的东西。EMR 里最关键的两位是附加延迟 AL位 [5:3]和 ODT 使能位 [2]还有驱动强度位 [1]和 DLL 使能位 [0]注意这里 0 才是使能是个反直觉的极性。AL 的作用值得单独说。它允许控制器在 tRCD 还没走完的时候就把 READ/WRITE 命令发出去颗粒内部先收着延迟 AL 个周期再执行。这样一来命令总线的占用被提前控制器的调度灵活性变高但代价是「有效延迟 AL CL」。如果控制器假设的读延迟是 CL实际配了 AL3数据会比预期晚 3 个周期回来读通道的 FIFO 就错位了。手册里那条「WRITE latency READ latency - 1 tCK」也是同一套延迟体系的一部分写没有 DLL 参与输出所以比读少一个时钟。EMR2 主要放 OCD 相关位和 DQS#/RDQS 使能默认值 0x380 表示 OCD 处于默认校准状态。EMR3 是 DDR2 里最「空」的一个寄存器多数位保留写它是为了走完流程。4.3 上电 200us 之后命令队列的固定顺序初始化流程是这份手册里最不能自由发挥的部分。大致顺序是上电后 CKE 保持低电平、时钟已经稳定运行至少 200us然后拉高 CKE发至少 400ns 的 NOP 或 DESELECT接着 PRECHARGE ALL两次 REFRESH最后按 EMR2、EMR3、EMR、MR 的顺序写寄存器。为什么必须是这个顺序PRECHARGE ALL 是为了让所有 bank 的行缓冲处于已知的关闭状态否则上电时阵列里的残留状态不可预期。两次 REFRESH 是因为刷新计数器上电时状态不确定需要走完两轮才能保证所有行都被刷新过。而 MRS 放在最后是因为 MR 里的 DLL Reset 位一旦置 1颗粒会开始重新锁定 DLL此时其他寄存器必须先配好否则 DLL 锁定后用的还是旧配置。/* 初始化序列骨架每一步之间都要满足 tMOD 和 NOP 数量要求 */ static void ddr2_init(volatile uint32_t *ctl) { ctl_set_cke(0); delay_us(200); /* 时钟稳定 CKE 低电平保持 */ ctl_set_cke(1); send_nop(10); /* 至少 400ns 的 NOP */ send_cmd(CMD_PRECHARGE_ALL); send_cmd(CMD_REFRESH); send_cmd(CMD_REFRESH); /* 连续两次清掉不确定的刷新计数 */ send_mrs(EMR2, 0x0380); /* OCD 默认档 DQS# 使能 */ send_mrs(EMR3, 0x0000); send_mrs(EMR, emr_value); /* AL / ODT / 驱动强度在这里定 */ send_mrs(MR, mr_value | (1u 8)); /* 最后写 MR同时触发 DLL 复位 */ wait_tck(200); /* DLL 锁定窗口期间只能发 NOP */ }这段代码里最容易改错的是mr_value的拼装和最后的wait_tck(200)。前者错一位就是延迟或突发长度错后者少等就是读第一批数据时 DLL 还没锁住。send_mrs之后到下一个命令之间还要满足 tMOD很多控制器的命令队列不会自动插这个间隔得手动补。5. 避坑与排查手册写了但没人读的五个细节5.1 ODT 打开得太晚读第一个 beat 就是脏数据现象写操作偶尔丢第一个 beat 的数据跑内存测试时表现为每 N 次写错一个 16 位字位置不固定。原因控制器在发出 WRITE 命令的同一时刻才拉高 ODT 引脚而颗粒内部端接生效需要 tAOND 的延迟这段时间里反射还没被吸收第一个数据 beat 正好落在窗口里。反过来读方向也一样ODT 关得太晚颗粒发出的信号被自己的端接吃掉一部分。解决把 ODT 的拉高时刻提前到 WRITE 命令之前至少 tAOND拉低延后到最后一个 beat 之后 tAOFD。手册里 ODT Timing 那一节有完整的波形定义照着把控制器的 ODT 状态机改成提前一拍的形状即可。5.2 AL 不是「省延迟」是把命令提前现象配了 AL 之后读回来的数据总是错开若干个字看起来像地址算错。原因把 AL 理解成了「减少延迟」。实际上 AL 只是让命令提前进队列数据出来的时刻是 AL CL比不配 AL 时更晚。控制器如果按 CL 去等数据就会在数据还没到的时候采样。解决读通道的期望延迟统一改成 AL CL写通道改成 AL CL - 1 tCK。改完之后再检查一遍 AL 的编码表——EMR 里 AL 用三位编码取值 0 到 6编码和数值是直接对应的这点和 MR 里的 CL 不同很容易混。5.3 tREFI 7.8us 被打断刷新欠账会积累成位翻转现象设备连续跑几个小时之后出现随机单比特错误重启就好跑久了又出现。原因64ms 内要完成 8192 次刷新平均下来每次间隔 7.8125us手册里这个数写作 tREFI。控制器在高负载时如果一直推迟刷新短时间看不出问题但电容里的电荷泄漏是累积的欠账到一定程度就是位翻转。更隐蔽的是「刷新被长突发打断」一个 BL8 的突发加上 tRC可能吃掉好几个微秒。解决把刷新做成硬约束而非软请求命令调度器在 tREFI 到期前必须插入 REFRESH允许的推迟量不能超过手册给的批量刷新上限通常是 8 次集中补刷。另外自刷新退出后要重新走一遍完整的刷新补偿。5.4 DLL 没锁就读那段死区是硬性的现象冷启动第一次读成功热复位后第一次读失败或者初始化后前几毫秒数据全错后面就正常了。原因带 DLL Reset 位的 MRS 发出后DLL 需要固定数量的时钟周期才能锁定。这段时间里颗粒的输出延迟是漂的控制器读到的数据可能整体错位。解决把 DLL 锁定等待做成初始化流程里的显式步骤用时钟计数而不是微秒延时因为不同频率下需要的周期数不同。等待期间只发 NOP不要插 PRECHARGE 或 REFRESH避免打乱内部状态机。5.5 上电斜率与 CKE最容易背锅的「焊接不良」现象同一批板子大部分正常个别板子上电后颗粒完全无响应重新焊一遍有时好了有时没好。原因DDR2 对上电顺序和斜率有要求VDD 与 VDDQ 的爬升时间、CKE 在电源稳定前必须保持低电平这些条件在手册的绝对最大额定值和上电时序图里写得很细。如果电源爬升太慢或者 CKE 上拉电阻让它提前变高颗粒可能进入一个未定义状态。解决先量上电波形确认 CKE 在 VDD 达到阈值前被可靠拉低再检查 VDD 的爬升时间是否落在手册允许的区间。很多时候换颗粒没用改一个上拉电阻的阻值就好了——但没人愿意承认问题出在自己画的电路上。6. 把「契约」当代码看DDR2 为什么把自适应全推给控制器DDR2 这一代的设计哲学很清楚颗粒只负责「按你配的做」不负责「猜你想怎么做」。没有写均衡没有读训练没有 ZQ 校准的自动回路所有补偿都变成了控制器侧的一次性动作和寄存器里的一个位。这个取舍在当年是合理的——1.8V 的 SSTL 接口、400 到 1066MT/s 的速率PVT 漂移还在可以用固定时序余量兜住的范围内把复杂度放在颗粒里会让每颗芯片都变贵。代价则是系统能不能跑稳几乎完全取决于控制器设计者有没有把手册里那些表格当真读。这也是这份手册值得反复翻的原因。它不是一本「怎么用」的说明书而是一份条件清单——每一条时序、每一个寄存器位、每一段初始化顺序背后都对应一个具体的物理过程电荷泄漏、延迟漂移、阻抗失配、状态机上电不确定性。把这些条件当成契约条款去逐条核对比在示波器前猜波形有效得多。我现在拿到任何一颗新颗粒习惯是先把手册的寻址表、MR/EMR 位域表、初始化流程图三页单独打出来对照控制器代码逐位比对然后才上板。这个习惯是被一次「换料后跑了两小时才出错」的现场调试逼出来的——那次的问题最后定位在刷新间隔上手册第 112 页写着而我跳过了那一页。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?