3. 生成时钟约束

生成时钟,说白了就是从你已有的主时钟里“变”出来的时钟。比如PLL出来的分频时钟、倍频时钟,或者通过寄存器分出来的时钟。这些时钟不能直接用create_clock去定义,得用create_generated_clock。我刚开始做高速接口时,就吃过这个亏——直接把分频时钟当主时钟约束,结果时序分析全乱套了。

3.1 为什么需要create_generated_clock?

你想想看,主时钟是芯片的“心脏”,所有时序都围着它转。但实际设计中,我们经常需要不同频率的时钟。比如DDR接口需要倍频时钟,SPI接口需要分频时钟。这些时钟和主时钟有明确的相位关系,如果不用create_generated_clock,工具就不知道它们之间的时序路径该怎么算。

举个例子:

// 错误做法:直接创建新时钟
create_clock -name clk_div2 -period 20 [get_pins u_div/clk_out]

// 正确做法:定义生成时钟
create_generated_clock -name clk_div2 \
    -source [get_ports clk_in] \
    -divide_by 2 \
    [get_pins u_div/clk_out]

我在项目中遇到过,有人图省事直接create_clock,结果跨时钟域路径全报错。嗯,这里要注意:生成时钟必须指定-source,告诉工具它从哪个主时钟派生出来的。

3.2 分频时钟的约束方法

分频时钟是最常见的生成时钟。整数分频用-divide_by,非整数分频用-edges。我个人习惯,能用-divide_by绝不用-edges,因为后者容易写错。

分频类型 常用参数 示例
2分频 -divide_by 2 create_generated_clock -divide_by 2 ...
4分频 -divide_by 4 create_generated_clock -divide_by 4 ...
1.5分频 -edges {1 3 4} 需要指定边沿位置

重要提醒:分频时钟的相位关系一定要搞清楚。比如2分频时钟,上升沿和主时钟的上升沿对齐,但下降沿就不一定了。我曾经因为没注意这个,导致DDR接口的写数据眼图完全闭合。

3.3 倍频时钟的约束

倍频时钟通常由PLL产生。约束时用-multiply_by参数。但要注意,PLL输出的时钟可能有相位偏移,需要配合-phase参数。

// 2倍频时钟,相位偏移90度
create_generated_clock -name clk_x2 \
    -source [get_ports ref_clk] \
    -multiply_by 2 \
    -phase 90 \
    [get_pins pll/clk_out]

这里有个坑:-phase的单位是度,不是皮秒。我见过有人把相位偏移写成-phase 1.5,以为是1.5ns,结果时序分析全错。说白了,工具是按周期比例算相位的,90度就是四分之一个周期。

3.4 相位关系的处理

生成时钟和主时钟的相位关系,是时序分析的核心。工具会根据-divide_by、-multiply_by和-phase自动计算。但有些特殊情况需要手动指定-edges。

比如一个3分频时钟,占空比50%:

create_generated_clock -name clk_div3 \
    -source [get_ports clk_in] \
    -edges {1 2 4} \
    [get_pins u_div/clk_out]

这里的{1 2 4}是什么意思?

  • 第一个边沿:主时钟的第1个边沿
  • 第二个边沿:主时钟的第2个边沿
  • 第三个边沿:主时钟的第4个边沿

这样生成的时钟周期就是3个主时钟周期,占空比50%。

小技巧:如果你不确定相位关系对不对,可以用report_timing -clock_generated查看。工具会列出所有生成时钟和主时钟的相位关系。我每次做完约束都会跑一遍这个命令,确保没写错。

3.5 避坑指南

我曾经在某个项目中,把PLL输出的多个时钟都用了create_clock,结果跨时钟域路径全报红色。后来发现,这些时钟其实都是从同一个参考时钟派生出来的,应该用create_generated_clock。改了之后,时序分析就正常了。

还有一次,我忘了给生成时钟加-master_clock参数。当有多个主时钟时,工具不知道生成时钟从哪个主时钟派生,就会报错。所以,如果你的设计有多个主时钟,一定要指定-master_clock。

警告:生成时钟的-source引脚必须是主时钟的源点,不能是中间节点。否则工具会报“source pin not found”错误。我见过有人把-source连到PLL的输出上,结果时序分析完全不对。

3.6 知识体系图

主时钟 (create_clock) create_generated_clock -source -divide_by -multiply_by -phase 分频时钟 -divide_by 2/4/8 -edges {1 2 4} (非整数分频) 倍频时钟 -multiply_by 2/4 -phase 0/90/180/270 相位关系 自动计算 vs 手动指定 report_timing -clock_generated 生成时钟约束知识体系

3.7 总结

生成时钟约束,说白了就是告诉工具:这个时钟是从哪个主时钟来的,频率是多少,相位偏移多少。记住三点:

  • 必须指定-source,指向主时钟的源点
  • 分频用-divide_by,倍频用-multiply_by
  • 相位关系用-phase或-edges指定

我个人习惯,每次写完约束都会跑一遍report_clock -generated,看看生成时钟的波形对不对。嗯,这个习惯帮我避免了好几次流片失败的风险。