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 知识体系总览
下面这张图是我自己总结的时序约束入门框架,你一看就明白:
2.6 实战建议
最后,我分享几个实战中的小习惯:
- 先写时钟,再写IO延迟——工具需要先知道时钟,才能分析IO路径。顺序错了,工具会报warning。
- 伪路径要写注释——比如
# 异步FIFO跨时钟域,不需要时序分析。半年后你回头看代码,会感谢自己的。 - 用
report_timing验证——写完约束跑一遍report_timing -max_paths 10,看看最差路径是不是你预期的。
我的经验:刚开始学约束时,别追求一次写对。先写一个基础版本,跑时序分析,看哪些路径违例,再逐步加约束。这样你才能理解每条约束的作用。
好了,这一章的内容就到这里。时序约束入门其实不难,关键是理解每条命令背后的物理意义。下一章咱们聊聊更进阶的约束技巧——多周期路径和时钟分组。