一、时序约束基础概念
什么是时序约束?
时序约束,说白了就是你告诉工具:「我的芯片要在多快频率下跑,哪些路径是关键路径,哪些可以放松。」
我刚开始做设计时,觉得这玩意儿就是个形式。随便写几个周期约束,剩下的全交给工具自动推断。结果呢?流片回来,芯片在低温下能跑,高温下就挂了。查了两个月,发现是一条跨时钟域的路径没约束住。
从那以后,我养成了一个习惯:每写一行 RTL,先想清楚这条路径的时序约束该怎么写。
时序约束的本质:告诉 EDA 工具你的设计在什么条件下工作,让工具去检查这些条件下所有寄存器之间的路径是否满足建立时间和保持时间要求。
为什么需要时序约束?
你想想看,一个芯片里有几百万个寄存器。每个寄存器都有建立时间和保持时间的要求。如果不加约束,工具怎么知道哪些路径是关键的?
我归纳了三个核心原因:
- 保证功能正确:数据必须在时钟沿之前稳定下来(建立时间),之后也要保持一段时间(保持时间)。违反任何一个,寄存器就可能采到错误数据。
- 指导工具优化:综合和布局布线工具会根据约束来优化。约束越精确,优化越到位。我曾经见过一个项目,因为约束写得太松,工具把关键路径优化成了非关键路径,结果时序收敛不了。
- 验证设计裕量:芯片要量产,必须留出足够的时序裕量。温度、电压、工艺偏差都会影响时序。没有约束,你根本不知道裕量够不够。
避坑指南:我曾经接手过一个项目,前同事写了 2000 多行约束,但大部分是假的。比如把异步信号当同步约束,或者把多周期路径标成了单周期。结果 STA 报告一片绿,流片回来却频繁死机。所以,约束不仅要写,还要写对。
建立时间与保持时间
这两个概念是时序分析的基石。我每次给新人培训,都会画这张图:
建立时间(Setup Time)
建立时间,就是时钟沿来之前,数据必须提前到达并稳定住的时间窗口。这个窗口有多长?取决于工艺库。比如 28nm 工艺下,一个普通 D 触发器的建立时间可能在 50ps 到 200ps 之间。
我举个例子:假设时钟频率是 1GHz(周期 1ns),组合逻辑延迟是 600ps,寄存器建立时间是 100ps。那么留给时钟偏斜的裕量就是 1ns - 600ps - 100ps = 300ps。如果时钟偏斜超过 300ps,建立时间就违例了。
个人经验:我在做一款 AI 加速芯片时,发现一条关键路径的建立时间裕量只有 20ps。按常规做法,应该降频。但客户要求必须跑 1.2GHz。最后我通过调整时钟树,把偏斜从 80ps 降到了 30ps,硬是救回来了。所以,建立时间违例不一定非要降频,时钟树优化也是条路。
保持时间(Hold Time)
保持时间,是时钟沿来之后,数据还要继续稳定一段时间。这个时间通常比建立时间短,但同样重要。
保持时间违例有个特点:频率越低越容易出问题。为什么?因为保持时间检查的是同一个时钟沿上的数据,跟周期没关系。你降频只能解决建立时间问题,对保持时间没用。
| 对比项 | 建立时间(Setup) | 保持时间(Hold) |
|---|---|---|
| 检查对象 | 数据从上一个时钟沿到当前时钟沿的路径 | 数据在当前时钟沿上的稳定性 |
| 与频率关系 | 频率越高越容易违例 | 与频率无关 |
| 修复方法 | 降频、减少组合逻辑级数、优化时钟树 | 插入延迟单元、增加路径长度 |
| 典型场景 | 长路径、高扇出、跨模块路径 | 短路径、时钟偏斜过大、扫描链 |
| STA 报告 | 显示负裕量(Negative Slack) | 显示保持时间违例(Hold Violation) |
我曾经踩过的坑:做一款低功耗 IoT 芯片时,为了省电,我把所有寄存器都换成了低功耗库。结果保持时间全面违例。查了半天才发现,低功耗库的保持时间比标准库大了 3 倍。从那以后,我换库之前一定会先跑一遍 STA 对比。
时序约束的核心公式
嗯,这里要记住两个公式,所有时序分析都基于它们:
// 建立时间检查
T_clk_period + T_clk_skew >= T_clk_to_q + T_comb_logic + T_setup
// 保持时间检查
T_clk_to_q + T_comb_logic >= T_hold + T_clk_skew
其中:
- T_clk_period:时钟周期
- T_clk_skew:时钟偏斜(接收时钟 - 发射时钟)
- T_clk_to_q:寄存器时钟到输出的延迟
- T_comb_logic:组合逻辑延迟
- T_setup:建立时间
- T_hold:保持时间
核心要点:建立时间检查的是「数据跑得太慢」,保持时间检查的是「数据跑得太快」。一个要加延迟,一个要减延迟。两者是矛盾的,设计时要权衡。
小结
时序约束不是什么高深的东西。它就是告诉工具你的设计目标,然后让工具去验证。建立时间和保持时间是两个最基本的检查项,所有复杂的时序分析都从这里衍生出来。
我个人习惯是:写 RTL 之前先画时序图,标出关键路径的建立时间和保持时间裕量。这样写出来的代码,时序收敛的概率会高很多。