3. 时钟约束基础:create_clock语法、时钟周期与占空比、虚拟时钟

时钟,是FPGA设计的“心跳”。

我刚开始做收发器项目时,总觉得时序约束是件麻烦事。直到有一次,板子调了三天死活不工作,最后发现是时钟约束写错了。嗯,从那以后,我再也不敢小看这个create_clock命令了。

3.1 create_clock 语法:给时钟“上户口”

在Vivado里,create_clock是最基础的约束命令。说白了,就是告诉工具:“嘿,这个端口上有个时钟,它的频率是多少,什么时候跳变。”

基本语法长这样:

create_clock -name <时钟名> -period <周期(ns)> [ -waveform <上升沿时间> <下降沿时间> ] [get_ports <端口名>]

我来拆解一下:

  • -name:给时钟起个名字。我个人习惯用clk_xxx的格式,比如clk_156m25,一看就知道是156.25MHz的时钟。
  • -period:时钟周期,单位是纳秒。注意,不是频率,是周期!
  • -waveform:定义上升沿和下降沿的位置。如果不写,默认占空比50%,上升沿在0ns,下降沿在period/2处。
  • get_ports:指定时钟从哪个管脚进来。

举个实际例子。我在一个10G光模块项目中,参考时钟是156.25MHz:

create_clock -name refclk_156m25 -period 6.4 [get_ports refclk_p]

这里周期6.4ns是怎么算的?1 / 156.25MHz = 0.0000000064秒 = 6.4ns。这个换算要熟记,我见过有人把周期和频率搞反,结果约束全错了。

小技巧: 如果你记不住换算,可以直接用Tcl表达式:-period [expr 1.0/156.25e6 * 1e9],让工具帮你算。

3.2 时钟周期与占空比:别被50%骗了

大部分时钟都是50%占空比,但收发器里的时钟不一定。

比如,有些SerDes的参考时钟是占空比45%/55%的。这时候就要用-waveform来精确描述。

# 占空比60%的时钟,周期10ns
create_clock -name clk_duty60 -period 10 -waveform {0 6} [get_ports clk_in]

-waveform {0 6}的意思是:上升沿在0ns,下降沿在6ns。高电平持续6ns,占空比60%。

为什么会用到非50%占空比?

我在调试一个JESD204B接口时,遇到过一个问题。接收端的时钟恢复电路对占空比很敏感,如果约束里写的是50%,但实际芯片输出的是55%,时序分析就会不准。后来我专门量了波形,把占空比写进约束里,问题就解决了。

注意: 对于收发器(GTX/GTH/GTY)的参考时钟,Vivado通常会自动推断占空比。但如果你手动创建了时钟,一定要确认-waveform和实际硬件一致。否则,时序报告会给你“好看”。

3.3 虚拟时钟:看不见的“参考系”

虚拟时钟,是约束里一个比较抽象的概念。

它不绑定任何物理端口,只存在于时序分析的计算中。说白了,就是一个“假想时钟”,用来描述外部器件的时序行为。

什么时候用?

  • 输入延迟约束:当外部芯片的数据是相对于某个时钟发送的,但这个时钟并没有进入FPGA。
  • 输出延迟约束:FPGA输出的数据,要被外部芯片在某个时钟沿采样。

语法很简单,就是不加get_ports:

create_clock -name virt_clk -period 10

然后配合set_input_delay或set_output_delay使用:

set_input_delay -clock virt_clk -max 2.5 [get_ports data_in]

我曾经犯过一个错误:把虚拟时钟的周期设错了。外部ADC是100MHz的,但我写成了125MHz。结果时序分析一直报错,我查了两天才发现是虚拟时钟周期不对。嗯,这种低级错误,犯一次就记住了。

核心要点: 虚拟时钟只存在于约束文件中,没有物理连线。它的作用是为输入/输出延迟提供一个时间参考基准。

3.4 本章知识体系

下面这张图,是我自己总结的时钟约束知识结构。你可以把它当成一个“思维导图”来看:

时钟约束基础 create_clock 语法 -name 命名规范 -period 周期换算 -waveform 波形定义 get_ports 端口绑定 周期与占空比 频率→周期换算 50%占空比默认 非50%用-waveform 收发器特殊要求 虚拟时钟 无物理端口 输入延迟参考 输出延迟参考 配合set_*_delay 核心:时钟约束是时序分析的“尺子”,尺子不准,分析全错

这张图把三个核心知识点串起来了。你写约束的时候,可以对照着检查:时钟名写了吗?周期算对了吗?占空比要不要改?是不是需要虚拟时钟?

我的习惯: 每写一个时钟约束,我都会在Tcl Console里敲一遍report_clocks,看看Vivado认出了几个时钟,周期对不对。这一步花不了10秒钟,但能避免很多低级错误。

好了,时钟约束的基础就这些。记住,create_clock是你和Vivado沟通的第一句话,这句话说错了,后面全是白搭。