一个时钟,数百万个触发器
想象一个体育场里观众在做人浪。如果所有人都在完全相同的一瞬间站起来,你根本看不到什么人浪——你看到的会是一个干净利落、整齐划一的动作。这种「瞬间处处同步」的爆发,正是芯片里时钟应该做到的样子:单单一拍,在同一时刻到达每一个触发器,让它们一起发射、一起捕获数据。一个现代的模块里可以装下数百万个触发器,而在每一个时钟沿上,它们当中的每一个都需要这一拍。
比人浪更贴切的画面是草坪洒水器。你希望水同时浇到每一株植物上——花圃角落里的那株,不该比水龙头旁边那些晚整整一秒才被淋到。要是真晚了,整个花园就会长得参差不齐。时钟干的是同一件活儿:它必须把自己的边沿喷洒到茫茫一片触发器上,喷得足够均匀,以至于对逻辑而言,它们全都是同时听到这一拍的。
为什么时钟不能只是一根线
最显而易见的办法恰恰是错的:从时钟源拉出一根粗线,然后把每个触发器都从这根线上挂出去。它会失败,原因有两个,而且两个都是纯粹的物理。第一是扇出。一个时钟引脚要去驱动数百万个触发器输入,就像一个人想同时推一千个秋千——这个源头根本提供不出足够的电流,去把那么多晶体管栅极干脆利落地翻转过来。那条本该像悬崖一样陡峭的边沿,会变成一段懒洋洋、软塌塌的斜坡,而缓慢的边沿会让触发器变得不可靠。
第二个原因更阴险,那就是RC 延迟。每根导线都有电阻(R)和电容(C),让信号沿着一根导线传过去,与其说像啪地一下打开电灯开关,不如说更像让水冲过一根又长又细的软管——远端的反应明显比近端晚。所以,就算一根线*真能*驱动一切,紧挨着时钟源的那个触发器,也会比模块对角另一头的那个触发器早得多地听到这一拍。这单单一根线送出的并不是一拍同步的节奏;它把这一拍在整个芯片上抹开、糊掉了。这种「糊开」是有名字的,而它正是整篇指南要对付的敌人。
# A wire's delay grows roughly with the SQUARE of its length: # t_delay ~ R_per_um * C_per_um * length^2 # # Double a clock wire's length and you ~quadruple its delay. # That is why a single source wire smears the clock edge across # the die instead of delivering it everywhere at once.
偏斜与插入延迟
有两个数描述了一个真实的时钟是怎么到达的,这两个你都得知道。第一个是插入延迟(也叫时钟延迟,clock latency):这一拍从时钟源一路走到某个触发器的时钟引脚,要花多长时间。如果源在时刻零产生一个边沿,而某个特定触发器在 320 皮秒之后才看到它,那么这个触发器的插入延迟就是 320 ps。有一部分延迟是躲不掉的——信号总得在物理上走到那儿——而它本身并不有害,因为发射触发器和捕获触发器搭的是同一班「被延迟了的时钟」。
那个*确实*有害的数,是偏斜(skew):所有端点之间到达时间的离散程度。正式地写出来是:
# Skew = how UNEVEN the clock arrival is across all endpoints skew = t_arrival_max - t_arrival_min # Insertion delay (latency) = source -> one flop's clock pin # e.g. flop A hears the edge at 300 ps (latency_A = 300 ps) # flop B hears the edge at 345 ps (latency_B = 345 ps) # skew between A and B = 345 - 300 = 45 ps
接下来是从业者真正在意的那一点,它跟时序预算直接挂钩。回想一下前端轨道里讲过的建立时间约束:时钟周期必须容得下 `t_clk-to-q + t_logic + t_setup`,再用时钟偏斜去调整。当发射触发器和捕获触发器在略有差异的时刻听到边沿时,这点不一致会直接落进裕量的等式里。让捕获时钟到得*更晚*的那种偏斜,会给一条建立时间路径塞进一点额外的时间——但它是从下一级那里偷来的,于是反过来咬你的保持时间检查。失控的偏斜就是不确定性,而不确定性就是从每一份预算里被扣掉的皮秒。这正是为什么 CTS 几乎把追求低而均衡的偏斜摆在所有事情之上。
缓冲器与 H 树
如果一根线送不出这一拍,那你就搭一张分发网络——而其中的窍门,是让从源到每一个触发器的每一条路径都花掉一样多的时间。教科书上的理想是 H 树。想象字母 H。时钟从那道横梁的正中央进来;横梁分叉到两个端点;在每个端点上你再画一个更小的、转了向的 H;每一个这样的 H 又再分叉成更小的 H。因为这套几何是对称的,从中心到这个分形里每一片叶子的线长都完全相同——于是到每一片叶子的延迟都完全相同,偏斜便朝着零塌缩过去。这就是那台被设计成让角落里的植物和水龙头旁的植物在同一瞬间都得到水的洒水器。
对称解决的是距离,但它解决不了驱动能力——这就轮到时钟缓冲器登场了。在每一个分叉点上,工具都会插入一个缓冲器:一个小小的放大器,把那条正在变弱的边沿接过来,重新「盖章」成一条崭新、锐利的边沿,再去驱动下一组分支。把它想成一场接力赛,而不是一个跑得精疲力竭的选手从头跑到尾——每个缓冲器都是一名接过接力棒的、体力充沛的新选手。这些缓冲器还会重新平衡延迟:如果某条分支天生快了一点点,工具可以把它的线拉长,或者加一个缓冲器去故意把它放慢,把每一片叶子都拽回到彼此一致。一棵做完的时钟树,就是从树根到叶子层层散开的好几级这样的缓冲器。
# A clock-tree spec the CTS engine works to honor (vendor-neutral): create_clock -name CLK -period 1.0 [get_ports clk] ;# 1.0 ns = 1 GHz beat set_clock_uncertainty 0.05 [get_clocks CLK] ;# 50 ps budget reserved for skew+jitter set_clock_transition 0.08 [get_clocks CLK] ;# keep clock edges sharp (<= 80 ps) # CTS targets, conceptually: # max_skew <= 30 ps # max_latency <= 350 ps (insertion delay budget) # max_tree_depth: a handful of buffer levels root -> leaf
为什么 CTS 在布局之后才跑
这里有个值得停下来想一想的问题:为什么时钟树综合是第三步,排在布局之后、布线之前?为什么不先把时钟建好呢?因为你没法去平衡那些你根本量不出来的延迟,而在你知道触发器到底在哪儿之前,你又量不出延迟。偏斜是几何的属性——是距离和线长——所以工具得先有版图上真实的单元位置,才能决定缓冲器放在哪里、每条分支的线该画多长。想在布局之前就把时钟树搭起来,就好比花园还没种下去,你就想规划好洒水管要怎么铺。
- 先做布图规划:把裸片、宏单元、I/O 和电源网格定下来——这是后面整条流程赖以存在的、固定的地貌。
- 把每一个标准单元,连同所有触发器,都摆放到合法的位置上。现在工具就确切地知道每一个时钟端点坐落在哪儿了。
- 跑 CTS:把邻近的触发器聚成簇,长出那棵平衡的缓冲器树,并插入时钟缓冲器去命中偏斜和延迟目标——用的正是布局刚刚敲定的真实距离。
- 然后绕着这张此刻已固定下来的时钟网络去布线信号线。完事之后要重新检查时序,因为真正布出来的线带着真实的 RC,那是纸面估算之前只能猜个大概的东西。
这个先后次序之所以要紧,还有一个更深的原因:建起时钟树会改变整个时序的地貌。在 CTS 之前,时序工具假定的是一个*理想*时钟——零偏斜,处处同时到达——好让它们能专心把逻辑摆好。而真实的树一旦存在,每个触发器真正的时钟到达时刻就被锁定了,工具于是切换到传播(propagated)时钟模式:它在计算裕量时,现在用的是真实的插入延迟和偏斜。先布局,好让那棵树是诚实的;建起那棵树,好让时序变得诚实;然后再布线,并对着这份诚实的时序去做签核。
瞥一眼有用偏斜
整篇指南我们都把偏斜当成敌人——而百分之九十五的时候它确实是。但这里有个把剧本翻过来的高阶技巧:有用偏斜(useful skew)(也叫有意偏斜,或时钟偏斜调度)。它的想法是,与其逼着每个触发器都在完全相同的一瞬间听到这一拍,不如*故意*把时钟稍微晚一点点送到某些捕获触发器去——好给它们待接收的数据多腾出几皮秒的到达时间。
把它想成挪动一个截止期限,而不是把活儿干得更快。假设有一条路径,它的建立时间检查差了 20 ps——它就是没法及时把数据送到捕获触发器。工具不必去重新设计逻辑,而可以把*那个触发器的*时钟边沿延后,比方说 25 ps。现在数据有截到那个更晚的边沿为止的时间可以慢慢到,于是这条路径就通过了。这里有个坑——而且永远都有个坑——就是同一个触发器又是下一级的*发射*触发器,所以延后它的时钟,是靠从下一条路径那里偷时间,来给这条路径借时间(同时它会收紧下一级的保持时间裕量)。有用偏斜就是跨着时钟去借时间,而工具会把整条链平衡好,让这些借来借去最终能轧平。
把镜头拉远,CTS 就是一句话:把这一拍在每个角落同时送到,然后只在划算的地方才有意去掰弯这条规矩。 把树平衡好,你那数百万个触发器就会步调一致地齐步前进;做砸了,你就悄无声息地给芯片上每一份时序预算都课了税。如今时钟已经分发到位、单元也已经摆好,这个设计终于可以迎接导线了——那就是接下来的布线。