4、时序例外约束:伪路径、多周期路径、最大/最小延迟

好,咱们接着聊时序约束。前面几章讲的都是常规路径,比如寄存器到寄存器、输入到输出这些。但实际项目中,总有一些“不听话”的路径——它们要么不需要满足一个周期,要么压根不用管时序。这时候,就需要时序例外约束出场了。

我个人习惯把时序例外分成三类:伪路径多周期路径最大/最小延迟。这三兄弟各有各的脾气,用错了可是会出大问题的。

4.1 伪路径(set_false_path)

伪路径,说白了就是告诉工具:“这条路径你甭管了,我不在乎它跑多快。” 为什么会有这种路径?你想想看,有些信号在正常工作模式下根本不会同时翻转,比如复位信号和时钟信号之间,或者两个不同时钟域之间的异步信号。

什么时候用?

  • 跨时钟域的同步器路径(比如两级触发器同步)
  • 复位信号到寄存器的路径
  • 测试模式下的路径(比如扫描链)
  • 静态配置信号(比如通过SPI写入的寄存器值)

怎么用?

# 从时钟域clk_a到时钟域clk_b的所有路径
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

# 复位信号rst_n到所有寄存器的路径
set_false_path -from [get_ports rst_n] -to [all_registers]

# 特定模块的路径
set_false_path -through [get_pins u_dma/ready_sync/*]
⚠️ 警告: 伪路径不是万能药!我曾经见过一个新手,把一条关键数据路径设成了伪路径,结果芯片跑起来数据全是错的。记住:只有你100%确定这条路径不会影响功能时,才用set_false_path
💡 我的经验: 在项目初期,我习惯先把所有跨时钟域路径都设成伪路径,等后期再逐个确认哪些需要做同步处理。这样能避免早期STA跑出几千条violation,看得人头皮发麻。

4.2 多周期路径(set_multicycle_path)

多周期路径,就是告诉工具:“这条路径可以跑慢点,两个周期甚至三个周期都行。” 为什么需要这个?因为有些操作天生就慢,比如乘法器、大位宽加法器、或者某些状态机的判断逻辑。

什么时候用?

  • 使能信号控制的路径(比如每两个时钟周期才采样一次)
  • 慢速外设接口(比如I2C、SPI的慢速模式)
  • 复杂的组合逻辑(比如32位乘法器)

怎么用?

# 数据路径需要2个时钟周期
set_multicycle_path 2 -setup -from [get_pins u_mult/start] -to [get_pins u_mult/done]

# 保持时间也要相应调整(默认保持时间不调整)
set_multicycle_path 1 -hold -from [get_pins u_mult/start] -to [get_pins u_mult/done]

嗯,这里要注意一个坑:set_multicycle_path默认只调整建立时间,保持时间不变。什么意思呢?如果你设了2个周期的建立时间,但保持时间还是1个周期,工具可能会在保持时间上出问题。我建议你养成习惯,每次设多周期路径时,都显式地把保持时间也设上。

🔑 关键点:

  • 建立时间(setup):设成N,表示数据可以在N个周期后到达
  • 保持时间(hold):设成N-1,表示数据在N-1个周期内不能变化
  • 默认情况下,保持时间不随建立时间自动调整

4.3 最大/最小延迟(set_max_delay / set_min_delay)

这两个命令比较特殊,它们不是用来约束时钟路径的,而是用来约束组合逻辑路径或者异步信号的。说白了,就是给路径的延迟设一个硬性上限或下限。

什么时候用?

  • 异步信号之间的握手(比如两个不同时钟域的信号)
  • 输入输出端口的组合逻辑延迟
  • 复位信号的释放时间
  • 某些特殊的总线协议(比如异步FIFO的空满判断)

怎么用?

# 输入端口到寄存器的最大延迟
set_max_delay 5.0 -from [get_ports data_in] -to [get_pins u_logic/data_reg/D]

# 异步握手信号的最小延迟(防止毛刺)
set_min_delay 1.0 -from [get_pins u_async/req] -to [get_pins u_async/ack]

# 复位释放路径的约束
set_max_delay 10.0 -from [get_ports rst_n] -to [all_registers]
⚠️ 警告: set_max_delay/set_min_delay会覆盖掉时钟约束!如果你在一条有时钟的路径上用了set_max_delay,工具会忽略时钟周期,只按你设的延迟来优化。我曾经在调试一个bug时,发现一条路径的时序怎么都修不好,查了半天才发现是set_max_delay把时钟约束覆盖了。
💡 我的建议: 尽量少用set_max_delay/set_min_delay。能用set_false_path或者set_multicycle_path解决的问题,就别用这两个。它们太“暴力”了,容易把工具搞糊涂。只有在处理异步信号或者特殊接口时,才考虑使用。

4.4 三种约束的对比

约束类型 适用场景 对STA的影响 风险等级
set_false_path 跨时钟域、复位、测试模式 完全忽略该路径 低(但误用风险高)
set_multicycle_path 慢速逻辑、使能信号 放宽建立/保持时间 中(需注意保持时间)
set_max_delay/set_min_delay 异步信号、组合逻辑 覆盖时钟约束 高(容易误用)

4.5 避坑指南

做时序例外约束这么多年,我踩过的坑不少。这里分享几个最常见的:

  1. 伪路径设多了:我曾经在一个项目中,为了快速收敛时序,把几十条路径都设成了伪路径。结果后端反馈说有些路径根本没法布线,因为工具以为它们不重要。后来我花了三天时间一条条排查,才把真正需要约束的路径找出来。
  2. 多周期路径忘记设保持时间:这个坑我至少见过五次。设了2个周期的建立时间,但保持时间还是1个周期,结果数据在第一个周期就变了,导致采样错误。
  3. set_max_delay和时钟约束冲突:如果你在一条路径上同时用了set_max_delay和create_clock,工具会优先使用set_max_delay。这会导致时钟约束失效,时序分析结果完全不对。
  4. 跨时钟域路径没做同步处理:设了伪路径不代表你可以不管跨时钟域问题。伪路径只是让STA忽略这条路径,但功能上你还是要做同步器或者握手协议。

📌 总结一下:

  • 伪路径:用于你确定不需要时序检查的路径
  • 多周期路径:用于可以放宽时序要求的路径
  • 最大/最小延迟:用于特殊场景,慎用
  • 所有时序例外约束,都要在功能仿真中验证

好了,时序例外约束就讲到这里。下一章咱们聊聊时钟约束的高级技巧,比如时钟门控和时钟复用。这些东西在实际项目中非常常见,处理不好很容易出问题。