FPGA 进阶(04):时序约束与收敛——create_clock、set_input_delay、set_output_delay
更新时间:2026-09-02。本文是
fpga/FPGA 领域第 04 篇,接 常用 IP 核。时序约束是 FPGA 设计最难的环节之一。不是写完代码就完了,必须让综合工具知道时序要求,才能把设计映射到正确的路径上,保证 setup/hold 都满足。
本文要回答的问题
- 时序约束的基本框架是什么?有哪些约束?
- 主时钟约束
create_clock怎么用? - 输入延迟
set_input_delay和输出延迟set_output_delay分别约束什么? - 时序分析报告怎么看?setup/hold 违例怎么修?
- 时序收敛的常用技巧有哪些?
一、时序约束的基本框架
FPGA 时序约束:
┌─────────────────────────────────────────────────────┐
│ 1. 时钟约束(Clock Constraints) │
│ → create_clock(主时钟) │
│ → create_generated_clock(衍生时钟) │
│ → set_clock_latency / set_clock_uncertainty │
├─────────────────────────────────────────────────────┤
│ 2. IO 约束(IO Constraints) │
│ → set_input_delay(输入延迟) │
│ → set_output_delay(输出延迟) │
├─────────────────────────────────────────────────────┤
│ 3. 异常约束(Exception Constraints) │
│ → set_false_path(伪路径) │
│ → set_multicycle_path(多周期路径) │
│ → set_max_delay / set_min_delay │
└─────────────────────────────────────────────────────┘二、主时钟约束:create_clock
create_clock 是最基本的约束,定义时钟的周期和波形
语法:
create_clock -name <时钟名> -period <周期ns> [ -waveform {上升沿 下降沿} ] [get_ports <端口名>]
示例:
# 100 MHz 时钟,占空比 50%
create_clock -name sys_clk -period 10.000 [get_ports clk]
# 50 MHz 时钟,60% 占空比
create_clock -name custom_clk -period 20.000 -waveform {0 12} [get_ports clk2]
# 引脚输入时钟
create_clock -name eth_rx_clk -period 8.000 [get_ports eth_rxc]衍生时钟:create_generated_clock
PLL 输出的时钟要用 generated_clock 约束
语法:
create_generated_clock -name <名称> -source <源时钟> -divide_by <分频> -multiply_by <倍频> [get_pins <PLL输出>]
示例:
# PLL 输出 200 MHz,源时钟 100 MHz,2 倍频
create_generated_clock -name clk_200m -source [get_pins clk_gen/clk_in1] \
-multiply_by 2 [get_pins clk_gen/clk_out1]三、IO 约束:set_input_delay / set_output_delay
输入延迟
输入延迟约束从外部芯片到 FPGA 的数据路径
set_input_delay -clock <时钟> -max <最大延迟ns> [get_ports <输入端口>]
set_input_delay -clock <时钟> -min <最小延迟ns> [get_ports <输入端口>]
示例:
# 外部芯片在时钟上升沿后 2ns 送出数据
# 数据到达 FPGA 引脚有 0.5ns 走线延迟
set_input_delay -clock sys_clk -max 2.5 [get_ports data_in]
set_input_delay -clock sys_clk -min 0.5 [get_ports data_in]
# 总线数据
set_input_delay -clock sys_clk -max 3.0 [get_ports {data_bus[*]}]
set_input_delay -clock sys_clk -min 1.0 [get_ports {data_bus[*]}]输出延迟
输出延迟约束从 FPGA 到外部芯片的数据路径
set_output_delay -clock <时钟> -max <最大延迟ns> [get_ports <输出端口>]
set_output_delay -clock <时钟> -min <最小延迟ns> [get_ports <输出端口>]
示例:
# 外部芯片要求在时钟沿前 1ns 数据稳定(建立时间)
# 外部芯片要求在时钟沿后保持 0.5ns
set_output_delay -clock sys_clk -max 2.0 [get_ports data_out]
set_output_delay -clock sys_clk -min 0.5 [get_ports data_out]四、时序分析基础
时序分析检查的是 setup time 和 hold time
Setup(建立时间)检查:
数据必须在时钟沿前到达,留出建立时间
Slack = 时钟周期 - 数据路径延迟 - 建立时间 - 时钟歪斜
Slack > 0 → 满足
Slack < 0 → 违例
Hold(保持时间)检查:
数据必须在时钟沿后保持一段时间
Slack = 数据路径延迟 - 保持时间 - 时钟歪斜
Slack > 0 → 满足
Slack < 0 → 违例时序路径类型:
1. 寄存器到寄存器(Reg-to-Reg):内部路径,最常用
2. 输入到寄存器(Pin-to-Reg):IO 路径
3. 寄存器到输出(Reg-to-Pin):IO 路径
4. 输入到输出(Pin-to-Pin):纯组合路径五、时序报告怎么看
Vivado 时序报告结构:
1. 路径信息
From: <源寄存器>
To: <目的寄存器>
Delay: <延迟>
2. 数据路径延迟(Data Path)
Logic Delay: 逻辑延迟(LUT)
Net Delay: 布线延迟
Total: Logic + Net
3. 时钟路径延迟(Clock Path)
Clock Skew: 时钟歪斜
4. 时序余量
Slack: 正数 → 满足,负数 → 违例典型违例:
Slack: -0.234 ns (违例 0.234 ns)
分析:
Logic Delay: 3.2 ns ← 逻辑太深
Net Delay: 4.5 ns ← 布线太长
时钟周期: 8.0 ns
解决方法:
1. 减少逻辑深度(加流水线)
2. 优化布线(位置约束)
3. 降低时钟频率(设计妥协)六、时序收敛技巧
1. 减少逻辑深度
加流水线寄存器
一行长的组合逻辑拆成多行
2. 合理设置 false_path
跨时钟域路径,不需要 constraining
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]
3. 多周期路径(set_multicycle_path)
有些路径不需要在一个周期内完成
set_multicycle_path -setup 2 -from [get_pins ...] -to [get_pins ...]
4. 逻辑复制
高扇出信号复制多份,减少布线延迟
5. 位置约束
手动指定寄存器位置,就近布局
6. 控制资源利用率
利用率 > 70% 时布线困难,时序难收敛
留出余量给布线工具七、常见坑对照
| 错误 | 现象 | 正确做法 |
|---|---|---|
| 没写约束 | 时序分析不准确 | 所有时钟都要约束 |
| 衍生时钟没用 create_generated | PLL 输出没有约束 | 用 generated_clock |
| 跨时钟域没设 false_path | 时序违例误报 | 异步 FIFO 或 set_false_path |
| 扇出太大 | 布线延迟大 | 逻辑复制,减少扇出 |
| 利用率太高 | 时序难收敛 | 留 20-30% 余量 |
| IO 约束不准确 | 板级信号不稳定 | 根据芯片手册填写 |
相关与延伸
下一篇:高速接口——LVDS、SERDES、DDR3/4、PCIe、Ethernet;时序分析 STA 算法,见 EDA 核心算法——STA。
一句话总结
FPGA 时序约束:create_clock 定义主时钟周期和波形;create_generated_clock 定义 PLL 衍生时钟;set_input_delay 约束外部到 FPGA 的数据延迟;set_output_delay 约束 FPGA 到外部的数据延迟;时序报告看 Slack(setup 检查数据是否在时钟沿前到达,hold 检查是否保持住);收敛技巧:加流水线减少逻辑深度、设 false_path 排除跨时钟域路径、多周期路径、逻辑复制、控制利用率在 70% 以下。