第三章 主时钟约束(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。别偷懒用默认名,否则后期看时序报告时,满屏的 clkclk_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]  ← 不要这样!
我曾经踩过的坑: 有次做SERDES项目,差分时钟约束了两次,结果时序分析报告里出现了两个同名的时钟域,导致跨时钟域检查全乱套。排查了整整半天才找到原因。记住:差分时钟,只约束P端。

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到周期之间
关键点: 占空比设置影响的是建立时间和保持时间的检查窗口。如果你把占空比设错了,工具算出来的时序余量就是错的。我见过有人把50%的时钟写成了 {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 知识体系总览

下面这张图,是我梳理的主时钟约束的核心逻辑。你看一眼,心里就有谱了:

主时钟约束(create_clock)知识体系 create_clock 语法参数 -name / -period / -waveform -add / source_objects 时钟类型 单端时钟:约束在端口 差分时钟:只约束P端 占空比设置 默认50%:{0 T/2} 自定义:{rise fall} 实际案例 常见误区 晶振时钟 / GMII时钟 / 多模式时钟 差分约束两端 / 忘记-add / 占空比写错

这张图把主时钟约束的四个核心维度串起来了。你写约束时,对着这张图过一遍,基本不会漏东西。

3.6 小结

主时钟约束是时序分析的起点。它不复杂,但细节决定成败。

我给你的建议是:

  • 每个时钟都显式命名
  • 差分时钟只约束P端
  • 占空比不确定时,问硬件工程师要波形图
  • 多模式时钟别忘了 -add
  • PLL输出让工具自动处理

把这些记住了,主时钟约束这块你就稳了。


公众号:蓝海资料掘金营,微信deep3321