ARTICLE DETAIL

资讯详情

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

数字IC后端项目实战:从congestion到低功耗的完整问题排查清单

数字IC后端项目实战:从congestion到低功耗的完整问题排查清单 做了快两年的数字IC后端项目从28nm一路做到更先进的节点最大的感受是后端这个活儿真正值钱的不是把一条流程流水线式跑通而是每一次跑完flow之后面对那一堆或红或黄的问题报告能快速定位根因、给出方案。最近一个项目顺利tapeout我把这一路上踩过的坑、填过的洞、复盘过的问题整理成一份记录。趁着项目收尾的间隙分享出来内容围绕place阶段的congestion问题、CTS异常、hold修复的连锁反应、物理验证里的天线效应和seal ring短路、多电压域的低功耗实现以及最后那些查问题查到头秃时攒下来的脚本。这些问题在项目之间反复出现值得沉淀成一份可以反复翻看的清单。1. 项目背景与后端全流程问题地图1.1 项目基础信息与工具链选型先说项目背景。我们是一个SoC级的芯片规模大概在三百万门左右主时钟跑200MHz附近内部还有几个从低频到中高频的模块时钟。工艺节点用的是比较新的主流节点不是最前沿但也不是老古董金属层十层左右。整个数字后端流程用的是Cadence的Innovus做主体布局布线综合用DCRTL仿真用VCS混合仿真用Xcelium静态时序分析用Temps配合PT交叉做signoff检查物理验证用Calibre跑DRC/LVS功耗分析用PTPX和Voltus配合。选这套工具链没什么特别的玄学主要是跟fab的PDK和参考流程对齐。VCS和Xcelium同时存在是因为验证组不同模块的习惯不一样有的老模块一直沿用Xcelium的流程新模块则倾向VCS的生态后端这边只要拿到的是标准sdf和vcs兼容的网表就行。Innovus做后端主体流程数据交互格式用OpenAccess脚本统一用Tcl管理这一点后面会详细说很多问题其实出在数据格式和脚本传递上。项目不是一版流片就完事的前后迭代了三个版本第一次是功能验证平台第二次修了一版时序和功耗问题第三次才真正做量产前的收敛。每一版跑flow产生的不是一堆pass而是一长串需要人工判断的问题清单。最初我们是用Excel记后来改成内网wiki再后来我发现真正好用的不是所谓的问题管理平台而是把每一次「问题—根因—处理动作—后续预防」这种结构化的记录直接沉淀成脚本、约束文件和检查项。1.2 一个后端项目会遇到的六类典型问题把两年里见过的问题归归类后端项目的坑基本逃不出这几类第一类是布局阶段就埋下的congestion和density问题这类问题越早发现越好改拖到CTS之后再返工成本极高。第二类是时序收敛问题包括CTS之前的ideal时钟阶段和CTS之后的propagated时钟阶段问题会以setup、hold、skew、latency各种形式暴露出来。第三类是物理验证问题DRC、LVS、antenna、metal density、seal ring每一样都能让项目在最后关头卡壳。第四类是低功耗相关多电压域划分、level shifter和isolation cell的插入、电源关断顺序这类问题功能验证阶段不一定暴露但后端不做对芯片回来就直接废。第五类是可靠性相关EM、IR drop、电迁移这类问题往往到项目后期才集中爆发。第六类是流程和脚本问题比如数据版本管理混乱、约束文件不一致、报告解析出错这类问题最隐蔽也最容易浪费好几天。后面几节我就挑这六类里最有代表性的问题逐个拆解。每个问题尽量写清楚当时的现场、定位过程、根因和最终解决方式希望能帮遇到类似情况的人少走点弯路。2. Place阶段的congestion问题全记录2.1 怎么把congestion map和density map从Innovus里导出来先说一个看着小但实际很常被问到的问题place阶段想看congestion map和density map到底怎么从Innovus的database里把图吐出来。经常有同事问我跑完place之后工具界面里是有热力图但没法直接保存成图片发给前端同事评审总不能对着屏幕拍照吧。其实Innovus里至少有两种常规做法。第一种是GUI操作方式。跑完placement之后在菜单栏打开Floorplan视图然后通过视图模式下拉菜单切换到Density或者Congestion这时画布上会显示对应的热力图。想要保存成图片直接用工具自带的截图功能File菜单下面的Save Image或者命令行输入gui_save_image -file cong_map.png就能把当前视图保存成png。如果只是临时发给别人看一下这个方式最直接也够用。第二种是命令行方式适合要批量出图或者跑在服务器上的场景。可以用reportCongestion直接输出文本报告比如reportCongestion -dir all -overflow 0.05 -util 0.8 -outfile cong_rpt.txt这条命令会把所有方向、超过5%溢出且利用率超过80%的区域以坐标形式列出来。想更直观一点可以用reportDensity或者自定义dbQuery抓取局部密度数据set congested_blocks [dbQuery -area ALL -obj insts -util 0.85 -isPhysical true] foreach inst $congested_blocks { puts [dbGet top.insts.name -this $inst] }命令行方式的优势在于可以脚本化每次跑完place自动吐一份报告再跟上一次跑的结果做diff就能看出congestion问题是在改善还是恶化。这种方式特别适合做flow的人因为可以集成到回归测试里每次跑完自动报警。这里有个注意点很多人导出图之后喜欢只看颜色深浅但其实更应该看的是工具自己算出来的overflow数值以及横向和纵向走线资源各自的溢出情况。两个方向的溢出含义完全不同纵向溢出通常跟标准单元密集度有关横向溢出则更可能与宏单元blockage和pin access有关。只看颜色很容易把方向搞反定位到错误的方向上浪费时间。2.2 从热图颜色到根因判断说实话congestion热力图这东西初看就是一坨红色黄色看不出门道。但如果你知道它背后是怎么算出来的再看就清楚很多。工具在做global routing的时候会把整个芯片划分成一个个小的GCell网格统计每个网格里需要的走线数量再跟该网格实际可用的走线资源做比较超出一定比例就标记为congested。颜色越偏红说明该区域的走线需求超出资源越多偏蓝就表示很宽松还有大量空间富余。density map则是最直观的利用率图显示每个网格内标准单元面积占可用面积的比例。这个图能直接反映floorplan是否合理——比如某个区域利用率已经到90%以上那不用看congestion map也能猜到那里必定拥堵。我之前遇到过一个案例某个模块在place之后的congestion map上红得发黑怎么调都降不下来。后来把density map调出来一看发现是两个memory hard macro之间只留了一条极窄的通道通道顶部堆了一堆标准单元利用率达到了93%。因为memory的pin都集中在上方边缘所有信号都要从这条通道出纵向走线资源完全不够用。这种情况你再怎么调place option都没用因为问题根本不在placement而在floorplan阶段把macro放得太近了。最后是把两个memory的间距拉开到原来的两倍congestion瞬间就降下来了。对这个案例印象特别深是因为它说明了一个很朴素的道理congestion map的作用不是告诉你哪里堵而是通过堵在哪里反推floorplan哪里不合理。看到congestion热点第一反应不该是立刻去调工具参数而是先回到floorplan层面问一句这个区域为什么会堆积这么多走线需求是macro位置不对还是pin分布太集中还是电源网络占据了太多走线资源再补充一个判断技巧。congestion分横向和纵向Innovus的reportCongestion结果里会区分H和V两个方向。如果H方向溢出严重优先检查同一行标准单元的密度和旁边macro的halo设置如果V方向严重优先检查标准单元行的高度、double height cell的使用情况以及M2/M3层的pin access是否被电源rail切断。很多工程师习惯拿到图只看整体红不红从来不分开看方向这会导致明明问题在横向走线却一直在调纵向的资源分配怎么调都调不对。2.3 改善congestion的六种手段与取舍改善congestion的手段按介入阶段从早到晚排大概有六类。第一类是调整floorplan这是成本最低、收益最大的手段。具体做法包括加大macro间距、调整macro朝向、给macro加halo、把高利用率功能模块尽量分散放置。前面那个memory案例就是这一类。这类手段适合在place之前做到了place之后再改floorplan整个网表布局都要重新跑代价很大。第二类是设置routing blockage和placement blockage。在特定区域禁止布线或者禁止放置单元可以人为引导工具把资源让给最需要通道的地方。比如在macro之间的窄通道上方加一条routing blockage强制走线绕行虽然会损失一点性能但能换来整体congestion的改善。第三类是改线的宽度和间距也就是Non-Default RuleNDR。对于少数关键的时钟线和高速信号线可以设置double width和double spacing这样虽然单条线占的资源多但减少了串扰和delay反而能减少因时序修复带来的额外绕线。这个手段不要全局使用只针对个别net使用否则会让congestion雪上加霜。第四类是局部低密度placement。Innovus里可以用createPlacementBlockage控制某个区域的最大利用率把标准单元密度限制在比如70%剩余空间用来给后续的buffer插入和hold修复。这个方法在修复hold的时候尤其有用后面讲时序案例时还会提到。第五类是手动spread cells。跑完place后如果发现某个局部区域异常拥挤可以用moveInstance或者工具自带的spread功能把单元手动挪到附近空闲区域。这个操作需要跟density map配合看手动挪完之后一定要重跑congestion report确认没有把问题搬到别处。第六类是调整power grid和routing layer direction。某些设计中高层金属被电源网络占掉了太多走线资源导致绕线通道不足。比如M7、M8整层都打了密密麻麻的power stripe那不管tool怎么绕横向资源都紧张。这种情况需要在floorplan阶段算好power stripe的宽度和间距留出足够的走线通道。很多时候到place之后发现congestion已经很难救了再回头查原因其实是在initial floorplan阶段power strap打得太密。六种手段各有适用场景实际操作中往往是组合使用。我的习惯是先看floorplan有没有硬伤再看power grid有没有挤占走线资源然后才轮到place层面的参数调整。倒着来的人十有八九会陷入越调越糟的循环。3. 时序收敛实战两个真实案例3.1 CTS后skew异常与边界约束冲突时序问题里CTS时钟树综合阶段最容易出幺蛾子。有一个案例我印象特别深某个模块的时钟树综合完之后Innovus里的clock skew报告显示只有0.05ns看着非常健康。结果把时钟树数据导出到Tempus里做signoff STA却冒出来一堆hold violation。追查了半天发现有一个触发器接收到的时钟比其他触发器早了将近0.3ns。一开始怀疑是CTS过程中clock root buffer选得不对反复检查clock tree的log和报告debug了一整天都没有头绪。后来仔细比对约束文件才发现问题出在边界约束的重复设置上。这个模块在SDC里同时写了create_clock和set_propagated_clock而且上游模块的时钟约束里又另外定义了一组clock uncertainty。两套约束同时生效Innovus在CTS阶段用的是一套边界条件而Tempus读SDC时又叠加了另一套条件两边算出来的skew自然对不上。这个案例给我们的教训是CTS之前一定要把SDC里的时钟约束理干净特别是时钟分组、uncertainty和latency设置前后端要使用完全一致的约束环境。有人可能会说Innovus和PT各读各的约束只要都从同一个SDC文件生成不就行了吗实际上很多问题恰恰出在同一个SDC上——CTS阶段工具会主动改写时钟网络结构某些时钟树节点被插入buffer后原本边界上的clock latency发生了改变但SDC里有些基于理想时钟的约束没有同步更新。后来我们做了一套检查脚本每次CTS之前自动检查SDC里有没有重复定义的时钟、有没有ideal clock和propagated clock混用的情况确认无误后才继续往下走。这之后类似的skew问题再也没有出现过。3.2 Hold修复引发的congestion连锁反应第二个案例是关于hold修复的。项目第二版迭代时某模块hold violation一大堆工具在优化阶段自动插了几百个buffer来修复hold。修复完成之后timing报告确实变好了但再看congestion map之前还算干净的区域彻底红了有些地方density从65%飙到了85%以上。这还不算完因为congestion恶化后续绕线绕不开线长反而变长setup violation又冒出来一截。工具为了修这些新出来的setup问题又尝试upsize cell或者挪位置结果进一步加剧了局部密度不均。最后整个place和CTS反复迭代了好几轮始终在hold和setup之间来回打摆子。根因其实很简单hold修复是全芯片范围内的普遍问题工具选择插buffer的位置时默认逻辑是就近找最方便的位置插而不会考虑这个位置的局部利用率。几十个buffer看着不起眼但如果它们恰好都落在同一个已经快要饱和的区域那这个区域的走线资源立刻就不够用了。解决方式是双管齐下。一是给高密度区域设置density上限用createPlacementBlockage把某些重点区域的利用率控制在75%以内强迫工具把新插入的buffer放到更远但更空的区域。二是对于特别严重的hold violation路径放弃完全依赖工具自动修复改为手动选点修复。所谓手动选点就是根据violation路径的物理位置人工在合适的位置插入指定尺寸的buffer确保修复单元分散放置避免扎堆。踩过这次坑之后我带项目的习惯改成了先看congestion report再看timing report。只要congestion热点和hold violation点在空间上有重叠就先处理congestion再处理hold。这个顺序反过来十有八九要做返工。4. 物理验证与DFM问题实录4.1 Antenna违例原理、定位和修复天线效应是物理验证里很高频的一个坑。原理说起来并不复杂芯片制造过程中金属层是一层一层往上沉积和刻蚀的在刻蚀某层金属时这一层金属会像天线一样收集等离子体中的电荷。如果一条金属线连接着晶体管的栅极积累的电荷没有泄放路径就可能击穿栅氧化层导致晶体管永久损坏。实际项目中天线效应最容易出现在跨层走线的长net上。我遇到过一个典型的案例一条从标准单元输出端连接到远处接收端的信号线在M6层走了很长一段CALIBRE跑antenna检查时报了一个比较严重的违例。这条net要驱动很多个接收端栅极面积本身不大但天线上积累电荷的面积与栅极面积的比例严重超标。定位antenna问题比定位DRC问题麻烦一点因为它不是简单的几何图形违规而是涉及到电荷积累和泄放路径。好在Calibre的RVE工具里可以直接高亮报violation的net和天线层我一般先看报出来的net是哪一层积累电荷最多再结合绕线图判断这个问题是局部绕线方式导致的还是整条net的拓扑结构导致的。修复antenna的方法有几种。最常用的是跳线layer hopping把一段敏感的长线从高层金属跳到底层金属再跳回来因为底层金属在制造过程中先于高层金属被刻蚀已经通过接触孔和扩散区泄放掉了电荷。这个操作在Innovus里可以手动做用ecoRoute切换金属层。如果局部环境不允许跳线第二种方案是插入antenna diode也就是在敏感节点附近加一个反偏二极管给积累的电荷提供泄放通道。加二极管会占用一点面积但这是最稳妥的修法。第三种方案是调整绕线顺序让长线尽量在低层金属完成不过这通常需要回到绕线阶段重新做成本较高。关于antenna修复合不合格有一个关键参数叫天线比也就是天线面积与栅极面积之比。不同工艺给的限值不一样有的工艺是400:1有的是1000:1。修的时候不要只看VIOLATION本身还要看它的余量。如果天线比只是略微超出限值跳线一层就能解决如果超出好几倍那可能需要换更彻底的方案。4.2 Seal ring与IO电源的LVS short教训再说一个更让人抓狂的物理验证问题。项目第一次跑全芯片LVS时报出来几千个short看起来非常吓人。一开始还以为是电源网络哪里接错了一层一层排查后来发现所有short都集中在芯片边缘的seal ring和IO power rail之间。seal ring是芯片切割道内侧的一圈金属环起到保护芯片内部电路、防止切割应力损伤的作用。它通常由高层金属和通孔围成一个闭合环而且为了可靠性往往不止一层金属。问题在于我们做floorplan的时候直接用了fab提供的seal ring standard cell但我没有仔细检查IO cell的电源环和seal ring在physical上的连接关系。结果seal ring的金属层与IO power rail在同一个layer上有重叠但逻辑上它们并不应该短接LVS自然就报short了。排查这个问题的过程也很折腾。因为short数量太多单独看任何一个short都以为是孤立的后来用Calibre RVE把所有short的坐标导出来画到一张图上才看出规律——所有short都在同一条环状带上。这时候才意识到是seal ring层次设置的问题。解决方式倒不复杂重新检查seal ring的层次生成规则把可能与IO电源rail重叠的那层金属在seal ring边界处做cut确保物理上没有连接。另外在netlist层面把seal ring的接地关系明确描述出来让LVS知道这里是一个虚拟环而不是悬空的金属块。这个问题修完之后LVS short数量从几千直接降到零瞬间清爽。这个案例给我一个很深的教训物理验证的问题很多时候不是验证阶段能发现的而是在floorplan阶段就埋下的。流程上最好在floorplan完成之后就做一次preliminary LVS check不用完整跑只检查顶层电源连接和seal ring这类全局结构能提前挡掉一大半后期的大坑。5. 低功耗设计的后端落地问题5.1 多电压域划分在floorplan中的实现与陷阱低功耗设计在后端的落地核心是处理好多电压域power domain的物理划分。我们项目里有三个主要的电压域分别跑在不同的电压下其中两个在功能模式下还会动态调压。在Innovus里做多电压域设计需要先创建Voltage Area把不同domain对应的std cell区域物理限定出来然后设置Voltage Domain定义每个域的电压范围。这里最大的陷阱是domain边界的设计。一开始我们天真地把低电压域放在芯片正中央四周围着高电压域的逻辑因为这样内外部逻辑交互的距离最短。结果place跑完一看level shifter数量爆炸达到了一万多个占了将近4%的面积而且因为level shifter在物理上必须放在两个domain的边界上边界区域被挤得密不透风congestion直接失控。后来我们把低电压域挪到了芯片边缘只跟高电压域共享一条边界。level shifter的数量直接减了一半多因为两个domain之间的信号交换面大幅缩小了。domain的边界越短需要跨域的信号越少level shifter和isolation cell的数量就越少。这是一个非常朴素的面积和功耗权衡把domain放在正中心是拿level shifter的面积代价换取逻辑上的布线短距放在边缘是牺牲一点跨域路径的绕线长度换取更少的单元插入。对于大多数设计来说后者更划算。另一个多电压域常见的坑是domain内部出现孤岛。当一个电压域被另一个电压域完全包围时低电压域内部必须有自己的tap cell和well tie连接不能依赖外围的array。有些工具自动插入tap cell时按全芯片统一处理没有单独考虑domain内部的连接结果LVS时冒出大量floating well的违例。解决办法是在每个voltage area内部单独跑一次加tap cell的步骤确保每个domain内部都有完整的well连接结构。5.2 Level shifter与isolation cell的插入位置问题低功耗设计里level shifter和isolation cell的插入位置是功能正确性的关键。level shifter负责把信号从一个电压域的电平转换到另一个电压域isolation cell负责在某个domain断电时把输出信号钳制到安全电平防止浮空信号灌入仍然上电的电路。这两个cell的插入规则一句话总结就是level shifter必须放在目的domain一侧isolation cell必须放在受保护domain的输入侧。听起来很简单但实际操作中经常放错位置。我们项目有一次做DVFS验证某个模块在电压切换过程中出现了偶发性的数据错误查了很久定位到是isolation cell的位置不对。那个模块的输出信号直接跨到了另一个常开domain但我们把isolation cell放在了输出模块内部所在domain一断电isolation cell自己也没电了根本起不到钳制作用。正确做法是把isolation cell放在常开的接收domain那一侧这样即使发送domain断电接收侧的isolation cell仍有供电能持续输出稳定的钳制电平。在Innovus里控制level shifter和isolation cell的位置主要靠insertLevelShifter和insertIsolationCell命令里的domain选项。比如指定inbound方向、放置在destination domain等。跑完之后一定要用工具自带的low power check功能做一遍排查检查每个跨域信号的level shifter和isolation cell是否真的放在了预期位置而不是只看log里有没有报错。还有一个容易被忽略的细节某些低功耗cell比如always-on buffer在物理上必须接在常开电源上如果floorplan阶段没有给这些cell单独划分出always-on区域工具就会把它们放在普通区域接的是可关断电源。这样即使逻辑上都对实际流片回来一断电整个链条依然会瘫掉。所以每次做完低功耗cell插入之后我都会用工具查一遍always-on cell的power pin连接确保它们接的是真正的常开电源。6. 脚本化提效与面试复盘6.1 三个后端工程师值得收藏的Tcl脚本片段后端工程师的日常很大一部分时间花在看报告和定位问题上。把这个过程脚本化能省出大量时间。分享三个我实际用下来觉得最有价值的Tcl脚本片段。第一个是从congestion报告里自动提取热点坐标。跑完reportCongestion -dir all -overflow 0.05 -util 0.8 -outfile cong.txt之后再用一小段Tcl把报告里所有坐标区域解析出来并输出每个热点区域对应的模块名称方便快速定位是哪个block出的问题set fp [open cong.txt r] set data [read $fp] close $fp foreach line [split $data \n] { if {[regexp {^RECT: ([0-9]) ([0-9]) ([0-9]) ([0-9])} $line match x1 y1 x2 y2]} { set insts [dbQuery -area [list $x1 $y1 $x2 $y2] -obj insts] puts Congested area: ($x1,$y1)-($x2,$y2), insts: [llength $insts] foreach inst $insts { puts [dbGet top.insts.name -this $inst] } } }第二个是批量汇总时序违例路径并聚合到模块。跑完report_timing之后解析出所有违例path的endpoint再用dbQuery查每个endpoint属于哪个hierarchical module最后按模块统计违例数量。这个脚本的价值在于它能把几百条零散的violation转换成一张模块维度的清单一眼看出哪个模块是时序问题的重灾区优先投入人力去修。第三个是自动检查power pin连接。每次做完floorplan或者低功耗cell插入之后跑一个快速检查确认所有always-on cell的VDD pin都接在常开电源域上所有普通cell都接在正确的电压域上。脚本核心就是遍历指定区域内的instance检查power pin net是否符合预期不匹配就输出告警。这个脚本在项目后期每周跑一次成了低功耗检查的第一道防线。这三个脚本单独看都不复杂但组合起来能形成一套很高效的自动化体检流程。每跑完一版flow先跑脚本A看congestion热点再跑脚本B看时序分布最后跑脚本C确认电源连接三份报告加起来不用十分钟就能对整版状态的健康程度有一个全局判断比逐条翻log高效得多。6.2 面试中被反复问到的后端问题都在这后端工程师面试特别是数字IC相关的岗位问的项目问题其实都逃不出前面讲的这几类。那些手撕题、笔试编程题背后的知识点也是相通的。面试官最常问的是你项目中遇到过最难的congestion问题是怎么解决的这时候千万不要只回答我用工具调了一下参数要把问题讲成一个完整的故事问题现象是什么congestion map哪里红、overflow数值多少怎么定位的发现是两个macro通道过窄、pin access受限为什么这样会导致问题走线资源不足的本质是需求大于供给采用了什么方案拉大macro间距、加halo、控制局部density最终效果如何overflow从多少降到多少timing有没有改善。再比如CTS后出现skew异常怎么排查这个问题考察的是对时钟树综合的理解。回答的关键点在于先确认CTS是否建立了正确的时钟树结构再检查SDC时钟约束是否一致然后对比Innovus和STA工具的clock report。如果能把自己踩过的坑——比如边界约束重复设置导致两边skew对不上——讲清楚面试官会明显觉得你是有实际经验的而不是背八股文。还有一类高频题是低功耗后端设计怎么做要能说出voltage area划分的基本思路、level shifter和isolation cell的插入规则、以及always-on buffer的电源连接要求。如果能再补充一个实际项目中的案例比如level shifter数量过多的优化过程那就更有说服力了。面试这件事跟后端项目排查问题的逻辑是一模一样的光知道结论没用得能把问题链路完整讲清楚。这套问题—定位—根因—处理—预防的思路既是做项目的核心方法也是面试答辩的核心框架。把项目记录里的每个问题用这个框架过一遍面试无论怎么问都能接得住。整理到这儿其实还有个感受没说完。做后端项目这两年最值钱的东西不是最终tapeout的那一版GDS而是每次遇到问题之后的复盘记录。新同学进来与其让他们拿文档从零学起不如直接把这份问题清单甩过去挨个踩一遍比看十遍教程都管用。以后大概率还会遇到新的坑但有了这套「问题—根因—动作」的记录方法至少每一跤都不会白摔每一版问题记录都能变成下一版项目的排雷手册。
返回列表