基于串口的FPGA远程升级程序设计与实现
【基于串口的FPGA远程升级程序】 verilog源代码,vivado开发,支持xilinx等各类厂家FPGA。 串口升级支持RS422,RS232,RS485,最大速率可达921600bps,波特率可设置,奇偶校验可设置。具备升级过程中异常掉电防变砖的功能,具备golden镜像、multiboot镜像、支持镜像损坏回退,看门狗超时回退等功能。串口预留FIFO接口,后续可移植其它接口升级
搞硬件调试最怕啥?不是代码写不对,是板子突然变砖。尤其是那种部署在偏远地方的设备,你没法随身带个 JTAG 下载器爬上去救场。这时候,一个靠谱的串口 Bootloader 就是最后的救命稻草。最近把之前攒的一个基于串口的 FPGA 远程升级方案整理了一下,核心思路其实很直接:把复杂的烧录逻辑下沉到 FPGA 内部,利用 UART 这种最底层的通信通道做传输,配合一些防呆机制,尽量保证“怎么折腾都不死机”。
这玩意儿是用 Vivado 写的 Verilog,虽然主要是在 Xilinx 板上跑的,但原理层面对 Altera 或者其他厂家的 FPGA 基本通用,只要你有 SPI Flash 或者 QSPI Flash 接口就行。物理层上我做了兼容,RS232、RS422、RS485 都支持,毕竟工业现场有时候环境恶劣,差分信号更稳些。波特率最高给到了 921600bps,速度不算快,但对于固件升级来说,稳定性比速度重要得多。
先看看核心的状态机部分。很多简单的升级程序只关心数据收发,忽略了握手和校验,结果网络稍微有点抖动就废了。我这里把接收过程拆得很细,特别是针对异常掉电的保护,不能简单地把数据往 Flash 里灌。
// 简化版的状态定义 typedef enum logic [2:0] { ST_IDLE, // 空闲等待命令 ST_WAIT_SYNC, // 等待同步头 ST_RECV_LEN, // 接收长度信息 ST_RECV_DATA, // 接收有效载荷 ST_VERIFY_CRC, // 校验 CRC ST_WRITE_FLASH, // 写入 Flash ST_UPDATE_DONE, // 更新完成 ST_ERROR_ROLLBACK // 错误回滚 } t_upgrade_state; always_ff @(posedge clk) begin case (curr_state) ST_RECV_DATA: // 这里有个小细节,接收过程中如果看门狗超时,直接跳去回滚 if (watchdog_timeout || uart_rx_error) next_state <= ST_ERROR_ROLLBACK; else if (data_cnt == expected_len) next_state <= ST_VERIFY_CRC; ST_WRITE_FLASH: // 双 Bank 切换逻辑,确保旧镜像还在 if (current_bank == 0) target_bank <= 1; else target_bank <= 0; endcase end这段代码里埋了两个关键点的影子。一个是看门狗超时检测 (watchdog_timeout),另一个是双 Bank 切换。以前遇到过几次,升级写到一半电源闪了一下,Flash 里的分区表乱了,下次启动找不到入口。现在的方案里,引入了 Golden 镜像的概念。你可以理解为系统里始终保留一份绝对可靠的“出厂设置”,一旦新刷入的镜像损坏或者启动失败超过阈值(比如启动失败 3 次),Bootloader 会自动切回 Golden 分区引导。
为了防止校验出错,我在数据链路层加了奇偶校验配置,并且每个数据包都有 CRC 校验。一旦校验失败,不会尝试修复,而是直接丢弃并重传,实在重传多次失败,就触发那个STERRORROLLBACK状态。这个状态下,程序会检查主备用分区的标志位,强制从已知良好的分区启动。
// 镜像健康度检查与回退逻辑 always_comb begin boot_partition = 0; // 默认从 Partition A 启动 if (partition_a_valid && partition_a_crc_pass) begin boot_partition = 0; end else if (partition_b_valid && partition_b_crc_pass) begin // 如果 A 坏了,尝试 B boot_partition = 1; end else begin // 全挂了,强制进入 Golden 模式或复位 golden_mode_enable = 1'b1; reset_system = 1'b1; end end这里还有个挺有意思的设计,就是预留了 FIFO 接口。你可能觉得串口升级慢,以后想换以太网或者 USB 升级怎么办?如果整个协议栈都绑死在 UART IP 核上,改起来得推倒重来。所以我把数据接收模块抽象成了一个标准的 FIFO 输入接口。
【基于串口的FPGA远程升级程序】 verilog源代码,vivado开发,支持xilinx等各类厂家FPGA。 串口升级支持RS422,RS232,RS485,最大速率可达921600bps,波特率可设置,奇偶校验可设置。具备升级过程中异常掉电防变砖的功能,具备golden镜像、multiboot镜像、支持镜像损坏回退,看门狗超时回退等功能。串口预留FIFO接口,后续可移植其它接口升级
现在的架构是:外部物理接口(UART/Ethernet/USB) -> FIFO -> 升级控制器。这样以后如果要移植成网口升级,只需要替换最外层的收发模块,中间的校验、Flash 写入、多版本管理逻辑完全不用动。这在项目后期维护时能省不少事,特别是那种系列化产品,不同型号可能通讯介质不一样,但固件升级流程必须统一的时候。
关于波特率设置,虽然标称 921600bps,但在实际工程里,如果线缆比较长或者干扰大,建议降到 115200 甚至更低。代码里留了一个寄存器配置位,可以在编译前通过参数宏定义固定波特率,也可以通过上位机下发指令动态调整。不过为了安全起见,动态调整最好在升级流程开始前完成,不要在数据传输中间改,否则时序对不上容易丢包。
最后说下 Multiboot 的支持。有些场景需要测试新功能,但不想覆盖掉生产环境的稳定代码。这个方案允许同时存在多个镜像副本,启动时根据特定的按键组合或者 GPIO 电平来选择加载哪个版本的固件。这就像手机的双系统一样,万一新版本有问题,重启选个旧的就完事了,不用重新烧录。
总之,这套东西的核心不在于功能有多炫酷,而在于“兜底”。硬件这东西,一次意外就能让之前的努力白费,所以能在 FPGA 内部把这些容错逻辑写扎实,比什么都强。代码已经开源了一部分,有需要的可以拿去改改,希望能帮大家少踩几个坑。
