10.1 延迟模型的类型
在真实电路中,信号从输入到输出需要时间,这个时间来自两个方面:
-
门延迟(Gate Delay) :信号通过逻辑门(与门、或门等)需要的时间
-
线延迟(Wire Delay) :信号在导线中传播需要的时间
Verilog 提供了三种延迟模型来描述这些延迟。
一、分布延迟(Distributed Delay)
定义 :将延迟值分散在电路中每一个独立的元件上。
两种建模方式:
// 方式1:在门级原语上指定延迟
module and4(out, a, b, c, d);
output out;
input a, b, c, d;
wire an1, an2;
and #1 (an1, a, b); // 第1个与门延迟 1 个单位
and #2 (an2, c, d); // 第2个与门延迟 2 个单位
and #1.5 (out, an1, an2); // 第3个与门延迟 1.5 个单位
endmodule
// 方式2:在 assign 语句上指定延迟
module and4(out, a, b, c, d);
output out;
input a, b, c, d;
wire an1, an2;
assign #1 an1 = a & b; // 延迟 1 个单位
assign #2 an2 = c & d; // 延迟 2 个单位
assign #1.5 out = an1 & an2; // 延迟 1.5 个单位
endmodule
特点分析:
| 特点 | 说明 |
|---|---|
| 精度高 | 每个元件的延迟都可以单独指定,与实际电路更接近 |
| 建模繁琐 | 大规模电路需要为每个门指定延迟,工作量巨大 |
| 依赖内部结构 | 需要了解模块内部的逻辑门细节 |
| 适用场景 | 小规模电路、底层单元库建模 |
二、集总延迟(Lumped Delay)
定义 :将整个模块所有路径的延迟汇总到最后一个输出门上。
// 集总延迟:将所有延迟集中到最后一个与门
module and4(out, a, b, c, d);
output out;
input a, b, c, d;
wire an1, an2;
and (an1, a, b); // 无延迟
and (an2, c, d); // 无延迟
and #3.5 (out, an1, an2); // 所有延迟集中在这里(取最大路径延迟)
endmodule
计算集总延迟的方法:
-
计算所有输入到输出的路径延迟
-
取最大值作为集总延迟值
以上面分布延迟的电路为例:
-
路径 a → out:1 + 1.5 = 2.5
-
路径 b → out:1 + 1.5 = 2.5
-
路径 c → out:2 + 1.5 = 3.5
-
路径 d → out:2 + 1.5 = 3.5
-
集总延迟 = 3.5(取最大值)
特点分析:
| 特点 | 说明 |
|---|---|
| 建模简单 | 只需在最后一个输出门指定一个延迟值 |
| 精度较低 | 无法区分不同输入到输出的不同延迟 |
| 不依赖内部结构 | 只需知道整体延迟,无需了解内部细节 |
| 适用场景 | 小规模电路、快速评估 |
三、路径延迟(Path Delay / 引脚到引脚延迟)
定义 :为模块的每一个输入引脚到每一个输出引脚的路径分别指定延迟时间。
module and4(out, a, b, c, d);
output out;
input a, b, c, d;
wire an1, an2;
// 路径延迟:为每条输入→输出路径指定延迟
specify
(a => out) = 2.5;
(b => out) = 2.5;
(c => out) = 3.5;
(d => out) = 3.5;
endspecify
and (an1, a, b);
and (an2, c, d);
and (out, an1, an2);
endmodule
特点分析:
| 特点 | 说明 |
|---|---|
| 精度最高 | 每个输入到输出的延迟都可以独立指定 |
| 易于获取 | 可以从数据手册中直接查到 |
| 模块独立 | 即使模块内部逻辑改变,路径延迟说明可保持不变 |
| 适用场景 | 大规模电路、标准单元库、IP核(最常用) |
四、三种延迟模型对比
┌─────────────────────────────────────────────────────────────────────────────┐
│ 三种延迟模型对比 │
│ │
│ 分布延迟 集总延迟 路径延迟 │
│ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ │
│ │1│ │2│ │1.5│ │ │ │ │ │3.5│ │2.5│ │2.5│ │3.5│ │
│ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ │
│ 每个门有自己的延迟 所有延迟集中到最后 每个输入→输出有独立延迟 │
│ │
│ 精度:高 精度:低 精度:最高 │
│ 工作量:大 工作量:小 工作量:中等 │
│ 适用:小规模 适用:快速评估 适用:大规模/标准库 │
└─────────────────────────────────────────────────────────────────────────────┘
0.2 路径延迟建模
路径延迟是最常用的延迟模型,通过 specify 块来实现。
一、specify 块
specify 块是模块中一个独立的部分 ,以 specify 关键字开始,以 endspecify 结束。
重要规则:
-
specify块不能 出现在initial、always等其他块中 -
specify块中不能包含其他语句块
specify 块的三大功能:
| 功能 | 说明 |
|---|---|
| 指定路径延迟 | 为所有输入→输出路径指定引脚到引脚的延迟 |
定义 specparam 常量 |
在块内定义延迟参数常量 |
| 设置时序检查 | 使用 $setup、$hold、$width 等系统任务 |
二、并行连接(Parallel Connection)
语法 :( <源域> => <目标域> ) = <延迟值>;
特点 :源域的每一位与目标域中相应位 连接,位宽必须相同。
module M(out, a, b, c, d);
output out;
input a, b, c, d;
wire e, f;
specify
// 并行连接:每个输入独立连接到输出
(a => out) = 9;
(b => out) = 9;
(c => out) = 11;
(d => out) = 11;
endspecify
and a1(e, a, b);
and a2(f, c, d);
and a3(out, e, f);
endmodule
向量并行连接:
// a[3:0] 和 out[3:0] 都是4位向量
specify
(a => out) = 9; // 等价于下面4条语句
// (a[0] => out[0]) = 9;
// (a[1] => out[1]) = 9;
// (a[2] => out[2]) = 9;
// (a[3] => out[3]) = 9;
endspecify
位宽必须匹配 :(a[4:0] => out[3:0]) 是非法的,因为位宽不同(5位 vs 4位)。
三、全连接(Full Connection)
语法 :( <源域> *> <目标域> ) = <延迟值>;
特点 :源域的每一位 与目标域的每一位 都建立连接,位宽可以不同。
module M(out, a, b, c, d);
output out;
input a, b, c, d;
wire e, f;
specify
// 全连接:a、b的每一位都与out连接,延迟9
(a, b *> out) = 9;
// 全连接:c、d的每一位都与out连接,延迟11
(c, d *> out) = 11;
endspecify
and a1(e, a, b);
and a2(f, c, d);
and a3(out, e, f);
endmodule
全连接的展开:
// (a, b *> out) = 9; 等价于:
// (a => out) = 9;
// (b => out) = 9;
// 如果 a[31:0] 是32位,out[15:0] 是16位
// (a *> out) = 9; 等价于 32 × 16 = 512 条并行连接!
四、并行连接 vs 全连接
| 对比维度 | 并行连接 => |
全连接 *> |
|---|---|---|
| 连接方式 | 一对一(位对位) | 一对多(每位到每位) |
| 位宽要求 | 必须相同 | 可以不同 |
| 连接数量 | N 条(N = 位宽) | M × N 条(M和N为位宽) |
| 适用场景 | 数据总线按位对应 | 控制信号到多位输出 |
五、specparam 声明
在 specify 块内部,可以使用 specparam 定义延迟参数常量。
module D_FF(q, d, clk, reset);
output q;
input d, clk, reset;
specify
// 在 specify 块内定义参数
specparam d_to_q = 9;
specparam clk_to_q = 11;
(d => q) = d_to_q;
(clk => q) = clk_to_q;
endspecify
endmodule
specparam 的特点:
| 特点 | 说明 |
|---|---|
| 作用域 | 只能在定义它的 specify 块内部使用 |
与 parameter 的区别 |
parameter 是全局参数,specparam 是 specify 块专用 |
| 用途 | 集中管理延迟值,方便修改 |
| 建议 | 使用 specparam 而不是硬编码数值 |
六、边沿敏感路径(Edge-Sensitive Paths)
只在源信号的特定边沿才有效的路径延迟。
specify
// 在 clock 上升沿,从 clock 到 out 的延迟
// 上升延迟10,下降延迟8
(posedge clock => (out +: in)) = (10:8);
endspecify
语法说明:
-
out +: in:表示从in到out的数据路径(不反相) -
out -: in:表示从in到out的数据路径(反相)
七、条件路径延迟(Conditional Path Delay / SDPD)
路径延迟的值可能依赖于输入信号的状态。
module M(out, a, b, c, d);
output out;
input a, b, c, d;
wire e, f;
specify
// 条件路径延迟:根据信号a的值选择不同的延迟
if (a) (a => out) = 9;
if (~a) (a => out) = 10;
// 条件表达式包含两个信号
if (b & c) (b => out) = 9;
if (~(b & c)) (b => out) = 13;
// 使用拼接操作符
if ({c, d} == 2'b01) (c, d *> out) = 11;
if ({c, d} != 2'b01) (c, d *> out) = 13;
endspecify
and a1(e, a, b);
and a2(f, c, d);
and a3(out, e, f);
endmodule
特点:
-
条件表达式可以包含逻辑操作符、位操作符、缩减操作符、拼接操作符等
-
if语句中不能使用else结构 -
又称状态依赖路径延迟(SDPD)
八、上升/下降/关断延迟
引脚到引脚的延迟可以详细指定上升、下降和关断延迟值。
specify
// 1个延迟值:所有类型都用这个值
specparam t_delay = 11;
(clk => q) = t_delay;
// 2个延迟值:上升延迟、下降延迟
// 上升:0→1, 0→z, z→1
// 下降:1→0, 1→z, z→0
specparam t_rise = 9, t_fall = 13;
(clk => q) = (t_rise, t_fall);
// 3个延迟值:上升延迟、下降延迟、关断延迟
// 关断:0→z, 1→z
specparam t_rise = 9, t_fall = 13, t_turnoff = 11;
(clk => q) = (t_rise, t_fall, t_turnoff);
// 6个延迟值:精确指定所有转换
// 顺序:0→1, 1→0, 0→z, z→1, 1→z, z→0
specparam t_01 = 9, t_10 = 13, t_0z = 11;
specparam t_z1 = 9, t_1z = 11, t_z0 = 13;
(clk => q) = (t_01, t_10, t_0z, t_z1, t_1z, t_z0);
endspecify
延迟值数量规则:
| 延迟值数量 | 含义 | 是否合法 |
|---|---|---|
| 0个 | 无延迟(默认0) | ✅ |
| 1个 | 所有类型相同 | ✅ |
| 2个 | 上升、下降(关断取较小者) | ✅ |
| 3个 | 上升、下降、关断 | ✅ |
| 4个 | - | ❌ 非法 |
| 5个 | - | ❌ 非法 |
| 6个 | 0→1, 1→0, 0→z, z→1, 1→z, z→0 | ✅ |
| 7-11个 | - | ❌ 非法 |
| 12个 | 完整指定所有转换(含x) | ✅ |
九、最小/典型/最大延迟值(Min/Typ/Max)
与第5章门延迟一样,路径延迟也可以指定最小值、典型值、最大值。
specify
// 上升延迟:min=8, typ=9, max=10
// 下降延迟:min=12, typ=13, max=14
// 关断延迟:min=10, typ=11, max=12
specparam t_rise = 8:9:10;
specparam t_fall = 12:13:14;
specparam t_turnoff = 10:11:12;
(clk => q) = (t_rise, t_fall, t_turnoff);
endspecify
仿真器命令行控制:
| 命令选项 | 使用的延迟值 |
|---|---|
+maxdelays |
使用最大值 |
+typdelays |
使用典型值(默认) |
+mindelays |
使用最小值 |
十、X状态转换的保守处理
如果没有显式指定 X 转换的延迟,Verilog 采用保守方法:
-
X → 已知状态 :消耗可能的最大时间
-
已知状态 → X :消耗可能的最小时间
// 6个延迟的路径延迟说明
specparam t_01 = 9, t_10 = 13, t_0z = 11;
specparam t_z1 = 9, t_1z = 11, t_z0 = 13;
(clk => q) = (t_01, t_10, t_0z, t_z1, t_1z, t_z0);// X 转换的计算:
// 0 → x : min(t_01, t_0z) = min(9, 11) = 9
// 1 → x : min(t_10, t_1z) = min(13, 11) = 11
// z → x : min(t_z0, t_z1) = min(13, 9) = 9
// x → 0 : min(t_10, t_z0) = min(13, 13) = 13
// x → 1 : min(t_01, t_z1) = min(9, 9) = 9
// x → z : min(t_1z, t_0z) = min(11, 11) = 11
10.3 时序检查(Timing Checks)
指定路径延迟的目的是让仿真更接近实际电路。但光有延迟还不够------还需要检查设计是否满足时序约束(如建立时间、保持时间等)。
重要规则: 所有时序检查系统任务必须放在 specify 块内部。
一、$setup ------ 建立时间检查
建立时间(Setup Time) :数据必须在有效时钟边沿之前到达的最小时间。
specify
// 检查 data 相对于 clock 上升沿的建立时间
// 如果 T(posedge_clk - Tdata) < 3,报告违规
$setup(data, posedge clock, 3);
endspecify
时序图:
────建立时间────
←──────────────→
┌──────────────┐
data │ 有效数据 │
────────┴──────────────┴────────
↑
clock上升沿
二、$hold ------ 保持时间检查
保持时间(Hold Time) :数据必须在有效时钟边沿之后保持不变的最小时间。
specify
// 检查 data 相对于 clock 上升沿的保持时间
// 如果 T(data - Tposedge_clk) < 5,报告违规
$hold(posedge clock, data, 5);
endspecify
注意: $setup 和 $hold 的参数顺序是不同的!
| 系统任务 | 参数顺序 |
|---|---|
$setup |
$setup(data_event, ref_event, limit) |
$hold |
$hold(ref_event, data_event, limit) |
三、$setuphold ------ 同时检查建立和保持
Verilog 提供了同时检查建立时间和保持时间的系统任务。
specify
// 同时检查建立时间(2ns)和保持时间(3ns)
$setuphold(posedge clk, data, 2, 3);
endspecify
四、$width ------ 脉冲宽度检查
检查脉冲宽度是否满足最小宽度要求。
specify
// 检查 clock 的高电平脉冲宽度
// 如果高电平持续时间 < 6,报告违规
$width(posedge clock, 6);
endspecify
时序图:
←──── 宽度 ────→
┌──────────────┐
clock │ │
────────┘ └────────
$width 会自动检测 posedge 指定的边沿到下一个反向边沿之间的时间。
五、完整示例:带时序检查的 D 触发器
module D_FF(q, d, clk, reset);
output reg q;
input d, clk, reset;
// 时序逻辑
always @(posedge clk or posedge reset) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
// 时序检查和路径延迟
specify
// 路径延迟
specparam t_d_to_q = 2;
specparam t_clk_to_q = 3;
(d => q) = t_d_to_q;
(clk => q) = t_clk_to_q;
// 时序检查
specparam t_setup = 1.5;
specparam t_hold = 2.0;
specparam t_width = 3.0;
$setup(d, posedge clk, t_setup);
$hold(posedge clk, d, t_hold);
$width(posedge clk, t_width);
endspecify
endmodule
10.4 延迟反标注(Delay Back-Annotation)
一、什么是延迟反标注?
延迟反标注(Delay Back-Annotation) 是将物理版图提取的真实延迟信息标注回门级网表的过程。
简单说:前面的延迟都是"估算"的,而反标注是把芯片制造前的真实延迟填进去,让仿真结果更接近实际芯片。
二、为什么需要延迟反标注?
┌─────────────────────────────────────────────────────────────────────────────┐
│ 为什么需要延迟反标注? │
│ │
│ 1. 初步估算的延迟不精确 │
│ └── 综合工具给出的延迟是粗略估计的,没有考虑布线延迟 │
│ │
│ 2. 布局布线后的延迟才真实 │
│ └── 版图确定后,可以根据电阻R和电容C计算出真实的线延迟 │
│ │
│ 3. 验证时序是否真正满足 │
│ └── 用真实延迟重新仿真,确认芯片流片后能正常工作 │
└─────────────────────────────────────────────────────────────────────────────┘
三、延迟反标注的完整流程
第1步:RTL设计 + 功能仿真
↓
第2步:逻辑综合 → 门级网表(含粗略延迟估计)
↓
第3步:初步时序仿真 + STA(静态时序分析)
↓
第4步:布局布线 → 版图
↓
第5步:从版图提取R和C参数 → 计算真实延迟
↓
第6步:生成SDF文件(标准延迟格式文件)
↓
第7步:$sdf_annotate 将延迟反标注到门级网表
↓
第8步:精确时序仿真 + STA
↓
第9步:验证通过 → 流片生产
四、SDF 文件(Standard Delay Format)
SDF 是用于延迟反标注的标准格式文件。
SDF 文件包含的内容:
-
IOPATH:输入到输出的路径延迟
-
INTERCONNECT:互连线延迟
-
TIMING CHECK:时序约束参数(建立时间、保持时间等)
SDF 文件格式示例(简化):
(DELAYFILE
(DESIGN "top")
(DATE "Sep 7 2026")
(TIMESCALE 1ns)
(CELL
(CELLTYPE "and_gate")
(INSTANCE u_and)
(DELAY
(ABSOLUTE
(IOPATH A Z (1.5:1.8) (1.3:1.7))
(IOPATH B Z (1.5:1.8) (1.3:1.7))
)
)
)
)
五、$sdf_annotate 系统任务
$sdf_annotate 是 Verilog 提供的用于延迟反标注的系统任务。
语法:
$sdf_annotate("sdf_file.sdf", top_module);
示例:
module testbench;
// 实例化设计模块
top u_top(...);
initial begin
// 将 SDF 文件中的延迟反标注到设计
$sdf_annotate("top.sdf", u_top);
#1000 $finish;
end
endmodule
常用选项:
+sdf_verbose:输出详细的反标注信息,用于验证反标注是否正确