FPGA 时序约束实用指南:input_delay / output_delay / max_delay
目录
- 1. 核心认知
- 2. set_input_delay
- 3. set_output_delay
- 4. set_max_delay
- 5. set_false_path
- 6. ASYNC_REG
- 7. 决策树:遇到一个信号该用什么约束
- 8. 实战:ADS1675 ADC 接口约束案例
1. 核心认知
1.1 约束不是在给 FPGA 设延迟
set_input_delay和set_output_delay不是在设 FPGA 内部的延迟值,而是在告诉工具外部世界的数据到达/出发时间。工具拿这些值反推出 FPGA 内部还能花多少时间。
1.2 约束影响布局布线
三类约束都会影响布局布线,但机制不同:
| 约束 | 对布局的影响 | 机制 |
|---|---|---|
create_clock | ✅ 强 | 周期本身是硬上限 |
set_input_delay/set_output_delay | ✅ 中~强 | 通过 setup/hold 方程间接驱动距离 |
set_max_delay | ✅ 强 | 直接限制路径延迟上限 |
set_false_path | ⚠️ 放任 | 砍掉后工具可能放飞,需 max_delay 补刀 |
2. set_input_delay
2.1 物理模型
外部器件 FPGA 内部 ┌────────────┐ ┌──────────────────┐ │ 寄存器 │ │ 寄存器 │ CLK ──────┼────────────┼────────────┼──────────────────┤ │ │ │ │ │ clk→Q ────┼── DATA ───┼── 组合逻辑 ──────┤ │ ↑ │ ↑ │ ↑ │ └─────↑──────┘ ↑ └────────↑─────────┘ ↑ ↑ ↑ DF1 时钟沿 input_delay 内部路径 外部延迟 (工具要算的)set_input_delay= 数据从外部寄存器的时钟沿到FPGA pin的时间。
工具知道时钟周期 T 和 input_delay 后,算出内部路径上限:
内部最大允许 = T - input_delay_max - Tsu (管 setup) 内部最小允许 = input_delay_min - Th (管 hold)2.2 max 和 min 分别管什么
| 参数 | 对应什么 | 约束的是什么 |
|---|---|---|
| max | 外部最慢情况下的数据到达时间 | FPGA 内部setup(数据不能到太晚) |
| min | 外部最快情况下的数据到达时间 | FPGA 内部hold(数据不能到太早) |
2.3 怎么算值
input_delay_max = 外部器件 t_co_max (手册) + PCB 走线延迟_max input_delay_min = 外部器件 t_co_min (手册) + PCB 走线延迟_min2.4 详细例子:ADS1274 接 FPGA
ADS1274 手册参数:
t_DO = 5~12ns (SCLK↑ → DOUT 有效,即 ADS1274 的 clk→Q) PCB 走线 = 1~2ns FPGA 时钟 = 25MHz / 40ns 周期计算:
input_delay_max = 12 + 2 = 14ns input_delay_min = 5 + 1 = 6ns时序分析:
0 10 20 30 40ns ├────────┼────────┼────────┼────────┤ CLK: ──┘ └── ← 采沿在 0ns 外部延迟: ────────┬──────────────┬───────────── 最快6ns │ 最慢14ns │ ↓ ↓ DATA到pin: t=6 t=14 内部setup预算 = 40 - 14 - 0.5(Tsu) = 25.5ns ← 充裕 ✅ 内部hold检查: 数据最快6ns到,比采沿(0ns)晚,hold自然满足 ✅XDC 写法:
create_clock -period 40.000 -name sclk [get_ports i_sclk] set_input_delay -clock sclk -max 14.000 [get_ports i_dout] set_input_delay -clock sclk -min 6.000 [get_ports i_dout]2.5 max/min 设反了会怎样
| 错误 | 后果 |
|---|---|
| min 设太大 | 告诉工具"数据来得晚",工具不防 hold → 实际数据到太快 → 芯片 hold 违例 → 可能死机 |
| max 设太小 | 告诉工具"数据来得早",工具过度优化 → 假 setup 违例满天飞 |
工程原则:max 往大设(悲观),min 往小设(悲观)。
3. set_output_delay
3.1 物理模型
FPGA 内部 外部器件 ┌──────────────────┐ ┌────────────┐ │ 寄存器 │ │ 寄存器 │ CLK ───┼──────────────────┼───────────┼────────────┤ │ │ │ │ │ clk→Q ── DATA ──┼───────────┼── 组合 ────┤ │ │ ↑ │ ↑ │ └──────────────────┘ ↑ └─────↑──────┘ ↑ ↑ FPGA内部 output_delay 路径 外部延迟set_output_delay= 数据从FPGA pin到外部寄存器 D 脚的时间(PCB + 外部器件 Tsu)。
工具用它算内部还能花的时间:
内部最大允许 = T - output_delay_max - Tco(内部)3.2 max 和 min 分别管什么
| 参数 | 值 | 物理意义 |
|---|---|---|
| max | PCB_max + Tsu(下游) | 数据最晚必须在下游采沿前多久到 pin |
| min | PCB_min - Th(下游) | 数据最早会到达(可以是负数) |
3.3 详细例子:FPGA 输出给 DAC
FPGA→DAC,同源 100MHz (T=10ns) PCB 走线 = 0.5~2.0ns DAC Tsu = 2.0ns, Th = 0.8ns output_delay_max = 2.0 + 2.0 = 4.0ns output_delay_min = 0.5 - 0.8 = -0.3ns ← 注意,min 可以是负数!为什么 min 是负数?最快路径下数据跑到太快,比时钟沿还早到下游器件:
FPGA 最快 clk→Q + routing = 0.3 + 0.2 = 0.5ns → 到 pin PCB = 0.5ns 数据到下游 D 脚 = 1.0ns 但下游还在抓上一笔数据!(Th = 0.8ns → 需要 t=0.8ns 后数据才允许变) → 数据 1.0ns 就到了,只差 0.2ns ← 危险! → min = -0.3ns,工具知道了会插 buffer 拖慢 0.3nsXDC 写法:
create_clock -period 10.000 -name clk_100m [get_ports i_clk] set_output_delay -clock clk_100m -max 4.000 [get_ports o_data[*]] set_output_delay -clock clk_100m -min -0.300 [get_ports o_data[*]]4. set_max_delay
4.1 和 input/output delay 的本质区别
| set_input_delay / set_output_delay | set_max_delay | |
|---|---|---|
| 参考时钟 | 必须有物理关系的时钟 | 不需要 |
| 约束什么 | 通过 setup/hold 方程间接约束 | 直接限制路径走线延迟 |
| 适用场景 | 同步/源同步 | 异步信号、CDC、伪路径 |
| 有 min 吗 | 有 (管 hold) | 有但不常用 |
4.2 两种主要使用场景
场景 A:异步信号防布线放飞
# ADC 的 SCLK/DRDY/DOUT 和 FPGA 100MHz 异步 # 不用 input_delay,用 false_path + max_delay: set_false_path -from [get_ports {i_ads_sclk i_ads_drdy i_ads_dout}] set_max_delay -from [get_ports i_ads_sclk] \ -to [get_cells -hier -filter {REF_NAME =~ "*r_sclk_r0*"}] \ -datapath_only 5.000-datapath_only表示只看走线+缓冲延迟,不算时钟偏斜和 Tsu。
场景 B:CDC 路径防布线放飞
clk_a 域 → clk_b 域,数据总线 32 位,异步 FIFO 握手: set_false_path ← 砍时序检查 set_max_delay ← 但布线不能飞到几百 ns4.3 max_delay 值怎么定
多比特信号防偏斜(多根线有物理先后关系):
手册规定: SCLK↑ → DRDY↑ = 2.2ns (最小间隔) 要保证内部不反转: 内部偏斜 < 2.2ns 工程放宽到 5ns: 内部偏斜 5ns 虽然反转了先后, 但在 FPGA 100MHz(10ns) 的采样粒度下, 这个反转被同步链消化了。 5ns = 半个 FPGA 时钟周期,工具轻松收敛。单比特异步信号:值不敏感,设一个合理的即可(如 5~10ns),目的是别让走线绕芯片一圈。
4.4 max 和 min
| 干什么 | 异步场景用不用 | |
|---|---|---|
-max | 路径不能太慢 | ✅ 用,防布线放飞 |
-min | 路径不能太快 | ❌ 不用,异步没有 hold 概念 |
异步信号 min 没有意义——数据早到 5ns 还是 0.1ns,没有时钟沿跟它比,不存在 “hold 违例”。
5. set_false_path
5.1 作用
告诉工具:这条路径不需要做时序检查。
5.2 使用场景
- 异步输入(没有参考时钟)
- CDC 跨时钟域路径(带握手/同步器保护)
- 纯静态信号(配置寄存器,上电后不变)
- 复位信号(异步复位)
5.3 注意
set_false_path砍掉后工具可能把路径放到芯片另一边不加约束。多比特异步信号或 CDC 数据总线必须加set_max_delay补刀。
6. ASYNC_REG
6.1 作用
标记同步链上的寄存器,告诉工具:
- 这是跨时钟域的第一/二级同步器
- 把它们物理上放近(降低 MTBF)
- 时序分析时特殊处理
6.2 写法
set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_sync_r0*"}] set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_sync_r1*"}]6.3 和其他约束的配合
false_path: "别算时序" ← 管分析 max_delay: "但也别太慢" ← 管布线质量 ASYNC_REG: "这两级要贴紧" ← 管 MTBF三者各管一摊,互补不互斥。
7. 决策树:遇到一个信号该用什么约束
这个信号有合法的参考时钟进了 FPGA 吗? ├── 有 → 用 set_input_delay / set_output_delay │ 参考时钟 + 外部延迟值(从手册算) │ └── 没有 (异步) → 信号有几根? ├── 单根 (如独立按钮) → set_false_path 就够了 │ └── 多根且互有时序关系 (如 ADC 的 SCLK+DRDY+DOUT) → set_false_path (砍时序检查) → set_max_delay 5ns (控三根线偏斜, 保留板级先后关系) → ASYNC_REG (标同步器, 降 MTBF)速查表
| 场景 | 用哪些约束 |
|---|---|
| 源同步数据 (DCO+DATA) | create_clock+set_input_delay |
| 系统同步数据 (同源 CLK) | create_clock+set_input_delay |
| FPGA 输出给 DAC/SDRAM | create_clock+set_output_delay |
| ADC 异步过采样 (多根线) | set_false_path+set_max_delay+ASYNC_REG |
| 独立异步信号 (按钮) | set_false_path |
| CDC 数据总线 | set_false_path+set_max_delay+ASYNC_REG |
| 静态配置信号 | set_false_path |
| 异步复位 | set_false_path |
8. 实战:ADS1675 ADC 接口约束案例
8.1 场景
- ADS1675 ADC:CMOS 接口,SCLK=32MHz,DRDY=脉冲,DOUT=24bit 串行
- FPGA:100MHz 过采样这三个信号
- SCLK 没有接 BUFG 当时钟用,三个都是普通异步 I/O
- RTL 中有状态机依赖 SCLK/DRDY 的先后关系
8.2 为什么不能用 input_delay
ADS1675 的 SCLK 在 FPGA 里当数据采、没当时钟用,FPGA 100MHz 和 ADC 没有任何相位关系。没有合法参考时钟 →set_input_delay不适用。
8.3 约束方案
# ===== 1. 时钟 ===== create_clock -period 10.000 -name i_clk [get_ports i_clk] # ===== 2. 三条异步输入: 砍时序检查 ===== set_false_path -from [get_ports {i_ads_sclk i_ads_drdy i_ads_dout}] # ===== 3. 三条线 pin→首级寄存器 布线 ≤ 5ns ===== # 同一上限 → 任意两根内部偏斜 ≤ 上限 → 板级先后关系保留 set_max_delay -from [get_ports i_ads_sclk] \ -to [get_cells -hier -filter {REF_NAME =~ "*r_sclk_r0*"}] \ -datapath_only 5.000 set_max_delay -from [get_ports i_ads_drdy] \ -to [get_cells -hier -filter {REF_NAME =~ "*r_drdy_r0*"}] \ -datapath_only 5.000 set_max_delay -from [get_ports i_ads_dout] \ -to [get_cells -hier -filter {REF_NAME =~ "*r_shift_reg*"}] \ -datapath_only 5.000 # ===== 4. 同步器标记 ===== set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_sclk_r0*"}] set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_sclk_r1*"}] set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_drdy_r0*"}] set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_drdy_r1*"}] set_property ASYNC_REG TRUE [get_cells -hier -filter {NAME =~ "*r_drdy_r2*"}] # ===== 5. 输出 (给下游 FIFO) ===== set_output_delay -clock [get_clocks i_clk] -max 5.0 [get_ports {o_fifo_wr o_fifo_data[*]}] set_output_delay -clock [get_clocks i_clk] -min 1.0 [get_ports {o_fifo_wr o_fifo_data[*]}]8.4 约束策略总结
false_path: "别拿不存在的时钟沿做 setup/hold 分析" max_delay 5ns: "三根线走线都控制在 5ns 内, 板上的先后顺序别搞反了" ASYNC_REG: "同步链两级寄存器靠紧放, 亚稳态概率最低" output_delay: "输出给下游 FIFO, 留半周期余量"