FPGA 时序约束实用指南:input_delay / output_delay / max_delay

FPGA 时序约束实用指南:input_delay / output_delay / max_delay

目录

  • [1. 核心认知](#1. 核心认知)
  • [2. set_input_delay](#2. set_input_delay)
  • [3. set_output_delay](#3. set_output_delay)
  • [4. set_max_delay](#4. set_max_delay)
  • [5. set_false_path](#5. set_false_path)
  • [6. ASYNC_REG](#6. ASYNC_REG)
  • [7. 决策树:遇到一个信号该用什么约束](#7. 决策树:遇到一个信号该用什么约束)
  • [8. 实战:ADS1675 ADC 接口约束案例](#8. 实战:ADS1675 ADC 接口约束案例)

1. 核心认知

1.1 约束不是在给 FPGA 设延迟

set_input_delayset_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 走线延迟_min

2.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 写法:

tcl 复制代码
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.3ns

XDC 写法:

tcl 复制代码
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:异步信号防布线放飞
tcl 复制代码
# 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    ← 但布线不能飞到几百 ns

4.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 写法

tcl 复制代码
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 约束方案

tcl 复制代码
# ===== 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, 留半周期余量"