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。这个换算要熟记,我见过有人把周期和频率搞反,结果约束全错了。
-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%,时序分析就会不准。后来我专门量了波形,把占空比写进约束里,问题就解决了。
-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 本章知识体系
下面这张图,是我自己总结的时钟约束知识结构。你可以把它当成一个“思维导图”来看:
这张图把三个核心知识点串起来了。你写约束的时候,可以对照着检查:时钟名写了吗?周期算对了吗?占空比要不要改?是不是需要虚拟时钟?
report_clocks,看看Vivado认出了几个时钟,周期对不对。这一步花不了10秒钟,但能避免很多低级错误。
好了,时钟约束的基础就这些。记住,create_clock是你和Vivado沟通的第一句话,这句话说错了,后面全是白搭。