第四章:路径分类与分析方法

各位同学,今天我们来聊聊时序分析里最基础、也最容易被忽视的内容——路径分类。我做了这么多年STA,发现很多新人一上来就盯着setup/hold看,却连自己在分析什么路径都没搞清楚。这就像看病连症状都没问清楚就开药,你说能准吗?

其实路径分类没那么玄乎。说白了,就是搞清楚信号从哪来、到哪去。根据起点和终点的不同,我们把它分成四大类:reg-to-reg、in-to-reg、reg-to-out、in-to-out。再加上一个特殊的时钟域交叉(CDC),基本上就覆盖了所有场景。

4.1 四大路径类型详解

我们先看个表格,把四种路径的基本特征理清楚。这是我当年带新人时总结的,简单粗暴但好用。

路径类型 起点 终点 典型约束 常见问题
reg-to-reg 寄存器时钟端 寄存器数据端 create_clock 组合逻辑过长
in-to-reg 输入端口 寄存器数据端 set_input_delay 输入延时过紧
reg-to-out 寄存器时钟端 输出端口 set_output_delay 输出负载过大
in-to-out 输入端口 输出端口 set_input_delay + set_output_delay 纯组合路径

4.2 reg-to-reg 路径分析

这是最核心的路径类型,也是我们花时间最多的地方。我习惯叫它「内部路径」,因为它完全在芯片内部打转。

分析reg-to-reg路径时,你只需要关注两件事:时钟周期和组合逻辑深度。举个例子:

// 一个典型的reg-to-reg路径
always @(posedge clk) begin
    data_out <= data_in + 8'h1F;  // 加法器组合逻辑
end

这里从data_in的寄存器到data_out的寄存器,中间只有一个加法器。如果时钟周期是10ns,加法器延迟是2ns,那setup slack就是10 - 2 - 0.5(clock skew) - 0.2(register setup time) = 7.3ns。嗯,很充裕。

关键点:reg-to-reg路径的时序裕量 = 时钟周期 - 组合逻辑延迟 - 时钟偏斜 - 寄存器建立时间

我在项目中遇到过最夸张的一次,一个reg-to-reg路径里塞了30多级组合逻辑。你想想看,那得是什么场景?后来发现是设计人员把状态机的译码逻辑全堆在一个周期里了。拆成流水线后,问题立马解决。

4.3 in-to-reg 路径分析

这种路径涉及芯片的输入端口。说白了,就是外部信号进来后,经过一些组合逻辑,最终被内部寄存器采到。

分析in-to-reg路径时,set_input_delay是关键。这个值怎么设?我建议你多跟系统工程师聊聊,搞清楚外部器件的输出时序。

// 设置输入延时
set_input_delay -clock clk -max 2.5 [get_ports data_in]
set_input_delay -clock clk -min 0.8 [get_ports data_in]

这里-max 2.5ns表示外部数据最晚在时钟沿后2.5ns才稳定。你想想看,如果内部组合逻辑再花3ns,时钟周期是10ns,那setup slack就是10 - 2.5 - 3 - 0.5 = 4ns。还行。

我的经验:in-to-reg路径最容易出问题的地方是输入延时设得太乐观。我曾经吃过一次亏,把set_input_delay设小了0.5ns,结果流片回来芯片在高温下就挂。从那以后,我每次都会留10%的余量。

4.4 reg-to-out 路径分析

这种路径是从内部寄存器到输出端口。外部器件等着你的数据,你得保证在它要求的时间内把数据送出去。

set_output_delay在这里扮演重要角色。它表示外部器件需要数据提前多久准备好。

// 设置输出延时
set_output_delay -clock clk -max 4.0 [get_ports data_out]
set_output_delay -clock clk -min 1.0 [get_ports data_out]

如果内部寄存器到输出端口的组合逻辑延迟是3ns,时钟周期10ns,那setup slack就是10 - 4 - 3 - 0.5 = 2.5ns。嗯,还行。

注意:reg-to-out路径的hold检查往往比setup更棘手。因为输出路径上的组合逻辑可能很少,导致数据变化太快,外部器件来不及采样。我见过一个案例,就是因为输出路径上只有一级buffer,hold violation修了整整两周。

4.5 in-to-out 路径分析

这种路径最特殊,它没有寄存器参与。信号从输入端口进来,经过纯组合逻辑,直接从输出端口出去。

说实话,现代芯片设计中这种路径越来越少见了。但有些简单的控制信号、复位信号还是会用到。

分析in-to-out路径时,你需要同时考虑输入延时和输出延时。公式很简单:

setup slack = 时钟周期 - 输入延时 - 组合逻辑延迟 - 输出延时 - 时钟偏斜

举个例子:时钟周期10ns,输入延时2ns,组合逻辑延迟3ns,输出延时4ns,时钟偏斜0.5ns。那setup slack就是10 - 2 - 3 - 4 - 0.5 = 0.5ns。很紧张了。

避坑指南:我曾经在in-to-out路径上栽过跟头。当时一个复位信号从输入到输出走了5级组合逻辑,结果复位时间不够,导致芯片初始化失败。后来我强制要求所有跨芯片边界的信号都必须经过寄存器同步,从此再没出过类似问题。

4.6 时钟域交叉(CDC)的时序处理

CDC是时序分析里最头疼的问题之一。为什么?因为跨时钟域的信号,STA工具根本没法准确分析。你想想看,两个不同频率的时钟,它们的相位关系是不确定的,setup/hold检查怎么做?

我个人的处理方法是:对于CDC路径,STA只做形式检查,真正的时序验证交给专门的CDC工具(比如SpyGlass CDC、RealIntent等)。

常见的CDC处理方式有几种:

  • 双级同步器:最简单、最常用。两个寄存器串联,降低亚稳态传播概率。
  • 握手协议:用req/ack信号进行跨时钟域通信,适合控制信号。
  • 异步FIFO:适合大批量数据传输,用格雷码指针避免多bit同步问题。
  • DMUX结构:数据信号加使能信号,通过多路选择器实现跨时钟域。
// 双级同步器示例
always @(posedge clk_dst) begin
    sync_reg1 <= async_signal;
    sync_reg2 <= sync_reg1;
end

这里要注意,sync_reg1的输出可能处于亚稳态,但经过一个时钟周期后,sync_reg2大概率已经稳定了。这就是双级同步器的原理。

我的建议:做CDC分析时,一定要设置false_path。因为STA工具默认会对所有路径做setup/hold检查,如果不设false_path,它会报一堆假违例,让你白忙活。

// 设置CDC路径为false_path
set_false_path -from [get_clocks clk_a] -to [get_clocks clk_b]

但注意,设了false_path不代表你可以不管CDC了。你仍然需要用专门的CDC工具去验证同步器的正确性。我见过有人设了false_path就以为万事大吉,结果流片回来发现亚稳态导致数据错误,那叫一个惨。

4.7 实战建议

好了,四种路径类型和CDC的处理方法都讲完了。最后给大家几个实战建议:

  1. 先分类,再分析。拿到时序报告后,先看路径类型,再对症下药。
  2. reg-to-reg是重点。80%的时序问题都出在这里,花最多精力。
  3. in-to-reg和reg-to-out要跟系统工程师对齐。输入输出延时设错了,后面全白干。
  4. in-to-out尽量少用。能用寄存器隔开就隔开,省心。
  5. CDC一定要设false_path。但别忘了用专门工具做验证。

嗯,这一章的内容就到这里。下一章我们会深入讲解setup violation的定位方法,到时候我会拿一个真实的案例来拆解,保证让你看得过瘾。