
1. ICG时序违例到底难在哪做数字后端的人十个里有八个被ICG相关的setup违例折磨过。尤其是CTS做完之后时钟树已经长出来了这时候报告里突然冒出一堆跟clock gating cell相关的时序问题那感觉就像装修完了才发现水管漏水——返工成本极高但不修又不行。先把这个问题的本质说清楚。ICG全称Integrated Clock Gating cell中文叫集成时钟门控单元。它的作用是在电路不需要工作的时候把时钟掐断从而省掉大量动态功耗。你可以在几乎任何一颗低功耗芯片里找到成百上千个ICG。但问题在于ICG本身是一个时序器件它内部有latch或者flip-flop来采样enable信号这就意味着它天然会引入额外的时序路径和约束需求。CTS之前时钟还是理想时钟工具对ICG的检查相对宽松。CTS之后真实的时钟树插入延迟进来了clock skew变成了一个实实在在的数值ICG的enable信号需要在正确的时钟窗口内到达否则要么时钟被意外掐断功能错误要么时钟该掐没掐功耗浪费要么setup/hold直接违例。这三种情况里setup违例最常见也最让人头疼。这篇文章面向的是已经有一定STA和PnR基础的工程师不管你是刚接手CTS后时序修复的新手还是已经修过几轮但总觉得效率不高的老手下面这些内容应该都能帮你理清思路。我会从ICG的时序检查原理讲起然后重点拆解set_clock_gating_check这个命令的用法和参数选择逻辑再结合CTS后的实际场景给出可复现的修复流程最后把我自己踩过的坑和排查技巧一并倒出来。2. ICG时序检查的核心原理拆解2.1 ICG内部结构与时序路径分析要修ICG的setup违例你得先搞清楚ICG内部到底长什么样。常见的ICG有两种结构基于latch的latch-based和基于flip-flop的FF-based。低功耗设计里用得最多的是latch-based ICG因为它面积小、功耗低而且能在时钟低电平期间透明采样enable信号。一个典型的latch-based ICG包含一个负电平敏感的latch和一个与门或者或门取决于时钟极性。enable信号先经过latchlatch的输出和时钟信号一起送到与门与门的输出就是gated clock。这个结构的关键在于latch在时钟低电平期间是透明的enable信号可以在这个窗口内变化当时钟拉高时latch锁存当前值保证gated clock不会出现毛刺。从时序检查的角度看这里涉及两条关键路径。第一条是enable信号到latch数据端的setup路径这条路径的参考时钟是ICG的输入时钟检查的是enable信号必须在时钟上升沿之前稳定下来。第二条是latch的clk端到Q端的传播路径这条路径影响的是gated clock的输出延迟。很多工程师第一次修ICG违例的时候会懵因为报告里显示的起点可能是某个寄存器的Q端终点是ICG的enable端但参考时钟和捕获时钟的关系跟普通寄存器到寄存器的路径不太一样。这就是为什么需要专门用set_clock_gating_check来告诉工具怎么检查。2.2 CTS前后ICG约束的差异CTS之前时钟是理想的工具用set_clock_gating_check设定的setup和hold值直接作为约束不涉及实际的clock skew和insertion delay。这时候你设一个setup值比如0.5ns工具就按0.5ns来检查简单直接。CTS之后情况变了。时钟树已经插入到设计中每个ICG的clk端都有一个实际的insertion delayenable信号的发射端寄存器也有自己的clock latency。工具需要计算enable信号从发射端到ICG数据端的实际到达时间再跟ICG捕获时钟的到达时间做比较。这时候set_clock_gating_check的作用就变成了一个额外的margin叠加在实际的时序计算之上。这里有个很容易被忽略的点CTS之后ICG的enable路径可能跨越不同的时钟域或者经过clock divider、mux等结构。这时候如果你还用CTS之前的约束值要么过约束导致大量假违例要么欠约束导致真实问题被掩盖。我见过不少项目在CTS后直接沿用之前的约束结果修了半天发现方向完全错了。2.3 set_clock_gating_check命令的参数逻辑set_clock_gating_check的基本语法是这样的set_clock_gating_check -setup value -hold value [object_list]其中object_list可以是clock、pin或者cell。如果不指定对象命令会作用于当前设计的所有clock gating check。关键参数解释-setup指定setup检查的额外裕量。这个值会叠加在工具计算的实际时序之上。-hold指定hold检查的额外裕量。CTS后hold问题通常比setup少但也不能完全不管。-clock指定作用于哪个时钟。如果设计有多个时钟域建议分别设置。-rise/-fall指定检查的边沿。对于latch-based ICG通常需要检查enable信号在时钟上升沿前的setup。这里有个经验值可以参考CTS之后如果时钟频率在500MHz以上setup值建议设在0.15ns到0.3ns之间如果频率较低可以适当放宽到0.1ns左右。hold值一般设0.05ns到0.1ns就够了。当然具体值要根据你的工艺、时钟树质量和enable路径的复杂度来调整。注意set_clock_gating_check设置的值是额外裕量不是绝对约束。工具会先计算实际的setup slack再减去这个裕量。所以不要设得太大否则会制造大量假违例浪费修复时间。3. CTS后ICG setup违例的实操修复流程3.1 定位问题从报告到根因拿到CTS后的时序报告第一步不是急着修而是先分类。ICG相关的setup违例通常出现在以下几种场景enable信号路径太长经过多级逻辑延迟太大enable信号来自不同时钟域跨域路径没有正确约束ICG的clock端insertion delay过大导致捕获时钟到达太晚enable信号的发射寄存器本身就有setup问题连带影响ICG我通常会用这样的命令来筛选ICG相关的路径report_timing -to [get_pins -hier *ICG*/EN] -delay max -max_paths 50或者更精确一点report_timing -to [get_pins -hier -filter directionin pin_nameEN] -delay max拿到报告后重点看几个东西起点寄存器的clock latency、路径上的逻辑级数、ICG的clock latency、以及最终的slack值。如果slack是负的但很小比如-0.05ns可能是约束设得太紧如果slack负得很多比如-0.5ns以上那就是真实的路径延迟问题需要从逻辑或物理层面修。3.2 约束调整set_clock_gating_check的正确打开方式约束调整是最快的手段但也是最容易用错的手段。我的建议是分三步走第一步先确认当前设计里ICG的检查值是多少。用这个命令report_clock_gating_check这会列出所有clock gating check的当前设置。如果发现某些clock没有设置或者设置值明显不合理那就需要调整。第二步根据时钟频率和工艺节点设定合理的值。下面这张表是我在多个项目里总结的经验值供参考时钟频率工艺节点setup建议值hold建议值1GHz7nm及以下0.20-0.30ns0.08-0.12ns500MHz-1GHz12-16nm0.15-0.25ns0.05-0.10ns200-500MHz22-28nm0.10-0.15ns0.05-0.08ns200MHz40nm及以上0.05-0.10ns0.03-0.05ns第三步分时钟域设置。不要用一个全局值打天下。比如set_clock_gating_check -setup 0.20 -hold 0.08 [get_clocks clk_core] set_clock_gating_check -setup 0.15 -hold 0.05 [get_clocks clk_peri]这样做的好处是高频时钟域用更严格的约束保证功能正确低频时钟域用宽松约束避免过度修复。实操心得调整约束后一定要重新跑一次STA对比调整前后的违例数量和slack分布。如果违例数量大幅减少但仍有少量负slack说明约束值基本合理剩下的就是真实路径问题。如果违例数量没怎么变说明问题不在约束上得从物理实现入手。3.3 物理修复从逻辑优化到布局调整约束调完之后还有违例那就得动真格了。物理修复的手段按代价从低到高排列逻辑优化是最温和的手段。如果enable路径上有多级缓冲器或者冗余逻辑可以尝试用工具自动优化或者手动替换成驱动能力更强的单元。比如把普通的BUF换成高驱动的BUF或者把多级逻辑合并成一级。布局调整是中等代价的手段。如果enable路径的起点和ICG距离太远可以尝试把起点寄存器往ICG方向挪一挪或者把ICG往起点方向挪。在Innovus里可以用place_opt或者手动move命令来调整。注意不要为了修一条路径把其他路径搞崩了移动之前先看看周边有没有关键路径。时钟树调整是代价最高的手段。如果ICG的clock latency明显大于enable路径的发射时钟latency可以考虑在时钟树上加buffer或者调整clock tree结构让ICG的clock到达时间提前。但这个操作影响面很大搞不好会把整个时钟树的skew搞乱一定要谨慎。我个人的修复顺序是先调约束再试逻辑优化然后布局调整最后才动时钟树。大部分ICG setup违例在前两步就能解决。3.4 跨时钟域ICG的特殊处理跨时钟域的ICG是最容易出问题的地方。比如enable信号从clk_a域来ICG的clock是clk_b这时候setup检查的参考时钟和捕获时钟不同工具默认的检查方式可能不适用。这种情况下我通常会用set_clock_groups先把两个时钟域设成异步set_clock_groups -asynchronous -group {clk_a} -group {clk_b}然后针对ICG单独设置检查set_clock_gating_check -setup 0.25 -hold 0.10 [get_clocks clk_b]如果enable信号确实需要跨域传输那必须加同步器比如两级flip-flop同步否则不仅时序有问题功能上也可能出现亚稳态。这一点在低功耗设计里尤其重要因为ICG的enable信号如果出现毛刺可能导致时钟被意外掐断整个模块直接挂掉。4. 常见问题与排查技巧实录4.1 违例修不完先检查这几个地方很多人反映ICG setup违例越修越多修完一条又冒出来三条。这种情况通常不是修复方法有问题而是约束或者设计本身有系统性缺陷。我建议按以下顺序排查第一检查是否有clock gating check作用在了错误的时钟上。比如把高频时钟的约束误设到了低频时钟上导致低频路径被过约束。第二检查enable路径上是否有组合逻辑环或者多驱动。这种情况工具会报奇怪的违例但根因不在时序上。第三检查ICG的clock端是否连接了错误的时钟。有时候clock tree synthesis会不小心把ICG的clock接到相邻的时钟分支上导致latency异常。第四检查是否有未约束的时钟域。如果某个时钟没有create_clock工具会用默认值检查结果往往不可信。4.2 约束值设多少才合适这个问题没有标准答案但有一个实用的判断方法先把setup值设成0跑一次STA记录ICG相关路径的slack分布。如果大部分路径的slack在0.1ns以上说明设计本身没问题可以设一个0.1-0.15ns的裕量。如果很多路径的slack在0附近甚至为负说明设计本身就有问题设再小的约束也没用得先修设计。另外不同阶段的约束值应该不一样。CTS之前可以设得宽松一些CTS之后要收紧signoff阶段要最严格。我一般会在CTS后的第一次STA用中等值然后根据结果逐步调整。4.3 ICG setup和hold的权衡setup和hold是一对冤家修setup的时候很容易把hold搞坏反之亦然。ICG的hold问题通常出现在enable信号变化太快的时候latch还没来得及锁存新值时钟就来了。如果setup和hold同时违例优先修hold。因为hold违例是功能性的setup违例在低频下可能只是性能问题。修hold的手段通常是加延迟单元delay cell在enable路径上但这会增加setup的负担。所以加delay的时候要算好加多少刚好能修hold又不至于让setup崩掉。实操心得在Innovus里可以用set_clock_gating_check -hold单独设置hold裕量不要跟setup混在一起调。先修hold再修setup顺序不能反。4.4 工具报告与实际不符怎么办有时候工具报告ICG setup违例但你手动算了一下发现不应该违例。这种情况通常是以下原因时钟不确定性clock uncertainty设得太大吃掉了太多裕量片上偏差OCV的derate值设得不合理报告里的路径经过了不需要检查的逻辑比如测试逻辑排查方法是先用report_timing -unconstrained看看有没有未约束的路径再用report_clock_property检查时钟的uncertainty和latency设置。如果确认是约束问题调整后重新跑STA即可。4.5 常见问题速查表问题现象可能原因排查命令解决方向ICG setup违例数量多且slack小约束值过紧report_clock_gating_check放宽setup值单条ICG路径slack负很多enable路径延迟大report_timing -to ICG/EN逻辑优化或布局调整跨时钟域ICG违例时钟域未正确分组report_clock_groups设异步时钟组修完setup后hold违例延迟单元加太多report_timing -delay min调整delay cell数量工具报告与手动计算不符约束或derate问题report_clock_property检查uncertainty和OCV5. 几个容易被忽略的细节5.1 ICG的测试模式约束很多设计在测试模式下会 bypass 所有的ICG让时钟直接穿透。这时候ICG的enable端可能被强制拉高或者拉低时序检查的对象就变了。如果你在功能模式下修好了ICG setup但测试模式下又冒出一堆违例那就要检查测试模式的约束是否正确。通常的做法是在测试模式下用set_case_analysis把ICG的enable端固定住然后禁用clock gating checkset_case_analysis 1 [get_pins -hier *ICG*/EN] set_clock_gating_check -setup 0 [get_clocks clk_core]这样工具就不会在测试模式下检查ICG的setup了。5.2 低功耗模式下的ICG行为如果设计支持多电压域或者电源关断ICG的行为会变得更复杂。比如在某个电源域关断的时候ICG的enable信号可能来自一个已经断电的模块这时候时序检查的结果没有意义。需要在低功耗模式下用set_case_analysis或者set_power_state来正确约束。5.3 时钟树上的ICG级联有些设计会把多个ICG级联起来形成时钟门控树。这种情况下每一级ICG都有自己的setup检查需求而且上一级ICG的输出时钟会成为下一级ICG的参考时钟。如果级联层数多约束会变得很复杂。我的建议是尽量减少ICG级联的层数能并行就并行实在需要级联的话每一级都要单独设置clock gating check。6. 写在最后ICG的setup违例修复说到底是一个“理解原理、合理约束、精准修复”的过程。set_clock_gating_check这个命令看起来简单但用好了能省掉大量修复时间用错了反而会制造更多问题。我的经验是CTS后的第一次STA一定要仔细分析ICG相关的路径把约束调到一个合理的范围然后再动手修物理问题。另外不同工具对set_clock_gating_check的支持程度不太一样。Innovus和PrimeTime的语法基本一致但某些参数可能有细微差别。建议在使用前先查一下所用版本的命令手册确认参数含义。最后分享一个小技巧如果你不确定setup值设多少合适可以先设一个比较大的值比如0.3ns跑一次STA看看违例的slack分布。如果大部分违例的slack在-0.1ns以内说明实际路径的裕量是够的可以把约束放宽到0.15ns左右再跑一次。这样迭代两三次就能找到合适的值比一次性拍脑袋设一个值靠谱得多。