网表文件格式解析:EDIF、NGC、XDL、Verilog网表的结构与差异
做FPGA逆向这么多年,我接触过各种各样的网表文件。说实话,刚开始看到那些格式各异的文件,头都大了。但后来我发现,只要掌握了它们的核心逻辑,解析起来其实没那么玄乎。
今天咱们就聊聊四种最常见的网表格式:EDIF、NGC、XDL和Verilog网表。它们各自有什么特点?怎么解析?我踩过哪些坑?嗯,一个一个来。
EDIF格式:老牌工业标准
EDIF(Electronic Design Interchange Format)是EDA工具之间交换数据的标准格式。说白了,它就是不同工具之间的“翻译官”。
EDIF文件的结构很清晰,用括号嵌套的方式组织。我刚开始看的时候觉得像Lisp语言,后来习惯了反而觉得挺直观。
(edif test_design
(edifVersion 2 0 0)
(edifLevel 0)
(keywordMap (keywordLevel 0))
(cell TOP (cellType GENERIC)
(view netlist (viewType NETLIST)
(interface
(port CLK (direction INPUT))
(port RST (direction INPUT))
(port DATA (direction INPUT))
(port Q (direction OUTPUT))
)
(contents
(instance U1 (cellRef DFF))
(instance U2 (cellRef INV))
(net CLK_NET
(joined
(portRef CLK)
(portRef CLK (instanceRef U1))
)
)
)
)
)
)
解析EDIF的关键在于理解它的层级结构。我个人习惯用递归下降解析器来处理,因为嵌套深度不确定。我在一个项目中遇到过EDIF文件里嵌套了十几层,用栈式解析器差点爆栈。
核心要点:EDIF的cell/view/contents三层结构,对应了设计中的模块/视图/实例关系。解析时重点关注instance和net的关联。
NGC格式:Xilinx的二进制黑盒
NGC是Xilinx的二进制网表格式。说实话,这玩意儿最让人头疼。为什么?因为它是二进制的,不能直接看。
NGC文件本质上是一个容器,里面包含了网表信息、约束文件和物理布局信息。我记得第一次尝试解析NGC时,对着十六进制看了半天,啥也没看出来。
后来我找到了方法:NGC文件其实遵循一种叫“Xilinx Design Language”的内部格式,只是被压缩和编码了。你可以用Xilinx自带的工具把它转成XDL格式,这样就能看懂了。
实用技巧:用 ngc2edif 或 ngc2xdl 命令可以把NGC转成可读格式。我曾经用这个命令救回了一个丢失源码的项目。
NGC的二进制结构大致是这样的:
文件头 (Magic Number + 版本号)
├── 符号表 (Symbol Table)
├── 网表部分 (Netlist Section)
│ ├── 模块定义
│ ├── 实例列表
│ └── 连接关系
├── 约束部分 (Constraints)
└── 物理信息 (Physical Info)
解析NGC需要逆向它的二进制格式。我建议先用现成的工具转格式,实在不行再自己写解析器。你想想看,自己从头解析一个二进制格式,工作量可不小。
XDL格式:Xilinx的“明文”网表
XDL(Xilinx Design Language)是Xilinx的文本格式网表。它比NGC友好多了,至少你能直接打开看。
XDL的结构和FPGA的物理结构高度对应。它包含了SLICE、IOB、BRAM等基本单元,以及它们之间的连线关系。
design "test_design" xc7a100tcsg324-1 v3.2 ;
module "TOP" ;
wire CLK, RST, DATA, Q ;
wire N1, N2 ;
inst "U1" "SLICE_X0Y0", placed R8C8 ,
cfg " #OFF
#BEL:D6LUT => LUT6 #LUT:D6LUT.O = (A6@(A1@(A2@(A3@(A4@(A5@(A6))))))
#BEL:FF2 => FDRE #INIT:FF2 = #LOW
" ;
inst "U2" "SLICE_X0Y1", placed R8C9 ,
cfg " ... " ;
net "CLK_NET",
outpin "U1" CK ,
inpin "U2" CLK ;
endmodule "TOP" ;
解析XDL时,我建议重点关注三个部分:inst(实例)、net(网络)和cfg(配置)。cfg字段里藏着LUT的初始值和FF的配置,这对逆向分析至关重要。
注意:XDL的cfg字段是字符串形式的配置信息,解析时要注意转义字符。我曾经因为漏掉一个反斜杠,导致整个LUT逻辑解析错误,查了两天才找到原因。
Verilog网表:最熟悉的陌生人
Verilog网表,说白了就是用Verilog语言描述的门级连接关系。它不像RTL代码那样有行为描述,而是直接例化了基本门单元。
module TOP (
input CLK, RST, DATA,
output reg Q
);
wire N1, N2;
DFF U1 (
.CLK(CLK),
.RST(RST),
.D(DATA),
.Q(N1)
);
INV U2 (
.I(N1),
.O(N2)
);
DFF U3 (
.CLK(CLK),
.RST(RST),
.D(N2),
.Q(Q)
);
endmodule
解析Verilog网表,我推荐用现成的解析器,比如Yosys的read_verilog命令。为什么?因为Verilog语法太灵活了,自己写解析器容易漏掉边界情况。
我记得有一次,一个Verilog网表里用了generate语句来批量例化,我手写的解析器直接崩溃了。后来改用Yosys,几秒钟就搞定了。
四种格式的对比
| 特性 | EDIF | NGC | XDL | Verilog网表 |
|---|---|---|---|---|
| 可读性 | 中等 | 差(二进制) | 好 | 好 |
| 厂商依赖 | 中立 | Xilinx | Xilinx | 中立 |
| 物理信息 | 无 | 有 | 有 | 无 |
| 解析难度 | 中等 | 高 | 低 | 中等 |
| 工具支持 | 广泛 | 有限 | Xilinx工具 | 广泛 |
解析策略:我的实战经验
在实际项目中,我通常采用这样的策略:
- 先转格式:把NGC转成XDL或EDIF,把EDIF转成Verilog网表。这样能降低解析难度。
- 统一中间表示:不管什么格式,都解析成统一的中间表示(比如JSON或自定义的数据结构)。这样后续分析就只需要处理一种格式。
- 关注连接关系:网表的核心是实例之间的连接关系。解析时优先提取instance、port和net的对应关系。
- 保留原始信息:不要丢弃任何字段,尤其是配置信息。你永远不知道哪些信息在后续分析中会派上用场。
我的建议:如果你刚开始做网表逆向,先从Verilog网表入手。它最直观,工具支持也最好。等熟悉了基本流程,再挑战EDIF和XDL。NGC嘛...能转格式就转格式,别跟自己过不去。
解析流程示意图
下面这张图展示了我常用的网表解析流程:
这张图展示了我常用的解析流程。核心思路就是:不管输入什么格式,最终都转成统一的中间表示,然后在这个基础上做分析。
避坑指南
最后,分享几个我踩过的坑:
- 字符编码问题:EDIF文件有时会用ISO-8859-1编码,而你的解析器默认是UTF-8。我曾经因为这个原因,解析到一半就报错了。
- 版本差异:不同版本的EDIF和XDL,语法细节可能有差异。解析时最好先检查版本号,再选择对应的解析规则。
- 隐藏的约束:NGC文件里可能嵌入了时序约束和物理约束。这些约束会影响网表的逻辑行为,解析时不要忽略。
- 空实例:有些网表里会有空的实例(没有逻辑功能),它们可能是为了占位或调试用的。解析时保留它们,但分析时可以忽略。
小技巧:解析完成后,用Yosys的show命令生成网表的图形化视图。一眼就能看出连接关系有没有解析正确。我每次解析完都会跑一遍这个命令,省了不少调试时间。
好了,网表格式解析就聊到这里。记住,解析只是第一步,真正的挑战在于从网表中还原出设计意图。下一章我们会聊聊怎么从网表中提取控制流和数据流,到时候见。
公众号:蓝海资料掘金营,微信deep3321