第一章 FPGA基础回顾:从芯片内部到网表世界

各位同学,欢迎来到《FPGA网表逆向工程从零到精通》的第一课。

我是你们这门课的主讲。干FPGA逆向这行有些年头了。说实话,刚入行时我也觉得网表这东西很神秘,黑盒子一样。但后来我发现,搞懂网表之前,必须先搞懂FPGA本身。就像修车,你得先知道发动机长什么样,才能去拆解它,对吧?

这一章,我们就来把FPGA的老底翻一翻。我会从最基础的内部结构讲起,再到开发流程,最后引出网表的概念。这些都是后续逆向分析的地基,打不牢,后面会摔得很惨。

1.1 FPGA内部结构:你手里的积木盒

FPGA说白了是什么?就是一个可以无限次重新配置的芯片。你买回来一块芯片,想让它当CPU就当CPU,想让它当GPU就当GPU(当然性能有差别)。这靠的就是它内部那些可编程的“积木块”。

我个人习惯把FPGA内部结构分成五大块:

  • LUT(查找表):实现逻辑运算的核心。你可以把它想象成一个真值表,输入几个信号,输出一个结果。
  • FF(触发器):用来存储状态的。说白了就是寄存器,在时钟边沿锁存数据。
  • BRAM(块RAM):片上的存储资源。用来存数据、做FIFO、缓存。
  • DSP(数字信号处理单元):专门做乘加运算的硬核。搞通信、图像处理离不开它。
  • IO(输入输出):芯片与外界通信的接口。

嗯,这里要注意,不同厂商(Xilinx、Altera/Intel、Lattice)的叫法略有不同,但本质都一样。我在项目中遇到过有人把LUT和FF混为一谈,结果综合出来的面积大了好几倍。这俩东西,功能完全不同。

核心理解: LUT负责“算”,FF负责“存”。两者配合,才能构成时序逻辑。

下面这张图,是我用SVG画的一个FPGA内部结构简图。你一看就明白这些模块是怎么连起来的。

FPGA 内部结构示意图 可编程逻辑块 (CLB) LUT + 触发器 (FF) 实现组合逻辑与时序逻辑 例:4输入LUT + D触发器 块RAM (BRAM) 片上存储,容量大,速度快 DSP 单元 乘加运算,信号处理核心 可编程互连 布线资源 连接所有模块 开关矩阵 + 连线 IO 块 输入/输出接口 电平标准可配置 LVCMOS, LVDS, SSTL... 图:FPGA内部由CLB、BRAM、DSP、IO通过可编程互连组成

1.2 典型开发流程:从代码到比特流

你写了一段Verilog代码,怎么变成FPGA里实际跑起来的电路?这中间要经过三个关键步骤:综合实现生成比特流

我刚开始学的时候,总觉得综合就是“翻译”,把代码翻译成电路。其实没那么简单。综合是把RTL代码映射到FPGA的基本单元(LUT、FF、BRAM、DSP)上。而实现,则是把这些单元放到芯片的物理位置上,并连好线。

你想想看,综合后的网表,其实还只是个“逻辑蓝图”。实现后的网表,才是真正的“施工图纸”。

我的经验: 逆向分析时,我们拿到的网表大多是实现后的网表。因为它包含了具体的LUT配置、BRAM初始化值、DSP的级联方式。这些信息在综合后网表里是看不到的。

下面这个表格,帮你快速区分这两个阶段的网表:

对比项 综合后网表 实现后网表
抽象层次 逻辑级(与门、或门、触发器) 物理级(LUT、FF、BRAM、DSP)
包含信息 逻辑连接、层次结构 物理位置、布线信息、LUT真值表
文件格式 EDIF、Verilog网表 XDL、NCD、RBT(厂商特定)
逆向难度 较低(逻辑清晰) 较高(需要反推LUT配置)

1.3 网表的概念与分类:逆向分析的起点

网表是什么?说白了,就是一张“电路连接清单”。它告诉你,芯片里有哪些元件,这些元件的引脚是怎么连在一起的。

在FPGA开发中,网表主要分两类:

  • 综合后网表(Post-Synthesis Netlist):综合工具生成的,基于标准单元库(如AND2、OR2、DFF)。它不关心物理位置。
  • 实现后网表(Post-Implementation Netlist):经过布局布线后生成的,基于FPGA原语(如LUT6、FDRE、RAMB36)。它包含了物理位置信息。

我记得有一次,客户拿了一个加密的比特流文件给我,让我分析里面用到了哪些IP核。我第一步就是把它反解成实现后网表。为什么?因为只有实现后网表才能看到LUT的配置内容,才能知道BRAM里存了什么数据。

避坑指南: 我曾经在分析一个通信模块时,直接拿综合后网表去分析,结果发现很多信号名都被优化掉了,根本对不上。后来换成实现后网表,才找到真正的关键路径。所以,做逆向,一定要用实现后网表。

这里再补充一点。网表文件本身是文本格式的,但不同厂商的格式差异很大。Xilinx用XDL(Xilinx Design Language),Altera用QXP(Quartus XML)。不过,它们描述的核心内容是一样的:实例(Instance)网络(Net)引脚(Pin)

举个例子,一个简单的LUT6实例在XDL中大概长这样:

inst "my_lut" "LUT6", 
  pin "I0" "net_a", 
  pin "I1" "net_b", 
  pin "O" "net_out",
  config "INIT" #h8000
;

你看,它明确说了:这是一个LUT6,输入是I0和I1,输出是O,LUT的初始值(INIT)是0x8000。这个INIT值,就是逆向分析的关键。通过它,我们可以反推出LUT实现的逻辑函数。

嗯,这一章的内容就到这里。网表的世界才刚刚打开,后面我们会一步步深入进去。


专注资料整理