在数字IC后端流程里,CTS(时钟树综合)可能是被误解最多的环节。很多人以为CTS就是把时钟缓冲器插一插、让时钟到每个触发器的时间差不多,跑完工具报个0 violation就完事。但真正做过几个项目之后你会发现,CTS阶段的每一个决策都在为后面的布线、签核和芯片回来之后的调试买单。时钟树做得好,后端流程顺风顺水;时钟树做得糙,后期setup/hold修到怀疑人生,甚至芯片回来之后因为时钟偏差导致功能失效。
这篇文章不打算从教科书定义讲起,而是从一个后端工程师的实际视角,拆解CTS优化中真正影响结果的几个关键点:偏差和延迟怎么权衡、约束怎么设置才能让工具干正确的活、分层CTS什么时候该用、以及我在实际项目中踩过的那些时钟树大坑。内容以Innovus/ICC2这类主流工具的通用逻辑为主线,不绑定具体版本,适合正在做后端实现或者准备往后端方向深耕的工程师参考。
1. 时钟树综合在数字IC后端流程中的位置:它到底卡住了多少问题
1.1 CTS之前,布局阶段已经决定了时钟树的命运
很多新人会有个误解,觉得CTS是从工具跑到时钟树综合这一步才开始的。实际上,从布局(Placement)阶段开始,时钟树的雏形就已经被悄悄决定了。布局决定了每个触发器(Flip-Flop)的物理位置,而时钟树综合的核心就是把这些散布在芯片各处、物理距离可能相差几千微米的寄存器,用一组缓冲器串联起来,让时钟信号从时钟源点到达每个寄存器的时钟引脚时,延迟足够小、偏差足够小、信号边沿足够陡峭。
如果你在布局阶段没有考虑时钟结构,比如把某个模块的寄存器打散到芯片的各个角落,那CTS阶段工具再怎么优化,也很难在物理上做出一个漂亮的时钟树。因为时钟树的走线长度、缓冲器级数、负载电容,都直接受寄存器物理分布的影响。这也是为什么有经验的工程师在做布局规划时,就会提前看floorplan里各个寄存器群的聚散程度,甚至在place阶段就会对高频模块做region约束,让寄存器尽可能集中。
我见过一个比较典型的场景:某个模块的寄存器分布非常散,CTS做完之后clock skew达到400ps以上,而设计的总预算只有600ps。后来反复调整CTS约束效果都不理想,最后回到布局阶段,把寄存器的分布重新约束,让关键路径上的寄存器聚集在几个区域内,再跑CTS,skew直接降到150ps以内。所以说,CTS优化不是一个独立的步骤,它跟前端布局紧密耦合。做CTS优化之前,先检查floorplan和placement质量,往往比死磕CTS参数更有效。
1.2 CTS失败后的连锁反应:为什么setup/hold都会崩
时钟树综合的核心任务,是让时钟信号在规定的时序预算内到达每个时序单元。如果CTS做不好,最直接的后果就是时钟偏差(skew)变大,导致建立时间(setup)和保持时间(hold)同时出问题。
打个比方,建立时间要求数据在时钟边沿之前稳定下来,如果时钟到达某个触发器的路径特别长,数据在另一个触发器的输出端已经变化了,但时钟还没到,那这个触发器采样到的就是旧数据或者中间态。这在高速设计中是非常致命的。保持时间则是要求数据在时钟边沿之后保持稳定,如果时钟到达得太快,数据变化也太快,触发器可能会采到变化中的数据。
CTS做得不好的时候,工具会在后续的布线阶段尝试修复这些时序违例,但布线阶段的修复能力是有限的。尤其是hold违例,如果时钟偏差太大,需要插入大量延迟单元来平衡,这不仅增加面积,还会严重影响功耗和布线拥堵。setup违例就更难办了,因为你不能让数据路径再缩短,只能靠优化逻辑或者调整时钟树,手段非常局限。所以,CTS阶段宁可稍微多花点时间把时钟树做扎实,也不要为了赶进度草草跑完,后期返工的代价远大于前期优化的成本。
1.3 CTS的真正交付标准:不是“平衡”,而是可收敛
我刚做后端那会儿,以为CTS的目标就是让所有触发器的时钟延迟尽量一致,skew越小越好。后来被一个老师傅点醒:CTS的交付标准不是“平衡”,而是让整个设计的时序在后续布线阶段可以收敛。
这句话怎么理解?时钟树综合本质上是在做一个多目标优化:
- 时钟延迟(Insertion Delay)尽可能小,减少对时序收敛的压力;
- 时钟偏差(Clock Skew)控制在可接受范围内,避免setup/hold违例;
- 时钟树的功耗尽量低,尤其是在高频翻转的时钟网络上;
- 时钟树的DRC(最大负载电容、最大转换时间、最大扇出)全部满足工艺库要求;
- 留出足够的余量给后面的布线阶段和OCV效应。
这五个目标往往是相互矛盾的。你想减小延迟,就得减少缓冲器级数,那负载大的分支可能就需要更宽的走线或者更大的缓冲器;你想让skew小,就得在延迟长的分支上加缓冲器补偿,结果延迟变大了,功耗也上去了。
所以,做CTS优化时要有一个“全局最优”的意识,而不是追求单一的“最小skew”。真正好的CTS,是在给定约束下,找到一组缓冲器尺寸、级数和拓扑结构,让整体时序、功耗、面积达到一个平衡点。
2. CTS的核心原理:偏差、抖动与skew的三方博弈
2.1 从时钟源点到触发器时钟引脚:延迟是怎么组成的
做CTS优化之前,先搞清楚一个基本概念:时钟信号从时钟源点(比如PLL输出或者芯片的时钟输入引脚)到达某个触发器的时钟引脚,这段路径上的总延迟,在时序分析里被称为时钟延迟(Clock Latency)。它由两部分组成:
- 源延迟(Source Latency):从时钟源点到时钟树根节点的延迟。在片上时钟生成时,这通常是PLL输出到时钟树根节点的片上走线延迟。
- 网络延迟(Network Latency):从时钟树根节点,经过各级缓冲器(Clock Buffer/Inverter),到达触发器时钟引脚的延迟。
CTS优化的重点就是网络延迟部分。工具通过插入不同驱动能力的缓冲器、调整走线长度、改变缓冲器级数,来匹配不同分支的网络延迟,最终达到时序目标。
在实际项目中,还要区分“理想时钟”(Ideal Clock)和“真实时钟”(Propagated Clock)。在布局阶段,工具通常假设时钟是理想的,也就是所有触发器的时钟边沿同时到达,没有延迟差异。但到了CTS阶段,时钟开始走真实物理路径,必须通过时钟树传播计算每个端点上的实际延迟。这时候,之前理想时钟状态下看似没问题的时序路径,会因为时钟延迟差异而暴露出真实的setup/hold情况,这也是很多设计在CTS之后出现大量时序违例的原因。
2.2 时钟偏差、有用偏差和不确定性:三者必须分开理解
时钟偏差(Clock Skew)指的是同一个时钟域内,时钟信号到达不同触发器时钟引脚的时间差。假设时钟到达FF1的时间是1.2ns,到达FF2的时间是1.5ns,那这两个触发器之间的时钟偏差就是300ps。
偏差并不总是坏事。在时序分析中,如果时钟信号先到达发起触发器(Launch FF),后到达捕获触发器(Capture FF),那数据在捕获端会多出300ps的“额外时间”,这相当于变相放宽了setup余量。这种主动利用时钟到达时间的差异来优化时序的做法,叫做有用偏差(Useful Skew)。
举个例子,假设某条路径的setup slack是-100ps,也就是建立时间违例了100ps。如果我们在时序收敛允许的范围内,让捕获触发器的时钟比发起触发器的时钟晚到120ps,那这条路径的setup slack就变成了+20ps,违例就被修复了。这是CTS优化里非常强大的手段。
但有用偏差不是随便用的。它只是在局部路径上“借”了时间,实际上是把时序压力转嫁到了路径的另一端。如果A触发器作为发起端,它的捕获端是B,你让B的时钟晚到,那B作为发起端,它的捕获端是C,时钟也晚了……这个链条可能一环扣一环,处理不好就会引起连锁反应。所以,有用偏差一般只用于修复局部少数违例,不能大规模依赖。
时钟不确定性(Clock Uncertainty)则是一个统称,包含了时钟抖动(Jitter)、时钟边沿的不确定性和时钟树本身的残余偏差。刷过时序的人都知道set_clock_uncertainty,就是给setup/hold分析加上一个悲观值,覆盖实际芯片工作中各种不确定因素的影响。这个值设得太大,会让时序收敛变得不必要地困难;设得太小,可能又无法覆盖真实的抖动和偏差风险。CTS之后,要特别关注工具报告里的clock uncertainty值,确认它跟约束文件里设置的一致,没有被工具额外叠加或抵消。
2.3 为什么“越平衡越好”是个认知陷阱
很多工程师初学CTS时,会把“clock skew越小越好”当成本能反应。实际上,追求绝对的skew=0不仅不现实,也没有必要。
从物理上看,芯片里几十万甚至上百万个触发器,它们的物理位置分布极不均匀,要让时钟到每个触发器的延迟完全相同,本质上是不可能的。你只能通过增加缓冲器级数、加长走线的迂回,去“补齐”延迟差。这意味着那些物理上离时钟源点很近的寄存器,也要被迫把时钟路径做长,结果就是整个时钟网络的延迟被拉大,功耗上升,同时给setup时序带来更大的压力。
从时序上看,局部区域内的skew控制才是关键。真正影响时序收敛的是“相互有时序关系的触发器对”之间的偏差,而不是所有触发器之间的全局偏差。两个没有任何逻辑路径关系的触发器,即使skew很大,也完全不影响功能。所以做CTS优化时,聪明的做法是优先保证逻辑相关性强的区域——比如同一个功能模块内、同一条关键数据通路上的触发器——它们的skew尽量小,而不是盲目地追求整个芯片时钟树的全域平衡。
这也是tools里面set_clock_tree_options -target_skew这类参数的意义所在:工具在构造时钟树时,会尽量满足目标skew,但它是通过物理聚类算法来平衡的,不是让所有点的时间完全一样。理解了这一点,你在看CTS报告时就不会被那些“skew=50ps”的漂亮数字迷惑,而是会去关心这个skew是否出现在真正需要关心的路径上。
3. 时钟树优化策略:约束设置、工具参数与分层设计的落地做法
3.1 时钟约束的合理设置:uncertainty、latency与transition
很多人跑CTS之前,约束是从综合阶段直接沿用过来的,但CTS阶段对时钟约束的要求远比综合阶段严苛。几个关键参数的设置逻辑我分开讲。
set_clock_uncertainty:这个值要分setup和hold分别设置,不能图省事只设一个。通常setup的uncertainty要覆盖时钟抖动(PLL或内部时钟源)、时钟树自身的残余偏差、以及一定的设计余量,比如100ps到200ps之间。hold的uncertainty则主要覆盖时钟树偏差和一些工艺偏差,通常比setup小一些。如果MMMC(多模式多角)环境下,还要确认不同corner下uncertainty怎么叠加。
尤其需要注意:有些工程师为了“留足余量”,把uncertainty设得特别大,比如set 500ps。结果CTS阶段工具会为了满足这个严格的uncertainty,疯狂插入缓冲器来平衡skew,导致时钟延迟变得巨大,反而把setup路径弄得很紧张。实际项目里我见过因为uncertainty设置过大,CTS之后所有收敛路径都出现大量setup违例的情况。所以,uncertainty要基于真实情况设置:查PLL的jitter规格,根据时钟树的目标skew留出余量,不要拍脑袋。
set_clock_latency:CTS之前,工具会根据约束里的source latency来估算外部时钟源到芯片引脚的延迟。对于片上生成的时钟(比如PLL的输出),source latency一般设为一个较小的值或者直接用create_generated_clock定义好关系。这个地方很容易出错,尤其是多级分频时钟链的时候,如果source latency设置不当,CTS之后不同时钟域之间的相位关系会乱掉,出现“看着时钟树很平衡,但跨时钟域路径全违例”的诡异情况。
set_clock_transition:时钟信号的转换时间直接决定了触发器采样的时间精度和功耗。转换时间太长,时钟边沿不陡峭,触发器在两个逻辑电平之间停留的时间变长,不仅功耗增大,还可能产生亚稳态风险。CTS工具一般会根据库里的target transition来优化,但你在约束里设置的这个值会影响工具对缓冲器尺寸的选择。不要设置得太激进(比如50ps),否则工具会选大尺寸缓冲器,功耗爆炸;也不要太宽松(比如500ps),否则时序余量被吃掉。一般中等工艺下建议设置在150ps~250ps之间,具体需要结合工艺库的建议值。
3.2 时钟缓冲器与普通缓冲器的区别:为什么不能用普通单元做时钟树
工艺库里会专门提供时钟树专用的缓冲器和反相器(Clock Buffer/Inverter),它们和普通逻辑缓冲器的区别在于:
- 结构对称性更好:时钟专用单元的上升沿和下降沿延迟更匹配,能减少时钟波的占空比失真。
- 延迟对负载的敏感性更低:工艺偏差(PVT Variation)对它们的影响更小,在OCV分析下更稳定。
- 驱动能力档位更细分:方便工具在平衡延迟时做更精细的微调。
所以在做CTS时,一定要配置好库单元列表,让工具只使用时钟专用单元。我之前遇到过一个项目,CTS工具默认的cell list里混入了普通buffer,结果900ps的时钟树延迟在同一个corner下,前后两次跑出来的结果差了200ps,查了半天才发现是普通buffer在负载变化后延迟漂移太严重。把普通buffer从clock cell list里清掉之后,稳定性立刻好了很多。
另外,两级反相器(CLKINV)对有时比单个缓冲器(CLKBUF)更值得优先使用。因为反相器在CMOS工艺中天然比同尺寸的缓冲器延迟更小、占空比特性更好,而且级数成对出现时,可以很好地利用“奇数级反相”的特性做占空比修正。在高速时钟节点上,很多后端工程师会刻意把时钟树做成对称反相器对结构,代价是单元数量多一些,但延迟和偏差的稳定性显著提升。
3.3 分层CTS的使用场景:什么时候值得“切树”
随着设计规模增大,单棵时钟树如果从根部一路推下来,要带的负载可能达到几十万甚至上百万个时钟引脚。一根巨大的时钟树在全局平衡、功耗、IR drop、布线资源上都会遇到瓶颈。这时就要考虑分层CTS(Hierarchical CTS / Clock Mesh)。
分层CTS的核心思路是:先构建一个“全局树”(Global Tree),把时钟信号送到芯片各个区域的局部根节点(Local Root);然后各个局部区域再分别做自己的局部树(Local Tree),带自己区域内的触发器。这种方法的好处是:
- 全局树负载小,层次清晰,容易控制全局skew;
- 局部树可以由工具并行综合,优化更充分;
- 不同模块如果时钟关系不紧密,可以各自独立做最优的局部树。
但分层CTS的难点在于全局树和局部树之间的接口时序。你需要精确约束局部根节点的插入延迟,让全局树到达各局部根节点的时间偏差足够小,否则局部做得再平衡,局部之间也会因为根节点延迟不一致而出现跨模块路径的skew违例。
在我做过的项目里,超过200万寄存器规模的设计,几乎都需要某种形式的分层或分组CTS,单纯依靠全平铺时钟树很难收敛。另一个常见的用法是VLSI设计中“时钟网格”(Clock Mesh)结构——在芯片顶层布置一个金属网格,时钟信号通过网格均匀分布到各个局部区域,再从网格上的驱动点接入局部树。Mesh结构的偏差天然很小,但代价是功耗显著增加——因为网格本身是持续翻转的金属结构。所以做不用mesh的混合树,往往是高性能低功耗设计的主流选择。
3.4 功耗与IR drop约束下的CTS决策
时钟网络的功耗在数字IC总功耗里占比很可观,通常能到30%~50%。CTS阶段是决定时钟网络功耗的关键时刻,因为时钟树上的缓冲器每个时钟周期都在翻转,数量越多、尺寸越大,动态功耗越高。
在CTS优化时,有几个降低时钟功耗的常见策略:
- 减少缓冲器级数:能用3级解决的不要用5级,每一级缓冲器的负载和功耗都直接计入总功耗;
- 合理选择驱动强度:不要一味选最大驱动力的缓冲器,按照负载估算选择中等驱动档位,往往更省功耗;
- 利用门控时钟(Clock Gating):在CTS之前确保RTL中的clock gating cell被正确识别和使用,让空闲模块的时钟不翻转。这通常是前端综合时就做好的,但后端CTS时也要检查,确保gating cell在后端实现中没有被工具优化掉或者放到了不恰当的位置;
- 避免重复的平衡路径:有些工具为了追求skew小,会给物理位置近的寄存器绕远路,增加功耗。可以通过约束
useful skew的最大值,限制工具过度绕线。
IR drop是CTS阶段另一个容易被忽略的问题。时钟缓冲器在时钟沿翻转的瞬间需要大量瞬态电流,如果这些缓冲器分布过于集中,或者供电网络在局部区域电阻过大,就会造成局部的IR drop。时钟树上的IR drop会直接转化为时序延迟的不确定性,导致setup/hold余量被吃掉。做CTS时我会特别关注时钟缓冲器密集区域的电源网络密度,必要时在floorplan阶段就加强这些区域的power strip。
4. 完整案例分析:一次真实项目里的时钟树失衡排查与修复
4.1 案例背景:六个时钟域汇聚的SoC子系统
这个案例来自一个28nm工艺的多媒体SoC芯片,我做的是其中一块子系统,包含CPU子系统的接口逻辑、DDR控制器、视频编解码单元和几个低速外设总线桥。设计总共用了6个时钟域,其中两个高速时钟域的频率分别为1.2GHz和800MHz,其余是133MHz、100MHz、25MHz、32.768kHz这些低速时钟。总寄存器数量约85万,芯片面积约12mm²。
在第一次完整的CTS跑完之后,我按照常规流程看CTS报告,时钟树总延迟大约1.8ns(1.2GHz时钟域),全局skew报告是180ps。乍一看没啥大问题,但是在布线前的时序收敛检查中,有200多条setup违例集中在DDR控制器和视频编解码单元的接口路径上,且这些违例路径的clock latency差异非常明显。更让人头疼的是,还有几十条hold违例出现在同一个模块内部,修起来非常棘手。
4.2 时钟树失衡的根因排查:从报告里找线索
遇到这种集中性的时序违例,我先不看数据路径本身,而是优先检查时钟树。用工具分别报出DDR控制器和视频编解码模块内部触发器的clock latency分布,发现一个明显的规律:
- 视频编解码模块的寄存器比较集中,时钟树级数只有3级,平均clock latency大约1.4ns;
- DDR控制器的寄存器分布在两个相邻但跨越了供电边界的区域,时钟树为了维持整体平衡,在其中一个分支上多插了两级缓冲器,导致该区域clock latency高达1.9ns;
- 这两个模块之间存在大量跨时钟域握手路径,启动时钟来自视频编解码模块的时钟端、捕获时钟来自DDR控制器的时钟端,实际skew高达500ps。
也就是说,CTS全局skew报告里的180ps是“全域平均”或者“触发器对”级别的统计结果,并没有充分暴露这两个局部区域之间的真实偏差。
接着我用report_clock_tree详细查看时钟树的拓扑结构,发现视频编解码和DDR控制器分属时钟树的两大分支,它们在根节点之后第2级才分开。第2级缓冲器选了一个驱动能力小一级的cell,在负载差异很大的条件下,两个分支的延迟失配被进一步放大。也就是说,时钟树在分叉点上的缓冲器尺寸选择不当,是导致失衡的直接原因。
4.3 优化方案:利用useful skew与缓冲器尺寸重选
搞清楚根因之后,我没有急着推翻整棵时钟树重做,而是分两步处理。
第一步是调整分叉点的缓冲器尺寸。把第2级那个驱动能力偏小的缓冲器换大一号,同时让两个分支在第4级、第5级分别使用不同的驱动档位组合,目标不是两边延迟完全一样,而是让两个模块区域的clock latency差从500ps压缩到200ps以内。这个调整直接减掉了大部分跨模块路径的setup违例。
第二步是针对残余的几十条setup违例路径,使用useful skew做局部修复。具体操作是把工具箱的set_clock_tree_options -useful_skew相关的选项打开,并指定允许的最大skew调整量为150ps。工具会自动识别那些setup slack为负的路径,把捕获触发器的时钟延迟微调变大,来借出时间。这一步修复了大约80%的残余setup违例。
这里要特别强调:useful skew打开后,一定要跑一个“skew后的hold检查”。因为借出时间会同时压缩hold余量,如果原本hold余量就所剩无几的路径,useful skew可能会把hold推向违例。我在这次优化中就遇到了这样的情况,调整之后新增了23条hold违例,集中在原本hold slack只有几十ps的短路径上。
4.4 修复效果与局部hold违例的处理
最终,通过分叉点尺寸修正和useful skew的组合策略,跨模块的setup违例从200多条降到个位数,再配合数据路径上少量的尺寸调整就全部收敛了。
新增的那23条hold违例,我用两种方法处理:一部分时序路径物理距离极短,直接在CTS后的布线阶段插入延迟buffer修复;另一部分路径本身就是由useful skew引入的,属于“借用时间”的副作用,我通过把个别捕获触发器的skew调整量下调10~20ps,让它们回到安全范围。
这里也补充一个经验:项目里CTS和布线往往是迭代进行的。第一轮CTS结束后,如果发现大量setup违例,不要马上怀疑是CTS做得不好,先搞清楚违例路径是“跨模块skew”导致还是“逻辑级数真实不够”导致。如果是后者,那可能是综合阶段逻辑优化不到位,CTS再怎么做也救不回来。这两种情况在报告中表现非常相似,但处理方向完全不同,判断错了会浪费大量时间。
5. CTS调试中的常见坑:一些我踩过的实战经验
5.1 时钟定义不完整:工具做出的“隐式树”最危险
这是我最想提醒新人的一点。CTS工具只会对你正确定义过的时钟做时钟树综合。如果一个时钟信号在SDC里没有被定义,或者generated clock的master clock指定错了,工具通常会做两种事情:要么把它当普通数据信号处理,不做CTS;要么生成一棵隐式的时钟树,但这棵树的拓扑和延迟完全不透明,你从报告里根本看不出来。
我之前遇到过一个问题:芯片里有一个由硬宏(hard macro)内部产生的时钟信号,SDC里只定义了端口级别的时钟,没有定义宏内部的generated clock。结果CTS工具把这个宏的输出时钟当成了普通信号,在布线阶段才被当成数据线处理。芯片回来后,这个接口的时序完全不稳定,最后花了一周时间才定位到是时钟定义缺失。从那以后,每次CTS之前我都会额外花15分钟过一遍report_clock的完整列表,逐个确认所有时钟域都有明确的定义和正确的传播属性。
5.2 保持时间违例:CTS阶段就要“抢跑”处理
在很多流程里,hold修复被放到布线之后,理由是hold不受逻辑级数影响,主要是提高时钟延迟或增加数据路径延迟。但在工程实践中,CTS阶段就可以开始处理一部分hold违例,尤其是那些因为时钟树本身导致的结构性hold问题。
具体来说,如果某个模块的时钟延迟天然就比它的上游模块小很多——比如上游模块的时钟树多了两级缓冲器,而下游模块的时钟直接从根节点进来——那么任何从上游模块到下游模块的数据路径都会有hold风险。这种问题在CTS阶段调整树的结构其实是最高效的,比布线之后再一个个插buffer去修要省得多。
所以我的建议是:CTS跑完后,不要急着布线,专门做一轮快速hold检查,关注那些clock latency差异显著的跨模块路径。如果发现hold违例大量出现,优先在CTS阶段调整时钟树来消除结构性问题,剩下的零星违例留到布线后修复。
5.3 OCV与derate效应:CTS报告里的漂亮数字会骗人
现代先进工艺下,同一片晶圆上不同位置的晶体管性能并不完全一致,这就导致同一条路径在芯片不同角落的延迟会有差异,也就是片上工艺偏差(OCV,On-Chip Variation)。时序工具在做签核分析时,会给路径加上derate系数来模拟这种偏差。
在CTS阶段,问一个非常关键的问题:你看到的skew是“理想条件下的skew”还是“考虑了derate之后的skew”?理想条件下skew=100ps的时钟树,在derate系数1.1的影响下,实际signoff时的有效skew可能达到200ps甚至更多。
因此,我在关注CTS结果时,会特别看工具报告的“延迟计算模式”是在best case还是worst case,并且会主动检查签核模式下的时钟树时序。如果设计用的是老工艺(比如40nm以上),derate影响相对小;如果用的是先进工艺(28nm及以下),derate和OCV的分析必须从CTS阶段就纳入考量。否则你会在signoff时突然冒出来一堆setup/hold违例,而且回溯到CTS时发现时钟树本身“看起来”完全没问题。
5.4 CTS后DRC的分类处理:哪些问题必须当场解决
CTS跑完后,工具会跑一组针对时钟树的DRC检查,包括最大转换时间(Max Transition)、最大电容(Max Capacitance)、最大扇出(Max Fanout)。很多人一看到这些DRC violation就紧张,其实需要分类看待。
- Max Transition违例:通常是最需要优先解决的。因为transition过大会直接影响触发器采样时间点,而且会放大OCV偏差。这种违例一般通过增大驱动、减少扇出或插入中继缓冲器解决。
- Max Capacitance违例:要看是时钟树本身的容性负载过大,还是旁边有汇聚的走线电容。如果是前者,调整缓冲器驱动能力即可;如果是后者,可能需要在布局上挪动缓冲器位置。
- Max Fanout违例:单一缓冲器带的负载超过工艺库建议值,直接插缓冲器分流就行,但注意插的位置要尽量平衡,否则会引入新的skew。
我个人习惯在CTS阶段就尽量把transition和capacitance的violation清零,不留给布线阶段。因为布线后的修复空间更小,而且会导致时钟树结构在最后时刻被破坏,violation清零后skew又是另一副样子。CTS阶段多花30分钟清理DRC,省下的是布线后几天的返工。
另外还有一个容易被忽略的问题:CTS阶段清理DRC时新增的缓冲器,一定要重新跑一遍时钟树抽取和延迟计算,确认skew和latency没有因为新增缓冲器而显著变化。很多时候你为了消一个transition violation插入了一个缓冲器,结果它改变了整个分支的负载平衡,skew又变了。这就是为什么CTS优化是个反复迭代的过程,不能一锤子定音。
6. 从CTS到signoff:把时钟树的“余量意识”贯穿到底
6.1 CTS与布线的衔接:时钟树是动态的
很多工程师有一种思维定势,觉得CTS跑完了时钟树就固定了。实际上,后续的布线阶段会添加大量金属走线,这些走线会带来额外的电阻电容(RC),它们会附着在时钟网络的节点上,从而改变时钟树每个节点的实际延迟。也就是说,CTS阶段的“平衡”,在布线完成之后很可能就不平衡了。
这就引出两个实践原则:
第一,CTS阶段不要压着极限做。比如你目标skew是100ps,工具最后做到98ps,看起来很好,但布线RC一上来,skew可能变成180ps。反过来,如果CTS阶段你留了30%的余量,比如目标100ps,工具做到了70ps,布线新增的80ps RC影响也能被吸收。
第二,布线完成之后一定要重新做时钟树的RC抽取和延迟计算,确认CTS阶段的优化在真实RC下依然有效。这一点在先进工艺下尤其重要,因为细线宽带来的电阻效应更显著,时钟网络的RC变化幅度更大。
6.2 多corner多模式下的CTS一致性
真实项目的签核通常要在多个corner(工艺角+电压+温度的组合)下进行,比如SS corner(慢工艺、低压、高温)和FF corner(快工艺、高压、低温)。CTS阶段一般在某个代表性corner下做优化,但优化结果要在所有corner下都成立。
这意味着CTS优化时不能只看单一corner。比如你在SS corner下做平衡,树可能在某处用了很大尺寸的缓冲器以适应慢速条件下的负载,但到了FF corner下,这个缓冲器的延迟会变得很小,树的平衡反而被打破。所以实际项目中,CTS之后立即并行跑多个corner的时序分析,是很常规的操作。
如果你发现某个corner下出现大量因CTS导致的新增违例,可能就需要在CTS阶段切换优化corner,或者在MMMC模式下同时读入多个corner让工具联合优化。这会让CTS运行时间增加不少,但换来的是后续signoff的稳定。尤其是当设计工作电压范围跨度大的时候,多corner的CTS优化几乎是必须的。
6.3 个人经验:CTS优化中值得反复强调的三件事
做了这么多个项目的CTS,如果要我总结最值得分享的经验,大概是这三条。
第一,CTS优化要有“留白意识”。不要追求即时最优,要给后面的布线RC、OCV、IR drop留出余量。那些声称“CTS做到0 skew”的目标,多半会在布线后让你付出更大的代价去修复。
第二,CTS是物理约束和逻辑约束的交汇点。它既受floorplan、distribution这些物理因素限制,也受SDC里的时钟定义、uncertainty、generated clock这些逻辑约束影响。任何时候CTS出了问题,先分清楚是物理端的问题,还是逻辑端的问题,不要盲目优化。
第三,工具报告必须“穿透着看”。skew报告看关键的clock pair,不要只看全局平均;CTS DRC看是否会影响后续布线;held检查看是否是结构性失衡导致。只有把报告里的数字和物理版图上的实际位置对应起来,你才能真正理解时钟树在做什么。这也是区分一个后端工程师是“会跑流程”还是“会做设计”的分水岭。
时钟树综合做得好不好,往往要等到布线、签核甚至流片回来后才能真正验证。那时候再想回头改CTS,已经来不及了。所以,做CTS时多一点敬畏心,多留一点余量,多看一眼报告背后的物理含义,这是这个环节最值得投入的地方。