ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Innovus ECO修复时序违例:ecoAddRepeater插Buffer原理与实战

Innovus ECO修复时序违例:ecoAddRepeater插Buffer原理与实战 做数字后端这些年ECO是最常打交道的工作之一。跑到项目后期时序报告里往往就那么几条DRV违例max_transition挂在几根长线net上你不可能为了两三个点去重新跑一遍place和CTS。这时候在Innovus里用ecoAddRepeater插几颗buffer或者inverter配合ecoRoute局部绕线是最快也最稳妥的修法。这命令我从Encounter时代用到现在一路跟到Innovus不敢说每个版本细节都背下来但踩过的坑和沉淀下来的套路是真不少。这篇文章就把ecoAddRepeater从原理、参数到实战流程完整拆一遍新手能照着操作老手也能看看有没有自己忽略的细节。这里说的buffer是标准单元库里的驱动单元不是PCIe链路里那个做跨时钟域补偿的elastic buffer俩东西完全不是一回事。我们要做的是在数字后端物理实现阶段通过ECO方式在已有版图上插入buffer或inverter把过长的net拆成两段降低每段的RC负载从而修掉transition、capcitance、fanout违例或者在某些场景下修hold。全文涉及的流程是Innovus环境下的标准ECO操作适合刚接触物理设计ECO的新人也适合已经在用但想梳理得更系统的后端工程师。1. 搞懂ecoAddRepeater之前先明白你为什么要插Buffer1.1 ECO里修时序绕不开的三个场景ecoAddRepeater插buffer/inverter绝大多数情况下是为了处理下面三类问题。第一类也是最常见的DRV违例。DRV是指Design Rule Violation在时序报告里通常表现为max_transition、max_capacitance、max_fanout超限。一根net从驱动端出来连了很远、很多负载线本身带来的电阻电容太大或者负载总电容超出了驱动单元的驱动能力信号沿就变得很慢transition变差。插入buffer之后等于在net中间加了一个中继站前半段net只需要驱动buffer的输入pin电容骤然变小后半段由buffer自己驱动它就是专门干这个的驱动能力强transition自然就回来了。第二类是修hold违例。setup不够通常靠优化逻辑、挪单元、换驱动但hold不够尤其是时钟偏斜没给够余量的时候常见做法就是在数据路径上插入delay buffer人为增加数据到达时间满足hold检查。ecoAddRepeater插出来的buffer自带延迟比辛辛苦苦抻线实在多了。第三类是功能ECO。有时候后端跑到后期前端突然要加一个测试mux或者门控逻辑需要把某个信号复制成两路或者要调整某个逻辑的极性。这类小改动不需要重新综合直接在Innovus里插inverter pair或者buffer就能完成逻辑重建。ecoAddRepeater在这里扮演的角色很纯粹给你一个在当前版图合法位置插入标准单元的手段。1.2 为什么是ecoAddRepeater而不是重新布局布线我见过不少工程师一看到时序违例就想重新跑flow这是没必要的。ECO的核心价值就两个字局部。重新布局布线是全局行为跑一遍好几个小时而且会把之前已经收敛的时序、DRC、clock skew全部打乱属于杀鸡用牛刀。而ecoAddRepeater只动你指定的那根net和附近一小块区域改动面小风险可控跑一轮只要几分钟甚至几秒。选它还有个重要原因工具内部会自动处理net的断开和重建。插入buffer之后你原来那根net从拓扑上被分成了两段buffer的输入pin接管前半段buffer的输出pin驱动后半段原来的driver不再直接驱动全部负载。手动做这些连接非常容易出错而ecoAddRepeater把这些底层操作封装好了你只需要告诉它插什么cell、插在哪、插到哪根net上。另外在修DRV的场景里常见的替代思路是直接换大驱动cell比如把BUF_X2换成BUF_X8。但换大驱动有一个副作用输入电容变大上游的transition可能会变差等于把问题往前甩。而插buffer是把负载拆开每段net的RC都变小是从拓扑层面解决问题效果更彻底。我自己的经验是当net负载确实很大、扇出很多的时候插buffer比单纯加大驱动有效得多。2. 命令参数逐个拆解别等报错才知道2.1 最小可用示例一句话插出一个Buffer先看一个最基础的调用在Innovus命令窗口里输入这么一行ecoAddRepeater -cell BUF_X8 -net clk_gate_net_01 -location {40.5 66.2}含义很直白在坐标40.5, 66.2这个位置放置一颗名称为BUF_X8的buffer单元把它插到clk_gate_net_01这根net中间自动断开原netbuffer输入接前半段buffer输出驱动后半段。执行完成后命令会返回新生成的instance名字比如ECO_BUF_1。注意-cell后面填的必须是当前lib里存在的cell名字不同工艺库命名差异很大有的叫BUF_X8有的叫BUFX8还有带后缀的。动手之前先查一下库get_db lib_cells -if {.name ~ BUF*}把库里可用的buffer列出来再从中挑一个合适的驱动强度。别凭记忆敲名字敲错了工具直接报错。2.2 关键参数说明与选型思路ecoAddRepeater最常用的选项其实不多我把它们整理成一张表选项作用使用建议-cell指定要插入的库单元名必须和库里名字完全一致建议先用get_db lib_cells确认-net指定要插入的net名事先确认net确实存在且不在clock树上-location新单元的物理坐标格式是{x y}坐标单位跟工艺库一致一般是um或nm-inverterPair成对插入两个inverter在需要修极性、但库里buffer驱动不够时使用-prefix新instance的命名前缀建议统一加ECO_后面做比对和review时一目了然-noDRC插入时跳过DRC检查批量操作时能省时间但最后必须统一跑DRC关于-location坐标不是随便填的。你要保证这个位置在当前floorplan范围内没有被hard macro挡住不在placement blockage里并且附近有足够的绕线资源。工具会尽力合法化但如果你指定的位置实在太离谱比如放在macro正中间它会报错或者把cell放到一个很奇怪的地方。我的习惯是先用GUI把目标net高亮report_net -physical看一下线长和连接关系再手动估算一个差不多的坐标。2.3 Buffer还是Inverter怎么选这个选择看起来简单实际操作里有讲究。库里面的inverter往往比同尺寸buffer面积更小、驱动能力更强因为inverter本身就是一级反相器而buffer相当于两级inverter串联。所以有的工艺库里用inverter pair替代buffer能省面积、还能获得更强的驱动。但inverter有个绕不开的问题它翻转逻辑极性。如果你插入一颗invnet后半段的逻辑值就被反相了功能直接错掉。解决办法是成对插入插两颗inv反两次等于不反逻辑极性不变这就是-inverterPair的用途。工具会自动在指定位置附近生成一对inv前一个的输出接后一个的输入整体对外表现和buffer一样但占的是inverter的面积和驱动特性。我的建议是修DRV、修hold这类常规时序修复直接上buffer最省心逻辑上不会出幺蛾子如果项目对面积扣得很细或者库里的buffer驱动档位不够那就用-inverterPair。另外如果ECO的意图就是翻转某个信号的极性比如功能ECO里需要把enable变成enable_b那直接用单颗inverter就行不需要成对。前提是你清楚上下游逻辑真的需要这个反相。3. 实操全流程从一个时序违例到完成修复3.1 第一步定位到目标Net并读取关键坐标说一个我在项目里实际处理过的场景。某条数据通路一根叫data_bus_07[3]的net从寄存器U_FF_A的Q端出来驱动了三个相距很远的寄存器D端时序报告上这跟net的max_transition违例很刺眼。修复思路就是在这根net中间插一颗buffer把三个负载拆成前后两部分。先在Innovus里拿到这根net的driver和load pinset netName {data_bus_07[3]} set driverPin [get_pins -of_object [get_nets $netName] \ -filter direction output] set loadPins [get_pins -of_object [get_nets $netName] \ -filter direction input]注意get_pins返回的可能不止一个pin所以driverPin要用lindex取第一个。接着读取坐标我习惯分别取driver和第一个load的坐标然后计算中点set drvXY [get_property [lindex $driverPin 0] loc] set ldXY [get_property [lindex $loadPins 0] loc] set midX [expr {([lindex $drvXY 0] [lindex $ldXY 0]) / 2.0}] set midY [expr {([lindex $drvXY 1] [lindex $ldXY 1]) / 2.0}]这里有个细节loc属性返回的是坐标list格式是{x y}用lindex取值。我特意在expr里写了2.0而不是2避免整数除法把坐标截断。如果坐标单位是um截断零点几个um问题不大如果单位是nm整数除法丢掉的精度就有点可惜了。3.2 第二步确定插入位置与Buffer规格中点坐标算出来之后别急着插。先看看这个位置附近有没有macro、blockage、或者已经被其他instance占了。简单粗暴的办法是在GUI里高亮这个坐标肉眼扫一眼稳妥一点的办法是用legalize_eco或者placement legality检查命令验证一下。实际项目里我还会再看一眼net的物理连接report_net -physical $netName确认负载的分布是不是往某一个方向集中。如果三个负载分布得很散几何中点可能不是最优选择。更合理的做法是看哪个负载离driver最远、贡献的RC最大把插入点放在driver和这个最远负载之间。buffer的主要任务是替driver分担最恶劣的那部分负载不是平均主义。这个逻辑类似快递分拣不是所有包裹都拉回总站再分发而是在中途设一个站点把最远的几个小区先分流掉。Buffer规格的选择我一般这么定如果原net的负载电容报告里写的是0.3pF上下插一颗BUF_X4到BUF_X8足够如果负载在0.5pF以上直接上BUF_X12或者更强。拿不准的时候先插一颗X8跑完时序看结果再调整ECO迭代的成本很低没必要一步到位赌一个完美值。另外如果原driver本身驱动能力很弱比如是某些特殊cell的输出那buffer驱动能力选太强反而会让前段net的输入电容增加需要注意平衡。3.3 第三步执行ecoAddRepeater与后续布线坐标和cell都定了执行插入ecoAddRepeater -cell BUF_X8 -net $netName \ -location [list $midX $midY] \ -prefix ECO_DRV命令执行完工具会返回一个新instance的名字比如ECO_DRV_1。这就是你刚插进去的那颗buffer。此时它的物理位置已经摆好逻辑连接也基本建立但绕线还没做net还是断开的。下一步必须跑ECO routeecoRoute -noEco true这个命令会把自己标记为ECO的未连接pin、断开的net重新连好。有的版本里也能用globalDetailRoute -eco效果类似。跑完之后最好再确认一下这条net的连接关系report_net -connections $netName看看是不是driver、buffer输入、buffer输出、三个load pin都在正确的net上了。这一步别省我见过不止一次因为插完buffer忘了route结果ECO的cell飘在版图上时序报告看起来变好了但实际物理上根本没连通的尴尬情况。4. 插入Buffer之后别忘了这些验证和善后4.1 ecoRoute布线之后的DRC检查ecoAddRepeater插进去的cell是新的物理对象会改变局部congestion和绕线资源分布。即便ecoRoute能把net连上也不能保证DRC干净。常见的坑是short——新cell的pin和旁边已有cell的pin靠得太近或者绕线时穿过了一些被block的区域。所以布线之后我会习惯性跑一遍DRC检查verify_drc跑出来如果有违例先看是不是新插入的buffer附近。如果是short优先考虑换一颗更小的cell或者把插入点往空旷区域挪一点。如果是有spacing违例试试调整location坐标让buffer和周边保持足够距离。ECO本身就是微创手术一旦动了位置就要承受DRC出问题后的连锁调整所以插入点选择越稳妥后续返工越少。4.2 时序复验与负载变化确认DRC干净了接下来是关键一步重新提取RC并跑时序。ECO之前那个transition违例是不是真的没了不能凭感觉要看数据。update_timing report_timing -from [get_pins U_FF_A/Q] -to [get_pins U_FF_B/D]看两点一是原来违例的max_transition、max_capacitance是否消除二是路径delay有没有被buffer的延迟影响会不会把原来的setup/hold弄坏。插buffer相当于在数据路径上加了额外延迟对hold是有帮助的但可能吃掉setup的余量。我在项目里遇到过修完DRV结果某条路径setup反而违例的情况原因就是buffer延迟叠加在关键路径上。这种时候需要权衡如果DRV的违例值很小buffer可以选低驱动强度的档位延迟更小如果DRV违例很严重那不得不接受它在setup上的代价。另外插入buffer之后原driver的负载确实变小了但buffer输入pin本身也有电容前段net的总负载等于原来的前半段线电容加buffer输入电容。所以理论上原driver的transition应该改善但改善幅度取决于插入点离driver多远。插得越靠driver前段net越短载荷越轻但后段net就越长buffer的压力越大。这个折中点是ECO修DRV的核心没有公式能算完美值靠经验和对版图的直觉。4.3 保存与版本管理的习惯ECO操作做完、验证通过最后一步是保存。但保存之前我强烈建议把这次ECO的改动记录整理清楚。用-prefix前缀就是一个好习惯比如所有ecoAddRepeater新插入的cell都以ECO_DRV开头后续做report_eco -instances或者diff两个版本之间的instance列表时一眼就能看出哪些是这次新加的。我还习惯在ECO结束后把改动集合导出到一个文本文件里report_eco -instances eco_added_instances.rpt report_eco -nets eco_modified_nets.rpt这样如果后面发现有问题翻记录就知道动了什么。尤其是在多人协作的流程里你插的buffer可能在别人的模块边界附近没有记录就说不清楚。另外保存前跑一遍before_save_check有些版本会检查数据库的一致性提前暴露问题。版本管理上ECO前后的数据库要分开存出了问题能快速回滚。这里顺便说一个查pg连接的小技巧。检查新插buffer的电源地是否连对时我经常用类似下面这种命令选中特定pg termget_pins -of_object [get_cells ECO_DRV_1] -filter name biasnwbiasnw这类名字在很多工艺库里是衬底或阱连接端子ECO插入的cell如果pg连接没处理好终端电阻、闩锁风险都会跟着来。虽然大多数标准单元在place时pg会自动接好但ECO这种半路插入的操作多检查一步总没坏处。5. 高频问题与排查实录附避坑清单5.1 几种典型的“插了不如不插”的情况我踩过的坑里最典型的一种是插完buffer之后transition不但没改善反而更差了。排查下来发现插入点选得太靠driverbuffer输入pin变成了driver的前段负载而后段长线仍然由这颗驱动能力不足的小buffer撑着等于把压力从driver转移给了更弱的buffer。解决方案是调整插入位置往负载方向移或者换更大驱动强度的cell。另一种情况是选错了cell。比如net负载明明很大却插了一颗BUF_X1自身驱动能力不足后段net照样违例。这个问题的根源是没看时序报告里的cap值就随手选cell。我的习惯是ECO前先跑report_net -physical把net的total cap和transition列出来再对照库里各档buffer的驱动能力做选择。一般X4起步、X8最常见只有在负载很小或者只是修一点hold的时候才用X1/X2。还有一种是重复修复。一根net已经插了一颗buffer修完后transition差一点点于是又插一颗结果负载被拆得太碎每段之间都有延迟累积反而引入了新的setup问题。这种情况要有全局观宁可用一颗驱动能力稍强的buffer一步到位也不要拆成多级。5.2 命令报错的常见原因与处理ecoAddRepeater报错最常见的几类我整理成一张表方便对照报错或异常可能原因处理方法找不到cell名字-cell填的名字和库里不一致用get_db lib_cells查准确名字找不到合法插入位置坐标在macro内部或blockage上换坐标或打开GUI确认位置插入后电源地悬空库单元的pg连接逻辑特殊用pin查询命令检查pg手动补齐连接布线后出现short插入点太拥挤换更小cell或挪位置net连接断开但没route忘了跑ecoRoute补跑ecoRoute -noEco true时钟树上插buffer后skew变差不该在时钟树上随意插入走专门clock ECO流程或回退这里想特别说一句“时钟树”。CTS做完之后时钟树的拓扑是经过平衡的你手动在树中间插一颗buffer等于改变了skew结构轻则skew恶化重则直接造成时序违例。除非你做的就是clock ECO否则不要在clock net上使用ecoAddRepeater。判断方法很简单先report_net看一下net属性或者get_property确认它不在clock tree上再动手。5.3 经验向的几条避坑建议第一改动前先备份。ECO是微创手术但也是有风险的数据库备份是最基本的保险。第二批量插入时建议用脚本循环逐条敲命令容易漏。比如你要在同一根net上插多颗buffer可以用foreach把坐标list和cell列表串起来执行。第三版本差异要留意。Innovus不同版本对ecoAddRepeater的支持细节有细微差别某些选项在老版本里可能不识别动手前养成敲ecoAddRepeater -help的习惯比翻手册快。最后再分享一个小技巧如果一条net的扇出特别大比如几十个负载先别急着插一颗buffer可以先把负载分群在每群的几何中心插一颗形成两级结构。这样比单点插入的收敛效果稳定得多后续调试时也能按群定位问题。做ECO和做设计一样先想清楚拓扑再动手敲命令。
返回列表