3、FPGA架构与网表基础:主流FPGA架构(LUT、FF、BRAM、DSP、IO)、网表格式(EDIF、Verilog网表、BLIF)、综合与映射概念
各位同学好,我是老李。今天咱们聊聊FPGA逆向的基础——你得先知道手里这块芯片到底长什么样,网表又是个什么玩意儿。说白了,不懂架构和网表,逆向就是瞎蒙。
我刚开始做逆向那会儿,拿到一个EDIF文件,打开一看全是乱码,差点以为文件坏了。后来才明白,网表这东西,就是芯片的“骨架”,你得学会怎么看它。
3.1 主流FPGA架构:你手里的芯片到底有啥?
FPGA内部,说白了就是一堆可编程的逻辑单元,加上互联线和IO。我习惯把它想象成一个乐高底板——上面有各种积木块,你可以随意拼搭。
3.1.1 LUT(查找表)——最核心的积木
LUT是FPGA的灵魂。它本质上是一个小型的RAM,输入地址,输出结果。比如一个4输入LUT,内部有16个存储位,你往里面写什么真值表,它就实现什么逻辑。
关键点:LUT的输入数量决定了它能实现的逻辑复杂度。常见的4输入、5输入、6输入LUT,我见过最夸张的是7输入。
我在项目中遇到过一个问题:一个组合逻辑太大,一个LUT放不下,结果综合工具自动拆成了两个LUT级联。嗯,这时候时序就容易出问题。你想想看,多一级LUT就多一级延迟。
3.1.2 FF(触发器)——时序的基石
FF就是用来存状态的。D触发器最常见,时钟沿来的时候,把输入锁存到输出。没有FF,你就做不了时序逻辑,只能搞组合逻辑。
我记得有一次逆向一个计数器,发现它用了大量的FF做流水线。我当时还纳闷,为什么同一个逻辑要复制好几份?后来才明白,这是为了提升吞吐率。
个人经验:逆向时,看到FF就要想到“状态”。一个FF就是一个bit的状态寄存器。几百个FF连在一起,八成是个状态机或者计数器。
3.1.3 BRAM(块RAM)——存储大户
BRAM是FPGA里的专用存储资源。它不像LUT那样可以随便拼,而是固定大小的存储块。常见的BRAM大小是18Kb或36Kb。
BRAM可以配置成单口、双口、甚至伪双口。我建议你重点掌握双口RAM——很多协议栈、FIFO都用它。
| BRAM配置 | 深度×位宽 | 典型用途 |
|---|---|---|
| 单口RAM | 512×36 | 简单数据缓存 |
| 双口RAM | 1024×18 | 跨时钟域FIFO |
| 伪双口 | 2048×9 | 视频行缓冲 |
避坑指南:我曾经以为BRAM可以无限拼接,结果发现地址译码逻辑会吃掉大量LUT。后来我学乖了,能用BRAM就别用LUT搭RAM,否则资源爆炸。
3.1.4 DSP(数字信号处理单元)——算力担当
DSP单元是专门做乘加运算的。一个DSP48E1可以做一个25×18的乘法,然后累加。做滤波、FFT、矩阵运算,全靠它。
我见过有人用LUT搭乘法器,结果一个乘法吃掉200个LUT,频率还跑不高。你想想看,一个DSP单元才占多少面积?所以,能用DSP就别手撸乘法器。
3.1.5 IO(输入输出)——芯片的嘴巴和耳朵
IO就是FPGA和外界通信的接口。有单端IO(LVCMOS、LVTTL)和差分IO(LVDS、SSTL)。每个IO bank的电压可以独立配置。
嗯,这里要注意:IO的电气特性很重要。我曾经因为没注意IO bank的电压,直接把3.3V的信号怼到1.8V的bank上,结果芯片冒烟了。别学我。
3.2 网表格式:芯片的“骨架”长什么样?
网表就是芯片内部所有逻辑单元和连线的描述。它不关心你代码怎么写,只关心你最终连成了什么样子。
3.2.1 EDIF(电子设计交换格式)——老古董但还在用
EDIF是一种文本格式,用括号嵌套来描述电路。看起来像Lisp语言。我刚开始看EDIF的时候,头都大了。
(cell MY_CELL (cellType GENERIC)
(view NETLIST (viewType NETLIST)
(interface
(port A (direction INPUT))
(port B (direction INPUT))
(port Y (direction OUTPUT))
)
(contents
(instance U1 (viewRef NETLIST (cellRef AND2)))
(net N1 (joined (portRef A (instanceRef U1)) (portRef A)))
)
)
)
你看,一个AND2门,在EDIF里要写这么多行。我建议你只了解它的结构就行,不用深究。因为现在主流工具已经很少直接输出EDIF了。
3.2.2 Verilog网表——最常用的格式
Verilog网表就是用Verilog语言描述的门级电路。它比EDIF直观多了。每个实例就是一个门,连线就是wire。
module top (A, B, CLK, Y);
input A, B, CLK;
output Y;
wire w1;
AND2 U1 (.I0(A), .I1(B), .O(w1));
DFF U2 (.D(w1), .CLK(CLK), .Q(Y));
endmodule
我个人习惯用Verilog网表做逆向。因为它可以直接用仿真器跑,还能和原始RTL做对比。你想想看,多方便。
小技巧:拿到Verilog网表后,先用nLint或SpyGlass跑一下,看看有没有悬空引脚。我遇到过很多次,网表里有些引脚没接,结果仿真结果对不上。
3.2.3 BLIF(伯克利逻辑交换格式)——学术界的宠儿
BLIF是伯克利大学搞出来的格式,主要用于逻辑综合和优化。它用.model和.end来定义模块,用.names来描述逻辑函数。
.model top
.inputs A B
.outputs Y
.names A B Y
11 1
.end
你看,一个AND门,BLIF只用三行。它把真值表直接写出来了。嗯,这种格式在逆向工程中很有用,因为你可以直接提取逻辑函数。
3.3 综合与映射概念:从代码到网表的魔法
综合就是把RTL代码变成网表的过程。映射则是把网表中的逻辑门对应到FPGA的LUT、FF等资源上。
3.3.1 综合——RTL到门级
综合工具(比如Synplify、Vivado)会做三件事:
- 解析RTL:读懂你的always块、assign语句
- 逻辑优化:把冗余逻辑去掉,共享公共子表达式
- 技术映射:把优化后的逻辑映射到目标工艺库
我刚开始做综合的时候,总觉得工具是黑盒子。后来有一次,我发现同样的RTL,两个版本综合出来的网表不一样。查了半天,原来是综合选项不同。所以,我建议你每次综合都记录一下选项。
3.3.2 映射——门级到FPGA资源
映射这一步,工具会把门级网表中的AND、OR、NOT等门,转换成LUT的配置。比如一个4输入AND门,工具会把它映射到一个4输入LUT,LUT的真值表就是全1。
为什么会这样?因为LUT本质上就是一个可编程的ROM。你给它什么真值表,它就实现什么逻辑。所以,映射的本质就是“填表”。
核心概念:逆向工程中,你看到的LUT配置,其实就是综合工具填进去的真值表。读懂这个真值表,你就能还原出原始的逻辑函数。
3.3.3 综合与映射的坑
我遇到过最坑的事情:综合工具把两个独立的逻辑合并到了一个LUT里。结果逆向的时候,我怎么也分不清哪个信号是哪个。后来我学乖了,先看LUT的输入输出关系,再反推逻辑。
避坑指南:我曾经以为综合后的网表一定和RTL一一对应。结果发现,工具会做大量优化——比如资源共享、逻辑复制、寄存器重定时。所以,逆向时别指望能直接看到RTL的影子,你得学会“猜”工具的思路。
3.4 知识体系总览
下面这张图,是我自己画的FPGA架构与网表知识体系。你看一眼,心里就有数了。
这张图把FPGA架构、网表格式、综合映射串起来了。你逆向的时候,就是从网表反推架构,再从架构反推RTL。说白了,就是把这个流程倒着走一遍。
好了,这一章的内容就到这里。记住:架构是基础,网表是桥梁,综合映射是魔法。搞懂这三样,逆向就成功了一半。