资讯动态

数字后端CTS时钟树综合:clock_opt与build_clock子步骤深度拆解

发布时间:2026/10/23 7:16:43 来源:尧图企业网站定制
1. 从一条时序路径说起CTS 到底在解决什么问题数字后端做久了你会发现一个很有意思的现象综合出来的网表跑 STA 看着还行一旦进了布局布线时序就开始崩。尤其是时钟路径明明约束写得没问题setup 和 hold 的违例却像约好了一样同时冒出来。这时候老手通常会甩一句先跑 CTS 看看然后问题就神奇地缓解了。但 CTS 到底干了什么它凭什么能救时序CTS全称 Clock Tree Synthesis时钟树综合。名字听着挺唬人本质上就干一件事把时钟信号从时钟源PLL 输出、时钟端口或者内部生成的时钟尽可能均匀、低偏斜地送到每一个触发器的时钟端。你可以把它想象成城市供水系统——水厂是时钟源每家每户是触发器水管就是时钟网络。如果水管粗细不一、路径长短悬殊有的住户水压大有的水压小那整个城市的用水节奏就乱了。CTS 要做的就是重新铺设这套管网让每个用水点的水压尽量一致、到达时间尽量接近。在 FCFusion Compiler的clock_opt流程里CTS 不是一个孤立的命令而是一整套子步骤的集合。很多人跑clock_opt的时候只看到日志刷屏却不知道每一步在忙什么。这篇文章我就把build_clock这条主线拆开把里面的子步骤一个个摆出来讲清楚每一步的意图、参数怎么调、坑在哪里。适合已经跑过完整后端流程、但对 CTS 内部机制还比较模糊的工程师也适合想搞清楚为什么我的时钟树长成这样的进阶读者。核心关键词先摆出来clock_opt、build_clock、时钟树综合、skew、insertion delay、clock gating、NDR、balance point。这些词后面会反复出现先把它们和实际动作对应上读起来会顺很多。2. clock_opt 的整体设计思路与 build_clock 的定位2.1 为什么 CTS 要放在 place 之后、route 之前时序问题的根源往往不在数据路径而在时钟路径。综合阶段工具对时钟网络是理想化处理的——它假设所有触发器的时钟端同时收到时钟沿skew 为零。这个假设在真实芯片里根本不成立。一旦进入布局阶段触发器被撒在芯片各处时钟走线的长度差异、负载差异、串扰影响全部暴露出来skew 可能达到几百皮秒甚至更大。skew 一大setup 和 hold 的余量就被吃掉违例自然出现。那为什么不在 place 之前就做 CTS因为 CTS 需要知道每个触发器的物理位置才能规划时钟树的拓扑。位置没定你没法算走线长度也没法做缓冲器插入。所以标准流程是综合 → 布局place→ CTS → 布线route→ 时序签核。CTS 卡在 place 和 route 之间是因为它既要依赖布局结果又要为后续布线提供时钟网络的物理结构。FC 的clock_opt命令把 CTS 和时钟相关的优化打包在一起内部会调用build_clock来实际构建时钟树。你可以把clock_opt理解成一个总调度器build_clock是执行核心。理解build_clock的子步骤就理解了 CTS 的全貌。2.2 build_clock 的核心目标skew、latency、面积三者的平衡CTS 不是单纯追求 skew 最小。如果只看 skew把所有触发器堆在一起、时钟线走得一样长就行了但那样面积爆炸、布线拥塞、功耗飙升。实际工程里 CTS 要在三个目标之间找平衡skew 控制让同一时钟域内所有触发器的时钟到达时间差异尽可能小。通常目标是把 skew 压到时钟周期的 5% 以内高频设计可能要求更严。insertion delay 控制从时钟源到触发器时钟端的延迟。这个值不能太大否则时钟树级数多、功耗高也不能太小否则缓冲器驱动能力不够、信号质量差。面积与功耗时钟树上的缓冲器和反相器会消耗大量面积和动态功耗。一个设计里时钟树功耗占到总功耗的 20% 到 40% 很常见所以 CTS 必须控制缓冲器数量。build_clock的每一步子操作本质上都是在为这三个目标的平衡服务。下面我把子步骤按执行顺序拆开讲。2.3 从 NDR 到 balance point几个必须先建立的概念在进入子步骤之前有几个概念必须先说清楚否则后面看参数会懵。NDRNon-Default Rule非默认布线规则。时钟线通常要求比普通信号线更宽、间距更大目的是降低电阻减少延迟和减少串扰。CTS 之前一般要先给时钟网络定义 NDR比如线宽用 2 倍最小线宽、间距用 2 倍最小间距。这个规则会直接影响时钟树的延迟和信号完整性。balance point平衡点。时钟树是树状结构从根到叶有多条路径。balance point 是指某条路径上开始分叉的那个节点。CTS 的目标就是让从根到各个 balance point 再到叶子的延迟尽量一致。理解 balance point 有助于你读懂 CTS 日志里那些balancing开头的行。clock gating时钟门控。为了省功耗很多设计会在时钟路径上插入门控单元integrated clock gating cellICG。CTS 必须把这些 ICG 也纳入时钟树保证门控后的时钟仍然满足 skew 要求。ICG 的插入位置和时钟树结构强相关是 CTS 的一个难点。skew group偏斜组。FC 会把需要一起平衡的时钟端归到一个 skew group 里。同一个 group 内的触发器会被 CTS 统一平衡。跨 group 的 skew 通常不保证。所以 CTS 之前要检查 skew group 的划分是否符合设计意图。这些概念在后面每个子步骤里都会出现先有个印象就行。3. build_clock 子步骤逐个拆解3.1 第一步时钟网络预处理与 NDR 施加build_clock真正开始建树之前会先做一轮预处理。这一步的核心动作是给时钟网络打上 NDR 标记并识别哪些 net 属于时钟网络。时钟网络的识别依赖 SDC 里的create_clock、create_generated_clock以及set_clock_gating_check等约束。工具会从时钟源出发沿着组合逻辑路径追踪把所有受时钟驱动的 net 标记出来。这里有个常见坑如果时钟路径上经过了多路选择器MUX或者门控单元工具可能追踪不全导致部分时钟 net 没被标记后续 CTS 就不会去平衡它们。解决办法是在 CTS 之前用report_clock_network之类的命令检查时钟网络的覆盖情况确认没有遗漏。NDR 的施加方式通常是在clock_opt之前用set_clock_routing_rules或者类似命令定义。一个典型的 NDR 配置是这样的# 定义时钟网络的非默认布线规则 set_clock_routing_rules -net_type clock \ -rules {double_width double_spacing} \ -min_routing_layer M3 \ -max_routing_layer M7这里double_width和double_spacing是工艺库里预定义的规则名具体叫什么取决于代工厂。min_routing_layer和max_routing_layer限制时钟线只能走中间层因为底层金属电阻大、顶层金属太宽不适合做时钟。这个限制很重要走错层会导致延迟剧增。注意NDR 不是越宽越好。线宽加倍会让电容也增加如果驱动能力跟不上延迟反而变大。一般先用默认 NDR 跑一版看 insertion delay 和 skew 报告再决定要不要加宽。预处理阶段还会做一件事检查时钟网络上有没有组合逻辑环、有没有未约束的时钟端。这些检查在日志里通常以check_clock_network或verify_clock_tree的形式出现。如果这一步报错后面建树一定出问题所以别跳过日志。3.2 第二步时钟树拓扑规划与 balance point 选择预处理完成后build_clock进入拓扑规划阶段。这一步工具会根据触发器的物理位置决定时钟树怎么分叉、在哪里放 balance point。拓扑规划的核心逻辑是聚类。工具会把物理位置相近的触发器归为一组每组共享一个 balance point。聚类算法通常基于曼哈顿距离或者类似的度量。你可以通过set_clock_tree_options里的-target_skew和-max_fanout等参数影响聚类结果。max_fanout是个关键参数。它限制一个缓冲器最多驱动多少个时钟端。设得太小缓冲器数量爆炸面积和功耗上去设得太大单个缓冲器驱动能力不足延迟和 skew 变差。经验值一般在 20 到 40 之间具体看工艺和时钟频率。高频设计取小值低频设计可以取大值。拓扑规划阶段还会决定时钟树的级数。级数越多skew 越好控制但 insertion delay 越大。一般 4 到 8 级比较常见。级数的选择跟芯片尺寸和时钟频率有关大芯片、高频时钟需要更多级来平衡。这一步的日志里会出现大量balancing和clustering开头的行。如果你看到balance point的数量异常多可能是max_fanout设得太小或者触发器分布太散。这时候可以考虑调整布局约束让触发器更集中。3.3 第三步缓冲器插入与 sizing拓扑定下来之后工具开始在时钟树的各个节点插入缓冲器或反相器。这一步是 CTS 里最耗计算资源的因为工具要同时决定插入什么类型的缓冲器、插在哪里、驱动能力多大。缓冲器插入的策略通常是自顶向下从时钟源开始逐级往下插。每一级根据负载大小选择合适的缓冲器尺寸。负载大就用大尺寸缓冲器负载小就用小尺寸。工具内部会有一个缓冲器库里面记录了每种缓冲器的驱动能力、输入电容、延迟特性。选择过程本质上是查表和优化。这里有个实操技巧可以在 CTS 之前用set_dont_use把一些驱动能力过小或过大的缓冲器禁掉缩小工具的选择范围加快收敛。比如# 禁用驱动能力过小的缓冲器避免时钟树上出现太多小缓冲器 set_dont_use [get_lib_cells */BUF_X1] set_dont_use [get_lib_cells */BUF_X2]但要注意禁得太狠可能导致某些节点找不到合适的缓冲器工具会报错或者用不理想的单元凑合。建议先跑一版看报告再决定禁哪些。缓冲器插入阶段还会处理 clock gating 单元。ICG 通常需要特定的时钟树结构——它的时钟输入必须来自一个 balance point输出再往下分。如果 ICG 插得不好门控后的时钟 skew 会很大。FC 里可以用set_clock_gating_options来控制 ICG 的处理方式比如是否允许工具移动 ICG 的位置、是否允许复制 ICG 来平衡负载。实操心得ICG 的 enable 信号时序经常被忽略。CTS 只保证时钟路径的 skewenable 信号的 setup/hold 需要单独约束和检查。我踩过的坑是 CTS 后时钟 skew 很好但 ICG 的 enable 路径违例导致功能错误。所以 CTS 后一定要跑一遍带 clock gating check 的 STA。3.4 第四步skew group 平衡与跨域处理缓冲器插完之后工具会做一轮全局平衡。这一步的目标是让同一个 skew group 内所有时钟端的到达时间尽量一致。平衡的过程是迭代的工具先测量每个时钟端的实际到达时间找出最早和最晚的然后调整缓冲器尺寸或者插入延迟单元delay cell来拉平。延迟单元是一种专门用来增加延迟的单元通常只在 CTS 里用。如果某个时钟端到得太早就给它加一个延迟单元到得太晚就减小它前面缓冲器的尺寸或者减少级数。跨 skew group 的处理是另一个话题。不同 group 之间的 skew 不保证但如果两个 group 有数据交互跨域路径的时序就会受影响。常见做法是把相关 group 合并或者用set_clock_groups声明异步关系。如果两个时钟域确实是同步的但被分到了不同 groupCTS 后跨域路径的 setup/hold 会很难看。这一步的日志里会有skew group和balancing的统计信息。重点关注target skew和achieved skew的差距。如果 achieved 远大于 target说明平衡没做好可能需要放宽 target 或者调整布局。3.5 第五步时钟树合法化与 DRC 修复平衡做完时钟树的结构基本定了。但这时候的时钟树还只是逻辑结构物理上可能违反设计规则。合法化legalization这一步就是把缓冲器和延迟单元放到合法的位置上避免重叠、避免超出 row 边界。合法化之后是 DRC 修复。时钟网络的 DRC 包括最小面积、最小间距、天线效应等。因为时钟线用了 NDR间距要求比普通线大更容易出现 DRC 违例。工具会自动插入填充单元或者调整走线来修复但有些违例需要手动介入。这一步的常见问题是缓冲器放不下。如果某个区域触发器太密集缓冲器插不进去工具会报legalization failure。解决办法是调整布局密度给时钟树预留空间。可以在 place 阶段用set_place_options里的-clock_tree_reserve之类的参数预留时钟树面积。注意合法化之后时钟树的延迟会变化因为缓冲器的实际位置和规划位置有偏差。所以合法化后通常还要再做一轮平衡这就是为什么 CTS 是迭代的。3.6 第六步时钟树报告与质量检查build_clock跑完之后工具会生成一堆报告。这些报告是判断 CTS 质量的关键必须逐项看。核心报告包括报告名称内容关注指标clock tree report时钟树结构、级数、缓冲器数量级数是否合理、缓冲器是否过多skew report各 skew group 的 skewachieved skew 是否接近 targetinsertion delay report从时钟源到各时钟端的延迟最大值是否可接受latency report时钟网络总延迟是否在预算内DRC report时钟网络的设计规则违例违例数量是否为零power report时钟树功耗占总功耗比例是否合理看报告的顺序建议是先看 DRC有违例先修再看 skew 和 insertion delay判断是否达标最后看功耗和面积评估代价是否可接受。如果 skew 不达标优先检查 skew group 划分和max_fanout设置。如果 insertion delay 太大检查时钟树级数和 NDR 是否过宽。如果功耗太高考虑减少缓冲器数量或者降低 NDR 规格。4. 实操过程与关键参数配置4.1 一个完整的 clock_opt 脚本框架下面给一个可参考的clock_opt脚本框架基于常见实践整理具体命令名可能因 FC 版本略有差异。# 1. 设置时钟树基本选项 set_clock_tree_options \ -target_skew 0.05 \ -max_fanout 32 \ -max_transition 0.15 \ -max_capacitance 0.2 \ -buffer_list {BUF_X4 BUF_X8 BUF_X16} \ -delay_cell_list {DEL_X1 DEL_X2} # 2. 设置时钟门控选项 set_clock_gating_options \ -allow_icg_movement true \ -allow_icg_duplication true \ -icg_max_fanout 16 # 3. 设置 NDR set_clock_routing_rules -net_type clock \ -rules {double_width double_spacing} \ -min_routing_layer M3 \ -max_routing_layer M7 # 4. 运行 clock_opt clock_opt -only_cts -no_clock_route # 5. 生成报告 report_clock_tree -summary report_clock_timing -type skew report_clock_timing -type latency report_clock_tree -drc这里-only_cts表示只做 CTS 不做时钟布线-no_clock_route表示先不布时钟线。实际流程里 CTS 和时钟布线可以分开跑先看 CTS 结果再决定要不要布线。target_skew设成 0.05 表示目标 skew 是 50ps。这个值要根据时钟周期来定。如果时钟周期是 1ns50ps 就是 5%比较合理。如果时钟周期是 500ps50ps 就是 10%可能偏松需要设更小。max_transition和max_capacitance是信号完整性约束。transition 太大意味着信号边沿变缓会影响时序和功耗。一般工艺库里会给出推荐值不要超过库里的最大值。4.2 参数选择背后的计算逻辑以max_fanout为例怎么定这个值假设一个缓冲器的驱动能力是能驱动 0.1pF 负载每个触发器时钟端的输入电容是 0.003pF那么理论上一个缓冲器最多驱动 33 个触发器。取 32 就是留了余量。实际选择时还要考虑走线电容所以通常比理论值小一些。再以target_skew为例。假设时钟周期 T1nssetup 余量要求 100pshold 余量要求 50ps。如果 skew 是 50ps那么 setup 和 hold 各被吃掉 50ps余量还剩 50ps 和 0pshold 就很危险。所以 target_skew 要结合时序余量来定不能拍脑袋。insertion delay 的预算也类似。假设时钟源到触发器的延迟预算 500ps时钟树级数 6 级每级缓冲器延迟 50ps走线延迟 30ps那么总延迟约 6×(5030)480ps接近预算。如果级数加到 8 级延迟就超了。所以级数不是越多越好。4.3 CTS 后的时序检查与迭代CTS 跑完不是终点必须跑 STA 验证。重点检查三类路径时钟路径本身skew、latency、transition 是否达标。跨时钟域路径setup/hold 是否满足clock gating check 是否通过。时钟到输出的路径输出端口的时钟延迟是否在约束范围内。如果发现违例迭代方向通常是调整布局让触发器更集中 → 调整max_fanout和target_skew→ 调整 NDR → 重新跑 CTS。一般迭代 2 到 3 轮能收敛。实操心得CTS 后不要急着跑 route。先跑一版带时钟网络的 STA确认时序没问题再布线。因为布线会改变时钟线的寄生参数如果 CTS 后时序就紧张布线后一定崩。留 10% 到 15% 的余量给布线阶段比较稳妥。5. 常见问题与排查技巧实录5.1 skew 压不下去怎么办skew 压不下去是最常见的问题。排查顺序建议如下检查 skew group 划分用report_clock_tree -skew_group看分组是否合理。如果两个物理位置很远的触发器被分到同一组skew 很难压。检查 max_fanout如果设得太大单个缓冲器驱动太多负载延迟差异大。试着调小。检查 NDR如果时钟线太宽电容大延迟对负载敏感skew 也会大。试着用默认 NDR 跑一版对比。检查布局如果触发器分布太散聚类效果差。考虑在 place 阶段加时钟树友好的布局约束。5.2 insertion delay 过大怎么优化insertion delay 过大通常是级数太多或者 NDR 太宽导致的。优化方向减少时钟树级数但要注意 skew 可能变差。用驱动能力更强的缓冲器减少级数。放宽 NDR减少走线电阻。检查时钟源到第一个 balance point 的路径如果这段太长考虑在布局阶段把时钟源放中间。5.3 时钟树功耗太高怎么降时钟树功耗高一般是缓冲器太多或者 NDR 太宽。降功耗的手段减少缓冲器数量调大max_fanout。用 clock gating 关掉不活跃区域的时钟。降低 NDR 规格但要注意时序不能崩。检查有没有冗余的缓冲器比如工具为了平衡插了不必要的延迟单元。5.4 常见问题速查表问题现象可能原因排查命令解决方向skew 超标skew group 划分不合理report_clock_tree -skew_group重新划分 groupskew 超标max_fanout 太大report_clock_tree -summary调小 max_fanoutinsertion delay 大级数太多report_clock_tree -levels减少级数insertion delay 大NDR 太宽report_clock_routing_rules放宽 NDR功耗高缓冲器过多report_clock_tree -power调大 max_fanoutDRC 违例时钟线间距不够report_clock_tree -drc调整 NDR 或手动修ICG 时序违例enable 路径未约束report_timing -to ICG/enable加约束重新优化合法化失败布局太密report_legalization调整布局密度5.5 几个容易忽略的坑第一个坑是时钟网络的覆盖不全。如果时钟路径上有多路选择器或者自定义的门控逻辑工具可能追踪不到导致部分触发器没被纳入 CTS。解决办法是在 CTS 前用report_clock_network检查覆盖必要时手动标记。第二个坑是generated clock 的处理。分频器、倍频器产生的时钟需要单独约束否则 CTS 会把它们当成普通时钟处理skew 和 latency 都不对。用create_generated_clock明确声明。第三个坑是CTS 后的 ECO。CTS 后如果改了网表时钟树可能失效需要重新跑 CTS 或者做时钟树 ECO。ECO 比全量 CTS 快但要注意保持 skew 不退化。第四个坑是多时钟域的交互。如果两个时钟域有数据交互但被声明为异步CTS 不会平衡它们之间的 skew跨域路径的时序要靠同步器或者 FIFO 解决。如果误声明为异步功能可能出错。6. 从 build_clock 到 clock_opt把子步骤串起来看把build_clock的子步骤串起来其实就是一条从预处理到报告的质量链预处理保证时钟网络识别正确、NDR 到位拓扑规划决定树的骨架缓冲器插入填充血肉平衡调整让 skew 收敛合法化和 DRC 修复保证物理可实现报告检查确认质量达标。每一步都影响最终结果跳过任何一步都会在后面付出代价。clock_opt作为上层命令把这些子步骤按顺序调度并在必要时迭代。理解build_clock的内部机制最大的好处是当 CTS 结果不理想时你知道该调哪个参数、该看哪个报告而不是盲目重跑。我个人在实际操作中的体会是CTS 的功夫一半在 CTS 之前。布局阶段给时钟树留好空间、约束写清楚、时钟网络识别完整CTS 跑起来就顺。反过来如果布局一团糟、约束有遗漏CTS 怎么调都救不回来。所以别把 CTS 当成万能药它只是后端流程里的一环前后配合才能出好结果。最后再分享一个小技巧CTS 报告里的achieved skew和target skew如果差距在 10% 以内基本可以接受不用死磕。时序签核看的是最终 STA 结果不是 CTS 报告里的数字。把精力放在跨域路径和 clock gating check 上收益更大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑