iverilog 门级网表后仿真流程:Yosys 综合加 SDF 延时反标怎么做
更新时间:2026-11-03。仿真是什么 讲过前仿真验证的是 RTL 功能,所有赋值零延时;综合是什么 讲过综合产物是门级网表。网表拿到手之后还有一个自然的问题:这些门在真实延时下连起来,testbench 还能不能跑过?这就是门级后仿真。本篇用 Yosys 0.33 综合一个 4 位计数器,生成一份最小 SDF(Standard Delay Format,标准延时格式),再用容器内 Icarus Verilog 12.0 完成反标后仿真,和 RTL 前仿逐条对照。
本文要回答的问题
- 前仿已经 PASS,为什么还要在门级网表上再跑一遍后仿?
- 没有真实 PDK 和标准单元库时,开源工具链怎么做一版教学口径的后仿?
$sdf_annotate、SDF 文件、门级仿真模型三者怎么配合,iverilog 编译时为什么要加-gspecify?- 综合网表里的
$_NAND_这类 Yosys generic 门,仿真器从哪里找到它们的定义? - SDF 反标后输出该在时钟沿后多久变化,怎么从波形时间戳确认延时真的灌进去了?
整条链路先给一张流程卡:
读这张卡注意两个输入的区别:第 1 步的前仿和第 4 步的后仿共用同一个 testbench,激励与判分逻辑完全一致,差别只在被编译的「设计」是 RTL 还是网表加延时。
一、这一步在流程里的位置
门级后仿真卡在综合之后、签核之前。它的上游是综合器写出的门级网表和延时数据,下游是「门级向量能不能过」的结论;它不产布局布线,也不替代静态时序分析。
读图抓一条主线:testbench 同时指向前仿和后仿,因此两次仿真结果可以直接相减比较。网表、SDF、门模型是后仿的三件套——在 Xilinx 等厂商流程里第三样通常是库模型加全局复位模块 glbl,在 Yosys generic 门流程里换成自写的 gates_sim.v。需要强调口径:本机没有真实 PDK,SDF 里的延时是按门类型统一自定的教学数字(门 18-40ps、触发器时钟到输出 70-80ps),只用于演示反标机制;产线流程里 SDF 由实现工具结合 Liberty 库与 SPEF 寄生写出,完整工业链路见 SDF 反标门级仿真。
二、命令逐段解读
一条 bash run.sh 走完四件事,核心命令有三条。
综合脚本 synth.ys 只有五行:read_verilog counter.v 读入 RTL;hierarchy -top counter 定顶层;synth -flatten -top counter 完成化简并映射到 Yosys 自带 generic 门($_* 单元),-flatten 拍平层次让网表只剩一个模块;stat 打印 cell 账单;最后 write_verilog -noattr -noexpr netlist.v 导出逐门实例化的门级网表。-noattr 去掉源码路径等属性,-noexpr 禁止把门折回 assign 表达式,保证每个实例与 stat 的计数一一对应。
Yosys 0.33 这个版本没有 write_sdf 命令,所以 SDF 用一个 awk 小生成器从网表实例逐行产出:awk -f gen_sdf.awk netlist.v > counter.sdf。它按门类型查延时表(NAND 30/25ps、XOR 40/35ps 等),为每个实例写一段 (CELL (CELLTYPE ...) (INSTANCE ...) (DELAY (ABSOLUTE (IOPATH ...))))。
后仿编译命令参数最多,逐个讲:
iverilog -g2012 -gspecify -DGATES -o gate.vvp gates_sim.v netlist.v tb_counter.v
vvp gate.vvp-gspecify:启用 specify 块支持。iverilog 默认省略 specify 块,此时连$sdf_annotate也会被整体丢掉并告警;不加这个开关,SDF 等于白写。-DGATES:打开 testbench 里的`ifdef GATES分支,执行initial $sdf_annotate("counter.sdf", u_dut);,让同一个 tb 在不定义宏时就是纯前仿。-g2012:允许较新的 Verilog 语法,门模型里转义单元名配合它最稳妥。- 文件顺序:
gates_sim.v(门定义)、netlist.v(门的实例化)、tb_counter.v(顶层)。少了门模型,仿真器会报Unknown module type: $_NAND_。
三、输入输出文件:网表、SDF、门模型各长什么样
综合后的网表每个触发器和逻辑门都是显式实例。为避免实例名带 q_reg[0] 这种位选括号(iverilog 的 SDF 解析器不认),RTL 特意把四位寄存器拆成 q0/q1/q2/q3 四个标量触发器。真实片段如下:
\$_NAND_ _05_ ( .A(q[1]), .B(q[0]), .Y(_00_) );
\$_ANDNOT_ _08_ ( .A(en), .B(_02_), .Y(carry) );
\$_DFFE_PN0P_ q0_reg ( .C(clk), .D(_04_[0]), .E(en), .Q(q[0]), .R(rst_n) );生成的 SDF 是纯文本,一段一个实例,min:typ:max 三元组这里三列相同:
(CELL (CELLTYPE "$_NOT_")
(INSTANCE _04_)
(DELAY (ABSOLUTE
(IOPATH A Y (20:20:20) (18:18:18)))))门模型 gates_sim.v 为网表实际出现的 7 种单元提供功能和 specify 路径,路径默认延时为 0,数字全部等 SDF 覆盖。组合门直接用内建原语;触发器的输出端口经连续赋值驱动,时钟到输出才能成为可标注路径:
module \$_NAND_ (A, B, Y);
input A, B; output Y;
specify (A => Y) = (0,0); (B => Y) = (0,0); endspecify
nand g (Y, A, B);
endmodule
module \$_DFFE_PN0P_ (D, C, E, Q, R);
input D, C, E, R; output Q; reg qi;
specify
(posedge C => (Q+:D)) = (0,0);
(negedge R => (Q-:0)) = (0);
endspecify
assign Q = qi;
always @(posedge C or negedge R)
if (!R) qi <= 1'b0; else if (E) qi <= D;
endmodule四、常见错与检查点
| 现象 | 先查什么 | 纠正 |
|---|---|---|
告警 Omitting $sdf_annotate() since specify blocks are being omitted,输出时刻毫无变化 | 编译是否漏了 -gspecify | iverilog 默认省略 specify;加 -gspecify 重编 |
| SDF 不报错,但 0.03ns 级延时全部变成 0 | 模块时间单位;实测 SDF 数字按模块时间单位解释并舍入 | 设计与 tb 统一 `timescale 1ps/1ps,SDF 直接写皮秒整数(如 30) |
Unable to match ModPath C -> q0 | SDF 的 IOPATH 终点写成了连接线名 | 终点必须是模块端口名 Q,不是它外部连的 q0;实例名也不能含 [] |
Unknown module type: $_XOR_ | 编译清单缺门模型 | 把网表用到的 generic 门全部在门模型文件中定义并参与编译 |
一个通用核对动作:SDF 里 CELL 段的数量必须等于网表实例数。本次两边都是 13;运行时 SDF WARNING 计数应为 0,出现一条就说明有延时没挂上去,后仿结论不成立。还要留意 iverilog 的 specify 支持边界:setup/hold 等时序检查并不会因为 -gspecify 就自动报警,那是 iverilog specify 块支持边界 的话题,门级 X 传播的保守性见 门级仿真 X 传播。
五、真实跑一遍:前仿与门级后仿对照
在 geek-eda:0.3-sta 容器里执行 make run,先看综合账单与 SDF 头部:
实录第 2 节按类型统计了 13 个实例(4 个 $_DFFE_PN0P_ 加 9 个组合门),第 3 节生成器回写「13」并打印 SDF 开头,NOT 单元的 A→Y 上升/下降延时分别是 20ps、18ps,与延时表逐行对得上。
前仿和后仿的功能判分都在每个时钟沿后 1ns 采样,19 个周期含一次 15 到 0 的回卷。两次仿真的时间戳差异就是这一步的核心证据:
读这张终端卡对照两段输出:前仿(第 4 节)中 q 严格在 10000/30000ps 等时钟沿变化,计到 15 时 carry 与 q 在同一拍 290000ps 拉高;后仿(第 5 节)中 q=1 出现在 10080ps,恰好是时钟沿加 80ps 的 C→Q 延时;回卷周期还能看到低位先变(30070ps,下降 70ps)、高位后变(30080ps)的逐位涟漪。最能说明问题的是进位:第 15 拍 carry 在 290161ps 才拉高——80ps 触发器输出延时,再加 NAND、OR、ANDNOT 三级共 81ps 组合链。两次仿真最后都打印 PASS,证明延时没有改变功能,只是把每个事件推到了它物理上该出现的时刻。复位路径同理:复位拉低后 q 在 1060ps(异步复位延时 60ps)清零,经进位组合逻辑后 carry 在 1145ps 落定。
实验环境
- 机器/系统:macOS(Darwin 25.5,Apple Silicon),容器镜像 geek-eda:0.3-sta(Docker)
- 工具版本:
Yosys 0.33 (git sha1 2584903a060)、Icarus Verilog version 12.0 (stable),均为容器内yosys -V、iverilog -V照抄 - 日期:2026-11-03
- 完整命令:
bash run.sh(等价make run),容器内顺序执行yosys -s synth.ys→awk -f gen_sdf.awk netlist.v > counter.sdf→iverilog -o pre.vvp counter.v tb_counter.v && vvp pre.vvp→iverilog -g2012 -gspecify -DGATES -o gate.vvp gates_sim.v netlist.v tb_counter.v && vvp gate.vvp
代码获取
完整 RTL、testbench、Yosys 脚本、门级模型、SDF 生成器与 Makefile 已同步到公开仓库:geek-doc.cn/experiments/flow-iverilog-sdf-post-sim,克隆后进入该目录执行 make run 即可在 geek-eda:0.3-sta 容器中复现综合与前、后仿真,make clean 删除网表和 vvp 产物。
这一篇在知识体系里的位置
- 想先理解「前仿真/后仿真」这两个词的入门含义:读 仿真是什么;
- 想看工业签核口径下 SPEF、Liberty 如何参与 SDF 生成,INTERCONNECT 与 IOPATH 各管什么:读 SDF 反标门级仿真 与 SPEF 与 SDF 数据格式;
- 关心网表作为交接物的契约与门仿在流程中的位置:读 网表交接:门级仿真与 ECO。
总结
iverilog 门级网表后仿真流程的关键是「同一份 tb,换一个被测世界」:先用 Yosys synth -flatten 加 write_verilog -noattr -noexpr 得到逐门实例化的 generic 门网表,再为每个实例写出 IOPATH 延时的 SDF(Yosys 0.33 无 write_sdf,教学口径用小脚本按门类型生成),然后把网表、带 specify 路径的门模型、testbench 三件套用 iverilog -gspecify 一起编译,由 $sdf_annotate 把延时挂上去。四个检查点务必记住:必须加 -gspecify;时间单位统一用 1ps 并在 SDF 写皮秒整数,否则亚纳秒延时会被舍成 0;IOPATH 终点写端口名而不是线网名,实例名避开位选括号;SDF 段数等于网表实例数且运行时零 WARNING。本次 19 个周期前仿、后仿均 PASS,后仿中触发器输出晚 80ps、最长进位链晚 161ps,功能不变而事件时刻全部带上了物理延时——这正是前仿真验证功能、后仿真验证带延时门级网表的分工所在。