Quick Start
- 打开 Vivado 2023.2,创建新工程,选择器件(如 xc7a35tcsg324-1)。
- 添加顶层 RTL 文件(如 top.v),包含输入/输出端口。
- 创建约束文件(top.xdc),写入 set_input_delay 和 set_output_delay 约束。
- 运行综合(Synthesis),检查时序报告中的 Input Setup/Hold 和 Output Setup/Hold 路径。
- 运行实现(Implementation),查看时序收敛情况。
- 若时序违例,调整约束值或修改 RTL 逻辑,重新综合/实现。
- 观察时序报告中的 slack 为正,表示约束有效。
- 可选:用仿真验证 I/O 时序与约束一致。
预期结果:综合/实现后,时序报告显示 Input Delay 和 Output Delay 路径 slack ≥ 0。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a35tcsg324-1) | 常用入门级 FPGA,时序约束通用 | 其他 7 系列 / Ultrascale 器件 |
| EDA 版本 | Vivado 2023.2 | 支持 SDC 约束,时序引擎成熟 | Vivado 2018.3+ / Quartus Prime |
| 仿真器 | Vivado Simulator | 内建,无需额外安装 | ModelSim / Questa / VCS |
| 时钟/复位 | 外部 50MHz 时钟,异步低有效复位 | 用于约束中的时钟定义 | 其他频率(需调整约束值) |
| 接口依赖 | 无外部器件(仅 FPGA 自身端口) | 约束基于理想外部环境 | 可扩展至 DDR / SPI 等真实接口 |
| 约束文件 | top.xdc | SDC 格式,含时钟、I/O 约束 | 也可用 GUI 添加 |
目标与验收标准
- 功能点:对 FPGA 输入/输出端口正确添加 set_input_delay 和 set_output_delay 约束。
- 性能指标:时序收敛,slack ≥ 0,Fmax 满足设计需求(如 50MHz 时钟下无违例)。
- 资源:不额外增加逻辑资源(约束本身不消耗 LUT/FF)。
- 验收方式:运行 report_timing_summary 检查 Input/Output 路径,slack 非负;运行 report_timing 查看具体路径。
- 关键波形:仿真中数据在时钟沿附近稳定,满足外部器件建立/保持时间。
实施步骤
1. 工程结构与端口定义
创建顶层模块 top.v,定义输入/输出端口。本例中,我们有一个输入数据总线 data_in(8 位),一个输出数据总线 data_out(8 位),以及时钟 clk 和复位 rst_n。
module top (
input wire clk,
input wire rst_n,
input wire [7:0] data_in,
output wire [7:0] data_out
);
reg [7:0] data_reg;
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
data_reg <= 8'd0;
else
data_reg <= data_in;
end
assign data_out = data_reg;
endmodule逐行说明
- 第 1 行:module top — 模块声明,端口列表包含 clk、rst_n、data_in、data_out。
- 第 2-5 行:端口方向与类型定义 — input 表示输入,output 表示输出,wire 类型用于组合逻辑。
- 第 7 行:reg [7:0] data_reg — 内部寄存器,用于寄存输入数据。
- 第 9-13 行:always 块 — 在 clk 上升沿或 rst_n 下降沿触发;异步复位将 data_reg 清零;否则在每个时钟沿将 data_in 赋值给 data_reg。
- 第 15 行:assign data_out = data_reg — 将寄存器输出直接连接到输出端口。
- 第 17 行:endmodule — 模块结束。
2. 创建时钟约束
在 top.xdc 中首先定义时钟。假设外部时钟频率为 50MHz,周期 20ns。
create_clock -name clk -period 20.000 [get_ports clk]逐行说明
- 第 1 行:create_clock — 创建时钟对象;-name clk 指定名称;-period 20.000 设置周期 20ns;[get_ports clk] 指定时钟源端口。
- 作用:告诉 Vivado 时钟频率,用于计算所有时序路径的建立/保持时间要求。
- 验收:综合后 report_clock_interaction 应显示 clk 被识别。
3. 添加 set_input_delay 约束
假设外部器件在时钟上升沿之后 2ns 输出数据给 FPGA,且数据在时钟上升沿之前 1ns 就有效(即外部器件输出延迟)。我们约束 data_in 端口。
set_input_delay -clock clk -max 3.000 [get_ports data_in]
set_input_delay -clock clk -min 1.000 [get_ports data_in]逐行说明
- 第 1 行:set_input_delay -clock clk -max 3.000 — 设置输入最大延迟为 3ns。含义:外部数据在时钟沿后最多 3ns 到达 FPGA 引脚。这个值用于建立时间分析(setup check)。
- 第 2 行:set_input_delay -clock clk -min 1.000 — 设置输入最小延迟为 1ns。含义:外部数据在时钟沿后至少 1ns 到达 FPGA 引脚。这个值用于保持时间分析(hold check)。
- 注意:-max 和 -min 通常基于外部器件数据手册的 max/min 输出延迟。如果不指定,默认 -max 和 -min 相同。
- 验收:综合后 report_timing -setup -from [get_ports data_in] 应显示路径起点为 data_in。
4. 添加 set_output_delay 约束
假设外部器件要求数据在时钟上升沿之前 1.5ns 稳定(建立时间),在时钟上升沿之后 0.5ns 保持(保持时间)。我们约束 data_out 端口。
set_output_delay -clock clk -max 1.500 [get_ports data_out]
set_output_delay -clock clk -min 0.500 [get_ports data_out]逐行说明
- 第 1 行:set_output_delay -clock clk -max 1.500 — 设置输出最大延迟为 1.5ns。含义:外部器件在时钟沿之前需要数据稳定 1.5ns(即外部建立时间)。这个值用于建立时间分析。
- 第 2 行:set_output_delay -clock clk -min 0.500 — 设置输出最小延迟为 0.5ns。含义:外部器件在时钟沿之后需要数据保持 0.5ns(即外部保持时间)。这个值用于保持时间分析。
- 注意:-max 对应外部建立时间,-min 对应外部保持时间。如果外部器件要求更严格,则约束值更大。
- 验收:report_timing -setup -to [get_ports data_out] 应显示路径终点为 data_out。
5. 综合与实现
在 Vivado 中运行综合(Synthesis),然后运行实现(Implementation)。打开时序报告。
# 在 Tcl Console 中运行
report_timing_summary -delay_type min_max -report_unconstrained -check_timing_verbose
report_timing -setup -from [get_ports data_in] -to [get_nets data_reg_reg*/D]
report_timing -hold -from [get_ports data_in] -to [get_nets data_reg_reg*/D]
report_timing -setup -from [get_nets data_reg_reg*/Q] -to [get_ports data_out]
report_timing -hold -from [get_nets data_reg_reg*/Q] -to [get_ports data_out]逐行说明
- 第 1 行:report_timing_summary — 生成综合时序总结,包含所有路径。
- 第 2 行:report_timing -setup — 专门查看 data_in 到寄存器 D 端的建立时间路径。
- 第 3 行:report_timing -hold — 查看 data_in 到寄存器 D 端的保持时间路径。
- 第 4 行:report_timing -setup — 查看寄存器 Q 端到 data_out 的输出建立时间路径。
- 第 5 行:report_timing -hold — 查看寄存器 Q 端到 data_out 的输出保持时间路径。
- 预期:所有路径 slack ≥ 0。若违例,检查约束值是否合理或逻辑延迟是否过大。
验证结果
| 路径 | 约束值 (ns) | slack (ns) | 测量条件 |
|---|---|---|---|
| data_in → reg (setup) | max=3.0 | +1.2 | Vivado 实现后,50MHz 时钟 |
| data_in → reg (hold) | min=1.0 | +0.8 | 同上 |
| reg → data_out (setup) | max=1.5 | +2.5 | 同上 |
| reg → data_out (hold) | min=0.5 | +1.1 | 同上 |
说明:以上 slack 值为示例,实际值取决于器件速度等级和逻辑复杂度。所有路径 slack 为正,表示时序收敛。
故障排查(Troubleshooting)
- 现象:建立时间违例(slack 负)。原因:约束过严或逻辑延迟过大。检查点:查看 data path 是否过长;修复建议:增加流水线级数或放宽约束值。
- 现象:保持时间违例。原因:约束过严或数据路径太短。检查点:查看 data path 是否有过多缓冲;修复建议:在数据路径上添加延迟单元(如 LUT 或寄存器),或放宽 -min 值。
- 现象:约束被忽略(报告无 I/O 路径)。原因:端口名称拼写错误或未匹配。检查点:运行 report_ports 查看所有端口;修复建议:修正 get_ports 名称。
- 现象:时钟未定义。原因:未 create_clock。检查点:运行 report_clocks;修复建议:添加时钟约束。
- 现象:-max 和 -min 值相同导致保持时间违例。原因:外部器件数据手册未提供 min 值。检查点:查看数据手册;修复建议:假设 min = 0 或使用默认值。
- 现象:综合后时序通过,实现后违例。原因:布线延迟增加。检查点:对比综合与实现报告;修复建议:优化布局布线约束(如 Pblock)。
- 现象:多时钟域交叉路径未约束。原因:set_input_delay 只针对一个时钟。检查点:检查跨时钟路径;修复建议:使用 set_false_path 或 set_max_delay。
- 现象:输出路径 slack 为负但内部路径正常。原因:set_output_delay 值过大。检查点:查看外部器件数据手册;修复建议:减小 -max 值。
- 现象:输入路径 slack 为负但内部路径正常。原因:set_input_delay 值过大。检查点:查看外部器件数据手册;修复建议:减小 -max 值。
- 现象:仿真时序正常但上板失败。原因:约束不准确或未包含 PCB 延迟。检查点:测量实际信号;修复建议:加入 PCB 走线延迟到约束中。
原理与设计说明
set_input_delay 和 set_output_delay 本质上是将外部器件的时序特性“映射”到 FPGA 内部时序分析中。FPGA 工具无法知道外部器件何时输出数据或何时需要数据,因此需要用户通过约束告知。
为什么需要 -max 和 -min
外部器件的输出延迟通常有最大值和最小值(因工艺、电压、温度变化)。-max 用于最坏情况(数据最晚到达),影响建立时间;-min 用于最好情况(数据最早到达),影响保持时间。同时约束两者才能覆盖所有 PVT 条件。
约束值的计算
对于输入延迟:set_input_delay -max = 外部器件 Tco_max + PCB 走线延迟;-min = 外部器件 Tco_min + PCB 走线延迟。对于输出延迟:set_output_delay -max = 外部器件 Tsetup;-min = 外部器件 Thold。注意:输出延迟的 -max 对应外部建立时间,因为 FPGA 必须在时钟沿之前使数据稳定。
Trade-off 分析
- 资源 vs Fmax:严格的约束(小延迟)可能迫使工具使用更快的逻辑(如高速进位链),增加面积;宽松的约束则可能降低 Fmax。
- 吞吐 vs 延迟:I/O 约束不影响内部流水线吞吐,但过严的约束可能迫使寄存器靠近 I/O,增加布线延迟。
- 易用性 vs 可移植性:直接写死数值简单,但换器件或 PCB 后需重新计算;建议用参数化脚本生成约束。
扩展与下一步
- 参数化约束:使用 Tcl 变量或脚本生成约束,便于在不同项目间复用。
- 多时钟域 I/O:为不同时钟域分别添加 set_input_delay / set_output_delay,并处理跨时钟路径。
- DDR 接口:使用 set_input_delay 的双边沿约束(-clock_fall 选项)处理双倍数据率。
- 加入断言:在仿真中添加 SVA 断言检查 I/O 时序。
- 形式验证:使用 Vivado 的 formal 工具验证约束一致性。
- 跨平台:将约束移植到 Quartus Prime(使用 set_input_delay 类似命令)。
参考与信息来源
- Xilinx UG903: Vivado Design Suite User Guide: Using Constraints
- Xilinx UG949: Vivado Design Suite User Guide: Methodology
- Synopsys SDC 标准文档
- FPGA 设计实战:时序约束与优化 (书籍)
- Vivado 官方 Tcl 命令参考
技术附录
术语表
- Slack:时序裕量,正数表示满足要求。
- Setup Time:建立时间,数据必须在时钟沿之前稳定的最短时间。
- Hold Time:保持时间,数据必须在时钟沿之后稳定的最长时间。
- Tco:时钟到输出延迟,外部器件从时钟沿到数据输出的延迟。
- SDC:Synopsys Design Constraints,标准约束格式。
检查清单
- [ ] 已创建时钟约束。
- [ ] 所有输入端口已添加 set_input_delay。
- [ ] 所有输出端口已添加 set_output_delay。
- [ ] -max 和 -min 值已根据数据手册设置。



