4. 输出延迟约束:set_output_delay命令、最大/最小输出延迟、与外部器件的时序关系
输出延迟约束,说白了就是告诉工具:芯片往外送数据的时候,外部器件到底能等多长时间。我刚开始做这个的时候,总觉得只要把内部时序调好就行了,结果第一次板级联调,数据就是采不对。后来才明白,输出延迟没设对,芯片再快也是白搭。
4.1 set_output_delay 命令详解
这个命令的语法其实不复杂,但参数多了容易搞混。我个人习惯先把外部器件的时序图拿过来,再对着写约束。
set_output_delay -clock clk -max 5.0 [get_ports data_out]
set_output_delay -clock clk -min 2.0 [get_ports data_out]
这里有几个关键参数:
- -clock:指定参考时钟,就是外部器件用哪个时钟采样数据
- -max:最大输出延迟,对应外部器件的建立时间要求
- -min:最小输出延迟,对应外部器件的保持时间要求
- -rise/-fall:分别约束上升沿和下降沿,差分信号常用
嗯,这里要注意:set_output_delay 的值是相对于时钟边沿的。正值表示数据在时钟边沿之后到达,负值表示在时钟边沿之前到达。我在项目中遇到过有人把正负搞反了,结果仿真全对,上板全错。
4.2 最大输出延迟 vs 最小输出延迟
这两个概念,说白了就是外部器件对数据到达时间窗口的要求。
| 类型 | 含义 | 对应外部器件参数 | 约束方向 |
|---|---|---|---|
| 最大输出延迟 | 数据从时钟边沿到输出的最晚时间 | 建立时间(Tsu) | 数据必须早于下一个时钟边沿到达 |
| 最小输出延迟 | 数据从时钟边沿到输出的最早时间 | 保持时间(Th) | 数据不能太快改变 |
举个例子你就明白了。假设外部器件要求数据在时钟上升沿前 3ns 稳定,那么最大输出延迟就是时钟周期减去 3ns。如果时钟是 10ns,那最大输出延迟就是 7ns。我曾经遇到一个案例,外部器件的保持时间要求是 1ns,但最小输出延迟设成了 0.5ns,结果数据变化太快,外部器件根本采不到正确的值。
核心公式:
最大输出延迟 = Tcycle - Tsu_external
最小输出延迟 = Th_external
其中 Tcycle 是时钟周期,Tsu_external 和 Th_external 是外部器件的建立/保持时间。
4.3 与外部器件的时序关系
这部分我建议你画个时序图来理解。我自己每次做输出约束,都会先画一个简单的波形图。
我的小技巧:把外部器件的 datasheet 时序图截图下来,然后在上面标注我们的输出延迟范围。这样一眼就能看出约束是否合理。
外部器件通常有两种采样方式:
- 中心对齐采样:数据在时钟边沿的中间位置变化,外部器件在时钟边沿采样。这种模式下,输出延迟通常设为时钟周期的一半左右。
- 边沿对齐采样:数据在时钟边沿变化,外部器件在下一个时钟边沿采样。这种模式对输出延迟的要求更严格。
我做过一个 DDR 接口的项目,外部存储器要求数据在时钟上升沿和下降沿都采样。那时候我用了 -rise 和 -fall 参数分别约束,还加了 -add_delay 防止覆盖。你想想看,如果只设了一个边沿的约束,另一个边沿的时序就完全没检查到,那流片回来肯定出问题。
警告:当使用多时钟域或双边沿采样时,一定要用 -add_delay 参数。否则后面的 set_output_delay 会覆盖前面的设置。
4.4 知识体系结构图
下面这张图是我自己总结的输出延迟约束的核心逻辑,你可以对照着理解:
4.5 实战中的避坑指南
我曾经在一个项目中,外部器件的 datasheet 上写的是建立时间 2ns,保持时间 1.5ns。我直接按这个值算了输出延迟,结果时序分析报了一堆 violation。后来仔细一看,原来外部器件用的是负沿采样,而我默认用了正沿。所以这里要提醒你:
- 确认采样沿:外部器件是上升沿还是下降沿采样?这直接影响延迟计算
- 考虑 PCB 延迟:输出延迟约束里要不要包含 PCB 走线延迟?我个人习惯是单独约束,不混在一起
- 检查时钟抖动:如果时钟质量不好,输出延迟要留余量。我一般会加 10% 的余量
- 多 bit 总线:对于多 bit 输出,要检查 skew。我曾经因为没约束 bus skew,导致数据在接收端出现毛刺
总结一下:输出延迟约束的核心就是理解外部器件的时序要求,然后准确翻译成 set_output_delay 命令。多花点时间看 datasheet 的时序图,比盲目调约束值要有效得多。
好了,输出延迟约束就讲到这里。记住一句话:约束不是写给别人看的,是写给工具看的,更是写给流片回来的芯片看的。设错了,板子会告诉你答案——只不过那时候已经晚了。