时钟树综合(CTS)
想象一座花园,里面每一棵植物都必须在同一瞬间被浇到水。如果你只接一根水管通到最近的植物,再让水顺势往远处的角落慢慢流,那么近处的植物先喝到水,远处的就要等很久。于是你换一种做法:搭一套不断分叉、每一段管长都相等的供水歧管,一次次地一分为二,让水几乎在同一时刻到达每一个喷头。时钟树综合(CTS)对时钟信号做的就是这件事。布局把每个单元放在哪里固定下来之后,时钟在源头仍然只是一根网络(net),若放任不管,它会极不均匀地扇出到成千上万乃至上百万个触发器。CTS 把这根朴素的网络拆掉,重新构建成一棵由时钟缓冲器和反相器组成的平衡树,对它们做尺寸选择并摆放到合适位置,使有效时钟边沿几乎同时落到每一个触发器的时钟引脚上。一种常见的结构是 H 树(H-tree),导线真的按一层套一层的 H 形状分叉,让每一片叶子到树根的距离都相等。
有两个数字用来衡量这棵树平衡得好不好。插入延迟(insertion delay,也叫 latency)是边沿从时钟源穿过所有这些缓冲器、一路传到某个触发器所花的时间。偏斜(skew)则是你所关心的那些触发器之间到达时间的离散程度:如果一个触发器在 1.10 ns 看到边沿,另一个在 1.13 ns 才看到,它们之间的偏斜就是 30 ps。你没法把插入延迟做到零,因为缓冲器和导线本身就要花掉实实在在的时间,但你会拼命把偏斜压小,因为时序收敛(timing closure)就指望它。建立时间(setup)检查的是数据必须在下一个时钟边沿之前提前多久到达,保持时间(hold)检查的是数据不能在当前边沿之后太快到达。偏斜会让这些边沿彼此之间相对挪位,所以一棵马虎的时钟树会凭空制造出时序违例,而这种违例靠把逻辑做得再快也救不回来。
CTS 在布局之后、详细布线之前运行,而它在不止一个意义上都是一种权衡。更大的缓冲器、更多的平衡层级能压低偏斜,却会烧掉动态功耗,因为这些缓冲器每一个都在每个周期翻转一次,而时钟网络往往是整颗芯片上功耗最大的部分之一。工具还会为片上变异(OCV)预留余量——现实中,两个完全相同的缓冲器位于芯片的不同角落时,会因为电压跌落和工艺离散而以略微不同的速度翻转,所以一棵在标称条件下看起来完美平衡的树,仍然需要留出裕量才能熬过真实的硅片。现代流程往往放宽了过去那种追求全局近零偏斜的目标,转而采用有用偏斜(useful skew),故意延迟某些触发器,为吃紧的路径借来时间,但其底层任务没有变:驯服一个时钟,让它到达那一整片触发器之海时足够整齐,以满足时序要求。
两个触发器之间的时钟偏斜,就是时钟边沿到达各自引脚的时间之差;CTS 所做的就是尽量把这个差距压小。
务必拿布线后、提取得到的实际时序来校验时钟树,而不要只信 CTS 引擎自己的估算,因为真实的 RC 寄生参数和 OCV 降额(derating)经常会把建树阶段看起来很干净的偏斜和插入延迟数字重新捅出问题。