本节摘要:约束文件是写给时序工具的"设计意图说明书"。本节讲最常用的三类约束——主时钟周期、输入输出延迟、伪路径例外,逐条解释物理含义与书写方法,并给出单时钟设计的完整约束模板。
综合器把设计变成电路后,静态时序分析器要回答一个问题:每个触发器到下一个触发器的路径,能不能在一个时钟周期内走完?要回答它,分析器至少需要知道周期是多少——这正是约束提供的。不加约束,工具要么不分析、要么按默认频率敷衍分析,报告一片"绿色"的假象;板子一上电,真实时钟下的违例立刻现形。约束的首要作用由此而来:声明时钟,激活真实的时序分析。
约束的通用语法是 SDC 风格(各厂商工具均兼容或近似),三条最常用命令:
# 主时钟:周期 20 ns(50 MHz),占空比 50%,接到 clk 端口 create_clock -period 20.000 -name sys_clk -waveform {0.000 10.000} [get_ports clk] # 输入延迟:上游器件数据在时钟沿后最多 6 ns 到达输入端口 set_input_delay -clock sys_clk -max 6.0 [get_ports {a b op}] # 输出延迟:下游器件需要至少 4 ns 的数据建立时间 set_output_delay -clock sys_clk -max 4.0 [get_ports {y}]
逐条读物理含义。create_clock 声明板上真实时钟:周期、波形、挂在哪个端口。此后分析器以此为节拍基准做全芯片分析。set_input_delay 描述外部世界的慢:输入信号不会踩着时钟沿精确到达,它在沿后最多六纳秒才稳定,这六纳秒要从本芯片的周期预算里扣掉。set_output_delay 同理描述下游的要求:本芯片输出必须在时钟沿前四纳秒就稳定,供下游采样。输入输出延迟合起来,把"板级互联"纳入了芯片时序分析——没有它们,端口路径不被分析,板级偶发错误无从预警。

真实设计里有几类路径天生不需要单周期检查,硬查反而虚报违例。伪路径(false path)最常见:
# 异步输入经过两级同步器后进入主时钟域,同步器本身不查时序 set_false_path -to [get_cells {sync_ff1_reg*/D}] # 复位信号异步释放,不参与建立检查 set_false_path -from [get_ports rst]
两级同步器(输入打两拍)是跨时钟域的标准防线,它的第一级触发器可能进入亚稳态,时序本来就"管不着"——标成伪路径,分析器不再用主时钟苛求它。多周期路径(multicycle)是另一类例外:某些数据两拍才更新一次,单周期检查过于严苛,用 set_multicycle_path 放宽一拍。例外约束的原则:每条例外都要有明确的物理理由,并在注释里写清——例外是免检通道,开多了报告全绿但板子翻车,比不查更危险。
入门阶段的绝大多数设计是"单时钟 + 若干异步输入 + 若干输出",约束模板可以固化:
# 时钟声明(按板上晶振改周期) create_clock -period 20.000 -name sys_clk [get_ports clk] # 异步输入:不进时序分析核心,靠两级同步器进域 set_false_path -from [get_ports {rst_n key_in}] # 常规输入输出延迟 set_input_delay -clock sys_clk -max 6.0 [get_ports {a b op}] set_output_delay -clock sys_clk -max 4.0 [get_ports {y seg}]
四段结构:时钟、异步例外、输入延迟、输出延迟。约束文件随设计代码一起版本管理,改时钟周期时只动第一段。这个模板能覆盖到本教程第 8 章上板前的全部需要;未来接触多时钟域(DDR、高速接口)再扩展生成时钟、时钟组隔离等高级约束。
⚠️ 常见坑:约束里的端口名与实体端口对不上(改名后忘同步),get_ports 匹配不到对象,工具静默跳过——约束形同虚设。每次跑完约束,检查日志里"已应用约束数"与预期一致;工具一般会警告未匹配的对象,这条警告同样必须清零。
💡 关键直觉:约束是设计规格的一部分,不是工具的配置杂务。"这个设计跑 50 兆、输入最慢六纳秒、输出要提前四纳秒稳定"——这些话写给工具,才有人替你全芯片核查一遍。
# 1. 创建时钟 create_clock -name clk_100m -period 10 [get_ports clk] # 2. 输入延迟(数据相对于时钟的到达时间) set_input_delay -clock clk_100m -max 4.0 [get_ports data_in*] set_input_delay -clock clk_100m -min 1.0 [get_ports data_in*] # 3. 输出延迟(数据相对于时钟的输出时间) set_output_delay -clock clk_100m -max 5.0 [get_ports data_out*] set_output_delay -clock clk_100m -min 0.5 [get_ports data_out*] # 4. 伪路径(不需时序分析的路径) set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
前 3 个定义时钟域和接口时序,第 4 个排除跨时钟域(CDC)路径——CDC 需要专门的同步器处理,约束工具不知道。
忘记给新时钟 create_clock——Vivado 报告"no clock found",综合器把整个模块当异步,结果不可预期。
只约束主时钟不约束衍生时钟(PLL/MMCM 输出)——衍生时钟频率不固定,工具会做最坏估计,可能误报时序违例。
set_false_path 或 set_max_delay -datapath_onlyreport_timing_summary 看总体时序