时序库基础:时序弧、建立保持时间、传输延迟与转换时间

各位同学,今天我们来聊聊时序库的根基——时序弧、建立保持时间、传输延迟和转换时间。这些概念,说白了就是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_edgecombinational搞混,结果时序分析全乱套。

关键点:每个时序弧必须明确指定“从哪到哪”、“什么条件下触发”、“延迟是多少”。缺一不可。

二、建立时间与保持时间

这两个概念,是时序分析的“命门”。我当年做第一个项目时,就因为建立时间违例,芯片流片回来跑不到目标频率,差点被老板骂死。

建立时间(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_typesetup_rising,表示上升沿建立时间检查。保持时间则是hold_rising

我的经验:建立时间和保持时间不是固定值,它们会随输入转换时间和输出负载变化。所以库文件里用的是二维查找表。我曾经遇到一个案例,某工艺角下保持时间违例,就是因为没考虑PVT(工艺、电压、温度)变化。

三、传输延迟与转换时间

这两个参数,决定了信号在单元内部的“旅行速度”。

传输延迟(propagation delay):从输入变化50%到输出变化50%的时间差。它分为cell_rise(输出上升延迟)和cell_fall(输出下降延迟)。

转换时间(transition time):信号从低到高(或高到低)变化所需的时间。通常定义为10%到90%(或20%到80%)。库文件里用rise_transitionfall_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_riserise_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格式描述这些时序信息,包括查找表的构建和索引选择。有什么问题,欢迎课后交流。