2026/10/9 6:46:15

Innovus addRepeaterByRule实用教程:规则驱动批量修复DRC与时序

Innovus addRepeaterByRule实用教程:规则驱动批量修复DRC与时序 做数字后端的人应该都有这种经历CTS和布线跑完之后打开时序报告和DRC报告总能看到几条net的transition超标、电容超标或者一条长线从模块一头拉到另一头delay大得离谱。以前我都是手动打开版图一个个点net、算位置、再ecoAddRepeater一个晚上能插二三十个buffer就算效率不错了。后来发现Innovus里有个addRepeaterByRule命令用规则批量处理这类问题省了至少一半时间。这篇就把这个命令的用法从头到尾拆一遍覆盖语法、rule文件写法、三个典型场景和一堆实操踩坑记录适合正在做数字后端PR、经常被DRC和时序折腾的工程师。1. addRepeaterByRule到底是干什么的1.1 从修DRC到修时序一个命令覆盖三类场景addRepeaterByRule是Innovus里用来批量插入repeater缓冲器/反相器的命令。所谓repeater本质上就是标准单元库里的buffer或inverter插在信号路径中间主要干三件事切分长线降低RC延迟、隔离高扇出降低负载电容、改善transition时间。实际项目中这个命令最常出现在三个场景里。第一是修DRC比如reportDrc里报了一堆maxTransition和maxCap违规而且集中在某几条高扇出net上第二是修时序特别是hold time violation利用buffer本身的delay来推路径第三是给跨模块长信号加中继这类net往往从floorplan阶段就知道肯定要插几级buffer只是具体插在哪、插几级光靠肉眼很难定准不如交给规则去算。很多人容易把addRepeaterByRule和optDesign、repairDesign这类自动优化命令搞混。后者是工具整体评估时序和DRC后自动修你只能通过选项限制它动哪些东西而addRepeaterByRule是你明确指定“给我处理这组net、用这些cell、最多插几级、离pin多远”目标更聚焦可控性更强。所以它特别适合那种你心里已经知道问题在哪、但不想手动一个个点的场景。1.2 为什么推荐规则驱动而不是手动一个个加早年间我习惯用ecoAddRepeater手动插图命令本身不难难的是确定插入位置和级数。一个高扇出信号负载在版图里散成一团你在哪个位置切分、切分后两级buffer的驱动能力怎么配都需要反复试。一次项目里修几十条net每条都要开GUI看半天效率非常低。addRepeaterByRule把这件事变成了“写规则、批量跑”。你只需要告诉工具哪些net要处理、允许用哪些cell、线路超过多长就要切、插入点离pin至少多远、最多分几级工具自己会沿着走线方向计算最优插入位置并且尽量把多个插入点分散开避免挤在一起造成局部congestion。它背后用的是工具内置的布线拓扑分析和延迟计算引擎比我手动目测准得多。另外规则可以复用。同一个block里如果有多条结构相似的net比如一组并行的数据总线、一组寄存器复位信号写一份rule文件几条命令全部处理完。下次另一个block遇到类似问题把rule文件拷过去改个net名就行。这才是规则驱动真正的价值——不是你这次省了多少时间而是你以后每次都能省。2. 命令语法与rule文件写法2.1 基本语法骨架与常用选项addRepeaterByRule的调用方式不复杂核心是-rule、-net、-cell这几个参数。我常用的基本骨架是这样的addRepeaterByRule \ -rule $rule_file \ -net $net_name \ -cell BUFX8 BUFX12 BUFX16 \ -prefix REP \ -numStages 2拆开看每个选项的作用。-rule指向规则定义可以是一个rule文件路径也可以直接在命令行里写一段inline规则后面详细说。-net指定要处理的net名支持通配符比如*data*。-cell是允许使用的cell列表工具会在这个列表里选合适的型号不会自己乱用库里的其他单元。-prefix是生成的实例名前缀这个特别重要后面检查插了几级buffer、追踪ECO改动都靠它。-numStages是允许的最大级数等于告诉工具最长可以串几级buffer。除了这几个还有几个我几乎每次都会带上的选项。-skipDriver和-skipLoad分别表示在driver端和load端附近不插buffer避免把buffer怼在原有pin脸上给后续布线留空间。-Halo是插入点与周围pin和走线的最小距离单位通常是um设得太小容易插进congestion区域设得太大又可能找不到合法位置。-maxRouteLen是最大走线长度超过这个值的net会被自动切分。-onRoute在有现有routing的情况下尽量贴着已有走线插这样对布线资源的扰动最小。2.2 rule文件的关键参数逐一拆解rule文件是addRepeaterByRule的核心配置里面定义了工具做决策时遵循的规则集合。不同版本Innovus对rule文件的格式支持略有差异但核心字段是通用的。我给一个自己在项目里用过的模板Rule: rep_rule1 { net: *; cell: {BUFX8 BUFX12}; numStages: 2; maxRouteLen: 250; maxCap: 0.5; maxTransition: 0.5; Halo: 5; skipDriver: true; skipLoad: true; onRoute: true; prefix: REP; }逐个解释关键字段。net定义这条规则匹配哪些网络*是匹配所有也可以写具体net名或用通配符缩小范围。cell就是允许使用的单元列表我建议至少给两三个不同驱动能力的型号让工具根据实际负载自己挑。numStages是级数上限一般高扇出net设2到3级足够设多了反而引入多余的延迟和面积。maxRouteLen是切分阈值单位um超过这个走线长度的net就会被插入repeater切段。maxCap和maxTransition是触发条件相当于说“这条net如果电容或transition超过某个值就启动插入流程”。Halo和skipDriver、skipLoad这几个字段跟在命令行里的同名选项作用一样。onRoute字段决定是否优先沿着已有走线路径插入我强烈建议保持true。需要特别注意rule文件里的分号和花括号格式。Rule后面是规则名名称不能和已有规则冲突字段之间用分号分隔最后一个字段后面不写分号也不会报错但我习惯写全方便复制修改。要是rule文件写错了工具会在启动时提示parse error不过报错位置有时候定位得很模糊所以写完后先用小范围net试跑一次比较稳妥。2.3 inline rule与rule文件的选择并不是每次都要单独写一个rule文件。处理一条临时net时我更倾向于直接在命令行里写inline rule这样改动灵活不用来回切文件。写法就是把rule内容作为-rule的值传进去addRepeaterByRule \ -rule Rule: tmp_rule { net: rst_n; cell: {BUFX4 BUFX8}; numStages: 2; maxRouteLen: 200; skipDriver: true; skipLoad: true; Halo: 5; } \ -net rst_n什么时候用文件、什么时候用inline我的判断标准是看“复用性”。如果只是临时修一条netinline最快如果打算把一组规则沉淀下来复用或者规则比较复杂、字段超过十几个那就写成独立文件放在项目目录的scripts/eco目录下方便版本管理。另外提醒一点-rule选项本身也可以不依赖rule文件指定net匹配范围在命令行里用-net单独指定即可。但要注意如果rule文件里也写了net字段两者同时存在时规则的匹配范围会对命令行指定的net做交集。曾经有同事因为rule里写了net: *以为万事大吉结果命令行里又传了一个具体net名工具处理的范围比他预期的小很多半天没查出原因。建议要么rule文件里只写配置型字段把net匹配完全交给命令行要么在rule文件里明确写清楚两条路径不要混用。3. 三个典型实操场景与具体命令3.1 场景一高扇出复位信号插buffer tree寄存器复位信号是addRepeaterByRule最典型的应用对象。一个全芯片的异步复位信号扇出经常几百甚至上千后端PR时如果没做reset tree这条net的cap和transition基本必爆。处理思路是把单根高扇出线通过多级buffer展开成树形结构每一级只驱动一部分负载。假设现在有一条名为rst_n的net接了几百个寄存器的复位端reportDrc显示maxCap违规。我一般这样处理report_net -net rst_n -drv addRepeaterByRule \ -rule Rule: reset_rule { net: rst_n; cell: {BUFX4 BUFX8 BUFX12}; numStages: 3; maxRouteLen: 150; maxCap: 0.3; skipDriver: true; skipLoad: true; Halo: 8; onRoute: true; } \ -net rst_n这里numStages: 3是因为几百个负载不可能靠一两级buffer撑住工具会分布成三级树。maxRouteLen: 150是为了让每条分支走线不要过长尽量分散到负载区域内部。cell里给了三个驱动档位工具在靠近驱动端的地方选大驱动力的BUFX12靠近负载端选BUFX4这种自动分级匹配正是手动插图时最费神的环节。跑完命令后我建议立刻做两件事。一是用dbGet top.insts.name REP_*确认插入了多少实例数量是不是符合预期二是打开版图看分布如果发现所有buffer都挤在一起、负载区域反而空着说明maxRouteLen设得太小工具切分得太密集把阈值调大一些重跑。3.2 场景二长距离跨模块信号加中继repeater另一个高频场景是跨模块长走线。floorplan阶段就会知道某些信号从IP A的端口出来要走到IP B的端口中间隔着好几百um甚至上千um。这种长走线即便没有cap和transition违规信号延迟也大得离谱setup timing基本没救必须中途插入中继buffer把长线切短。这种场景下maxRouteLen字段就是主角。我常用的参数addRepeaterByRule \ -rule Rule: longwire_rule { net: *; cell: {BUFX8 BUFX16}; numStages: 1; maxRouteLen: 300; skipDriver: true; skipLoad: true; Halo: 10; onRoute: false; } \ -net long_sig_*long_sig_*用通配符匹配一组跨模块信号。numStages: 1是因为每条线只需要一级中继不需要像复位信号那样摊成树形。maxRouteLen: 300的意思是从driver到load如果走线超过300um就在距离driver约300um的位置插一个buffer把线切成两段如果实际走线长度600um可能会插两级。这里特别说一下onRoute: false的用意。跨模块长线在布线阶段很可能已经绕得很远了如果onRoute开着工具会贴着既有走线路径找插入点插入位置会被布线绕行方向限制。而有些项目里这类信号更希望后续重新绕线这时关掉onRoute让工具以曼哈顿距离估算切分点反而更合理。这个选项没有绝对好坏取决于你的布线策略。3.3 场景三hold time violation用delay buffer修复第三个场景是修hold。setup slack足够、hold violation的路径上我们需要在数据路径上增加延迟。常见做法是插delay buffer如果库里专门有delayload cell或者串联普通buffer。修复前先跑report_timing -hold找到violation路径确认是哪条net负责数据路径上的信号传递然后在命令里指定这条netaddRepeaterByRule \ -rule Rule: hold_fix { net: data_mid_*; cell: {BUFX2 BUFX4}; numStages: 2; numRepeaters: 2; skipDriver: true; skipLoad: true; Halo: 5; } \ -net data_mid_*numStages: 2和numRepeaters: 2在这个场景下的区别要搞清楚。numStages指的是可以串几级buffer链numRepeaters限制的是整条net上一共最多插入多少个repeater。修hold时我通常两个都限制尤其用numRepeaters防止插入过多造成hold过度修复反而引入新的setup问题。还有个关键点修hold时插入的buffer会增加延迟但具体能增加多少工具估算基于当前寄生参数并不完全精确。所以我跑完这个命令后一定会重新提取寄生参数再做一次hold分析确认每条violation都收敛了。项目里就遇到过工具算出来hold修好了但重新提参后delay比预估小了0.1nsviolation回来了一部分所以千万别省提参这一步。4. 执行结果检查与效果评估4.1 如何确认repeater真的插对了命令执行完后最怕的就是“工具说success但实际啥也没插”。我的检查习惯分三步。第一步查实例。用dbGet按prefix过滤dbGet top.insts.name REP_* -l这能看到所有新增的repeater实例数量、在哪个hierarchical cell下面、连在哪个net上一目了然。如果返回空说明这条net压根没匹配到规则或者被其他属性挡住了。第二步查连接关系。选一个新增实例看它的输入输出dbGet [dbGet top.insts.name REP_0].instTerms.net.name -e确认buffer的输入连的是原net、输出连的是新net信号流方向正确。第三步回到GUI里看分布。输入gui_show -net $net_name高亮整条net和新增buffer检查插入位置是否合理有没有插在hard blockage里有没有把几个buffer挤在一堆。这三步都过了才算真正插入成功。4.2 插入后的时序与DRC复核插入repeater这件事本身不是目的最终要看时序和DRC是否改善。这里要掌握一个原则先提参再分析。setExtractRCOMode -engine postRoute extractRC report_drc -all report_timing -setup -max_paths 50 report_timing -hold -max_paths 50把setup和hold的top 50条路径都报一遍重点看之前violation的路径是否改善同时也要扫一眼原本clean的路径有没有被插入操作连累变差。尤其是hold前面说过数据路径上多串一级buffer延迟增加几十皮秒就有可能把原本干净的setup路径推爆。DRC方面重点看maxTransition和maxCap是否收敛。如果报告里还有残留violation先别急着再插一轮buffer用report_net -drv看是哪些pin还超标。很多时候是工具为了满足maxRouteLen把buffer插在了负载区域边缘最后一小段线还是太长。这种情况把rule里的maxRouteLen调小一点或者numStages加一级通常就能解决。4.3 与后续流程的衔接addRepeaterByRule插入的实例不是插完就不管了。它属于ECO性质的改动后面有几件收尾工作必须做。首先插入的buffer如果不在标准单元row上工具有时为了沿走线插入会把位置定在非row区域需要跑一遍legalize placement把这些实例吸到最近的合法位置。我一般用legalizePlacement -verbose跑完后重点看moving distance如果某些buffer被移动很远说明插入时Halo设得不合理或者附近根本没有空的row。其次net拓扑变了原有routing很可能需要调整。建议对涉及的net做增量ecoRouteecoRoute -net $net_name最后确认一切正常后saveDesign -design $design_name保存ECO后的设计版本。顺便用write_eco -eco_change或write_changes记录这次插buffer的改动清单方便debug和回滚。这里必须强调一个习惯insertion前后各存一份design。ADDRepeaterByRule跑完之后工具不会自动给你存备份一旦后面发现插入位置有问题想回滚没有备份就只能靠手动删buffer非常痛苦。我自己是每次跑大batch之前先save一份带时间戳的设计跑完再save一份哪怕只是临时调试一个几um的Halo参数。5. 常见问题与避坑经验5.1 命令执行了但什么都没插排查思路这是新人最容易卡住的问题命令没有任何报错log里甚至提示successfully finished结果回GUI一看net还是老样子。我的排查顺序是这样。先查net有没有dontTouch属性。Innovus尊重dontTouch标记如果net被set_dont_touch或setDoNotTouch标记了addRepeaterByRule默认不会碰它。查法dbGet top.nets.name $net_name.dontTouch返回1就是被锁了。这种时候必须跟前端确认能不能临时去掉标记或者改用别的修法不能自己偷偷解开否则后续LVS/formal会有麻烦。再查rule的匹配范围。如果rule文件里net字段写了具体的net名而你要处理的net名不匹配比如多了hierarchical前缀、大小写不一致命令会静默跳过。所以我建议rule里的net匹配尽量用通配符具体处理目标交给命令行的-net指定。最后查cell的可用性。-cell指定的单元库型号如果当前floorplan的lib库里没有这些cell或者这些cell被标记为dont use工具会找替代型号。实在找不到就直接放弃插入而且不一定报warning。所以命令行里多给几个同功能但不同驱动强度的cell能大幅提高插入成功率。5.2 cell选型与驱动能力匹配cell选型是addRepeaterByRule最容易“能用但不优”的环节。典型错误是只给一个大驱动力的cell比如只写BUFX20工具确实把所有插入点都用了BUFX20但靠近负载端的几级根本不需要那么强的驱动白白浪费面积和功耗还增加漏电。正确做法是在rule里给2到3个驱动档位让工具自己搭配。我常用的组合是X4、X8、X12具体看这条net的负载情况。负载很重的高扇出net起点给X12中间分支用X8末端用X4负载一般的长线X8配X4就够。另外一个容易忽略的点是inverter和buffer的选择。Cutting长线时用inverter切分在延迟上略优于buffer因为inverter本身延迟更小、输入电容更低。但inverter会翻转逻辑所以必须在rule里用preferInverter之类的选项或者手动控制cell列表。如果你不希望在逻辑上引入额外翻转直接只用buffer最稳妥至少不会出现功能问题。这个取舍没有标准答案看项目对timing面积功耗哪个更敏感。5.3 与dontTouch、blockage、partition的冲突除了一开始说的net dontTouch还有几种情况会让命令执行得“扭扭捏捏”或者插入位置奇奇怪怪。placement blockage是最常见的捣乱者。如果net走线穿过一个hard blockage区域而区域里没有可用的row工具找不到合法插入点就会把buffer往blockage边界挤结果可能在边界处形成局部congestion。遇到这种情况我一般先裁剪blockage范围留出一条走线通道或者改用-Halo更小的值让工具能在blockage边缘找到位置。partition boundary也会制造麻烦。如果这条net跨越了两个partition两个partition各自有独立的floorplan和power规划工具在跨boundary处插buffer有时会因为两侧的boundary pin没有定义好而失败。处理思路是先把net在partition内部可处理的部分处理好跨boundary的中继buffer单独手动加不要完全依赖规则自动处理。最后是macro旁边的placement keepout。memory等macro周围通常有keepout marginbuffer不能插在keepout里但工具计算插入点时如果不考虑keepout就会反复尝试失败。我在跑命令前会用setPlaceMode -place_global_keepout相关设置确认keepout区域定义完整避免白跑一轮。5.4 我踩过的坑清单经验都是用时间换的下面几条是我在项目里实际踩过的坑写出来给大家当参考。第一条别在高频时钟net上乱用。addRepeaterByRule处理普通信号毫无问题但时钟树net的buffer是CTS阶段精心平衡过的你在post-CTS阶段贸然往时钟路径里塞buffer会直接打乱各branch的延时差skew立刻爆炸。如果时钟树里某段确实需要修应该走clock tree eco的流程而不是用这个命令硬来。第二条rule里的maxRouteLen不能拍脑袋定。设得太小一条net被切成七八段插入几十个buffer面积和功耗起飞设得太大切了等于没切违规依旧。我的经验是先看report_net -net $net里的net length如果整条线500um违规那么把maxRouteLen设在250到300之间切两段通常就够了不要一上来就设100。第三条批量跑多条net前先在单条net上试。addRepeaterByRule虽然可以配合-net *一次处理整组net但我是从来不敢这么干的。因为不同net的负载分布、走线绕行差异巨大一份rule很难同时适配几十条net一次全跑完可能一半net修好了、另一半被插得乱七八糟。稳妥的做法是先在代表性net上试跑、检查分布、微调参数确认没问题后再用通配符批量处理。第四条也是最重要的一条跑完一定要重新提取寄生参数。命令执行后工具报告timing改善那是基于增量估算不是真实走线后的结果。只有extractRC重新提参再做一轮STA才能在signoff前真正确认这波操作有效。省略这一步后面timing signoff多半会让你把同样的事重做一遍。写在最后的实操体会玩这个命令这么长时间我最大的体会是addRepeaterByRule不是用来替代人做设计的它是用来把人从重复劳动里解放出来的。你依然需要理解什么是好的buffer分布、理解的cell驱动能力怎么匹配、理解长线为什么需要切分但具体到“这条net在哪切、切几刀、用什么cell切”让工具按规则去算比人肉估算高效得多也稳定得多。另外每做完一个项目我都会把用过的rule文件按场景分好类存档复位信号一份、长线中继一份、hold修复一份新项目遇到类似问题改几个net名和参数直接复用。这个习惯帮我省过很多次返工的时间强烈建议你也试试。