3、FPGA配置与比特流基础
各位好,欢迎来到第三章。这一章我们聊聊FPGA的配置和比特流。说实话,这是逆向工程里最核心、也最绕不开的一关。你想想看,FPGA本身是一张白纸,上电后它怎么知道自己该干什么?全靠配置过程把“灵魂”灌进去。这个灵魂,就是比特流。
我个人习惯把FPGA的配置过程比作“给芯片喂饭”。喂的方式不同,速度、安全性、适用场景都不一样。今天我们就从配置模式开始,一步步拆解。
3.1 FPGA配置模式详解
Lattice的FPGA支持多种配置模式。我挑三个最常用的讲:SPI、JTAG、Slave Serial。这三种模式,我在项目里都踩过坑,咱们一个一个说。
3.1.1 SPI配置模式
SPI模式,说白了就是用外部Flash存比特流,FPGA上电后自己去读。这是最常用的方式,没有之一。
为什么常用? 因为便宜、灵活。你买一颗SPI Flash,几毛钱到几块钱,容量从几Mb到几百Mb随便选。FPGA上电后自动加载,用户无感。
SPI配置的典型连接是这样的:
FPGA (Master) SPI Flash (Slave)
----------------- -----------------
CCLK (时钟输出) ----> CLK
SI (数据输出) ----> DI
SO (数据输入) <---- DO
CS (片选) ----> CS#
嗯,这里要注意:Lattice的FPGA在SPI模式下是Master,时钟由FPGA自己产生。我曾经遇到过一个问题,Flash的时钟频率上限是33MHz,但FPGA默认配置时钟跑到了50MHz,结果死活加载不上。后来查了半天,才发现是时钟太快了,Flash跟不上。
3.1.2 JTAG配置模式
JTAG模式,这是调试阶段的标配。你想想看,开发板上电后,用一根下载线就能把比特流烧进去,靠的就是JTAG。
JTAG有4根线:TCK(时钟)、TMS(状态机控制)、TDI(数据输入)、TDO(数据输出)。Lattice的FPGA还多了一根PROGRAMN(配置触发)和INITN(初始化状态指示)。
JTAG配置的好处是:
- 实时性:可以随时下载、调试,不用擦写Flash
- 可读性:通过JTAG可以回读FPGA内部状态,这对逆向分析太重要了
- 安全性:JTAG端口通常可以被永久关闭(后面会讲Security Bit)
我个人习惯在开发阶段用JTAG,量产时切到SPI。但要注意:JTAG端口如果不加保护,别人拿个下载器就能把你的比特流读出来。嗯,这个后面细说。
3.1.3 Slave Serial配置模式
Slave Serial模式,FPGA是Slave,外部控制器(比如MCU、CPU)提供时钟和数据。这种模式在复杂系统里很常见,比如你的FPGA被一个ARM处理器控制,ARM从SD卡或网络加载比特流,然后通过Slave Serial喂给FPGA。
Slave Serial的时序很简单:
外部控制器提供:
- CCLK:配置时钟
- DATA:串行数据(每时钟1bit)
- PROGRAMN:拉低触发重配置
- INITN:FPGA初始化完成指示
- DONE:配置完成指示
我曾经在一个项目里用STM32通过Slave Serial配置Lattice FPGA,踩了一个坑:STM32的GPIO翻转速度不够快,导致配置时钟只有几MHz,整个配置过程花了快1秒。后来改用SPI+DMA才解决。
3.2 比特流的物理结构
比特流是什么?说白了就是一堆二进制数据,FPGA上电后加载到内部配置存储器里,决定每个LUT、每个MUX、每个布线开关的状态。
Lattice的比特流结构,我拆解给你看:
| 段 | 内容 | 长度(典型值) |
|---|---|---|
| Preamble | 同步头,用于FPGA识别比特流起始 | 32 bits |
| Header | 包含器件ID、配置选项、CRC校验等 | 64-128 bits |
| Configuration Data | 真正的配置数据,按帧(Frame)组织 | 取决于器件容量 |
| Postamble | 结束标志,触发DONE信号 | 32 bits |
| CRC | 循环冗余校验,验证数据完整性 | 16-32 bits |
这里有个关键点:配置数据是按帧组织的。每一帧对应FPGA内部的一列逻辑资源。你想想看,一个Lattice ECP5有几百列,每列有几十个逻辑单元,每个单元又有几十个配置位。整个比特流就是这些配置位的线性排列。
我刚开始逆向比特流时,最头疼的就是找到帧的边界。后来发现,Lattice的比特流里有一个固定的同步字(0xFFFFFFFF或者0xAAAAAAAA),找到它就能定位帧起始。
3.3 配置时钟与初始化序列
FPGA上电后,不是立刻就能工作的。它有一个严格的初始化序列。我画了一张图,帮你理解这个过程:
这个序列里,有几个关键信号你要记住:
- PROGRAMN:拉低触发重配置。我经常用它来强制FPGA重新加载
- INITN:初始化完成指示。如果它一直拉低,说明配置出错了
- DONE:配置完成指示。它拉高后,FPGA才开始执行用户逻辑
- CCLK:配置时钟。在SPI模式下由FPGA产生,Slave模式下由外部提供
配置时钟的频率很关键。太快了Flash跟不上,太慢了配置时间太长。我一般建议:SPI模式用25-33MHz,Slave Serial可以跑到50-66MHz。但具体要看器件手册,别超了上限。
3.4 安全位(Security Bit)机制
终于讲到安全位了。这是逆向工程里最让人头疼的东西,也是保护FPGA知识产权的最重要手段。
安全位是什么?说白了就是一个熔丝位(或者非易失存储位),烧进去之后,JTAG端口就不能再回读比特流了。有些Lattice器件还支持永久锁定,烧了就改不回来。
安全位的典型设置:
// 伪代码示意
if (Security_Bit == 1) {
JTAG_Read_Disable = TRUE;
// 不能通过JTAG回读配置数据
// 不能通过JTAG擦除配置
// 只能通过重新上电加载新比特流
}
我遇到过最狠的一次:客户拿来一块板子,FPGA里烧了核心算法,但安全位锁死了。我们想逆向分析,结果JTAG读出来全是0。后来用了物理攻击手段才搞定,但那又是另一个故事了。
- 侧信道攻击(通过功耗、电磁辐射分析)
- 物理探针(直接读取配置存储器)
- SPI Flash直接读取(如果比特流未加密)
Lattice的比特流加密支持AES-128或AES-256。加密密钥存储在FPGA内部的非易失存储器里,外部无法读取。配置时,FPGA从SPI Flash读取加密的比特流,内部解密后再加载。
嗯,这里有个细节:加密后的比特流,在SPI Flash里是密文。你就算把Flash拆下来用编程器读,也是一堆乱码。没有密钥,谁也解不开。
最后总结一下:配置模式决定了你怎么喂饭,比特流结构决定了饭的内容,初始化序列决定了吃饭的节奏,安全位决定了这顿饭会不会被别人偷吃。这四个知识点,是FPGA逆向工程的基石。你把这些搞透了,后面分析具体器件时就会轻松很多。
公众号:蓝海资料掘金营,微信deep3321