3. 时序约束入门:create_clock、set_input_delay、set_output_delay 的基本用法

时序约束这东西,说白了就是给芯片定规矩。

你告诉工具:我的时钟长什么样,数据从外面进来花了多少时间,数据送出去又要预留多少时间。工具才能帮你算——这条路能不能跑通。

我刚开始学的时候,觉得约束就是写几行 Tcl 命令,没啥技术含量。后来踩了几个坑才明白:约束写不对,后端的物理实现全是白搭。

3.1 create_clock —— 给芯片一颗跳动的心脏

时钟是时序分析的基准。没有时钟,工具根本不知道什么时候该采样数据。

create_clock 的基本语法很简单:

create_clock -name clk_sys -period 10 [get_ports clk]

这条命令的意思是:在端口 clk 上创建一个名为 clk_sys 的时钟,周期是 10ns,也就是 100MHz。

我个人习惯把时钟名字取得跟端口名区分开。端口叫 clk,时钟对象就叫 clk_sys。这样在后续约束里引用时钟时,一眼就能看出是哪个时钟域。

关键参数说明:

参数含义示例
-name时钟对象的名字clk_sys
-period时钟周期,单位 ns10
-waveform上升沿和下降沿的时刻{0 5}
[get_ports]时钟源端口clk

如果时钟不是 50% 占空比,可以用 -waveform 指定:

create_clock -name clk_odd -period 10 -waveform {0 3} [get_ports clk_odd]

这表示上升沿在 0ns,下降沿在 3ns。占空比是 30%。

我的小习惯: 写约束文件时,我会把所有 create_clock 放在文件最前面。时钟定义清楚了,后面的 input/output delay 才能引用。顺序乱了,工具会报一堆找不到时钟的 warning。

3.2 set_input_delay —— 数据从外面进来,花了多少时间?

芯片不是孤岛。数据从上一级芯片(通常是 FPGA 或者另一个 ASIC)送过来,经过 PCB 走线,才到达你的输入引脚。

这个过程中消耗的时间,就是 set_input_delay 要描述的东西。

set_input_delay -clock clk_sys -max 5.0 [get_ports data_in]

这条命令的意思是:数据 data_in 相对于时钟 clk_sys 的上升沿,最晚可能晚到 5ns。

你想想看,工具拿到这个约束后会怎么算?

它会认为:数据在时钟沿之后 5ns 才稳定下来。那么内部寄存器的 setup 检查,就要从时钟沿往前推,留出足够的裕量。

我在项目中遇到过一种情况:输入 delay 设得太松,导致内部时序看起来很好,但实际芯片跟外部对接时频频出错。后来发现是 PCB 走线太长,外部数据本来就晚到了,我又没给够约束,工具就过度优化了内部路径。

避坑指南: 我曾经犯过一个低级错误——把 -max 和 -min 搞反了。-max 对应最晚到达时间(setup 检查用),-min 对应最早到达时间(hold 检查用)。写反了,工具会报出匪夷所思的违例。

完整的写法通常包含 -max 和 -min:

set_input_delay -clock clk_sys -max 5.0 [get_ports data_in]
set_input_delay -clock clk_sys -min 1.5 [get_ports data_in]

这表示数据最早在 1.5ns 后到达,最晚在 5ns 后到达。工具会同时检查 setup 和 hold。

3.3 set_output_delay —— 数据送出去,外面要等多久?

输出 delay 跟输入 delay 是镜像关系。

数据从芯片内部寄存器出发,经过输出 buffer、封装引脚、PCB 走线,到达下一级芯片。下一级芯片对数据的到达时间有要求——它得在时钟沿之前准备好。

set_output_delay -clock clk_sys -max 4.0 [get_ports data_out]

这条命令的意思是:数据 data_out 必须在时钟沿之前 4ns 到达外部芯片的输入引脚。

说白了,工具会保证内部路径的延迟足够小,让数据提前 4ns 就送出去。

我记得有一次做 DDR 接口的约束,输出 delay 设得不够精确,导致后仿一直报错。后来拿着示波器去量 PCB 上的信号,才发现外部芯片的建立时间要求比 datasheet 上写的还要严苛。嗯,从那以后我养成了一个习惯:写约束之前,一定先把外部芯片的 datasheet 翻烂。

输出 delay 的两种模式:

  • 相对于时钟上升沿: 最常用,数据在时钟沿之后输出
  • 相对于时钟下降沿: DDR 接口或双沿采样场景

用 -clock_fall 可以指定下降沿:

set_output_delay -clock clk_sys -clock_fall -max 3.0 [get_ports ddr_data]

3.4 三条命令的配合使用 —— 一个完整的例子

光讲理论没意思。我们来看一个实际场景:

假设有一个芯片,输入端口 addr 从外部接收地址,输出端口 data 送出数据。时钟是 100MHz。

# 1. 定义时钟
create_clock -name sys_clk -period 10.0 [get_ports clk]

# 2. 输入约束:外部地址在时钟沿后 2~6ns 到达
set_input_delay -clock sys_clk -max 6.0 [get_ports addr]
set_input_delay -clock sys_clk -min 2.0 [get_ports addr]

# 3. 输出约束:外部芯片要求数据在时钟沿前 3ns 准备好
set_output_delay -clock sys_clk -max 3.0 [get_ports data]
set_output_delay -clock sys_clk -min 1.0 [get_ports data]

这三条命令写完后,工具就能自动检查:

  • 从 addr 到内部寄存器的路径,setup 和 hold 是否满足
  • 从内部寄存器到 data 的路径,setup 和 hold 是否满足

我的经验: 刚开始写约束时,可以把 -max 和 -min 设得宽松一点,先跑一遍 STA 看看有没有明显的违例。等基本路径都通了,再收紧约束。一步到位反而容易把自己搞晕。

3.5 本章知识体系

下面这张图概括了三条命令的核心逻辑:

时序约束三条核心命令 外部发送端 (FPGA/ASIC) 当前芯片 内部寄存器 组合逻辑 时钟树 外部接收端 (下一级芯片) set_input_delay 数据到达时间 set_output_delay 数据输出要求 clk create_clock 定义时钟周期与波形 输入路径约束 输出路径约束 时钟定义

从这张图可以看得很清楚:

  • create_clock 是基础,定义了芯片内部的时序参考
  • set_input_delay 描述数据从外部进入芯片的时间窗口
  • set_output_delay 描述数据从芯片送出的时间要求

三条命令配合起来,就构成了完整的芯片边界时序约束。

最后提醒一句: 约束不是写完了就完事的。每次跑完 STA,一定要看 report_timing 的违例路径。我曾经遇到过一条路径 setup 违例 2ns,查了半天发现是 input delay 设错了——把 5ns 写成了 50ns。这种低级错误,看报告一眼就能发现。

好了,这一章的内容就到这里。记住:约束写得好,后端没烦恼。多花点时间在约束上,后面能省十倍的时间。


可可精品资料库