4、输入输出延迟约束:set_input_delay/set_output_delay、虚拟时钟、系统同步约束

好,咱们今天聊点硬核的——输入输出延迟约束。

做误码测试,说白了就是看数据从发出去到收回来,到底有没有出错。但这里有个关键问题:你芯片管脚上的信号,跟内部时钟到底是什么关系?

我刚开始做FPGA通信接口时,就吃过这个亏。板子调通了,误码率却一直下不去。折腾了两周,最后发现是约束没写对,芯片内部采样的时机偏了半个周期。嗯,从那以后,我对IO延迟约束再也不敢马虎了。

4.1 为什么需要输入输出延迟约束?

你想想看,芯片内部是理想世界——时钟走线延迟、门延迟,工具都能算清楚。但芯片外部呢?PCB走线、连接器、对端器件的输出特性……这些工具一概不知。

所以,我们必须告诉工具:

  • 数据从芯片外部进来时,相对于时钟,到底晚了多久?
  • 数据从芯片内部出去时,对端器件希望它什么时候稳定?

这就是 set_input_delay 和 set_output_delay 存在的意义。

核心思想: 把外部延迟“折算”到芯片内部,让STA工具能统一分析时序。

4.2 set_input_delay —— 告诉工具数据什么时候来

先看输入延迟。假设外部器件在时钟上升沿发送数据,经过PCB走线,数据到达FPGA管脚。

这个“到达时间”相对于时钟沿,就是 set_input_delay。

# 数据在时钟上升沿之后 2ns 到达
set_input_delay -clock clk -max 2.0 [get_ports data_in]

# 数据在时钟上升沿之前 0.5ns 到达(负值表示提前)
set_input_delay -clock clk -min -0.5 [get_ports data_in]

这里有个细节——为什么分 max 和 min?

max 对应建立时间检查,数据来得越晚越危险。min 对应保持时间检查,数据来得越早越危险。我在项目中见过有人只写一个值,结果保持时间违例了都不知道。

我的习惯: 输入延迟的 max 值,通常取 PCB 走线最大延迟 + 对端器件最大 Tco。min 值取 PCB 走线最小延迟 + 对端器件最小 Tco。别偷懒,两个都要写。

4.3 set_output_delay —— 告诉工具数据什么时候要

输出延迟正好反过来。芯片内部把数据送到管脚,外部器件在时钟沿采样。这个“外部器件需要的建立/保持时间”,就是 set_output_delay。

# 外部器件需要数据在时钟沿之前 1ns 建立
set_output_delay -clock clk -max 1.0 [get_ports data_out]

# 外部器件需要数据在时钟沿之后保持 0.5ns
set_output_delay -clock clk -min -0.5 [get_ports data_out]

注意看 min 值是负的。为什么?因为外部器件的保持时间要求,折算到FPGA内部,相当于数据要“晚一点”变化。嗯,这个逻辑有点绕,但多想想就通了。

我曾经踩过的坑: 输出延迟的 min 值写成了正数,结果综合出来的电路面积大了30%。后来发现是约束方向搞反了。记住:set_output_delay -min 对应的是外部器件的保持时间,通常取负值。

4.4 虚拟时钟 —— 处理跨时钟域的好帮手

有时候,输入数据的参考时钟并不是FPGA内部那个。比如,外部器件用 100MHz 时钟发数据,但FPGA内部用 200MHz 时钟处理。

这时候就需要虚拟时钟了。

# 创建一个虚拟时钟,不连任何管脚
create_clock -name virt_clk -period 10.0

# 用虚拟时钟做输入延迟约束
set_input_delay -clock virt_clk -max 2.0 [get_ports data_in]

虚拟时钟说白了就是一个“假时钟”,只用来描述外部时序关系,不参与内部逻辑。我习惯用它来处理异步接口,比如 SPI、I2C 这类协议。

关键点: 虚拟时钟的周期和相位,必须跟外部器件的实际时钟一致。否则约束就是错的,工具分析出来的结果毫无意义。

4.5 系统同步约束 —— 让整个系统对齐

误码测试里,经常有多个芯片协同工作。比如,发送端、接收端、时钟源,三者之间需要同步。

系统同步约束,就是把这些外部关系“翻译”成工具能理解的语言。

# 假设系统时钟从外部输入,经过 PLL 产生内部时钟
create_clock -name sys_clk -period 10.0 [get_ports clk_in]

# 约束 PLL 输出时钟与输入时钟的关系
set_clock_groups -asynchronous -group [get_clocks sys_clk] \
                                -group [get_clocks pll_out_clk]

这里要注意:如果两个时钟是同步的(同源),就别设成异步。我见过有人把所有跨时钟域都设成异步,结果时序分析漏掉了真正的路径。

4.6 知识体系总览

下面这张图,是我自己总结的输入输出延迟约束的核心逻辑。你看一眼,应该就能明白整体脉络。

输入输出延迟约束知识体系 set_input_delay set_output_delay 虚拟时钟 max: 建立时间检查 min: 保持时间检查 参考时钟:内部或虚拟 max: 外部建立时间 min: 外部保持时间(负值) 参考时钟:内部时钟 不连任何物理管脚 描述外部时序关系 用于异步接口约束 系统同步约束 时钟同源关系 异步时钟分组 目标:让STA工具准确分析芯片管脚到内部寄存器的时序路径

4.7 实战中的几个要点

最后,分享几个我在误码测试项目中总结的经验:

  1. 先量后设 —— 别凭感觉写延迟值。用示波器实测PCB走线延迟,或者查数据手册拿到对端器件的Tco和Tsu/Th。
  2. 双向口要小心 —— 同一个管脚既做输入又做输出?那就得分别约束,而且要用 -clock 区分方向。
  3. 多跑几次STA —— 改一次约束就跑一次时序分析。我习惯在综合后和布局布线后各跑一次,确保没漏掉什么。
  4. 别过度约束 —— 有人为了保险,把延迟值设得特别大。结果工具为了满足约束,拼命加buffer,面积和功耗都上去了。适可而止。

一个小技巧: 如果你不确定输入延迟该设多少,可以先设一个宽松的值(比如周期的一半),跑通功能后再逐步收紧。这样调试起来更从容。

好了,关于输入输出延迟约束,今天就聊到这儿。这些内容看起来琐碎,但每一条都直接关系到误码测试的成败。下次你写约束文件的时候,不妨想想我今天说的这些——尤其是那个负值的坑,别再踩了。