时钟树综合(CTS)核心概念与实战:从Skew、Latency到时序收敛
2026/9/8 12:00:12 网站建设 项目流程

做数字后端的人应该都有体会,流片回来如果时钟有问题,整颗芯片基本就废了。时序违例还能靠ECO补救,时钟偏斜如果控不住,尤其是那种跨die的偏斜,可能连基本功能都跑不起来。所以时钟树综合(CTS)在整个数字后端流程里,可以说是最考验功力的环节之一。这篇笔记想聊的,就是CTS里头最核心的主角——时钟信号本身,从定义、参数,到工具里的处理方式,再到实际项目中常见的坑,尽量一次说透。

这篇内容适合刚接触数字后端、准备跑第一个CTS的工程师,也适合那些已经跑过几步流程,但对工具报告里的Jitter、Skew、Latency还一知半解的朋友。我会从概念讲起,但重点会放在工具实操和项目经验上,毕竟那些报告和命令,才是每天要面对的东西。

1. 内容整体设计与思路拆解

1.1 为什么CTS在数字后端里地位这么特殊

做数字后端的人常说一句话:Floorplan是骨架,Placement是血肉,CTS是神经系统。前两步把逻辑放对位置,CTS则决定了这些逻辑之间能不能按时通信

举一个很直观的例子:假设一个数据路径,源寄存器到目的寄存器的组合逻辑延迟只有2ns,但是时钟到达这两个寄存器的时刻差了0.5ns。如果数据是时钟上升沿发出、上升沿采到,那这0.5ns会直接影响建立时间裕量。更麻烦的是,如果在物理上离得很近的两个寄存器,因为时钟路径上负载不同,出现了0.3ns的偏斜,那数据路径本来算好的时序就有可能在真实硅片上直接翻转结果。

这就是CTS存在的意义:在物理上构建时钟网络,把时钟信号的到达时间差(Skew)控制到可接受范围,同时减小延迟(Latency),保证芯片能在一个合理的主频下稳定工作。

我见过不少刚入行的同事,觉得CTS无非是让工具自动插buffer,跑通就行。但实际上,CTS的质量直接决定后续route的时序收敛难度,也直接决定芯片能不能跑到目标频率。工具默认参数确实可以跑出结果,但那个结果通常是“能用”而不是“好用”。想要“好用”,必须自己理解时钟信号的特性,并且愿意花时间去调。

1.2 时钟信号在芯片里的三种存在形态

理解CTS之前,先要清楚时钟信号在芯片里是怎么演变的,这决定了工具在各阶段如何处理它。

第一个阶段是真实物理时钟,也就是从芯片外部PAD进来的时钟,或者由内部PLL产生的时钟。这个时候的时钟是真实存在的物理信号,有RC延迟,会受到工艺波动影响,也是CTS要最终构建出来的对象。

第二个阶段是理想时钟(Ideal Clock)。在综合和布局阶段,工具假设时钟到达每个寄存器的时间完全相同,没有延迟、没有偏斜、没有抖动。这时候做的STA(静态时序分析)只是一个非常粗粒度的检查,只能用来排除明显的逻辑问题,不能用来做精确的时序收敛。所有数字后端工具,在place阶段都是按照理想时钟来优化逻辑,只关注数据路径的物理距离和拥塞情况。

第三个阶段是传播时钟(Propagated Clock)。CTS工具会用真实单元和真实绕线,把时钟网络物理实现出来。时钟从源头到达各个寄存器,走的是一条实实在在的RC网络,每一段都有延迟,每个单元都有delay,不同路径之间的延迟差就是Skew。只有到了这个阶段,时序分析才真正贴近实际硅片的行为。

这个演化过程想说明一件事:CTS是把“理想”变为“真实”的分水岭。在做CTS之前,时序分析是虚的;做完CTS之后,时序分析才落地。这也是为什么业界常说CTS是数字后端最核心的环节之一。

1.3 整篇笔记的内容走向

接下来的章节我是这么安排的:先从时钟信号的参数定义讲起,帮你构建一个“什么是好的时钟”的认知框架。然后深入到CTS的核心实现环节,解析工具如何从无到有构建时钟树,包括长中短时钟树的策略选择。接着讲实操层面的核心技术,比如Skew Group、有用偏差、报告分析这些每天都要用的东西。最后是信号完整性和实际项目中的问题排查,把那些“跑完就挂”的典型案例摆出来分析。

这样一条线走下来,你既能理解CTS背后的逻辑,也能在工具里实操落地。

2. 核心概念:时钟信号的参数与质量衡量

2.1 时钟信号的三个核心参数:Skew、Latency、Jitter

围绕时钟信号,有三个参数是天天要看的:Skew、Latency、Jitter。它们分别刻画了时钟信号在“空间不一致性、时间延迟、瞬时波动”三个维度上的表现。

Latency,中文叫时钟延迟,指时钟信号从源头到达某个寄存器时钟端的时间。它由两部分组成:Source Latency(时钟源到时钟树根节点的延迟,包含片外走线或PLL内部延迟)和Network Latency(时钟树根节点到各寄存器端点的延迟)。在物理设计里,我们最头疼的往往是Network Latency,因为它直接受时钟树缓冲器级数和绕线长度影响。工具会在约束里给你一个target latency的期望,但实际是否满足取决于时钟树构建质量。

Skew,时钟偏斜,指同一时钟域内,时钟信号到达各个寄存器的时间差。这个差的本质是不同路径长度和负载不同导致的delay差。Skew是CTS要解决的头号问题。假设一个时钟域里有1万个寄存器,工具希望到达这1万个寄存器的时钟沿尽量对齐。但物理上这是不可能的,因为位置有远有近,负载有大有小。工具能做的,是把Skew压制到一个足够小的范围内,让时序预算里的不确定性足够低。

Jitter,时钟抖动,指时钟信号在时域上每个周期的边沿位置相对理想位置有微小随机偏移。这个偏移来自PLL自身的噪声、电源噪声、串扰,是物理世界无法消除的。Jitter在CTS阶段通常作为外部约束输入,你没法通过物理实现手段去消除它,只能在时序分析时把它当作uncertainty的一部分扣除。

从业这么多年,我遇到过不少新人混淆这三个概念。其实有个很简单的类比:Latency像是公交车从总站到达某一站的时间,Skew是不同站点的公交车到达时间差,Jitter则是同一辆车每次到达同一站点的时间波动。CTS管的是Skew,同时尽量压低Latency,而Jitter更多是设计架构和电源网络的范畴。

2.2 全局偏斜与局部偏斜:哪个更重要

Skew还有全局和局部之分,这个区分在实际项目中非常关键。

Global Skew是时钟源到达时钟域内所有寄存器端点的最大时间差。Local Skew则是时钟源到达彼此有时序关系的两个寄存器(比如一条数据路径上的源寄存器和目的寄存器)的时间差。

从时序分析的角度看,Local Skew才是真正影响路径时序的。因为一条时序路径上,只有两个端点:源寄存器和目的寄存器。它们之间的时钟到达时间差,直接影响这条路径的建立和保持时间裕量。

但是关键在于:CTS工具优化的主要目标通常是Global Skew,因为让所有叶子尽量对齐是最简单、最robust的策略。这样做的好处是,对于任意一条路径,Local Skew的上限天然就被Global Skew限制住了,不需要逐一分析。这种设计哲学很像“宁可同归于尽也要绝对公平”——把时钟偏差压到全局统一,换来时序稳定。

不过,Global Skew压得太低也是有代价的。把一片片上物理位置差距极大的寄存器都对齐到同一个时钟边沿,意味着要为远的寄存器插入更多buffer,增加延迟和功耗,也增加布线资源占用。所以实际项目中,高扇出、大时钟域一般不追求极致低的Global Skew,而是追求“足够好”的Local Skew。这一点,后面讲Skew Group的时候会再展开。

2.3 从约束角度看:时钟定义是CTS的输入前提

CTS不是一个凭空的物理构建过程,它的行为完全受约束驱动。时钟定义不对,后面做得再漂亮也是错的。

这里就涉及SDC(Synopsys Design Constraints)里的核心时钟约束命令。用得最多的就是两个:create_clockcreate_generated_clock

create_clock用于定义原始时钟,通常定义在芯片的输入端口或者PLL输出端口。它需要指定周期(period)、波形(waveform),还可以定义不确定性(uncertainty)和延迟(latency)。如果你定义了一个错误的周期,比如周期写小了,后续所有CTS优化都会按照过紧的时序预算去跑,工具会插一大批多余的buffer,功耗和面积直接爆掉。反过来,周期写大了,CTS跑出来的时钟树松垮垮,时序检查过于宽松,流片回来大概率翻车。

create_generated_clock用于定义分频、倍频、多路选择后的时钟。它需要指定源时钟(master clock)和分频系数或倍频系数。这个命令在CTS中同样重要,因为工具需要知道哪些时钟是从哪些源演变来的,才能正确判断时钟域之间的关系和时钟树结构的归属。

所以在启动CTS之前,我强烈的建议是先花一个小时把SDC里的时钟定义逐条看一遍。确认每个时钟定义在正确的端口、周期正确、generated clock的主时钟源正确。这一步省下来的时间,至少是后排查CTS问题时间的五倍。很多人CTS跑完发现Skew报告乱得没法看,回头一查,果然是时钟定义里有个master pin选错了。这种事情,我几乎每做一个项目都能碰上至少一次。

3. CTS的实现机制:从短时钟树到长时钟树

3.1 为什么不能用简单buffer链搞定CTS

很多人对CTS的理解是:插buffer,把信号从根节点送到所有终点。听上去简单,为什么工具实现起来这么费劲?

核心原因有三个。第一,分支路径绝对不可能等长。芯片上寄存器的物理位置分布极不均匀,有的在时钟网络根节点旁边,有的在die的对角。要把这些位置完全不同的点串成一棵树,自然会出现路径长短不一的情况。

第二,每个节点的负载不同。寄存器的时钟输入端电容、时钟网络的走线长度,都会影响delay。负载大的分支天然更慢,负载小的分支天然更快,不加以干预,Skew自然就形成了。

第三,前级buffer的驱动能力有限。一个buffer能驱动的负载是有上限的,负载超过上限,delay会急剧增大,甚至导致信号边沿变缓,引发保持时间问题。所以CTS本质上是一个需要不断插入buffer、调整buffer尺寸、调整树的拓扑,反复迭代收敛的过程。

3.2 短时钟树策略:面向低频与少量寄存器

在进入工具如何构建长时钟树之前,先说说“短时钟树”这个概念。因为很多人做项目,面对的其实不是那种动辄几万寄存器的超高频时钟域,而是一两个甚至十几个寄存器的小模块。

如果一个时钟域只有二十个寄存器,分布在一块很小的区域里,离时钟源端口也很近,这时候CTS就走短时钟树(比如单一层或两层buffer)就够了。短时钟树的优势是延迟小、功耗低、面积小,且天然Skew小,因为你不需要为了平衡远处寄存器而人为拖慢近处寄存器。

工具一般是根据扇出和物理分布自动决定时钟树层级的。但如果你的约束里对某些时钟设置了非常严格的target latency,工具就可能被迫多插几层buffer,把短时钟树变成中时钟树。这时候你的约束就起到了反作用——非但没有让时钟更好,反而让功耗和面积白白浪费了。

所以在写SDC的时钟约束时,不要想当然地给时钟加一个特别小的latency期望。除了少数高频接口时钟,大多数内部时钟,latency大一点小一点并没有那么关键,关键是Skew和transition达标。这个观念一定要扭转过来。

3.3 长时钟树策略:平衡偏斜与延迟的全局考量

当寄存器数量上去了(比如上万),物理范围铺满了整个芯片,CTS就不得不用长时钟树策略了。长时钟树通常是H-tree或者在工具看来是“多级平衡树”的形式。

工具构建长时钟树时,逻辑上会经历这样几步:

第一步,确定叶子节点集合。工具从时钟根节点出发,沿着时钟网络去追踪所有终点(通常是寄存器的clock pin)。这个过程在Innovus里可以通过report_clock_tree来查看,工具会列出所有sink以及它们所属的时钟域。

第二步,分层构建。工具不会从根节点直接接一长串buffer把每个sink都驱动起来,而是先驱动一个大buffer作为“大干线”,再从大干线分出几条支路,每条支路再驱动下一级buffer,逐级展开,直到覆盖所有叶子。每一级都在做一个子树的平衡,最终整棵树近似平衡。

第三步,插入延迟单元。为了平衡整体Skew,光靠选对buffer尺寸还不够,有时候必须牺牲某些路径的延迟来配合全局。一个常见的做法是在较短路径上插入额外的delay单元,让该路径的延迟与其他路径拉齐。这个做法在时序分析里有专门的名字——useful skew,我们放到后面讲。

第四步,迭代修正。工具会根据当前树的实测delay和Skew情况,重新调整buffer的尺寸和位置。这个过程在Innovus的CTS log里能看到多次迭代的记录。每次迭代都会更新wire load模型和RC值,逐步逼近收敛。

对于长时钟树,有一个容易被忽视的点:时钟树的顶层走线宽度和处理方式,与普通信号完全不同。在Innovus里,你通常会为时钟树专门定义一组宽金属、低电阻的route层,并且允许它使用比普通信号更宽的线宽。这不是为了好看,而是为了降低电阻,减小RC延迟,保证时钟信号在长距离传输过程中的边沿不会恶化。

3.4 时钟树参考单元与硬件准备

CTS要能跑起来,除了逻辑本身,还必须有时钟树参考单元,也就是CTS库(通常在.lib文件里标记为clock_gating_integration_cellclock_bufferct_cell等属性的单元)。

这些参考单元主要分三类:

  • 时钟缓冲器(Clock Buffer):专用buffer,特点是延迟对负载的敏感性较低、输入电容匹配好、rise/fall延迟对称性好。对称性在时钟树里特别重要。因为时钟信号在传递个几百个寄存器的过程中,如果rise delay和fall delay不一致,高电平占空比会逐渐失真,导致时序分析时遇到底部偏移的问题。

  • 时钟反相器(Clock Inverter):在某些工艺节点上,clock inverter因为本身结构简单,延迟波动更小,反而更能做出低Skew的树。不过用inverter搭时钟树会带来相位翻转的问题,工具需要额外处理。

  • 时钟门控单元(ICG):用于时钟门控,在低功耗设计里是标配。ICG通常自带latch和AND门结构,能避免门控时钟产生毛刺。

在定义CTS参考单元的时候,有两种策略:一种是限制工具只能使用指定的几组buffer,另一种是放开让工具从标准单元库中自动选择。我的建议是,除非你的库非常成熟、参考单元分类很良好,否则尽量手动指定一个或几个尺寸的buffer。工具自动选库里的所有单元去优化时钟树,虽然理论上能找到一个更优的尺寸点,但在库里混杂了通用逻辑buffer、低功耗buffer的情况下,工具选出来的单元未必适合时钟用途,容易在后期出现transition违例。手动圈定一个缓冲器集合,既是经验之谈,也是逼着自己去熟悉库里的单元特性。

4. 实操要点:Skew Group、有用偏差与时钟树质量评估

4.1 Innovus中CTS的基本流程与常用命令

进入工具实操环节。这里以Innovus为例,因为目前业界用的最多。流程上,place做完之后,跑CTS之前有几件事是必须做的:

第一件,确认clock定义无误。可以通过report_clock -summary快速浏览所有时钟。重点看周期、主时钟、group。如果发现group是默认的,最好手动分组。

第二件,设置CTS专用变量。Innovus里有大量与CTS相关的变量,其中最重要的几个包括:cts.target_max_trans(目标transition时间)、cts.target_skew(目标偏斜值)、cts.max_fanout(最大扇出限制)、cts.buffer_cells(允许使用的buffer列表)。这些变量在开始之前就要设置好。

第三件,执行时钟树综合。在Innovus里直接跑clock_design就能启动CTS。跑的过程中,工具会打印出很多信息,包括正在建的树、当前延迟、Skew收敛情况等。第一次跑CTS不要着急去翻tcl脚本细节,先关注三个内容:log里是否有warning/error、报告出来的Skew是多少、tree的depth是多少。

第四件,做post-CTS的时序分析。跑完CTS之后,需要把时钟设为propagated,重新做一次时序检查。命令是set_propagated_clock [all_clocks],然后重跑STA。这时候的结果,比place阶段要真实得多,可以看出来CTS到底合不合格。

我个人的习惯是,跑CTS之前先手动创建一个“时钟树调试用”的工程目录备份。这样如果在CTS后发现是时钟定义的问题,可以快速回到place阶段重新来过,不用再做一次place。这个习惯救过我很多次,尤其是处理一些上百个宏单元、多时钟域混杂的复杂设计时。

4.2 Skew Group:为什么应该手动划分时钟组

默认情况下,Innovus的CTS会为每个时钟自动建一个时钟组(Clock Group)。但实际设计中,一个时钟域内部,由于数据流的关系,并不是所有寄存器都需要被严格对齐的。

最典型的场景是可测性设计(DFT)。在shift模式下,scan链上的寄存器要求时钟能够同时翻转,这需要一个极低的Skew。但在功能模式下,扫描链路两端的时序要求可能是完全不同的。如果工具按照功能模式下的时钟约束去建树,得到的Skew可能在shift模式下不满足。

这时候就需要手动把同一个时钟里的寄存器划分成不同的Skew Group,让工具在不同的组间做有倾向性的优化。比如scan时钟域,可以建立一个专门的group,要求极低global skew;而功能时钟域,只要保证local skew够好就行。

Innovus里划分Skew Group用create_clock_tree_group命令。你可以根据时钟树根节点和组内sink来划分,也可以指定一组特定的寄存器pin作为组内成员。比如:

create_clock_tree_group -name func_group -clock CLK -sinks [get_pins ...list_of_ff_ck_pins...]

实际项目管理中,我们通常先跑一版默认CTS,然后从报告里看哪些路径的时序最差,再针对性地把这些路径上的寄存器划到同一个group里做精细优化。这个过程要迭代好几轮,也正是数字后端工程师最耗时间的地方。

4.3 有用偏差(Useful Skew)的高级用法

讲到Skew Group,就绕不开Useful Skew。这个概念,前文提过一句,这里展开讲透。

传统CTS的目标是把全局Skew压到最小。但如果我们放宽这一目标,允许一定的偏斜,利用它去“帮忙”修复时序,那就是Useful Skew。它本质上是一种主动地、有方向性地控制Skew的行为。

举一个具象的例子:假设有一条数据路径,从FF_A到FF_B,路径延迟是2.2ns,而时钟周期是2ns,正常时序无法满足。如果我们能让时钟到达FF_B的时间比到达FF_A的时间晚0.3ns,那么在数据沿到达FF_B之前,时钟沿还没到来,目的寄存器“晚一点采样”,就多给了0.3ns的建立时间预算。这条路径就修好了。

反过来,对于保持时间的违例,我们可以让FF_B的时钟比FF_A早一点到,这样数据变化沿到达之前,旧数据已经被采样过了,从而消除保持时间违例。这就是保持时间方向上的Useful Skew。

但是Useful Skew是一把双刃剑。你今天让FF_B晚采0.3ns,虽然满足了FF_A到FF_B的时序,但FF_B它自己作为源寄存器,到达下游FF_C的数据路径是独立的一条,它的晚采样不一定能保证下游路径。如果下游路径也是紧的,那你用Skew补了上游,漏了下游,反而可能越调越乱。

所以实际工程中,Useful Skew通常不是手工去调的,而是交给工具。Innovus里有一个专门的模式——在时钟树综合时允许工具利用skew来修复时序,比如set_analysis_mode配合时序预算、或者时钟树选项ccopt_useful_skew相关的变量。新手阶段我不建议手动做useful skew,经历不够的话,非常容易把自己绕进去。

4.4 怎么通过报告评估时钟树质量

CTS跑完,怎么判断质量好坏?我一般按这个顺序看报告。

先看report_clock_tree -detail,里面每一层有Cell count、Net Length、Cpin等。重点看第一列:根节点到sink的延迟(Latency)以及sink之间的Skew。如果延迟异常大,排除时钟定义或库的问题后,很可能是工具用了太多级的buffer,可以考虑换更大的主干buffer。

再看report_clock_timing -type skewreport_clock_timing -type latency,这里展示每个时钟域的skew统计和latency。Skew达标与否,不光看平均,还要看最大差值。有的工具报告显示平均skew是几十ps,但max差值可能几百ps,这种情况说明少数sink拖了后腿,需要单独检查这些sink的物理位置。

再然后看report_timing -from [时钟根] -to [哪个sink],用于逐条分析延迟构成。如果发现某一段延迟异常,要看是不是因为绕线太长,或者经过的特殊单元太多。

最后看transition报告。时钟信号的transition如果太大,不只影响本路径,还会影响下级寄存器的时钟输入端的建立和保持时间计算。Innovus里CTS做完后,如果check_report说有transition violation,你可以在工具里设set_clock_tree_options -target_max_trans来收紧目标,或者调整clock buffer的尺寸。

看这些报告,最忌讳的就是只看一个数字然后下结论。时钟树是一个整体网络,局部指标好不代表整体好,整体skew达标也可能存在局部transition问题。要结合起来看。

5. 时钟树的信号完整性与可靠性问题

5.1 时钟树上的串扰与噪声

到了深亚微米工艺之后,时钟树的信号完整性问题越来越突出。时钟树是全局网络中走线最长、buffer级数最多的网络之一,天然容易受到相邻信号的干扰。

串扰导致的结果有两类:一类是串扰延迟,即相邻信号的翻转改变了时钟信号的传播速度;另一类是串扰噪声,即相邻信号翻转时在时钟信号线上感应出电压脉冲,严重时可能导致时钟沿提前或延迟到达。

在Innovus里,CTS做完后一般会有一个ccopt_design或post-CTS的SI分析环节。如果报告显示时钟树上有明显的SI问题,常见处理手段包括:

  • 将时钟网络的绕线间距扩大(double spacing),减少与相邻signal net的耦合电容。
  • 对关键时钟网络做特殊绕线屏蔽(shielding),但这会占用大量绕线资源,一般只用在高频超关键路径上。
  • 调整时钟网络所在的金属层,让它们避开拥塞区域,减少串扰风险。

需要注意的是,这些SI修复手段都会额外增加绕线资源和功耗,所以不是所有时钟网络都需要做,一般只对high-frequency clock或high-fanout clock做。低频时钟,串扰的影响相对有限,放宽一点也没有关系。

5.2 时钟树上的IR Drop与电源噪声

再说电源问题。时钟buffer在翻转的瞬间会抽取瞬时大电流,如果供电网络不好,局部的IR Drop会很大。而IR Drop又直接影响了buffer本身的延迟,导致不同位置的时钟buffer延迟不一致,间接恶化Skew。

这就是为什么在时钟树优化时,除了看时序报告,还要关注电源域划分power mesh的密度。如果某个角落的IR-drop报告特别严重,就得提前在物理实现阶段把power mesh加密,而不是在CTS阶段想着靠插buffer去弥补。工具再强,也补不出电源质量问题。

另外,时钟树本身也会消耗不小的动态功耗。时钟每翻转一次,所有clock buffer以及所有寄存器的clock pin都要跟着翻转。一个几万寄存器的时钟域,时钟树的动态功耗可能是整体芯片功耗的15%到25%。如果你做的是低功耗设计,CTS阶段就要考虑如何减小时钟树深度、减少高功耗buffer的使用。

5.3 时钟门控(Clock Gating)的注意事项

低功耗设计里,ICG(Integrated Clock Gating cell)用得非常多。时钟门控的引入,带来一个CTS里特有的副作用:它把时钟树的负载切成了几个子域,并且每个ICG输出的时钟信号只有在使能时才翻转,这就导致每个ICG输出的子时钟树,虽然在物理上是同一根时钟网路上分出去的,但在逻辑上可能在某些时间点是相对独立的。

CTS工具在构建时钟树时,会把每一个ICG视为一个“半透明”的节点。也就是说,时钟到达ICG输入端之前,要走一段公共路径;到达ICG之后,各子树单独平衡。这个结构带来一个麻烦:如果ICG的EN信号质量不好,或者ICG本身的delay在不同电压温度下差异大,那么ICG之后的子时钟树之间会产生额外的skew。

在检查CTS报告时,额外关注一下ICG的输出端到叶子之间的延迟。如果发现不同ICG下的子树skew偏大,极有可能是因为ICG插入位置不合理,比如某些ICG离时钟根节点太近,某些又太远。这时可以通过移动ICG的位置、或者在ICG输入端加缓冲器,来重新平衡各子树。

6. 常见问题与排查技巧实录

6.1 问题一:CTS后Skew反而恶化

我遇到最多的情况是:CTS跑完后,报告里的Skew比预估的还要大。排查思路如下。

先确认时钟定义。在Innovus里执行report_clock -summary -skew,看这个时钟域的sink数量是不是异常多。有些时候,时钟定义没有正确排除DFT mux,导致工具把scan时钟和功能时钟当作同一棵时钟树来建,工具为了平衡两边的延迟,只能靠加buffer,最终结果就是Skew变大、面积变大,两边都没讨好。

其次看sink的分布。用GUI或命令打开sink的物理坐标,如果发现sink分布呈现两个极端(比如大部分集中在左上角,少部分跑到右下角),说明工具为了平衡全局,必然要把左上角也拉长到和右下角一样的delay,Skew自然就大了。这时候的思路是拆成多组时钟树,而不是死磕单一平衡树。

6.2 问题二:transition时间过大导致setup违例

时钟信号transition过大,本质是驱动能力不够或者绕线过长。排查方式很简单:在报告里找到transition违例的时钟网络,去GUI里看它的物理路径,看是不是绕了一圈才到达端点。

如果是绕线过长,手段是换驱动能力更大的clock buffer,或者让工具把该时钟网络的绕线层限制到低电阻层。如果是因为负载太重,先看sink数量,是不是一个buffer带了异常多的寄存器,可以通过set_clock_tree_options -max_fanout限制最大扇出,让工具手动多插一级buffer。

但要小心:分叉层级的增加会直接增加latency,而latency增大会影响hold margin的计算。所以transition和latency是CTS里一对需要平衡的矛盾指标。修transition时,要时刻关注latency的变化,不要按下葫芦浮起瓢。

6.3 问题三:多个时钟域交互处的CTS优化

多个时钟域交互(CDC)是CTS里的一块硬骨头。异步时钟域之间不用做同步,但时钟树物理上如果重叠,工具会默认去平衡两个域的skew,导致两边都做不好。

这里的关键是把异步时钟域的CTS约束隔离开。在Innovus里可以用set_clock_groups -asynchronous把时钟组之间的时序路径设为false path,让工具不去优化它们之间的时序。这样做之后,CTS会更加“自私”地优化各自域内的skew,效果通常立竿见影。

另外一个常见陷阱是跨时钟域的保持时间违例。因为异步域数据不是同步的,可能会有偶尔的保持违例命中。处理办法是检查SDC里是否对CDC路径做了set_false_pathset_max_delay约束。如果没做,请尽快补上。

6.4 项目实战中的经验心得总结

做CTS这几年,最大的感受是:CTS最难的从来不是工具怎么用,而是你怎么理解你的设计。工具是一个高度智能的求解器,你给它正确的约束,它能给你一个高质量解;你给它糊涂的约束,它只会装模作样地“优化”出一个满足你错误约束的糊涂结果。

有几个原则是我一直坚持的:

时钟定义一定自己过一遍,不要轻信综合脚本里的SDC。综合工程师可能为了时序收敛,在SDC里写了各种奇怪的时钟约束,这些约束在逻辑阶段是好的,但到后端阶段可能变成CTS的枷锁。

CTS之前先做floorplan review。寄存器密度、macro位置、电源网络密度,这些都会深刻影响时钟树质量。如果floorplan里macro摆放把一块时钟区域切割得支离破碎,再厉害的CTS也建不出漂亮的树。与其在CTS阶段折腾,不如回到floorplan阶段把关键模块调整好。

每个项目都要积累属于自己的CTS check list。把上一步骤、本步骤、下一步骤的检查项目列表记录下来,每次做新项目都翻一下,按顺序打钩。CTS环节的不少问题都有强烈的上下文依赖性,换一个设计、换一个工艺库,之前管用的参数可能就不管用了,但检查清单是通用的。

写在最后

时钟树综合是一面镜子,照见的是你对芯片物理本质的理解程度。理解了时钟信号的传输特性,理解了Skew、Latency、Jitter在时序分析中的角色,理解了工具优化目标的取舍逻辑,你才能真正驾驭CTS,而不是被工具的报告追着跑。

这一篇侧重的是时钟信号本身的原理和CTS的整体框架,下一期我打算结合具体的Innovus脚本来做一次完整的CTS实战演示,包括约束设置、时钟树选项调优、时钟树报告解读那个环节。如果你们在项目里遇到过什么特别的CTS问题,欢迎在评论区聊,我尽量都回复。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询