第三章 主时钟约束(create_clock):语法详解、单端时钟与差分时钟、时钟周期与占空比设置、实际项目中的时钟约束案例
时钟,是FPGA设计的“心脏”。
我做了这么多年设计,见过太多因为时钟没约束好,导致上板跑飞的情况。说白了,时序约束的第一步,就是告诉工具:你的时钟长什么样。
今天咱们就聊聊 create_clock 这个命令。它不复杂,但细节很多。我踩过的坑,你最好别踩。
3.1 create_clock 语法详解
先看最基础的写法:
create_clock -name clk_sys -period 10.000 [get_ports sys_clk]
这条命令的意思是:在端口 sys_clk 上,创建一个名为 clk_sys 的时钟,周期是10ns(对应100MHz)。
完整的语法长这样:
create_clock [-name <clock_name>]
-period <value>
[-waveform <edge_list>]
[-add]
[-comment <string>]
[source_objects]
| 参数 | 说明 | 必填? |
|---|---|---|
-name |
时钟名称,建议有意义的命名 | 推荐 |
-period |
时钟周期,单位ns | 必填 |
-waveform |
上升沿和下降沿时刻,默认50%占空比 | 可选 |
-add |
在同一个端口上叠加多个时钟 | 可选 |
source_objects |
时钟源,通常是端口或网络 | 必填 |
-name。别偷懒用默认名,否则后期看时序报告时,满屏的 clk、clk_1 会让你崩溃。
3.2 单端时钟与差分时钟
单端时钟最简单,直接约束在输入端口上就行:
create_clock -name clk_50m -period 20.000 [get_ports clk_in]
差分时钟呢?很多新手会犯一个错误——把两个差分引脚都约束了。
千万别这么做。
差分时钟只需要约束在 正端(P端) 上。工具会自动识别差分对。
# 正确做法:只约束P端
create_clock -name clk_125m -period 8.000 [get_ports clk_p]
# 错误做法:不要同时约束P和N
# create_clock -name clk_125m -period 8.000 [get_ports clk_n] ← 不要这样!
3.3 时钟周期与占空比设置
默认情况下,create_clock 认为时钟是50%占空比。也就是上升沿在0ns,下降沿在周期的一半。
但实际项目中,有些时钟不是50%的。比如DDR接口的时钟,或者某些PLL输出的特殊时钟。
这时候就需要 -waveform 参数了:
# 50%占空比,100MHz时钟
create_clock -name clk_100m -period 10.000 [get_ports clk_in]
# 等价于:
create_clock -name clk_100m -period 10.000 -waveform {0 5} [get_ports clk_in]
# 40%占空比,上升沿在0ns,下降沿在4ns
create_clock -name clk_40pct -period 10.000 -waveform {0 4} [get_ports clk_in]
# 非0起始的时钟,比如上升沿在2ns,下降沿在7ns
create_clock -name clk_shift -period 10.000 -waveform {2 7} [get_ports clk_in]
-waveform 的格式是 {上升沿时刻 下降沿时刻}。注意:
- 第一个值必须是上升沿
- 第二个值必须是下降沿
- 两个值都在0到周期之间
{0 2},结果保持时间违例一大堆,其实根本没问题。
3.4 实际项目中的时钟约束案例
光讲理论没意思,咱们看几个真实案例。
案例1:板级晶振时钟
最常见的场景。板子上有个50MHz晶振,接到FPGA的专用时钟引脚上。
create_clock -name clk_50m -period 20.000 [get_ports sys_clk]
嗯,就这么简单。但要注意:sys_clk 必须是 get_ports 能找到的顶层端口名。
案例2:以太网GMII接口时钟
GMII接口的RX时钟是125MHz,但它是从外部PHY芯片送过来的,而且占空比可能不是完美的50%。
create_clock -name gtx_clk -period 8.000 [get_ports gtx_clk_p]
create_clock -name rx_clk -period 8.000 [get_ports rx_clk_p]
这里我习惯把时钟名字和端口名区分开。端口名是硬件原理图上的,时钟名是时序分析用的。方便后期查错。
案例3:同一个端口上多个时钟
有些芯片支持多种工作模式,同一个引脚在不同模式下输出不同频率的时钟。这时候用 -add:
create_clock -name clk_100m -period 10.000 [get_ports mode_clk]
create_clock -name clk_50m -period 20.000 [get_ports mode_clk] -add
不加 -add 的话,第二条命令会覆盖第一条。加了之后,工具会同时分析两种可能性。
-add,结果工具只分析了50MHz的情况。100MHz模式下的时序违例完全没报出来。流片回来才发现……嗯,那是一次惨痛的教训。
案例4:PLL输出时钟
PLL输出的时钟,通常由工具自动生成约束。但如果你手动写了,要注意:
# 不推荐手动约束PLL输出
# create_clock -name pll_out -period 5.000 [get_pins pll_inst/CLKOUT0]
# 推荐:让工具自动生成,或者用derive_pll_clocks
derive_pll_clocks
我个人建议:PLL输出时钟让工具自己处理。你只需要约束好输入时钟就行。
3.5 知识体系总览
下面这张图,是我梳理的主时钟约束的核心逻辑。你看一眼,心里就有谱了:
这张图把主时钟约束的四个核心维度串起来了。你写约束时,对着这张图过一遍,基本不会漏东西。
3.6 小结
主时钟约束是时序分析的起点。它不复杂,但细节决定成败。
我给你的建议是:
- 每个时钟都显式命名
- 差分时钟只约束P端
- 占空比不确定时,问硬件工程师要波形图
- 多模式时钟别忘了
-add - PLL输出让工具自动处理
把这些记住了,主时钟约束这块你就稳了。
公众号:蓝海资料掘金营,微信deep3321