2、网表格式解析:EDIF、Verilog网表、SPICE网表的格式对比与解析工具

说实话,做硬件逆向这些年,我打交道最多的就是各种网表文件。

你拿到一个芯片的版图,提取出门级电路,最后落到手里的,就是一堆网表。但网表也有好几种「方言」——EDIF、Verilog网表、SPICE网表。不懂它们的脾气,解析起来能让你怀疑人生。

今天我就带你把这三种格式掰开揉碎,看看它们到底长什么样,以及怎么用工具搞定它们。

2.1 三种网表格式的「出身」与「性格」

先说说它们各自的来历。我个人习惯把网表分成两类:一类是给EDA工具看的,一类是给仿真器看的。EDIF和Verilog网表属于前者,SPICE网表属于后者。

特性 EDIF Verilog网表 SPICE网表
全称 Electronic Design Interchange Format Verilog HDL 的门级描述 Simulation Program with Integrated Circuit Emphasis
主要用途 EDA工具间数据交换 数字综合后的门级网表 模拟/混合信号电路仿真
抽象层级 逻辑门级 逻辑门级 + 寄存器传输级 晶体管级(电阻、电容、MOS管)
可读性 差(括号嵌套地狱) 中等(类C语法) 较好(文本行式)
解析难度

嗯,这里要注意:EDIF是上世纪80年代的产物,设计初衷是为了让不同厂商的EDA工具能互相读懂对方的数据。结果呢?它自己成了最难看懂的格式。我在项目中遇到过好几次,拿到客户的EDIF文件,打开一看,满屏的括号,头皮发麻。

2.2 EDIF格式解析:括号里的「天书」

EDIF的核心语法就一个字:括号。左括号开头,右括号结尾,层层嵌套。说白了,它就是Lisp语言的亲戚。

一个典型的EDIF网表长这样:

(edif hello-world
    (edifVersion 2 0 0)
    (edifLevel 0)
    (keywordMap (keywordLevel 0))
    (status
        (written
            (timeStamp 2024 01 15 10 30 00)
            (program "Synopsys")
            (version "2023.12")
        )
    )
    (library my_lib
        (edifLevel 0)
        (technology (numberDefinition (scale 1 (unit distance) (e 6))))
        (cell AND2
            (cellType GENERIC)
            (view netlist
                (viewType NETLIST)
                (interface
                    (port A (direction INPUT))
                    (port B (direction INPUT))
                    (port Z (direction OUTPUT))
                )
                (contents
                    (instance I1 (cellRef AND2 (libraryRef my_lib)))
                    (net N1
                        (joined
                            (portRef A)
                            (portRef I1 A)
                        )
                    )
                )
            )
        )
    )
    (design hello-world
        (cellRef hello-world (libraryRef my_lib))
    )
)

看到没?一个简单的AND2门,写了快30行。我刚开始做逆向时,对着这种格式硬啃,眼睛都快瞎了。

我的解析技巧: 别试图用肉眼去读EDIF。写个脚本,把括号对齐缩进打印出来,或者直接用现成的解析库。Python的pyedif库就挺好用,能帮你把嵌套结构转成字典。

2.3 Verilog网表解析:相对「友好」的门级描述

Verilog网表就好多了。它本质上是把RTL代码综合后的门级实现,用Verilog语法写出来。你想想看,如果你会写Verilog,那读网表基本没障碍。

一个典型的Verilog网表片段:

module top (clk, rst, data_in, data_out);
    input clk, rst;
    input [7:0] data_in;
    output [7:0] data_out;

    wire [7:0] w1, w2;
    wire n1, n2;

    // 实例化一个与门
    AND2X1 U1 (.A(data_in[0]), .B(data_in[1]), .Z(w1[0]));
    // 实例化一个D触发器
    DFFX1 U2 (.D(w1[0]), .CK(clk), .Q(data_out[0]));

    // 组合逻辑
    assign n1 = ~(data_in[2] & data_in[3]);
    assign n2 = n1 | data_in[4];

endmodule

这里有个坑,我曾经踩过:综合工具生成的Verilog网表,单元名称是跟工艺库绑定的。比如上面的AND2X1,不同工艺库可能叫AND2AN2甚至A2。解析时一定要建立单元映射表。

避坑指南: 我曾经因为没注意单元名称的差异,把一个台积电工艺的网表当成中芯国际的来解析,结果整整浪费了两天时间。拿到Verilog网表后,第一件事就是确认工艺库版本。

2.4 SPICE网表解析:晶体管级的「硬核」描述

SPICE网表是三种格式里最「底层」的。它直接描述晶体管、电阻、电容这些物理器件。做硬件安全分析时,如果你要逆向的是模拟电路或者混合信号芯片,SPICE网表是绕不开的。

一个简单的CMOS反相器SPICE网表:

* CMOS Inverter Netlist
.SUBCKT inv A VDD VSS Z
M1 Z A VDD VDD PMOS W=2u L=0.18u
M2 Z A VSS VSS NMOS W=1u L=0.18u
.ENDS inv

* Top level
.SUBCKT top VDD VSS A Z
X1 A VDD VSS Z inv
.ENDS top

* Simulation commands
VDD VDD 0 DC 1.8V
VIN A 0 PULSE(0 1.8 0 1n 1n 10n 20n)
.TRAN 0.1n 100n
.END

你看,SPICE网表的结构很直白:.SUBCKT定义子电路,M定义MOS管,WL是宽长比。解析起来其实不难,难的是后续的电路识别——你要从几万个晶体管里找出哪些是反相器、哪些是与非门。

关键点: SPICE网表的解析核心是「器件-节点」关系图。我建议用图数据库(比如Neo4j)来存储解析结果,这样后续做子图匹配会快很多。

2.5 解析工具对比:选对工具,事半功倍

工具选得好,下班回家早。我这些年用过不少解析工具,挑几个有代表性的说说:

工具名称 支持格式 语言 优点 缺点
Yosys Verilog网表 C++ / Python 开源、功能强、支持综合与优化 对EDIF支持有限
pyedif EDIF Python 轻量、易用、适合快速解析 不支持写EDIF
ngspice SPICE C 开源仿真器、解析准确 只做仿真,不做逆向分析
Verilator Verilog网表 C++ / Perl 高性能、支持Lint检查 学习曲线陡峭
自定义脚本 全格式 Python / Perl 灵活、可定制 开发周期长

我个人习惯是:Verilog网表用Yosys,EDIF用pyedif,SPICE网表直接写Python脚本解析。为什么?因为SPICE格式虽然简单,但各家EDA工具生成的SPICE网表在注释、缩进、参数顺序上都有细微差别,通用工具反而容易出错。

2.6 三种格式的解析流程对比

下面这张SVG图,是我自己总结的三种网表解析流程对比。你看一眼就能明白,它们的解析路径其实大同小异,只是中间的处理细节不同。

三种网表格式解析流程对比 EDIF 解析流程 1. 词法分析(括号匹配) 2. 语法分析(S-表达式) 3. 构建抽象语法树 4. 提取单元与端口 5. 生成网表图结构 Verilog 网表解析流程 1. 预处理(宏展开) 2. 词法/语法分析 3. 模块实例化展开 4. 线网与端口连接 5. 生成层次化网表 SPICE 网表解析流程 1. 行解析(逐行读取) 2. 器件类型识别 3. 节点编号映射 4. 子电路展开 5. 构建器件-节点图 核心差异总结 EDIF:括号嵌套 → 树结构 | Verilog:模块实例化 → 层次结构 | SPICE:器件行 → 图结构

从这张图你能看出来,EDIF的解析最「绕」,因为它本质上是树形结构,你得先把括号匹配好,才能提取出有效信息。Verilog网表相对直接,但要注意模块实例化的层次展开。SPICE网表最简单,逐行读就行,但后续的图构建反而最复杂。

2.7 实战建议:从零开始搭一个解析器

如果你真想学会网表解析,我建议你动手写一个最小化的解析器。别怕,不用写几千行,几百行就够了。

以SPICE网表为例,核心代码其实就三步:

# 伪代码:SPICE网表最小解析器
def parse_spice(netlist_file):
    devices = []
    subckts = {}
    current_subckt = None
    
    for line in netlist_file:
        line = line.strip()
        if not line or line.startswith('*'):
            continue  # 跳过空行和注释
        
        if line.startswith('.SUBCKT'):
            # 开始一个子电路定义
            parts = line.split()
            current_subckt = parts[1]
            subckts[current_subckt] = []
        elif line.startswith('.ENDS'):
            current_subckt = None
        elif line.startswith('M'):
            # MOS管: M name drain gate source bulk model W= L=
            parts = line.split()
            device = {
                'type': 'MOSFET',
                'name': parts[0],
                'drain': parts[1],
                'gate': parts[2],
                'source': parts[3],
                'bulk': parts[4],
                'model': parts[5],
                'params': parts[6:]
            }
            if current_subckt:
                subckts[current_subckt].append(device)
            else:
                devices.append(device)
        # ... 其他器件类型类似处理
    
    return devices, subckts

你看,核心逻辑就这么几行。剩下的就是处理各种边界情况——比如续行符、参数顺序变化、注释格式不统一等等。这些坑,我当年一个一个踩过来的。

我的建议: 解析器写好后,先用小网表测试,再逐步加大。别一上来就解析几百万门的网表,你会被bug淹死的。

好了,网表格式解析这块,核心内容就这些。记住一句话:格式是死的,工具是活的。多动手写脚本,多积累经验,你也能成为网表解析的老手。


公众号:蓝海资料掘金营,微信deep3321