4. 时序约束入门:时钟定义、输入延迟、输出延迟、时序例外

时序约束,说白了就是给芯片设计定规矩。你告诉工具:信号什么时候来,什么时候走,哪些路径可以慢一点。没有约束,综合工具就像没头苍蝇,做出来的网表可能根本跑不起来。

我个人习惯,写RTL之前先把约束框架搭好。别等到代码写完了再补,那时候发现问题就晚了。好,咱们直接进入正题。

4.1 时钟定义——设计的“心跳”

时钟是时序逻辑的基准。没有时钟,寄存器就不知道什么时候采样数据。定义时钟,是约束的第一步。

最基本的时钟用 create_clock 命令。举个例子:

create_clock -name clk_sys -period 10 [get_ports clk]

这条命令告诉工具:端口 clk 上有一个时钟,周期10ns,也就是100MHz。嗯,这里要注意,周期单位默认是纳秒。

如果时钟来自PLL或者内部逻辑,用 create_generated_clock:

create_generated_clock -name clk_div2 -source [get_ports clk] -divide_by 2 [get_pins u_pll/clk_out]

我在项目中遇到过,有人忘了定义生成时钟,结果工具把分频后的路径当异步路径分析,时序报告全是乱的。所以,每个时钟节点都要明确告诉工具。

时钟定义要点:
  • 主时钟用 create_clock,生成时钟用 create_generated_clock
  • 时钟名要规范,别用默认的 clk 这种名字,容易混淆
  • 虚拟时钟(-name 不带 [get_ports])用于输入输出延迟约束

4.2 输入延迟——数据从片外来

数据从外部芯片进来,经过PCB走线,到达你的芯片引脚。这个过程中,数据相对于时钟有延迟。你需要告诉工具这个延迟范围。

输入延迟用 set_input_delay 定义:

set_input_delay -clock clk_sys -max 2.5 [get_ports data_in]
set_input_delay -clock clk_sys -min 1.0 [get_ports data_in]

这里 -max 是最大延迟,-min 是最小延迟。为什么要有两个值?因为外部芯片的时序有波动,温度、电压都会影响。

我曾经踩过一个坑:只设了最大延迟,没设最小延迟。结果工具认为数据可以提前很多到达,hold检查全过,但实际芯片就是跑不稳。后来加上最小延迟,问题才解决。

我的经验: 输入延迟的值怎么定?看外部芯片的数据手册。它给出 t_co(时钟到输出延迟)加上PCB走线延迟,就是你的输入延迟。

4.3 输出延迟——数据要去片外

输出延迟和输入延迟是对称的。你的芯片输出数据,外部芯片要在时钟沿附近采样。你需要告诉工具,外部芯片的建立时间和保持时间要求。

命令是 set_output_delay:

set_output_delay -clock clk_sys -max 3.0 [get_ports data_out]
set_output_delay -clock clk_sys -min 0.5 [get_ports data_out]

你想想看,输出延迟的本质是什么?是外部芯片的建立时间加上PCB走线延迟。所以,-max 对应外部建立时间,-min 对应外部保持时间。

我个人习惯,输出延迟留一点余量。比如外部芯片要求建立时间2ns,我设2.5ns。这样即使PCB走线有点偏差,也能兜住。

注意: 输入输出延迟都是相对于时钟沿的。如果数据是DDR(双沿采样),要分别约束上升沿和下降沿。用 -clock_fall 选项。

4.4 时序例外——打破常规

不是所有路径都需要严格的时序检查。有些路径天生就慢,有些路径根本不需要检查。这时候就要用时序例外。

4.4.1 False Path(伪路径)

False path,就是不需要检查的路径。比如跨时钟域的同步器路径,或者测试模式下的路径。

set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

这条命令告诉工具:从 clk_a 到 clk_b 的所有路径,都不用检查时序。

我记得有一次,一个同事把异步FIFO的跨时钟路径设成了false path。结果工具没检查,但实际电路因为格雷码同步延迟太大,偶尔丢数据。所以,false path要慎用。只有确定数据不会影响功能,才设false path。

False path 常见场景:
  • 异步复位信号
  • 测试模式(scan_enable)路径
  • 跨时钟域的同步器(但要注意,同步器本身要检查)
  • 上电初始化阶段的信号

4.4.2 Multicycle Path(多周期路径)

有些路径不需要在一个时钟周期内完成。比如,一个使能信号每4个周期才有效一次,那这条路径就可以放宽到4个周期。

set_multicycle_path -setup 4 -from [get_pins u_reg_a/Q] -to [get_pins u_reg_b/D]
set_multicycle_path -hold 3 -from [get_pins u_reg_a/Q] -to [get_pins u_reg_b/D]

这里 -setup 4 表示建立时间检查放宽到4个周期。-hold 3 是保持时间检查的偏移量。为什么hold要设3?因为默认hold检查是在setup检查的前一个沿,设了多周期后,hold也要跟着调整。

我在项目中遇到过,有人只设了 -setup 没设 -hold,结果hold检查还是按单周期来,导致大量hold违例。所以,多周期路径一定要成对出现。

小技巧: 多周期路径的hold值 = setup值 - 1。比如setup设4,hold就设3。这是最常用的配置。

4.5 知识体系总览

下面这张图,把时序约束的核心逻辑串起来了。你看一遍,心里就有谱了。

时序约束知识体系 时钟定义 create_clock / create_generated_clock 输入延迟 set_input_delay 数据从片外来,定义外部时序 输出延迟 set_output_delay 数据去片外,定义外部要求 时序例外 set_false_path set_multicycle_path 核心原则 1. 先定义时钟,再约束输入输出 2. 输入输出延迟要留余量 3. 时序例外要谨慎,false path不是万能药 4. 多周期路径要成对设置(setup + hold)

这张图把四个核心知识点串起来了。时钟是基础,输入输出延迟是接口约束,时序例外是特殊处理。你写约束的时候,按这个顺序来,基本不会漏。

避坑指南: 我曾经见过一个项目,综合后的网表时序全红。查了半天,发现是时钟定义错了——主时钟周期设成了20ns,但实际PLL输出是10ns。工具按20ns分析,当然全过。但流片回来,芯片死活跑不到100MHz。所以,时钟定义一定要和PLL配置对得上。

好了,时序约束入门就讲到这里。记住,约束不是写一次就完事的。综合后要看时序报告,发现问题就回来调。多调几次,你就有感觉了。

本章要点回顾:
  • 时钟定义:create_clock 和 create_generated_clock 是基础
  • 输入延迟:set_input_delay 要设 max 和 min
  • 输出延迟:set_output_delay 对应外部芯片的建立/保持时间
  • False path:只用于真正不需要检查的路径
  • Multicycle path:setup 和 hold 要成对设置

可可精品资料库