网表文件格式解析: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格式,这样就能看懂了。

实用技巧:ngc2edifngc2xdl 命令可以把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工具 广泛

解析策略:我的实战经验

在实际项目中,我通常采用这样的策略:

  1. 先转格式:把NGC转成XDL或EDIF,把EDIF转成Verilog网表。这样能降低解析难度。
  2. 统一中间表示:不管什么格式,都解析成统一的中间表示(比如JSON或自定义的数据结构)。这样后续分析就只需要处理一种格式。
  3. 关注连接关系:网表的核心是实例之间的连接关系。解析时优先提取instance、port和net的对应关系。
  4. 保留原始信息:不要丢弃任何字段,尤其是配置信息。你永远不知道哪些信息在后续分析中会派上用场。

我的建议:如果你刚开始做网表逆向,先从Verilog网表入手。它最直观,工具支持也最好。等熟悉了基本流程,再挑战EDIF和XDL。NGC嘛...能转格式就转格式,别跟自己过不去。

解析流程示意图

下面这张图展示了我常用的网表解析流程:

网表解析流程 NGC (二进制) EDIF (文本) XDL (文本) Verilog 格式转换层 ngc2edif / ngc2xdl / edif2verilog 统一中间表示 (JSON / 自定义结构) 模块 | 实例 | 端口 | 网络 | 配置 网表分析层 连接关系提取 | 逻辑功能还原 | 时序路径分析 逆向分析结果 / 黑盒测试用例

这张图展示了我常用的解析流程。核心思路就是:不管输入什么格式,最终都转成统一的中间表示,然后在这个基础上做分析。

避坑指南

最后,分享几个我踩过的坑:

  • 字符编码问题:EDIF文件有时会用ISO-8859-1编码,而你的解析器默认是UTF-8。我曾经因为这个原因,解析到一半就报错了。
  • 版本差异:不同版本的EDIF和XDL,语法细节可能有差异。解析时最好先检查版本号,再选择对应的解析规则。
  • 隐藏的约束:NGC文件里可能嵌入了时序约束和物理约束。这些约束会影响网表的逻辑行为,解析时不要忽略。
  • 空实例:有些网表里会有空的实例(没有逻辑功能),它们可能是为了占位或调试用的。解析时保留它们,但分析时可以忽略。

小技巧:解析完成后,用Yosys的show命令生成网表的图形化视图。一眼就能看出连接关系有没有解析正确。我每次解析完都会跑一遍这个命令,省了不少调试时间。

好了,网表格式解析就聊到这里。记住,解析只是第一步,真正的挑战在于从网表中还原出设计意图。下一章我们会聊聊怎么从网表中提取控制流和数据流,到时候见。


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