1. 为什么要在FPGA项目里折腾Multiboot
做FPGA项目时间长了,总会碰到一个绕不开的需求:板子已经装在设备里了,固件要升级,但现场没有人会去按复位键,更不可能把机箱拆开重新插拔JTAG。这时候Multiboot就是救命稻草。简单说,Multiboot就是让FPGA在上电或触发重配置时,能从SPI Flash里的多个镜像中选择一个来加载。如果主镜像因为某些原因启动失败,还能自动回退到Golden镜像,保证设备不会彻底变砖。
我第一次接触Multiboot是在一个工业相机的项目上,客户要求远程升级FPGA逻辑,而且升级过程中如果断电或者数据传输出错,设备必须还能正常启动到旧版本。当时试过不少方案,最后发现用ICAPE3原语配合SPI Flash的多镜像分区是最稳妥的。这套方案的核心思路是:在Flash里划分出Golden区和Update区,Golden区放一个绝对可靠的基础镜像,Update区放新版本。上电后FPGA先加载Golden,然后通过ICAPE3原语触发一次重配置,跳转到Update区。如果Update区校验失败,看门狗或者逻辑内部的状态机会自动触发回退,重新加载Golden。
这套流程听起来不复杂,但实际做的时候坑非常多。Vivado里怎么设置、ICAPE3的时序怎么控制、SPI Flash的地址怎么算、bitstream怎么合并,每一步都有讲究。下面我就按实际操作的顺序,把整个流程拆开讲清楚。
2. 整体方案设计与关键决策点
2.1 为什么选ICAPE3而不是其他重配置方式
Xilinx FPGA支持多种重配置方式,比如通过JTAG、通过SelectMAP、通过ICAP原语。JTAG适合调试,但现场设备不可能留个JTAG口给你远程操作。SelectMAP需要额外的引脚和外部控制器,硬件成本高。ICAP是内部配置访问端口,直接通过FPGA内部逻辑触发重配置,不需要任何外部引脚,最适合做远程升级和Multiboot。
ICAPE3是ICAP的第三代原语,7系列和UltraScale系列都支持。它本质上是一个硬核模块,接收配置命令和数据,然后转发给配置引擎。你可以把它理解成FPGA内部的一个“配置命令翻译器”。通过它,你可以发送IPROG命令、写配置寄存器、读配置状态。
注意:ICAPE3原语在使用时,必须确保配置时钟(ICAPCLK)是稳定的。如果时钟抖动太大,配置命令可能发送失败,导致FPGA进入未知状态。
2.2 SPI Flash分区规划的核心逻辑
SPI Flash的分区不是随便划的,必须和FPGA的配置地址对齐。Xilinx 7系列FPGA的配置地址是按32位字寻址的,但SPI Flash是按字节寻址的。所以计算地址时要注意转换。
假设我用的是128Mb的SPI Flash,也就是16MB。我通常这样划分:
| 分区 | 起始地址(字节) | 大小 | 用途 |
|---|---|---|---|
| Golden区 | 0x000000 | 4MB | 基础镜像,绝对可靠 |
| Update区 | 0x400000 | 8MB | 新版本镜像 |
| 参数区 | 0xC00000 | 1MB | 存储版本号、校验值 |
| 预留区 | 0xD00000 | 3MB | 未来扩展 |
Golden区放在0地址是必须的,因为FPGA上电后默认从0地址加载。Update区的起始地址必须是配置地址粒度的整数倍。7系列FPGA的配置地址粒度是1MB(对于大多数器件),所以0x400000是合法的。
2.3 回退机制的设计要点
回退机制的核心是:如果Update区镜像启动失败,FPGA必须能自动回到Golden区。实现方式有两种:一种是利用FPGA内部的看门狗定时器,如果逻辑在指定时间内没有喂狗,就触发重配置;另一种是在逻辑里主动检测启动状态,如果发现异常,主动发送IPROG命令。
我一般用第二种,因为更可控。具体做法是:在Golden镜像里放一个状态机,上电后先尝试跳转到Update区。跳转后,Update区的逻辑会通过某个GPIO或者寄存器写一个“启动成功”的标志。如果Golden区的状态机在等待一段时间后没有收到这个标志,就认为Update区启动失败,重新加载Golden区。
提示:这个等待时间不能太短,要给Update区足够的初始化时间。我一般设500ms到1s,具体看逻辑复杂度。
3. ICAPE3原语的使用细节与实操
3.1 ICAPE3的端口定义与连接
ICAPE3的端口不多,但每个都很关键。下面是我常用的Verilog例化模板:
ICAPE3 #( .DEVICE_ID(32'h03628093), // 根据具体器件修改 .SIM_CFG_FILE_NAME("NONE") ) ICAPE3_inst ( .AVAIL(avail), .O(o_data), .PRDONE(prdone), .PRERROR(prerror), .CLK(icap_clk), .CSIB(icap_csib), .I(icap_i), .RDWRB(icap_rdwrb) );DEVICE_ID必须和你的FPGA型号匹配,这个值可以在Vivado的器件手册里查到。如果填错了,ICAPE3不会工作,而且不会报错,只会默默失效。我第一次用的时候就是抄了别人的DEVICE_ID,结果调了两天才发现是这个问题。
CLK是配置时钟,频率不能超过100MHz,我一般用50MHz。CSIB是片选,低有效。RDWRB是读写控制,低写高读。I是输入数据,O是输出数据。
3.2 IPROG命令的发送时序
IPROG命令是触发重配置的关键。它的格式是:先发送命令头0xFFFFFFFF,然后发送命令码0x0000000F,最后发送目标地址。目标地址是配置地址,不是字节地址。
具体时序如下:
- 拉低CSIB,拉低RDWRB(写模式)
- 在CLK上升沿发送0xFFFFFFFF
- 发送0x0000000F
- 发送目标地址的高16位和低16位
- 拉高CSIB
目标地址的计算方法是:字节地址除以4,然后取高16位和低16位。比如Update区的字节地址是0x400000,除以4得到0x100000,高16位是0x0010,低16位是0x0000。
注意:发送IPROG命令后,FPGA会立即开始重配置,当前逻辑会停止运行。所以发送命令前必须确保所有需要保存的状态都已经写入非易失存储器。
3.3 状态机设计
ICAPE3的控制逻辑最好用一个状态机来管理。我一般设计成以下几个状态:
- IDLE:等待触发信号
- SEND_HEADER:发送0xFFFFFFFF
- SEND_CMD:发送0x0000000F
- SEND_ADDR_H:发送地址高16位
- SEND_ADDR_L:发送地址低16位
- WAIT:等待重配置完成
每个状态持续一个时钟周期,用计数器控制。这样时序最干净,不容易出错。
4. Vivado工程配置与bitstream生成
4.1 约束文件的关键设置
Multiboot的约束文件和普通工程差不多,但有几个地方要特别注意。首先是ICAPE3的时钟约束,必须确保时钟频率不超过100MHz。其次是SPI Flash的引脚约束,要和你板子上的实际连接一致。
set_property CFGBVS VCCO [current_design] set_property CONFIG_VOLTAGE 3.3 [current_design] set_property BITSTREAM.CONFIG.SPI_BUSWIDTH 4 [current_design] set_property BITSTREAM.CONFIG.CONFIGRATE 50 [current_design]SPI_BUSWIDTH设成4表示用Quad SPI模式,速度更快。CONFIGRATE是配置时钟频率,50MHz是保守值,可以调到66MHz甚至更高,但要看Flash的支持。
4.2 生成Golden镜像和Update镜像
Golden镜像和Update镜像的生成方式略有不同。Golden镜像需要包含ICAPE3的控制逻辑和回退逻辑,Update镜像只需要包含实际的功能逻辑。
生成bitstream时,Golden镜像要设置起始地址为0:
set_property BITSTREAM.CONFIG.STARTUPADDR 0x000000 [current_design]Update镜像要设置起始地址为0x400000:
set_property BITSTREAM.CONFIG.STARTUPADDR 0x400000 [current_design]提示:STARTUPADDR的单位是字节,但实际写入Flash时,Vivado会自动处理地址对齐。
4.3 bitstream合并与MCS文件生成
Vivado生成的bitstream是独立的,要烧录到SPI Flash里,需要合并成一个MCS文件。我一般用write_cfgmem命令:
write_cfgmem -format MCS -size 16 -interface SPIx4 \ -loadbit "up 0x00000000 golden.bit up 0x00400000 update.bit" \ -file multiboot.mcs-size 16表示16MB的Flash。-interface SPIx4表示Quad SPI模式。-loadbit后面的参数指定了每个bitstream的加载地址。
生成MCS文件后,用Vivado的Hardware Manager或者第三方编程器烧录到Flash里。
5. 常见问题与排查技巧实录
5.1 FPGA卡在配置状态无法回退
这是最常见的问题。现象是:上电后FPGA不工作,LED不亮,JTAG也连不上。原因通常是Update区的镜像损坏,但回退机制没有生效。
排查步骤:
- 检查Golden区的回退逻辑是否正确。重点看状态机的超时计数器是否溢出。
- 检查ICAPE3的IPROG命令是否发送成功。可以用ILA抓一下ICAPE3的端口信号。
- 检查SPI Flash的地址是否正确。用Flash编程器读一下0x400000处的数据,看是否和Update镜像一致。
注意:如果Golden区的回退逻辑本身有问题,FPGA会彻底变砖,只能通过JTAG重新烧录。所以Golden镜像一定要经过充分测试。
5.2 ICAPE3不工作
ICAPE3不工作的原因很多,常见的有:
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| AVAIL一直为低 | 时钟不稳定 | 检查ICAPCLK的频率和抖动 |
| PRDONE一直为低 | 配置引擎忙 | 等待配置完成后再发送命令 |
| PRERROR为高 | 命令格式错误 | 检查IPROG命令的字节序 |
| 无输出 | DEVICE_ID错误 | 查手册确认正确的DEVICE_ID |
5.3 SPI Flash烧录失败
烧录失败通常是硬件连接问题。检查以下几点:
- Flash的供电是否正常(3.3V)
- SPI引脚是否连接正确(CS、CLK、MOSI、MISO)
- Flash是否被写保护(检查状态寄存器)
- 烧录速度是否太快(降低CONFIGRATE试试)
我遇到过一次,Flash的WP引脚悬空,导致写保护随机生效,烧录十次成功一次。后来把WP引脚上拉到3.3V,问题解决。
5.4 重配置后逻辑不启动
重配置后逻辑不启动,通常是时钟或复位问题。Update区的逻辑要确保有自己的时钟源和复位电路。如果Update区依赖Golden区提供的时钟,重配置后时钟会中断,导致逻辑无法启动。
提示:Update区的逻辑最好独立于Golden区,包括时钟、复位、电源。这样重配置后能立即开始工作。
6. 实操心得与避坑指南
6.1 Golden镜像要尽可能简单
Golden镜像的唯一任务是保证FPGA能启动,并能在Update区失败时回退。所以Golden镜像不要放复杂逻辑,越简单越可靠。我一般只放ICAPE3控制逻辑、回退状态机和一个简单的LED闪烁,用来指示Golden区正在运行。
6.2 版本号和校验值要存在参数区
参数区用来存储Update区的版本号和CRC校验值。Golden区在跳转前,先读参数区,确认Update区的版本号比当前高,且CRC校验通过,才跳转。这样可以避免跳转到损坏的镜像。
6.3 测试时要模拟断电
Multiboot最怕的是升级过程中断电。测试时一定要模拟这种情况:在烧录Update区的过程中,随机断电,然后重新上电,看FPGA是否能正常回退到Golden区。这个测试至少做20次,确保万无一失。
6.4 用ILA抓ICAPE3的信号
调试ICAPE3时,ILA是最好的工具。把ICAPE3的CLK、CSIB、RDWRB、I、O都接到ILA上,触发条件设为CSIB下降沿。这样可以看到完整的命令发送过程,快速定位问题。
6.5 注意Flash的扇区大小
SPI Flash的擦除是按扇区进行的,通常是4KB或64KB。烧录MCS文件时,Vivado会自动处理扇区对齐。但如果你手动计算地址,一定要确保地址是扇区大小的整数倍,否则擦除会失败。
7. 后续扩展与个人体会
这套Multiboot方案我已经在三个项目里用过,最长的已经稳定运行了两年多。中间经历过几次远程升级,每次都很顺利。唯一一次出问题是在一个电磁环境很差的现场,SPI Flash的时钟受到干扰,导致配置失败。后来在时钟线上加了磁珠,问题解决。
如果你要做更复杂的升级,比如差分升级或者压缩升级,可以在参数区里加更多的元数据,Golden区的逻辑也可以做得更智能。但核心思路不变:Golden区保底,Update区升级,ICAPE3触发重配置,回退机制兜底。
最后分享一个小技巧:在Golden区的逻辑里加一个计数器,记录回退次数。如果回退次数超过阈值,就点亮一个故障灯,提示现场人员检查。这个计数器存在参数区里,掉电不丢失。这样即使远程升级反复失败,也能快速定位问题。