对于FPGA开发者而言,现场设备固件升级一直是个令人头疼的问题。传统方式需要工程师携带专用烧录器前往现场,不仅效率低下,在设备分布广泛或安装位置特殊的情况下更是苦不堪言。本文介绍的纯Verilog实现远程升级方案,基于Xilinx 7系列FPGA,通过串口通信实现QSPI Flash的在线更新,并创新性地采用了双分区冗余设计,有效解决了"变砖"风险。
这个方案的核心价值在于:
系统采用主从式架构,上位机通过串口与FPGA通信,FPGA作为中间桥梁控制QSPI Flash的读写操作。整个升级过程分为以下几个阶段:
系统主要包含以下功能模块:
QSPI Flash被划分为两个完全独立的分区:
典型16MB Flash的地址分配示例:
| 区域 | 起始地址 | 结束地址 | 大小 |
|---|---|---|---|
| 分区A | 0x000000 | 0x7FFFFF | 8MB |
| 分区B | 0x800000 | 0xFFFFFF | 8MB |
| 标志区 | 0xFFFF00 | 0xFFFF02 | 3字节 |
分区切换通过以下Verilog代码实现:
verilog复制// 分区选择逻辑
reg [7:0] golden_reg;
always @(posedge clk) begin
if(reconfig_start) begin
flash_read(GOLDEN_REG_ADDR, golden_reg); // 读取标志位
end
end
assign active_partition = (golden_reg == 8'hA5) ? PARTITION_B : PARTITION_A;
// 升级成功后的切换操作
task update_golden_reg;
input verify_ok;
begin
if(verify_ok) begin
flash_write(GOLDEN_REG_ADDR, 8'hA5); // 写入切换标志
soft_reset <= 1'b1; // 触发软复位
end
end
endtask
为防止升级过程中断导致系统不可用,设计了多级保护:
上位机与FPGA采用自定义串口协议通信,基本帧结构如下:
| 字段 | 长度(字节) | 说明 |
|---|---|---|
| 帧头 | 1 | 固定0xAA |
| 命令类型 | 1 | 高4位指令,低4位长度 |
| 数据载荷 | N | 实际数据,N≤15 |
| CRC16 | 2 | 可选校验 |
典型指令集包括:
协议解析通过状态机完成,核心代码如下:
verilog复制localparam [2:0]
IDLE = 3'd0,
CMD_HEADER = 3'd1,
PAYLOAD = 3'd2,
CRC_H = 3'd3,
CRC_L = 3'd4;
reg [2:0] state;
reg [3:0] payload_cnt;
reg [7:0] payload_data[0:15];
always @(posedge clk) begin
if(rst) begin
state <= IDLE;
end else begin
case(state)
IDLE:
if(uart_rx_valid && uart_data == 8'hAA)
state <= CMD_HEADER;
CMD_HEADER:
if(uart_rx_valid) begin
cmd_type <= uart_data[7:4];
payload_len <= uart_data[3:0];
state <= (uart_data[3:0] > 0) ? PAYLOAD : CRC_H;
payload_cnt <= 0;
end
PAYLOAD:
if(uart_rx_valid) begin
payload_data[payload_cnt] <= uart_data;
payload_cnt <= payload_cnt + 1;
if(payload_cnt == payload_len - 1)
state <= CRC_H;
end
// CRC校验状态省略...
endcase
end
end
QSPI控制器采用寄存器配置方式,主要操作包括:
典型写操作时序实现:
verilog复制task flash_write;
input [23:0] addr;
input [7:0] data;
begin
// 发送写使能指令
spi_tx(8'h06); // WREN
// 发送页编程指令
spi_tx(8'h02); // PP
spi_tx(addr[23:16]);
spi_tx(addr[15:8]);
spi_tx(addr[7:0]);
spi_tx(data);
// 等待写完成
wait_write_done();
end
endtask
针对Flash可能出现的坏块问题,实现以下保护措施:
使用Verilog构建的Flash仿真模型支持:
模型初始化代码:
verilog复制reg [7:0] flash_mem [0:16*1024*1024-1];
initial begin
// 初始化为全FF
for(i=0; i<16*1024*1024; i=i+1)
flash_mem[i] = 8'hFF;
// 加载初始固件
if($test$plusargs("load_image"))
$readmemh("fw_image.hex", flash_mem);
end
完整的测试方案包括:
通信失败:
Flash操作异常:
升级后不启动:
虽然当前实现基于串口,但架构设计支持多种通信方式:
在实际项目中,我们曾遇到因Flash型号差异导致的兼容性问题。某次现场升级失败后发现,不同批次的Flash芯片在页编程时间要求上存在微小差异。解决方案是在初始化阶段自动检测Flash参数并动态调整时序。这个经验告诉我们,健壮的升级系统必须考虑硬件兼容性这一重要因素。