时钟生成的艺术:create_generated_clock 深度解析

时钟,是数字芯片的「心跳」。

做 STA 这么多年,我见过太多因为时钟约束没写对而导致的时序问题。说实话,create_generated_clock 这个命令,看起来简单,但坑特别多。今天我就把压箱底的经验拿出来,跟你聊聊分频、倍频、相移这些事儿。

一、什么是生成时钟?

简单说,生成时钟就是从主时钟(master clock)派生出来的时钟。比如你有一个 100MHz 的输入时钟,经过 PLL 或者分频器后,得到 50MHz、200MHz 或者相位偏移的时钟,这些就是生成时钟。

为什么要单独定义它们?因为工具需要知道这些时钟之间的时序关系。你不告诉它,它就没法做正确的分析。

核心命令格式:

create_generated_clock -name <clock_name> \
  -source <master_pin> \
  -divide_by <N> \
  -multiply_by <M> \
  -phase <degrees> \
  [get_pins <output_pin>]

二、分频时钟:最常用的场景

分频,就是把时钟频率降下来。比如你有个 100MHz 的主时钟,想要个 50MHz 的时钟给低速模块用。

我在项目中遇到过这样的情况:设计师直接用了一个二分频器,但忘了在 SDC 里定义生成时钟。结果 STA 工具把分频后的时钟当成了独立时钟,跟主时钟之间没有任何约束,时序分析完全乱套了。

正确的写法:

# 二分频
create_generated_clock -name clk_div2 \
  -source [get_ports clk_in] \
  -divide_by 2 \
  [get_pins divider/Q]

# 四分频
create_generated_clock -name clk_div4 \
  -source [get_ports clk_in] \
  -divide_by 4 \
  [get_pins div4_reg/Q]

我的习惯:分频时钟的 source 一定要指定到主时钟的源点,而不是分频器的输入 pin。这样工具才能正确计算时钟沿的对齐关系。

三、倍频时钟:PLL 的常见用法

倍频通常通过 PLL 实现。比如输入 50MHz,PLL 输出 200MHz,那就是 4 倍频。

说实话,倍频时钟的约束比分频要复杂一些。因为 PLL 内部有锁定时间、抖动等参数需要考虑。

# 4倍频
create_generated_clock -name clk_4x \
  -source [get_ports clk_in] \
  -multiply_by 4 \
  [get_pins pll/clk_out]

# 同时分频和倍频(比如 3/2 倍频)
create_generated_clock -name clk_1_5x \
  -source [get_ports clk_in] \
  -multiply_by 3 \
  -divide_by 2 \
  [get_pins pll/clk_out]

注意:倍频时钟的 source 要选 PLL 的输入 pin,而不是输出 pin。我曾经见过有人把 source 设成了 PLL 输出,结果工具报了一堆莫名其妙的时序违例。

四、时钟相移:调整时序窗口

相移,说白了就是把时钟沿往前或往后挪一挪。这在 DDR 接口、源同步接口中特别常见。

为什么要做相移?你想想看,数据在时钟沿附近变化最快,采样点放在数据稳定的区域才安全。相移就是帮你找到那个「安全区」。

# 90度相移
create_generated_clock -name clk_90 \
  -source [get_ports clk_in] \
  -multiply_by 1 \
  -phase 90 \
  [get_pins pll/clk_90]

# 180度相移(常用于 DDR)
create_generated_clock -name clk_180 \
  -source [get_ports clk_in] \
  -multiply_by 1 \
  -phase 180 \
  [get_pins pll/clk_180]

避坑指南:我曾经在某个项目中,把相移设成了 270 度,但工具分析时发现 setup 和 hold 都过不了。后来才发现,相移 270 度等价于 -90 度,但工具对负相移的处理方式不一样。所以,我建议尽量用 0~360 度范围内的正相移。

五、时钟关系:同步还是异步?

生成时钟和主时钟之间,默认是同步关系。但实际情况往往更复杂。

比如,两个生成时钟来自不同的 PLL,或者经过了不同的分频路径,它们的相位关系可能是不确定的。这时候就需要手动指定时钟关系。

时钟关系 命令 适用场景
同步 默认(无需额外命令) 同源时钟,相位确定
异步 set_clock_groups -asynchronous 不同源时钟,或相位不确定
逻辑互斥 set_clock_groups -logically_exclusive MUX 选择的时钟
物理互斥 set_clock_groups -physically_exclusive 测试模式与功能模式
# 异步时钟组
set_clock_groups -asynchronous \
  -group [get_clocks clk_a] \
  -group [get_clocks clk_b]

# 逻辑互斥(MUX 输出)
set_clock_groups -logically_exclusive \
  -group [get_clocks clk_pll1] \
  -group [get_clocks clk_pll2]

我的经验:对于不确定的时钟关系,宁可设为异步,也不要默认同步。因为同步分析会假设最严格的时序关系,导致过度约束。异步分析反而更宽松,只要做好同步器设计就行。

六、知识体系总览

下面这张图,是我自己总结的生成时钟知识框架。每次做新项目,我都会对照着检查一遍,确保没有遗漏。

生成时钟知识体系 主时钟 (Master Clock) 生成时钟 (Generated Clock) 分频时钟 -divide_by N 倍频时钟 -multiply_by M 相移时钟 -phase degrees 时钟关系:同步 / 异步 / 互斥

七、实战中的几个关键点

嗯,最后再唠叨几句。这些是我踩过的坑,你遇到了可以少走弯路。

  • source 的选择:一定要选主时钟的源点,而不是中间节点。否则工具会报「source not found」或者时序分析错误。
  • 时钟沿对齐:分频时钟的上升沿默认与主时钟对齐。如果你需要下降沿对齐,记得加 -invert 选项。
  • 组合逻辑生成时钟:尽量避免用组合逻辑产生时钟。如果实在要用,记得加 -combinational 选项,并且要特别小心毛刺问题。
  • 多个生成时钟:同一个输出 pin 上可以有多个生成时钟(比如测试模式和功能模式),用 -add 选项来叠加。

重要提醒:生成时钟的 -name 不要跟主时钟重名。工具不会报错,但分析结果会乱套。我习惯在生成时钟名字上加后缀,比如 clk_in_div2、clk_in_4x,一目了然。

好了,关于生成时钟的核心内容就这些。记住一句话:时钟约束写得好,时序分析没烦恼。下次遇到复杂的时钟树,别慌,按照分频、倍频、相移、时钟关系这个思路一步步来,准没错。

可可精品资料库