输入延迟约束:set_input_delay——约束输入路径的建立与保持
大家好,我是老吴。做时序约束这几年,我见过不少新手在输入延迟这块翻车。说白了,芯片工作就像接力赛——外部器件把数据递到你的芯片引脚,你得在规定时间内接住。这个“规定时间”怎么定义?就是靠 set_input_delay。
我个人习惯把它理解为一个“时间账本”:把外部器件的输出延迟、PCB走线延迟全部算到一起,告诉工具芯片外部已经花了多少时间。这样工具才能算清楚内部还有多少时间余量。
先看基本语法和参数
最简单的写法是这样的:
set_input_delay -clock clk [list 2.0] [get_ports {data_in}]
这条命令的意思:数据 data_in 相对于时钟 clk 的上升沿,到达端口时已经过去了 2.0 ns。嗯,这里要注意——这个延迟是相对于时钟沿的。
实际项目中我们很少只设一个值。因为既要做建立时间分析,也要做保持时间分析:
- -max:用于建立时间分析(最晚到达路径)
- -min:用于保持时间分析(最早到达路径)
代码如下:
set_input_delay -clock clk -max 3.5 [get_ports {addr_in}]
set_input_delay -clock clk -min 1.2 [get_ports {addr_in}]
这里 -max 和 -min 是分别指定的。为什么要分开?因为外部器件在不同工艺角下,输出延迟可能差很多。我记得之前做DDR接口时,-min只有0.8ns,-max到了4.2ns,差距大得惊人。
时钟关系:-clock 和 -clock_fall
默认情况下,set_input_delay 使用时钟的上升沿作为参考。如果你要用下降沿采样,加参数 -clock_fall:
set_input_delay -clock clk -clock_fall 1.8 [get_ports {data_in}]
在实际设计中,源同步时钟很常见——时钟和数据一起从外部芯片过来。这时候约束更复杂,你还需要同时约束时钟路径和数据路径的skew。这点后面我们再细说。
加不加 -add_delay 是个坑
当同一个端口被多个时钟驱动时(比如双沿采样),需要有两条 set_input_delay 命令。但工具默认第二条会覆盖第一条。这时候必须加上 -add_delay:
set_input_delay -clock clk_rise -max 2.0 [get_ports {dq}]
set_input_delay -clock clk_fall -max 1.5 [get_ports {dq}] -add_delay
不加 -add_delay?第一条就白写了。我曾经在一个项目里debug了半天,就是漏了这个参数,结果保持时间违规全报错了。
输入延迟约束里的建立时间和保持时间
我们来看核心:set_input_delay 怎么影响内部的建立和保持?
一张表格说清楚:
| 分析类型 | 工具计算公式 | set_input_delay 取值 |
|---|---|---|
| 内部建立时间检查 | 时钟周期 - input_delay(max) - 内部组合逻辑延迟 >= 建立时间要求 | 取 -max 值(最晚到达) |
| 内部保持时间检查 | input_delay(min) + 内部组合逻辑延迟 >= 保持时间要求 | 取 -min 值(最早到达) |
你想想看,如果 -max 设得太大,工具就会认为数据到芯片内部已经很晚了,留给内部走线的余量就少,容易报建立时间违规。如果 -min 设得太小,工具认为数据到达太早,又容易报保持时间违规。
个人经验: 一个常见的错误是直接拿外部芯片datasheet里的“最大输出延迟”作为 -max,但忽略了PCB走线延迟。走线延迟有时能到0.5ns甚至更多。我建议把PCB延迟也加上,比如- max = Tco_max + Tpcb_delay。曾经有个DDR3项目,因为少算了0.3ns的PCB走线延迟,仿真怎么都不收敛,害我加班到凌晨三点。
输入延迟的具体计算:一个完整例子
假设上一级芯片(U1)输出数据到我们芯片(U2):
- U1 的时钟频率 200MHz(周期5ns)
- U1 的 Tco_max = 2.0ns, Tco_min = 0.5ns
- PCB走线延迟(时钟 + 数据)约 0.3ns
那么我设置的输入延迟为:
set_input_delay -clock clk -max [expr 2.0 + 0.3] [get_ports {data}]
set_input_delay -clock clk -min [expr 0.5 + 0.3] [get_ports {data}]
结果是 -max = 2.3ns, -min = 0.8ns。
工具检查内部建立时间时,会用 5ns(周期)减去2.3ns,剩2.7ns给内部走线。内部保持时间检查时,用0.8ns作为基准,看看数据会不会变得太快。
注意: 如果你的时钟是内部产生的(比如PLL分频),那么输入延迟约束里的时钟必须是同一个源。我曾经见过有人用不同名字的时钟去约束,结果是工具认为时钟不同步,分析完全乱套。
关于负值输入延迟
有些场景下 input_delay 可以是负数。比如外部时钟比数据晚到,或者外部芯片采用“中心对齐”输出。负值输入延迟表示数据在时钟沿之前就已经到达。工具依然会用同样的公式计算,只是你的松弛度可能变成负数。遇到这种情况别慌,检查一下外部时序关系是不是正确理解了。
SVG图:输入延迟约束的时序账本
避坑指南:设置 input_delay 时常见的几个坑
- 忘记加 -clock 参数——工具不知道参考哪个时钟,会报错。
- -max 和 -min 设反了——工具检查保持时间时用了更大的值,导致内部保持时间余量被高估,流片出来可能触发亚稳态。
- 忽略输入路径的时钟 latency——如果外部时钟经过PLL或长走线才到芯片引脚,你的输入延迟计算也要考虑时钟偏斜。我建议用
set_clock_latency单独设置。 - 多时钟域不加 -add_delay——前面说过了,这是一个非常隐蔽的bug。
总结一句话:set_input_delay 就是把外部已经花掉的时间告诉工具。设得太松,内部可能不满足时序;设得太紧,过约束导致面积和功耗浪费。多读外部器件的datasheet,多算PCB走线,这份“时间账本”才能算对。