1. 项目背景与核心挑战
FPGA远程升级在工业控制、通信设备等领域是个硬需求。去年我们团队接手的一个风电项目里,12台风机控制器分布在80米高的塔筒内,每次升级都要搭升降机上去烧写,人工成本高不说,遇到恶劣天气还得停工。这种场景下,可靠的远程升级方案就成了刚需。
串口升级看似简单,实际藏着不少坑。最头疼的就是升级过程中断电或数据错误导致的"变砖"问题——我们吃过亏,有次现场升级时串口干扰导致配置文件损坏,整批设备返厂重烧,直接损失15万。双冗余设计正是为应对这种极端情况而生,相当于给升级过程上了双保险。
2. 硬件架构设计要点
2.1 双Bank存储方案
核心采用Xilinx Spartan-6的BPI Flash双Bank架构,物理划分Bank0(256Mb)和Bank1(256Mb)。关键设计在于:
- 地址线A24作为Bank选择信号(0=Bank0,1=Bank1)
- 通过CPLD控制PROG_B和INIT_B信号实现Bank切换
- 每个Bank保留最后4KB作为元数据区,存储版本号和CRC32校验值
实测中发现Flash切换时的时序问题:当Bank切换信号变化后,需要至少50ms延时才能稳定读取新Bank数据。我们在CPLD中增加了硬件延时电路,用74LVC1G123单稳态触发器实现精确延时。
2.2 串口通信加固
采用RS-485接口(MAX3485芯片)替代传统TTL串口,传输距离可达1200米。协议层做了三点优化:
- 数据包增加16位CRC校验(多项式0x8005)
- 实现滑动窗口协议,支持断点续传
- 每个数据包包含双Bank目标地址,便于异常恢复
重要提示:RS-485终端电阻(120Ω)必须匹配线路阻抗,我们曾因省略终端电阻导致升级包误码率飙升到10^-3
3. 固件升级流程实现
3.1 安全启动链验证
上电后Bootloader执行如下检查:
c复制void verify_banks() {
uint32_t crc0 = calculate_crc(BANK0_METADATA_ADDR);
uint32_t crc1 = calculate_crc(BANK1_METADATA_ADDR);
if(crc0 == stored_crc0 && crc1 == stored_crc1) {
// 双Bank完好,选择版本更新的
boot_newer_bank();
} else if(crc0 == stored_crc0) {
// 仅Bank0完好
boot_bank(0);
} else if(crc1 == stored_crc1) {
// 仅Bank1完好
boot_bank(1);
} else {
// 双Bank损坏,进入救援模式
enter_recovery();
}
}
3.2 双Bank交替升级算法
升级过程采用"乒乓操作"策略:
- 当前运行在Bank0时,新固件写入Bank1
- 写入完成后校验Bank1的CRC值
- 更新元数据中的启动标志位
- 重启后自动切换至Bank1运行
关键技巧:在擦除目标Bank前,先将新固件暂存到FPGA的Block RAM中(需提前计算所需BRAM大小)。我们用的XC6SLX45有2.1Mb BRAM,足够缓存压缩后的固件。
4. 防变砖机制详解
4.1 三级回滚保护
- 传输层校验:每个数据包带CRC和序列号,连续3次校验失败终止升级
- 写入验证:每写入256字节执行回读比对,差异超过5%触发Bank切换
- 启动保护:新固件首次运行时有30秒"试用期",期间看门狗不喂狗则自动回滚
实测数据:加入三级保护后,200次模拟异常断电测试中成功恢复198次,远高于单Bank方案的143次。
4.2 救援模式实现
当双Bank均损坏时,FPGA会:
- 将GPIO12拉低(触发CPLD切换至备份串口)
- 通过低速UART(9600bps)输出救援菜单
- 支持通过YMODEM协议重新烧写任一Bank
我们在PCB上预留了救砖触点:用镊子短接J7跳线两端即可强制进入救援模式,这个设计在后期维护中至少挽救了3台设备。
5. 实战问题排查记录
5.1 典型故障案例
| 故障现象 | 根本原因 | 解决方案 |
|---|---|---|
| 升级后频繁重启 | Bank切换时序违反setup时间 | CPLD增加时钟同步逻辑 |
| CRC校验通过但功能异常 | Flash位翻转累积 | 固件中加入EDC(Error Detection Code) |
| 救援模式无法触发 | 上电复位电路毛刺 | 在PROG_B信号端增加100nF电容 |
5.2 性能优化技巧
- 压缩传输:用LZSS算法压缩固件,实测115200bps波特率下升级时间从18分钟降至7分钟
- 差分升级:通过bsdiff生成差分包,典型更新包大小减少60-80%
- 后台验证:利用FPGA空闲时间预计算新固件CRC,减少重启等待时间
6. 生产测试方案
我们开发了自动化测试夹具,关键测试项包括:
- 模拟电压跌落(3.3V±10%)时的升级稳定性
- 注入可控噪声测试误码率(要求<10^-6)
- 连续100次Bank切换压力测试
- Flash寿命测试(每个扇区擦写1000次后验证数据保持性)
测试脚本示例(Python):
python复制def test_bank_switch():
for i in range(100):
dut.reset()
current_bank = get_current_bank()
new_bank = 1 - current_bank
program_bank(new_bank, test_firmware)
assert verify_bank(new_bank), f"Bank {new_bank} verify failed"
dut.reset()
assert get_current_bank() == new_bank, "Bank switch error"
这个方案已在风电、轨道交通等场景部署300+节点,最长的现场设备已稳定运行18个月,完成过27次远程升级。关键收获是:冗余设计不是简单的资源翻倍,而是要通过架构设计实现1+1>2的可靠性提升。下次我会分享如何在Zynq上实现三模冗余升级方案。