news 2026/9/25 4:55:30

Innovus分段长时钟树:5种特殊sink type选型与实战技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Innovus分段长时钟树:5种特殊sink type选型与实战技巧

1. 分段长时钟树到底难在哪:从sink type说起

做数字后端这行的朋友,尤其是经常跟Innovus打交道的,应该都有过这样的经历:跑完CTS,report一看,某条clock path的latency大得离谱,skew倒是压得不错,但insertion delay高得让人心里发毛。再仔细一看,这条clock tree从root到leaf穿过了大半个die,中间还跨了好几个power domain。这种场景下,clock tree的sink type选择就变得非常关键了。

所谓分段长时钟树,说白了就是clock tree的物理跨度很大,从clock root到最终的sink点,路径上可能经过多个hierarchy、多个voltage island、甚至多个clock domain的边界。这种树如果按常规方式一刀切地设成同一种sink type,要么工具优化不动,要么优化出来的结果在某个局部看起来还行,全局一看全是问题。

Innovus里CTS阶段支持的sink type有好几种,常用的包括stop pin、exclude pin、float pin、through pin、leaf pin等等。每种type背后对应的是工具对这条path的约束策略和优化自由度。选对了,工具知道哪里该停、哪里该穿、哪里该重点balance;选错了,要么工具在该停的地方继续往下buffer,要么在该穿的地方给你硬生生截断,最后latency和skew两头不讨好。

我见过不少项目,CTS阶段为了图省事,把所有sink都设成stop pin,结果工具在长路径上疯狂插buffer,insertion delay直接爆掉,后面再怎么调useful skew都救不回来。也见过把该设成through pin的地方设成了leaf pin,导致工具在中间节点就开始做balance,最后leaf端的skew反而压不住。

这篇文章主要面向有一定Innovus CTS经验的数字后端工程师,尤其是那些正在处理大规模SoC、多power domain、长clock latency场景的同行。我会把5种特殊sink type在分段长时钟树里的应用技巧拆开来讲,包括每种type的适用场景、设置方法、常见坑点,以及怎么配合其他CTS约束一起用。内容基于我自己在多个项目里的实操经验,有些是踩过坑之后总结出来的,有些是跟同行交流时学到的,希望能帮你在下次跑CTS时少走点弯路。

2. 五种特殊sink type的核心机制与选型逻辑

2.1 stop pin:什么时候该让工具“到此为止”

stop pin是CTS里最常用也最容易被滥用的sink type。它的核心语义是告诉工具:这条path到这里就结束了,不要再往下trace,也不要在这个点之后插任何CTS buffer。工具会把stop pin当作一个clock tree的终点来对待,围绕它做balance和optimization。

在分段长时钟树里,stop pin的典型应用场景是clock gating cell的enable端或者某个子模块的clock input port。比如你有一个大的CPU cluster,它的clock是从顶层root分下来的,但cluster内部有自己的clock tree。这时候你可以在cluster的clock input port上设stop pin,让顶层CTS只负责把clock推到cluster入口,cluster内部的tree由它自己的CTS run去处理。这样做的好处是顶层CTS的优化范围可控,不会因为cluster内部复杂的clock结构而把顶层tree搞得过于臃肿。

但这里有个坑:stop pin设多了,clock tree会被切得太碎。我见过一个项目,为了控制latency,在中间hierarchy上设了十几个stop pin,结果顶层tree的skew怎么都压不下去,因为每个stop pin都是一个独立的balance目标,工具在多个目标之间来回妥协,最后哪个都做不好。所以stop pin的使用原则是:只在真正的clock domain边界或者物理分区边界上设,不要为了局部优化而随意设。

设置方法上,Innovus里可以通过set_ccopt_property sink_type来指定,也可以在create_ccopt_clock_tree_spec文件里直接写。推荐的做法是在spec文件里显式声明,这样可追溯性好,后面ECO的时候也容易改。

# 在ccopt spec文件里设置stop pin set_ccopt_property sink_type stop [get_pins cpu_cluster/ck_in]

注意:stop pin一旦设定,工具就不会再往该pin的fanout方向trace。如果这个pin后面还有你希望工具balance的leaf,那就要慎重,因为工具根本看不到它们。

2.2 exclude pin:把不需要balance的点摘出去

exclude pin的语义比stop pin更彻底:工具不仅不往这个pin后面trace,而且完全不把这个pin纳入clock tree的balance目标。换句话说,这个pin在CTS眼里就是透明的,它既不是sink也不是through,工具不会为它做任何latency匹配。

这个type在分段长时钟树里的典型用途是测试逻辑的clock端或者某些always-on的monitor clock。比如DFT的scan clock,在function mode下你根本不关心它的latency,那就可以设成exclude pin,让工具把精力集中在function clock上。再比如一些performance monitor或者debug logic的clock,它们对skew不敏感,设成exclude可以避免工具为了balance它们而牺牲主clock的质量。

但exclude pin有个隐藏风险:如果这个pin后面实际上还有function logic的clock,你把它exclude了,那部分logic的clock就完全没人管了。我遇到过一种情况,某个模块的clock在RTL里被mux过,function mode下走主clock,test mode下走scan clock。工具默认会把mux的输出当作一个sink来balance,但如果你把mux的某个input设成exclude,工具可能会误判整个mux的输出都不需要balance。所以设exclude之前,一定要确认这个pin的fanout里没有你关心的function clock。

# 设置exclude pin set_ccopt_property sink_type exclude [get_pins dft_scan_ck]

2.3 float pin:让工具“看着办”的灵活选项

float pin是五种type里最灵活的一种。它的语义是:工具可以把这个pin当作sink来balance,也可以当作through来trace,具体怎么处理由工具根据全局优化目标来决定。换句话说,你把决定权交给了工具。

在分段长时钟树里,float pin适合用在那些你不太确定该stop还是该through的中间节点上。比如某个hierarchy的clock input,你既希望工具能balance它到和其他sink差不多的latency,又希望工具能继续往下trace到内部的leaf。这时候设成float,工具会根据实际情况判断:如果往下trace对全局skew有利,它就往下走;如果停在这里更有利于balance,它就停。

但float pin的问题是结果不可预测。同一个设计,两次CTS run可能会得到不同的结果,因为工具的优化算法有一定的随机性。所以如果你的clock tree对一致性要求很高,比如要做ECO或者要跟其他corner做对比,float pin可能会给你带来麻烦。我的建议是:在项目初期可以用float pin来探索工具的优化倾向,但到了signoff阶段,最好把float pin替换成明确的stop或through,让结果可控。

# 设置float pin set_ccopt_property sink_type float [get_pins sub_module/ck_in]

2.4 through pin:长路径上的“穿针引线”

through pin是分段长时钟树里最重要的type之一。它的语义是:工具必须穿过这个pin继续往下trace,但这个pin本身不作为一个balance目标。换句话说,这个pin在clock tree里是一个“路过”的节点,工具会在它上面插buffer来驱动后面的load,但不会为了它单独做latency匹配。

through pin的典型应用场景是长clock path上的中间buffer节点或者跨power domain的level shifter。比如你的clock从顶层root出发,经过一个always-on的power domain,再进入一个可关断的domain。在always-on domain里有一个level shifter或者isolation cell,它的clock input就是一个天然的through pin。工具需要穿过它继续往下走,但不需要为它做balance,因为它本身不是最终的sink。

through pin用得好,可以显著减少长路径上的buffer数量。我做过一个对比:同样一条跨三个power domain的clock path,如果中间节点都设成stop pin,工具会在每个domain边界都插一堆buffer来做balance,总buffer数多了将近40%。改成through pin之后,工具只在必要的地方插buffer,latency反而更小,因为路径上的逻辑级数少了。

但through pin也有坑:如果through pin后面的load太大,工具可能会在through pin前面插很多buffer来驱动,这时候through pin就变成了一个事实上的sink,latency会集中在它前面。所以设through pin的时候,要关注它后面的fanout情况,如果fanout太大,考虑在它后面再加一级buffer或者设一个stop pin来分担。

# 设置through pin set_ccopt_property sink_type through [get_pins level_shift/ck_in]

2.5 leaf pin:最终sink的精确控制

leaf pin是clock tree的最终终点,也就是flop的clock pin或者latch的gate pin。在分段长时钟树里,leaf pin的设置直接决定了工具在最后一级的balance策略。

默认情况下,工具会把所有flop的clock pin都当作leaf pin来处理。但在分段长时钟树里,有时候你需要手动指定某些pin为leaf,比如当某个flop的clock是从一个非标准的cell过来的时候,或者当你想把某个pin从through改成leaf来强制工具在那里结束tree的时候。

leaf pin的一个关键属性是leaf pin的balance group。工具会把leaf pin按照它们的clock domain、voltage island、以及你指定的group来分组,然后在组内做balance。在分段长时钟树里,如果你不显式地指定group,工具可能会把不同domain的leaf混在一起balance,结果就是跨domain的skew怎么都压不下去。所以我的习惯是:在CTS之前,先根据clock domain和power domain把leaf pin分好组,然后在spec里显式指定每个group的balance目标。

# 设置leaf pin并指定balance group set_ccopt_property sink_type leaf [get_pins cpu_core/reg_*/CK] set_ccopt_property balance_group cpu_core_grp [get_pins cpu_core/reg_*/CK]

提示:leaf pin的balance group不要设得太细,否则工具会在每个小组内单独balance,全局skew反而会变大。一般建议按clock domain来分,最多再按power domain细分一层。

3. 分段长时钟树里的组合应用与参数计算

3.1 怎么根据路径长度和domain数量选type

在实际项目里,一条clock path上往往需要组合使用多种sink type。我的经验是先看路径长度,再看domain数量,最后看leaf的分布。

如果路径长度超过2000微米,或者穿过3个以上的power domain,那中间节点大概率要用through pin来减少buffer级数。如果路径上某个节点后面有独立的clock tree,那这个节点设stop pin。如果某个节点只是路过,后面还有大量leaf需要balance,那设through。如果某个节点的latency你完全不关心,设exclude。

具体怎么判断?我一般会先跑一次CTS,用report_ccopt_clock_trees看一下工具默认是怎么处理的,然后根据report里的latency和buffer分布来调整。比如report显示某条path上插了20个buffer,latency 1.2ns,那就要考虑把中间的一些节点从stop改成through,让工具少插点buffer。

3.2 latency目标怎么定:一个实际的计算例子

假设你的clock period是2ns,root的latency是0.3ns,leaf端的setup time是0.1ns,clock uncertainty是0.05ns。那么leaf端的clock latency最大不能超过:

2ns - 0.3ns - 0.1ns - 0.05ns = 1.55ns

但这只是理论上限。实际做的时候,你要留足够的margin给OCV和后面的ECO。我一般会把目标latency定在理论上限的70%左右,也就是1.1ns左右。然后根据这个目标,反推每个中间节点的latency预算。

比如路径上有3个中间节点,那每个节点的latency预算大概是(1.1 - 0.3) / 3 ≈ 0.27ns。如果某个节点的实际latency超过了这个预算,就要考虑调整它的sink type,比如从stop改成through,让工具把buffer分散到后面的节点去。

3.3 跟useful skew的配合:别让sink type打架

useful skew是CTS里常用的技巧,通过故意让某些leaf的clock早到或晚到来改善setup或hold。但在分段长时钟树里,useful skew和sink type可能会打架。

比如你把某个中间节点设成stop pin,工具会围绕它做balance,这时候如果你又对这个节点后面的leaf施加useful skew,工具可能会因为stop pin的约束而无法实现你想要的skew。所以我的做法是:先确定sink type,再施加useful skew。如果某个leaf需要useful skew,那它的上游节点最好设成through或者float,给工具留出调整latency的空间。

# 先设sink type set_ccopt_property sink_type through [get_pins mid_node/ck_in] # 再施加useful skew set_ccopt_property useful_skew -0.1 [get_pins leaf_reg/CK]

4. 实操流程:从spec编写到CTS signoff

4.1 spec文件的编写要点

Innovus的CTS spec文件是控制sink type的主要入口。我一般会把spec分成几个部分:clock定义、sink type设置、balance group设置、以及exception设置。

clock定义部分要写清楚每个clock的root、period、以及相关的generated clock。sink type设置部分按hierarchy来组织,先设顶层的stop和through,再设中间层的float,最后设leaf的balance group。exception部分用来处理一些特殊情况,比如某个pin需要单独设成exclude。

# 示例spec结构 # 1. clock定义 create_ccopt_clock_tree -name func_clk -source [get_ports clk_in] set_ccopt_property target_skew 0.05 -clock_tree func_clk # 2. sink type设置 set_ccopt_property sink_type stop [get_pins cpu_cluster/ck_in] set_ccopt_property sink_type through [get_pins level_shift/ck_in] set_ccopt_property sink_type float [get_pins sub_module/ck_in] # 3. balance group set_ccopt_property balance_group cpu_grp [get_pins cpu_core/reg_*/CK] # 4. exception set_ccopt_property sink_type exclude [get_pins dft_scan_ck]

4.2 CTS run的检查清单

跑完CTS之后,我一般会按以下顺序检查:

  1. report_ccopt_clock_trees:看每个tree的latency、skew、buffer数量。重点看latency有没有超过预算,buffer数量有没有异常多。
  2. report_ccopt_skew_groups:看每个balance group的skew。如果某个group的skew特别大,检查它的sink type设置是不是有问题。
  3. check_ccopt_clock_tree_convergence:看CTS有没有收敛。如果没收敛,看log里的warning和error。
  4. report_ccopt_latency:看每条path的latency分布。如果某条path的latency明显高于其他,检查它的sink type是不是设成了stop但实际应该设through。

4.3 ECO阶段的sink type调整

CTS做完之后,ECO阶段可能还需要调整sink type。比如某个模块的clock latency在post-CTS timing里成了critical path,那就要考虑把它的sink type从stop改成through,让工具在ECO时重新balance。

ECO阶段调整sink type要注意:不要一次性改太多。每次改一两个节点,跑一次ECO,看结果。因为sink type的改变会影响工具对整个tree的优化策略,改多了容易导致ECO不收敛。

# ECO阶段调整sink type set_ccopt_property sink_type through [get_pins critical_module/ck_in] ccopt_design -cts

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

5.1 sink type设了没生效?先查这几项

这是最常见的问题。你明明在spec里设了stop pin,但report里显示工具还是往下trace了。排查顺序如下:

  • 检查spec文件有没有被正确加载。Innovus里用ccopt_design -spec来加载spec,如果路径写错了或者文件格式有问题,spec会被忽略。
  • 检查pin的hierarchy path对不对。get_pins的path必须跟网表里的完全一致,大小写敏感。
  • 检查有没有被后面的设置覆盖。比如你先设了stop,后面又设了through,那最终生效的是through。
  • 检查这个pin是不是被设成了exclude。exclude的优先级比stop高,如果同时设了,exclude生效。

5.2 latency压不下去?试试调整through pin的位置

如果某条path的latency怎么都压不下去,大概率是through pin的位置不对。我的经验是:through pin应该设在路径的中间偏后位置,而不是最前面。因为工具在through pin前面插的buffer会直接累加到latency上,如果through pin太靠前,前面的buffer级数就会很多。

比如一条path有5个中间节点,through pin设在第4个节点上,那工具只需要在前4个节点之间插buffer,级数可控。如果设在第1个节点上,工具要在第1个节点前面插buffer来驱动后面所有的load,级数就会爆炸。

5.3 skew在跨domain边界变大?检查balance group

跨power domain的skew是分段长时钟树的经典难题。工具默认会把所有leaf放在一个balance group里,但不同domain的leaf可能因为voltage不同而latency差异很大。这时候要把不同domain的leaf分到不同的balance group,然后分别设target skew。

但分group之后,group之间的skew可能会变大。这时候可以用useful skew来补偿:让latency大的group的target skew设小一点,latency小的group设大一点,这样group之间的skew就能压下来。

5.4 常见问题速查表

问题现象可能原因排查方法解决措施
sink type设了没生效spec未加载/路径错误/被覆盖检查spec加载log,确认pin path重新加载spec,修正path
latency过大through pin位置太靠前report_ccopt_latency看buffer分布把through pin往后移
跨domain skew大balance group未分或分得太细report_ccopt_skew_groups按domain分group,配合useful skew
CTS不收敛sink type冲突check_ccopt_clock_tree_convergence减少float pin,明确stop/through
ECO后latency变差sink type改动太多对比ECO前后的report每次只改一两个节点

提示:CTS的sink type设置没有“万能模板”,每个设计都要根据实际的clock结构、power domain划分、以及timing目标来调整。我的习惯是在项目初期多花点时间做sink type的探索,把各种组合都试一遍,找到最适合当前设计的方案,后面ECO阶段就会轻松很多。

6. 一些个人体会

做CTS这些年,我越来越觉得sink type的选择本质上是在工具优化自由度和结果可控性之间找平衡。stop pin和exclude pin给工具的自由度小,结果可控但可能不是最优;float pin给工具的自由度大,结果可能更优但不可控;through pin和leaf pin介于两者之间,用好了能兼顾。

分段长时钟树之所以难,是因为它把这种平衡问题放大了。一条短clock path,你随便设什么type,工具都能给你balance得差不多。但一条长path,type设错一个节点,latency可能就差出几百个ps,后面怎么调都调不回来。

我自己的做法是:在项目早期就用report_ccopt_clock_trees把每条长path的latency和buffer分布看清楚,然后针对性地设sink type。不要等到CTS跑完了发现latency爆了再回头改,那时候改的成本会高很多。另外,spec文件一定要写得清晰、可追溯,每个sink type的设置都要有注释说明为什么这么设,这样后面ECO或者换人接手的时候,不至于一脸懵。

最后再分享一个小技巧:如果你不确定某个节点该设什么type,可以先设成float跑一次CTS,看工具怎么处理。如果工具把它当成了sink,那说明它后面的load不大,设成stop可能更合适;如果工具把它当成了through,那说明它后面的load很大,设成through让工具继续往下trace可能更好。这个方法我用了很多次,基本上一次就能判断个八九不离十。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 4:54:21

AS2258固态硬盘量产开卡全攻略:从掉固件到修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:54:02

LabVIEW例程全集真相:版本匹配、依赖修复与串口改造实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:54:00

银河麒麟ARM版离线搭建Qt5开发环境完整实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:53:07

Agent上下文工程:分层压缩与业务指标驱动的实战方法论

1. 项目概述:当Agent开始“记不住事”,我们到底在压缩什么?你有没有遇到过这样的情况:一个精心设计的Agent,在处理一份30页的PDF合同分析任务时,前20页还能条理清晰地提取条款、比对风险点,到了…

作者头像 李华
网站建设 2026/9/25 4:53:02

CE指针扫描定位游戏血量地址:基址偏移与指针链实战教程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 4:50:58

华为昇腾Atlas 300V 24G推理卡部署YOLOv5/v8完全指南

搞推理加速这几年,我手里过过不少卡,但头一回拿到华为昇腾Atlas 300V 24G这块卡的时候,还是被周围人问过同一个问题:这玩意儿到底是不是运算加速卡?它能干嘛?能不能拿来跑YOLO?带着这些疑问&…

作者头像 李华