1. 先说点实际的:时钟信号在数字后端里到底有多重要
做数字后端,不管你是刚入门还是在项目里摸爬滚打了好几年,迟早都要面对时钟树综合(Clock Tree Synthesis,CTS)这一步。我记得自己刚接触数字后端的时候,看着Innovus界面里密密麻麻的时钟网络,完全不知道从哪里下手。后来经历了几次项目流片,踩过不少坑,才慢慢把这一块理顺。
其实时钟信号在芯片里的地位,就好比人体的心跳。所有时序逻辑——触发器、锁存器、寄存器堆——它们的动作都靠时钟沿来驱动。你可以在布局阶段把标准单元摆得整整齐齐,可以把布线绕得很漂亮,但如果时钟信号到达每个触发器的时间参差不齐,芯片一跑起来就会出现时序违例,轻则性能不达标,重则直接功能错误。
这也是为什么时钟树综合在整个数字后端流程里如此核心的原因。
那时钟树综合到底在解决什么问题?说白了,就是要在芯片里搭建一棵“树”,让时钟信号从时钟源出发,经过一级一级的缓冲器(buffer)和反相器(inverter),最终到达所有触发器的时钟端,并且保证大家收到时钟沿的时间尽量一致。这件事情听起来简单,但真正做起来,牵扯到的东西非常多。
这篇笔记是数字后端学习笔记的第四篇,我打算把时钟树综合中的一个核心主题——时钟信号,掰开揉碎了讲清楚。我会结合自己在Innovus里的实操经历,聊聊时钟信号的基本特性、时钟树综合的设计思路、关键参数、实际步骤,以及那些文档里不会明说但项目里一定会遇到的坑。
不管你是正在学数字IC后端设计基本概念的新手,还是已经接触过一些后端流程、想系统补一补时钟树知识的工程师,这篇内容应该都能给你一些参考。我会尽量用直白的话来讲,也会配上实际项目里用到的参数和配置方式,让你不光看懂逻辑,还能拿来用。
2. 时钟树综合的设计思路:为什么时钟信号要特殊对待
2.1 时钟信号不是普通信号,不能用普通逻辑的思维来看它
先说一个很多初学者容易困惑的问题:时钟信号在网表里,不就是一根线、一个端口吗?为什么CTS要单独拿出来做,不能在布局布线的时候顺手处理掉?
这里面的关键区别,在于时钟信号对时序的要求和普通数据信号完全不是一个量级。
普通的数据信号,只要在建立时间(setup time)和保持时间(hold time)窗口内稳定下来就行,信号早一点晚一点,只要不越过约束边界,功能上都是对的。但时钟信号不一样,它要驱动的是芯片里成千上万个触发器,相当于一个信号要同时给所有时序单元“发号施令”。如果这个“号令”到达各个触发器的时间不一致,有的触发器觉得是上升沿来了,有的还在等下一拍,那数据采样就会错位。
这种时钟到达时间的不一致,在数字后端里有一个专门的名词叫时钟偏斜(clock skew)。
我在做第一个后端项目的时候,对skew完全没有概念,觉得只要时钟能到就行。结果跑完STA(静态时序分析)一看,setup和hold违例一大堆,而且很多违例不是数据路径的问题,而是时钟路径本身歪七扭八导致的。后来才意识到,时钟树综合的第一步,就是要把时钟网络的拓扑结构设计好,让skew尽可能小。
2.2 三个绕不开的概念:latency、skew和jitter
聊时钟信号,有三个概念无论如何都要搞清楚:时钟延迟(latency)、时钟偏斜(clock skew)和时钟抖动(clock jitter)。这三个东西决定了时钟树综合的目标和约束怎么设置。
先说时钟延迟。它指的是时钟信号从时钟源(source)出发,经过时钟树的所有缓冲级,最终到达某个触发器时钟端口所花费的总时间。这个延迟在CTS里面通常分为两部分:一部分是时钟从芯片外部引脚或者PLL输出到时钟树根节点的延迟(source latency),另一部分是时钟从根节点经过buffer网络到达每个触发器时钟端的延迟(network latency or insertion delay)。
再说clock skew。它指的就是不同触发器之间,时钟到达时间的差值。假如触发器A在10ns收到时钟沿,触发器B在10.2ns收到,那它们之间的skew就是0.2ns。skew这个东西在数字后端里比较微妙——它太大,会让时序收敛变得非常困难;但它也不是绝对的越小越好,因为适当的有用偏斜(useful skew)反而可以帮助修复setup time违例。
最后是clock jitter。jitter是时钟周期本身在时间上的波动,它不是由后端CTS决定的,而是由时钟源(比如PLL)的稳定性和供电噪声决定的,是一个与工艺、环境相关的量。在数字后端里面,我们一般不能直接改善jitter,只能在约束文件里把它作为时钟不确定性(clock uncertainty)的一部分预留出来。
这三个概念放在一起看,就比较清晰了:latency决定时钟到达的绝对时间,skew决定不同触发器之间时钟到达的相对差距,jitter是时钟源自身的抖动。CTS的任务,主要是在给定的时钟源条件下,尽可能控制latency和skew在可接受范围内。
2.3 CTS的最终目的不是“零偏斜”,而是“满足时序”
很多初学者容易有一个误区,觉得CTS就是要把skew做越小越好,最好做到零偏斜。
实际上,在真实项目里,目标从来不是零skew,而是让时序收敛。时钟树综合的所有努力,最终都是为了让setup和hold都能满足约束要求。
打个比方:你在做项目排期,并不需要所有任务都在同一秒完成,只要关键的上下游任务能衔接上,整体项目能按时交付,中间有点时间差是完全可以接受的。时钟信号也是一样,只要每个触发器的数据采集窗口都满足setup和hold要求,有点skew其实没太大关系。
有时候我们甚至会故意引入useful skew——比如在setup容易违例的路径上,让接收端的时钟来得晚一点,等于给数据路径多争取了半个拍的时间。这种操作在CTS阶段就已经在做优化了,不是等到修timing的时候才开始。
所以理解CTS的正确思路,应该是:设置合理的时钟树约束,让工具在满足时序目标的前提下,自动选择buffer级数、buffer位置和buffer类型,构建一棵整体最优的时钟树。这才是我们在Innovus里做CTS时真正在做的事情。
3. 实操:在Innovus里跑时钟树综合的关键步骤
3.1 CTS之前,先把这些准备做好
我见过不少同学一上来就在Innovus里直接跑ccopt_design,结果跑出来一堆问题,根本不知道怎么排查。实际上CTS这个步骤非常依赖前面的准备工作。
在跑CTS之前,布局(placement)已经完成了,也就是说所有标准单元的位置都已经确定下来了。这时候时钟树要做的,就是在现有布局的基础上,选择合适的buffer位置,插入到时钟网络里,把时钟信号传输到每个寄存器端。
但这里有个很容易被忽略的问题:如果布局做得很差,触发器散落在版图的各个角落,那CTS再怎么做,也很难把时钟网络做短。因为时钟网络的走线长度是由寄存器之间的物理距离决定的。所以有一个经验之谈:在布局阶段就要有“时钟意识”,把时序关系密切的寄存器就近摆放,后面CTS会省很多事。
具体来说,跑CTS之前至少要确认这几件事:
- 时钟约束是否完整有效,包括create_clock、clock uncertainty、clock latency等是否都定义好了
- 有没有don't touch的网络,比如一些特殊的模拟信号、复位信号,不能乱插buffer
- 电源网络是否已经处理好,因为CTS插入的buffer需要驱动,没有电源就没有办法工作
- 有没有设置好时序约束文件(SDC),尤其是set_clock_tree_options相关的参数
这一步花的功夫,往往决定了后面CTS能不能顺利收敛。我以前做项目就试过,没有提前检查SDC就着急跑CTS,结果工具跑了几十分钟,最后生成的时钟树里skew大得离谱,后来排查发现是create_clock的定义本身就有问题,时钟频率定义错了,整个时刻基线都是歪的。
3.2 时钟树约束怎么设置才能不返工
在Innovus或者更早一些的Encounter里,时钟树综合是通过CTS spec file(时钟树约束文件)来配置的,里面会定义时钟网络的层次、插入延迟目标、skew目标、最大transition时间、使用的buffer类型、是否使用shielding等一堆参数。
我每次在新项目里设置CTS约束,都会重点关注这几个参数。
第一个是max transition。时钟信号不能太“钝”,否则会影响到触发器的触发精度。通常我会把时钟网络的最大transition时间设为100ps到300ps之间,具体看工艺节点和库的cell delay table。设置太严格,工具会插入大量buffer来满足transition,导致时钟树面积和功耗增加;设置太松,又容易在信号完整性上出问题。
第二个是max skew。这个值一般会按照周期的一定比例来设定,比如周期的5%到10%,然后再结合库特性和时钟结构做微调。举个例子,如果时钟频率是1GHz(周期1ns),那max skew我会先设为50ps,如果后面时序还有余量,再适当放宽一点。
第三个是target insertion delay或者说target latency。在比较老的流程里,这个值需要工程师手动设置,一般是Cover到所有寄存器的总延迟,通常在几百ps到几ns不等。现在Innovus新一代的时钟树综合引擎(CCopt)已经能够自动平衡insertion delay,但我还是习惯在约束里给一个参考范围,让工具不至于在某个方向跑偏。
下面给一个简单的CTS spec file片段参考(基于Innovus的create_clock_tree_spec流程):
set_ccopt_property target_insertion_delay -delay 0.5n set_ccopt_property target_skew 0.03n set_ccopt_property max_transition 0.15n set_ccopt_property buffer_cells {BUFH_16 BUFH_32 BUFF_16 BUFF_32} set_ccopt_property routing_clock_pin ? set_ccopt_property use_inverter_for_resynth all create_ccopt_clock_tree_spec这里用的buffer cell要结合工艺库去选,我会优先挑那种驱动能力中等偏上、延迟对负载不太敏感的buffer,跑出来的时钟树会更稳定。
3.3 从spec到完成:CTS实际上是怎么跑的
有了CTS spec之后,就可以在Innovus里跑时钟树综合了。现在主流工具的命令大概是这样的流程:
# 读入设计 restoreDesign /path/to/design.enc.dat design_name # 综合时钟树 set_ccopt_property update_io_latency create_ccopt_clock_tree_spec ccopt_design # 查看时钟树报告 report_ccopt_clock_trees report_ccopt_skew report_ccopt_insertion_delay这里面有个细节值得注意:ccopt_design这个命令跑完之后,工具会自动完成时钟树的综合、优化和合法化(legalization),也就是说,它会自动把插入的buffer放到不违反布局约束的位置上,并且保持时钟树结构不被后续摆放工具扰乱。
跑完之后,我一般会在GUI里打开时钟树视图,检查一下时钟树的层级结构,看它是不是一棵合理的“树”而不是一张乱七八糟的“网”。具体会看几个点:
- 时钟树的分叉点是否合理,有没有出现某个buffer扇出特别大的情况
- 关键的寄存器是不是都挂在合理的层级上,有没有寄存器离根节点特别远
- 时钟树的级数(levels)是不是太多,如果超过10级就要考虑是不是tool在过度插buffer了
每隔一段时间,我还是建议手动检查一下生成的时钟树物理布局。工具再好用,它也不会帮你想清楚所有项目特有的情况。你说这棵树走了非常绕的路径、在经过好几层RAM上方绕来绕去,即使skew数字是好的,实际在芯片上的信号完整性和可靠性迟早会有问题。
3.4 时钟树综合之后的精细优化
CTS跑完,并不代表就结束了。接下来还有两个重要的环节:时钟树优化(clock tree optimization)和post-CTS的时序修复。
在Innovus里,ccopt_design做完之后,工具会跑一遍时钟网络的时序分析,然后试着通过调整buffer size、拆分大扇出节点、局部替换cell来进一步优化skew和latency。这个阶段目标很明确:在维持时钟树结构稳定性的前提下,让setup和hold time的margin都变好。
实际项目里有一个比较常见的现象:CTS刚跑完,hold time违例往往比较多。原因也简单,CTS插入的buffer增加了data path的延迟,同时clock latency又把时钟推得很晚,二者叠加就容易触发hold违例。所以后续一般会有专门的hold mode来修,通过插入delay cell或者拉长时钟路径来解决。
不过需要注意的是,修hold的cell能不能插,插多少个,在时钟树综合阶段就要有预判。因为时钟树和data path在物理上是相互影响的,如果在CTS阶段做得太满,后面修hold的时候就没有空间插delay cell了。所以我会在跑CTS的时候,特意给后续修hold留一点余量,比如target_skew不要压得太死,max transition不要卡得太紧。
这一步算是我自己踩了两次坑之后总结出来的经验。其中第一次是没留余量,结果hold违例修到后期实在找不到地方插cell,只能手动改floorplan,非常痛苦。
4. 时钟信号在CTS里的关键参数与常见问题排查
4.1 时钟树的“体质”怎么看:skew、transition和duty cycle
CTS做完之后,我们要怎么评价这棵时钟树到底好不好?这里有一个简单粗暴的“体检清单”,我每次做完CTS都会照着查一遍。
第一项是整体skew。这相当于时钟树的“血压”,血压太高肯定不行,说明时钟到达各个触发器的节奏乱了。在Innovus里面可以直接用report_ccopt_skew来看,重点关注同一个时钟域、同一层级上的时钟路径是否一致。如果某些路径的skew明显大于其他路径,就要去查是不是这些路径上buffer的负载特别大,或者走线特别长。
第二项是transition time,这相当于时钟信号的“上升速度”。transition太慢,表示时钟信号阶梯化严重,到了触发器那边可能还没有稳定下来,已经决定了逻辑状态。我一般会把transition分成几个档位去看:全局最大transition、每级buffer的输出transition、以及最末级到达触发器时钟端口的transition。后者尤其关键,因为末级transition直接影响时序单元采样。
第三项是duty cycle,也就是时钟高低电平的占空比。CTS一般不会专门去调占空比,但时钟树里如果用了太多反相器(inverter)做驱动,或者buffer类型不匹配,占空比可能会发生偏移。在某些设计里(比如DDR接口、双沿采样逻辑),duty cycle失真会导致功能问题,检查思路是在关键模块的时钟入口设置duty cycle约束,然后看CTS报出来的实际值。
这三个指标,某种程度上就决定了时钟树这个“心脏”健不健康。体检不过关,后面修时序修到哭都修不完。
4.2 skew收敛不了?先别急着改约束
做CTS的时候,最烦的就是skew怎么都收敛不到目标值。有时候你明明已经把max skew设得很宽松了,工具还是报出来一大片违例。
我后来发现,这种问题绝大多数时候不是CTS参数的问题,而是设计本身的问题。
最常见的坑是时钟结构本身就不平衡。比如某个模块里有一组寄存器离时钟源特别远,而其他寄存器都在近处,这就成了一个天然的“长尾”。这种差距不是光靠调max skew参数能解决的,正确思路是调整整体布局,或者把这些寄存器分组处理,用多棵子时钟树来分别收敛。
另一个常见原因是clock gating cell(时钟门控单元)的处理不当。现在的设计里基本都有clock gating,我们在做CTS的时候,如果gating cell的摆放位置不合理,或者gating cell的驱动能力不够,就会导致它下游的时钟网络成为瓶颈。这时候要做的不是猛调CTS参数,而是去检查gating cell本身的物理位置和面积,看是不是需要重点优化。
还有一类情况是power domain的问题。在多电源域设计里,不同电压区域的buffer驱动能力不同,电平转换器(level shifter)插在时钟路径上,也会造成很大的延迟差异。遇到这种设计,skew目标就不能全局统一,要分域去设置。
所以遇到skew一直收敛不了的问题,我的排查顺序是:先看设计结构(是不是有长尾、gating、跨域),再看CTS约束(target skew、buffer type),最后才去调时钟树的topology。顺序反了,很容易在错误的方向上浪费好几个晚上。
4.3 时钟树插入buffer太多,功耗和面积都爆了怎么办
时钟树综合有一个隐形成本,就是大量插入的buffer会显著增加芯片的面积和功耗。尤其是在高扇出的时钟网络上,一级一级的buffer叠加起来,数量是非常可观的。
我参与过的一个项目,时钟域比较多,CTS跑完之后,时钟网络上的buffer占到了整个设计标准单元面积的将近15%,功耗也高得离谱,后期不得不返工优化。
这里提供几个可以减少时钟树buffer数量的思路:
- 合理选用高驱动能力的buffer,让一级buffer带更多的负载,减少级数
- 在满足时序的前提下面,适当放宽max transition的约束,工具就不会为了抢那一点点transition时间去多插buffer
- 对于低功耗模块,可以考虑让时钟树的浅一些,配合门控时钟,达到省功耗的目的
当然这些都要以满足时序为前提下做权衡。然后有一个经验:在CTS阶段就同步关注功耗和面积报告,别等到布线完成之后再看,那时候已经非常被动了。
4.4 hold time修不住?多半是时钟树“太深”了
CTS之后经常出现的一种“疑难杂症”是hold time修不住。明明setup都没问题,偏偏hold违例满天飞,而且修一个冒出来两个,仿佛在打地鼠。
这个问题背后往往藏着一个结构原因:时钟树的深度太深。
如果时钟树级数太多,从时钟源到每个寄存器都要经过一串漫长的buffer链,那意味着数据路径相对于时钟路径来说,available window变得更紧。因为数据信号是在时钟作用之后开始传播的,而时钟又经过了很多级buffer才到达寄存器,这个时间差很容易造成hold violation。
针对这种情况,比较有效的做法是:
- 在CTS阶段就控制最大级数(level限制),比如把max levels设为8到10,超过这个范围的路径要重点分析
- 对hold违例比较严重的寄存器群,可以尝试局部缩短时钟路径,让这些寄存器的时钟来得早一些
- 如果实在不行,才考虑在数据路径上插delay cell,但这只是“对症”,不是“治本”
我在项目里总结出来的体会是,setup违例多半可以通过调整约束和数据路径优化来解决,hold违例如果大面积出现,一定要回头审视时钟树本身的深度和结构,不要一头扎进数据分析的细节里。
5. 从一个实际案例看CTS前后时序是怎么变化的
说了这么多概念和步骤,不如找一个具体的例子来看,CTS到底给时序带来了什么样的变化。我拿一个之前跑过的模块来演示——假设这是一个AHB总线桥接模块,工作频率200MHz(时钟周期5ns),里面大约有3000个触发器,分布在几个子模块中。
CTS之前,时钟信号在网表里是理想网络(ideal network),就是说工具假设时钟到达所有寄存器的时间是一样的,不存在延迟和偏斜。这种情况下跑一下静态时序分析,看到的setup和hold余量,其实是“理想化”的,不能代表真实芯片里的表现。
跑完CTS之后,工具会给时钟网络加上真实的物理延迟。这时候再跑时序分析,我们会看到两组变化:一是setup 余量比CTS前要小,因为真实的时钟延迟和偏斜进来了;二是hold 余量也可能变差,尤其是那些时序路径又短又紧的地方。
我在这个模块里跑完CTS后,用report_ccopt_clock_trees看了一下,时钟树的级数在7级左右,平均insertion delay是1.2ns,最大skew是80ps。这个skew对于200MHz的设计来说算是在可控范围内,但跟CTS之前理想时钟下的0 skew比,已经多了80ps的不确定性,时序余量被吃掉了一部分。
然后我用report_ccopt_timing_summary看了一下整体情况,setup WNS(最差负余量)从CTS之前的+0.35ns变成了+0.21ns,hold WNS从正的变成了-0.08ns,有一些路径开始出现hold违例。这些违例主要集中在短路径上,也就是说两级的寄存器之间逻辑比较简单,数据本来很快就到了,但时钟却被延迟了,导致数据提前到达,不满足hold要求。
接下来在post-CTS的修复阶段,工具自动在违例路径上插了几个delay cell,把hold修了回去。这个过程就是个不停迭代的流程,一直到setup和hold都恢复到正余量,CTS阶段才算真正完成。
6. 时钟树综合做完之后,时钟信号还要盯哪些问题
CTS跑完,很多人以为时钟这块就算过关了。其实后面还有几个和时钟信号相关的问题,会在布线阶段或者Signoff阶段突然蹦出来。
第一个是信号完整性(signal integrity)问题。时钟网络在芯片里是最长、最宽的信号网络之一,它在传输过程中很容易受到旁边数据线的串扰(crosstalk)影响。数据线在翻转的时候,会在时钟线上耦合出额外的信号波动,如果这个波动恰好发生在时钟沿附近,就可能导致时钟沿提前或延后,产生functional的问题。
为了降低这种风险,很多项目里给时钟网络做了shielding,也就是在时钟线旁边加一条接地的保护线,隔离串扰。但这会增加布线资源和面积成本,所以一般只对关键时钟树做shielding。
第二个是EM(电迁移)问题。时钟网络的翻转率在整个芯片里是很高的,长期跑下来,时钟线上的电流密度比普通数据线大得多,如果不加控制,金属线可能因为电迁移而断裂,造成芯片失效。这块通常由后端物理验证环节去检查,但CTS阶段就要注意不要过度集中走线,让电流过于拥挤。
第三个是OCV(片上波动)问题。工艺制造过程中,同一片晶圆上不同位置的晶体管性能会有细微差别,这会导致同样结构的buffer在两个位置延迟不一样。在后端里会用OCV derate值来模拟这种影响。时钟树综合阶段的skew优化做得越好,OCV对时序的影响就越小。
所以可以说,CTS做完只是时钟信号处理的一个中场休息,后端的路还长得很。但正因为CTS阶段把时钟网络的“骨架”搭好了,后面这些信号完整性和可靠性问题才有机会顺利收敛。
7. 对几个常见困惑的回应
7.1 时钟树综合是不是一定得用工具自动跑,能不能手调
这个问题我常被问到。现在工业界的工具已经非常成熟了,全自动跑CTS是大势所趋,尤其在先进工艺节点,时序、功耗、面积的交互非常复杂,靠手动搭时钟树几乎不可能做完。
但在某些特定场景下,手动干预还是有价值的。比如对于特别敏感的时钟路径(像高速SerDes的时钟、ADC的采样时钟),有时候我会手动fixed某些buffer的cell类型和位置,避免工具自动优化时把这条路径“优化”坏了。再比如遇到某些特定结构的duty cycle要求极严的电路,手动搭一小段时钟树反而比让工具自动跑更可控。
总体原则是:自动为主,手动为辅,信心来自对这个模块时钟需求的深入理解。
7.2 时钟树综合和布局布线是怎样的关系
一个比较常见的理解误区,是把CTS当成一个独立的步骤。其实CTS的位置正好卡在布局(placement)和布线(routing)之间,起到了一个承上启下的作用。
布局决定触发器在哪里,CTS决定时钟怎么到这些触发器,布线决定数据和时钟线怎么在物理上连接。三者是强耦合的关系。如果布局没做好,CTS要做很多额外工作去弥补;如果CTS做得不好,后面布线阶段的时序收敛会非常痛苦。
我自己的一个工作习惯是,在布局阶段就打开时钟树预估(clock tree estimation)工具,大体看一下未来时钟网络的走向和负载分布,及时调整布局。这个习惯帮我避免过不少后面CTS跑不过去的尴尬局面。
7.3 在先进工艺节点,CTS有哪些新的挑战
随着工艺节点不断演进,CTS面临的挑战也在变化。在FinFET工艺和更先进的节点上,器件本身的延迟变异性增加,时钟树设计对工艺扰动的敏感性更高,这让OCV分析更加复杂。
同时,先进工艺里的供电电压更低,噪声容限变小,时钟信号更容易受到IR drop和电源噪声的影响。这些都在无形中给CTS提出了更高的要求:不光是延迟平衡,还要考虑噪声和电压变化条件下的鲁棒性。
这些新趋势也推动着EDA工具不断进化。现在Innovus里的CCopt引擎,已经在做“时序-时钟”协同优化,不像传统流程那样把时钟树综合和时序修复完全切开,而是在构建时钟树的过程中同步考虑数据路径的时序,从而优化全局的时序收敛效果。
我在实际项目中确实感受到了这种变化,同样的设计,用新的时钟树综合引擎跑出来的结果,比老流程在WNS和TNS上都有明显改善。这也是为什么我一直跟团队里的新人说,学习数字后端不能只学会点命令,更要理解工具背后的算法逻辑和流程设计思路。
8. 学习时钟树综合的一些心得
8.1 从概念到项目,需要刻意训练
如果你现在刚接触数字后端,正在学数字IC后端设计基本概念,那我建议你把时钟树相关的这章当成重点中的重点来啃。因为它横跨了物理设计、时序分析、信号完整性好几个领域,是把“逻辑设计”变成“物理实现”的桥梁。
光看书是不行的,一定要上工具,亲手跑一次CTS,然后看报告、查GUI、尝试改参数,看不同设置会带来什么样的变化。我自己就是从一次一次改动参数中,逐渐建立起对时钟树“直觉”的。
8.2 多看看工具生成的报告和日志
很多人在跑完CTS之后,就直接忽略终端窗口里的那一大堆日志,去看GUI上的图形。我建议反过来:GUI只是一种可视化辅助,真正信息量最大的其实是日志报告。
Innovus在CTS过程中会打印很多详细的信息,比如每一棵时钟树的布局、每一级buffer的插入位置、skew的计算结果、时序违例的变化趋势等。花时间读这些日志,能让你非常清晰地理解当前设计里到底发生了什么,也方便在出现问题的时候快速定位根因。
8.3 带着问题做实验,进步最快
我做助理工程师的时候,带我的师兄跟我说过一句话,我一直记住:不要只按照流程走,要带着问题走流程。比如同样是跑CTS,你可以问自己:如果把max_transition从150ps改到100ps,时钟树会多插多少buffer?如果把skew target设得更紧,时序余量会不会反而变差?这些问题一旦开始问,你就会真正理解工具在做什么,而不是在当一个“按钮操作员”。
这篇笔记写到这里,其实也就是把我这些年从概念到项目,再从项目回到概念的经验做了一次整理。时钟树综合和时钟信号这件事,内容很深,一篇笔记讲不完,但核心的思路和实操要点我相信已经表达清楚了。如果你正在做数字后端项目,又恰好卡在CTS这关,希望这篇笔记能帮你少走一些弯路。