2、时序库基础:Liberty(.lib)文件结构、PVT条件、时序弧与延迟计算
好,咱们今天聊聊时序库。说白了,它就是芯片的「速度说明书」。你想想看,没有这个文件,STA工具根本不知道每个门电路跑多快、延迟多大。我刚开始接触STA那会儿,总觉得.lib文件就是个黑盒子,后来被逼着去读里面的内容,才发现门道不少。
2.1 Liberty文件长什么样?
Liberty文件,后缀是.lib,本质上是文本文件。你可以用任何文本编辑器打开它。我习惯用vim,因为高亮显示后结构更清晰。
它的核心结构是这样的:
library(xxx_typ_1v8_25c) {
delay_model : "table_lookup";
in_place_swap_mode : "match_footprint";
...
cell(AND2X1) {
area : 5.6;
pin(A) {
direction : input;
capacitance : 0.003;
...
}
pin(Y) {
direction : output;
function : "(A & B)";
timing() {
related_pin : "A";
timing_sense : "positive_unate";
cell_rise(delay_template_7x7) {
index_1("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0");
index_2("0.01, 0.02, 0.04, 0.08, 0.15, 0.3, 0.6");
values( \
"0.12, 0.15, 0.19, 0.25, 0.35, 0.52, 0.78", \
"0.14, 0.17, 0.21, 0.27, 0.37, 0.54, 0.80", \
...
);
}
fall_transition(delay_template_7x7) { ... }
}
}
}
}
嗯,看着有点复杂对吧?其实拆开来看就三块:
- 库级别:定义工艺、PVT条件、延迟模型类型
- 单元级别:每个标准单元(比如AND2X1)的面积、功耗
- 引脚级别:输入电容、输出驱动、时序弧
2.2 PVT条件——芯片的「天气」
PVT是Process、Voltage、Temperature的缩写。说白了,就是芯片在不同「天气」下的表现。
| 参数 | 典型值 | 最差情况 | 最好情况 |
|---|---|---|---|
| Process (工艺角) | TT (Typical) | SS (Slow-Slow) | FF (Fast-Fast) |
| Voltage (电压) | 1.8V | 1.62V (-10%) | 1.98V (+10%) |
| Temperature (温度) | 25°C | 125°C (高温) | -40°C (低温) |
为什么会这样?因为芯片的延迟受这三个因素影响很大。温度越高,载流子迁移率下降,门延迟变大。电压越低,驱动能力变弱,延迟也变大。工艺角就更不用说了,同一片晶圆上不同位置的晶体管速度都不一样。
我在项目中遇到过一件事:有一次芯片在实验室跑得好好的,一到客户现场就死机。查了半天,发现是温度从25°C升到了85°C,setup时间不够了。从那以后,我每次做STA都坚持用最差PVT条件来检查setup。
2.3 时序弧——延迟的「路径」
时序弧,英文叫Timing Arc。它描述的是:从一个输入引脚到输出引脚,信号变化需要多长时间。
常见的时序弧类型有:
- 组合时序弧:输入变化直接导致输出变化,比如与门、或门
- 时序时序弧:从时钟到输出的延迟,比如触发器的clk->Q
- 建立/保持时序弧:数据相对于时钟的约束时间
每个时序弧都有两个方向:
- rise:输出从低到高
- fall:输出从高到低
而且,延迟还跟输入转换时间和输出负载有关。这就是为什么.lib文件里要用二维查找表。
timing() {
related_pin : "A";
timing_sense : "positive_unate";
cell_rise(delay_template_7x7) {
index_1("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0"); // 输入转换时间
index_2("0.01, 0.02, 0.04, 0.08, 0.15, 0.3, 0.6"); // 输出负载电容
values(...);
}
}
你看,index_1是输入转换时间,index_2是输出负载。工具根据实际值查表,然后插值得到精确延迟。我曾经踩过一个坑:以为查表是线性插值,结果发现工具用的是双线性插值,精度更高但计算量也更大。
2.4 延迟计算——工具到底怎么算的?
STA工具计算延迟的步骤,说白了就三步:
- 查表:根据输入转换时间和输出负载,在.lib的查找表中找到对应的延迟值
- 插值:如果实际值不在表格的格点上,就用插值算法估算
- 累加:把路径上所有单元的延迟加起来,再加上互连线延迟
举个例子,假设一个反相器:
- 输入转换时间:0.5ns
- 输出负载:0.12pF
- 查表得到cell_rise = 0.23ns
- 查表得到rise_transition = 0.18ns
那么,这个反相器的延迟就是0.23ns。输出转换时间0.18ns会作为下一级单元的输入转换时间。
关键点:延迟计算是迭代的。前一级的输出转换时间,就是后一级的输入转换时间。所以STA工具需要从输入到输出,一级一级地算下去。
我个人习惯在写时序约束时,先看看.lib里最慢的单元延迟是多少。如果最慢的单元延迟都超过时钟周期的10%,那这条路径大概率会有问题。提前发现,提前优化,省得后面返工。
嗯,关于时序库的基础知识,今天就聊到这儿。下一节咱们会讲如何用STA工具读取.lib文件,并生成时序报告。到时候我会分享一些实际项目中遇到的坑,比如库文件版本不匹配导致的错误,还有PVT条件选错导致的误报。