1. 为什么你的FPGA远程升级需要一个“双保险”?
大家好,我是老李,在FPGA和嵌入式这块摸爬滚打了十几年。今天想和大家聊聊一个非常实际,也容易让人头疼的问题:FPGA的远程升级。特别是用Altera(现在叫Intel PSG了)Cyclone IV这类经典器件的朋友,可能都遇到过这样的场景:设备部署在野外、工厂或者楼顶,出点问题就得派人爬高上低,成本高不说,效率还低。所以,远程升级(OTA)就成了刚需。
但远程升级最怕什么?怕“变砖”。想象一下,你正通过网络给远在千里之外的设备更新固件,数据传了90%,突然那边停电了,或者网络闪断了。如果设备里只有一个程序镜像,这次失败很可能就意味着设备彻底“罢工”,再也起不来了,唯一的挽救办法可能就是派人现场用JTAG重新烧录。这个风险,在工业控制和关键任务场景里是绝对不能接受的。
这就是我们今天要深入聊的 “双镜像容错升级” 方案的核心价值。它给你的FPGA固件上了个“双保险”。简单来说,就是在Flash里存两个“备份”:一个叫工厂镜像,你可以把它理解成手机的“恢复模式”或者电脑的“安全模式”,它非常精简、稳定,核心任务就是能启动并提供一个基础的升级通道;另一个叫应用镜像,这就是你设备正常运行时功能丰富的“主系统”。
整个升级过程,我们只对“应用镜像”所在的Flash区域进行擦写操作,工厂镜像那块区域是“只读”的,雷打不动。这样一来,即使在升级过程中发生任何意外断电或中断,设备重启后,依然能稳稳地从“工厂镜像”启动。一旦工厂镜像跑起来,它就能再次尝试连接服务器,重新下载完整的应用镜像,完成修复和升级。这个机制,从根本上解决了远程升级“变砖”的噩梦。
我刚开始做这个方案时,也走过弯路,以为就是简单地把两个程序塞进Flash。后来在Altera的文档和实际踩坑中发现,要实现稳定可靠的切换和容错,离不开两个关键的IP核:ASMI Parallel IP(负责对Flash进行精细的扇区擦除和写入)和 Remote Update IP(负责管理镜像切换、看门狗和重配置)。下面,我就结合自己的实战经验,把从原理到代码,从配置到异常测试的完整流程,给大家掰开揉碎了讲清楚。
2. 核心武器:ASMI Parallel IP的扇区擦除艺术
在简易版远程升级里,我们可能图省事,直接用了bulk_erase(批量擦除)命令,一下子清空整个Flash。这在只有一个镜像的时候没问题。但在双镜像方案里,工厂镜像和应用镜像在Flash中是分区存放的,你绝对不能一股脑全擦了,必须进行“外科手术式”的精确擦除。
这就需要用到ASMI IP的 sector_erase(扇区擦除) 功能。我当初也犯过糊涂,看到“sector”这个词,想当然地以为是按字节操作,结果上板测试时发现擦除速度慢得离谱。后来仔细看了手册才明白,Flash的存储结构是分“块(Block)”、“扇区(Sector)”和“页(Page)”的。一次扇区擦除,操作的是一个固定大小的存储块,对于我用的EPCS128(128Mb串行Flash)来说,一个扇区是64KB。
关键一步:找到你的镜像地图 在进行任何擦写操作前,你必须清楚你的两个镜像在Flash中的具体地址。这个地址是在Quartus II生成.jic合并文件时设定的。以我的工程为例:
- 工厂镜像:存放在地址
0x000000 ~ 0x3FFFFF(即前4MB)。 - 应用镜像:存放在地址
0x400000 ~ 0x7FFFFF(紧接着的4MB)。
那么,当我需要升级应用镜像时,只需要精确擦除0x400000到0x7FFFFF这个区间的扇区即可。根据EPCS128的扇区划分表(手册里都有),这个区间对应的是第16到第31号扇区。
实战代码:扇区擦除状态机 理解了原理,操作就清晰了。你需要设计一个状态机来驱动ASMI IP。下面是我工程里扇区擦除部分的核心状态机逻辑,我用Verilog写个简化的示例:
localparam S_IDLE = 4'd0;
localparam S_ERASE_CMD = 4'd1;
localparam S_ERASE_EXEC = 4'd2;
localparam S_ERASE_POLL = 4'd3;
localparam S_ERASE_DONE = 4'd4;
reg [3:0] erase_state;
reg [23:0] sector_base_addr; // 当前要擦除的扇区基地址
reg [7:0] sector_count; // 已擦除扇区计数
reg [31:0] delay_cnt;
always @(posedge clk or posedge rst) begin
if (rst) begin
erase_state <= S_IDLE;
asmi_busy_prev <= 1'b0;
sector_base_addr <= 24'h400000; // 从应用镜像起始地址开始
sector_count <= 8'd0;
end else begin
asmi_busy_prev <= asmi_busy;
case (erase_state)
S_IDLE: begin
if (erase_start) begin // 收到擦除启动命令
erase_state <= S_ERASE_CMD;
sector_base_addr <= 24'h400000;
sector_count <= 8'd0;
end
end
S_ERASE_CMD: begin
// 配置ASMI IP操作码为扇区擦除(0xD8),并写入目标地址
asmi_data_in <= {8'hD8, sector_base_addr};


5466

被折叠的 条评论
为什么被折叠?



