时序库基础:时序弧、建立保持时间、传输延迟与转换时间
各位同学,今天我们来聊聊时序库的根基——时序弧、建立保持时间、传输延迟和转换时间。这些概念,说白了就是STA(静态时序分析)的“语法规则”。你想想看,没有这些基础,你连一个最简单的触发器都分析不了。
我个人习惯把时序库比作一本“芯片行为词典”。它告诉EDA工具:每个标准单元在什么条件下会怎么工作。而时序弧,就是这本词典里的“动词”。
一、时序弧的概念
什么是时序弧?简单说,就是从一个输入引脚到输出引脚之间的时序关系。我刚开始接触时,觉得这名字挺唬人,其实它就是一条“路径”。
时序弧分几种类型:
- 组合时序弧:输入变化直接导致输出变化。比如一个与非门,A变低,Y就变高。这种弧没有时钟参与。
- 时序时序弧:从时钟到输出,或者从数据到时钟。比如触发器的CK到Q,或者D到CK的建立时间检查。
- 边沿敏感弧:只在时钟上升沿或下降沿触发。这是时序单元的核心。
在库文件里,时序弧是用timing()组描述的。我给你们看个例子:
pin (CK) {
timing () {
related_pin : "Q";
timing_type : "rising_edge";
cell_rise (delay_template_7x7) {
index_1 ("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0");
index_2 ("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0");
values ( \
"0.12, 0.18, 0.25, 0.35, 0.50, 0.70, 0.95", \
...
);
}
}
}
嗯,这里要注意:related_pin指定了弧的起点,timing_type定义了弧的类型。我见过不少新手把rising_edge和combinational搞混,结果时序分析全乱套。
关键点:每个时序弧必须明确指定“从哪到哪”、“什么条件下触发”、“延迟是多少”。缺一不可。
二、建立时间与保持时间
这两个概念,是时序分析的“命门”。我当年做第一个项目时,就因为建立时间违例,芯片流片回来跑不到目标频率,差点被老板骂死。
建立时间(setup time):数据在时钟有效沿之前必须保持稳定的最短时间。说白了,就是数据要“提前到场”,不能迟到。
保持时间(hold time):数据在时钟有效沿之后必须保持稳定的最短时间。也就是数据不能“早退”。
在库文件里,它们是这样定义的:
pin (D) {
timing () {
related_pin : "CK";
timing_type : "setup_rising";
rise_constraint (constraint_template_7x7) {
index_1 ("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0");
index_2 ("0.1, 0.3, 0.7, 1.2, 2.0, 3.5, 5.0");
values ( \
"0.08, 0.12, 0.18, 0.25, 0.35, 0.50, 0.70", \
...
);
}
}
}
这里timing_type是setup_rising,表示上升沿建立时间检查。保持时间则是hold_rising。
我的经验:建立时间和保持时间不是固定值,它们会随输入转换时间和输出负载变化。所以库文件里用的是二维查找表。我曾经遇到一个案例,某工艺角下保持时间违例,就是因为没考虑PVT(工艺、电压、温度)变化。
三、传输延迟与转换时间
这两个参数,决定了信号在单元内部的“旅行速度”。
传输延迟(propagation delay):从输入变化50%到输出变化50%的时间差。它分为cell_rise(输出上升延迟)和cell_fall(输出下降延迟)。
转换时间(transition time):信号从低到高(或高到低)变化所需的时间。通常定义为10%到90%(或20%到80%)。库文件里用rise_transition和fall_transition表示。
看个例子:
pin (Y) {
timing () {
related_pin : "A";
timing_type : "combinational";
cell_rise (delay_template_7x7) { ... }
rise_transition (delay_template_7x7) { ... }
cell_fall (delay_template_7x7) { ... }
fall_transition (delay_template_7x7) { ... }
}
}
你想想看,为什么要有转换时间?因为信号不是瞬间跳变的。转换时间越长,延迟就越大,功耗也越高。我在做低功耗设计时,经常需要平衡转换时间和延迟。
避坑指南:我曾经遇到一个bug——库文件里cell_rise和rise_transition的查找表维度不一致,导致工具报错。所以建模时一定要检查索引和值的维度是否匹配。
四、时序检查类型
时序检查,就是验证设计是否满足时序要求。常见的检查类型有:
| 检查类型 | 库文件关键字 | 说明 |
|---|---|---|
| 建立时间检查 | setup_rising / setup_falling |
数据必须在时钟沿前稳定 |
| 保持时间检查 | hold_rising / hold_falling |
数据必须在时钟沿后稳定 |
| 恢复时间检查 | recovery_rising / recovery_falling |
异步复位信号必须在时钟沿前释放 |
| 移除时间检查 | removal_rising / removal_falling |
异步复位信号必须在时钟沿后保持 |
| 最小脉宽检查 | min_pulse_width |
时钟或数据信号的最小高/低电平宽度 |
| 时钟抖动检查 | clock_uncertainty |
时钟边沿的不确定性 |
举个例子,恢复时间检查在库文件里是这样写的:
pin (RN) {
timing () {
related_pin : "CK";
timing_type : "recovery_rising";
rise_constraint (constraint_template_7x7) { ... }
}
}
嗯,这里要注意:恢复时间和移除时间,很多人容易搞混。说白了,恢复时间就是异步复位信号“醒来”的时间,移除时间就是它“睡去”的时间。
总结一下:时序弧是路径,建立/保持时间是约束,传输延迟和转换时间是性能指标,时序检查是验证手段。这四个概念,构成了时序库的骨架。我建议你们在建模时,先画一个时序图,把每个弧、每个检查都标出来,这样不容易出错。
好了,这一章就到这里。下一章我们会讲如何用Liberty格式描述这些时序信息,包括查找表的构建和索引选择。有什么问题,欢迎课后交流。