4. 输入输出延迟约束实战
在完成时钟约束之后,I/O 时序约束是时序收敛中最容易被忽视、也最容易出错的一环。FPGA 内部逻辑的时序由时钟周期和路径延迟决定,而接口时序则取决于外部器件的建立/保持要求、PCB 走线延迟以及 FPGA 引脚到内部寄存器的延迟。set_input_delay 和 set_output_delay 的本质,就是把"外部世界"的时序要求翻译成 FPGA 内部可以检查的约束。本节围绕三类典型接口——系统同步、源同步和 DDR 接口,说明约束的计算方法和板级参数的提取思路。
一、先明确参照系:延迟是相对哪个时钟沿定义的
set_input_delay 和 set_output_delay 的数值,都是相对于某个参考时钟沿而言的。这个参考时钟通常是接口时钟,通过 create_clock 或 create_generated_clock 定义在 FPGA 的时钟输入引脚或恢复时钟上。约束中的 -clock 参数指定参考时钟,-max/-min 分别对应建立和保持检查。理解这一点非常关键:如果参考时钟定义错了,延迟数值再精确也没有意义。
另一个必须统一的约定是延迟的正负号。Vivado 中,set_input_delay 表示数据到达 FPGA 引脚相对于参考时钟沿的延迟;set_output_delay 表示数据从 FPGA 引脚出发后,到达外部器件捕获沿之前所需的延迟。正号表示数据在时钟沿之后到达(或需要更晚发出),负号表示数据在时钟沿之前到达。全文和图示都遵循这一约定。
二、系统同步接口:延迟来自外部器件的 Tco 与走线
系统同步接口的特点是:发送端和接收端使用同一个系统时钟,时钟在板上以独立走线分发。以 FPGA 作为接收端为例,外部器件在时钟沿之后经过 Tco 输出数据,数据再经过 PCB 走线到达 FPGA 引脚。因此:
set_input_delay -max = Tco_max + Tpcb_max
set_input_delay -min = Tco_min + Tpcb_min
假设外部器件手册给出 Tco 范围为 1.5 ns 到 3.2 ns,PCB 走线延迟经提取为 0.4 ns 到 0.6 ns,则 -max 为 3.8 ns,-min 为 1.9 ns。这两个值分别用于建立和保持检查。如果 FPGA 是发送端,则 set_output_delay 的数值来自外部器件的建立时间 Tsu 和保持时间 Th:
set_output_delay -max = Tsu_ext + Tpcb_max
set_output_delay -min = -Th_ext + Tpcb_min
注意保持时间对应的 -min 通常为负值,因为外部器件要求数据在时钟沿之后还要保持一段时间,相当于要求 FPGA 更早发出数据。系统同步接口的板级参数主要来自器件数据手册和 PCB 走线延迟,走线延迟可用 IBIS 仿真或经验值(约 6~7 ps/mm)估算。
三、源同步接口:时钟随数据一起传输
源同步接口中,时钟由发送端随数据一起发出,接收端用这个随路时钟采样数据。此时参考时钟不再是系统时钟,而是随路时钟。以 FPGA 接收 DDR 源同步数据为例,数据在随路时钟的两个边沿都可能变化,因此需要分别约束上升沿和下降沿:
set_input_delay -clock [get_clocks rx_clk] -max 1.2 [get_ports data_in*]
set_input_delay -clock [get_clocks rx_clk] -min 0.3 [get_ports data_in*]
set_input_delay -clock [get_clocks rx_clk] -max 1.2 [get_ports data_in*] -clock_fall -add_delay
set_input_delay -clock [get_clocks rx_clk] -min 0.3 [get_ports data_in*] -clock_fall -add_delay
源同步接口的关键板级参数是数据与时钟之间的偏斜(skew)。这个偏斜包括发送端输出偏斜、PCB 走线长度差以及接收端引脚延迟差。实际提取时,应测量数据走线与时钟走线的长度差,再乘以单位长度延迟。例如数据线比时钟线长 8 mm,按 6.5 ps/mm 计算,偏斜约 52 ps。这个偏斜要分别叠加到 -max 和 -min 上。
四、DDR 接口:双边沿采样与更严格的偏斜控制
DDR 接口在时钟的上升沿和下降沿都传输数据,因此有效数据窗口只有半个时钟周期。约束时必须同时覆盖两个边沿,并且 -max 和 -min 的差值直接决定了数据有效窗口。以 FPGA 输出 DDR 数据为例:
set_output_delay -clock [get_clocks ddr_clk] -max 0.8 [get_ports dq*]
set_output_delay -clock [get_clocks ddr_clk] -min -0.2 [get_ports dq*]
set_output_delay -clock [get_clocks ddr_clk] -max 0.8 [get_ports dq*] -clock_fall -add_delay
set_output_delay -clock [get_clocks ddr_clk] -min -0.2 [get_ports dq*] -clock_fall -add_delay
DDR 接口的板级参数提取比 SDR 更严格:除了走线延迟,还要考虑 DQS 与 DQ 之间的偏斜、FPGA 内部 PHY 的延迟以及外部存储器的建立/保持窗口。实际项目中,通常先用 IBIS 仿真得到引脚处的时序,再反推约束值。如果约束值与仿真结果偏差超过 100 ps,就需要重新检查走线模型或参考时钟定义。
五、约束编写中的常见陷阱
第一,参考时钟定义错误。源同步接口如果误用系统时钟作为参考,约束会完全失效。第二,忘记 -clock_fall。DDR 和双边沿接口必须为下降沿单独添加约束,否则下降沿路径不会被检查。第三,-max 和 -min 写反。建立检查用 -max,保持检查用 -min,写反会导致时序报告出现虚假违例或漏报。第四,没有使用 -add_delay。当同一端口需要同时约束上升沿和下降沿时,必须加 -add_delay,否则后一条约束会覆盖前一条。
六、一个可复用的参数提取记录表
为了让约束值可追溯、可复核,建议为每个接口建立一份参数提取记录。以下是一个示例结构,字段可根据项目实际情况调整:
| 参数 | 来源 | 数值 | 单位 | 备注 |
|---|---|---|---|---|
| Tco_max | 器件手册 | 3.2 | ns | 最坏值 |
| Tco_min | 器件手册 | 1.5 | ns | 最坏值 |
| Tpcb_max | PCB 走线 | 0.6 | ns | 长度 × 6.5 ps/mm |
| Tpcb_min | PCB 走线 | 0.4 | ns | 长度 × 6.5 ps/mm |
| set_input_delay -max | 计算 | 3.8 | ns | Tco_max + Tpcb_max |
| set_input_delay -min | 计算 | 1.9 | ns | Tco_min + Tpcb_min |
这张表的价值在于:当接口出现时序违例时,可以快速定位是器件参数、走线参数还是约束写法出了问题。同时,它也是团队交接和设计复用的重要依据。约束不是一次写对就结束的工作,而是需要随着板级参数更新、器件替换和时钟方案调整持续维护的工程资产。