
做后端时间长了你会越来越认同一个观点在APR流程里扫描链重排是最像白捡的优化。逻辑没变、约束没变、库没换只是把一串移位寄存器的物理连接顺序重新排了排布线拥塞、时序余量、甚至动态功耗都可能给你意想不到的反馈。Cadence Innovus里这个功能的总开关就是setScanReorderMode不少团队在place阶段开了它之后扫描链的物理长度能缩短两三成局部拥塞能明显摊平。这篇文章我不打算念手册而是从一个实际跑过多个tapeout项目的后端工程师角度把这条命令的原理、参数、流程落法和坑一次讲清楚。这篇内容适合谁看正在做数字后端实现、被congestion或长绕线扫链折磨的工程师以及从DFT切到物理实现、想搞清楚scan chain在Innovus里到底怎么被重新连接的兄弟。读完你至少能回答三个问题扫描链为什么允许重排、setScanReorderMode到底在调什么、以及怎样把重排安全地嵌进现有流程而不翻车。1. 扫描链重排为什么说这是后端流程里性价比最高的优化1.1 一条扫描链的物理困境大部分团队的标准流程是这样的DFT工具Tessent、DFT Compiler这类在综合阶段插入扫描链时基本是按照RTL的逻辑层次和模块边界来串链的。它看图的是哪些寄存器在同一个always块/同一个module下或者时钟域和复位域怎么归属很少考虑这些寄存器最后会被摆到芯片的哪个角落。于是physical design阶段经常出现一个很滑稽的画面一条扫描链有60个寄存器前5个在芯片左下角中间30个稀稀拉拉横穿die最后25个又跑到右上角scan_in到scan_out的长线跟别的逻辑信号在某个局部区域挤成一团。Place阶段看到某个5x5 bin的congestion飙到1.2以上你第一反应是去调floorplan、去挪macro但很少有人先想到扫描链本身就是个巨大的布线污染源。1.2 重排到底动了什么、没动什么很多刚接触后端的人会担心扫描链重排会不会改变芯片的功能会不会让DFT测试向量全部失效要搞清楚这个问题得先理解扫描链的本质。扫描链在测试模式下是一串移位寄存器数据从scan_in打进来沿着链一级一级传到scan_out。这条链上每个寄存器只需要满足链上出现一次、移位方向一致至于谁排在第5位、谁排在第30位功能上完全等价。工具在物理重排时改变的只是TDI/TDO之间的连接关系D pin上的功能逻辑一个bit都不动。换句话说重排没有改变你芯片的逻辑行为也没有改变ATPG向量加载/卸载的基本机制它只是把逻辑顺序和物理位置重新对齐了一次。这就是为什么这个优化在逻辑上白捡——它不消耗你任何裕量只是把本来就存在的自由度用了起来。1.3 重排到底能换回什么根据我经手的项目经验扫描链重排的收益通常摸得到扫描链相关net的总线长能下降20%到30%如果开的是全局重排下降更明显局部congestion热点能明显降下来特别是原本有长扫链横穿的regionscan shift期间的动态功耗有小幅下降因为长线变短、翻转电容变小给CTS和hold fixing减少压力长距离scan net少了跨cell传播的skew问题也少一些。所以我的建议很直接如果你的设计过了place之后还有congestion热点而且你还没试过扫链重排先别急着调floorplan把setScanReorderMode这一套开关跑一遍往往比折腾两三天floorplan来得快。2. setScanReorderMode 的参数地图每个开关对应哪个痛点2.1 先分清两类模式逻辑序约束与物理位移约束setScanReorderMode这个命令的核心价值在于它让你决定重排的自由度有多大。工程师最容易犯的错就是一刀切地打开重排结果工具把整条链甩得到处都是后续检查只能干瞪眼。所以先理解模式分类很重要。按我的经验Innovus里这套机制可以粗略分成几个维度来理解全局重排 vs 局部重排。全局global重排允许工具跨模块、跨region去交换扫描链上的寄存器顺序甚至能把一条chain里的寄存器跟另一条chain交换换来的是最激进的拥塞改善局部local重排则限制在邻近区域工具只能在距离较近的寄存器之间调整顺序换来的是更可控的物理结果。没有特殊情况我都会先在local模式下跑一版看效果不够再放开到global。逻辑序约束。有些设计里DFT工具生成的链顺序其实是经过扫描向量压缩优化的乱改顺序虽然不影响正确性但可能影响某些特殊向量apply的效率。如果你们DFT团队明确说链序别动那你在setScanReorderMode里就要保留逻辑顺序约束只让工具做局部微调。物理位移距离约束。很多版本支持限制单个寄存器在重排中最多移动多远。我习惯叫它maxShift之类的距离上限具体开关名看版本它的价值在于防止工具为了追求拥塞最优把一个寄存器从A block搬到B block结果引发新的时钟树问题。扫描使能和其他group约束。如果scan enable / scan clock是分group控制的重排时必须保持group边界否则测试时序检查会出问题。这也是这个命令里常被忽略的选项。我建议你在跑之前先画一张表把设计关心的约束列清楚约束维度激进配置保守配置适用场景重排范围全局重排局部重排拥塞热点多且分散选全局只有局部热点选局部逻辑序不保留严格保留需要ATPG向量兼容性时选保留位移上限不限制限制在小范围担心影响CTS/floorplan时选限制跨group允许跨chain交换禁止跨chain交换DFG/DFT有明确chain归属时禁止2.2 版本差异与默认值陷阱这里必须多说一句Innovus版本更迭比较快setScanReorderMode在不同版本里的参数名字和默认行为真的不完全一样。早期版本里可能叫-sideEffect、-order新版本又引入了更细粒度的mode选择。我见过不止一个工程师照着网上老博客抄命令结果在Innovus 19或21版本上报错然后跑来问是不是工具坏了。所以我的经验是拿到一个新版本第一件事打开文档搜setScanReorderMode只看这一条命令的说明页搞清楚三件事——当前版本默认是开还是关、支持哪几种模式、跟placeDesign的交互方式是什么。别靠记忆别靠猜测。更隐蔽的坑是默认值。某些版本里setScanReorderMode -order true之后重排是自动生效的但最大移动距离没有限制另一些版本里你还得显式打开-mode global才会做跨模块重排。如果你在place之后没有看到任何扫描链物理顺序变化别急着说功能无效先确认是不是有-mode local和位移上限把工具绑死了。2.3 与Place/CTS的配合方式setScanReorderMode不是一条独立跑完就结束的命令它的执行时机非常关键。大多数情况下重排是在place过程中或place之后、clock tree synthesisCTS之前完成的。为什么必须在CTS之前原因很简单CTS的工具会基于寄存器的物理位置去平衡时钟树。如果你在CTS完成之后再做一次扫描链重排相当于把一堆寄存器的位置关系打乱了之前平衡好的时钟树局部结构很可能出现新的偏差你不得不重新做一趟CTS或者额外修一堆skew。另外要注意的是place阶段的legalization和scan reorder之间是有耦合的。工具如果能在place过程中边放边重排它可以把你原本要绕很远的一条scan net直接通过调换两个寄存器位置来化解。这就是为什么我坚持把扫描链重排放在place的早期阶段而不是place完成后再掏一个独立的flow步骤。3. 落地实操把扫描链重排嵌进 Innovus 的完整流程3.1 重排前的准备检查清单在敲任何一行命令之前先确认下面几件事都满足了不然重排跑完你会被各种离奇问题缠住Netlist里已经正确插入了扫描链scan_in、scan_out、scan_enable这些端口都定义完整SDC里扫描相关的时钟约束已经写好至少scan clock的create_clock存在没有手动fix的扫描链路径——如果有要明确告诉工具保留floorplan基本定型power grid至少有一个粗略的plan。没有power plan就做重排没有意义因为你根本看不出拥塞是不是因为电源绕线造成的和DFT同事确认过重排的边界条件chain长度上限、chain数量、是否需要保持chain成员完全一致。这个检查清单看起来基础但我见过有人因为没检查第3条跑了全局重排之后发现手工搭的一条redundant chain被工具拆了结果整个scan shift测试直接挂掉。花五分钟检查能省后面两天。3.2 推荐命令序列下面这套序列是我眼下在用的、比较稳妥的做法。注意具体开关名在不同版本里可能有差异但思路是通用的# 1. 保留来自DFT的chain定义 setScanReorderMode -order true -mode local -maxShift 3 # 2. place过程中自动执行扫描链重排 placeDesign -noPrePlaceOpt # 3. 如果需要更激进的优化可以在这里放开global setScanReorderMode -order true -mode global reorderScanChain -all -update # 4. 报告重排前后的对比结果 reportScanChain -order reportCongestion -hotspot第一步我通常用local模式限制寄存器最多移动3个site的距离。这样工具能在不破坏整体布局结构的前提下把同一条链里明显隔山相望的两个寄存器拉到一起。第二步的placeDesign -noPrePlaceOpt不是必须的但我习惯先skip prePlaceOpt避免前期优化把扫描链相关的逻辑挪得面目全非导致后面重排效果不好判断。第三步是个可选项。等place结束我其实会先看一眼reportCongestion如果仍有个别hotspot高得离谱再临时放开global重排。最后一步的reportScanChain -order会打印重排后的顺序我强烈建议你把这个报告留档后面DFT同事问起来你才说得清楚。3.3 如何用 reportScanChain 验证结果跑完上面命令别只看congestion数字就收工。我一般会按顺序check三份报告reportScanChain确认链的总条数没变每条链的寄存器数没超上限scan_in/scan_out端口连接正确reportCongestion重点看重排前后热点区域的overflow是否下降下降幅度够不够report_delay_through_scan_chain或者同类时序指标确认每一条scan chain上的总延迟没有因为重排而恶化。如果寄存器数量对不上先别急着骂工具。八成是你在setScanReorderMode里开了跨chain交换工具把原来属于chain A的某个寄存器划给了chain B。物理上没问题但DFT那边如果还拿旧chain定义反标覆盖率就会一脑袋浆糊。这种情况要么设成local模式要么在命令里禁止跨chain交换。另外还有一个非常实用的技巧重排前把每条chain的原始顺序dump一份重排后再dump一份用文本diff直接看工具到底动了哪些点。这样可以快速圈定某个可疑寄存器是不是被挪到了不该去的地方。4. 实测效果拥塞、时序和面积到底变好了多少4.1 一组有代表性的实测数据先声明下面这组数据来自我正在做的一颗28nm工艺、约600万instance的SoC不是benchmark仅供参考。但这组数据的走向在我在其他几个项目里反复出现过我觉得有一定代表性。指标重排前打开setScanReorderMode后变化扫描链总线长mm158.6121.3下降23.5%Congestion热点区over 1.1的bin数4721下降55%全局wirelengthm32.832.1下降2.1%WNSsetupns-0.12-0.06改善0.06TNSsetupns-0.9-0.3改善0.6Cell面积mm²9.829.81基本不变Scan shift功耗相对值1.000.91下降9%扫描链总线长下降23%是我最关注的一个数字因为长线才是congestion和功耗的根源。全局wirelength只下降2.1%看起来不算多但考虑到普通逻辑net几乎没动这2.1%可以说完全来自扫描链相关net的缩短几乎没有副作用。setup时序的改善其实是个间接红利。拥塞降下来之后工具在局部区域的绕线资源不再那么紧张标准单元的摆放密度也更合理关键路径上少了不必要的detourWNS自然就好了一点点。TNS从-0.9ns收窄到-0.3ns对于那个项目来说直接让我少插了两百多个hold buffer虽然hold修的是不同阶段的问题但资源是相关的。4.2 为什么它能把局部拥塞摊平从一个宏观视角看你想象一下一条扫描链拉着60个寄存器在版图上画了一条折线它本身占用的布线资源并不算特别多但它经过的区域往往会被堵住一大片。尤其当多条扫描链都喜欢沿着某些通道走时congestion就呈现为几个孤立的高热点。重排之后工具把这些折线拆成了一段段短连接原本集中在一条通道的流量被分散到了周围可用区域。说白了它不是在总量上抹掉多少布线而是把布线的空间分布抹匀了。这就像早晚高峰的地铁如果所有人在同一节车厢门口排队门边肯定堵死换乘站疏导一下让乘客分散到不同车厢总人数没变但每个门都通畅了。这也是为什么我说效率翻倍是有道理的你只是改动了一条命令的开关位置换来的是congestion热点减半、wirelength下降、功耗变好而实现成本几乎为零。4.3 什么时候收益不明显不是所有设计都能从扫描链重排里拿到同样的收益。根据我的经验以下情况效果会打折扣寄存器密度特别低、die面积很宽敞的设计。本来就有足够的绕线空间扫描链怎么走都不会成为瓶颈扫描链本身非常短比如每条链只有十几个寄存器重排空间不大寄存器分布已经和逻辑聚类高度一致比如DFT工具在插入时就已经参考了部分物理信息拥塞主要来自memory之间的窄通道跟扫描链没有关系。碰上前四种情况别把希望全押在setScanReorderMode上。先跑一版看看趋势如果reportScanChain前后总线长变化小于5%那说明你的设计本来就不缺这个优化赶紧把时间花到floorplan和memory placement上去更划算。5. 踩坑记录与团队脚本模板5.1 坑一滥用 maxShift 之后 debug 成本飙升我第一次在真实项目里用全局重排时图省事把位移上限放得很大希望工具一次优化到位。结果place是跑完了congestion也确实好看但等到做物理验证时发现某个module里原本一起放置的关键寄存器组被打散到了两个block里CDC检查的时序路径被拉出一个大圈debug花了我整整两天。现在我给自己定了一条规矩任何项目第一次跑重排一定用local 位移上限的保守组合先看收益再逐步放开。不能把工具当成黑盒一次喂最猛的药。保守配置跑出来的结果即使不是最优至少是可控的你知道每个寄存器大概都不会跑出原有区域太远。比较激进的做法我也试过先local跑一版留下重排后的chain报告然后在同一版上跑global重排对比两个结果的scan chain总线长和congestion。如果global只比local好3%以内我绝对选local因为后续流程的可预见性更好。5.2 坑二重排后Scan Enable的DRV被忽视扫描链重排后寄存器位置变了scan enable这条测试模式下一网多用的信号fanout和走线长度也会跟着变。很多工具在重排时对functional clock和data的优化是充分的但对scan enable这种测试模式才用、平时apex不care的net默认权重往往不够。我碰到过一次重排后扫描链的shift时序很好但scan enable的transition在某个角落超限DRVdesign rule violation报出一大片不得不回头补buffer。后来我在重排前就给工具显式设置了scan enable的权重并要求DRV修复的范围覆盖到test模式这个问题就没再复发。在命令里具体怎么设置文档里通常会叫setScanReorderMode -scanEnableWeight这类选项不同版本名字略不同。原则就是让工具知道scan enable和scan clock是重要人物重排时不能只顾着缩短TDI到TDO的距离而把它们晾在一边。5.3 坑三重排后的chain membership变了ATPG向量到底还能不能用每次重排之后最容易被DFT同事追着问的就是你们后端把扫描链重排了我的ATPG向量是不是要重新跑这里要看情况。如果重排只是调整了同一条逻辑链内部寄存器的物理顺序链的成员集合没变只是移位次序换了那么扫描向量加载/卸载的基本物理过程不变已有的ATPG向量可以继续用因为扫描链本身是个线性移位寄存器具备任意置换顺序的性质。但如果开了全局重排且允许跨chain交换某些寄存器可能被从chain A挪到了chain Bchain的成员集合变了那ATPG的加载关系就变了向量就不能直接沿用必须让DFT那边重新生成。你自己也要同步更新SDC或scan chain定义让后续的formal和测试准备一致。所以我的建议是如果你们项目已经进入了ATPG向量冻结阶段重排必须选择禁止跨chain交换的模式只做链内物理顺序调整。把团队约束写在脚本头注释里别让不知情的人把模式改掉。5.4 团队脚本模板与两个对象查询技巧我们团队现在的标准做法是把扫描链重排参数收敛成一份可复用的脚本片段新项目直接source。贴一个精简版# scan_reorder.tcl # 团队约定默认local禁止跨chain交换maxShift3 setScanReorderMode -order true \ -mode local \ -maxShift 3 \ -preserveChain 1 # 如果floorplan阶段已经发现明显congestion hotspot set_global optimize_scan_chain_reorder 1 # 跑place让重排在place过程中生效 placeDesign清清爽爽四行命令够用。最后顺带提一个后端日常高频操作。很多同事会来问我Innovus里怎么选中某个标准单元上名字带biasnw的PG term 这类操作在扫描链重排后的IR drop分析里非常常见。我当时是这么回答他的重排之后要检查某个标准单元VDD/VSS的连接情况用dbGet可以精准命中# 找到所有名字带biasnw的instance上的PG term dbGet [dbGet top.insts.name biasnw*.pgTerms.name -p].name VDD如果想直接选中到GUI里高亮可以这样select_obj [dbGet top.insts.name biasnw*.pgTerms.name -p].name VDD这个技巧跟扫描链重排本身关系不大但它们在同一个项目阶段经常会遇到——重排后IR drop物理验证一旦报出问题你要快速定位到某个PG pin看是不是电源连接被绕线影响了。会写这两条dbGet查询定位效率能提升一大截。我个人在实际项目里跑完setScanReorderMode这套流程最深刻的体会是这类优化真正的价值不在于那20%的扫描链总线长下降而在于它解放了后面CTS和ECO阶段的精力。你越早把物理顺序调对后面所有环节花在收拾烂摊子上的时间就越少。如果你还没在自己的流程里验证过扫描链重排我的建议很朴素挑一个正在进行中的设计保守配置跑一版把reportScanChain和reportCongestion前后对比打出来让数据告诉你该往哪个方向调。