☰
Innovus ECO实战:setEcoMode配合ecoAddRepeater批量插buffer的5个关键步骤
2026/10/3 15:02:36 网站建设 项目流程

做数字后端的人都知道,ECO不是“会不会”的问题,而是“熟不熟”的问题。流片前最后一版netlist改功能、修timing、清DRC,全靠ECO这一套操作兜底。而在所有ECO动作里,用ecoAddRepeater批量加buffer(也就是常说的repeater insertion)是我个人用得最频繁、也最容易出状况的一个。很多人以为这命令就是“选个net、插个buffer”这么简单,实际跑起来才发现,setEcoMode如果没设对,ecoAddRepeater要么插不进去,要么插进去之后把布线搞成一团乱麻。

这篇文章就围绕“Innovus ECO中用setEcoMode配合ecoAddRepeater做批量buffer替换”这个组合,把我自己从定位问题、配置ECO环境、批量插入到跑完DRC和timing验证的完整流程,整理成5个关键步骤。无论你是刚接触Innovus的后端新人,还是已经被ECO折腾过几轮的老手,这份操作路径和踩坑记录应该都能直接参考。

1. 先搞清楚:什么场景下才需要“批量buffer替换”

1.1 ecoAddRepeater能干什么,不能干什么

ecoAddRepeater字面意思很直白:在ECO模式下给指定net插入repeater。Repeater通常是buffer或inverter,核心作用是把一根长net或者高扇出net分段,让每一段的电阻电容负载都落在驱动单元的承受范围内,从而修复max transition、max capacitance、max fanout这类DRV(Design Rule Violation),或者优化长互连的延时斜率。

但要注意,它不能干的事也很多:

  • 它不是用来做gate sizing的。想把BUFX4换成BUFX8,应该用ecoChangeCell,而不是删掉旧cell再插个新的。
  • 它不是用来改逻辑功能的。功能改动要靠ECO的netlist update流程,ecoAddRepeater只能加物理buffer,不会改变netlist逻辑。
  • 它默认不会去动clock tree。时钟树上的net有特殊属性,盲目插buffer会破坏CTS结果,后面我会单独讲。

很多人一上来就批量插buffer,结果发现插完以后timing报告里出现一堆新的违例,问题往往不是ecoAddRepeater本身,而是没有搞清楚这个命令的边界。

1.2 实践中最常见的三种触发场景

从我经历的项目看,批量buffer替换通常来自以下三个场景:

场景A:功能ECO之后的长net修复。
前端在流片前改了逻辑,后端用eco -read_file读入新netlist,ECO place和ECO route之后,某些新加的net跨越了大半个block,slew和cap直接爆表。这种时候不可能手工一条条去绕,批量插buffer是最快的收敛手段。

场景B:ECO route之后的DRV批量回修。
ECO route本身为了减少对原有布线的影响,往往会牺牲部分绕线质量,跑完report_design_rules,满屏的max transition和max cap违例。此时用ecoAddRepeater批量插repeater,配合ecoRoute重新走线,是标准操作。

场景C:cell替换引入的负载失衡。
有时候为了修timing,把drive能力弱的cell换成了强驱动cell,但强驱动cell的输出电容规格可能和原来不同,导致相邻net出现新的DRV。这种局部问题虽然量不大,但积累起来也需要批量处理。

1.3 为什么必须和setEcoMode一起用

setEcoMode是用来设定当前ECO操作全局行为的命令,可以理解成“接下来这段时间工具以什么模式工作”。ecoAddRepeater只是在这个模式下的一个具体动作。

两者关系可以类比成:setEcoMode是设置相机的手动模式和参数,ecoAddRepeater是按快门拍照。你按快门的姿势再标准,模式没设对,拍出来的照片一样是糊的。

具体来说,setEcoMode里的-routeEco、-physicalCellOnly、-honorDontTouch、-updateTiming这些选项,直接决定了ecoAddRepeater在执行时:

  • 允不允许工具顺手改走线;
  • 能不能动那些被标记了dontTouch的cell和net;
  • 插入buffer后要不要立刻更新timing数据库;
  • 插入的新cell要不要遵守blockage和region约束。

所以“批量buffer替换”从来不是一条命令的事,而是一个“环境配置 + 执行 + 验证”的完整流程。这也是我接下来要展开的5个关键步骤的核心逻辑。

2. setEcoMode参数逐项拆解:设错一个,后面全白干

2.1 -routeEco与-physicalCellOnly的真实含义

这两个参数是setEcoMode里最容易被误用的一对。先看官方语义:

  • -routeEco true|false:是否允许ECO流程在需要时对布线层进行修改。如果设false,所有ECO动作都只能在现有布线基础上做加法,不能重绕已经存在的金属线。
  • -physicalCellOnly true|false:是否只允许物理层面的改动(加cell、挪cell),而禁止netlist逻辑层面的改动。

很多人设完-routeEco true以后以为万事大吉,却忽略了-physicalCellOnly可能还是默认的false或者反之,导致工具在你不想让它改逻辑的时候改了logic,或者在你需要它自动修连接关系的时候它什么都不动。

我个人的经验是:如果只是单纯的buffer插入,不涉及netlist功能变化,那应该用:

setEcoMode -physicalCellOnly true -routeEco true

这个组合的含义是“只做物理改动,但如果需要,允许重新走线”。这样ecoAddRepeater插入的buffer能正常打上PG和连线,同时工具不会擅自动netlist逻辑。

如果连走线都不希望动得太厉害,比如conflict zone里已经有手动绕好的关键线,那就保守一点:

setEcoMode -physicalCellOnly true -routeEco false

代价是ecoAddRepeater之后,新增buffer的pin可能连不上,需要你额外手动ecoRoute,或者接受部分net的DRC不干净。

2.2 容易被忽略的辅助选项:honorDontTouch、updateTiming、fixDrc

除了上面两个,下面几个选项在批量场景里非常关键:

  • -honorDontTouch true:默认情况下工具会尊重cell和net上的dontTouch属性。批量选取net的时候,如果你用get_nets -hier抓到的net里混了dontTouch标记,ecoAddRepeater会全部跳过并报warning。反过来,如果你确实想强制插入,可以在ecoAddRepeater上加-honorDontTouch false,但这非常危险,尤其不能用在clock net和dontTouch的保护cell上。
  • -updateTiming true|false:ECO过程中是否增量更新timing。批量插几百个buffer的时候,如果每次插入都实时更新全量timing,速度会非常慢;但完全关掉,最后你又得额外花时间重新提取RC和跑timing。实际项目里我会在批量阶段关掉,最后统一更新,前提是net数量可控。
  • -fixDrc true|false:ECO结束以后是否自动调用工具内部的DRC修复。这个选项和省心的程度成正比,但也会让工具多花不少runtime。如果batch里插buffer的位置比较紧张,建议打开,让工具在插入阶段就规避一部分short和spacing问题。

这些选项没有绝对正确的组合,完全取决于你当前design的状态和你希望ECO对现状的扰动程度。但有一点是共通的:在批量执行之前,先拿两三根net做单条实验,把选项组合的预期行为确认好,再全量跑。

2.3 一套可复用的ECO环境配置参考

以我自己用的配置为例(不涉及具体工艺,只讲套路):

# 批量buffer插入前的ECO环境 setEcoMode \ -physicalCellOnly true \ -routeEco true \ -honorDontTouch true \ -updateTiming false \ -fixDrc true \ -allowFillerRemoval true \ -maxChanges 10000

几个细节说明:

  • -allowFillerRemoval true:ECO插入新cell需要空间,如果filler挡路,工具可以把filler挪走。这个在density高的block里几乎是必开的。
  • -maxChanges 10000:限制单次ECO最大改动量,防止工具发疯全盘重排。具体数值根据block规模调整。
  • 如果当前design还在早期,place还不太紧张,-updateTiming true也可以开,这样每次插入后工具会维护增量时序数据,最后的验证负担小一些。

还有一个容易被忽略的动作:跑ECO之前保存一个独立的database副本。很多工程师习惯直接在working database上操作,结果ECO出问题想回退,只能从一个小时前的floorplan重新来。我的做法是ECO前先saveDesign eco_before_buffer_insert,跑完确认无误再删。

3. ecoAddRepeater批量替换5个关键步骤实操

3.1 第一步:锁定目标net清单并分级

这一步的核心是搞清楚“到底哪些net需要插buffer”,以及“这些net分别属于什么优先级”。

通常我这样抓net:

# 抓所有违反max transition / max capacitance的driver pin set vio_nets [get_nets -of_objects [get_pins -hier -filter "max_transition_violation != 0"]] append_to_collection vio_nets [get_nets -of_objects [get_pins -hier -filter "max_capacitance_violation != 0"]]

如果你已经有timing报告或者report_design_rules输出文件,更稳妥的方法是写脚本从report里解析net名,因为Innovus的collection接口在不同版本里filter字段名略有差异,直接照抄网上老脚本很容易抓空。

拿到net清单后,不要急着全量插入,先分级:

  • P0级:clock net、dontTouch net、macro pin直连net。这些要么不能动、要么动了要格外小心,建议排除或者单独手工处理。
  • P1级:长距离跨block的data net,slew/cap严重违例,优先处理。
  • P2级:轻度违例、边界情况的net,可以批量处理但需要更保守的buffer尺寸。

分级的目的很简单:避免批量操作把风险放大。尤其P0级,一旦在clock net上误插了buffer,CTS全部作废,那可不是重跑一次CTS的问题,是整轮ECO都要推翻的问题。

3.2 第二步:组好setEcoMode环境并验证

用上一节的配置把ECO模式设定好,然后拿第2~3根net做试验:

setEcoMode -physicalCellOnly true -routeEco true -honorDontTouch true -updateTiming false -fixDrc true ecoAddRepeater -cell {BUFX4 BUFX8} -net $test_net -numRepeaters 1 -prefix ECO_BUF

跑完以后不要急着看结果,先做三件事:

  1. ecoPlace看新cell能不能合法放下去;
  2. ecoRoute -modifyOnly看pin能不能连上;
  3. report_design_rules -check max_transition,max_capacitance看这个net的DRV有没有改善。

这三件事确认没问题,再决定全量跑。千万不要跳过这个试验步骤,我见过太多人全量跑完才发现buffer尺寸选小了,slew一点都没改善,白耗几个小时。

3.3 第三步:构造ecoAddRepeater命令

ecoAddRepeater的完整语法在不同版本里略有区别,但核心参数基本稳定。我常用的形式是:

ecoAddRepeater \ -cell {BUFX4 BUFX8 BUFX12} \ -net $net_list \ -numRepeaters 1 \ -prefix ECO_RPT_ \ -skipPinAssignment

逐个解释我为什么这么写:

  • -cell {BUFX4 BUFX8 BUFX12}:候选buffer列表。工具会根据负载情况自动选一个合适的尺寸。列表里最好放2~3个尺寸等级,不要只给一个,否则工具没有选择空间,可能插进去一个过大的buffer造成面积浪费,或者过小的buffer根本没效果。
  • -net:可以接单个net名,也可以接一个collection。批量场景里,接collection是常规操作。
  • -numRepeaters 1:每个net默认插1个repeater。如果你的net特别长,需要插多个,可以设成2或3,但注意插入数量越多,对布局资源的消耗越大,后续congestion风险越高。
  • -insertionMode:这个参数控制repeater插入位置的计算策略。网上很多老帖子给了具体模式编号,但不同版本支持的数值不一样,我的建议是先用工具默认值,确认有效再考虑微调,别照抄旧版本的数字。
  • -prefix ECO_RPT_:给新增cell统一加前缀,方便之后get_cells ECO_RPT_*快速选中和过滤,也能避免和设计中已有cell重名。选cell的时候Innovus支持通配和层级匹配,有了统一前缀,后面get_cells -hier ECO_RPT_*一下就能把所有ECO buffer抓出来,省很多事。
  • -skipPinAssignment:跳过插入时的pin分配,让工具在place阶段统一处理。批量场景下这个选项能省不少runtime。

还有两个经常用到的选项:

  • -honorDontTouch false:默认true。除非确认net安全,否则不要关。
  • -loc {x y}:手动指定插入位置。单条net调试时好用,批量场景基本不用。

3.4 第四步:批量化执行的脚本写法

批量化的核心不是把net名堆在一条命令里,而是做好三件事:筛选、循环、留日志。

推荐用文件驱动的脚本结构。先把第一步筛好的net清单写成文本文件,每行一个net名,然后脚本读入并循环执行:

set fp [open "eco_buf_nets.list" r] set net_list {} while {[gets $fp line] >= 0} { if {[string trim $line] eq ""} { continue } # 按名字精确抓net,避免通配符误抓 set n [get_nets -quiet [string trim $line]] if {$n ne ""} { lappend net_list $n } else { puts "WARNING: net $line not found, skipped" } } close $fp set num 0 foreach n $net_list { # 排除dontTouch和clock net(不同版本接口有差异,用dbGet或filter实现) if {[dbGet [dbGet -p top.nets.name $n].isClockNet -quiet]} { continue } if {[get_net_attribute $n dont_touch] == "true"} { continue } ecoAddRepeater -cell {BUFX4 BUFX8} -net $n -numRepeaters 1 -prefix ECO_RPT_ incr num # 每50根net打一次进度 if {[expr {$num % 50}] == 0} { puts "INFO: processed $num nets" } } puts "INFO: total processed $num nets"

这段脚本有几个细节值得注意:

  • 用文件驱动而不是直接在内存collection上循环,好处是可追溯、可重跑。哪根net没插入、哪根net找不到,都有log可查。
  • 每次循环都检查isClockNet和dont_touch,这是第二道保险。即便第一步已经筛过,保不齐中间有net属性被改过。
  • 每50根net打印一次进度,批量几千根net的时候,你能知道脚本跑到哪里了,不会干等。
  • 如果你担心一次性跑完出问题不好回退,可以每500根netsaveDesign一次,出问题从最近checkpoint重来,而不是整个ECO推倒。

另外,关于并行:Innovus的ECO建议在同一个session里串行执行,不要自己开多线程同时跑ecoAddRepeater。工具内部的database操作不是为并发设计的,多线程同时写database会出各种诡异问题,比如cell重复、net断连。真要并行,按block或者partition拆分,再用merge流程合并,那是另一个层面的话题了。

3.5 第五步:ECO后验证与收尾

批量插入buffer以后,并不意味着大功告成。以下几步一个都不能少:

1. 布局合法化。

ecoPlace -coarse -noLegalize legalizePlacement

如果插入数量不大,直接legalizePlacement就能搞定。插得多了,建议先ecoPlace -coarse让工具把新增cell放到大致位置,再legalize,速度和成功率都更好。

2. 布线ECO。

# 只重绕新插入buffer相关的net,避免动到无关走线 ecoRoute -modifyOnly [get_nets ECO_RPT_*]

-modifyOnly表示只重绕和新cell相关的net,避免动到无关的既有走线。这个参数在ECO里特别重要,因为ECO的基本原则就是最小扰动——你没有动到的net,不应该因为这次操作被重新绕线,否则回归验证的成本会无限放大。

3. Filler补齐。

ECO插buffer会挪走filler,ECO结束后需要重新加filler:

addFiller -cell {FILLER1 FILLER2 FILLER4} -prefix ECO_FILLER

注意这里和普通physical signoff时的addFiller是一样的,但加完以后一定要检查PG connection。尤其如果工艺里有metal filler或者decap filler的区分,别加错类型。用dbGet按名字筛选标准单元、查看PG term的时候,要养成先确认库单元属性的习惯,防止把不带PG的decap cell当成普通filler用。

4. DRC检查。

check_design -checks all_drc verify_drc -limit 1000

批量插buffer最容易在插入点附近产生short和spacing违例,特别是density高的区域。如果DRC爆出来一堆错,先别慌,按区域聚类,大概率是某几个buffer插到了pin access不好的位置,手工挪一下或者用ecoPlace -modifyOnly微调就能解决。

5. Timing和DRV回归。

extractRC report_design_rules -check max_transition,max_capacitance,fanout \ -violation_only -format text report_timing -slack_max 0.0 -net -format full

重点确认两件事:一是原本要修的DRV确实修掉了,二是没有产生新的严重timing违例。批量插buffer修了远端slew,往往会让近端的load变大、arrival变差,所以timing回归是必须的。

到这里,一轮标准的“setEcoMode + ecoAddRepeater批量buffer替换”才算真正落地。

4. 实测踩坑:批量buffer替换的4个高频问题与排查链路

4.1 ecoAddRepeater报了“no location found”却找不到原因

现象:ecoAddRepeater执行以后,log里一堆No legal location found for repeater的warning,net也没修好。

排查链路:

先看是不是blockage挡路。用:

report_placement_blockages -box ...

把目标net附近的placement blockage打出来。ECO插buffer的合法位置必须避开blockage、halo、region约束。高density区域里,filler和已有cell把空间占满了,自然没有地方放新cell。

再看是不是-insertionMode或者net的几何形状导致工具找不到一个好的插点。长net如果走线路径上有大量congestion,工具计算出来的插入点全在非法区域,也会报这个错。

我的处理习惯:先打开GUI,highlight -net $net_name,眼睛看一下这根net的形状和周围拥挤程度。如果是congestion问题,把-cell列表里加一个尺寸更小的buffer(比如BUFX1),或者用ecoAddRepeater -loc手工指定一个空闲位置,往往就通了。

4.2 插完buffer以后DRC一片红

现象:batch跑完,verify_drc报出几百个short和min spacing违例,集中在ECO buffer附近。

排查链路:

先用dbGet或者GUI定位DRC的具体坐标,看是不是所有违例都紧挨着新插入的buffer。如果是,十有八九是插入时工具没有考虑routing blockage或者pin access,buffer的A/Y pin落在了被阻挡的金属层上。

解决办法有三条路:

  1. 检查是不是setEcoMode -routeEco false导致的。这种情况下新cell的pin根本没有走线资源,必须打开routeEco并重新ecoRoute。
  2. 检查blockage是否把buffer周围的routing track全挡住了。设计里经常有routing blockage保护关键走线,ECO插buffer前要先确认目标区域routing blockage的分布。
  3. 手动把问题buffer挪一挪,再用ecoRoute -modifyOnly重新绕那一小片。

DRC问题不能靠硬扛,一定要从根上找原因。批量修DRC里的经验是:先抓一个点分析清楚,再去看分布规律,最后才是批量修。一上来就批量ecoRoute,大概率把原来的好线也重绕了,回归验证会很痛苦。

4.3 重复ECO后buffer重叠、cell重复

现象:同一个block里跑了好几轮ECO,每次插的buffer都带不同前缀,结果某片区域cell密度爆表、重叠严重。

原因:上一轮ecoAddRepeater插入的buffer,在下一轮ECO里没有被正确识别和复用。很多人直接用新的-prefix插新buffer,旧buffer还留在原处,越积越多。

正确的做法:

下一轮ECO开始前,先确认上一轮的ECO buffer哪些还有用。用:

get_cells ECO_RPT_*

把所有ECO buffer抓出来,逐个检查timing和DRC。已经没有用的buffer用:

ecoDeleteRepeater -cell $unused_eco_buf

删掉,再重新插。如果只是尺寸不合适(BUFX4要换成BUFX8),不要删了重插,用:

ecoChangeCell -inst $eco_buf -cell BUFX8

这样保留物理位置,只换master,对周边影响最小。

这个坑的本质是:ECO操作要像代码提交一样有可追溯性。每次ECO的改动记录、cell列表都要留档,否则几轮迭代下来,设计中堆满了历史遗留的ECO cell,谁都说不清楚哪些该留哪些该删。

4.4 插完buffer timing反而更差

现象:DRV修掉了,但setup或者hold的slack比ECO之前更差。

原因:buffer把net分成两段,第一段的负载虽然小了,但buffer本身的cell delay加进了路径;第二段如果buffer选小了,驱不动后面一大片负载,slew反而更差。另外,插buffer带来的绕线长度变化也会改变RC,net delay不一定变小。

排查链路:

先report_timing -from $driver -to $sink -net -delay看每一段的delay分解。重点看buffer input到output的cell delay,以及前后两段net的RC delay。

如果是buffer cell delay占了太大比重,说明buffer尺寸偏大或偏小。尺寸偏大,输入cap大,前一段delay变大;尺寸偏小,输出驱动不够,后一段slew差。

处理方式:

  • 把-cell列表换成更合适的尺寸组合,重新跑;
  • 减少-numRepeaters,一个buffer能解决的就不要插两个;
  • 如果某个特定net反复出问题,单独用-loc手工指定插入位置,放在net的中点附近(物理中点,不是绕线中点),通常delay分配最均衡。

还有一个常被忽略的点:ECO buffer的VT类型。默认ecoAddRepeater会选库里的标准VT buffer,如果是在关键路径上,可以考虑用HVT或者LVT的buffer来微调delay。用-useLibCell可以指定具体cell,比如:

ecoAddRepeater -cell BUFX8_LVT -net $n -numRepeaters 1 -useLibCell BUFX8_LVT

但这种做法会让ECO buffer列表复杂化,建议只在关键路径的局部net上手工用。

5. 从单次操作到可复用的ECO流程

5.1 一套可以直接落地的脚本框架

把前面5个步骤串起来,我一般会组织成三个文件:

文件1:eco_buf_config.tcl,放setEcoMode和公共变量:

set ECO_BUF_CELLS {BUFX1 BUFX4 BUFX8} set ECO_BUF_PREFIX ECO_RPT_ set ECO_NET_LIST eco_buf_nets.list set ECO_BEFORE_DB eco_before_buffer_insert setEcoMode -physicalCellOnly true -routeEco true \ -honorDontTouch true -updateTiming false \ -fixDrc true -allowFillerRemoval true

文件2:eco_buf_insert.tcl,放循环插入逻辑(就是前面那段脚本,略作完善)。

文件3:eco_buf_verify.tcl,放验证逻辑:

ecoPlace -coarse -noLegalize legalizePlacement ecoRoute -modifyOnly [get_nets ${ECO_BUF_PREFIX}*] addFiller -cell {FILLER1 FILLER2 FILLER4} -prefix ECO_FILLER check_design -checks all_drc report_design_rules -check max_transition,max_capacitance -violation_only report_timing -slack_max 0.0

执行的时候:

source eco_buf_config.tcl source eco_buf_insert.tcl source eco_buf_verify.tcl

这套框架的好处是:第二次、第三次用的时候,只需要更新net list和cell列表,不用重写脚本。而且每个文件职责清晰,出了问题能快速定位是哪一步的锅。

5.2 和周边ECO命令的搭配

批量buffer替换很少是孤立操作,它经常和以下命令一起出现:

  • ecoChangeCell:替换已有cell尺寸,适用于gate sizing场景。
  • ecoDeleteRepeater:删除不再需要的repeater,配合addRepeater使用。
  • ecoPlace -modifyOnly:只挪动指定cell,保持其它cell不动。
  • ecoRoute -modifyOnly:只重绕指定net。
  • addFiller -eco:ECO后的filler补齐。

一个常见的工作流是:先用ecoChangeCell把关键路径上的cell尺寸调整一遍,再用ecoAddRepeater修剩余的长net DRV,最后ecoRoute -modifyOnly + addFiller收尾。这套组合拳能覆盖后端ECO里绝大部分的timing和DRV修复需求。

5.3 我的几点个人体会

做ECO这么多年,最大的体会是:ECO不是炫技,而是最小代价解决问题。每次做ECO之前,我都会问自己三个问题:这次改动真的有必要吗?改动范围能不能更小?如果出了问题,能不能快速回退?

批量buffer替换看上去是个体力活,但真正拉开工程师差距的,恰恰是setEcoMode里那几个看似不起眼的选项,以及你对net清单的筛选和分级能力。把每一步都留好日志、存好checkpoint,比任何高级技巧都管用。

还有一个小技巧:批量插入完成后,记得用setDontTouch把已经确认没问题的ECO buffer保护起来,防止后续其它ECO操作把这些buffer又挪走或者替换掉,造成二次污染。这一步很多人不做,等到下一轮ECO跑完才发现上一轮的buffer全被动了,那种返工真的是欲哭无泪。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询