ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

DFT插复位后RTL怎么改?从DRC报错到修复实践

2026/10/1 18:38:20 拓冰建站 浏览量
DFT插复位后RTL怎么改?从DRC报错到修复实践 1. 先聊清楚DFT为什么要跟复位死磕前阵子一个同事跑过来问我DFT插完复位之后工具报了一堆复位不受控的DRC要我改RTL可这RTL到底从哪儿下手我听完就想笑这个问题几乎每过一个项目就要在群里被捞出来问一次。DFTDesign for Test可测试性设计跟复位的恩怨其实是所有做数字芯片的人迟早都要撞上的一面墙。1.1 DFT在芯片里到底管哪几件事先把定位说清楚。DFT不是“加一个模块”这么简单它是一整套让芯片在制造出来后能被快速、高效测试的设计方法学。主干是三件事扫描链Scan Chain、内建自测试BIST、边界扫描Boundary Scan。其中扫描链是绝对的核心思路是把芯片里成千上万个时序单元DFF在测试模式下串成一条或几条大链子测试机往链子头灌激励一段一段移进去然后捕获组合逻辑的响应再把结果移出来比对以此判断这个die有没有制造缺陷。这里有个容易被忽略的前提所有DFF在扫描移位和捕获的时候初始状态必须是已知的、确定的。因为故障模型和覆盖率计算都建立在“我知道你在什么状态”的基础上。如果你告诉我某个寄存器上电后可能是0也可能是1那ATPG工具根本没法算。复位恰恰是给寄存器一个确定初始状态的最直接手段所以在DFT的世界里复位不是一个“选配信号”而是扫描测试的基石。不过现实很骨感。RTL工程师写复位的时候脑子里想的通常是功能上电复位、软复位、看门狗复位、模块级局部复位。很少有人从一开始就考虑“这个复位信号在测试模式下能不能被测试逻辑压制住”。于是等DFT工具一跑DRC报告红成一片才发现当初写得爽现在改得慌。1.2 复位在扫描测试里的特殊位置扫描测试分成两个阶段移位shift和捕获capture。移位时所有扫描单元通过scan_en打开被串成链子测试机的数据从scan_in一路推过去整个过程严格跟着shift clock走。捕获时scan_en关掉功能时钟打一拍组合逻辑的响应被装进寄存器里下一个移位周期再把这些值推出去。这个流程里复位扮演的角色非常特殊。测试开始的时候两条路可以给寄存器灌初值一是靠扫描移位本身把已知数据推进每级寄存器二是靠复位把整片寄存器统一刷到一个确定值。大多数ATPG策略会优先用复位来初始化因为它省时间、覆盖全。但前提是复位信号在测试模式下必须听话——该有效的时候有效该失效的时候彻底失效不能在扫描移位过程中冒个毛刺进来不然整条链上的数据全乱套。所以DFT工具对复位的态度特别直接最好有全局复位方向单一树干净路径上不要有组合逻辑门控更不要又是同步又是异步混着来。你一旦在复位路径上搞花活工具判断不了这个信号的状态就只能报DRC然后把责任推回给RTL。这也是为什么“dft插复位怎么改rtl”会成为热搜词——这根本不是一个奇怪问题而是每个项目都要过的坎。2. 插复位之后RTL为什么经常要回炉很多人以为DFT插复位是工具自动完成的事RTL不用动。这话只对了一半。工具确实能自动插复位但前提是你的RTL写出来的复位结构符合DFT的“审美”。问题是功能设计里的复位写法五花八门十个项目有九个会在DRC里翻车。2.1 工具说的“复位可控可观测”到底是什么DRC报告里最常见的词汇就是“Uncontrolled Reset”和“Unobservable Reset”。翻译成人话就是这个复位信号测试逻辑控制不了它或者它出了问题测试逻辑也观测不到。可控指的是在测试模式下复位信号能够被推到满足ATPG要求的固定电平。比如test_mode为1时所有内部寄存器要么被复位到0要么被稳定地释放总之你是知道它们状态的。可观测指的是如果复位链路本身出了问题比如某个寄存器的复位端悬空了或者被组合逻辑截断了这个故障能在扫描测试里被体现出来。工具期望的是每一个DFF的复位端要么直接接全局复位引脚要么接经过测试逻辑可靠旁路的版本。问题在于功能设计里的复位往往是“嵌套”的。顶层有个全局复位中间某个模块又用组合逻辑生成了局部复位再往下还有复位使能、复位门控。工具沿着这些路径往前追追到某个没有定义的组合节点它不认识于是立刻报“不受控”。你不是非要不让组合逻辑参与复位而是要让工具在test_mode下能看穿这条路径。2.2 哪些复位写法会把DFT工具逼疯根据我见过的项目能稳定触发复位DRC的写法基本逃不出这几类。第一种局部模块只做了一部分寄存器的复位另一部分寄存器全靠上电随机态。这种最头疼因为工具就会报“scan flop has no reset”你连初值都说不清楚。第二种复位信号由内部逻辑产生比如一个计数器超时后拉低复位。功能上完全没问题但在DFT看来这个复位不受任何测试端口控制它什么时候来、什么时候走工具不知道。第三种复位路径上有组合逻辑门控。典型写法是assign rst_n_blk rst_n_raw byte_en[3];功能上你可能是想在某类操作时禁止复位生效但这个写法放到DFT工具眼里就是复位端接了一个组合函数路径不可追踪。第四种异步复位信号跨时钟域没有做同步释放。异步复位的释放沿本身就是亚稳态高发区工具在检查复位时序关系时会直接标红因为它没法保证capture时钟沿和复位释放沿之间的时序关系。第五种RTL里用了“x”赋值给寄存器比如always (posedge clk) if (rst_n) q x;。这在仿真里可能不报错但在DFT看来x是完全不可接受的ATPG没法定义x到底是0还是1。2.3 一个关键判断是RTL的问题还是约束的问题这里我要给所有刚接触DFT的人提个醒看到DRC报复位问题先别急着改代码。先确认一件事情——你有没有在工具里正确声明复位信号和test_mode信号。很多时候RTL本身是干净的只是DFT工具不知道哪个端口是复位哪个是测试模式。你忘了set_dft_signal -type Reset工具默认把复位端当成普通时钟或数据信号看那它自然觉得这个信号不可控。我在DFT Compiler里就踩过这个坑第一次跑dft_drc报了八十几个复位错误我差点把整个RTL重写一遍。后来发现只是约束文件里少了一句复位声明加上之后错误瞬间归零。所以判断顺序应该是先查约束再查RTL。约束文件里把复位、test_mode、scan_en、scan_in/out都声明干净再跑一遍DRC剩下的报错才是RTL真正要还的债。这个顺序能帮你省掉至少一半的无效返工。3. RTL到底怎么改按顺序抄作业现在到了正题真要改RTL的时候按什么顺序改最稳我的经验是四步走一步都不要跳。3.1 第一步把test_mode端口规划清楚改RTL之前先问一个问题你的芯片顶层有没有一个统一的test_mode信号很多项目里只有scan_en没有test_mode这就是最大的坑。test_mode和scan_en是两回事。scan_en只在扫描移位的时候拉高capture时候要拉低。而test_mode是一个在整段测试期间都保持恒定的信号它告诉所有功能逻辑“现在不是正常工作是测试模式”。DFT工具希望所有可配置的逻辑、门控逻辑、复位旁路逻辑都能由test_mode统一切换而不是各自为政。强烈建议在顶层加一个test_mode输入端口或者由测试控制器产生的内部信号并且让它具备绝对的优先级。哪怕内部有安全逻辑带反转test_mode也必须能一票否决。代码层面往往体现为一个高优先级的组合条件wire test_mode_int test_mode_port || test_ctrl_force_test;有了这个统一的信号后面做复位旁路、时钟门控旁路、多路选择切换才有一个共同的锚点。否则每个模块你都要自己猜测试模式的来源后面的RTL修改会一地鸡毛。3.2 第二步加复位同步器并预留测试旁路第二步是处理异步复位的同步释放。这几乎是每个带异步复位的设计都绕不开的问题。经典做法是两级同步器把异步复位释放沿同步到时钟域内module rst_clk_sync ( input wire clk, input wire rst_n, input wire test_mode, output wire rst_n_sync ); reg rst_n_r1, rst_n_r2; wire rst_n_isolated (~test_mode) rst_n; always (posedge clk or negedge rst_n_isolated) begin if (!rst_n_isolated) begin rst_n_r1 1b0; rst_n_r2 1b0; end else begin rst_n_r1 1b1; rst_n_r2 rst_n_r1; end end assign rst_n_sync rst_n_r2; endmodule解释一下我为什么要在输入端加这个(~test_mode) rst_n。这是“测试模式强复位”策略当test_mode拉高时rst_n_isolated被强制为0同步器输出被刷成0整片寄存器全部复位到已知状态。这样做的好处是scan chain一开始就是一个确定的初值ATPG覆盖率计算很省心。也有团队采取相反的“测试旁路”策略test_mode拉高时把功能复位旁路掉让复位完全失效寄存器初值完全靠扫描移位灌进去。代码就是wire rst_n_scan test_mode ? 1b1 : rst_n;两种策略都能用关键是前后端约定一致。强复位策略的好处是初值干净坏处是capture阶段复位有效的话某些ATPG pattern测不到复位端故障旁路策略反过来。我实际项目里更常用强复位因为初值确定对覆盖率提升立竿见影。3.3 第三步处理复位路径上的门控逻辑第三步是清理复位路径上所有“多余”的组合逻辑。前面提到过门控复位的写法现在说怎么改。先说原理。DFT工具扫描每个DFF的reset端时它查找的是这个端口最终能不能跟踪到一个全局信号比如芯片引脚或受控测试点。如果路径中碰到一个组合门工具就要分析这个门的真值表在test_mode下是否可预测。分析器这东西一旦碰到多级嵌套门控推导路径一复杂很容易放弃然后报错。所以改法不是“不要门控复位”——功能上你真的需要门控就保留但必须让test_mode能一键旁路。拿最经典的例子// 修改前复位被模块使能信号门控 assign rst_n_blk rst_n_raw block_enable; // 修改后test_mode下绕过门控直接使用全局复位 assign rst_n_blk (~test_mode) ? (rst_n_raw block_enable) : rst_n_raw;别看只多了一个muxDFT工具沿着复位端追上去最终看到的是一颗由test_mode控制的二选一而test_mode是声明的constant信号它推导得动DRC就放行了。记住这句话DFT不排斥逻辑排斥的是它推导不动的逻辑。3.4 第四步确认扫描单元的初始状态公式最后一步不是写代码而是检查。综合工具会为每一个扫描DFF生成一个初始状态公式通常叫reset value或者initial value。你要在综合/DFT的报告中查到每个寄存器的复位值是不是确定的0或1。这一步最容易出问题的点是RTL里用了非0/1的复位赋值。比如always (posedge clk or negedge rst_n) begin if (!rst_n) q 1bx; else q d; end仿真里这条也许能跑因为很多工具会把x当无关项。但DFT不是仿真x会让前后仿真不一致ATPG覆盖率直接失真。正确做法是明确写0或1哪怕你功能上根本不关心这个初值也要写成1b0给DFT一个确定答案。还有个细节别在always块里对同一个寄存器既做异步复位又做同步置位。比如always (posedge clk or negedge rst_n) begin if (!rst_n) q 1b0; else if (set_enable) q 1b1; else q d; end功能上这叫“异步复位、同步置位”仿真没问题但DFT工具在处理这类多控制端DFF时会非常痛苦复位端和置位端同时存在会让扫描单元选定受限甚至导致某些库单元不可用。能拆就拆拆不了就改成纯同步逻辑。4. 从工具视角走一遍实操流程RTL改完不等于结束你得在DFT工具里真正跑通一遍才能确认改对了。这一节我按Synopsys DFT Compiler的流程讲Tessent的流程大同小异命令名不同但思路一致。4.1 读入RTL与初始约束先跑一遍DRC操作顺序是这样的读设计设约束声明DFT信号跑DRC。第2.3节我说过约束先于RTL排查这里落实到具体命令。先说DRC前的初始设置。用DFT Compiler时至少要声明这几类信号set_scan_style multiplexed_flip_flop set_dft_signal -view existing_port -port clk -type Clock -timing {15 55} set_dft_signal -view existing_port -port rst_n -type Reset -active_state 0 set_dft_signal -view existing_port -port test_mode -type Constant -active_state 1 set_dft_signal -view existing_port -port test_se -type ScanEnable -active_state 1 set_dft_signal -view existing_port -port test_si -type ScanDataIn set_dft_signal -view existing_port -port test_so -type ScanDataOut稍微解释一下为什么test_mode用-type Constant。在DFT工具里Constant意味着整个测试期间这个信号的电平是固定的不会翻转。工具拿到这个信息后就能把test_mode接入的所有mux、与门逻辑提前化简从而推导出复位路径的最终状态。如果你漏了这行声明test_mode就跟普通功能信号一样工具不知道它在测试时是0还是1一切依赖它的旁路逻辑全部推导失败。跑完这些设置执行dft_drc先看总览报告把所有Violation按类型归类。这时候你会看到我后面要讲的复位DRC错误。4.2 在DFT Compiler / Tessent里约束复位复位约束的关键是告诉工具两件事哪个信号是复位、有效电平是什么。同时还要告诉它复位与被测时钟的关系是异步复位还是同步复位。DFT Compiler里的常见写法是# 声明功能复位 set_dft_signal -view existing_port -port rst_n -type Reset -active_state 0 # 声明测试模式下的复位控制由test_mode强制 set_dft_signal -view spec -port test_mode -type Constant -active_state 1Tessent Shell里的风格不一样但逻辑等价# 示意写法具体命令以你手上的Tessent版本为准 add_clocks 0 clk add_reset -reset rst_n -mode async -active_state 0 add_dft_signals -type shift_enable -port test_se add_dft_signals -type test_mode -port test_mode report_resets这里特别提一个容易忽略的点如果你做的是异步复位同步释放工具需要知道复位同步器在哪否则它会把同步器的两级DFF当成普通扫描单元处理复位时序分析就错了。有的工具能自动识别同步器结构识别不了就需要你在约束里指出来或者用set_dft_signal -view spec -port rst_n_sync -type Reset -active_state 0把同步后的复位作为后续逻辑的实际复位源。我建议在设计里给所有同步器的输出统一命名比如*_rst_n_sync这样约束脚本可以用通配符批量处理省得一个个指。4.3 综合与插入扫描后网表里应该看到什么插完扫描链你应该回头检查网表确认以下几件事都落地了。第一扫描链数据路径。打开网表找一个扫描DFF它的D端前面应该接了一个由test_se控制的二选一一路是功能数据一路是scan_in驱动的移位数据。如果没有这个mux说明scan insertion没生效。第二复位旁路逻辑。如果你在RTL里写了~test_mode ? (rst_n_raw block_enable) : rst_n_raw这种结构网表里应该能看到与门和test_mode驱动的mux。找不到的话多半是综合把这样的逻辑优化掉了你得回看综合脚本有没有加dont_touch。第三同步器的输出驱动着一大片寄存器。打开复位树报表从顶层复位源出发看看所有DFF的reset端是不是最终收敛到同一个信号上。如果发现某个寄存器跟大部队不在一条分支上那就是漏改的局部复位。第四扫描单元的复位端连接类型。用报告检查每个扫描单元是否具备可识别复位端以及复位值是不是0或1。有些库里的DFF支持异步复位端有些是同步复位端混合使用会让ATPG的timing收敛很痛苦。理想情况是整个设计统一使用同一种DFF类型。5. 常见的复位DRC报错和现场修复经验DRC的报告通常很长但复位相关的错误翻来覆去就那几类。我整理了一个速查表方便你对症下药。5.1 错误速查表典型报错含义常见原因修复方向Uncontrolled Reset复位不受测试逻辑控制复位由内部逻辑产生、忘记声明test_mode加test_mode旁路或强复位逻辑Asynchronous Reset Violation异步复位时序无法收敛异步复位没有同步释放、跨时钟域乱碰加同步释放器统一到各时钟域Gated Reset复位路径上有组合门控rst_n被与门/或门嵌套用test_mode旁路口门控Scan Cell Without Reset Value扫描寄存器没有确定初值RTL里赋了x、寄存器没接收复位明确写复位值0或1Conflicting Reset/ScanEnable复位与扫描使能同时有效冲突复位释放沿与scan_en切换沿叠在一起约束复位时序错开capture/shift沿这张表看着简单实际排查的时候要注意报错之间的连带关系。往往第一个“Uncontrolled Reset”会导致后面几十个寄存器报“Without Reset Value”因为主复位挂掉了下游所有DFF全部失去初值来源。所以经验是别看到错误就挨个改先解决第一层主复位问题再看报告刷新后剩下还真正报错的。5.2 两个真实场景的修复过程我挑两个我踩过的真实场景把修复过程完整走一遍。场景一内部软复位产生器不受控。当时那个模块不用外部复位而是靠一个计数器超时后内部拉低rst_n_soft。功能设计很合理但DFT完全不买账因为计数器本身也要复位才能工作复位自己管自己形成工具推导不动的环。修复方案分成两步。第一步在计数器前面加一个test_mode强复位保证test_mode1时软复位产生器的输入是确定值。第二步把软复位输出接一个test_mode旁路mux确保test_mode1时软复位信号被强制拉高低有效信号的“释放”状态让下游所有寄存器在测试模式下不受软复位影响。改完后DRC清零扫描链初值稳定ATPG覆盖率的损失小于0.1%。场景二异步复位跨了两个时钟域释放沿产生毛刺。那个设计里A时钟域的复位被直接用来复位B时钟域的寄存器两个时钟不存在父子关系。工具在检查异步复位相对于B时钟的时序时直接报红。修复方案是标准的异步复位同步释放在B时钟域旁边加两级同步器A时钟域的异步复位作为同步器的异步置位输入同步器输出再分发给B时钟域的所有寄存器。注意这里同步器的“异步置位”接收的是原始复位而不是同步后的复位。同时我还加了一句test_mode1时强复位置位端拉低保证扫描初值一致。这个修复做完不仅DRC过了后端的STA也好收敛了许多。6. 说了这么多最后几条压箱底的经验项目做多了以后我开始觉得DFT改复位RTL这事真有几条值得写在便利贴上的原则。第一改RTL之前先把整棵复位树画出来。这个树不只指顶层到模块的连接还包括每个局部复位信号是从哪儿来的、中间被谁门控过、流向几个时钟域、每个寄存器是同步复位还是异步复位。画完这棵树再动手改起来就不是东一榔头西一棒子而是整片整片地清理。第二DRC报告一定要先看前30条后面的报错绝大多数是连锁反应。我也犯过傻抱着五百多条错误从头改到尾改了三天才意识到前三条改完后后面全消了。判断连锁反应的办法是报错里大片涉及同一个复位源、同一个模块、同一组信号那基本就是一条主根烂了整片叶子都黄。第三别在复位写法上秀操作。功能上能用全局同步复位解决的绝不用局部异步复位能用简单与门旁路的绝不做多级mux嵌套。DFT工具不是人它推断不了的逻辑一定会变成报错打回给你最后省下的时间才是你的。第四RTL里使用test_mode时一要保持命名全局统一不要每个模块叫法都不一样二要保证它在综合过程中不会被误优化掉。我曾经因为一个test_mode信号在综合时被工具当作恒定为0结果整个复位旁路逻辑全被优化没了流片回来后DFT模式完全测不了。这个问题查了将近两周最后是翻了综合日志才发现的。现在我做综合一定会对test_mode信号设置dont_touch并且出网表后第一件事就是在网表里确认它还存在。做DFT这行很多时候你解决的问题并不难难的是你没想到问题会在那里出现。复位改RTL这件事把规则讲清楚之后也就是一个小时能修干净的事。但如果你自己踩过一轮坑就会明白那份DRC报告背后的来龙去脉远远比“改代码”三个字复杂得多。希望这份口语样本能给正在跟复位DRC搏斗的朋友一点实际帮助。