2、时序约束入门:创建时钟、输入延迟、输出延迟、伪路径约束

各位同学,今天咱们来聊聊时序约束。说实话,我刚入行那会儿,觉得时序约束就是个形式——把SDC文件往那一扔,工具跑通就行了。直到有一次,我做一个1080p的HDMI输出项目,板子调通了,画面也能显示,但就是偶尔闪一下。折腾了三天,最后发现是时钟约束没写对,工具把一条关键路径优化错了方向。嗯,从那以后,我再也不敢小看这几行命令了。

2.1 创建时钟——一切时序分析的起点

时序分析说白了,就是检查你的数据能不能在时钟边沿之间稳定传输。所以第一步,你得告诉工具:时钟长什么样。

最基本的时钟约束是 create_clock。我个人习惯这样写:

create_clock -name clk_sys -period 10.000 [get_ports clk_in]

这条命令的意思是:在端口 clk_in 上创建一个周期为10ns的时钟,名字叫 clk_sys。10ns对应100MHz,这是最常见的基准频率。

你可能会问:为什么非要给时钟起个名字?直接写端口名不行吗?其实也可以,但我在项目中吃过亏——当你有多个时钟域时,工具默认用端口名作为时钟名,一旦你改了端口名,所有约束都得跟着改。所以我的建议是:永远给时钟起一个有意义的名字。

重要原则:时钟周期要留余量。比如你的目标频率是100MHz,我建议按110MHz(9.09ns)来约束。为什么?因为PCB走线、温度变化、芯片老化都会让时序变差。留10%的余量,是我个人比较稳妥的做法。

还有一种情况是生成时钟,比如用PLL倍频出来的时钟:

create_generated_clock -name clk_pll -source [get_pins pll_inst/clk_in] \
    -divide_by 1 -multiply_by 2 [get_pins pll_inst/clk_out]

这里要注意:-source 要指向PLL的输入时钟引脚,而不是输出引脚。我见过有人写反了,结果时序分析完全乱套。

2.2 输入延迟——告诉工具数据从哪来

数据从芯片外部进来时,工具不知道它相对于时钟是什么时候到达的。你得用 set_input_delay 告诉它。

举个例子,一个外部ADC在时钟上升沿之后5ns输出数据:

set_input_delay -clock clk_sys -max 5.0 [get_ports data_in*]

这里 -max 表示最大延迟,对应最差情况。你想想看,如果外部器件的最差输出延迟是5ns,那FPGA内部留给数据建立的时间就只剩5ns了(周期10ns减去5ns)。

小技巧:输入延迟的值怎么定?看外部器件的datasheet。一般会给出 t_co(时钟到输出延迟)或者 t_su(建立时间要求)。把这些值加上PCB走线延迟,就是你的输入延迟。

我曾经在一个项目中,外部器件的datasheet写的是「最大4.5ns」,我直接用了4.5。结果板子回来发现,PCB走线长了2cm,算下来延迟多了0.4ns。虽然没出大问题,但余量几乎没了。所以我的建议是:PCB走线延迟一定要算进去,每英寸大约0.15ns。

2.3 输出延迟——告诉工具数据要去哪

输出延迟和输入延迟是对称的。它告诉工具:FPGA输出数据后,外部器件需要多长时间来采样。

set_output_delay -clock clk_sys -max 4.0 [get_ports data_out*]

这个4.0ns怎么来的?假设外部器件要求数据在时钟边沿之前2ns就稳定(建立时间2ns),再加上PCB走线延迟2ns,那输出延迟就是4ns。

说白了,输出延迟就是「外部器件需要的时间 + 走线时间」。工具会根据这个值反推:FPGA内部必须在时钟边沿之前多久把数据送到输出引脚。

注意:输出延迟的 -max 和 -min 都要设。-max 对应建立时间检查,-min 对应保持时间检查。我见过有人只设了 -max,结果保持时间违例了,工具也不报错——因为没约束。

2.4 伪路径约束——别让工具白费力气

什么叫伪路径?就是那些不需要做时序分析的路径。比如跨时钟域的信号,或者复位信号。

举个例子,两个异步时钟域之间传数据:

set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

这条命令告诉工具:从 clk_a 域到 clk_b 域的路径,你不用分析。因为这两个时钟没有确定的相位关系,分析也是白分析。

还有一种情况是复位信号:

set_false_path -to [get_pins *rst_n_reg*]

复位信号通常异步,不需要做时序收敛。但要注意:别把同步复位也设成伪路径。我见过有人图省事,把所有带 rst 的信号都设成伪路径,结果同步复位路径的时序完全没检查,板子跑起来偶尔复位失败。

核心原则:伪路径要精确。能用 -from 和 -to 指定具体路径,就别用通配符。我曾经接手一个项目,前工程师写了个 set_false_path -from [all_inputs],结果所有输入路径都不分析了——这等于把输入延迟约束全废了。

2.5 知识体系总览

下面这张图是我自己总结的时序约束入门框架,你一看就明白:

时序约束入门知识体系 时序约束 创建时钟 输入延迟 输出延迟 伪路径约束 主时钟 生成时钟 -max / -min 参考时钟 建立/保持 PCB走线 跨时钟域 复位信号 四种约束类型,覆盖90%的时序约束场景

2.6 实战建议

最后,我分享几个实战中的小习惯:

  • 先写时钟,再写IO延迟——工具需要先知道时钟,才能分析IO路径。顺序错了,工具会报warning。
  • 伪路径要写注释——比如 # 异步FIFO跨时钟域,不需要时序分析。半年后你回头看代码,会感谢自己的。
  • 用 report_timing 验证——写完约束跑一遍 report_timing -max_paths 10,看看最差路径是不是你预期的。

我的经验:刚开始学约束时,别追求一次写对。先写一个基础版本,跑时序分析,看哪些路径违例,再逐步加约束。这样你才能理解每条约束的作用。

好了,这一章的内容就到这里。时序约束入门其实不难,关键是理解每条命令背后的物理意义。下一章咱们聊聊更进阶的约束技巧——多周期路径和时钟分组。

可可精品资料库