简介:面向集成电路物理实现工程师的Cadence Innovus CCOpt技术文档,聚焦高性能、低功耗设计中复杂时钟网络的配置与调试问题。内容覆盖时钟树综合(CTS)基础术语、运行前的时钟约束与缓冲器选型设置,以及时钟网络分析、时钟树构建、时钟优化、后处理和验证四个阶段各自的调试点,并展开基本检查、CCOpt时钟树调试器、时钟路径追踪与跨视图探测、报告命令、CCOpt属性分析等调试手段,还涉及数据路径屏蔽、时钟点刻意偏斜、非连通时钟树平衡、公共主时钟派生从时钟处理等架构性时钟控制场景,适合具备一定数字后端基础、需处理复杂时钟网络时序的设计师对照实践。资源包共1个pdf文件,约1.3MB,为官方英文技术指南,结构按目的、受众、术语、准备、流程阶段与调试技术顺序编排,便于按章节检索。目前已有211人学习,可作为CTS阶段配置与排错时的案头参考。
1. 时钟树综合后时序反而更差:一个反直觉的常见坑
跑完 CCOpt,report_timing一看,setup 非但没收敛,某些路径反而比 CTS 之前更糟;或者 hold 出现大面积违例,时钟树看着平衡,latency 却对不上。这类问题在复杂时钟网络里出现频率极高,尤其在多时钟域、门控时钟多、宏单元带时钟直通的 Design 里,一根时钟从源到 sink 跨了三四层结构,SDC 根本没法完整描述真实时钟架构。新手第一反应是加大优化力度,老手会先停掉流程,回头看时钟抽取和 skew group 划分。这份《Configuration and Debug of Complex Clock Networks Using CCOpt》面向的就是 Innovus 18.1/19.1 下这类场景,讲的是把时钟网络拆开、看清结构、再做定点干预的排查方法。如果你也在 cadence innovus cts 流程里被“越优化越差”折腾过,下面的排查顺序可以直接抄。
2. 越优化越差:先确认基础配置没埋雷
CCOpt 能自动从库、Design、SDC 里推断大部分参数,问题是这些数据源的设定本来是为 signoff 服务的,直接拿来建树,结果大概率不是最优。很多“优化不动”或者“建出来的树又大又慢”,根因就在开跑之前那几行配置没显式设置。
2.1 单元列表为空与 dont_use 被覆盖
最容易翻车的一条:cts_buffer_cells、cts_inverter_cells这些列表留空。空列表在 CCOpt 里不是“不限制”,而是“所有单元都参与挑选”,不管 Vt 家族、不管驱动能力,最后选出来的 buffer 可能尺寸离谱,面积和功耗一起爆。另外要特别注意,CCOpt property 里指定的单元会覆盖库定义的dont_use,而 18.1 之后用户手动设的 don't touch 会被尊重。也就是说,你写进列表里的单元,哪怕库标了禁用,照样会被用。
# legacy 命令写法:显式给出可用单元列表 set_ccopt_property buffer_cells {BUF_X2 BUF_X4 BUF_X8} set_ccopt_property inverter_cells {INV_X2 INV_X4 INV_X8} set_ccopt_property clock_gating_cells {CKGATE_X2 CKGATE_X4} set_ccopt_property logic_cells {AND2_X2 OR2_X2 MUX2_X2} # CUI(Stylus)写法 set_db cts_buffer_cells {BUF_X2 BUF_X4 BUF_X8} set_db cts_inverter_cells {INV_X2 INV_X4 INV_X8} set_db cts_clock_gating_cells {CKGATE_X2 CKGATE_X4} set_db cts_clock_gating_cells {CKGATE_X2 CKGATE_X4}逻辑说明:这几个 property 决定了时钟树构建时可用的“积木”。buffer 和 inverter 决定树的驱动能力分布,clock gating cell 决定门控逻辑能不能被一起优化(设了之后能显著减少额外插 buffer 的数量),logic cell 让组合逻辑也进入优化范围。参数上,每个列表里放 2~3 档驱动就够,放太多会让求解空间变大、run time 上升。如果设计里有 always-on 单元,必须手动加进列表,工具不会替你补。
配完之后不是就完事了,工具内部还会对库列表做过滤和裁剪,你给的单元不一定全被用上。想知道为什么某个单元没被选中,直接查:
# 查看单元被过滤的原因 report_ccopt_cell_filtering_reasons report_cts_cell_filtering_reasons这两条报告会列出每个候选单元被剔除的理由,比如驱动不够、Vt 不匹配、被 don't use 排除。我一般会在正式跑之前先跑一遍,确认列表里没有整类单元被静默过滤掉——这种情况下的报错不会出现在 log 里,只会表现为“树建得不对劲”。
2.2 transition 与 skew 目标:过紧反而制造问题
target_max_trans和target_skew是影响时钟网络规模和功耗最大的两个参数,也是最容易被拍脑袋设错的两个。skew 目标设得过紧,工具会拼命做平衡,插一堆 buffer,面积上去、功耗上去,结果时序收益很有限;在低功耗设计里,transition 目标设得过严,时钟网络的电容和动态功耗会成倍增长。而且这两个参数不只影响建树阶段,还会影响 clustering 阶段,也就是说结构调试的时候它们必须已经设对。
# legacy set_ccopt_property target_max_trans 0.12 set_ccopt_property target_skew 0.05 # CUI set_db cts_target_max_transition_time 0.12 set_db cts_target_skew 0.05参数含义:target_max_trans单位与工艺库时间单位一致(常见 ns),一般取库内标准单元典型 transition 的 1.5~2 倍作为起点;target_skew是同一 skew group 内 sink 之间的目标偏差,不是越小越好,通常取时钟周期的 3%~5% 起步再按结果调整。如果 SDC 里带了 transition/skew 约束,不设全局目标时工具会退回用 SDC 的值,所以两处都要看一眼是否一致。
提示:过紧的 skew 目标在复杂时钟结构里会触发过量平衡,表现为某些分支 buffer 数量异常多。遇到这种情况,先放宽目标再对比面积和时序,而不是继续加优化选项。
2.3 抽取前必须落地的 sink 与路由设置
时钟抽取阶段决定了工具“看见”的是什么样的时钟网络。sink_type(stop / ignore / exclude)用来裁剪抽取范围:stop 表示到此为止不再往下追,ignore 表示这个点不算 sink,exclude 表示整条路径排除。带宏单元时钟直通、或者有测试逻辑挂在时钟上的设计,如果不设 sink_type,抽取出来的网络会混进一堆本不该参与平衡的点。
# 时钟网络走非默认布线规则 create_route_type -name clk_ndr_1 -bottom_preferred_routing_layer M3 \ -top_preferred_routing_layer M7 -width_multiplier 2 set_ccopt_property route_type -net_type {top trunk leaf} clk_ndr_1 # CUI 对应写法 set_db cts_route_type_top clk_ndr_1 set_db cts_route_type_trunk clk_ndr_1 set_db cts_route_type_leaf clk_ndr_1逻辑说明:时钟网络默认走普通布线规则,宽度和间距都不够,RC 提取出来偏大,latency 和 skew 都会受影响。给 top/trunk/leaf 三级分别指定 NDR 是常规做法,也可以只给 trunk 和 top 加宽。参数上width_multiplier 2表示线宽翻倍,层范围按工艺调整,低层金属电阻大,一般不让时钟走太低层。抽取设置(extraction settings)通常不用动,除非你的 RC 提取配置本身有问题。
还有一条容易被忘的:update_io_latency。芯片级设计或者时钟架构特别复杂的 Design,建树时修改 IO latency 会让整条时钟路径的延迟基准漂移,后面怎么调都对不上。
# 关闭 latency 修改,chip level 场景常用 set_ccopt_property update_io_latency false set_db cts_update_clock_latency false3. 时钟树看着平衡但时序崩了:分阶段停下来看
复杂时钟网络最忌讳一口气跑完再看结果。CCOpt 的构建分 cluster、trial、full 三个阶段,每个阶段能暴露的问题不一样,中途停下来看中间结果,往往能提前发现面积、功耗、时序上的优化空间——这些在最终结果里是看不出来的。有个硬限制要记住:cluster 和 trial 阶段停下后不能续跑,只能重来,所以停之前想清楚要看什么。
3.1 三个构建阶段各自能查出什么
cluster 阶段做的是 sink 聚类和初步结构规划,这时候能看出 skew group 划分是否合理:如果某个 group 的 sink 数量异常多、或者地理分布极度分散,说明 SDC 或结构约束没描述清楚时钟的真实归属。trial 阶段会试建一部分树,用来看 buffer 类型选择和初步平衡效果,重点看有没有出现异常长的路径或者驱动明显不足的分支。full 阶段才是完整建树,到这里主要验证最终 skew、transition 和 DRC。
分阶段跑的命令结构大致是这样,关键是-stage参数的取值:
# 抽取时钟网络,之后所有针对 clock_tree / skew_group 的设置都要在这之后 ccopt_design -cts -extract # ...此处做 sink_type、per-group 属性等修改... # 只跑到 clustering 阶段,检查分组是否合理 ccopt_design -cts -stage cluster # 检查后重跑,跑到 trial ccopt_design -cts -stage trial # 完整建树 ccopt_design -cts逻辑说明:-extract只做时钟抽取,不建树,是插入各类 per-group 设置的唯一窗口期。cluster 和 trial 是观测窗口,不是可以接着往下走的中间态,观察完必须从头跑。full 阶段没有-stage参数,直接执行即可。这样拆开跑,单次耗时短,出问题时也容易定位到是分组错了还是建树策略错了。
3.2 skew group 与 clock tree 不是一回事
这两个术语在实际设计里经常被混用。skew group 是 CCOpt 用来“搭结构、做初步平衡”的结构性划分,clock tree 是它做“平衡”的时钟网络子集。大多数设计里两者是 1:1,但复杂结构下,一个 clock tree 可能对应好几个 skew group。理解这个区别的意义在于:当你在 clock tree 层面调不动的时候,问题可能出在 skew group 的划分上,得回去改抽取结果或者 SDC。
抽取完成后可以做的操作包括:修改工具自动生成的 clock 和 skew group、增删 skew group、对单个 clock 或 group 施加专属属性、覆盖从 SDC 推出来的属性(比如 SDC 里的 transition/skew 目标,在没有全局目标时会被采用)。这一层是架构级调试的主战场,后面的很多“定点干预”都是在这一步做。
注意:exclusive skew group 的创建要慎重。它会让工具认为这些 group 之间不需要互相平衡,用错地方会直接导致跨域 skew 失控,而且这种问题在最终报告里表现为“某些路径莫名 hold 违例”,很难反推到分组设置上。
3.3 用属性查询反推设计意图
CCOpt 的 property 不只用来设,也可以用来读。当你不确定工具当前“认为”的时钟结构是什么样,先查属性比看 log 快得多。
# 查看当前生效的全局与分组属性 get_ccopt_property target_skew get_ccopt_property -clock_tree <tree_name> target_skew get_ccopt_property -skew_group <group_name> buffer_cells # 列出所有 clock tree 与 skew group report_ccopt_clock_trees report_ccopt_skew_groups逻辑说明:get_ccopt_property不带-clock_tree/-skew_group时查全局值,带上就查该层级的覆盖值。这一对比经常能发现“全局设了但分组上被 SDC 覆盖了”的情况。report_ccopt_*系列报告输出结构清单,重点看 group 数量、每个 group 的 sink 数和归属,跟你在 SDC 里写的时钟关系对照,不一致的地方就是抽取出问题的地方。
4. 定点干预:架构级调整的几种实战场景
分析做完,如果确认问题出在时钟结构本身而不是 SDC 写得不对,就需要对建树过程做定向干预。这一节列的都是复杂时钟网络里高频出现的场景,共同点是:默认行为在这个结构下是错的,必须手工告诉工具“这里不该这么做”。
4.1 把数据通路挡在时钟网络之外
很多设计里,时钟会经过一段组合逻辑再分发给下游,比如带使能控制的时钟门控链、或者为了功能安全加的冗余逻辑。默认情况下 CCOpt 可能把这些数据通路上的单元也当成时钟网络的一部分去做平衡,结果插了一堆本不需要的 buffer,或者把数据路径的时序拖垮。
# 把指定 instance 或 pin 标记为时钟抽取的边界 set_ccopt_property sink_type stop -pin <inst>/<pin> set_ccopt_property sink_type ignore -pin <inst>/<pin> set_ccopt_property sink_type exclude -pin <inst>/<pin>三个取值的差别:stop让抽取到此为止,这个点本身仍是 sink,参与平衡但不再往下追;ignore表示这个点不算 sink,路径继续往下抽;exclude是整条路径都不进时钟网络。选错取值的典型症状是:用了 stop 但下游还有真实的 sink,那些 sink 就脱离了平衡,skew 直接失控;用了 exclude 但下游其实有时钟负载,那部分时钟就根本没被建树,时序分析会出现“时钟到达不了”的诡异违例。判断依据是看这个点下游到底还有没有需要平衡的寄存器。
4.2 故意制造 skew 与拆开不相干的树
有些场景需要主动引入 skew,比如为了给关键路径腾出 setup 余量而让捕获时钟晚到,或者多周期路径上的刻意错位。CCOpt 支持对特定时钟点设定 skew 目标,但这里的原则是“少而准”:只在被分析验证过的点上设,设完必须重新验证跨域 hold。
# 对指定 clock tree 施加非零目标 skew,制造刻意的时钟偏移 set_ccopt_property -clock_tree <tree_name> target_skew 0.08 # 两个时钟树物理上不相干,不需要互相平衡 set_ccopt_property -skew_group <group_a> exclusive true第一段的逻辑:把某个 clock tree 的目标 skew 从全局值改成特定值,工具会按新目标去平衡这个树,从而实现相对其它树的偏移。参数要落在全局 skew 的合理范围内,超出太多会引发其它分支的连锁重平衡。第二段的逻辑:exclusive 声明这两个 group 之间无需平衡,适用于 slave clock 由同一 master 派生但功能上不应互相对齐的情况。用错会导致本该对齐的时钟被拆开,hold 大面积违例。
与之相对的一个场景是“防止本应对齐的树被拆开”:当多个 slave clock 从同一个 master 派生,默认可能被分到不同 skew group 各自平衡,结果彼此之间出现不该有的偏移。这时候要做的是检查抽取结果,把它们归并或显式声明关系,而不是加 exclusive。
4.3 宏单元时钟直通与输出引脚 skew 保持
带时钟直通的宏(clock feedthrough)是复杂时钟网络里最经典的坑之一。宏内部把时钟从输入脚透传到输出脚,默认抽取可能把宏当成黑盒,导致宏两端的时钟被当成两个不相干的网络分别平衡,跨宏的 skew 完全失控。
# 让工具识别宏的时钟直通关系,把两端视为同一时钟网络 set_ccopt_property sink_type stop -pin <macro_inst>/CLK_IN set_ccopt_property feedthrough -pin <macro_inst>/CLK_OUT <macro_inst>/CLK_IN逻辑说明:先在宏的输入脚设 stop,阻止抽取继续往宏内部钻;再用 feedthrough 声明输出脚与输入脚的对应关系,工具就知道这两个点是同一条时钟路径的两端,平衡时会一起考虑。参数上两个 pin 的顺序是“输出脚在前、输入脚在后”,写反了不报错但关系不生效,症状就是跨宏 skew 依旧失控。这类设置必须在抽取之后、建树之前完成。
另一个相关场景是驱动输出引脚(output pin)的逻辑:芯片输出端口上的时钟需要和内部时钟保持相近的 skew,否则下游器件的时序基准就错了。处理思路是把输出引脚路径上的逻辑纳入时钟网络平衡范围,或者在抽取时显式保留这段路径,具体用 sink_type 的取值组合来控制。
4.4 禁用特定时钟的综合与抑制过量 buffer
有些时钟在功能上存在,但在这个实现阶段不该被综合,比如只在测试模式用的时钟、或者由其它模块提供的时钟。默认情况下工具会为所有抽取到的时钟建树,多建的树既占资源又可能干扰真实时钟的平衡。
# 禁止为指定时钟生成时钟树 set_ccopt_property -clock <clock_name> create_clock_tree false这条设置的效果是让工具跳过该时钟的结构生成,但时钟本身在时序分析里依然存在。典型用法是测试时钟和扫描链专用时钟,它们不需要在功能模式下被平衡。要注意的是,禁用之后如果这些时钟上挂着真实的时序路径,分析结果会出现无时钟可达的违例,所以禁用清单要和时序约束的 mode 定义对齐。
过量 buffer 的抑制则来自另一个方向:复杂控制逻辑上如果被误判为需要平衡,工具会疯狂插 buffer。处理手段包括用 sink_type 把这段逻辑排除、放宽局部 skew 目标、或者对特定 group 关闭某些优化。判断是否“过量”的参照是同类结构的 buffer 密度,某个分支明显高出其它分支一个量级,基本就是误判。
5. 避坑清单:五条踩过的坑与对应解法
5.1 停在中途想续跑,结果只能重来
现象:跑到 trial 阶段停下来分析,改了几个设置后想接着往下跑,工具报错或者行为异常。 原因:cluster 和 trial 阶段不是可续跑的状态,流程设计上只支持从抽取开始重跑。 解决:把“停下来看”当成纯观测动作。需要改设置的话,改完从ccopt_design -cts -extract重新走一遍,把观测结论用在下一轮完整流程里。
5.2 空单元列表导致选出的 buffer 完全不符合预期
现象:建出来的树用了大量小驱动单元,级数多、latency 大,或者反过来用了超大驱动单元,面积异常。 原因:cts_buffer_cells等列表为空时有特殊语义——所有单元参与挑选,不受 Vt 和驱动限制。 解决:显式给出 2~3 档驱动的单元列表,always-on 单元手动补进去,跑之前用report_ccopt_cell_filtering_reasons确认没有被整类过滤。
5.3 feedthrough 的 pin 顺序写反,跨宏 skew 依旧失控
现象:宏单元两端时钟分别平衡,跨宏路径 skew 很大,但检查设置看起来“已经配了”。 原因:feedthrough 属性的输出脚和输入脚顺序写反,关系静默失效,不报错。 解决:确认写法是“输出脚在前、输入脚在后”,配完用属性查询确认关系已生效,再看跨宏路径的 skew 报告。
5.4 exclusive skew group 用在了该对齐的时钟上
现象:两个从同一 master 派生的时钟出现不该有的偏移,hold 违例集中在跨这两个域的路径上。 原因:默认抽取把它们分到不同 group,又加了 exclusive 声明,工具认为无需互相平衡。 解决:检查 group 划分是否与 SDC 描述的时钟关系一致,该对齐的时钟要么归并,要么去掉 exclusive,改完重新抽取并复验跨域 hold。
5.5 skew 目标设太紧,面积和功耗双爆
现象:时钟树 buffer 数量明显多于同类设计,功耗超标,时序却没明显改善。 原因:target_skew过紧触发过量平衡,工具为了压 skew 不断插 buffer。 解决:把目标放宽到时钟周期的 3%~5% 起步,对比面积、功耗、时序三者的变化曲线,找到收益拐点再定值。全局目标和 SDC 推导值要一起检查,避免两处不一致。
6. 快速定位:把排查顺序固化成习惯
复杂时钟网络的问题,真正难的不是解决,是定位。我的做法是把排查压成一个固定顺序,从便宜的动作开始,避免一上来就翻大报告。
第一步永远是基础配置核对:单元列表非空且含 always-on、transition 和 skew 目标都显式设了、update_io_latency在芯片级场景已关闭、NDR 已指定。这一步用属性查询就能完成,一分钟内出结果,能排掉相当比例的“玄学”问题。
第二步看抽取结果,用report_ccopt_clock_trees和report_ccopt_skew_groups输出结构清单,跟 SDC 里写的时钟关系逐条对照。重点看三件事:group 数量和归属是否符合预期、有没有本该是 1:1 的关系变成了多对一、有没有宏单元或门控逻辑被错误纳入。这一层的偏差是后面所有问题的源头。
第三步才是分阶段跑。cluster 阶段看分组是否合理、sink 数量分布是否均匀;trial 阶段看 buffer 选择、有没有异常长的分支;full 阶段验证 skew、transition、DRC。每个阶段停下来只做观测,把结论记下来,改设置就从抽取重跑。
定位过程中有几个报告值得常备:
| 排查目标 | 命令 | 关注点 |
|---|---|---|
| 单元被过滤原因 | report_ccopt_cell_filtering_reasons | 是否有整类单元被静默剔除 |
| 时钟树结构清单 | report_ccopt_clock_trees | tree 数量、归属、与 SDC 对照 |
| skew group 划分 | report_ccopt_skew_groups | group 数量、sink 分布、exclusive 使用 |
| 当前生效属性 | get_ccopt_property -clock_tree/-skew_group | 全局值与分组覆盖值是否冲突 |
| 时钟路径追踪 | CCOpt clock tree debugger | 单条路径逐级 latency 与 buffer 类型 |
CCOpt clock tree debugger 是定位单条异常路径的主力工具,配合 clock path tracing 和跨设计视图的 cross probing,可以把一条路径从源到 sink 逐级展开,每一级的 latency、驱动单元、扇出都看得见。基本检查反而是最容易被跳过的:时钟路径延迟是否在合理范围、树是否平衡、扇出比有没有异常突变。这三项查完,问题基本能圈到某个分支或某个 group 上。
从那以后我每次跑 CCOpt,都强制先做一遍基础配置核对和抽取结果清单,再决定要不要动架构级设置——很多所谓“工具跑得不对”的问题,都在第一分钟就被排掉了。希望帮到你。
本文还有配套的精品资源,点击获取