4. 输入延迟约束:set_input_delay命令、系统同步与源同步接口的区别、最大值与最小值设置

好,咱们今天聊聊输入延迟约束。说实话,这块儿是很多工程师容易搞混的地方。我见过不少项目,时序跑不过,最后查下来,就是输入延迟设错了。

输入延迟约束,说白了就是告诉工具:数据从芯片外部进来,到底花了多长时间。这个时间,是从时钟源到芯片引脚的路径延迟。你想想看,如果这个时间你给错了,工具算出来的时序结果就是错的,那后面的一切努力都白费了。

4.1 set_input_delay 命令详解

先看命令长什么样。这是最基础的形式:

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

嗯,这里要注意几个关键点:

  • -clock:指定参考时钟。这个时钟必须是已经在设计中定义好的。
  • -max:最大延迟值。对应建立时间分析。
  • -min:最小延迟值。对应保持时间分析。
  • [get_ports]:指定要约束的输入端口。

我个人习惯,会把所有输入端口都加上约束。哪怕有些端口暂时用不到,也先设个默认值。为什么?因为空端口工具会按理想情况算,但实际走线是有延时的。我曾经吃过这个亏,一个没约束的输入端口,综合后时序报告一片绿,结果板子调起来就是不行。

重要原则:输入延迟的值,是相对于时钟沿的。正值表示数据在时钟沿之后到达,负值表示数据在时钟沿之前到达。

再来看一个更完整的例子:

# 系统同步接口,数据在时钟上升沿采样
create_clock -period 10.0 [get_ports clk_in]
set_input_delay -clock clk_in -max 3.5 [get_ports data_bus]
set_input_delay -clock clk_in -min 1.2 [get_ports data_bus]

# 加上 -add_delay 选项,用于多时钟情况
set_input_delay -clock clk_in -max 3.5 -add_delay [get_ports data_bus]
set_input_delay -clock clk_in2 -max 4.0 -add_delay [get_ports data_bus]

这里有个坑:如果你要对同一个端口约束多个时钟,必须加上 -add_delay。不加的话,后面的约束会覆盖前面的。我刚开始用Vivado时就犯过这个错,查了半天才发现是约束被覆盖了。

4.2 系统同步 vs 源同步接口

这两种接口,处理方式完全不同。咱们得分开说。

特性 系统同步 源同步
时钟来源 外部时钟源,与数据独立 数据发送方提供时钟
典型接口 传统并行总线 DDR、SDRAM、SPI
约束方式 直接设 input_delay 需考虑时钟与数据的相对关系
时序裕量 较小,受PCB走线影响大 较大,时钟与数据同路径

系统同步接口,说白了就是时钟和数据各走各的。比如一个外部ADC,时钟由FPGA提供,数据由ADC返回。这种情况下,时钟和数据到达FPGA引脚的时间差,取决于PCB走线长度。我做过一个项目,时钟线和数据线长度差了2英寸,结果输入延迟差了将近500ps。嗯,这种问题只能靠约束来补偿。

系统同步的约束写法:

# 假设外部器件输出延迟为 Tco = 2ns
# PCB走线延迟 Tpcb = 1ns
# 那么输入延迟 = Tco + Tpcb = 3ns

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

源同步接口就不一样了。时钟和数据是从同一个源发出来的,比如DDR内存。这种情况下,时钟和数据在PCB上走的是相似的路径,所以它们的相对关系更稳定。你想想看,温度变了,两条线一起变;电压变了,也是两条线一起变。这就是源同步的优势。

源同步的约束要复杂一些:

# 源同步接口,数据与时钟边沿对齐
# 需要先约束生成时钟
create_generated_clock -name clk_ddr -source [get_ports clk_in] \
  -divide_by 1 [get_ports ddr_clk]

# 然后设置输入延迟,通常用负值表示数据提前于时钟
set_input_delay -clock clk_ddr -max -0.5 [get_ports ddr_dq]
set_input_delay -clock clk_ddr -min -1.5 [get_ports ddr_dq]

我的经验:源同步接口的约束,最好先看芯片手册。手册里通常会给出 setup/hold 时间窗口。把这个窗口换算成 input_delay 的 max/min 值,基本就对了。

4.3 最大值与最小值设置

最大值和最小值,分别对应建立时间和保持时间分析。这个逻辑其实很简单:

  • -max:数据最晚到达的时间。用于检查建立时间是否满足。
  • -min:数据最早到达的时间。用于检查保持时间是否满足。

为什么要有两个值?因为实际电路中,延迟不是固定的。温度、电压、工艺偏差都会导致延迟变化。最大值是最坏情况,最小值是最好的情况。

举个例子:

# 假设外部器件输出延迟范围是 1ns ~ 3ns
# PCB走线延迟范围是 0.5ns ~ 1.5ns
# 那么:
# 最大输入延迟 = 3 + 1.5 = 4.5ns
# 最小输入延迟 = 1 + 0.5 = 1.5ns

set_input_delay -clock clk -max 4.5 [get_ports data]
set_input_delay -clock clk -min 1.5 [get_ports data]

这里有个容易忽略的点:最大值和最小值之间的差值,反映了路径上的不确定性。差值越大,说明路径越不稳定。我曾经在一个项目中,发现输入延迟的 max 和 min 差了 5ns,查了半天,原来是PCB上有一段走线经过了噪声源。后来改了布线,差值降到了 2ns 以内。

注意:不要为了省事,把 max 和 min 设成同一个值。那样工具会认为路径是理想的,但实际上不是。最终结果就是,时序报告看着漂亮,板子跑起来就崩。

还有一个常见问题:负的输入延迟。什么时候用负值?当数据在时钟沿之前到达时。比如源同步接口中,数据可能比时钟早到 1ns,那 input_delay 就设成 -1ns。负值不是错误,它只是表示数据提前了。

我个人习惯,在设置输入延迟时,会留一点裕量。比如计算出来最大值是 4.5ns,我会设成 4.7ns。为什么?因为实际PCB走线、连接器、温度变化,都可能引入额外的延迟。留 0.2ns 的裕量,不算多,但能避免很多麻烦。

4.4 实战中的常见错误

我总结几个常见的坑,你遇到了可以对照看看:

  1. 忘记加 -add_delay:多时钟约束时,后面的会覆盖前面的。
  2. max 和 min 搞反:max 对应建立时间,min 对应保持时间。别弄混。
  3. 忽略时钟抖动:输入延迟的值,应该包含时钟抖动。但很多人忘了加。
  4. 源同步接口当系统同步处理:两种接口的约束逻辑完全不同,别混用。

嗯,关于输入延迟约束,今天就聊这么多。记住一句话:输入延迟设对了,时序分析就成功了一半。

输入延迟约束知识体系 set_input_delay 命令参数详解 系统同步 vs 源同步 最大值与最小值 -clock 指定时钟 -add_delay 选项 get_ports 端口 时钟与数据独立 时钟与数据同源 约束方式差异 -max 建立时间 -min 保持时间 负延迟处理

可可精品资料库