ARTICLE DETAIL

资讯详情

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

数字IC后端DRV修复失败:don‘t touch hierarchy属性排查与解决

数字IC后端DRV修复失败:don‘t touch hierarchy属性排查与解决 先交代一下背景。我最近在带一个SoC顶层的物理实现任务做完floorplan和初步布局之后开始跑PR流程里最让人头疼的环节——修DRVDesign Rule Violation。工具报出来一个非常不愉快的数字477条net存在DRV问题绝大多数是max_transition违规。按说这个数量虽然不少但也不至于让人绝望真正让人头皮发麻的是我连续跑了三轮optDesign加ECOviolation数量几乎没变化甚至有少数几条net越修越差。这种情况在DRV修复里非常反常一般只要驱动级够、拥塞不严重、约束合理修DRV就是时间问题。可这次不一样工具像是被什么东西绑住了手脚所有修复动作都推不动。排查了一整天之后根因锁定在一个我早有耳闻却很少在实际项目中正面踩中的属性上dont touch hierarchy。这次问题让我重新理解了DRV修复的边界。很多人把DRV当成单纯的“时序和物理参数超限”却忽略了一个关键事实工具的修复能力是被属性和约束框住的。它不能碰的区域不管violation报得多刺眼都只能眼巴巴看着。下面就把这次477条net的完整排查过程、根因分析和解决方案整理出来遇到过类似“修不动”问题的人应该能少走不少弯路。1. 问题现象477条net的DRV到底意味着什么1.1 DRV的本质transition、capacitance、fanoutDRV在我这边的语境里特指数字IC后端实现流程中的三类物理规则问题max_transition、max_capacitance、max_fanout。max_transition是信号跳变需要的时间上限一般由标准单元库的Liberty文件定义常见值在0.1ns到0.5ns之间max_capacitance是net上挂的总负载电容上限取决于驱动单元的驱动能力max_fanout是输出引脚能直接驱动的负载数量上限。这三类规则关注的核心不是时序收敛而是物理可制造性和信号完整性。transition变差会直接让cell delay变大路径delay跟着变差哪怕时钟树已经做完后期修setup和hold也会非常痛苦。capacitance过大会引起IR drop问题fanout过大会让负载不均、时钟偏斜。所以在PR流程里DRV一般是优先级最高的修复目标之一通常会在时钟树综合之前先修掉避免把大量高负载net带到后续步骤。1.2 “修不动”的报告长什么样我这次用的主流程是Innovus环境所以后面的命令会以Innovus风格为主ICC2的读者稍作替换即可使用。当时查看DRV违例用的命令是report_design_rules -all输出里刷出一排类似这样的内容Net: n_1234 (477 nets) Total capacitance: 0.98pF Transition time: 0.453ns (limit: 0.300ns) Driver: u_top/u_mod1/OUT1 Loads: 18 pins477条net的transition全部超限而且超限幅度不是一点点有些甚至到了0.45ns对0.3ns的程度。正常情况下这样的违例并不算极端随便插两级buffer或者把驱动单元加大一号就能修完。但奇怪的是工具在修复过程中也确实插了不少buffer报告却显示违例基本没少。这种情况一旦出现就得怀疑是不是有“看不见的手”拦住了修复路径。1.3 为什么批量出现477条并且高度集中我先按hierarchy归属把violation net做了聚合统计结果发现一个非常清晰的特征477条net里面有421条全部连接到同一个hierarchy模块的端口上。换言之这不是零散分布的工艺偏差也不是个别net的负载算错而是某个模块边界上的系统性问题。这个模块面积不大输出端口却非常多所有输出net都接到了顶层逻辑。单看结构就能猜到一个方向这个模块很可能被加了特殊属性导致工具不能碰它的输出端。很多做过hierarchical flow的人到这里已经能猜出答案了——dont touch hierarchy。但当时我还是按流程一步步把证据坐实毕竟这种批量属性问题动错地方可能比不动更麻烦。2. 为什么工具修不动dont touch hierarchy的作用机制2.1 dont touch是什么、设置在什么对象上dont touch工具里通常写成dont_touch是数字IC设计流程里一个非常基础的属性。它可以设置在cell、instance、net、port、clock上含义是让工具在所有优化步骤里都不要动这个对象。设置在cell上工具不能改变这个cell的尺寸、不能删除它、不能改变它的pin连接设置在net上工具不能在这条net上插buffer、不能改拓扑设置在port上通常等价于不能在该port处插入多余逻辑。设置在hierarchy instance上含义就更重了工具会把这个模块当成一个整体保护的盒子盒子内部所有cell、所有端口的连接关系都不能变。实际项目里常见的有几种来源。一种是综合阶段为了保护某个子模块不被优化器改结构直接对整个instance做了set_dont_touch另一种是floorplan阶段对memory、IP、模拟模块设置了保护属性再一种是从UPF/CPF带进来的isolation cell、level shifter相关约束甚至公司内部的floorplan脚本会自动给所有memory instance加dont_touch。这些属性如果不清不楚地一路带到PR和ECO阶段就是定时炸弹。2.2 hierarchy被锁死后DRV修复被切掉了哪些手段DRV修复的本质手段其实只有三类加大驱动强度、在net中间插buffer分流、把负载拆到多个驱动点上。当dont_touch设置在hierarchy instance上时这三类手段会被逐个封死驱动cell在instance内部不能做size up因为cell不能动需要在net上插buffer打断长线时如果这条net的起点pin在instance内部、终点在外部工具无法在内部插入新的驱动器在外部插buffer理论可行但如果net跨越hierarchy boundary工具需要改变net拓扑或新增pin这会被“不能碰port”的限制挡回去如果dont_touch还连带把该hierarchy的某些输出net也设置了等于double lock。结果就是工具手里三张牌全部作废剩下唯一能做的选择是把负载往后推到更下游的逻辑但那通常意味着要穿越多个hierarchy层级优化引擎不会冒这么大的风险。所以你看到的现象就是工具报了DRV也尝试修但每轮eco之后violation数量纹丝不动只有少数不受dont_touch约束的net被顺手修掉。2.3 一个典型的边界net违规场景推演为了把逻辑讲得更清楚我画一个典型场景模块A的输出端口out[31:0]驱动单元是2X drive的INV顶层load是32个寄存器transition超限。正常修法先尝试upsize到4X或8X如果因为拥塞或者sink太分散再在输出net上插两级buffer或者复制一个buffer分别驱动部分寄存器。现在如果模块A被设了dont_touch情况会变成upsize被禁止因为单元不能动在模块A内部插buffer被禁止因为内部net和port都不能动在模块A外部插buffer需要把net打断并新增驱动点虽然不直接改模块内部但工具一旦发现这条被打破的net原本由模块A的输出port驱动而port又属于dont_touch范围通常也会选择放弃。最终结果就是这条net只有两条出路一是你手动到工具之外去改网表二是放弃修。477条net用手动方式处理根本不现实所以整批net都处在“天天报、天天修不掉”的状态。3. 定位与诊断怎么把根因一步步逼出来3.1 先搞清楚477条net背后的对象关系排查这种事不能凭感觉先把数据拉出来。我习惯分三步走第一步拿到DRV报告后把violation net的名字、driver cell、load pin数量、所在hierarchy路径全部导成表格。Innovus里可以直接用report_design_rules输出再用Tcl脚本解析ICC2里的report_constraints -all_violators也能导出类似信息。第二步按照所属模块做聚合统计看是否集中在某一个或某几个instance。这个统计看起来简单但能直接改变排查方向。我看到421条集中在u_mod1时基本就把范围从“全芯片找原因”缩小到“研究u_mod1为什么特殊”。第三步对高嫌疑instance检查属性重点看dont_touch、preserve、is_hierarchical这类关键字。这一步往往能快速定位问题。3.2 用Tcl命令把dont_touch属性全量摸出来在Innovus里检查dont_touch的常用Tcl命令如下# 列出所有被设了dont_touch的对象 all_dont_touch # 查看某个cell或instance的属性 get_attribute [get_cells u_mod1] dont_touch # 查看hierarchy下所有instance的dont_touch情况 foreach_in_collection inst [get_cells -hier u_mod1/*] { set attr [get_attribute $inst dont_touch] if {$attr true} { puts [get_full_name $inst] : dont_touch } }ICC2环境可以用 report_attributes 来查report_attributes [get_cells u_mod1] -applies dont_touch我当时执行完发现u_mod1的dont_touch属性是true而且hierarchy下面有一批cell也都带着dont_touch。这已经高度怀疑是根因但还不够因为dont touch不一定只来源于cell属性。有些SDC里会写 set_dont_touch_network有些UPF/CPF会把isolation cell或者level shifter设成dont_touch还有floorplan脚本会给memory instance统一加dont_touch。所以排查时不仅要查cell还要查net和port上的属性。3.3 验证“去掉dont_touch就能修”的假设确认嫌疑之后先别急着在正式工程上改。更稳妥的办法是开一个分支或临时session做实验把u_mod1的dont_touch解除跑一轮增量optDesign观察477条net里有多少能被修复。remove_dont_touch [get_cells u_mod1] optDesign -postRoute -drv report_design_rules -all我当时的实验结果很干净421条里修好了398条剩下23条是因为局部routing资源太紧属于拥塞问题和dont_touch无关。这个对比已经足以证明dont touch hierarchy是根因而不是表象。有了这个实验证据后面跟前端同事、跟决定加保护属性的同事沟通时就不再是“我觉得是某某导致的”而是“我验证过了去掉之后修复效果确实好”。这在跨团队协作里非常重要。4. 解决方案与落地操作4.1 先判断dont touch是否必须保留在动手改之前一定要先问一个问题这个hierarchy为什么会被设成dont_touch不同原因处理方式完全不同。如果是第三方IP、memory compiler生成的RAM/ROM、模拟IP边界通常应该保留dont_touch因为改动会影响IP的物理验证和时序模型。这类情况不能为了修DRV而强行移除应该改成在IP边界外面做处理必要时让IP供应商提供带输出缓冲的wrapper。如果是普通逻辑模块只是为了防止综合改结构或者为了以后单独做block实现那就可以评估移除dont_touch的风险。对于后者很多时候真正需要保留的是hierarchy边界信息而不是锁死所有cell。可以考虑只对端口加dont_touch内部逻辑放开或者用更精细的保护策略。这里要特别提醒dont touch和preserve hierarchy是两回事。preserve hierarchy只是不让工具flatten掉层级不代表不能改内部cell的size也不代表不能插buffer。很多团队在综合阶段随手给整个模块设了dont_touch一路带到布局布线这属于历史债务越早清理越好。4.2 按场景选择修复策略临时解除、局部解除、手动size_cell如果真的确认dont touch是多余的最简单粗暴的办法是直接remove_dont_touch后重新优化。但dont touch确实有保留必要的时候或者一次性别动太多结构时可以考虑几个务实手段。第一种临时解除修复完成后再恢复。适合ECO阶段不想大动干戈的情况。具体做法是把dont_touch的恢复命令写进脚本修复完立刻恢复然后做形式验证确保网表等价。但要注意恢复dont_touch后工具之前修复插入的buffer不会被当成违规源因为它们不是dont_touch对象。不过如果后续再跑优化工具可能会把之前的修复成果改掉所以最好在解除dont_touch完成修复后给新插入的buffer设上保护把成果固定住。第二种局部解除。如果只有某几条net需要修不要整个模块解开只对被维修的driver cell和它所在net解除dont_touch。示例如下set_dont_touch [get_cells u_mod1] false # 只修指定net ecoRoute -fix_drc -nets {n_1234 n_1235 ...} set_dont_touch [get_cells u_mod1] true这种方式风险最小但批处理起来比较费劲适合net数量少的场景。第三种手动size_cell。如果dont_touch锁死了hierarchy但手动改单元尺寸是允许操作不同工具对dont_touch范围的实现有差异可以尝试手动把边界上的驱动单元替换成更大驱动版本。这个操作没有从根本改变net拓扑只是加大驱动很多情况下能同时满足dont_touch和修复DRV。但在477条net的场景里不现实因为涉及的驱动cell数量太大手动换单元既费时又容易出错。4.3 修复完成后的恢复与等价性检查无论选哪种方案完整落地步骤建议这样走保存当前design快照防止改坏回不去在Tcl脚本里把目标instance的dont_touch属性打印出来作为修改前状态解除dont_touch运行DRV修复optDesign -postRoute -drv跑一遍quick timing check确认没有因DRV修复引入新的setup/hold问题用report_design_rules -all确认violation数量下降恢复dont_touch并对新插入的buffer打上set_dont_touch避免后续流程把它们优化掉跑formal verification或LEC确认功能等价。第7步非常关键。ECO阶段修完DRV后工具还会做后续的hold fixing或时钟树微调如果新插入的buffer没有保护很可能在下一次优化里被挪走或删除问题复发。我一般会给新插入buffer统一命名为DRV_BUF_开头的prefix然后写一条命令凡是匹配这个前缀的cell全部设dont_touch。这样后续流程能保持稳定也方便同事追溯。5. 常见问题排查与避坑实录5.1 排查命令速查表把这次用到的命令整理成一份速查表以后遇到类似问题可以直接对照目标Innovus/SOC EncounterICC2/FC查看DRV违规report_design_rules -allreport_constraints -all_violators -max_transition -max_cap列出所有dont_touch对象all_dont_touchreport_dont_touch -verbose查instance属性get_attribute [get_cells u_mod1] dont_touchreport_attributes [get_cells u_mod1] -applies dont_touch解除dont_touchremove_dont_touch [get_cells u_mod1]remove_dont_touch [get_cells u_mod1]查net上的dont_touchget_attribute [get_nets n_1234] dont_touchreport_attributes [get_nets n_1234] -applies dont_touch运行DRV修复optDesign -postRoute -drvrefine_opt -type drv / opt_design -post_route -drv表格里列的是最常用的命令形式实际项目里不同版本会有细微差别但思路一致。5.2 我踩过的三个坑第一个坑只查cell的dont_touch没查net的。有些综合流程里写的是 set_dont_touch_network [get_nets -of_object [get_cells u_mod1/*]]这种设置不会体现在cell的dont_touch属性里而是挂在net上。如果只检查cell属性就会漏掉真正的元凶。我第一次排查就踩了这个坑后来把net和port的attribute也拉出来比对才发现一部分violation net上带着dont_touch属性。第二个坑修复之后没有及时恢复dont_touch。有一次我解除dont_touch后修完DRV顺手继续跑了一个大的ECO结果工具把当初“锁存”的整个模块结构调整得乱七八糟再看时序和面积全变了最后只能回滚重来。从那以后我的所有dont_touch操作都必须穿“恢复脚本”修完立刻执行绝不过夜。第三个坑用false path去掩盖DRV。DRV是物理真实问题不是时序例外能解决的。有人为了pass flow对某些DRV违例路径设置false path确实会让报告变干净但物理上transition依然很差到后面做IR drop、signal integrity甚至芯片回来之后问题会以更难看的方式爆发。数字IC里DRV报告只是暴露问题掩盖问题是最危险的做法。5.3 如何在项目流程上预防这类问题预防的成本永远低于修复。我的几个建议综合阶段约束文件里不要轻易给大模块整体设dont_touch。如果只是希望保留hierarchy用编译器的preserve_hierarchy选项而不是set_dont_touch。floorplan阶段对memory和IP类的dont_touch做登记定期用脚本检查全design的dont_touch列表和真正需要保护的清单比对发现多余的就清理。hierarchical flow交接时明确规定sub-design的输出net上不允许带dont_touch最多只对输出port做边界约束保护给顶层集成阶段留出插入buffer的空间。DRV修复前先把全design的dont_touch审计作为常规checklist和DRC、时序报告一起跑。我一直觉得数字IC后端工程师最大的杀器不是会跑多少命令而是知道工具为什么不敢动某些地方。很多看似无法收敛的问题其实都藏在某个不起眼的属性里。6. 实操教程一套可直接套用的DRV修复流程6.1 场景假设和脚本目标假设场景顶层集成阶段u_mod1的dont_touch导致477条net的DRV修不掉。目标是先解除dont_touch修复DRV再恢复保护属性并给新插入的buffer打上保护防止后续优化改掉。这个脚本以Innovus风格为主ICC2用户把optDesign部分替换成对应命令即可。运行前建议先保存design快照。6.2 一套可运行的Tcl修复脚本下面是我这次实际使用并精简过的脚本可以直接放到项目中改改用# fix_drv_dont_touch.tcl # 用途解除u_mod1的dont_touch修复DRV恢复保护属性 # 0. 记录修改前状态 set pre_attr [get_attribute [get_cells u_mod1] dont_touch] puts INFO: u_mod1 dont_touch before $pre_attr # 1. 保存快照防止修复失败回不去 saveDesign pre_fix_drv_keep_dont_touch.enc # 2. 解除dont_touch remove_dont_touch [get_cells u_mod1] # 3. 运行DRV修复 optDesign -postRoute -drv # 4. 输出修复后的DRV报告 report_design_rules -all drv_after_fix.rpt # 5. 固定新插入的buffer避免后续优化改掉 foreach_in_collection inst [get_cells -hier u_mod1/*] { if {[regexp {DRV_BUF} [get_full_name $inst]]} { set_dont_touch $inst true } } # 6. 恢复原属性按需 if {$pre_attr true} { set_dont_touch [get_cells u_mod1] true } puts INFO: fix_drv_dont_touch.tcl done这个脚本的灵魂在最后两步先固定新插入的buffer再恢复原来的dont_touch。如果只恢复dont_touch新插buffer会暴露在后续优化里如果只固定新插入buffer而不恢复原属性那这个模块的保护也就形同虚设了。两个动作缺一不可。6.3 执行后如何验证执行完脚本后不要只看DRV数量有没有降下来还要验证三件事第一功能是否等价。跑formal verification确认u_mod1的端口逻辑和内部逻辑没有被意外改变。因为remove_dont_touch之后工具可能对内部做了一些优化形式验证能兜住最后一道底线。第二时序是否恶化。修DRV有时候会改变路径上的插入延迟导致setup/hold变化。至少跑一轮quick timing看关键路径有没有从绿色变成红色。第三后续步骤是否稳定。恢复dont_touch之后继续往下走时钟树综合或hold fixing观察之前修好的DRV会不会反弹。如果反弹多半是step 5的buffer保护没做好应该回去检查命名规则是否和工具自动插入buffer的命名规则匹配。我自己跑这个流程的经验是整个过程需要仔细盯第一轮ECO之后的报告。工具优化是全局行为可能为了修A路径的DRV把B路径多插了一级buffer导致B的setup变差。遇到这种情况不要慌先看是不是个别路径如果是个别情况可以用局部size或删除多余buffer的方式处理如果是批量出现就得检查约束里是不是有什么全局设置诱导工具过度布线。最后再分享一个小习惯。我现在每次拿到一个“修不动”的DRV清单第一件事不是加约束而是查属性的历史来源。我会打开综合约束、floorplan脚本、SDC、UPF四份文件搜索dont_touch、preserve、set_dont_touch_network这几个关键字把东西全部列出来再动手。项目里被这种问题卡过的次数多了之后你会发现大多数所谓“诡异问题”最后都是人为设置锁出来的人工障碍。工具不是万能的但它不会无缘无故罢工它罢工通常是因为有人在它脚下埋了绊马索。
返回列表