ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

数字IC后端CCOPT时钟树综合实战:skew group划分与useful skew控制

数字IC后端CCOPT时钟树综合实战:skew group划分与useful skew控制 数字IC后端实现里时钟树综合CTS永远是那个让人又爱又恨的环节。爱的是只要时钟树做得干净后面时序收敛能省下一大半精力恨的是CCOPTClock Concurrent Optimization这套机制虽然强大但它的行为逻辑跟传统CTS完全不是一回事很多人第一次跑完CCOPT看到useful skew的结果直接懵了——明明约束写得好好的怎么hold就崩了或者skew group设了一堆结果工具压根没按你想的方式去balance。我自己在多个先进工艺节点上跑过Innovus的CCOPT流程从28nm到5nm都有涉及踩过的坑足够写一本小册子。这篇内容主要面向已经有一定Innovus使用基础、但对CCOPT内部机制和skew group配置还不够清晰的后端工程师从clock spec的编写逻辑开始一路拆到skew group的划分策略和CCOPT的concurrent optimization行为把那些工具文档里不会明说的细节讲透。如果你正在被CTS后的setup/hold违例折磨或者对useful skew的边界控制感到困惑这篇应该能帮你理清思路。1. 为什么CCOPT不是传统CTS的简单升级1.1 传统CTS的balance逻辑与局限传统CTS的核心思路非常朴素把同一个时钟域内所有寄存器的clock latency尽量做相等让skew趋近于零。工具做的事情就是建一棵缓冲树通过插入buffer和inverter来匹配各条路径的延迟。这个思路在早期工艺节点下工作得很好因为那时候线延迟占比不高cell delay是主导因素balance起来相对容易。但到了16nm以下情况完全变了。互连线延迟在总延迟中的占比越来越高有时候能到70%以上。这时候你再去追求零skew代价是什么是大量的buffer插入是面积和功耗的飙升而且由于OCVon-chip variation的影响名义上的零skew在实际硅片上根本不可能实现。更关键的是传统CTS把时钟树和datapath完全割裂开来优化时钟树只管做balancedatapath的时序问题留给后面的优化去修这种串行思路本身就限制了PPA的进一步优化空间。1.2 CCOPT的concurrent optimization到底在优化什么CCOPT的全称是Clock Concurrent Optimization关键词在concurrent上。它不再把时钟树综合和datapath优化当成两个独立的阶段而是在CTS阶段就同时考虑setup和hold的时序需求通过有意地引入useful skew来改善整体时序。举个具体的例子你就明白了。假设有一条路径从寄存器A到寄存器Bsetup比较紧张而另一条路径从寄存器C到寄存器Dhold有富余。传统CTS会把A、B、C、D的clock latency都做一样然后setup违例靠后面插buffer或者降阈值电压来修。CCOPT则会想我能不能把B的clock latency做短一点让数据更早到达同时把C的clock latency做长一点让hold更容易满足这样一来setup和hold同时受益而且不需要额外的datapath优化。这就是useful skew的本质——有意地在时钟树上制造latency差异让时序路径上的时间余量重新分配。但这里有个关键问题skew不能无限做做过头了会导致时钟树上的物理实现变得极其困难而且OCV的derate会吃掉你辛苦赚来的余量。所以CCOPT需要一个机制来控制useful skew的范围和方向这就是skew group存在的意义。1.3 CCOPT对clock spec的依赖关系CCOPT的行为高度依赖于你给的clock spec。如果你只是简单地create_clock然后跑CCOPT工具会按照默认策略去优化但默认策略未必适合你的设计。clock spec里需要明确告诉工具哪些时钟域是相关的哪些路径允许做useful skewskew的边界在哪里。我见过很多项目clock spec写得非常粗糙就一个create_clock加上create_generated_clock然后抱怨CCOPT结果不理想。这就像你去餐厅吃饭只说了来点吃的然后怪厨师做的不是你想要的。clock spec是你和CCOPT之间唯一的沟通渠道你给的信息越精确工具的行为就越符合预期。2. Clock Spec编写中那些容易翻车的细节2.1 时钟定义的基本结构与被忽视的参数一个完整的clock spec至少应该包含这几个部分主时钟定义、生成时钟定义、时钟组关系、时钟不确定性、以及transition约束。很多人只写前两个后面三个用默认值结果CCOPT跑出来的时钟树要么过于保守要么过于激进。先看主时钟定义。create_clock的时候-period和-waveform这两个参数大家都不会忘但-name参数经常被忽略。如果你不指定name工具会用端口名作为时钟名后面写skew group或者做clock gating检查的时候引用起来就很麻烦。我的习惯是统一用clk_前缀加功能名来命名比如clk_core、clk_ddr、clk_peri这样在log里一眼就能看出是哪个时钟域。-waveform参数也值得多说一句。默认情况下工具假设时钟的上升沿在0时刻占空比50%。但如果你的设计里有时钟分频或者负沿触发的逻辑就必须显式指定waveform。我遇到过一个case设计里有个二分频时钟上升沿在0和period/2处都有但clock spec里没写waveformCCOPT就按单沿去balance了结果分频后的时钟skew完全失控。# 推荐的主时钟定义写法 create_clock -name clk_core -period 2.0 -waveform {0 1.0} [get_ports clk_core_in] create_clock -name clk_ddr -period 1.25 -waveform {0 0.625} [get_ports clk_ddr_in]2.2 生成时钟的source latency处理生成时钟generated clock的定义是另一个重灾区。create_generated_clock的时候-source参数指向源时钟的端口或pin-divide_by和-multiply_by控制分频倍频关系。但这里有个细节source latency的继承问题。默认情况下生成时钟会继承源时钟的source latency。但如果你在生成时钟上又设了set_clock_latency -source那就会覆盖继承来的值。我见过有人在generated clock上重复设source latency导致CCOPT在计算useful skew的时候把latency算重了时钟树做得莫名其妙。正确的做法是只在主时钟上设source latency生成时钟让它自然继承。如果确实需要单独调整生成时钟的source latency那就要非常清楚自己在做什么并且用report_clock_timing去验证实际生效的值。# 生成时钟定义示例 create_generated_clock -name clk_core_div2 \ -source [get_ports clk_core_in] \ -divide_by 2 \ [get_pins div_reg/Q] # 只在主时钟上设source latency set_clock_latency -source 0.5 [get_clocks clk_core]2.3 clock group与skew group的本质区别这是很多人混淆的地方。clock groupset_clock_groups是用来声明时钟之间异步关系的告诉时序分析工具这些时钟之间的路径不需要分析。而skew group是CCOPT用来控制时钟树balance范围的它决定哪些sink点会被放在同一棵子树下做balance。两者的作用域完全不同。clock group影响的是STA分析skew group影响的是CTS的物理实现。你可以有两个时钟在STA里是异步的设了clock group但在物理上它们可能共享同一棵时钟树这时候就需要用skew group来告诉CCOPT怎么处理。我通常会在clock spec里把这两者都写清楚并且加上注释说明每个skew group的划分依据。这样后面review的时候别人能快速理解你的意图自己过几个月再来看也不会忘。# clock group声明异步关系 set_clock_groups -asynchronous \ -group {clk_core} \ -group {clk_ddr} \ -group {clk_peri} # skew group控制CCOPT balance范围 create_skew_group -name sg_core -clocks {clk_core} create_skew_group -name sg_ddr -clocks {clk_ddr}3. Skew Group划分策略与CCOPT行为控制3.1 skew group的物理意义与划分原则skew group在CCOPT里的角色可以理解为告诉工具哪些寄存器应该被放在同一个时钟子树下做balance。同一个skew group内的sink点CCOPT会尽量让它们的clock latency接近不同skew group之间的sink点CCOPT不保证latency关系甚至可以有意识地拉开latency来做useful skew。划分skew group的核心原则是把有时序路径交互的寄存器放在同一个skew group里把没有交互或者交互很少的寄存器分到不同skew group。这样CCOPT在做useful skew的时候不会因为跨group的latency差异导致意外的时序问题。但实际操作中这个原则需要灵活运用。比如一个大的CPU core内部有多个流水线阶段阶段之间有大量的寄存器到寄存器路径。如果你把整个core放在一个skew group里CCOPT的优化空间会很大但时钟树的balance难度也会很高。这时候可以考虑按流水线阶段或者按功能模块拆分成多个skew group给CCOPT更多的灵活性。3.2 skew group的边界控制与useful skew限制CCOPT做useful skew不是没有边界的。默认情况下工具会根据时序需求自动决定skew的大小和方向但你可以通过set_skew_group_options来限制skew的范围。这里有个经验值可以参考对于高性能设计useful skew一般控制在时钟周期的5%到10%之间。超过这个范围OCV的derate会显著吃掉你赚来的时序余量而且时钟树上的buffer插入会变得不均衡物理实现难度急剧上升。# 限制skew group内的useful skew范围 set_skew_group_options -name sg_core \ -useful_skew true \ -max_skew 0.15 \ -min_skew -0.15-max_skew和-min_skew的单位是ns具体值需要根据你的时钟周期和工艺节点来调整。2GHz的时钟周期是0.5ns10%就是0.05ns这个值在先进工艺下已经相当可观了。3.3 跨skew group的latency关系处理不同skew group之间的latency关系CCOPT默认是不管的。但有些场景下你需要显式控制比如两个skew group之间有数据路径你希望它们的clock latency保持一个大致的关系。这时候可以用set_clock_latency或者set_inter_clock_latency来约束。但要注意这些约束会直接影响CCOPT的优化空间设得太紧会让工具没有余地去修时序设得太松又起不到控制作用。我的做法是先让CCOPT自由优化一轮看report_clock_timing里的latency分布然后根据实际结果去调整约束。不要一上来就把latency锁死那样CCOPT跟传统CTS就没区别了。4. CCOPT运行中的关键配置与调试手段4.1 CCOPT的启动流程与关键变量CCOPT的启动不是简单跑一个ccopt_design就完事了。在跑之前有几个关键变量需要确认# 确认CCOPT相关变量设置 set_db cts_ccopt_enable_useful_skew true set_db cts_ccopt_skew_group_balance_mode auto set_db cts_ccopt_clock_tree_layer_preference {M4 M5 M6} set_db cts_ccopt_buffer_list {BUF_X4 BUF_X8 BUF_X16} set_db cts_ccopt_inverter_list {INV_X4 INV_X8 INV_X16}cts_ccopt_enable_useful_skew控制是否开启useful skew优化默认是true。如果你发现CCOPT跑出来的skew很小检查一下这个变量是不是被关掉了。cts_ccopt_skew_group_balance_mode控制skew group内的balance策略auto模式下工具会根据时序需求自动决定。也可以设成strict强制做零skew balance但这样就失去了CCOPT的优势。buffer和inverter的list需要根据你的库和设计需求来选。我一般会选3到4个驱动强度的buffer太弱的驱动会导致时钟树级数过多太强的驱动会导致功耗和EM问题。4.2 用report_ccopt_clock_trees看什么跑完CCOPT之后report_ccopt_clock_trees是最重要的调试报告。这个报告里会列出每个时钟树的sink点数量、buffer数量、latency范围、skew值等关键信息。重点看这几个指标第一latency的min和max差距如果差距很大说明useful skew做得很激进需要检查是否有hold违例第二buffer的数量和类型分布如果某个驱动强度的buffer占比过高可能需要调整buffer list第三skew group之间的latency关系确认是否符合预期。# 生成CCOPT时钟树报告 report_ccopt_clock_trees -file ccopt_clock_trees.rpt report_ccopt_skew_groups -file ccopt_skew_groups.rpt4.3 常见CCOPT异常与排查思路CCOPT跑完之后最常见的异常有三种setup违例没改善、hold违例大量出现、时钟树latency异常大。setup没改善先检查useful skew是不是被限制了。如果max_skew设得太小CCOPT没有足够的空间去调整latency。另外检查一下clock spec里的uncertainty是不是设得太大把余量都吃掉了。hold大量出现说明useful skew做得太激进某些路径的clock latency被拉得太开。这时候需要收紧max_skew或者检查skew group的划分是否合理有没有把不该放在一起的寄存器放进了同一个group。latency异常大通常是buffer list选得不好或者clock tree layer preference设得有问题。检查一下是不是用了太多弱驱动buffer导致级数过多。另外确认一下clock net有没有被错误地设了dont_touch或者fixed属性。5. 从实战case看skew group划分对PPA的影响5.1 一个DDR子系统的skew group重构案例之前做过一个DDR子系统初始的skew group划分是把整个DDR PHY和controller放在一个group里。跑完CCOPT之后setup在PHY侧有少量违例hold在controller侧有大量违例整体latency偏大。分析后发现PHY侧的寄存器工作频率高对setup敏感controller侧的寄存器工作频率低对hold敏感。放在同一个skew group里CCOPT做useful skew的时候顾此失彼为了修PHY的setup把controller的latency拉得太开导致hold崩了。重构方案是把PHY和controller拆成两个skew groupPHY group允许较大的useful skew来修setupcontroller group限制skew范围来保hold。拆完之后setup和hold同时改善整体latency也降下来了。# 重构后的skew group划分 create_skew_group -name sg_ddr_phy -clocks {clk_ddr} set_skew_group_options -name sg_ddr_phy -max_skew 0.08 -min_skew -0.08 create_skew_group -name sg_ddr_ctrl -clocks {clk_ddr} set_skew_group_options -name sg_ddr_ctrl -max_skew 0.03 -min_skew -0.035.2 skew group数量与CCOPT运行时间的关系skew group不是越多越好。每增加一个skew groupCCOPT就需要多维护一组latency约束优化问题的规模会增大。我实测下来skew group数量控制在10到20个之间比较合理超过30个之后运行时间会显著增加而且优化效果不一定更好。如果设计特别大可以考虑分层做CCOPT。先对顶层做一次粗粒度的skew group划分然后对每个子模块单独做CCOPT最后在顶层做整合。这样既能控制运行时间又能保证每个模块的时钟树质量。5.3 工艺节点对skew group策略的影响不同工艺节点下skew group的策略需要调整。在28nm及以上节点线延迟占比不高useful skew的空间可以大一些skew group可以划得粗一些。到了7nm及以下线延迟占比高OCV影响大useful skew要更加保守skew group需要划得更细。我在5nm项目上的经验是skew group的划分要跟着clock tree的物理层次走。如果两个模块在floorplan上距离很远即使它们有时序交互也最好放在不同的skew group里因为跨die的latency匹配成本太高强行balance反而会引入更多buffer。6. CCOPT与后续ECO的衔接要点6.1 CCOPT后的时钟树锁定与ECO策略CCOPT跑完、时钟树质量确认OK之后需要把时钟树锁定防止后续的ECO阶段意外改动时钟树。Innovus里可以用set_db cts_ccopt_freeze_clock_tree true来锁定或者对clock net设dont_touch属性。但锁定之后如果ECO阶段发现时钟树上确实有问题需要修就得先解锁修完再锁。这个流程要跟团队约定好不然容易出现有人改了时钟树但没通知其他人的情况。ECO阶段修时钟树上的问题优先用size_cell而不是insert_buffer。因为insert_buffer会改变时钟树的拓扑结构可能影响已经balance好的latency关系。size_cell只是改变驱动强度对拓扑影响小。6.2 useful skew在ECO阶段的维护ECO阶段如果插入了新的寄存器或者改了datapath逻辑可能会破坏CCOPT建立的useful skew关系。这时候需要重新评估受影响的路径必要时重新跑局部CCOPT。我的做法是ECO阶段每改一轮就跑一次report_ccopt_skew_groups对比改动前后的latency分布。如果某个skew group的latency范围变化超过10%就触发局部CCOPT重跑。6.3 时钟树质量的最终验收标准时钟树质量的验收不能只看skew值。我一般会看这几个指标第一skew group内的skew是否在预期范围内第二时钟树的latency是否合理有没有异常大的分支第三buffer数量和类型分布是否健康第四时钟树上的transition是否满足约束第五EM和IR drop有没有问题。这些指标都OK之后才算时钟树质量达标。只看skew值就签字后面很容易出问题。# 时钟树质量验收检查清单 report_ccopt_clock_trees -file final_clock_trees.rpt report_clock_timing -type skew -file final_skew.rpt report_clock_timing -type latency -file final_latency.rpt check_clock_tree -file clock_tree_check.rpt7. 一些踩坑之后的个人体会CCOPT这套机制刚上手的时候很容易把它当成传统CTS的替代品觉得跑完ccopt_design就完事了。但实际上CCOPT的优化空间和风险都比传统CTS大得多。useful skew是一把双刃剑用好了能显著改善时序用不好会让hold崩得一塌糊涂。我的经验是clock spec要写得足够细skew group要划得足够合理useful skew的范围要控制得足够保守。宁可让CCOPT少做一点useful skew也不要让它放飞自我。后面ECO阶段还有机会去修但如果CCOPT阶段把时钟树做坏了后面修起来成本就高了。另外report_ccopt_clock_trees和report_ccopt_skew_groups这两个报告一定要仔细看不要只看最后的时序summary。时钟树的问题往往在summary里看不出来但在详细报告里一目了然。最后说一个容易被忽略的点CCOPT的运行结果跟floorplan质量高度相关。如果floorplan做得不好寄存器分布不均匀CCOPT再厉害也做不出好的时钟树。所以在跑CCOPT之前先确认floorplan的寄存器密度和分布是否合理这比调CCOPT参数更有效。
返回列表