1. 时序报告怎么看:先分清Cell Delay和Net Delay的账
1.1 一条时序路径的延迟到底是怎么算出来的
做数字后端的人,几乎每天都要跟PT(PrimeTime)打交道。不管是做综合后的快速评估,还是布局布线后的signoff时序收敛,最后所有问题都会汇总到那一份份时序报告里。而报告里最常出现、也最容易让人看得头大的两个名词,就是Cell Delay和Net Delay。
Cell Delay,说白了就是信号经过一个标准单元(比如与非门、反相器、触发器)内部所花费的时间。这个值取决于单元的工艺模型、输入转换时间(Input Transition)、输出负载电容,还有单元本身的驱动强度。Net Delay则是信号在这个单元的输出引脚到下一个单元输入引脚之间的互连线上花费的时间。在先进工艺下,Net Delay通常由线电阻、线电容和耦合电容决定,情况复杂得多。很多刚入行的朋友一看到时序违例,第一反应就是“这条路径太长了”,但如果连到底是Cell Delay占大头还是Net Delay占大头都没分清楚,后面所有的优化动作都会跑偏。
我们来算一笔最简单的账。假设有一条路径,数据从A触发器的Q端出发,经过两级组合逻辑,到达B触发器的D端。你在PT里报出来的路径延迟可能是这样:
Startpoint: reg_A/CK (rising edge-triggered flip-flop clocked by clk) Endpoint: reg_B/D (rising edge-triggered flip-flop clocked by clk) Path Group: clk Path Type: max Capture clock edge 5.000 Launch clock latency 0.300 Clock network delay 0.200 (propagated) ------------------------------------------------ Data required time 4.900 Data arrival time -4.720 ------------------------------------------------ Slack 0.180这个例子slack是正的,不违例。但假如中间某一段组合逻辑特别重,arrival time变成了5.010,slack变成-0.110,那问题就来了。你需要在报告里往下翻,逐行看每个pin上的transition、每个cell的delay、每段net的delay。你会发现,某些cell的delay特别扎眼,比如一个INV_X1在负载很大的情况下delay能有100多ps,而旁边同样功能的INV_X4只有40ps左右。另一条路径上,一个跨模块的长走线net delay甚至能占到总延迟的60%以上——这时候去换cell驱动强度就是白费力气,问题出在物理位置上。
1.2 没看清报告就动手改,是最容易踩的坑
我很早以前犯过一个典型错误。当时拿到一个新工艺节点的后端项目,有一条setup violation大概违了150ps。我看了前几行报告,发现中间有个LATENCY很大的时钟路径,就顺手把CTS的target skew调紧了一点,结果重新跑完PT,违例非但没变好,旁边几条原本干净的路径反而开始出现hold violation。
后来才发现,真正的问题根本不在时钟路径上,而是数据路径里某个逻辑锥末端接了太多负载,那个输出cell的transition已经大到快1ns了,完全可以把中间那级cell直接换成驱动能力更高的型号。当时我只盯着时钟网络,属于典型的“没看完全部报告就动手”。
所以拿到一份PT时序报告,我现在的习惯是“三步走”:
- 第一步,看路径的起点、终点和slack,搞清楚是哪一种违例(setup还是hold)、在哪个时钟域、哪条path group。
- 第二步,横向扫一遍整个路径的delay分配,把每个cell delay和net delay都列出来,看占比。如果cell delay总和占80%以上,那就是逻辑和单元的问题;如果net delay占40%以上,优先怀疑物理实现和拥塞。
- 第三步,再往下看transition和capacitance是否违反DRC上限——很多时候timing问题只是表面现象,真正要处理的是transition violation。
这三步走完,心里基本就有数了。如果一上来就急着优化,很容易把简单问题越改越复杂。
2. 定位Cell Delay违例:先分清“是慢还是快”
2.1 从逻辑级数和transition看第一个warning sign
Cell Delay占主导的场景,最常出现在逻辑级数偏长的路径里。比如一个复杂的加法器、比较器或者数据选择器,综合工具在一开始可能没有把逻辑深度压到足够小,最后到了布局布线阶段,多出来的那一级逻辑就会变成实打实的延迟。
打个比方,Cell Delay就像你开车经过城市的每一个红绿灯路口,每个路口都要停车等灯,就算每段路很短,红绿灯多了,整个行程也会很慢。逻辑级数就是红绿灯的数量,transition就是每个路口的起步速度。如果某个cell的输入transition特别大,意味着前一级的输出信号“爬坡”太慢,这一级cell翻转得就不干脆,延迟自然就上去了。
我之前遇到过一个实际案例,某条路径上有8级组合逻辑,其中第4级是一个NOR2_X1,fanout有6个,输出负载超过了1.2pF,transition直接到了0.93ns。PT报告里显示这个cell的delay是0.18ns,而同一路径上另一个NOR2_X2的delay只有0.07ns。这里有两个问题叠加在一起:一是这级cell的驱动能力太弱,带不动6个fanout;二是6个fanout本身就意味着下游负载过大,相当于一辆小排量车拖着一辆大挂车,跑不快是必然的。
所以在定位Cell Delay违例的时候,我通常先看两点:第一,路径上有没有transition特别大的pin,一般先进工艺下超过0.2ns ~ 0.3ns就要警惕;第二,有没有fanout特别集中的net,fanout一多,就算后面的每个cell本身不大,累计的输入电容也会拖慢前级。这两个信号在PT报告里都很明显,只要不是只看最底下的slack summary,基本都能发现。
2.2 换cell、调驱动、降load的先后次序
如果确认了某条路径上Cell Delay占大头,接下来就是怎么改的问题。很多人第一个念头就是“把cell换大”——把X1换成X2,X2换成X4。这个思路本身没错,但不是所有时候都管用。
我自己的经验排序是:先降load,再换驱动,最后才考虑改逻辑结构。
先降load,就是说把某个cell后面挂着的重负载分摊出去。常见做法包括:把fanout大的net做buffer tree,或者在逻辑允许的前提下复制一份逻辑,把负载拆成两路。这样做的好处是几乎不改变原来的逻辑结构,风险最低。比如上面那个NOR2_X1带6个fanout的例子,我第一个动作就是在中间插一级BUF_X4,把负载一分为二,让NOR2_X1的transition从0.93ns降到了0.4ns以下。光是这一个改动,路径总延迟就能吃掉一大截。
再换驱动,就是把原来驱动能力不够的单元换强一点。这里有一个容易被忽略的细节:直接换大cell有时候会引入新的hold问题,而且功耗和面积都会涨。所以换的时候要有度,不要一上来就把所有cell都换成X4。通常我会挑transition最大、delay占比最高的两三处,换完之后重新评估。
至于改逻辑结构,那是最后的手段。比如把串联的长逻辑链拆成并行,或者把某些数据选择逻辑提前到前一级做,这种改动的风险相对较高,需要回归验证功能。一般只有在单元级优化效果有限、确实没有别的办法时才动这一层。
3. 定位Net Delay违例:不是所有线长都能怪绕线
3.1 线延迟占比高,第一反应看floorplan
Net Delay的优化思路和Cell Delay完全不一样。Cell Delay是单元内部的事,Net Delay是单元之间的事。当你看到一条路径里net delay加起来比cell delay还高的时候,不要第一时间觉得“绕线工程师不行”,而要先去查物理位置——是不是这两个cell在floorplan上隔得太远了。
我见过的最典型的例子,是一条跨模块的数据总线,起点在模块A的角落里,终点跑到模块B的另外一侧,中间跨越了大半个芯片。绕线工具已经尽力走最短路径了,但物理距离摆在那里,线延迟降到一定程度就再也降不下去了。这种问题,你在PT报告里反复优化cell驱动是没用的,因为延迟的大头在互连线上,它跟线长、线宽、层数和耦合电容有关。
那怎么判断是物理距离的问题还是绕线路径的问题?一个很实用的方法是看net的layer和总长度。如果整条net都在M4、M5这些中间层上绕来绕去,而且长度动辄几百微米,多半是floorplan或者Pin assignment的锅。如果报出来的net长度并不夸张,但delay很大,那就要怀疑是绕线路径经过了太多via,或者旁边有长距离并行线带来了较大的耦合电容。
处理这类违例,我一般从三个方向入手。第一,检查两端cell的相对位置,能不能在floorplan层面把距离缩短,跨模块的关键路径最好在早期就做好约束和budget。第二,看route guidance和blockage,是不是关键路径被某些macro或者hard block挡住,导致绕线工具只能绕远路。第三,必要时可以直接给这条net加route_type的约束,让它走高层金属,高层金属电阻小、绕线宽度大,延迟会低不少。
3.2 从congestion map到具体layer调整的实操
如果距离没问题,那就继续往下查:是不是拥塞导致绕线工具走了弯路。
有一个很直观的类比:Net Delay就像快递配送,cell是发货点和收货点,绕线资源是道路。如果某一片区域道路拥堵严重,快递员只能绕行,那就算发货点和收货点就在隔壁,配送时间也会很长。优化Net Delay,本质上就是给快递员开通一条高速通道,或者错开拥堵区域。
怎么做呢?在布局布线工具里,先打开congestion map,看那条违例net附近的绕线资源情况。如果红色拥塞区域刚好覆盖了net的路径,那就先通过调整blockage、soft macro placement或者cell padding来降低拥塞,让绕线工具能够选一条更直的路径。如果整体拥塞不严重,只是某条net本身走线质量差,那就直接在SDC或route constraint里指定net的layer范围,比如允许它走M6、M7,同时关闭该区域的blockage限制。
实操中还有一个细节:不要只看timing报告里的那一条net,要多看它周围的sibling net。一段区域如果同时出现多条net delay偏大,大概率是局部拥塞或者电源网络(PG)遮挡住了绕线资源,单独修一条net是修不完的,要从布局层面想办法。
4. 真实案例分析:一条违例路径从PT报告到收敛的全过程
4.1 案例背景和原始违例报告
这里分享一个真实项目中处理过的案例。工艺节点是7nm,模块规模大概200万门,运行频率1GHz。后端流程走到place & route之后,做第一版signoff时序收敛时,出现了一条setup violation,违例量是负的180ps左右。
我把PT报出来的数据路径delay打开,逐行看延迟分布:
Data arrival time: ------------------ reg_A/CK (clock network delay) 0.120 reg_A/Q (CK-to-Q delay, seq cell) 0.082 U1234/ZN (NAND2_X2, cell delay) 0.064 n1234 (net delay) 0.311 U2345/ZN (NOR2_X1, cell delay) 0.148 n2345 (net delay) 0.087 U3456/ZN (BUF_X8, cell delay) 0.055 n3456 (net delay) 0.121 U4567/ZN (AOI22_X1, cell delay) 0.077 n4567 (net delay) 0.093 reg_B/D (setup requirement) -0.020 ------------------------------------------------注意看,n1234这一段的net delay居然有0.311ns,而前后cell delay都只有0.06~0.08ns的量级。这条路径上总数据延迟大概1.17ns,光n1234这一段就占了将近27%。而这条net从NAND2_X2的输出到NOR2_X1的输入,物理距离并不算特别夸张,问题在于它跨过了两个macro之间的窄通道,绕线工具只能走M2/M3层,几经波折才连上,中间还打了好几个via,层间寄生电阻一叠加,delay自然就大了。
4.2 修复过程:按报告逐级排查
我当时的处理顺序是这样:
第一步,先看floorplan里这两个cell的物理位置。打开布局图,确认NAND2_X2在模块左下角,NOR2_X1在右侧靠近macro的位置,中间刚好夹着一个SRAM macro和一条电源strip,导致绕线资源很紧张。
第二步,检查这条net是不是需要跨区域连接。确认了它不是跨模块的关键路径,只是模块内部因为macro摆放过密导致绕线受阻。于是我先尝试了给这条net手动加一条局部routing guide,让它走M5绕开macro区域。重新跑了一遍route和PT,net delay从0.311ns降到了0.204ns,但离目标还差一点。
第三步,调整cell位置。把NAND2_X2从macro边上往右挪了大概100um,让它更靠近NOR2_X1的入口方向,重新布局布线后,net delay降到了0.132ns。与此同时,NOR2_X1那个0.148ns的cell delay在新的负载条件下也降到了0.094ns,因为net短了,total load也变小了。
第四步,确认没有引入新的Violation。重新跑完PT、DRC和LVS,setup slack从-0.180ns变成了+0.045ns,这条path成功收敛。同时相邻几条路径没有出现新的违例,说明这个局部修改没有造成附带损伤。
4.3 最后的结果和复盘
这个案例最终只改动了两个cell的placement和一条routing guide,没有动逻辑、没有换工艺库,也没有牺牲功耗和面积。整个过程花了大半天,大部分时间耗在定位那条0.311ns的net delay上。
复盘下来有一个很关键的教训:第一版时序报告出来的时候,如果光看slack是负数,很容易让人焦虑,然后病急乱投医。但只要把报告里每个delay分量摊开,找到那个明显不合理的异常点,问题往往就变成了“为什么这条net这么慢”而不是“为什么这条路径违例”。从异常点入手,比从违例结果入手要高效得多。
5. 常见坑与排查技巧
5.1 CRPR和时序报告里的那些“隐身延迟”
有一个细节,很多做后端一段时间的朋友也容易忽略:PT报告里的clock network delay,并不等于时钟到达触发器的真实时间,里面还藏着CRPR(Clock Reconvergence Pessimism Removal)的处理逻辑。
简单说,launch clock path和capture clock path在时钟树中会有一段公共路径,这段路径上的延迟是共享的,PT在计算时序时会把这段公共路径的悲观量去掉。CRPR本身是好事,它避免了因为公共路径的片上偏差被重复计算而导致的过度悲观。但它也带来一个副作用:你在报告里看到的某些数值不是实数,而是经过correction之后的“可用余量”。有些新手看到某个cell delay特别大,想去优化,但仔细一查,那是公共路径上的cell,CRPR已经把它对slack的影响抵消掉了——这种情况下动手优化它,效果会非常有限。
另外一个常见的“隐身延迟”是OCV(On-Chip Variation)的derate factor。在signoff模式下,PT会对cell delay和net delay分别加一个early/late derate系数。报告里一行行看下来,有些delay看起来偏大,可能就是被derate放大了。这种情况下,优化方向不一定是改cell或net,而是看能不能通过调整时钟结构的平衡性、减小时钟路径上的公共部分偏差来释放悲观量。
5.2 timing budget:多模块协同时的另一个维度
前面聊的都是在单个模块内部处理违例的经验,但实际项目中很多路径是跨模块的,这时候就绕不开timing budget的问题。
简单说,timing budget就是在顶层芯片划分多个子模块时,把顶层时序路径的约束分配到各个子模块头上,让每个模块在做独立收敛时都能有自己的时序目标。如果不做budget,模块A以为自己只要跑到2ns就够了,结果到了顶层集成的时候发现路径总预算只有1.8ns,这时再回头做收敛,成本会高出很多。
我在实际项目里比较常用的做法是:先用顶层网表跑一遍全局时序,把每一条跨模块路径按物理边界切分,然后用PT导出每个模块的接口约束(包括输入延迟、输出延迟、虚拟时钟等),再把这些约束反标回子模块的时序脚本里。这样每个子模块在设计的时候,并不知道顶层链路上别的地方是什么样,但时序约束已经替它”预留“好了余量。
这里有一个经验要分享:timing budget不要把余量压得太死。接口约束上留出5%到10%的余量,既能逼着模块内做到足够好,又不至于让前端团队在逻辑综合阶段就失去优化空间。预算一旦定死,后期修改成本很高,所以最好在项目早期就跟前端、Floorplan团队对齐清楚。
5.3 几个实用的排查技巧速查
整理几个我自己用的比较多的绕坑技巧,希望能有帮助:
- 看PT报告时,先过滤出所有transition超过阈值的pin,很多时候transition大就是cell delay偏大的第一原因,先处理这里,比盲目换cell更有效。
- 修setup违例时,优先看路径上的“高叶子浓度”节点,也就是驱动很多单元的节点,在这些节点附近插buffer或换驱动,效果通常最明显。
- 如果一条net的delay明显偏高但长度并不长,优先排查它是否走了低层金属,或者中途经过太多层切换(via数量多)。via的寄生电阻在高频下很可观,能让net delay成倍上涨。
- 注意hold time分析中的clock skew,setup修完之后一定要重新跑一遍完整PT确认hold没有恶化。后端流程中setup和hold往往是一个跷跷板,按下葫芦浮起瓢的情况太常见了。
- 数据路径过长时,优先确认有没有不合理的多周期约束(multicycle path)。有时候路径不需要在同一个时钟周期内完成,约束改对了,违例自然消失,根本不用去动物理实现。
结尾(个人经验收尾)
我在实际项目里最常说的一句话是:时序报告不是用来“看”的,是用来“审”的。每一行delay数值背后都是一段具体的物理实现,可能是某个cell的驱动能力选小了,可能是floorplan里某个macro挡住了路,也可能只是约束里忘了一条multicycle path。多问几个为什么,把问题拆成Cell Delay和Net Delay两大块去排查,大部分违例都能找到明确的优化方向。希望这篇从PT时序报告实战出发的拆解,能帮你在下次遇到负slack的时候少走一些弯路。记住,真正有经验的工程师不是没有遇到过违例,而是知道从报告里最快锁定那一个值得改的地方。