
项目一拿到手光看名字就知道这是块硬骨头DFT本来就是个“不出彩但出问题就是大问题”的环节再叠加上多时钟域基本等于把最容易出事的两样东西凑到了一起。做了这么多年可测性设计我最大的感受是——多时钟域Scan Chain翻车从来不是因为工具不行而是因为设计者在动手之前没把时钟架构想清楚或者忽略了跨域路径的时序本质。这篇内容就写给正在用Synopsys DFT Compiler和TetraMAX做多时钟域Scan Chain的朋友从原理到命令到坑尽量一次讲透。1. 多时钟域Scan Chain到底难在哪1.1 先搞清楚Scan Chain的两个工作模式Scan Chain的物理基础是扫描单元绝大多数设计用的是muxed-D触发器。这种触发器比普通D触发器多了一个测试数据输入端scan_in和一个测试使能端scan_enable简称SE。功能模式下SE为0触发器走正常的数据通路进入扫描模式后SE为1触发器的输入被强制切换到前一扫描单元的输出上整条链就变成了一个巨大的移位寄存器。这套机制的完整工作周期分两个阶段Shift阶段SE拉高测试数据从scan_in引脚进入沿着链一级一级往后推。这个阶段所有Flop必须有一个可用的时钟沿驱动否则链上某个区域的数据根本推不动链就“断”了。Capture阶段SE拉低时钟送出一到两个捕获脉冲Flop采下功能逻辑的响应结果。这个阶段的麻烦在于——捕获脉冲到底发给谁多个时钟域之间有没有建立保持时间关系单时钟域设计里这两个阶段都很直觉同一个时钟沿既负责把数据推出来也负责在下个沿把它采回去只要把setup/hold约束做好就完事。多时钟域不是这样。launch沿和capture沿可能来自完全不同的时钟它们之间的时间关系不是天然确定的你得自己想办法让这个关系变得可控。1.2 多时钟域和单时钟域的差别到底在哪拿一颗典型的SoC举例CPU核跑800MHz总线跑400MHz某个外设IP跑100MHz还有一个来自外部晶振的32.768kHz低频时钟。这些时钟可能由同一个PLL分频得到相位关系相对固定也可能来自两个完全独立的PLL频率和相位都在“漂移”唯一的共同点是它们共享同一个硅片。扫描模式下麻烦就来了。如果你把不同时钟域的Flop串在同一条扫描链上前一个Flop在clk_a的上升沿把数据推出来后一个Flop在clk_b的上升沿去采数据可能到得太早hold violation也可能到得太晚setup violation甚至由于两个时钟的相位关系完全不确定时序分析工具根本无法给出一条确定的时序路径。更隐蔽的是Scan模式下的时钟树和功能模式不是一回事。功能模式里我们可以做时钟门控、多级缓冲、跨域同步器扫描模式下为了测试方便通常会绕开这些逻辑直接给扫描单元灌测试时钟。这意味着你在功能时序收敛时看到的那套时钟skew数据在scan模式下完全失效需要单独分析和收敛。1.3 多时钟域Scan Chain最典型的四类问题问题现象典型表现根因跨时钟域hold/setup违例dft_drc报violation仿真出现X态chain test挂不同时钟域Flop串在同一条链上launch沿和capture沿时间关系不可控Scan Enable时序错位链移位数据乱序pattern仿真大量mismatchSE信号在多个时钟域到达时间不一致跟时钟沿发生竞争时钟门控导致链断裂某段Flop完全无法移位扫描模式下门控逻辑把测试时钟关掉了Flop收不到时钟沿共享总线/IO冲突多个驱动同时打开电流过大甚至烧毁IO功能复用的三态总线在测试模式下方向失控这张表基本就是多时钟域Scan Chain问题的全貌。后面所有内容包括工具配置、flow设计、调试手段都是围绕这张表展开的。把这几类问题的根因刻在脑子里比背一万条命令都管用。2. 动手之前想清楚这四件事在打开DFT Compiler之前有四个问题必须先想明白。我见过太多人一上来就写set_scan_configuration结果跑了三轮dft_drc才发现时钟架构压根就没摸清楚浪费时间还在其次关键是问题被一层层掩盖后面越改越乱。2.1 摸清时钟架构同源还是异源第一步不是写命令是读设计。拿到底层网表之后先把时钟架构梳理成一张清单挨个回答这几个问题芯片有哪些时钟端口哪些是PLL输出哪些走了分频/倍频这些时钟之间有没有确定的相位关系同源分频出来的时钟是理想情况异源PLL是麻烦情况。每个时钟在测试模式下是否都有效很多SoC会有一个test_mode信号强制PLL切换到测试时钟源如果是这样扫描时所有时钟域会退化成同一个源问题就直接简化了。每个时钟有没有门控门控点在什么位置我习惯把这个阶段的产出做成一张“时钟域清单”表格包含时钟名、源、频率、相位关系、是否有门控、是否异步、备注。后面所有DFT配置都以这张表为依据写-timing、配-clock_mixing都不是拍脑袋而是有据可依。2.2 选择chain的组织策略no_mix还是mixSynopsys DFT Compiler用set_scan_configuration的-clock_mixing选项控制一个scan chain里能放几种时钟域的Flopno_mix每条链只能包含来自同一个时钟域的Flop。最保守时序最容易满足但chain数量多需要更多测试引脚或更大的压缩逻辑。mix_edges允许同一时钟域内不同时钟沿上升沿、下降沿的Flop混在同一条链。如果你有下降沿触发的Flop这个选项很有用但你必须把launch沿和capture沿定义清楚。mix_clocks允许不同时钟域的Flop混在同一条链。工具会自动处理跨域时序并且建议插入lockup latch。我的经验是异步时钟域之间尽量不要用mix_clocks宁可多拉几条链。混时钟的代价不在插链阶段而在后端CTS阶段——跨时钟域的skew收敛会消耗大量迭代时间修hold比多接两根scan引脚痛苦得多。同一个PLL分频出来、相位对齐的时钟才值得用mix_edges或者小范围mix_clocks。2.3 要不要插lockup latch以及为什么当你决定混链之后lockup latch就是必须考虑的问题。多时钟域混链时链上相邻两个Flop可能由不同时钟驱动launch沿到capture沿之间的时间关系不可控hold违例几乎是必然的。Lockup latch的解决思路很巧妙在跨域Flop之间插入一个电平敏感型锁存器用后一个Flop的时钟反相位控制。时钟低电平时锁存器透传数据高电平时锁存住数据。这样即使launch沿和capture沿之间时间很紧数据也能先在低电平窗口被稳定抓到再被后一个Flop在高电平沿采走。可以粗暴地理解为给跨时钟域的数据装了一个“蓄水池”先把水接住再放给下游。DFT Compiler里用set_scan_configuration -add_lockup true -lockup_type latch控制插入。但要注意lockup latch本身也是时序单元后端CTS阶段需要对它做专门的时钟平衡否则会引入新的skew问题。这一点务必提前和后端同事打招呼。2.4 把DFT约束骨架先搭好理解了前面这些才能开始写脚本。下面这段是典型的骨架定义了扫描时钟、扫描使能、复位和测试模式信号set_dft_signal -view spec -type ScanClock \ -port clk_a -timing [list 0 40] set_dft_signal -view spec -type ScanClock \ -port clk_b -timing [list 0 40] set_dft_signal -view spec -type ScanEnable \ -port test_se -active_state 1 set_dft_signal -view spec -type Reset \ -port test_rst_n -active_state 0 set_dft_signal -view spec -type TestMode \ -port test_mode -active_state 1 set_scan_configuration -chain_count 4 \ -clock_mixing mix_clocks \ -add_lockup true-timing [list 0 40]的意思是这个时钟的上升沿在0ns出现下降沿在40ns出现。如果你有下降沿Flop必须把完整的沿信息给工具否则create_test_protocol阶段工具可能把下降沿单元判为非法。test_se和test_rst_n在功能模式下必须通过test_mode信号正确disable掉别让它们在功能路径上造成额外负担。3. 用DFT Compiler跑通完整实现流程这一节把流程从头到尾走一遍。我默认你已经有了综合完成的门级网表手里有SDC和DFT脚本模板下面重点讲每一步为什么要这么做以及做完之后要看什么。3.1 从综合到DFT的交接三个文件一个都不能少正式流程通常是先跑一轮不带扫描链插入的综合拿到门级网表后再做DFT。为什么不直接在RTL级操作因为DFT Compiler的很多检查——比如跨时钟域路径的时序评估、SE和时钟沿的关系——需要真实的门级单元延迟信息才能判断。RTL阶段能查逻辑连接查不了时序。交接时三个东西必须齐门级网表.v或.vg、SDC约束、DFT约束脚本。SDC里的时钟周期和不确定性一定要写准。DFT工具插链时会拿这些约束评估Flop之间的时序关系如果你的SDC里时钟uncertainty设得过于乐观或者完全没设后面dft_drc会报一堆莫名其妙的violation排查起来非常痛苦。3.2 关键配置命令逐条解读在骨架之外有几个实战中绕不开的配置要重点讲# 指定扫描输入输出端口 set_dft_signal -view existing_dft -type ScanDataIn -port si0 set_dft_signal -view existing_dft -type ScanDataOut -port so0 # IO有限时允许多条chain共享输入输出 set_scan_configuration -shared_scanin true set_scan_configuration -shared_scanout true # 控制chain长度上限保持各链均衡 set_scan_configuration -max_length 300-shared_scanin这里多说一句。很多SoC的扫描引脚数量有限多条chain需要共享同一个输入引脚。工具会把输入信号并行接到所有chain的链头。共享本身没问题但注意如果多条chain的链头Flop属于不同时钟域shift时可能出现有的链进了数据、有的没进。所以共享输入时最好配合mix_clocks一起用并且每个chain的链头单元类型尽量保持一致。report_scan_path输出里链头单元一目了然务必逐条核对。3.3 create_test_protocol和dft_drc把问题提前炸出来配置好之后第一件事不是直接insert_dft而是先建测试协议再跑DRCcreate_test_protocol dft_drccreate_test_protocol根据你定义的扫描时钟、SE、复位信号生成一份测试协议文件SPF/STIL/CTL。协议里定义了shift和capture的时钟沿关系、SE的翻转时刻、复位时序。这份文件后续会直接传给TetraMAX所以这里定义错了后面全错。dft_drc是整个DFT flow里最值得认真对待的“体检”。它会检查扫描单元是否满足移位要求、每个时钟域是否有可用测试时钟、SE信号能不能在shift和capture之间正确切换、跨时钟域路径是否合理以及有没有未驱动的三态网络。举例来说你可能遇到这样的报错Rule 3.8 - Clock not controlling any scan cell说明有些扫描单元挂在了一个未定义为测试时钟的时钟上或者Rule 10.2 - Scan enable may change simultaneously with clock edge说明SE沿和时钟沿存在竞争这在多时钟域里太常见了。看到这些violation别急着insert先把每个报错都看懂、修掉再往下走。3.4 insert_dft之后如何确认chain真的对了violation清零后执行insert_dft report_scan_path -view existing -chain all report_dft_configuration这三条命令里最关键的是report_scan_path的输出。你需要确认四件事每个chain的连接顺序是否符合预期各chain长度是否均衡有没有一条链特别长导致测试时间爆炸跨时钟域连接的相邻Flop之间有没有自动插入lockup latch工具会在报告中以特定单元类型标出来每个chain的起始单元是否正确接到了对应的扫描输入端口或压缩逻辑端口。如果发现两个完全异步的时钟域Flop紧挨着却没有lockup马上回查-add_lockup配置和dft_drc报告。这里的疏忽等到TetraMAX或仿真阶段再暴露定位成本会翻好几倍。4. 从网表到测试向量TetraMAX实战插完链、确认无误进入ATPG环节。TetraMAX做的事情用一句话概括就是根据网表和测试协议决定每个测试周期的激励怎么灌、捕获沿拉在哪里、期望响应是什么然后生成测试向量文件。4.1 标准流程和时钟沿定义一套基本的TetraMAX流程如下tmax read_netlist ./outputs/top_scan.v run_build_model top read_test_protocol ./outputs/top.spf add_clocks 0 clk_a add_clocks 0 clk_b add_atpg_clock_edges 0 clk_a capture run_drc run_atpg -auto report_faults -summary report_coverage write_patterns ./outputs/top_patterns.v -format verilog这里有一个细节必须强调add_clocks里指定时钟沿信息要和DFT Compiler里set_dft_signal -timing保持一致。比如你在插链阶段定义了-timing [list 0 40]在TetraMAX里就要按同样的相位关系配置否则ATPG工具算出来的捕获窗口是错的生成的pattern跑仿真必然挂。read_test_protocol读入SPF后TetraMAX会自动建立扫描链模型。如果你是首次做这个设计建议先跑一遍run_drc看有没有ATPG级别的违例。ATPG的DRC和DFT Compiler的DRC不是完全一回事这里主要检查扫描链在ATPG建模下是否可测、有没有影响测试的冲突。4.2 覆盖率和chain test怎么解读TetraMAX跑完后先看两个指标总的fault coverage一般成熟项目要求98%以上异步跨域路径不收敛的情况另说以及每条scan chain的chain test是否通过。Chain test是扫描链自身的“通路测试”验证的是链上每个Flop能否正常移位不验证功能逻辑。如果chain test挂了说明链上有物理故障或时序问题后面所有逻辑测试结果都不可信。多时钟域设计里chain test挂的最常见原因就是跨域连接处的hold违例或者某个时钟域在shift模式下时钟被门控掉了。这里还要提醒一句不要只盯最终coverage数字。TetraMAX的report_faults -summary会列出uncovered fault我习惯统计一下其中有多少落在跨时钟域路径上。如果这个比例偏高大概率不是ATPG算法问题而是插链策略或约束方向不对。这时候回到DFT Compiler重新调整-clock_mixing和lockup配置比在TetraMAX里死磕更有效。5. 避坑指南这些坑我帮你踩过了这节是全文的重点以下每一条都是真实项目中反复出现的问题有些我踩过不止一次。5.1 跨时钟域hold time一切麻烦的源头两个不同时钟域的Flop串进同一条链最经典的故障就是hold time违例。Shift模式下前一个Flop在launch沿把数据推出来后一个Flop要在下一个时钟沿采到。当这两个沿来自不同时钟域时只要时钟skew稍不理想数据就可能提前冲到后级Flop的输入端在capture沿还没到的时候就把不该更新的数据更新了或者在下一个沿到来时旧数据已经消失于是整个链的移位结果全错。对策是lockup latch前面讲过原理。这里补充一个频率差距的问题如果链上两个时钟域频率差太大比如clk_a是500MHz、clk_b是1MHz低速时钟域的半周期会非常长lockup latch反而可能引入过大的延迟裕量问题。这种场景下不要硬混链把低速域单独拆一条链是更稳的选择。高频低频混链看似省了引脚实际付出的时序代价和后端迭代成本远超收益。5.2 Scan Enable的时序比想象中更容易翻车Scan Enable在多时钟域里是另一个隐形炸弹。SE虽然是全局信号但它到达每个时钟域内Flop的时间不一样。如果某个域的SE跳变和该域时钟沿“撞车”Flop可能已经在shift状态但输入还没有从功能路径切换到扫描路径导致链上数据错位。DFT Compiler会检查SE沿和时钟沿的关系但有些项目为了省寄存器会省略SE的同步逻辑。我的习惯是SE在进入每个时钟域之前先用该域时钟打一拍同步做一个“SE同步树”。这样虽然增加几个触发器但能保证所有Flop的SE在同一时钟沿对齐。工具里可以通过set_dft_signal -view spec -type ScanEnable配合相关参数控制SE路径的实现方式不同版本命令名略有差异建议参考对应版本的Command Reference。5.3 时钟门控扫描模式下时钟集体失踪这是多时钟域设计里最“玄学”的问题。功能模式下为了省功耗很多模块的时钟是门控的模块空闲时时钟关掉。问题在于如果这些门控逻辑在扫描模式下没有被正确旁路测试时钟进入该模块时也被关掉了这个模块里的所有Flop就完全无法移位链在这里“断”掉了。处理思路有两种。一种是在设计阶段就留好test_mode信号当test_mode1时所有门控时钟的与门变成直通这是最干净的做法。另一种是在DFT Compiler里用set_dft_signal -type TestClock把门控点声明为测试时钟让工具自动处理门控逻辑。我强烈建议选第一种因为设计阶段解决的成本远低于DFT阶段打补丁的成本。成熟项目里test_mode接管时钟门控是底线要求。5.4 共享总线和IO的DFT处理多核或带总线的SoC里经常有多个模块分时复用一组数据总线或者一组IO口既当功能口又当扫描口。到了扫描模式这些总线上的多个驱动可能同时被使能造成总线冲突。轻则pattern仿真失败重则ATE上实际灌电流过大直接损伤芯片。对这类结构DFT工具层面能做的是通过set_dft_signal -type Constant约束某些总线驱动器在测试模式下处于高阻或关闭状态或者对三态IO设置set_dft_driving_cell指定方向。但我的经验是工具只能负责约束真正干净的方案还是设计阶段留一个test_mode信号专门接管总线方向控制。尤其是把共享IO用作扫描输出时要确认三态门在scan模式下的方向控制不会让扫描输出点落在三态总线上否则输出的驱动能力和方向都是不确定的。5.5 仿真不一致永远先查这三个点TetraMAX生成的pattern拿到VCS里跑门级仿真结果对不上这是DFT工程师收尾阶段最头疼的事。按我个人的经验90%的仿真不一致都出在三个地方按顺序排查基本能解决时钟沿定义TetraMAX里add_clocks定义的时间和仿真TestBench提供的时钟沿是否一致很多不一致都是测试平台时钟相位和ATPG预期相位对不上造成的。SE跳变时序pattern里SE在什么时间点从1变0仿真里SE是理想时序还是带单元延迟如果用了带延时的std cell库SE和时钟沿之间的竞争会被放大要确认仿真设置是否包含真实的时序检查。复位和初始状态扫描测试要求芯片上电后先复位到已知态否则前几拍全是X态期望响应当然对不上。VCS里可以配合负延时检查和适当的复位时序控制来减少这类误报。按这个顺序排查大多数仿真不一致问题都能定位到根因而不是在那里瞎试乱猜。做多时钟域Scan Chain这几年我最深的体会是好的DFT设计不是靠事后修出来的而是在动手之前就把时钟架构、链组织策略、lockup策略、门控处理这些事都想清楚。Synopsys的工具链确实强DFT Compiler能自动插lockup、能报出大部分跨域violationTetraMAX也能在几分钟内给你跑出几万条pattern但它们都只是把设计者想清楚的事情落地。如果你自己都没想清楚哪个Flop该归到哪条链、哪个跨域点该怎么处理工具只能在错误的基础上更快地生成错误的结果。最后再分享一个小技巧每次做完一轮insert_dft我都会把report_scan_path的结果存一份带日期的快照放进项目归档目录。等后端CTS阶段或者TetraMAX出问题时翻这份快照能快速确认当前看到的网表是从哪一版DFT配置来的省去大量“当前网表到底是谁生成”的扯皮时间。做DFT理性、留档、按流程走比什么都重要。