4. 时钟域与时钟约束:时钟定义、时钟不确定性、时钟延迟、生成时钟

时钟,是数字芯片的“心跳”。

做STA这么多年,我最大的体会就是:时钟约束定好了,时序分析就成功了一半。很多新手一上来就急着跑PT,结果报出几千条violation,一看,原来是时钟没定义对。嗯,这种事我也干过。

4.1 时钟定义——你得告诉工具“心跳”长什么样

说白了,时钟定义就是告诉STA工具:
你的时钟从哪个端口进来?频率多少?占空比如何?

最基本的命令是 create_clock。我习惯这样写:

create_clock -name clk_sys -period 10 [get_ports clk_in]

这条命令的意思是:
在端口 clk_in 上定义一个10ns周期的时钟,名字叫 clk_sys。

但实际项目中,时钟往往没那么简单。比如:

  • 时钟可能有多个沿(比如DDR)
  • 时钟可能有抖动(jitter)
  • 时钟可能有多个源(比如MUX选择)

我遇到过最坑的一次,是某个IP的时钟定义漏了 -waveform 参数。默认是50%占空比,但那个IP实际需要40%占空比才能正常工作。结果仿真全过,流片回来却偶尔出错。查了整整两周,才发现是时钟沿没对齐。

核心要点: 时钟定义必须精确到每个沿的位置。别偷懒。

4.2 时钟不确定性——给时序留点“余量”

时钟不确定性(clock uncertainty),说白了就是“时钟可能不准”。

为什么不准?原因很多:

  • PLL的抖动(jitter)
  • 时钟树的偏差(skew)
  • 电源噪声的影响
  • 温度变化

在STA中,我们用 set_clock_uncertainty 来建模这些因素:

set_clock_uncertainty -setup 0.2 [get_clocks clk_sys]
set_clock_uncertainty -hold 0.05 [get_clocks clk_sys]

你可能会问:为什么setup和hold的uncertainty不一样?

嗯,这个问题问得好。setup检查的是最晚路径,受时钟抖动影响大;hold检查的是最早路径,受时钟偏差影响大。所以setup的uncertainty通常比hold大。

我的习惯: setup uncertainty取0.1~0.3ns,hold uncertainty取0.03~0.1ns。具体数值要看工艺和PLL的datasheet。

4.3 时钟延迟——时钟从哪儿来,到哪儿去

时钟延迟(clock latency),就是时钟信号从源头到寄存器时钟端的传播时间。

它分两部分:

  • source latency:从时钟源到芯片管脚
  • network latency:从芯片管脚到寄存器时钟端

在综合阶段,我们通常用 set_clock_latency 来预估:

set_clock_latency -source 0.5 [get_clocks clk_sys]
set_clock_latency 0.8 [get_clocks clk_sys]

等做完时钟树综合(CTS)后,工具会自动反标真实的latency。这时候你之前设的latency就会被覆盖掉。

我记得有个项目,前端工程师设的latency特别大,结果CTS做完发现根本达不到。后来一查,是他把source latency和network latency搞反了。你想想看,一个设反了,整个时序分析就全偏了。

注意: 时钟延迟的单位是ns,不是ps。别写错了,否则差1000倍。

4.4 生成时钟——分频、倍频、门控时钟

实际芯片里,很少有只用一颗时钟的。PLL出来一个主时钟,然后分频给不同模块用。这些分频后的时钟,就是生成时钟(generated clock)。

用 create_generated_clock 来定义:

create_generated_clock -name clk_div2 -source [get_pins pll/clk_out] \
  -divide_by 2 [get_pins divider/clk_out]

这条命令的意思是:
在 divider/clk_out 这个pin上,定义一个对 pll/clk_out 二分频的时钟。

生成时钟的常见场景:

  • 分频:除以2、除以4等
  • 倍频:乘以2(比如DDR的时钟)
  • 门控时钟:用使能信号控制的时钟
  • 多路选择时钟:MUX输出的时钟

我曾经在一个项目中,看到有人把分频时钟直接定义成 create_clock。结果工具认为这是两个独立时钟,跨时钟域分析全乱了。后来改成 create_generated_clock,问题才解决。

记住: 生成时钟必须指明它的源时钟(master clock)。否则工具不知道它们之间的相位关系。

4.5 本章知识体系

下面这张图,是我自己总结的时钟约束知识结构。你看一眼,心里就有谱了:

时钟约束知识体系 时钟定义 create_clock / 周期 / 占空比 时钟不确定性 set_clock_uncertainty / jitter / skew 时钟延迟 set_clock_latency / source / network 生成时钟 create_generated_clock / 分频 / 倍频 实战要点 • 时钟定义要精确到每个沿 • uncertainty要区分setup和hold • 生成时钟必须指明源时钟

4.6 避坑指南

做时钟约束这么多年,我踩过的坑不少。挑几个典型的说说:

  1. 时钟名重复——同一个时钟名定义两次,工具会报错。我建议用统一的命名规范,比如 clk_模块名_频率。
  2. 生成时钟的源没找对——源必须是时钟树上的一个点,不能随便找个组合逻辑输出。我曾经因为这个,查了三天。
  3. 忘了设虚拟时钟——做IO约束时,虚拟时钟(virtual clock)是必须的。否则输入输出路径的时序分析会乱套。
  4. uncertainty设太大——设太大导致过度约束,面积和功耗白白增加。设太小又可能漏掉真正的时序问题。这个平衡,要靠经验。
小技巧: 做完时钟约束后,用 report_clock 检查一遍。看看时钟名、周期、波形、源对不对。这一步花不了几分钟,但能省下后面大量debug时间。

好了,时钟约束这部分就讲到这里。记住一句话:时钟约束是STA的基石,基石不稳,后面全白搭。

可可精品资料库