- 编译器
- 硬件仿真
- EDA
【免费下载链接】iverilog
Icarus Verilog
Icarus Verilog 的-tblif目标把精化(elaborate)后的设计转换为 BLIF(Berkeley Logic Interchange Format)网表文件,供伯克利大学开发的 ABC 综合与验证系统通过read_blif命令直接读取。本文从官方文档 tgt-blif.rst 出发,结合 tgt-blif/ 目录下的真实源码与 ivtest/blif 回归测试,完整讲解该目标的命令行用法、输入约束、输出网表结构、底层发射逻辑、支持单元清单与已知限制,帮助你快速上手用 Icarus Verilog 驱动 ABC 做逻辑综合。
1. BLIF 目标是什么:定位与应用场景
BLIF 是 ASIC 设计流程中常见的一种门级网表交换格式。Icarus Verilog 的 BLIF 代码生成器(即-tblif目标)把精化后的设计输出为.blif文件,这个文件可以被 ABC(A System for Sequential Synthesis and Verification,伯克利大学开发的时序综合与验证工具)通过其read_blif命令读入。也就是说,这条路径的核心价值在于:用 Icarus Verilog 做前端解析与精化,把 HDL 设计变成 ABC 能消费的门级网表,随后交给 ABC 做逻辑综合与优化。
需要强调的是,该目标主要服务于 ASIC 设计场景(文档原话为 “This package contains tools sometimes used by ASIC designers”),它与 Icarus Verilog 自带的仿真目标(-tvvp)用途完全不同:它不生成可执行仿真代码,而是生成面向综合工具的文本网表。
2. 基本用法:一条命令完成转换
BLIF 目标的调用方式与其他目标一致,通过-t开关选择目标、用-o指定输出文件。官方文档给出的命令格式为:
iverilog -tblif -o<path>.blif <source files>...其中:
-tblif:选择 BLIF 代码生成目标(-t后跟目标名blif,见 Documentation/targets/index.rst 对-t开关的说明);-o<path>.blif:指定输出网表文件路径,-o与路径之间不需要空格(也可以写作-o <path>.blif);<source files>...:一个或多个源文件,可以是 Verilog、SystemVerilog、VHDL——凡是 Icarus Verilog 支持的 HDL 都可以,前提是它们能精化(elaborate)到该代码生成器支持的有限子集。
在源码层面,-o参数的解析位于 tgt-blif/blif.cc 的target_design()入口:函数首先通过ivl_design_flag(des, "-o")取得输出文件路径,随后定位设计的根作用域并最终调用emit_blif()把网表写入该文件;若-o缺失,程序会以assert(blif_path)的方式直接终止,因此输出路径是必填参数。
2.1 综合前的预处理链(functor 链)
值得关注的是,-tblif目标并不是直接把精化结果原样吐出,而是在发射网表之前运行一组综合优化 functor。这一点记录在目标的配置文件 tgt-blif/blif.conf(及其精简版 tgt-blif/blif-s.conf,两个文件内容一致)中:
functor:synth2 functor:synth functor:syn-rules functor:cprop functor:nodangle flag:DLL=blif.tgt其中:
functor:synth2/functor:synth:执行逻辑综合(synth与synth2分别位于仓库根目录的 synth.cc 和 synth2.cc),把高层表达式转换为门级逻辑;functor:syn-rules:应用综合规则(syn-rules.y);functor:cprop:做常量传播(cprop.cc),化简带常量的逻辑;functor:nodangle:移除悬空节点(nodangle.cc);flag:DLL=blif.tgt:加载blif.tgt动态库作为代码生成器。
也就是说,你的 HDL 源码会先经过 Icarus Verilog 的标准精化,再经上述综合链优化为门级结构,最后由 BLIF 发射器写出。这也是为什么文档要求输入必须是“结构化的”(structural)——顶层行为(如进程)无法被该目标处理(详见第 6 节)。
3. 输入要求:结构化设计的边界
文档明确指出:“This code generator is intended to process structural Verilog source code.” 即本目标面向结构化源码。具体约束如下:
- 源文件可以是 Verilog、SystemVerilog、VHDL,只要最终能精化到该生成器支持的有限子集;
- 设计必须能降级为**显式逻辑门(explicit logic gates)与连续赋值(continuous assignments)**的组合——这是当前支持的全部内容;
- 顶层模块(root module)会被实例化出的子模块递归展开,最终以单一扁平模型输出。
从 tgt-blif/blif.cc 的target_design()可以印证这一约束的实现方式:函数遍历设计的所有根作用域,只接受类型为IVL_SCT_MODULE的模块作用域;对于IVL_SCT_PACKAGE(SystemVerilog 包)会打印 “Skipping package scope …” 并跳过;遇到其他未知类型则报 “Don't know how to handle root scope …”。随后调用ivl_design_process()扫描设计中的进程,任何进程都会触发错误输出:
%s:%u: sorry: BLIF: Processes not supported yet.对应源码见 tgt-blif/blif.cc 的process_scan_fun()。因此,always/initial等行为代码(initial块在精化后同样可能形成进程)无法通过该目标输出,这与文档“仅支持显式逻辑门与连续赋值”的说明完全一致。
4. 输出模型特性:扁平化与位展开
BLIF 目标输出网表有三个结构性特征,理解它们有助于读懂生成的文件:
单一根模块:设计只能有一个根模块。该根模块的名字成为 BLIF 模型中
.model记录的名字(.model <name>)。如果设计包含多个根模块,生成器会列出所有找到的根模块并报错退出,见 tgt-blif/blif.cc:BLIF: The BLIF code generator requires that there be only one root module scope. Found these root modules: ...完全扁平化:根模块可以实例化子模块,子模块再嵌套子模块,但输出模型会被完全展开(flattened),不产生任何子电路(subcircuit)调用。从 tgt-blif/blif.cc 的
emit_scope()可以看到,发射器从根模型出发递归遍历每个子作用域(ivl_scope_childs()),把所有逻辑门与 LPM 器件平铺写入同一个.model内,最后统一以.end收尾。位向量展开(bit explosion):BLIF 和 ABC 处理的是“位”而不是“向量”,因此模型的端口和内部所有位向量都会被拆解成一个个独立的位。在端口层面,
print_signal_bits()(见 tgt-blif/blif.cc)把每个信号按宽度逐个打印到.inputs/.outputs行;在内部层面,每个连接点(nexus)经由blif_nex_data_t(见 tgt-blif/nex_data.h)分配符号名并维护“规范位索引 → 信号位号”的映射(get_name_index())。
4.1 顶层模型骨架
综合以上机制,一个典型输出文件的结构为:
.model <根模块名> .inputs <所有输入位,空格分隔> .outputs <所有输出位,空格分隔> <若干 .names / .latch / .subckt 记录> .end端口方向由ivl_signal_port()决定(tgt-blif/blif.cc):IVL_SIP_INPUT归入.inputs,IVL_SIP_OUTPUT归入.outputs;特别地,IVL_SIP_INOUT(双向端口)会同时报错 “Model port … is bi-directional.” 并同时计入输入与输出两侧——BLIF 模型不支持双向端口。
5. 源码级剖析:网表如何被发射
发射流程(emit_blif(),tgt-blif/blif.cc)按顺序完成:写.model头 → 收集并写出.inputs/.outputs→ 递归遍历作用域发射逻辑门与 LPM 器件 → 发射常量 → 写.end。下面逐类说明各类元件的发射细节。
5.1 逻辑门:.names 记录
逻辑门由 tgt-blif/logic_gate.cc 的print_logic_gate()发射为 BLIF 的.names表格(truth table)记录。每个门的每一位都单独写出一条.names,格式为:
.names <输入位1> <输入位2> ... <输出位> <输入组合> <输出值>支持的逻辑类型与对应真值表(由ivl_logic_type()分派)如下:
| Icarus 逻辑类型 | 发射的 .names 记录 |
|---|---|
IVL_LO_AND | 所有输入为 1 时输出 1(111… 1) |
IVL_LO_OR | 任一输入为 1 输出 1(1- 1、-1 1,两输入) |
IVL_LO_XOR | 10 1、01 1(两输入) |
IVL_LO_NAND | 0- 1、-0 1(两输入) |
IVL_LO_NOR | 所有输入为 0 时输出 1 |
IVL_LO_XNOR | 00 1、11 1(两输入) |
IVL_LO_BUF | 1 1 |
IVL_LO_NOT | 0 1 |
IVL_LO_PULLDOWN | 单输出、恒 0(0) |
IVL_LO_PULLUP | 单输出、恒 1(1) |
IVL_LO_BUFZ | 与 BUF 相同(1 1) |
任何未覆盖的逻辑类型会在网表中写入# ERROR: Logic type N not handled并累计错误计数(rc += 1)。
5.2 LPM 器件:算术与时序单元
精化后的较高层结构(如加法器、比较器、触发器、多路选择器、移位等)以 LPM(Library of Parameterized Modules)器件形式存在,由 tgt-blif/lpm.cc 的print_lpm()按ivl_lpm_type()分派到各自处理函数。当前覆盖的 LPM 类型包括:
IVL_LPM_ADD/IVL_LPM_SUB:加法/减法器(lpm_add.cc);IVL_LPM_CMP_EQ/IVL_LPM_CMP_EEQ/IVL_LPM_CMP_NE/IVL_LPM_CMP_NEE/IVL_LPM_CMP_GE/IVL_LPM_CMP_GT:比较器(lpm_cmp_eq.cc、lpm_cmp_gt.cc);IVL_LPM_CONCAT/IVL_LPM_CONCATZ:位拼接,逐位用.names缓冲实现;IVL_LPM_FF:触发器(lpm_ff.cc);IVL_LPM_MUX:多路选择器(lpm_mux.cc);IVL_LPM_PART_VP:向量部分选择(lpm_part_vp.cc);IVL_LPM_RE_AND/OR/XOR/NAND/NOR/XNOR:归约逻辑(lpm_re_logic.cc);IVL_LPM_SHIFTL/IVL_LPM_SHIFTR:移位(lpm_shift.cc);IVL_LPM_SIGN_EXT:符号扩展(lpm_sign_ext.cc)。
以加法器为例(tgt-blif/lpm_add.cc),HDL 写法assign Q = A ^ B ^ Cin; assign Cout = A&B | A&Cin | B&Cin;会被逐位展开为一系列.names真值表(最低位Q[0] = A[0] ^ B[0],逐位进位Cout通过/cout后缀命名的内部连接传递,1 位加法器则只发射一条 XOR 记录),并在文件头部以# file:line: IVL_LPM_ADD: width=N注释标明来源位置。
5.3 触发器:.latch 记录
时序单元由 tgt-blif/lpm_ff.cc 发射为 BLIF 的.latch记录。最简单的上升沿触发器输出为:
.latch <D位> <Q位> re <时钟位> 3其中re表示上升沿触发,末尾的3是 BLIF 规定的 latch 初始化值编码。若存在时钟使能(clock-enable),发射器会先做一次等价变换,把always @(posedge C) if (CE) Q <= D;改写成always @(posedge C) Q <= CE? D : Q;(注释中说明 “In ASIC-land, this is probably OK”),用一条.names与一条.latch组合实现。此外,异步清零(async clear)与异步置位(async set)当前未实现,遇到时会分别打印 “sorry: blif: Asynchronous clear not implemented yet” / “Asynchronous set not implemented yet” 并累计错误。
5.4 常量:.names 恒值记录
设计中的常量由 tgt-blif/constants.cc 的emit_constants()统一发射:它遍历设计中所有常量,通过scope_is_in_model()(tgt-blif/blif.cc)只保留属于当前模型作用域之下的常量,然后逐位写出。位值为 1 时输出.names <位名> # const 1加1行;位值为 0 时输出只有头的.names <位名> # const 0(空表即恒 0);其他值(如 x/z)则以注释形式记录。
6. 已知限制一览
根据文档与源码,-tblif目标当前的限制可以归纳为:
- 仅支持显式逻辑门与连续赋值:行为级代码(进程)不支持,遇到进程直接报 “Processes not supported yet”(tgt-blif/blif.cc 的
process_scan_fun()); - 只能有一个根模块:多个根模块会导致生成器报错并列出所有根模块名;SystemVerilog 包作用域会被跳过;
- 不支持双向(inout)端口:会报 “Model port … is bi-directional.” 错误;
- 触发器不支持异步清零/异步置位:对应 LPM 触发器的 async clear/set 均未实现(tgt-blif/lpm_ff.cc);
- 模型必须完全扁平化、位展开:不产生子电路引用,向量一律拆位。
所有错误都会累加到全局blif_errors(声明于 tgt-blif/priv.h),最终由target_design()返回给上层作为退出码,因此在命令行上可以通过退出状态判断转换是否成功。
7. 回归测试:如何验证转换正确性
仓库在 ivtest/blif 目录下提供了完整的回归测试集,包含 27 个.v源文件与对应的.v测试平台(testbench),并登记在 ivtest/blif.list 中(条目blif01a~blif02k、blif_shift、blif_sign_ext)。这些测试由 ivtest/regress 回归框架驱动,测试策略很有代表性:源设计(如blif01a.v)用iverilog -tblif生成 BLIF 网表,同时同一份文件在TEST_BENCH宏下编译为普通仿真,逐位比对行为,确保综合出的网表语义与 RTL 一致。
以 ivtest/blif/blif01a.v 为例:它定义了一个参数化宽度(parameter WID = 4)的加法器addN,内部用genvar生成循环实例化单比特加法器add1,每个add1由两条连续赋值实现:
assign Q = A ^ B ^ Cin; assign Cout = A&B | A&Cin | B&Cin;其测试平台用initial穷举所有A/B组合,比较Q与adx+bdx,失败即打印FAILED。这个例子恰好同时覆盖了文档强调的两类受支持结构:显式逻辑门(add1内部的连续赋值)与模块实例化(addN实例化add1,最终会被扁平化展开)。
如果你在自己机器上复现,可以按第 2 节的命令对该文件单独执行iverilog -tblif -oaddN.blif ivtest/blif/blif01a.v,并观察生成的.model addN、.inputs/.outputs位展开结果,再尝试用 ABC 的read_blif addN.blif读入验证。
8. 端到端工作流小结
综合来看,使用-tblif目标的完整工作流是:
- 编写结构化的 Verilog/SystemVerilog/VHDL 源码(显式门、连续赋值、可精化的模块实例化);
- 执行
iverilog -tblif -o<path>.blif <source files>...; - Icarus Verilog 完成精化,经
synth2 → synth → syn-rules → cprop → nodangle综合链优化(tgt-blif/blif.conf); - BLIF 发射器写出单一扁平
.model(tgt-blif/blif.cc),含.inputs/.outputs、.names真值表、.latch触发器与常量记录; - 将
.blif交给 ABC,用read_blif命令读入做后续逻辑综合与验证。
这条链路让 Icarus Verilog 可以扮演“HDL 前端 + 综合预处理器”的角色,把行为语义稳定的结构化设计无缝送入 ASIC 界常用的 BLIF/ABC 生态,是学习门级综合流程时一套轻量、可复现的本地工具链。
- 编译器
- 硬件仿真
- EDA
【免费下载链接】iverilog
Icarus Verilog
相关推荐
Icarus Verilog 网表(Netlist)格式全解析:从解析、编译到代码生成的核心数据结构指南
Icarus Verilog 网表(Netlist)格式全解析:从解析、编译到代码生成的核心数据结构指南 导读 本文基于 Icarus Verilog(iver
编译器硬件仿真EDAOpenDesign Design System 2.0 Source Evidence 机制:Token 溯源契约与派生输出治理实践
OpenDesign Design System 2.0 Source Evidence 机制:Token 溯源契约与派生输出治理实践 design syste
编译器硬件仿真EDAIcarus Verilog VHDL 代码生成器(-tvhdl)实战指南:从 Verilog 到 VHDL 的自动翻译
Icarus Verilog VHDL 代码生成器( tvhdl)实战指南:从 Verilog 到 VHDL 的自动翻译 导读 Icarus Verilog 内
编译器硬件仿真EDA
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考