1. 为什么一定要写时序约束?
不用记复杂原理,简单直白说 4 点,你就知道它有多重要:
- 不写约束 = 工具自由布线:工具不知道你的时钟频率、信号要求,只会随便布线,小频率可能没事,频率一高就跑飞、出现玄学 bug,上板直接不工作;
- 专业的标志:面试、工程、竞赛里,会不会写时序约束,是区分'新手'和'专业 FPGA 工程师'的关键,也是面试官必问的基础考点;
- 提升效率:约束写得好,时序收敛更快,不用反复改代码、调布线,节省大量调试时间;
- 优化资源:合理的约束能让工具更精准地分配资源,兼顾时序和资源利用率,避免资源浪费。
2. 最核心:时钟约束(必须会,工程第一步)
几乎所有 FPGA 工程,时序约束的第一件事,就是定义主时钟——时钟是时序约束的核心,时钟约束没写对,后面全白搭。
2.1 主时钟约束(直接复制改频率,新手零门槛)
SDC 约束用 TCL 语言编写,下面是最常用的主时钟约束模板,只需修改时钟名称和周期,就能直接复制到工程里:
# 50MHz 系统时钟(工程最常用,直接复制可用)
create_clock -name clk -period 20 [get_ports clk]
# 100MHz 时钟(按需开启,注释掉的解除注释即可)
# create_clock -name clk_100m -period 10 [get_ports clk_100m]
# 200MHz 时钟(高频场景示例)
# create_clock -name clk_200m -period 5 [get_ports clk_200m]
关键要点(新手必看,别算错):
- 语法解析:-name 后面是时钟名称(见名知意,比如 clk、clk_100m),-period 后面是时钟周期(单位:ns),[get_ports clk] 是时钟对应的端口名;
- 频率换算公式(记牢,不用反复查):1000 / 时钟频率 (MHz) = 时钟周期 (ns);
- 示例对照:50MHz → 1000/50 = 20ns,100MHz → 10ns,200MHz → 5ns,按需替换即可;
- 多时钟场景:如果工程有多个时钟(比如 50MHz 主时钟 + 100MHz 外设时钟),分别写对应的 create_clock 语句即可。
3. 复位与异步信号约束(减少无效报错,工程必备)
FPGA 工程里的复位信号(比如 rst_n),大多是异步复位、同步释放,这类信号不需要做时序检查,加上下面这句约束,能减少大量无效的时序报错,让时序报表更干净。
# 复位设为异步,不做时序检查(直接复制可用)
set_false_path -from [get_ports rst_n]
作用解析(新手易懂):
- set_false_path:中文意思是'设置伪路径',告诉工具'这条路径不用做时序检查';
- -from [get_ports rst_n]:指定伪路径的起点是复位端口 rst_n,即所有从 rst_n 出发的路径,都不做时序检查;
- 适配场景:不管是低电平有效复位(rst_n),还是高电平有效复位(rst),只需替换端口名(比如把 rst_n 改成 rst),约束写法不变。
4. 输入 / 输出延时约束(常用简化版,新手直接用)
输入输出延时约束,是针对工程中的外部输入信号(比如 data_i)、外部输出信号(比如 data_o)的约束,用于匹配外部芯片的时序。下面是工程里最常用、最简单的简化版写法,新手不用纠结复杂的理论,先写上,时序会立刻干净很多,后续再根据实际需求微调。
# 输入延时(示例:数据相对时钟滞后 5ns,直接复制可用)
set_input_delay -max 5 -clock clk [get_ports data_i*]
# 输出延时(示例:数据相对时钟超前 5ns,直接复制可用)
set_output_delay -max 5 -clock clk [get_ports data_o*]
关键要点(新手必看):
- 语法解析:-max 后面是最大延时(单位:ns),-clock clk 是指定参考时钟(和前面定义的主时钟名称一致),[get_ports data_i*] 是指定约束的端口;
- 端口匹配:data_i* 表示'所有以 data_i 开头的输入端口'(比如 data_i0、data_i1、data_i[7:0]),data_o* 同理,不用逐个端口写约束,简化操作;
- 新手技巧:5ns 是工程常用默认值,先按这个写,后续如果时序报错,再根据外部芯片 datasheet 调整延时数值;
- 参考时钟:如果输入输出信号对应其他时钟(比如 clk_100m),只需把 -clock 后面的 clk 改成对应的时钟名称即可。

