第三章的第1部分见链接文章
STA 静态时序分析 第三章——标准单元库(1)
目录
3.3时序模型-组合逻辑单元
考虑二输入与门的时序弧,他们都是正单调的,因此一个输入端口的上升对应着一个输出端口的上升,反之亦然。
对于两输入的与门,这里存在四种延时:
从上到下分别是:
- 由A引起的输出上升延迟
- 由A引起的输出下降延迟
- 由B引起的输出上升延迟
- 由B引起的输出下降延迟
对于NLDM非线性模型而言,将会有四个表模型用来指定延时。同样的,这里也会有四个表模型用来指定输出转换时间。
3.3.1 延迟以及压摆模型
对于一个三输入与非门,从一个输入端口INP1到输出端口OUT的时序模型如下所示
pin (OUT) {
max_transition : 1.0;
timing() {
related_pin : "INP1";
timing_sense : negative_unate;
cell_rise(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0513, 0.1537, 0.5280", \
"0.1018, 0.2327, 0.6476", \
"0.1334, 0.2973, 0.7252");
}
rise_transition(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0417, 0.1337, 0.4680", \
"0.0718, 0.1827, 0.5676", \
"0.1034, 0.2173, 0.6452");
}
cell_fall(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0617, 0.1537, 0.5280", \
"0.0918, 0.2027, 0.5676", \
"0.1034, 0.2273, 0.6452");
}
fall_transition(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0817, 0.1937, 0.7280", \
"0.1018, 0.2327, 0.7676", \
"0.1334, 0.2973, 0.8452");
}
. . .
}
. . .
}
在这个例子中,从INP1到OUT的时序弧的特性使用两个单元延迟表cell_rise和cell_fall,两个转换时间表rise_transition和fall_transition来描述。输出max_transition值也在上面的示例中。
与非门单元中的时序弧为负单调的,这表示输出引脚电平跳变方向与输入引脚电平跳变方向是相反的。因此,查找cell_rise表对应于输入引脚上的下降过渡时间。同样,通过与门单元或者或门单元的时序弧是正单调的,因为输出引脚电平跳变方向与输入引脚电平跳变方向相同。
3.3.2 通用的组合逻辑单元
试想一个组合逻辑单元,有三输入两输出。
一个像这样的模块有很多时序弧 ,因为从任意的输入指向任意的输出,如果时序弧是正单调的,那么输出就与输入有相同的极性;如果时序弧是负单调的,那么输出就与输入有相反的极性。有些时序弧既不是正也不是负单调。典型的例子就是异或门。在异或门中,可以通过改变一个输入的电平值,去改变输出随着另一个输入变化的极性。这样的时序弧称为状态依赖的。(简单解释:异或门的一个输入为0时,输出等于另一个输入,正;异或门的一个输入为1时,输出等于另一个输入的相反值,负;因此可以说这个异或门的时序弧是状态依赖的)
3.4 时序模型-时序逻辑单元
考虑上图所示的时序逻辑单元,对于同步的输入,会有以下时序弧
- 建立时间检查时序弧(上升&下降)
- 保持时间检查时序弧(上升&下降)
对于异步的输入,会有以下的时序弧
- 恢复时间检查时序弧
- 移除时间检查时序弧
对于触发器的同步输出,Q引脚或者是QN引脚,存在一个时钟到输出(Delay)的时序弧,可以上升也可以下降。
所有的同步时序弧都要关心时钟的有效边沿,也就是上升沿或者下降沿,因为在边沿处时序单元捕获数据。此外,时钟引脚和异步引脚(如复位引脚)还需要进行脉冲宽度(pulse width)时序检查。
3.4.1 同步时序检查-保持、建立时间
这两项时间检查可以保证时钟的有效边沿到来时候,数据不是模糊的,而是确定的,稳定的。
建立时间(setup time)定义:能接受的数据达到阈值的最晚时间与有效时钟沿达到阈值的时间间隔。
保持时间(hold time)定义:能接受的数据达到阈值的最早时间与有效时钟沿达到阈值的时间间隔。
人话:建立时间说数据不能来的太晚,否则捕捉不到稳定的数据;保持时间说数据在时钟有效沿过后,走的不能太早,否则捕捉不到有效数据。(上班就是早来晚走)
建立时间保持时间检查的实例:
对于一个时序单元的同步引脚,保持时间和建立时间的约束通常用二维表模型来描述。以下的代码描述了数据引脚的建立、保持时间的时序信息。
pin (D) {
direction : input;
. . .
timing () {
related_pin : "CK";
timing_type : "setup_rising";
rise_constraint ("setuphold_template_3x3") {
index_1("0.4, 0.57, 0.84"); /* Data transition */
index_2("0.4, 0.57, 0.84"); /* Clock transition */
values( /* 0.4 0.57 0.84 */ \
/* 0.4 */ "0.063, 0.093, 0.112", \
/* 0.57 */ "0.526, 0.644, 0.824", \
/* 0.84 */ "0.720, 0.839, 0.930");
}
fall_constraint ("setuphold_template_3x3") {
index_1("0.4, 0.57, 0.84"); /* Data transition */
index_2("0.4, 0.57, 0.84"); /* Clock transition */
values( /* 0.4 0.57 0.84 */\
/* 0.4 */ "0.762, 0.895, 0.969", \
/* 0.57 */ "0.804, 0.952, 0.166", \
/* 0.84 */ "0.159, 0.170, 0.245");
}
}
timing () {
related_pin : "CK";
timing_type : "hold_rising";
rise_constraint ("setuphold_template_3x3") {
index_1("0.4, 0.57, 0.84"); /* Data transition */
index_2("0.4, 0.57, 0.84"); /* Clock transition */
values( /* 0.4 0.57 0.84 */ \
/* 0.4 */ "-0.220, -0.339, -0.584", \
/* 0.57 */ "-0.247, -0.381, -0.729", \
/* 0.84 */ "-0.398, -0.516, -0.864");
}
fall_constraint ("setuphold_template_3x3") {
index_1("0.4, 0.57, 0.84"); /* Data transition */
index_2("0.4, 0.57, 0.84");/* Clock transition */
values( /* 0.4 0.57 0.84 */ \
/* 0.4 */ "-0.028, -0.397, -0.489", \
/* 0.57 */ "-0.408, -0.527, -0.649", \
/* 0.84 */ "-0.705, -0.839, -0.580");
}
}
}
以上的代码表示,建立、保持时间在输入引脚D上的约束,是与时序单元时钟的上升沿是有关系的。这个二维模型中,约束引脚为D,有关联的引脚为CK,这个查找表是基于setuphold_template_3x3 模版的,这个模板如下所示。
lu_table_template(setuphold_template_3x3) {
variable_1 : constrained_pin_transition;
variable_2 : related_pin_transition;
index_1 ("1000, 1001, 1002");
index_2 ("1000, 1001, 1002");
}
/* The constrained pin and the related pin can be in either order, that is, variable_1 could be the related pin transition.
However, these designations are usually consistent across all
templates in a library. */
在上面的表中,第一个索引,是外部变量(变化最小的);第二个索引是内部变量(变化最大的)。因此在数据引脚D上的上升转换时间为0.4ns,时钟引脚CK上的上升转换时间为0.84ns时,收到约束的D引脚的上升沿的建立时间为0.112ns。如果在D引脚是下降沿,那么就去查下面的那个表,如果没有正好匹配的值,就用之前说到的插值法。
注意:rise_constraint 和 fall_constraint指的都是约束引脚的rise和fall, 至于时钟的有效边沿是上升还是下降,是使用timeing_type来指定的。
建立-保持时间检查中的负值
有些时候保持(hold)时间可以是负值,当数据路径(从 D 到内部锁存点)比时钟路径(从 CK 到内部锁存点)长时,数据可以在时钟信号到达后仍然有效。这种情况下,数据可以提前改变而不会导致错误。建立(setup)时间同样可以是负值,此时数据可以晚于有效时钟沿变化到达。
但是注意!两个时间不能全部为负值。因为建立时间和保持时间的和应该是一个正数,如果建立时间为负,那么保持时间应该足够大,保证两者之和为正。下图中建立时间为正,保持时间为负,两个时间相加,就是信号保持稳定的时间段。(下图中留白的那段)
- 数据输入引脚的负保持时间具有一定优势,负保持时间允许数据在时钟边缘之后可以发生变化,从而增加了在设计中应对时钟偏差(clock skew)和其他时序不确定性的灵活性。
- 在传统设计中,如果维持数据有效所需的保持时间正值,则可能需要插入额外的缓冲器以满足保持时间要求。这不仅增加了设计复杂性,还可能影响性能和面积。负保持时间的使用意味着在面对扫描模式带来的时序问题时,设计人员几乎不需要插入缓冲器,从而简化设计。
- 扫描模式通常用于集成电路的测试,通过将触发器输出连接到下一个触发器的扫描数据输入引脚,形成扫描链。负保持时间有助于确保在测试过程中,时钟信号下的各种操作能顺利进行,而不至于因为数据改变过快而导致测试失败。(因为保持时间是负的,所以时钟沿过后随便变化数据,不会影响)
3.4.2 异步的检查
恢复时间(Recovery)和移除时间(Removal)检查
异步的操作可以不受时钟的控制,如置位和复位。当异步输入引脚不活跃的时候,时钟的有效边沿将会捕获数据。异步的恢复、移除约束检查将会保证,在下个有效的时钟沿到来的时候,异步输入引脚处于不活跃(inactive)的状态。
- 恢复时间:时钟沿之前,异步输入引脚达到稳定所需的最小时间。
- 移除时间:时钟沿之后,异步输入引脚的值需要稳定不能改变的最小时间。
可以联想之前的这张图:
脉冲宽度检查
目的是为了保证输入引脚的脉冲宽度,能够达到最小的要求。举例来说,如果时钟CK频率太高,导致脉冲宽度过窄,那么就有可能在时钟的有效边沿无法正确的锁存数据。脉冲检查也可适用于其他同步或非同步的输入端口。
以下是举例(恢复、移除以及脉冲宽度检查)
这个例子是针对于触发器的异步复位引脚CDN的,其中恢复时间和撤销时间检查还与时钟引脚CK有关。在进行最小脉冲宽度的时序检查时,指定 fall_constraint 就是信号下降时候的约束
pin(CDN) {
direction : input;
capacitance : 0.002236;
. . .
timing() {
related_pin : "CDN";
timing_type : min_pulse_width;
fall_constraint(width_template_3x1) { /*low pulse check*/
index_1 ("0.032, 0.504, 0.788"); /* Input transition */
values ( /* 0.032 0.504 0.788 */ \
"0.034, 0.060, 0.377");
}
}
timing() {
related_pin : "CK";
timing_type : recovery_rising;
rise_constraint(recovery_template_3x3) { /* CDN rising */
index_1 ("0.032, 0.504, 0.788"); /* Data transition */
index_2 ("0.032, 0.504, 0.788"); /* Clock transition */
values( /* 0.032 0.504 0.788 */ \
/* 0.032 */ "-0.198, -0.122, 0.187", \
/* 0.504 */ "-0.268, -0.157, 0.124", \
/* 0.788 */ "-0.490, -0.219, -0.069");
}
}
timing() {
related_pin : "CP";
timing_type : removal_rising;
rise_constraint(removal_template_3x3) { /* CDN rising */
index_1 ("0.032, 0.504, 0.788"); /* Data transition */
index_2 ("0.032, 0.504, 0.788"); /* Clock transition */
values( /* 0.032 0.504 0.788 */ \
/* 0.032 */ "0.106, 0.167, 0.548", \
/* 0.504 */ "0.221, 0.381, 0.662", \
/* 0.788 */ "0.381, 0.456, 0.778");
}
}
}
在进行脉冲宽度检查时,在timing_type指定所需要检查的时序类型。针对CDN的脉冲宽度检查是,指定为低脉冲,fall_constraint的原因是因为CDN是低电平有效。由于恢复时间和撤销时间检查是针对异步引脚被置为无效的时刻,因此在以下示例中仅存在上升约束。
3.4.3 传播延时
传播延时:时钟CK的有效边沿 到 输出的上升/下降沿所需要的时间。下面的例子是一个下降沿的触发器,测量的是由CKN时钟引脚到输出引脚Q的延时。这是一个非单调的时序弧,因为时钟的有效沿既可以造成Q的上升沿,也可以造成Q的下降沿。
timing() {
related_pin : "CKN";
timing_type : falling_edge;
timing_sense : non_unate;
cell_rise(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7"); /* Clock transition */
index_2 ("0.16, 0.35, 1.43"); /* Output capacitance */
values ( /* 0.16 0.35 1.43 */ \
/* 0.1 */ "0.0513, 0.1537, 0.5280", \
/* 0.3 */ "0.1018, 0.2327, 0.6476", \
/* 0.7 */ "0.1334, 0.2973, 0.7252");
}
rise_transition(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0417, 0.1337, 0.4680", \
"0.0718, 0.1827, 0.5676", \
"0.1034, 0.2173, 0.6452");
}
cell_fall(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0617, 0.1537, 0.5280", \
"0.0918, 0.2027, 0.5676", \
"0.1034, 0.2273, 0.6452");
}
fall_transition(delay_template_3x3) {
index_1 ("0.1, 0.3, 0.7");
index_2 ("0.16, 0.35, 1.43");
values ( \
"0.0817, 0.1937, 0.7280", \
"0.1018, 0.2327, 0.7676", \
"0.1334, 0.2973, 0.8452");
}
}
在之前已经提到,输出端口的延时以一个二维表的形式呈现,取决于输入转换时间以及输出引脚的电容。但是,在这个例子中,输入转换时间使用的是在CKN引脚的下降时间,因为这是一个下降沿触发的触发器,这在timeing_type中也指出了。如果是上升沿触发的触发器,在timing_type将会指出rising_edge。
部分内容翻译自“Static Timing Analysis for Nanometer Designs A Practical Approach” 欢迎指正!