修復(fù):ecoAddRepeater插入buffer/inverter指南)
做數(shù)字后端的人應(yīng)該都有這種經(jīng)歷整個(gè)chip的時(shí)序已經(jīng)收斂得差不多了floorplan也基本穩(wěn)定就差幾條長net的max_cap違例或者某個(gè)antenna violation卡在DRC里。這個(gè)階段你肯定不想為了一條net重新跑一遍optDesign——一跑就是好幾個(gè)小時(shí)還很可能把周圍本來干凈的cell全部挪動一遍。更合理的做法是直接定位到那條net手工插一個(gè)buffer或者一對inverter把負(fù)載斷開、把長線打斷用最小改動把violation修掉。Innovus里干這件事最順手的命令就是ecoAddRepeater。我最早用這個(gè)命令是在一個(gè)28nm的項(xiàng)目上當(dāng)時(shí)為了修一條跨了整個(gè)block的高扇出信號前前后后試了手動addInstance加連線、也試過直接把cell丟給preroute最后發(fā)現(xiàn)還是ecoAddRepeater最省事。它把創(chuàng)建一個(gè)instance、切斷原net、重連所有pin、保留net名這一串操作打包成一條命令省下的不僅是腳本量更重要的是避免了手動操作時(shí)漏連pin或者把net拆碎的問題。這篇文章把ecoAddRepeater從適用場景、命令參數(shù)、實(shí)操流程到翻車細(xì)節(jié)完整梳理一遍適合正在用Innovus做后端收斂、或者剛接觸ECO流程的工程師參考。1. 什么時(shí)候會想起ecoAddRepeater三類繞不開的修復(fù)場景很多教程一上來就講語法但實(shí)際工作中該不該用這個(gè)命令永遠(yuǎn)比這個(gè)命令怎么敲更重要。我回顧自己用ecoAddRepeater的幾次典型經(jīng)歷基本集中在下面三類場景里。1.1 時(shí)序收斂末期的定點(diǎn)修復(fù)項(xiàng)目跑到后端后期floorplan定了、時(shí)鐘樹綜合完了、繞線也基本閉合了這個(gè)時(shí)候時(shí)序報(bào)告里常常還剩少量violation。這些violation往往不是結(jié)構(gòu)性的而是某條net負(fù)載偏重、某段走線太長這類局部問題。這時(shí)候你的訴求是只動這一條net不影響周圍其他邏輯不改變已經(jīng)收斂的時(shí)鐘結(jié)構(gòu)。如果重新跑一遍optDesign工具會基于全局優(yōu)化目標(biāo)調(diào)整大量cell的位置和尺寸結(jié)果可能把本來已經(jīng)收斂的路徑又?jǐn)_動出新的violation。手動定點(diǎn)插入repeater則完全不同新加一個(gè)buffer只改變了目標(biāo)net上的驅(qū)動關(guān)系對周邊邏輯的影響范圍非常有限。我記得有次修一條hold violation就是在data path上定點(diǎn)插了一對inverter插入后只重新跑了局部時(shí)序就過了整個(gè)block的其余部分完全沒動。這種最小改動能力恰恰是ECO命令存在的意義。1.2 max_trans/max_cap violation的快速止血長net加高扇出是max_cap/max_trans violation最常見的來源。比如一條net從driver出來走了400um中間掛了七八個(gè)sink pin到了末端信號邊沿早就變緩了。這種問題在post-route階段特別常見尤其在信號跨block邊界或者穿過congestion區(qū)域的時(shí)候。處理方法就是在走線中段插入一個(gè)buffer把原來一段長net變成兩段短net。第一段是原driver到buffer input距離短了第二段是buffer output到剩余sink雖然距離可能還是有點(diǎn)長但buffer的輸出驅(qū)動能力比原driver強(qiáng)能夠扛得住。這里的關(guān)鍵是選對buffer尺寸選太小了后半段slew還是差選太大了新cell本身input cap大反而把前半段拖累。實(shí)際項(xiàng)目里我一般從X2或X4開始試然后看timing報(bào)告里哪一段變差了再往上調(diào)。1.3 天線效應(yīng)的定點(diǎn)打斷第三個(gè)常見場景是修antenna violation。芯片制造過程中長金屬net會像天線一樣收集電荷等離子體刻蝕時(shí)電荷積累到一定程度可能會擊穿柵氧化層。物理設(shè)計(jì)階段的修復(fù)手段最直接的就是把長net打斷讓天線變短。打斷的方式可以人工跳線也可以在中間插入inverter或buffer。這里有個(gè)經(jīng)驗(yàn)修antenna時(shí)大家更傾向于插inverter而不是buffer。原因很簡單庫里面inverter的cell種類通常比buffer多小尺寸inverter的面積和驅(qū)動能力選擇更靈活而且兩個(gè)inverter可以放在完全不同的位置靈活地切斷兩段不同的金屬長度。我自己在實(shí)際項(xiàng)目中修antenna時(shí)就經(jīng)常把一對inverter一個(gè)放在走線起點(diǎn)附近另一個(gè)放在終點(diǎn)附近中間天然形成一個(gè)斷點(diǎn)metal length立刻被劈成兩半。邏輯功能上兩個(gè)inverter級聯(lián)等于一個(gè)buffer極性不改變。1.4 順帶一提hold修復(fù)里的Buffer插入上面三個(gè)場景是ecoAddRepeater最常見的用途但還有一個(gè)容易被忽略的場景是hold時(shí)序修復(fù)。hold violation本質(zhì)上要求data path上的delay增大而插入buffer或者inverter對就是最簡單可靠的delay添加方式。相比在CTS階段大動干戈在post-route后用ecoAddRepeater在特定路徑上插入一兩級cell加delay的效果非??煽亍P枰⒁獾闹皇莄ell delay相對較小如果hold slack差得比較多可能需要串好幾級這時(shí)候不能光靠手動插得考慮工具自動修復(fù)了。2. ecoAddRepeater的語法與參數(shù)拆解命令手冊之外的關(guān)鍵點(diǎn)命令本身不難難的是把每個(gè)參數(shù)吃透知道哪些參數(shù)不加會出問題哪些參數(shù)加了反而添亂。我習(xí)慣先查一遍help ecoAddRepeater但真正決定腳本質(zhì)量的是你對每個(gè)參數(shù)背后行為的理解。2.1 一條命令完成建cell、斷net、改連接先看最基本的調(diào)用方式ecoAddRepeater -cell BUF_X4 -net data_net_1 -location {100.0 50.0} -insertName eco_buf_001 -noFix這條命令做了三件事創(chuàng)建一個(gè)叫eco_buf_001的BUF_X4實(shí)例把data_net_1這個(gè)net在指定位置斷開然后把新cell串接進(jìn)去——input接到原來的driver端output接到原來的sink端。注意這里說的斷開不是把net刪掉而是改變net的連接拓?fù)鋘et的名字仍然保留。這正是ecoAddRepeater和手動addInstance specifyConnection的本質(zhì)區(qū)別。手動方式需要你自己管理中間節(jié)點(diǎn)的連接關(guān)系很容易出現(xiàn)net斷成兩截、原driver懸空這類問題。ecoAddRepeater把這些細(xì)節(jié)全部處理掉了你只需要關(guān)心插什么cell、插在哪里。這也是我把這個(gè)命令推薦給所有做后端的工程師的原因——它把高頻操作封裝成了原子操作出錯(cuò)概率大幅降低。2.2 核心參數(shù)逐個(gè)說-cell、-net、-location、-insertName命令的參數(shù)不少我按使用頻率逐個(gè)說下我的理解和建議。參數(shù)含義我的建議-cell指定要插入的庫單元名稱先用get_lib_cells篩選寫全名別用通配符含糊帶過-net目標(biāo)net對象建議用get_nets獲取對象后傳入不要手敲net名字符串-location插入位置的坐標(biāo)注意這個(gè)坐標(biāo)是cell的origin不是cell中心-insertName新instance的名字強(qiáng)烈建議每次手動指定方便后續(xù)追溯和eco腳本管理-noFix插入后不固定cell位置需要后續(xù)繼續(xù)優(yōu)化的場景用freeze階段則不加這個(gè)參數(shù)-numStages插入級數(shù)插inverter時(shí)注意偶數(shù)級保持邏輯有的版本支持一次插多級-cell參數(shù)的選擇直接決定修復(fù)效果。我習(xí)慣用get_lib_cells先看看庫里有哪些可用的buffer和inverterset bufferCand [get_lib_cells -quiet *hvt/BUF_X*] set inverterCand [get_lib_cells -quiet *hvt/INV_X*]列出來之后我會關(guān)注每個(gè)cell的area和input pin cap。這里有個(gè)常見的認(rèn)知誤區(qū)不是驅(qū)動越大的cell越好。大驅(qū)動cell本身的input cap也大把它串在原driver后面原driver的負(fù)載反而增加了。如果原driver本身驅(qū)動能力就不強(qiáng)插一個(gè)大cell等于雪上加霜。穩(wěn)妥的做法是從X2開始試看timing報(bào)告再微調(diào)。2.3 buffer和inverter怎么選從cell列表到實(shí)際對比很多初學(xué)者會問一個(gè)很實(shí)際的問題修一條net我到底是插buffer還是插inverter這個(gè)問題的答案取決于你的需求和庫里的資源。對比項(xiàng)Buffer兩級Inverter邏輯極性保持保持兩級級聯(lián)單級面積通常略大可選用小尺寸inv兩級總面積可控?cái)[放靈活性一個(gè)cell只有一個(gè)位置可以是兩個(gè)分散位置打斷能力更強(qiáng)驅(qū)動強(qiáng)度選擇受庫中buffer種類限制小尺寸inv選擇通常更豐富典型場景max_cap/max_trans修復(fù)antenna打斷、hold修復(fù)從上面的對比能看出來如果只是單純需要增加一級驅(qū)動能力buffer確實(shí)更直接一個(gè)cell搞定。但如果你的核心訴求是打斷一段長線或者需要在時(shí)序路徑上增加可控delay兩個(gè)inverter反而更靈活。我個(gè)人的經(jīng)驗(yàn)法則是修復(fù)max_cap/max_trans優(yōu)先看buffer修復(fù)antenna、修hold、或者在floorplan上間隔很遠(yuǎn)的兩個(gè)點(diǎn)之間打斷net優(yōu)先考慮inverter對。2.4 插入后連接關(guān)系是怎么變的理解ecoAddRepeater插入后的連接變化是避免后續(xù)踩坑的基礎(chǔ)。以buffer為例插入前是driver - net_A - sink1/sink2插入后變成driver - net_A_pre - buffer_inbuffer_out - net_A_post - sink1/sink2。也就是說原來那條net被截?cái)喑闪藘啥蔚ぞ邥赜迷瓉淼膎et名來指代包含sink的那一段而driver到buffer input這一段會隱式存在一個(gè)內(nèi)部連接。這個(gè)行為對你寫腳本有什么影響最直接影響是如果你在插入buffer之后再用get_nets去查原始net名拿到的對象可能已經(jīng)變成了后半段net而前半段可能需要通過buffer的input pin去回溯。我早期寫批量腳本時(shí)就栽在這個(gè)上面對著已經(jīng)改變拓?fù)涞膎et做后續(xù)操作結(jié)果報(bào)了一堆no such net的錯(cuò)誤。所以我的習(xí)慣是每次ecoAddRepeater之后重新get_nets刷新一次net對象再進(jìn)行下一步操作。還有一個(gè)和-numStages相關(guān)的細(xì)節(jié)。如果你插的是inverter不要天真地以為工具會自動幫你保持邏輯極性。-numStages 1插一個(gè)inverter邏輯就是反相的你必須再插一級才能恢復(fù)。有些版本支持-numStages 2一次插兩級但我建議你還是分步做每插完一級就檢查一下連接關(guān)系邏輯錯(cuò)了可以及時(shí)發(fā)現(xiàn)。ECO階段最怕的就是連鎖錯(cuò)誤一步錯(cuò)后面全錯(cuò)。3. 從拿到violation到插入完成的完整實(shí)操流程說完了參數(shù)我們來走一遍完整的實(shí)操流程。這一章我按照真實(shí)項(xiàng)目的處理順序來寫先定位問題net再決定插入方案執(zhí)行ecoAddRepeater最后做legalize和ecoRoute收尾。3.1 先定位問題netreport_timing / report_net怎么用拿到時(shí)序報(bào)告不要急著找命令先把violation對應(yīng)的net找出來。我的做法是先用report_timing看路徑report_timing -through net_xyz -nosplit這里-through可以精確定位經(jīng)過某條net的所有路徑看哪些endpoint因?yàn)檫@條net的delay而violation。如果問題確實(shí)是這條net本身驅(qū)動不足report_net會給你更直接的信息report_net -net net_xyz -verbose這個(gè)命令會列出net的total capacitance、driver的transition、sink的個(gè)數(shù)等關(guān)鍵數(shù)據(jù)。我判斷是否需要插buffer的指標(biāo)很簡單total cap是否接近或超過driver cell的max_capdriver到最遠(yuǎn)sink的物理距離是否超過300um左右具體數(shù)值和工藝、金屬層有關(guān)但300um是一個(gè)常見的經(jīng)驗(yàn)閾值sink端接收到的transition是否已經(jīng)明顯劣化。定位完之后再打開GUI高亮這條net看一眼它的實(shí)際走線形狀。這一步花不了30秒但能幫你確認(rèn)插入點(diǎn)的可選范圍。有些走線會穿過macro上方的blockage區(qū)域或者擠在擁塞通道里這些位置都不能作為插入點(diǎn)。3.2 場景A修復(fù)一條高扇出net的max_cap違例假設(shè)我定位到一條叫din_valid的net驅(qū)動cell是一個(gè)INV_X1掛了12個(gè)sink總電容超標(biāo)40%。修復(fù)方案是插一個(gè)buffer把負(fù)載分成兩半。第一步從庫里選一個(gè)合適的bufferset bufCell [get_lib_cells tt1p0v25c/TYP/BUF_X4]第二步計(jì)算插入位置。最省事的辦法是取所有sink pin坐標(biāo)的幾何中心然后吸附到最近的合法row上。示意腳本set netName din_valid set netObj [get_nets $netName] set pinBbox [dbGet [dbGet -p top.nets.name $netName].box] set locX [expr ([lindex $pinBbox 0] [lindex $pinBbox 2]) / 2.0] set locY [expr ([lindex $pinBbox 1] [lindex $pinBbox 3]) / 2.0] ecoAddRepeater -cell $bufCell -net $netObj -location $locX $locY -insertName eco_buf_din_valid -noFix注意這里我的-location用了網(wǎng)表bbox的幾何中心在實(shí)際項(xiàng)目中這個(gè)點(diǎn)很可能落在cell上或者blockage里。所以我不會直接用這個(gè)坐標(biāo)而是把它當(dāng)初始值在GUI里手動微調(diào)選一個(gè)附近沒有擺放cell、不屬于placement blockage的位置。也有一些平臺有自動找空位的腳本但沒腳本的情況下用手動微調(diào)也夠用。第三步插入完成后跑一下legalize并重新布線legalize_placement ecoRoute -net $netNamelegalize_placement負(fù)責(zé)把新插入的cell放到合法的site上ecoRoute則把打斷后的兩段net重新連起來。這兩步缺一不可后面4.3還會細(xì)說。3.3 場景B定點(diǎn)打斷長net并保持邏輯兩級inverter再來看一個(gè)打斷長net的例子。假設(shè)有一條控制信號從block的東邊一路走到西邊總長度超過700umantenna報(bào)告好幾個(gè)pin都有風(fēng)險(xiǎn)。我的處理方案是在走線1/3處和2/3處各插一個(gè)inverter把一段700um的長net切成三段每段都不超過250um。set invCell [get_lib_cells tt1p0v25c/TYP/INV_X1] set netObj [get_nets ctrl_signal] # 第一級inverter放在走線前段 ecoAddRepeater -cell $invCell -net $netObj -location $x1 $y1 -insertName eco_inv_ctrl_a -noFix # 重新獲取net對象 set netObj [get_nets ctrl_signal] # 第二級inverter放在走線后段 ecoAddRepeater -cell $invCell -net $netObj -location $x2 $y2 -insertName eco_inv_ctrl_b -noFix這里有個(gè)非常關(guān)鍵的細(xì)節(jié)第一級inverter插入之后第二級的-net參數(shù)必須重新獲取。因?yàn)榈谝患塱nverter已經(jīng)改變了ctrl_signal這個(gè)net的拓?fù)涞诙壊迦氲奈恢檬钱?dāng)前net上的另一個(gè)斷點(diǎn)而不是原始net上的斷點(diǎn)。有些工程師在這里偷懶沿用第一次獲取的net對象結(jié)果第二級插完之后發(fā)現(xiàn)連接關(guān)系完全不對inverter串錯(cuò)了位置甚至串成了回路。還有一個(gè)我自己常用的驗(yàn)證方法插完之后馬上在GUI里高亮這條net沿著driver到sink看一遍確認(rèn)inverter的極性方向正確。兩級inverter的方向性如果搞反了表面看連接沒錯(cuò)但邏輯上就廢了。高亮檢查30秒能省掉后續(xù)排查很久。3.4 插入后的legalize與ecoRoute銜接ecoAddRepeater做完之后工序遠(yuǎn)沒有結(jié)束。這個(gè)命令只負(fù)責(zé)創(chuàng)建instance和修改連接關(guān)系不會自動幫你把cell放到合法的site上更不會自動繞線。我見過不止一個(gè)同事插完buffer后直接跑LVS結(jié)果報(bào)出一堆open就是漏了ecoRoute。我習(xí)慣的操作順序是ecoAddRepeater -cell $cell -net $netObj -location $x $y -insertName eco_cell_xxx -noFix legalize_placement ecoRoute -modifyOnly $netName其中ecoRoute -modifyOnly只會繞我指定的net不會觸碰其他已經(jīng)收斂的走線適合這種定點(diǎn)修改的場景。如果你的改動比較大或者net數(shù)量多也可以直接跑ecoRoute全局重繞但那樣耗時(shí)會長很多而且有擾動其他net的風(fēng)險(xiǎn)。我的原則是能局部繞就不全局繞ECO最大的價(jià)值就是改動可控。如果插入的位置附近有placement blockage或者macrolegalize_placement可能會把新cell推到一個(gè)出乎意料的位置。這種情況我會重新檢查一下位置再跑一次ecoRoute確保走線沒有異常繞遠(yuǎn)。4. 實(shí)測中容易翻車的細(xì)節(jié)與規(guī)避方法這一章是我最想寫的部分。命令語法、流程框架都是公開的但真正讓你在項(xiàng)目里少加班的是那些不會寫進(jìn)手冊的坑。下面這幾個(gè)都是我實(shí)際踩過的。4.1 坐標(biāo)定位為什么插完cell會和旁邊單元重疊我剛用ecoAddRepeater的時(shí)候最常遇到的問題就是插完cell之后和旁邊的cell重疊。原因其實(shí)不復(fù)雜-location參數(shù)指定的是cell的左下角origin坐標(biāo)不是cell中心坐標(biāo)。當(dāng)你用net的bbox幾何中心作為插入點(diǎn)時(shí)如果這個(gè)位置附近已經(jīng)有別的cell占位新插入的cell就會跟它重疊。另外還有一個(gè)隱蔽的問題是site row和電源軌的關(guān)系。就算坐標(biāo)沒有和已有cell重疊如果Y坐標(biāo)沒有對齊到power grid的row上新cell的VDD/VSS就無法和電源軌對齊后續(xù)連pg的時(shí)候會非常痛苦。我的規(guī)避方法是三步走第一步用dbGet查出附近已有cell的box范圍避開占位區(qū)域第二步在GUI里打開placement blockage層避開所有blockage和macro邊緣第三步確認(rèn)插入點(diǎn)能被legalize_placement正常吸附到row上。如果工具版本支持也可以直接把坐標(biāo)吸附到最近的sitesnapFpSite -pin $x $y這個(gè)命令在Innovus里可以幫你把坐標(biāo)對齊到合法的site grid配合ecoAddRepeater使用能省掉很多后期legalize麻煩。不過不同版本行為略有差異我建議你在自己的環(huán)境里先做個(gè)小實(shí)驗(yàn)驗(yàn)證一下。4.2 net名帶hierarchy時(shí)如何安全引用ECO階段面對的net經(jīng)常是hierarchical的名字里可能帶/或者轉(zhuǎn)義字符。這種情況下直接手敲net名很容易出問題。我踩過的一個(gè)坑是這樣的有次從時(shí)序報(bào)告里復(fù)制了一個(gè)net名看起來是top/block_a/inst_b/data_out直接傳給-net參數(shù)工具報(bào)錯(cuò)說找不到net。后來才發(fā)現(xiàn)在Innovus的命令行環(huán)境里這種帶hierarchy的net名需要經(jīng)過get_nets正確解析而且要處理轉(zhuǎn)義。安全寫法是set netObj [get_nets -hier top/block_a/inst_b/data_out] ecoAddRepeater -cell $bufCell -net $netObj -location ... -insertName ...這里的關(guān)鍵變化是用get_nets -hier獲取net對象而不是直接傳字符串。還有一個(gè)經(jīng)驗(yàn)如果這條net的名字在多個(gè)hierarchy層級都存在get_nets可能會返回一個(gè)list你需要確認(rèn)自己拿到的是不是目標(biāo)net否則插錯(cuò)位置比不插還麻煩。我一般會在get_nets之后打印一下net的名字和物理位置確認(rèn)無誤再執(zhí)行插入。4.3 插入后的PG連接不要想當(dāng)然這是最容易被忽略的一個(gè)點(diǎn)。ecoAddRepeater創(chuàng)建了一個(gè)新的標(biāo)準(zhǔn)單元但它會不會自動給你把VDD/VSS連上我的實(shí)測經(jīng)驗(yàn)是不要想當(dāng)然。不同版本、不同flow下行為不一樣有的版本在插入時(shí)會自動處理pg有的版本則不會。如果你做完ecoAddRepeater和ecoRoute之后沒有檢查pg極可能出現(xiàn)新cell的VDD/VSS懸空在后端LVS階段報(bào)出一堆奇怪的錯(cuò)誤。穩(wěn)妥的做法是插入之后主動檢查并補(bǔ)一次pg連接connect_pg_net或者根據(jù)你flow里的電源連接方式通過sroute把電源網(wǎng)重新連一遍。我的習(xí)慣是凡是ecoAddRepeater插了cell后面一定跟一句connect_pg_net或者全局sroute寧可多跑一步也不要留下懸空pg的隱患。這里還有個(gè)小細(xì)節(jié)如果新插入的cell用的是特殊的power domain比如always-on domain的buffer你要確保ecoRoute時(shí)不會把它的pg誤接到其他power domain上。跨domain的信號修復(fù)建議先用check_pg_net確認(rèn)power domain邊界再決定插入的cell類型。4.4 時(shí)序反而變差的幾個(gè)隱藏原因有時(shí)候插完bufferre-run timing發(fā)現(xiàn)violation不減反增。我總結(jié)下來最常見的是下面幾個(gè)原因。第一個(gè)原因是插入點(diǎn)離原driver太遠(yuǎn)。buffer input接的是原driver的輸出如果原driver到buffer input這段走線比原來更長了那原driver的slew可能變得更差相當(dāng)于把問題前移了。這時(shí)候要么把buffer往driver方向挪要么給前面的driver也換一個(gè)大驅(qū)動cell。第二個(gè)原因是buffer選得過大或過小。過大的buffer自己input cap大把前一級拖垮過小的buffer輸出驅(qū)動不足后半段slew依然差。參考做法是先用X2試看哪一段是瓶頸再決定往上還是往下調(diào)。第三個(gè)原因比較隱蔽插入的cell被工具加了dontTouch或者fixed屬性導(dǎo)致后續(xù)optDesign無法優(yōu)化它。如果你的流程是在ecoAddRepeater之后還會跑一輪optDesign一定要記得用-noFix參數(shù)否則這個(gè)cell被固定住工具只能繞著你插的cell做優(yōu)化反而可能做出奇怪的走線。5. 把ecoAddRepeater放進(jìn)日常ECO流程一些工程化經(jīng)驗(yàn)命令用熟了之后你會發(fā)現(xiàn)自己寫的ECO腳本越來越套路化。這一章聊聊怎么樣把這些套路固化成流程減少項(xiàng)目之間的重復(fù)勞動。5.1 和ecoRoute、optDesign -eco的正確配合順序我早期做ECO有個(gè)壞習(xí)慣插完buffer就急著跑optDesign想讓工具把時(shí)序徹底優(yōu)化干凈。結(jié)果工具一跑把我手動插的buffer又挪走或者換掉了等于白做。后來才理解手動ecoAddRepeater和工具自動優(yōu)化是有分工的——前者做定點(diǎn)干預(yù)后者做全局優(yōu)化。如果你希望工具尊重你插入的cell就得用-fix讓它固定住如果你希望工具接手后續(xù)優(yōu)化那就用-noFix并且不要對插入后的位置抱有過高期望。我目前最順手的流程是這樣的先用ecoAddRepeater做定點(diǎn)修復(fù)比如打斷長net、解除max_cap然后用legalize_placement確認(rèn)位置合法再用ecoRoute -modifyOnly把涉及到的net重新繞通最后再跑一輪optDesign -eco做整體微調(diào)。這個(gè)順序能保證手動干預(yù)和工具優(yōu)化之間不沖突。5.2 批量修復(fù)多條net的腳本模板單個(gè)net的修復(fù)會了批量處理其實(shí)就是在外面套一個(gè)foreach循環(huán)。但有幾個(gè)細(xì)節(jié)要注意我直接給一個(gè)比較完整的模板set bufCell [get_lib_cells tt1p0v25c/TYP/BUF_X4] set netList [list din_valid ctrl_signal data_bus_7] foreach netName $netList { # 每次都重新獲取net對象避免拓?fù)涓淖兒笠檬?set netObj [get_nets -quiet -hier $netName] if {[sizeof_collection $netObj] 0} { puts Warning: net $netName not found, skip continue } # 計(jì)算插入中心并稍微偏移降低重疊概率 set box [dbGet [dbGet -p top.nets.name $netName].box] set locX [expr ([lindex $box 0] [lindex $box 2]) / 2.0] set locY [expr ([lindex $box 1] [lindex $box 3]) / 2.0] ecoAddRepeater -cell $bufCell -net $netObj -location $locX $locY -insertName eco_${netName}_buf -noFix legalize_placement ecoRoute -modifyOnly $netName }這個(gè)腳本并不復(fù)雜但已經(jīng)能處理大部分批量插入的需求。真正重要的一點(diǎn)是循環(huán)里那句注釋——每次迭代都必須重新獲取net對象。因?yàn)榈谝淮尾迦霑淖僴et的拓?fù)涞诙稳绻€用舊的引用輕則插入位置偏差重則直接報(bào)錯(cuò)。批量修復(fù)時(shí)還有個(gè)小經(jīng)驗(yàn)插入前先把所有net按物理位置排序優(yōu)先處理距離相近的net可以減少legalize和ecoRoute的擾動范圍。5.3 什么時(shí)候不該手動插尊重工具的使用邊界ecoAddRepeater很好用但它不是萬能的。我見過有些工程師拿到幾十條violation第一反應(yīng)就是寫個(gè)foreach循環(huán)批量插buffer結(jié)果插了三十多個(gè)cell擁塞惡化、時(shí)序更差最后不得不全部ecoDeleteInstance清掉重來。我的判斷標(biāo)準(zhǔn)是如果violation數(shù)量是個(gè)位數(shù)而且每條都能明確定位到net和原因手動ecoAddRepeater是最好的選擇精準(zhǔn)、可控、改動小。但如果violation數(shù)量超過二十條或者分布在多個(gè)區(qū)域、原因各不相同這時(shí)候應(yīng)該考慮跑optDesign -eco或者針對性的工具自動修復(fù)流程讓工具基于全局成本函數(shù)去優(yōu)化。手動插入的優(yōu)勢是定點(diǎn)可控代價(jià)是缺乏全局視角數(shù)量一多就容易顧此失彼。另外如果是純antenna violation的批量修復(fù)現(xiàn)代Innovus在布線階段本身就有天線效應(yīng)修復(fù)選項(xiàng)優(yōu)先在route時(shí)解決比post-route手動插inverter效率高得多。ecoAddRepeater更像是補(bǔ)刀手段而不是主力的修復(fù)工具。5.4 建立自己的cell選型表和插入記錄項(xiàng)目做多了之后我發(fā)現(xiàn)一個(gè)簡單但很有價(jià)值的習(xí)慣在每個(gè)項(xiàng)目里建立一張ECO cell選型表把常用的buffer和inverter記錄下來包括cell名、面積、驅(qū)動強(qiáng)度、適合的場景。有了這張表每次修復(fù)就不用臨時(shí)去翻library。舉一個(gè)我常用的選型表示例Cell面積驅(qū)動我通常用在什么場景INV_X1小弱antenna打斷、hold微調(diào)INV_X2中中打斷長net、輕量修復(fù)BUF_X2中中max_cap輕中度修復(fù)BUF_X4大強(qiáng)max_cap重度修復(fù)、長net分段另一個(gè)習(xí)慣是記錄插入日志。在腳本里把每次ecoAddRepeater的net名、cell名、坐標(biāo)、插入時(shí)間寫到一個(gè)日志文件里。這樣做的好處是如果后續(xù)需要回退ECO或者要對比不同修復(fù)方案的效果你隨時(shí)能查到自己當(dāng)時(shí)做了什么而不是靠腦子回憶。配合Innovus的ECO history機(jī)制可以很方便地回到修復(fù)前的狀態(tài)重新嘗試。寫得再多不如自己動手跑一遍ecoAddRepeater這個(gè)命令單看語法十分鐘就能學(xué)會但真正用好在項(xiàng)目里解決問題還是需要親手踩幾次坑。我自己的體會是第一次在GUI里插buffer時(shí)一臉懵不知道坐標(biāo)怎么給、連完線怎么看等到第二個(gè)項(xiàng)目時(shí)已經(jīng)能閉著眼寫批量腳本了到第三個(gè)項(xiàng)目我已經(jīng)能給team里的新人講清楚為什么修antenna要優(yōu)先選inverter而不是buffer。工具本身不復(fù)雜復(fù)雜的是你對時(shí)序、物理實(shí)現(xiàn)和工藝的理解。把這些理解轉(zhuǎn)化成一條條合理的ECO命令才是后端工程師真正的價(jià)值所在。上面這些經(jīng)驗(yàn)來自我?guī)н^的多個(gè)項(xiàng)目也踩過不少坑希望能讓你少走幾步彎路。