时序例外:false_path / multicycle_path / max_delay
一、从一个问题开始上两篇我们写了基本时钟约束、IO 约束——给了工具足够的路径信息。但有一个问题没解决不是所有路径都需要做 setup/hold 检查。有些路径天生就不该被检查——比如异步时钟域之间的路径、芯片刚上电时的复位信号。有些路径确实需要检查但不是每个周期——比如 SPI 的数据路径数据可能在 2~3 个时钟周期后才稳定。这就是时序例外Timing Exception要解决的问题。学懂了时序例外你的 SDC 才真正属于这个设计而不是一个模板复制品。二、时序例外的全景三种时序例外对应三类场景╔══════════════════╦══════════════════╦══════════════════════╗ ║ false_path ║ multicycle_path ║ set_max/min_delay ║ ╠══════════════════╬══════════════════╬══════════════════════╣ ║ 不检查时序 ║ 放宽检查周期 ║ 自定义绝对延迟上限 ║ ║ → 异步跨域路径 ║ → 慢速逻辑路径 ║ → 异步组合信号 ║ ║ → 测试模式路径 ║ → 同步使能路径 ║ → IO 接口组合路径 ║ ║ → 复位路径 ║ → 特定计算路径 ║ → 跨时钟握手信号 ║ ╚══════════════════╩══════════════════╩══════════════════════╝三、false_path不存在的路径3.1 什么时候用打个比方一条路从 A 到 B但中间有扇永远锁着的门——你永远不会走这条路。那就别浪费时间在这条路上设交警查超速了。具体场景场景说明典型命令异步时钟域两个无关联时钟之间的所有路径set_false_path -from [get_clocks CLK_A] -to [get_clocks CLK_B]复位路径所有寄存器异步置位/清零端不限制源端适用于多级复位树set_false_path -to [all_registers -async]测试模式scan_enable / test_mode 在功能模式下的路径set_false_path -through [get_pins .../test_mode]DFT 隔离scan 链在 func 模式下不活动的路径set_false_path -from [all_registers -scan]DC 耦合路径仿真用但综合不需要的逻辑set_false_path -through [get_nets dc_bias_sim]3.2 异步时钟域的 false_path 写法最常见的场景——两个异步时钟域之间的所有寄存器到寄存器的路径# ⚠️ 错误写法只考虑了一个方向 set_false_path -from [get_clocks clk_spi] -to [get_clocks clk_sys] # ✅ 正确写法两个方向都要设或者用 clock groups set_clock_groups -asynchronous \ -group [get_clocks clk_spi] \ -group [get_clocks clk_sys] # 等效于 set_false_path -from [get_clocks clk_spi] -to [get_clocks clk_sys] set_false_path -from [get_clocks clk_sys] -to [get_clocks clk_spi]注意set_clock_groups -asynchronous是双向的而set_false_path是单向的。用 clock groups 更简洁不易遗漏。3.3 常见错误# ✗ 错误对复位端口设 from → 只覆盖了从该端口出发的路径, # 如果复位树有多级内部节点如 por_b → sftRst → regRst # 中间节点的复位路径不会被覆盖 set_false_path -from [get_ports rst_n] -to [all_registers -async] # → 这只覆盖了 rst_n 直连的路径漏掉了复位树内部的异步路径 # ✅ 正确不限制源端对所有寄存器的异步端设 false_path set_false_path -to [all_registers -async] # → 无论复位源来自顶层端口还是内部复位树节点全部覆盖四、multicycle_path多周期路径4.1 核心概念打个比方一辆快递车每天发一班但有的包裹需要 3 天才能到——你查快递状态时在第 3 天查才是合理的第 2 天查肯定显示未到达引发误报。multicycle_path 就是告诉工具这条路径的包裹要 3 个时钟周期才能到别在 1 个周期后催。默认情况没写 multicycle_path 时工具假设每个启动沿launch都在下一个捕获沿capture完成检查。如果数据实际需要 2 个或更多周期工具会报假违例。4.2 基础语法# 3-cycle setup mulicyle path set_multicycle_path 3 -setup -from [get_pins reg_a/CK] -to [get_pins reg_b/D] # 配套的 hold multicycle重要容易忘 set_multicycle_path 2 -hold -from [get_pins reg_a/CK] -to [get_pins reg_b/D]4.3 hold 的减一规则这是 multicycle 最让人困惑的细节。看公式setup check 的周期偏移 M - 1 hold check 的周期偏移 N - 1 其中 M setup multicycle 值 N hold multicycle 值规则hold multicycle 一般比 setup 少 1。原因默认M1, N1: setup check: launch 沿的下一个沿偏移01-1 hold check: launch 沿的同一个沿偏移01-1 3-cycle setupM3: setup check: launch 沿之后的第 2 个沿偏移23-1 hold check: 默认情况下会在同一个沿检查 → 太严格实际上数据要在第 3 个沿才被捕获 为什么 hold 要减 1: 如果 setup 在第 3 个沿检查hold 最早应该在 launch 沿之后的第 1 个沿检查 因为数据必须保持到第 3 个沿但第 0 个沿之后数据就变了 所以 hold multicycle setup - 1 2一句话记住写 M-cycle setup一定要配套写 (M-1)-cycle hold。忘了 hold 是 multicycle 最常见的 bug。打个比方你约了朋友 3 天后见面3-cycle setup那保持通信畅通的时间应该是到第 1 天还是到第 3 天答案是到第 1 天之后就解除保持要求了——因为真正的检查在第 3 天你需要在第 2 天之后才能改变计划。hold 的 (M-1) 就是这个道理。4.4 典型应用场景# 1. 使能控制的寄存器路径数据每 N 个周期才更新一次 # 数据路径经过一个 en 控制的寄存器只在 en1 时更新 set_multicycle_path 2 -setup -through [get_pins u_en_reg/Q] set_multicycle_path 1 -hold -through [get_pins u_en_reg/Q] # 2. SPI 数据路径在慢时钟域中组合逻辑较长 # SPI 时钟 2.5MHz寄存器到寄存器的组合逻辑超过 400ns # 允许 2 个 SPI 时钟周期 set_multicycle_path 2 -setup -from [get_clocks clk_spi] -to [get_clocks clk_spi] set_multicycle_path 1 -hold -from [get_clocks clk_spi] -to [get_clocks clk_spi] # 3. 分频器输出路径计数器分频后的信号 # 输出每 4 个时钟周期才翻转一次 set_multicycle_path 4 -setup -through [get_pins u_div_cnt/Q] set_multicycle_path 3 -hold -through [get_pins u_div_cnt/Q]五、set_max_delay / set_min_delay自定义延迟约束5.1 什么时候用打个比方你有一条没有红绿灯的辅路没有时钟同步交通规则说所有车必须在 10 秒内通过。你不能用 setup/hold 来约束因为没有时钟只能用直接的10 秒上限。5.2 典型场景# 1. 异步握手信号 # data_valid 从 CLK_A 域到 CLK_B 域组合路径没有时钟 set_max_delay 10 -from [get_pins u_sync/data_valid_reg/Q] \ -to [get_pins u_sync_b/data_valid_sync/D] set_min_delay 2 -from [get_pins u_sync/data_valid_reg/Q] \ -to [get_pins u_sync_b/data_valid_sync/D] # 2. IO 组合路径输入直接到输出不经过寄存器 set_max_delay 50 -from [get_ports data_in] -to [get_ports data_out] set_min_delay 5 -from [get_ports data_in] -to [get_ports data_out] # 3. 跨时钟域的 fast→slow 路径上的组合逻辑 set_max_delay 15 -from [get_clocks clk_fast] -to [get_clocks clk_slow]5.3 优先级规则当多个例外作用于同一条路径时最严格的一个生效同一路径上同时存在 set_max_delay 10 set_false_path → false_path 优先级最高覆盖 max_delay优先级从高到低1. set_false_path → 完全跳过检查 2. set_max/min_delay → 自定义延迟上限/下限 3. set_multicycle_path → 放宽检查周期 4. 默认 setup/hold 检查 → 最宽松六、虚拟项目实战充电管理芯片的 SDC 例外项目背景工艺180nm 9TV50 时钟 clk_sys 2.5MHz (400ns) — 系统主时钟 clk_spi 2.5MHz (400ns) — SPI 从模式时钟与 clk_sys 异步 clk_pwm 100kHz (10us) — PWM 调制时钟由 clk_sys 分频需要处理的例外路径类型原因SDC 命令clk_sys ↔ clk_spifalse_path异步时钟域set_clock_groups -asynchronousrst_n → 所有寄存器的异步端false_path复位路径set_false_path -to [all_registers -async]SPI data → SPI register (en1才更新)multicycle 2使能控制set_multicycle_path 2/1ADC 结果寄存器到 PWM 比较器multicycle 4/3每 4 个周期才更新set_multicycle_path 4/3test_mode → 所有 MUX 选择端false_path功能模式set_false_path -through [get_pins .../test_mode]输入 GPIO 到输出 GPIO不带寄存器max_delay纯组合 IO 路径set_max_delay 300 -from [IO_IN] -to [IO_OUT]异步中断信号 comb 路径max_delay无时钟同步set_max_delay 100完整 SDC 片段# 时钟例外 # 异步时钟域 set_clock_groups -asynchronous \ -group [get_clocks clk_sys] \ -group [get_clocks clk_spi] # false_path # 复位路径不限制源端覆盖多级复位树的全部异步端 set_false_path -to [all_registers -async] # 测试模式路径 set_false_path -through [get_pins */test_mode] # multicycle_path # SPI 数据路径使能控制 set_multicycle_path 2 -setup -through [get_pins u_spi_reg/Q] set_multicycle_path 1 -hold -through [get_pins u_spi_reg/Q] # ADC → PWM 路径慢速更新4 个周期 set_multicycle_path 4 -setup -from [get_clocks clk_sys] \ -to [get_clocks clk_pwm] set_multicycle_path 3 -hold -from [get_clocks clk_sys] \ -to [get_clocks clk_pwm] # max/min_delay # GPIO 纯组合路径 set_max_delay 300 -from [all_inputs] -to [all_outputs] set_min_delay 5 -from [all_inputs] -to [all_outputs] # 异步中断握手 set_max_delay 100 -from [get_pins u_int_gen/irq_reg/Q] \ -to [get_pins u_int_sync/irq_sync/D]验证方法写完例外后一定要检查# 列出所有被 false_path 覆盖的路径 report_timing -exceptions all -significant_fp 10 # 列出所有 multicycle 路径的 setup/hold 周期 report_multicycle_path -all # 检查没有被任何例外覆盖的路径遗漏检查 check_timing -override_defaults -verbose七、记忆曲线回访回访 SDC 时钟约束第一课2026-05-20当时我们写了 basic clock constraints但留下了例外这个缺口。现在补上了完整的 SDC 应该包含 ┌─────────────────────────────────────┐ │ 1. 时钟定义create_clock │ ← 已学 │ 2. 时钟特性uncertainty/latency │ ← 已学 │ 3. IO 约束input/output delay │ ← 已学 │ 4. 时序例外false/multi/max/min │ ← 现在 │ 5. 时钟组clock groups │ ← 现在 │ 6. 设计规则约束transition/cap/fanout│ ← 未学 └─────────────────────────────────────┘与 OCV / POCV 的关系例外和 OCV 是两个正交的概念例外影响检查什么路径 OCV 影响检查时用什么 margin 两者不冲突 先设例外确定哪些路径需检查 再设 OCV确定检查的严格程度八、总结┌────────────────────────────────────────────────────────────┐ │ │ │ 时序例外的选择法则 │ │ │ │ Q: 这条路径需要检查 setup/hold 吗 │ │ ├─ NO → set_false_path │ │ │ │ │ └─ YES → Q: 每 1 个时钟周期都检查 │ │ ├─ YES → 不需要例外默认 │ │ │ │ │ └─ NO → Q: 路径有同步时钟吗 │ │ ├─ YES → set_multicycle_path │ │ └─ NO → set_max/min_delay │ │ │ └────────────────────────────────────────────────────────────┘三个容易忘的点1. set_multicycle_path 的 hold 必须比 setup 少 1 2. set_clock_groups -asynchronous 比两个 false_path 更安全 3. false_path 优先级最高会覆盖 max_delay