第四章:输入延迟约束——set_input_delay命令,源同步与系统同步接口的约束

各位同学,今天我们来聊聊输入延迟约束。说实话,这个知识点在时序约束里算是比较绕的。我刚开始做FPGA那会儿,看到set_input_delay就头疼——到底该设多大?正数还是负数?源同步和系统同步有啥区别?

嗯,今天咱们就把这些事掰扯清楚。

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

先想一个问题:芯片外部来的数据,FPGA内部怎么知道它什么时候到?

你想想看,数据从外部芯片发出,经过PCB走线,再进入FPGA的IOB,这中间是有延迟的。如果不告诉工具这个延迟是多少,它怎么保证内部触发器能正确采到数据?

说白了,set_input_delay就是告诉综合工具:外部数据相对于时钟边沿,到底早到还是晚到,早/晚了多少。

核心概念:输入延迟 = 外部芯片的Tco + PCB走线延迟

4.2 set_input_delay命令详解

命令格式其实不复杂,我直接给个典型用法:

# 系统同步接口示例
set_input_delay -clock clk_sys -max 5.0 [get_ports data_in]
set_input_delay -clock clk_sys -min 2.0 [get_ports data_in]

# 源同步接口示例(DDR)
set_input_delay -clock clk_ddr -max 3.5 [get_ports ddr_data]
set_input_delay -clock clk_ddr -min 1.5 [get_ports ddr_data]
set_input_delay -clock clk_ddr -max 3.5 [get_ports ddr_data] -add_delay
set_input_delay -clock clk_ddr -min 1.5 [get_ports ddr_data] -add_delay

这里有几个关键参数,我挨个说:

  • -clock:指定参考时钟。注意,这个时钟是FPGA内部的,不是外部时钟
  • -max/-min:最大/最小延迟。max用于建立时间分析,min用于保持时间分析
  • -add_delay:源同步DDR接口必须加!不加的话,第二个边沿的约束会覆盖第一个

我的习惯:写约束时,我总喜欢把max和min分开写两行。虽然可以写在一行,但分开更清晰,后期调试也方便。

4.3 系统同步 vs 源同步

这两个概念,我当年花了很长时间才真正理解。咱们用一张图来说明:

系统同步 vs 源同步接口 系统同步接口 外部芯片 FPGA 时钟源 时钟和数据 分别走线 源同步接口 外部芯片 FPGA 数据 时钟 时钟和数据 一起走线 ⇄

看到区别了吗?

  • 系统同步:时钟和数据是分开走的。外部芯片用自己的时钟发数据,FPGA用同一个时钟源(但经过不同路径)来采数据。这时候,时钟偏斜(clock skew)是个大问题。
  • 源同步:时钟和数据一起从外部芯片发过来。FPGA直接用这个随路时钟来采数据。DDR、SDRAM、SPI Flash基本都是这种。

注意:源同步接口的约束,一定要搞清楚是中心对齐还是边沿对齐。DDR一般是边沿对齐,SDR可能是中心对齐。搞反了,时序分析全错。

4.4 实战:计算输入延迟值

好,理论说完了,咱们来点实际的。假设有个外部ADC,数据手册上写着:

参数 最小值 最大值
Tco (时钟到数据输出) 1.0 ns 3.0 ns
PCB走线延迟 0.5 ns 1.0 ns

那么输入延迟就是:

max_input_delay = 3.0 + 1.0 = 4.0 ns
min_input_delay = 1.0 + 0.5 = 1.5 ns

对应的约束:

set_input_delay -clock clk_adc -max 4.0 [get_ports adc_data*]
set_input_delay -clock clk_adc -min 1.5 [get_ports adc_data*]

避坑指南:我曾经遇到一个项目,PCB走线延迟算错了。因为数据手册里Tco是相对于时钟上升沿的,但PCB走线我忘了算过孔延迟。结果板子调了三天,最后发现是约束写错了。所以,PCB走线延迟一定要算上过孔和连接器。

4.5 源同步接口的特殊处理

源同步接口,尤其是DDR,有个坑:数据在时钟的上升沿和下降沿都有变化。这时候需要用到-add_delay参数。

我直接给个DDR3接口的例子:

# 上升沿
set_input_delay -clock clk_ddr -max 2.5 [get_ports dq[*]] -add_delay
set_input_delay -clock clk_ddr -min 1.0 [get_ports dq[*]] -add_delay

# 下降沿(相对于时钟的负沿)
set_input_delay -clock clk_ddr -max 2.5 [get_ports dq[*]] -clock_fall -add_delay
set_input_delay -clock clk_ddr -min 1.0 [get_ports dq[*]] -clock_fall -add_delay

注意看,-clock_fall表示这个约束是针对时钟下降沿的。不加-add_delay的话,第二个set_input_delay会覆盖第一个。

4.6 常见错误与调试

我总结几个新手容易犯的错:

  1. 忘记加-add_delay:源同步DDR接口,不加这个参数,工具只认最后一个约束
  2. 时钟选错了:系统同步接口,一定要选FPGA内部的时钟,不是外部时钟
  3. max和min搞反:max用于建立时间,min用于保持时间。max越大越难满足建立时间,min越小越难满足保持时间
  4. 单位搞错:Vivado默认单位是ns,但有些IP核用ps。我建议统一用ns,省得出错

调试技巧:写完约束后,在Vivado里打开Timing Report,看看Path Type是Input还是Output。如果是Input,再看Slack是正还是负。负的就要调整约束值或者改PCB走线了。

嗯,关于输入延迟约束,今天就讲这么多。记住一句话:set_input_delay不是猜出来的,是算出来的。数据手册的Tco加上PCB走线延迟,一步都不能少。

下一章咱们讲输出延迟约束,到时候你会发现,其实和输入延迟是对称的。理解了输入,输出就简单了。


可可精品资料库