ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

DRAM自测试模块实战:从March算法到RTL设计与调试

DRAM自测试模块实战:从March算法到RTL设计与调试 搞硬件的朋友应该都有过这种经历板子回板那天电源调好了时钟lock了CPLD的灯也闪了结果CPU起来没多久就死机或者启动到一半卡住。最后查了一圈要么是内存颗粒虚焊要么是颗粒本身有坏行要么是时序裕量不足导致偶发读错。这个时候如果系统里提前放了一个DRAM自测试模块能在一开机就把颗粒的行列功能、保持特性、地址译码全部撸一遍你会省下好几个通宵。这篇文章就围绕DRAM自测试模块这件事从为什么要做、测试算法怎么选、RTL怎么实现到系统里怎么集、仿真怎么验、上板踩了哪些坑完整走一遍。适合正在做SoC集成、嵌入式硬件、FPGA原型验证或者被“内存不稳定”折磨的朋友参考。有微控制器开发经验的也能看明白我会尽量把背景和原理说透。1. 必读背景DRAM为何需要自测试它和DDR、PSRAM到底什么关系1.1 一次让人印象深刻的假故障引发的思考之前帮朋友查一个工控设备的偶发启动失败故障率不算高大概十几台中有一台掉电静置几个小时后再上电有一定概率进不去系统。测量电源纹波、排查时钟晶振、换过主控芯片都没用。后来在固件里临时加了一段内存自检跑完直接报出一个行保持故障——这个颗粒放在那里几小时数据就丢了。这个案子之后我养成了一个习惯凡是新板卡回板第一件事不是上Linux而是先让DRAM自测试模块长跑一个晚上。这不是小题大做。DRAM靠电容存储数据电荷会漏靠定期刷新维持同时行列选择和地址译码又高度依赖物理走线和颗粒内部逻辑任何一环出问题表现都可能非常诡异。更麻烦的是DRAM故障往往是间歇性的可能只在温度高、频率快、数据模式特定时才暴露。没有自测试模块这类问题定位基本靠猜。所以DRAM自测试模块本质上就是在芯片或系统里内置一个“体检医生”按预定算法对存储阵列做写入、读取、比对、上报。它不依赖外部测试设备也不需要CPU参与细节控制复位之后可以自主运行理想情况下在CPU正式访问内存之前就把坏块和故障点圈出来。1.2 别把DRAM、DDR、PSRAM混为一谈搜资料时经常看到有人把DRAM、DDR、PSRAM这几个词放在一起问确实容易混淆但搞自测试之前必须分清楚因为不同存储类型的测试策略和接口时序差别很大。DRAM是动态随机存取存储器的统称存储单元由一个晶体管加一个电容组成靠电容电荷表示0和1必须周期性刷新否则数据会丢。DDR是DRAM的一种同步接口标准全称Double Data Rate即双沿采样。我们常说的DDR4、DDR5本质还是DRAM颗粒但在接口协议、预取宽度、时钟频率、模式寄存器配置上有明确的代际划分访问时必须通过DDR控制器完成初始化、训练、刷新调度。PSRAM通常指伪静态随机存取存储器存储单元实际是DRAM结构内部有自动刷新机制但外部接口做成SRAM的样貌不需要用户手动刷新时序简单很多。很多微控制器外扩内存、低端显示系统用的就是PSRAM它故障模型和DRAM类似但因为它内部帮你处理了刷新所以自测试时没法直接测试“暂停刷新后的数据保持能力”这一点需要注意。对自测试模块设计的影响在于DDR内存需要等控制器初始化完成后才能发起访问自测试实际上是在控制器后面“走标准读写字”的过程PSRAM接口简单自测试状态机可以直接面向总线而纯DRAM裸片场景下自测试模块往往要具备初始化刷新电路的能力。设计前先确认你面对的是哪一种“DRAM”整个模块的接口风格都会不同。2. 自测试算法怎么选从故障模型到March家族的取舍2.1 自测试到底想测出哪些问题写测试向量之前先列出DRAM的典型故障模型。自测试不是随便写0、读0这么简单而是要有针对性地触发每一类物理缺陷。固定故障SAF某个存储单元或某根数据线卡死在0或卡死在1转换故障TF单元能存0也能存1但0跳1或1跳0时太慢导致某次转换失败耦合故障CF一个单元在翻转时影响相邻单元常见于位线或字线靠得近的情况地址译码故障AF地址线和译码器存在开路、短路导致访问地址A实际选中的却是一个无关的单元数据保持故障电容漏电严重刷新间隔内数据就丢失了。不同的故障模型对应不同的测试算法。全覆盖意味着测试时间、硬件代价都上去实际工程中要根据内存容量、应用可靠性等级做平衡。2.2 March C-、March C、March 13N怎么选March类算法是DRAM自测试绝对的主流。它的基本思路是把整个内存地址空间从头到尾或从尾到头遍历每个地址执行一组固定的“操作序列”。一个操作序列里可以包含读、写、等待、比较等动作用上箭头、下箭头表示地址递增或递减。常见的March元素写法长这样⇑(w0) 从起始地址到结束地址依次写0 ⇑(r0,w1) 从起始地址到结束地址对每个地址先读0再写1 ⇓(r1,w0) 从结束地址到起始地址对每个地址先读1再写0最广为使用的是March C-操作数10NN表示地址总数。它覆盖固定故障、大部分转换故障、地址译码故障以及大部分单单元耦合故障算法时间和硬件开销都不大是中低端SoC自检、CPU上电内存检测用得最多的序列。如果需要更高覆盖率可以选March C一般实现为14N在March C-基础上增加额外读写能把一些邻位耦合故障覆盖得更干净。再往上还有March SS约22N针对双单元耦合故障和更复杂的单元间交互设计。存储容量越大算法的额外操作数乘上总地址数测试时间差距会被放大所以不能盲目追求高复杂度。下面这个表格是我在不同项目里用过的算法选型参考算法操作数故障覆盖特点工程适用场景March C-10NSAF、TF、大部分AF、单单元CF通用内存自检FPGA/SoC上电测试March C14N在C-基础上增强对部分耦合故障覆盖对可靠性要求较高的工业/车载场景March 13N13N覆盖率与开销折中版本测试时间敏感但不想牺牲覆盖率的场景March SS约22N覆盖双单元耦合故障内存介质工艺分析、极端可靠性验证棋盘格Checkerboard4N相邻位桥接、位线短路和March类算法配合使用实际设计中我通常把算法参数做成可配置比如内部寄存器选择March C-还是March C这样量产测试、老化筛选、现场诊断能用同一套模块跑不同深度。2.3 不能忽略的辅助测试棋盘格、地址走线、数据保持单纯跑March类算法还不够有几种辅助模式算是自测试模块的“补刀”。棋盘格测试很直观向内存写入0xAA和0x55交替的图案过一段时间后再取反写入并读回专门针对相邻位的桥接短路。DRAM颗粒内部位线间距只有几十纳米制造工艺里的微小金属残留或介质缺陷很容易在这种图案下暴露。地址走线测试也值得做。方法是选择2的幂次方地址写入与其索引相关的数据例如A[5]地址写0x20A[6]地址写0x40然后顺序读回。如果一根地址线虚焊访问地址会“折叠”到奇偶相邻地址上读回数据就会出现成片的重影。这个测试非常快适合快速定位。数据保持测试最容易被忽略它也最能暴露DRAM的物理漏电问题。做法是在一段地址范围内写入已知图案然后停掉对这部分地址的刷新等上几百毫秒甚至几秒再读回。如果数据变了说明有单元保持时间不达标。要注意保持测试一定要有定时控制测试期间刷新暂停时间不能无限拉长否则大量单元同时掉电你会拿到一张“全盘报废”的读回结果根本分不清故障点。实际项目中保留等待时间做成寄存器可配从50us到数秒甚至更长。3. RTL怎么实现自测试模块的架构与关键细节3.1 模块整体架构先画一下自测试模块的逻辑层次。一个最小可用的DRAM自测试模块通常包含四块控制与调度逻辑也就是中心状态机负责算法选择、启动停止、错误汇总地址发生器产生当前要访问的地址以及取反方向数据模式发生器根据算法和地址生成期望写入数据、预期读回数据数据比较与故障记录逻辑对读回数据和期望值逐位比较将错误地址和数据捕获到寄存器或FIFO里。对外接口方面如果集成到带DDR控制器的SoC里自测试模块不是直接连到DDR物理接口的而是通过一个内部端口接到内存控制器上。具体来说控制器通常提供用户侧接口类似AXI或Native端口BIST模块作为普通Master发起读写事务。这样做的好处是不用关心DDR PHY的训练、ZQ校准、刷新调度复用控制器现有能力。缺点是BIST无法精确控制颗粒物理时序所以BIST验证的是“从用户侧看进去的存储器功能”这在实际工程里通常已经足够。如果把自测试模块放到一个简单PSRAM或者SRAM接口场景BIST可以直接面向总线状态机反而更简单逻辑规模还能再小一圈。3.2 状态机设计思路与Verilog骨架核心状态机至少要有两层结构外层是March序列遍历即依次执行第0个到第5个March元素以March C-为例内层是对每一个地址执行当前March元素定义的操作。内层操作完成后地址递增或递减直到边界翻转外层状态。我自己写RTL时习惯把外层阶段编码成有限状态比如localparam [3:0] PHASE_WRITE0 4d0; // 全地址写0 localparam [3:0] PHASE_RD0_WR1 4d1; // 升序读0写1 localparam [3:0] PHASE_RD1_WR0 4d2; // 升序读1写0 localparam [3:0] PHASE_RD0_WR1_DN 4d3; // 降序读0写1 localparam [3:0] PHASE_RD1_WR0_DN 4d4; // 降序读1写0 localparam [3:0] PHASE_READ0_DN 4d5; // 降序读0 localparam [3:0] PHASE_DONE 4d6;核心的控制伪代码风格如下实际RTL里要处理valid/ready握手always_ff (posedge clk or negedge rst_n) begin if (!rst_n) begin phase PHASE_WRITE0; addr 0; fail 1b0; done 1b0; end else if (start !done) begin case (phase) PHASE_WRITE0: begin // 发起写请求地址递增写完最后一个地址后切到 PHASE_RD0_WR1 end PHASE_RD0_WR1: begin // 先发起读请求读回数据与预期0比较随后写1地址递增 end // ... 其他阶段类似 PHASE_READ0_DN: begin // 降序读0比较失败则 fail 1b1; 完成全部地址后 done 1b1 end endcase end end注意一点March元素的“读后写”在内存控制器接口上至少是两个事务要么等待读返回后再发写要么用控制器支持的posted写提前发出。虽然不影响最终结果正确性但会影响测试速度和方便性。工程上我更倾向于“读完成后再写”的保守做法这样比较逻辑和写数据生成可以串行复用逻辑更少。3.3 地址生成、数据比较与故障记录地址生成的关键是支持递增和递减两种方向。DDR映射到物理颗粒时地址顺序不是简单连续的还有bank、row、column的重叠排列。如果按照DDR地址矩阵的访问规律连续线性递增地址会先穿过同一行的所有column再切换row和bank。March算法本身不强制物理行切换策略但为了更优地覆盖DRAM的行激活/预充电路径有的设计会专门把低地址位映射为row高地址位映射为column让测试过程频繁换行、充电。这个做法在颗粒老化测试中更有价值代价是测试时间明显变长需要权衡。数据模式生成方面我通常会保留几组固定图案全0、全1、0xAA、0x55、地址翻转把地址位映射到数据位以及伪随机序列使用简单LFSR。数据宽度的处理要小心DDR控制器数据总线可能是32位、64位或128位March算法比较时按整个数据总线位宽并行比较任何一位不匹配都算该地址失败。一旦发生比较失败不要马上终止整个测试而是把错误地址、期望数据、实际数据锁存到故障寄存器同时置起错误标志继续跑完当前阶段这样能一次性收集多个故障点。故障记录FIFO的深度不用太大实际调试中先看前几个故障地址就够了但一定要保证错误地址与错误数据绑定存放否则上板用ILA抓波形时你会浪费大量时间在关联信号上。3.4 时钟域处理与刷新感知自测试模块如果工作在内存控制器的用户时钟域通常与系统总线上某个同步时钟同源相对简单。但如果DDR控制器和BIST模块工作在异步时钟域必须对启动、完成、失败这些控制信号做同步处理。比较典型的是系统控制寄存器在慢速APB总线上配置BIST的done/fail信号要跨时钟域到APB域供软件读取这时需要两级触发器同步必要时再加脉冲展宽防止慢时钟域漏采。刷新感知是DDR自测试与普通SRAM测试最大的区别。DDR控制器会定期发起刷新操作自测试发起的读写请求和刷新请求同时在控制器里排队。正常情况下仲裁器会处理好但如果BIST发送请求的频率太高可能影响刷新响应时间极端情况下导致个别行刷新不及时数据丢掉BIST就误报故障。处理办法是在BIST与控制器之间做一个简单的带宽限制比如连续访问若干笔后插入等待周期让刷新请求优先通过。对于数据保持测试需要能够暂停刷新实现时最好通过控制器提供的刷新控制寄存器来操作而不是在BIST内部暴力拉掉刷新使能避免造成控制器状态混乱。4. 系统集成与验证从仿真到上板的过程4.1 把自测试模块放进系统的三种典型方式从集成策略上看常见的有三种方式。第一是上电自检模式。系统复位释放后BootROM或硬件状态机先启动BIST测试通过后才释放CPU的取指访问。这种方案能保证CPU运行环境是可靠的但会延长启动时间。对启动时间敏感的消费类设备要仔细评估测试时长工业控制、医疗设备和车载控制器则往往宁可多等几百毫秒也要保证内存可靠。第二是软件触发模式。Bootloader或操作系统驱动在合适时机向BIST控制寄存器写启动命令软件可以决定跑完整算法还是快速抽测测试结果放到状态寄存器软件通过查询或中断获取。这种方案最灵活也最容易实现增量开发绝大部分SoC都推荐先做这种方式。第三是后台周期自检模式。在DDR控制器内部增加仲裁机制利用系统空闲带宽周期性地对未使用或可回收的内存区域执行March测试。不阻塞业务但实现复杂度最高涉及脏数据保护、内存热插拔、区域分配策略一般用在网络设备、服务器平台这类对长期可靠性有极高要求的场景。三种方式不冲突通常的做法是先上软件触发稳定后再考虑上电自检。后台周期自检则看产品定位决定要不要做。4.2 仿真平台与故障注入才能验证BIST本身自测试模块也是逻辑也会写错。最简单的仿真验证是搭一个行为级DRAM模型模型内部是一个二维数组并支持通过外部参数注入故障。故障注入法要覆盖几类单bit翻转在某个地址的某一位上强制写反验证BIST能报出该地址固定地址段故障模拟一片坏块验证测试完成后fail信号能保持并记录首地址数据线粘连比如模拟数据总线上的两个bit短路写入0xAA读回0x00验证比较逻辑能逐位识别地址线开路让地址A实际映射到另一个地址验证March序列能通过数据重影检测出来。更重要的是验证BIST的完成信号不会因为故障而丢失故障注入后模块仍然能跑完所有阶段并停在预期的DONE状态而不是死锁在某个等待上。仿真时最好加一个协议检查器监控BIST与内存控制器接口的握手信号防止出现地址越界、读写长度错误、比较窗口错位等问题。我自己踩过的坑是DDR控制器数据总线上存在读延迟BIST发出读请求后要等若干个周期才拿到数据比较状态机如果没做等待对齐会把上一个地址的数据当作当前地址的数据来比结果故障地址全是错的。4.3 上板调试ILA抓信号和长跑测试RTL仿真通过后上板阶段第一件事是用FPGA/片上逻辑分析仪抓BIST接口的波形确认状态机确实按预期在跑尤其是首地址和末地址的边界转换。DDR场景下重点观察控制器初始化完成信号是否已经有效如果BIST在训练完成之前发起了访问行为不可预期。边界测试花不了多少时间之后务必做一次专门的“长跑模式”让BIST循环执行完整测试不断复位重来连续运行数小时。长跑的目的有两个一是验证BIST自身没有偶发死锁或误报二是让内存颗粒在持续读写下升温暴露热相关时序故障。我曾经有一块板子单次测试全绿但循环跑到第37轮突然出一帧读回错误后来定位到DDR控制器参考电压漂移问题这在单次测试中根本测不出来。上板阶段还有个小技巧把BIST的地址生成模式改成伪随机LFSR模拟更接近实际应用的访问分布。相比线性递增伪随机方式更容易暴露某些特定地址组合下的颗粒内部干扰问题。当然伪随机模式不能当作March算法的替代只算附加压力模式。5. 工程实战中的坑与解法5.1 测试全绿系统却仍然会死机遇到过不止一次BIST报告全部通过可系统跑到高负载场景照样偶尔卡死。这种情况通常意味着BIST验证的“功能正确”和系统需要的“时序裕量”是两个维度。March类算法对地址全集做读写但访问节奏是规律的不会像真实负载那样产生大量行切换、读写交替、跨bank访问。此时要让BIST以更高频的伪随机模式、更宽数据翻转率、更长保留时间再去跑一遍同时重点检查DDR训练参数里的ODT阻抗、驱动强度、参考电压设置是否还有裕量。还有可能本来就是特定数据敏感故障比如某些物理单元在“上一次写入1后紧接着读0”的极短时间窗口内出错March算法里虽然有转换检测但操作顺序未必命中这个窗口。这种深层问题一般要靠专门的压力测试软件跑更长时间才能暴露。5.2 高温老化时测试失败率上升高温环境下DRAM保持时间变短是正常物理现象温度每升高10摄氏度漏电可能翻一倍。如果产品要在60度以上环境工作老化测试时就要把数据保持测试的等待时间适当加长用比常温更严格的窗口筛选颗粒。如果高温测试时BIST已经在跑常规March就出现失败问题往往不是存储单元本身而是电源或信号完整性。可以先用示波器测DDR供电电压在并发读写时的跌落幅度再检查VREF电平是否在规格范围内。有时候BIST跑得越快电源跌落越明显故障越容易被触发这不是存储器坏了而是板上DDR电源设计余量不够。5.3 固定地址区间反复报错故障集中在某个固定地址段大概率是物理问题比如颗粒的行列译码器局部缺陷、对应数据线的焊盘虚焊、bank调度逻辑异常、或者PCB走线在某段区域受到串扰。第一步应该用专门的地址线测试和棋盘格测试把故障范围缩小看报错的地址是否都落在同一个bank、同一个row或同一根数据线上。如果报错地址和数据位有固定的对应关系比如所有错误都发生在数据总线D[7]上那么优先怀疑D[7]的PCB走线或颗粒DQ7引脚焊接。如果错误地址跳跃性很大但始终在某一段区域则优先查行列译码和bank选择逻辑。此时可以拿热风枪局部加热排查焊接问题或者用压测探棒触碰可疑引脚观察BIST结果是否变化这个方法虽然土但效率很高。5.4 常见问题速查表现象可能原因建议处理BIST全绿系统仍不稳定测试模式过于规律或时序裕量不足增加伪随机压力模式、检查ODT/驱动强度、延长数据保持测试高温测试失败率上升保持时间变短或电源/SI余量不足加严保留等待时间、测量DDR电源跌落、检查VREF固定地址段报错颗粒局部缺陷、焊接虚焊、地址映射异常跑地址线测试和棋盘格、锁定bank/row/column范围、检查PCB走线错误集中在固定数据位数据线短路/开路/虚焊检查DQ位走线与引脚焊接用故障数据位反推颗粒球位BIST死锁或卡住控制器初始化未完成、比较等待逻辑BUG加仿真协议检查器、确认训练完成信号、增加看门狗复位测试耗时太长算法复杂度过高或保留等待过长分段测试、调整算法等级、测试时提高DDR时钟频率最后分享一个很实用的习惯。无论项目大小我都建议在系统固件里留一个永久可用的内存自测试例程哪怕只是通过寄存器触发、跑一轮快速March C-也一定要留。这就像给设备装了一个随时能查的体检入口现场出了疑难杂症第一件事先跑一遍自测试能省掉大量拿示波器追波形的过程。我自己的板子上电流程里已经固定加入了快速自检新板回板第一天就先让BIST以伪随机模式长跑一晚上跑过了再聊操作系统的事。这套流程帮我挡住过不少批次性的坏颗粒问题也算花小钱办大事的典型例子。
返回列表