3、时钟定义:create_clock命令详解,时钟周期、占空比、波形定义
时钟,是数字芯片的“心跳”。
没有时钟,整个设计就是一盘散沙。做静态时序分析,第一步就是告诉工具:你的时钟长什么样。说白了,就是通过 create_clock 这个命令,把时钟的周期、占空比、波形这些信息喂给工具。
我刚开始学STA的时候,觉得这命令太简单了,不就是写个周期嘛。后来发现,里面坑不少。今天咱们就把它掰开揉碎了讲清楚。
3.1 最基本的时钟定义
先看一个最常用的写法:
create_clock -name clk -period 10 [get_ports clk]
这条命令干了三件事:
- -name clk:给这个时钟起个名字,叫
clk。方便后面约束里引用。 - -period 10:时钟周期是 10ns。也就是频率 100MHz。
- [get_ports clk]:这个时钟是从哪个端口进来的?就是顶层叫
clk的那个输入端口。
嗯,这里要注意:-name 不是必须的。如果你不写,工具会自动用端口名作为时钟名。但我个人习惯,每次都显式写出来,这样读约束文件的人一眼就能看懂。
sys_clk,那 -name sys_clk 可以省略。但为了清晰,我建议别省。
3.2 时钟周期——核心中的核心
周期决定了芯片能跑多快。你想想看,周期设成 10ns,那所有逻辑路径都得在 10ns 内搞定。设成 5ns,难度直接翻倍。
周期单位是什么?纳秒(ns)。这是默认单位。你也可以用 -period 10000 表示皮秒,但一般没人这么干,容易搞混。
我在项目中遇到过一件事:有个同事把周期写成了 -period 100,他以为是 100MHz(周期10ns),结果工具按 100ns 去分析,所有路径都 slack 巨大。最后综合出来的芯片,频率根本跑不上去。这就是典型的“周期写错,全盘皆输”。
-period 100,导致时序分析完全失效。记住:100MHz = 10ns,不是 100ns!
3.3 占空比与波形定义
默认情况下,create_clock 定义的时钟是 50% 占空比。也就是高电平一半时间,低电平一半时间。
但现实世界没这么完美。有些时钟是 60% 占空比,有些是 40%。甚至有些 DDR 接口的时钟,占空比要求很严格。
这时候就要用到 -waveform 参数了。
# 50% 占空比,周期 10ns
create_clock -name clk -period 10 -waveform {0 5} [get_ports clk]
# 60% 占空比,高电平占 6ns
create_clock -name clk -period 10 -waveform {0 6} [get_ports clk]
# 40% 占空比,高电平占 4ns
create_clock -name clk -period 10 -waveform {0 4} [get_ports clk]
-waveform 后面跟两个值:上升沿时间 和 下降沿时间。单位也是纳秒。
{0 5}:上升沿在 0ns,下降沿在 5ns。高电平 5ns,低电平 5ns。标准的 50%。{0 6}:上升沿在 0ns,下降沿在 6ns。高电平 6ns,低电平 4ns。占空比 60%。
你可能会问:那下降沿在 0ns,上升沿在 5ns 行不行?
行!那就是低电平先开始,高电平在后。不过一般没人这么干,容易把自己绕晕。
-waveform,工具默认就是 {0 period/2}。也就是 50% 占空比。所以大部分情况下,你只需要写周期就够了。
3.4 多波形时钟——同一个端口,多个频率
有些芯片有多个时钟源,通过 MUX 选择后进入同一个端口。这时候,你需要定义多个时钟,并告诉工具它们之间的关系。
create_clock -name clk_100m -period 10 -waveform {0 5} [get_ports clk_in]
create_clock -name clk_50m -period 20 -waveform {0 10} [get_ports clk_in] -add
注意那个 -add 参数。如果不加,第二个 create_clock 会覆盖第一个。加上 -add,工具就知道这个端口上有两个时钟同时存在。
我记得有一次做项目,芯片有多个工作模式,时钟频率不同。我忘了加 -add,结果工具只分析了最后一个时钟,导致其他模式的时序完全没检查。流片回来,某个模式就是跑不起来。教训深刻啊。
-add 参数。否则后面的会覆盖前面的,导致时钟丢失。
3.5 虚拟时钟——没有物理端口的时钟
有时候,你需要定义一个时钟,但它并不对应芯片上的任何物理端口。比如,用来约束输入输出延迟的参考时钟。
这种时钟叫 虚拟时钟(virtual clock)。定义方式很简单:
create_clock -name vclk -period 10
看到了吗?没有 [get_ports ...] 这一项。这就是虚拟时钟和普通时钟的唯一区别。
虚拟时钟有什么用?我举个例子:
- 芯片的输入数据来自外部芯片,那个芯片有自己的时钟。这个时钟不会进入我们的芯片,但我们需要用它来约束输入延迟。
- 这时候,就定义一个虚拟时钟,代表外部芯片的时钟。
说白了,虚拟时钟就是个“假想敌”,用来帮我们算清楚外部时序的账。
vclk_ext_100m,一看就知道是外部 100MHz 的虚拟时钟。别起 clk1、clk2 这种,三个月后你自己都看不懂。
3.6 时钟的命名与分组
时钟多了以后,管理起来就麻烦了。我建议你养成好习惯:
- 时钟名要有规律,比如
clk_core、clk_io、clk_ddr。 - 用
group_path命令把相关时钟的路径分组,方便分析。
不过这是后话了。今天先把 create_clock 吃透。
3.7 总结一下
| 参数 | 作用 | 是否必须 | 默认值 |
|---|---|---|---|
-name |
时钟名称 | 否 | 端口名 |
-period |
时钟周期(ns) | 是 | 无 |
-waveform |
上升沿和下降沿时间 | 否 | {0 period/2} |
-add |
允许在同一个端口添加多个时钟 | 多时钟时必加 | 不加则覆盖 |
[get_ports ...] |
时钟所在的物理端口 | 虚拟时钟不需要 | 无 |
好了,关于 create_clock 的核心内容就这些。下一章咱们聊聊 create_generated_clock,也就是分频时钟和衍生时钟的定义。那个比这个稍微复杂一点,但掌握了基础,后面就好办了。