3. 时钟约束:create_clock命令详解、时钟周期与波形定义、虚拟时钟与生成时钟

时钟,是数字芯片的“心跳”。

我做了十几年STA,见过太多因为时钟没约束好,导致流片回来跑不起来的案例。时钟约束要是错了,后面所有分析都是白搭。今天咱们就把 create_clock 这个命令彻底聊透。

3.1 create_clock 命令详解

先看最基础的东西。create_clock 是STA工具里最常用的命令,没有之一。它的作用很简单——告诉工具,你的时钟长什么样。

基本语法长这样:

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

我习惯把 -name 放在最前面,这样一眼就能看到时钟叫什么。比如:

create_clock -name clk_sys -period 10.0 -waveform {0 5} [get_ports clk]

这条命令的意思是:在端口 clk 上定义一个叫 clk_sys 的时钟,周期10ns,上升沿在0ns,下降沿在5ns——说白了就是50%占空比。

关键点:

  • -name 可以省略,工具会自动用端口名作为时钟名。但我建议你显式写出来,方便后续引用。
  • -waveform 如果不写,默认就是 {0, period/2},也就是50%占空比。
  • 时钟定义在端口上,不是定义在内部节点上。内部节点的时钟要用 create_generated_clock。

3.2 时钟周期与波形定义

时钟周期,就是两个相邻上升沿之间的时间。单位通常是ns。比如10ns对应100MHz,2ns对应500MHz。

波形定义稍微绕一点。我举个例子你就明白了:

# 占空比30%的时钟,周期5ns
create_clock -name clk_30 -period 5.0 -waveform {0 1.5} [get_ports clk_in]

这里上升沿在0ns,下降沿在1.5ns。高电平持续1.5ns,低电平持续3.5ns。嗯,就是这么简单。

你可能会问:为什么波形只定义两个沿?因为时钟是周期性的,后面的沿会自动重复。工具会从第一个上升沿开始,每隔一个周期重复一次。

我的小技巧:

我在项目中遇到过一种情况——时钟源输出的波形不是标准的方波,而是有抖动。这时候我会在波形定义里加一个 -jitter 参数,模拟时钟抖动对时序的影响。不过这是后话了,初学者先把基础波形搞明白。

3.3 虚拟时钟

虚拟时钟,说白了就是“不存在”的时钟。它不绑定在任何端口或引脚上,只用来做约束。

为什么要用虚拟时钟?

我举个例子:你的芯片要和外部芯片通信,外部芯片的时钟是100MHz,但你芯片内部没有这个时钟信号。这时候你就需要定义一个虚拟时钟,用来约束输入输出路径。

# 定义一个虚拟时钟,周期10ns
create_clock -name vclk -period 10.0 -waveform {0 5}

注意,这里没有 get_ports。因为没有物理端口。

虚拟时钟最常见的用法,是配合 set_input_delay 和 set_output_delay 使用。比如:

set_input_delay -clock vclk -max 2.0 [get_ports data_in]

这条命令的意思是:数据 data_in 相对于虚拟时钟 vclk 的上升沿,最晚2ns到达芯片引脚。

注意:

虚拟时钟的名字不要和真实时钟重名,否则工具会报错。我习惯在虚拟时钟名前加个 v_ 前缀,比如 v_clk_100m,这样一眼就能区分。

3.4 生成时钟

生成时钟,就是从已有的时钟(主时钟)通过分频、倍频、相移等方式派生出来的时钟。

比如你有一个100MHz的主时钟,经过一个二分频器,得到50MHz的时钟。这时候你不能用 create_clock 再定义一个时钟,而要用 create_generated_clock。

# 主时钟
create_clock -name clk_100m -period 10.0 [get_ports clk_in]

# 二分频生成时钟
create_generated_clock -name clk_50m -source [get_ports clk_in] \
    -divide_by 2 [get_pins divider/q]

这里 -source 指定了源时钟(主时钟),-divide_by 2 表示二分频。工具会自动计算出生成时钟的周期是20ns。

生成时钟还有几个常用参数:

参数 说明 示例
-divide_by 分频系数 -divide_by 2 二分频
-multiply_by 倍频系数 -multiply_by 2 二倍频
-invert 反相(180度相移) -invert 时钟反相
-edges 指定使用源时钟的第几个沿 -edges {1 3 5} 使用第1、3、5个沿
-edge_shift 沿的偏移量 -edge_shift {0.5 0.5 0.5} 每个沿偏移0.5ns

我曾经遇到过一个坑:生成时钟的 -source 指定错了引脚,导致STA分析出来的时序完全不对。后来查了两天才发现,原来 -source 应该指向主时钟的源端口,而不是生成时钟自己的输出引脚。这个错误很隐蔽,大家一定要小心。

生成时钟的核心规则:

  • 生成时钟必须有一个源时钟(主时钟或其他生成时钟)。
  • 生成时钟的周期由源时钟周期和分频/倍频系数决定。
  • 生成时钟的波形由源时钟波形和 -edges / -edge_shift 决定。
  • 生成时钟不能定义在端口上,只能定义在内部节点(通常是寄存器输出引脚或分频器输出)。

3.5 知识体系总览

下面这张图,把时钟约束的核心逻辑串起来了。你一看就明白:

时钟约束知识体系 主时钟 create_clock 虚拟时钟 无物理端口 生成时钟 create_generated_clock -period 周期 -waveform 波形 -name 时钟名 get_ports 端口 -divide_by 分频 -multiply_by 倍频 -edges 沿选择 -edge_shift 偏移 派生关系 输入输出约束 set_input/output_delay 分频/倍频电路 PLL、分频器 跨时钟域约束 CDC 分析 时钟约束是STA的基石,主时钟、虚拟时钟、生成时钟三者缺一不可

这张图把三种时钟的关系和参数都画出来了。你仔细看,主时钟是基础,生成时钟从主时钟派生,虚拟时钟独立存在。每个时钟都有对应的参数和应用场景。

避坑指南:

我曾经在项目里犯过一个低级错误——把生成时钟定义在了组合逻辑的输出上。结果工具报了一堆时序违例,我查了半天才发现,生成时钟必须定义在时序单元的输出引脚上。组合逻辑的输出不能作为生成时钟的源。这个坑,你们千万别踩。

好了,时钟约束这块就聊到这儿。记住一句话:时钟约束对了,STA就成功了一半。剩下的就是慢慢调时序了。

可可精品资料库