主时钟约束:create_clock 命令详解
时钟约束,是时序约束的起点。说白了,你连时钟都没定义好,工具怎么知道你的设计能不能跑?今天我们就来聊聊 create_clock 这个命令,它到底能干什么,怎么用才不出错。
create_clock 的基本语法
先看一个最简单的例子:
create_clock -name clk -period 10.000 [get_ports clk]
这条命令的意思是:在端口 clk 上创建一个名为 clk 的时钟,周期是 10ns。嗯,10ns 对应 100MHz,这是最常用的写法。
我个人习惯把 -name 和端口名保持一致,这样后期查起来不费劲。你想想看,如果时钟名叫 my_clk,端口叫 sys_clk,调试的时候是不是得来回翻?
时钟周期、占空比、相位偏移
create_clock 支持三个核心参数:
| 参数 | 含义 | 默认值 |
|---|---|---|
| -period | 时钟周期,单位 ns | 无(必填) |
| -waveform | 上升沿和下降沿的时间点 | {0, period/2} |
| -duty_cycle | 占空比(部分工具支持) | 50% |
占空比设置:默认是 50%。如果你需要 60% 占空比,可以这样写:
create_clock -name clk -period 10.000 -waveform {0 6} [get_ports clk]
这里 {0 6} 表示上升沿在 0ns,下降沿在 6ns。高电平持续 6ns,占空比就是 60%。
相位偏移设置:相位偏移其实就是调整第一个上升沿的位置。比如你想让时钟延迟 2ns 再开始:
create_clock -name clk -period 10.000 -waveform {2 7} [get_ports clk]
这里第一个上升沿在 2ns,下降沿在 7ns。周期还是 10ns,但整体往后移了 2ns。
为什么要做相位偏移?我遇到过一种场景:两个芯片之间通信,发送端时钟和接收端时钟有固定的 skew。这时候你可以在约束里把接收时钟的相位偏移调一下,让工具知道实际的时序关系。嗯,这比在 PCB 上绕线要省事多了。
单端时钟与差分时钟约束
单端时钟很简单,就是上面那种写法。但差分时钟呢?
差分时钟通常有两个引脚:clk_p 和 clk_n。在 FPGA 里,差分时钟会先经过一个 IBUFDS 原语,变成单端时钟后再使用。
约束差分时钟的正确做法是:
create_clock -name clk -period 10.000 [get_ports clk_p]
注意!只约束 clk_p 端口,不要约束 clk_n。为什么?因为工具会自动识别差分对,clk_n 只是 clk_p 的反相版本,不需要单独定义。
如果你用的是 Xilinx 的器件,还有一种写法:
create_clock -name clk -period 10.000 [get_ports {clk_p clk_n}]
这种写法也支持,但本质上还是只创建一个时钟。我个人更推荐只写 clk_p,更清晰。
多时钟约束的场景
一个设计里通常有多个时钟。比如系统时钟 100MHz,DDR 时钟 200MHz,还有 SPI 接口的 10MHz 时钟。每个时钟都要单独约束:
create_clock -name sys_clk -period 10.000 [get_ports sys_clk]
create_clock -name ddr_clk -period 5.000 [get_ports ddr_clk]
create_clock -name spi_clk -period 100.000 [get_ports spi_clk]
这里要注意:不同时钟域之间的路径,工具默认会做跨时钟域分析。如果你确定这些时钟之间是异步的,需要加 set_clock_groups 来告诉工具不要分析。否则工具会报一堆时序违例,其实都是虚警。
知识体系图
下面这张图展示了本章的核心逻辑:
这张图把 create_clock 的三个核心维度都列出来了。你写约束的时候,对着这张图检查一遍,基本不会漏掉什么。
report_clocks 命令检查一下。看看时钟周期对不对,占空比是不是你想要的。我每次写完约束都会跑一遍这个命令,确认无误后再往下走。省得后期发现问题再回头改,那才叫一个痛苦。
公众号:蓝海资料掘金营,微信deep3321