AD9361项目Flash双冗余系统设计与实现指南

目录

  1. 项目概述
  2. 系统硬件架构
  3. 双冗余实现原理
  4. 详细设计与实现
  5. 使用指南
  6. 修改与配置说明
  7. 常见问题与故障排除
  8. 技术术语表
  9. 附录:项目文件结构

1. 项目概述

1.1 功能简介

本项目基于Xilinx Zynq-7000平台开发的AD9361射频系统,实现了在单一QSPI Flash芯片上存储两套不同程序并通过网络接口远程选择启动程序的功能。这种设计为系统提供了双重冗余保障,特别适用于无法现场操作或需要远程维护的应用场景。

系统通过以太网UDP协议接收特定命令,根据命令内容选择从Flash的主区域或备份区域启动,无需增加额外的Flash芯片,大大提高了系统的可靠性和可维护性。

1.2 应用场景

  • 远程部署设备:位于偏远地区或难以到达的设备,可通过网络远程切换程序
  • 高可靠性系统:需要双冗余保障的关键设备,如通信基站、监控系统等
  • 持续运行系统:需要在线升级但不能长时间中断的系统
  • 射频信号处理系统:基于AD9361的无线通信设备,需要灵活切换不同协议栈

1.3 主要特点

  • 双重冗余存储:在单一Flash芯片上划分两个独立区域,分别存储主程序和备份程序
  • 远程切换能力:通过以太网UDP协议发送命令控制启动程序的选择
  • 无需硬件冗余:不增加额外的Flash芯片,降低硬件复杂度和成本
  • 系统可靠性提升:在主程序出现问题时,可立即切换到备份程序继续运行
  • 兼容原有系统:对原有硬件系统改动小,易于集成
  • 完整开发支持:提供命令格式定义、工具示例及详细操作文档

2. 系统硬件架构

2.1 硬件平台

该项目基于Vivado 2018.3开发环境,在Xilinx Zynq-7000系列FPGA平台上实现。主要硬件组件包括:

  • 处理系统(PS):Zynq-7000系列SoC,集成ARM Cortex-A9双核处理器
  • 可编程逻辑(PL):实现自定义逻辑和接口控制
  • AD9361射频收发器:高性能RF前端,支持2x2 MIMO操作
  • QSPI Flash:系统启动和程序存储介质,典型容量16-32MB
  • DDR内存:系统运行时数据存储,典型容量512MB-1GB
  • 以太网接口:10G以太网PHY,用于高速数据传输和远程控制

2.2 硬件连接图

+---------------+     +---------------+     +-----------------+
|               |     |               |     |                 |
|    以太网      |---->|    网卡PHY    |---->|  FPGA/Zynq SoC  |
|    (RJ45)     |     |   (10G/1G)    |     |                 |
|               |     |               |     |                 |
+---------------+     +---------------+     |------+----------+
                                            |      |
+---------------+     +---------------+     |      |
|               |     |               |     |      |
| JTAG接口(烧写) |---->|   QSPI Flash  |<---+       |
|               |     |               |            |
+---------------+     +---------------+            |
                                                   |
                      +---------------+            |
                      |               |            |
                      |   AD9361模块  |<-----------+
                      |               |
                      +---------------+

2.3 系统逻辑框图

双冗余Flash系统的逻辑结构如下:

+---------------------+     +-------------------+     +-------------------+
|                     |     |                   |     |                   |
|  网络命令(UDP包)     +---->+  以太网控制器      +---->+  Flash选择逻辑    |
|                     |     |                   |     |  (GPIO寄存器)     |
+---------------------+     +-------------------+     +------+------------+
                                                             |
                                                             v
+---------------------+     +-------------------+     +------+------------+
|                     |     |                   |     |                   |
|  处理系统(PS)        <-----+  First Stage      <-----+  QSPI Flash       |
|  ARM Cortex-A9      |     |  Boot Loader      |     |  (双区域存储)      |
+---------------------+     +-------------------+     +-------------------+
        |                                             /        \
        |                                            /          \
        v                                           v            v
+---------------------+                  +----------+---+  +-----+--------+
|                     |                  |              |  |              |
|  AD9361/应用程序     |                  |  主程序区域  |  | 备份程序区域  |
|                     |                  |  (0x0)       |  | (0x800000)   |
+---------------------+                  +--------------+  +--------------+

3. 双冗余实现原理

3.1 技术概述

本项目实现了在单一QSPI Flash芯片上存储两套不同程序的冗余机制。通过合理的存储空间划分和特定的引导逻辑,系统可以根据网络命令或硬件信号选择从Flash的不同区域启动程序。这种方式不需要额外的硬件,只通过软件和固件修改实现双重保障。

3.2 存储区域划分示意图

Flash存储区域划分如下图所示:

+----------------------------+  0x000000
|                            |
|                            |
|        主程序区域           |  
|                            |
|                            |
+----------------------------+  0x800000 (默认偏移地址)
|                            |
|                            |
|        备份程序区域         |  
|                            |
|                            |
+----------------------------+  Flash末尾(视容量而定)

在同一片QSPI Flash上,我们采用以下区域划分方案:

  • 主程序区域:从Flash起始地址(0x0)开始,用于存储主要系统程序
  • 备份程序区域:从Flash的偏移地址(默认0x800000,8MB处)开始,用于存储备份系统程序

每个区域存储的内容结构完全相同,包括:

  • First Stage Boot Loader (FSBL)
  • FPGA比特流文件(bitstream)
  • U-Boot/应用程序
  • 配置文件等

3.3 启动选择机制详解

系统启动过程中的程序选择机制如下:

  1. 加电复位:系统加电或复位后,处理器开始执行内部BootROM
  2. 读取选择标志:修改后的FSBL在初始化阶段读取Flash选择寄存器值
  3. 判断区域:根据标志值(0或1)确定启动区域:
    • 标志为0:从主程序区域(0x0)加载程序
    • 标志为1:从备份程序区域(0x800000)加载程序
  4. 程序加载:FSBL从选定区域加载后续程序(比特流、应用程序等)
  5. 系统启动:完成程序加载后,控制权交给应用程序,系统正常运行

3.4 启动流程时序图

    上电/复位      FSBL初始化      读取选择标志     加载程序       系统运行
       |              |              |              |              |
       |              |              |              |              |
       v              v              v              v              v
+------+------+   +---+----+   +-----+------+   +---+----+   +----+-----+
|             |   |        |   |            |   |        |   |          |
| BootROM执行 |-->| 初始化  |-->| 检查标志位  |-->| 加载   |-->| 应用程序 |
|             |   | 硬件    |   | (GPIO)     |   | 程序区 |   | 运行     |
+-------------+   +--------+   +-----+------+   +---+----+   +----------+
                                     |              ^
                                     |             / \
                                     v            /   \
                              +------+------+    /     \
                              |             |   /       \
                              | 确定启动区域 |--+         |
                              |             |  |         |
                              +------+------+  |         |
                                    / \        |         |
                                   /   \       |         |
                                  v     v      v         |
                          +-------+   +-------+-----     |
                          |       |   |            |     |
                          | 主区域 |  | 备份区域    |←---+
                          | (0x0) |   | (0x800000) |
                          +-------+   +------------+

4. 详细设计与实现

4.1 UDP通信模块设计

UDP通信模块负责接收网络命令并解析Flash启动选择指令。此部分是实现远程控制功能的核心,由以下几个子模块组成:

4.1.1 UDP顶层模块(udp_look_back)

该模块实现了以太网物理层和数据链路层接口,负责UDP数据包的收发。主要功能:

  • 初始化以太网控制器
  • 管理数据包收发
  • 提供Flash选择信号接口
module udp_look_back(
  // 系统接口
  input            sys_clk_i,  
  // 以太网物理接口
  input            gtrefclk1_p, gtrefclk1_n,
  output [0:0]     sfp_tx_p, sfp_tx_n,
  input  [0:0]     sfp_rx_p, sfp_rx_n,
  // UDP数据接口
  input [63:0]     udp_rx_data,
  input            udp_rx_data_valid,
  // Flash选择输出 - 添加的新接口
  output           flash_sel,
  output           flash_sel_valid
);

// 内部逻辑...

// 将channel_wrapper的信号连接到外部接口
channel_wrapper channel_wrapper_i(
  // 其他连接...
  .flash_sel        (flash_sel),
  .flash_sel_valid  (flash_sel_valid)
);

endmodule

4.1.2 通道包装模块(channel_wrapper)

该模块封装了UDP协议栈,处理具体的数据包解析与转发。主要功能:

  • UDP数据包处理
  • 协议状态管理
  • 数据分包与组包
  • Flash指令解析与转发
module channel_wrapper(
  // 系统接口
  input           coreclk,
  input           areset_n,
  // MAC层接口  
  output          m_axis_tx_tvalid,
  input           m_axis_tx_tready,
  // 数据接口
  input [63:0]    udp_rx_data,
  input           udp_rx_data_valid,
  // Flash选择信号 - 添加的新接口
  output          flash_sel,
  output          flash_sel_valid
);

// 内部逻辑...

// 实例化UDP接收模块,处理命令解析
udp_rx udp_rx_inst (
  // 其他连接...
  .flash_sel        (flash_sel),
  .flash_sel_valid  (flash_sel_valid)
);

endmodule

4.1.3 UDP接收模块(udp_rx)

该模块实现了网络命令解析功能,是双冗余实现的核心部分。代码示例:

module udp_rx #(
  parameter [15:0] LOCAL_PORT = 16'd5000,  // 本地UDP端口号
  // 其他参数...
)(
  // 系统和数据接口...
  
  // Flash选择输出 - 新增接口
  output reg flash_sel = 1'b0,      // Flash启动选择信号: 0-主程序, 1-备份程序
  output reg flash_sel_valid = 1'b0 // Flash选择有效标志
);

// UDP命令解析常量定义
localparam CMD_FLASH_SEL_MAIN = 32'hF1A5_0000;    // 选择主程序命令
localparam CMD_FLASH_SEL_BACKUP = 32'hF1A5_0001;  // 选择备份程序命令

// 内部状态和逻辑...

// 命令解析逻辑
always @(posedge clk) begin
  if (!rst_n) begin
    flash_sel <= 1'b0;
    flash_sel_valid <= 1'b0;
  end else begin
    // 当接收到完整的UDP包时,检查是否包含Flash启动选择命令
    if (udp_rx_cnt >= 4) begin  // 确保已接收到至少4字节数据(命令字)
      // 检查数据包前32位是否为Flash选择命令
      if (udp_rx_data[0:31] == CMD_FLASH_SEL_MAIN) begin
        flash_sel <= 1'b0;             // 选择主程序
        flash_sel_valid <= 1'b1;       // 设置命令有效标志
      end else if (udp_rx_data[0:31] == CMD_FLASH_SEL_BACKUP) begin
        flash_sel <= 1'b1;             // 选择备份程序
        flash_sel_valid <= 1'b1;       // 设置命令有效标志
      end
    end else if (udp_rx_cnt == 0) begin
      // 在新数据包开始时复位命令有效标志
      flash_sel_valid <= 1'b0;
    end
  end
end

endmodule

该模块核心功能是识别特定的命令码并生成相应的Flash选择信号。当收到选择主程序命令(0xF1A5_0000)时,设置flash_sel为0;当收到选择备份程序命令(0xF1A5_0001)时,设置flash_sel为1。

4.2 Flash启动控制实现

Flash启动控制相关部分负责管理Flash选择信号并修改启动过程,主要由以下部分组成:

4.2.1 顶层模块(AD9361_prj)

顶层模块将网络解析出的Flash选择信号连接到硬件引脚,并维护当前的选择状态:

module AD9361_prj(
  // 标准系统接口
  input [14:0]DDR_addr,
  // 其他标准接口...
  
  // Flash启动控制信号 - 新增接口
  output flash_sel_pin,      // Flash启动选择引脚
  output flash_program_pin   // Flash编程启动信号
);

// 内部信号声明
// ...

// Flash双重启动控制信号
wire flash_sel;
wire flash_sel_valid;

// 状态寄存器,保存当前Flash启动状态
reg flash_sel_reg = 1'b0;
reg flash_program_reg = 1'b0;

// 当收到有效的Flash选择信号时,更新状态寄存器
always @(posedge ui_clk) begin
  if (flash_sel_valid) begin
    flash_sel_reg <= flash_sel;          // 保存当前选择状态
    flash_program_reg <= 1'b1;           // 触发Flash编程信号脉冲
  end else begin
    flash_program_reg <= 1'b0;           // 复位Flash编程信号
  end
end

// 将状态寄存器连接到输出引脚
assign flash_sel_pin = flash_sel_reg;
assign flash_program_pin = flash_program_reg;

// 实例化UDP模块,接收网络命令
udp_look_back udp_look_back_inst (
  // 其他连接...
  .flash_sel(flash_sel),
  .flash_sel_valid(flash_sel_valid)
);

// 其他模块实例化...

endmodule

该模块的主要功能是:

  1. 接收UDP模块解析出的Flash选择信号
  2. 将选择状态保存在寄存器中
  3. 生成编程信号脉冲,通知系统更新配置
  4. 通过物理引脚输出选择和编程信号

4.2.2 First Stage Boot Loader修改

FSBL代码修改是实现双冗余启动的关键部分,主要修改了以下文件:

1) qspi.h - 增加双冗余区域定义
/*
 * 双冗余Flash区域定义
 */

/* Flash区域定义 */
// 双重Flash区域偏移量 - 在Flash的中间位置开始存放备份程序
#define FLASH_BACKUP_OFFSET    0x800000  // 8MB偏移量,根据实际Flash大小可调整
#define FLASH_SEL_REG_ADDR     0x43C00000 // Flash选择寄存器地址,需要与PS端GPIO映射一致

// Flash选择标志
#define FLASH_SEL_MAIN         0  // 选择主程序区域
#define FLASH_SEL_BACKUP       1  // 选择备份程序区域

此头文件定义了关键的偏移量和寄存器地址。FLASH_BACKUP_OFFSET指定了备份程序的起始地址,默认设为8MB(0x800000)。FLASH_SEL_REG_ADDR指定了存储选择标志的寄存器地址。

2) qspi.c - 修改Flash访问函数,实现从不同区域读取
/**
 * QspiAccess - 提供QSPI Flash访问功能,支持双区域启动
 *
 * @param SourceAddress      要读取的Flash地址
 * @param DestinationAddress 目标内存地址
 * @param LengthBytes        要读取的字节数
 *
 * @return XST_SUCCESS 成功, XST_FAILURE 失败
 */
u32 QspiAccess(u32 SourceAddress, u32 DestinationAddress, u32 LengthBytes)
{
    u32 FlashSel;
    u32 SrcAddr;
    u32 Length;
    u32 BankSel;
    u32 Status;
    
    // 读取Flash选择标志
    FlashSel = Xil_In32(FLASH_SEL_REG_ADDR);
    
    // 根据标志调整源地址
    if (FlashSel == FLASH_SEL_BACKUP) {
        fsbl_printf(DEBUG_INFO, "Booting from backup flash region (offset: 0x%08x)\r\n", 
                   FLASH_BACKUP_OFFSET);
        SrcAddr = SourceAddress + FLASH_BACKUP_OFFSET;
    } else {
        fsbl_printf(DEBUG_INFO, "Booting from main flash region\r\n");
        SrcAddr = SourceAddress;
    }
    
    // 线性模式访问处理
    if (LinearBootDeviceFlag == 1) {
        // 银行选择逻辑 (用于大容量Flash)
        if (QspiFlashSize > FLASH_SIZE_16MB) {
            BankSel = SrcAddr/FLASH_SIZE_16MB;
            Status = SendBankSelect(BankSel);
            if (Status != XST_SUCCESS) {
                fsbl_printf(DEBUG_INFO, "Bank selection Failed\r\n");
                return XST_FAILURE;
            }
        }
        
        // 处理地址超过16MB的情况
        if (SrcAddr >= FLASH_SIZE_16MB) {
            SrcAddr = SrcAddr % FLASH_SIZE_16MB;
        }
        
        // 计算当前银行的读取长度
        if ((SrcAddr + LengthBytes) > FLASH_SIZE_16MB) {
            Length = FLASH_SIZE_16MB - SrcAddr;
        } else {
            Length = LengthBytes;
        }
        
        // 读取数据
        memcpy((void*)DestinationAddress, 
               (void*)(FlashReadBaseAddress + SrcAddr), 
               Length);
        
        // 处理跨银行读取
        if((SrcAddr + LengthBytes) > FLASH_SIZE_16MB){
            memcpy((void*)(DestinationAddress + Length),
                   (void*)(FlashReadBaseAddress),
                   LengthBytes - Length);
        }
    }
    
    // 非线性模式访问逻辑...
    
    return XST_SUCCESS;
}

此函数的修改是双冗余功能的核心,主要逻辑是:

  1. 读取Flash选择标志
  2. 如果选择备份区域,将源地址加上偏移量
  3. 处理Flash银行选择和地址映射
  4. 执行实际的数据读取操作
3) main.c - 初始化阶段检查Flash选择标志
int main(void)
{
    u32 BootModeRegister = 0;
    u32 Status = XST_SUCCESS;
    u32 RegVal;
    
    // 映射Flash选择信号寄存器
    // 将物理I/O地址映射到PS可访问区域
    Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR, 0x00000000); // 设置GPIO为输入模式
    
    // PS7初始化...
    
    // 在初始化阶段显示Flash选择信息
    RegVal = Xil_In32(FLASH_SEL_REG_ADDR);
    if (RegVal == FLASH_SEL_BACKUP) {
        fsbl_printf(DEBUG_GENERAL, "Boot Mode: QSPI Flash Backup Region\r\n");
    } else {
        fsbl_printf(DEBUG_GENERAL, "Boot Mode: QSPI Flash Main Region\r\n");
    }
    
    // 正常的FSBL启动流程...
}

main.c的修改主要是:

  1. 初始化GPIO寄存器,使其能够读取Flash选择信号
  2. 在启动早期读取并显示当前的启动区域

4.2.3 硬件信号连接

为了将Flash选择信号连接到物理引脚,在约束文件(AD9361_prj.xdc)中添加了相应的引脚定义:

# Flash启动选择引脚和编程信号引脚
# 选择引脚:指示当前使用的Flash区域(0=主区域, 1=备份区域)
set_property PACKAGE_PIN G15 [get_ports flash_sel_pin]
set_property IOSTANDARD LVCMOS25 [get_ports flash_sel_pin]

# 编程信号引脚:触发更新Flash选择状态
set_property PACKAGE_PIN G16 [get_ports flash_program_pin]
set_property IOSTANDARD LVCMOS25 [get_ports flash_program_pin]

这些约束将flash_sel_pinflash_program_pin信号分别映射到FPGA的G15和G16引脚,使用LVCMOS25电平标准。可根据实际硬件设计调整引脚分配。

4.3 数据流程图

下图展示了Flash选择命令从网络接收到系统启动的完整数据流:

+----------------+    +----------------+    +----------------+    +----------------+
|                |    |                |    |                |    |                |
| UDP网络命令     |--->| UDP数据包解析   |--->| Flash选择标志   |--->| GPIO引脚输出   |
| 0xF1A50000/1   |    | (udp_rx模块)   |    | (寄存器存储)    |    | (物理连接)     |
|                |    |                |    |                |    |                |
+----------------+    +----------------+    +----------------+    +----------------+
                                                                         |
                                                                         | 系统重启
                                                                         v
+----------------+    +----------------+    +----------------+    +----------------+
|                |    |                |    |                |    |                |
| 系统正常运行    |<---| 应用程序加载    |<---| FSBL读取选择   |<---| 读取GPIO状态  |
| (选定的程序)    |    | (从选定区域)    |    | (QspiAccess)   |    | (寄存器值)    |
|                |    |                |    |                |    |                |
+----------------+    +----------------+    +----------------+    +----------------+

5. 使用指南

5.1 环境准备

在使用双冗余Flash系统前,需要准备以下开发环境和工具:

工具/环境版本用途
Vivado2018.3FPGA设计、综合、实现及Flash编程
Xilinx SDK2018.3软件开发、FSBL编译及BOOT.bin生成
Wireshark任意网络数据包分析,验证UDP命令
网络测试工具netcat/Python发送UDP命令测试
JTAG下载器Xilinx兼容连接目标板进行编程
串口终端PuTTY/SecureCRT等系统调试信息查看

开发环境搭建步骤:

  1. 安装Xilinx Vivado 2018.3及SDK
  2. 配置Vivado开发环境和安装板卡支持包
  3. 准备网络连接,确保可以与目标板通信
  4. 安装串口驱动,确保可以查看调试信息

5.2 项目构建流程

完成双冗余系统的整体构建流程如下:

  1. FPGA设计修改

    • 添加Flash选择逻辑
    • 更新UDP命令解析模块
    • 添加Flash选择信号输出
  2. FSBL修改

    • 修改Flash访问逻辑支持双区域
    • 添加选择标志检测代码
  3. 系统编译与生成

    • 综合、实现FPGA设计
    • 生成比特流
    • 编译修改后的FSBL
    • 创建BOOT.bin文件
  4. 系统测试与部署

    • 烧写主区域程序
    • 烧写备份区域程序
    • 测试网络命令切换功能

5.3 程序烧写详细步骤

5.3.1 主程序烧写

以下是将主程序烧写到Flash主区域的步骤:

  1. 准备BOOT.bin文件

    • 在SDK中,选择File -> New -> Application Project
    • 选择FSBL和应用程序
    • 右键项目,选择Create Boot Image
    • 选择组件:FSBL、FPGA比特流、应用程序
    • 生成BOOT.bin文件
  2. 烧写主区域

    • 通过JTAG连接开发板
    • 打开Vivado Hardware Manager
    • 右键点击目标设备,选择Add Configuration Memory Device
    • 从列表中选择正确的Flash型号(查看开发板文档确认)
    • 在出现的对话框中设置以下参数:
      • 选择BOOT.bin文件
      • 起始地址设为0x0(默认)
      • 选择"执行擦除"
    • 点击"Program"开始烧写,等待完成

5.3.2 备份程序烧写

备份程序的烧写过程与主程序类似,但关键是要设置正确的偏移地址:

  1. 准备备份BOOT.bin文件

    • 可以使用与主程序相同的BOOT.bin或创建不同版本
    • 对于测试,可以修改应用程序以区分不同版本(如启动信息)
  2. 烧写备份区域

    • 通过JTAG连接开发板
    • 打开Vivado Hardware Manager
    • 右键点击目标设备,选择Add Configuration Memory Device
    • 选择与主程序相同的Flash型号
    • 在对话框中设置:
      • 选择备份版本的BOOT.bin文件
      • 关键步骤: 将起始地址设为0x800000(或您自定义的偏移量)
      • 选择"不执行擦除"选项(避免擦除主程序区域)
    • 点击"Program"开始烧写
  3. 使用命令行工具烧写(可选)

如果Vivado界面无法直接设置偏移地址,可以使用Vivado提供的命令行工具:

# 连接到硬件
open_hw
connect_hw_server
open_hw_target

# 添加Flash设备
create_hw_cfgmem -hw_device [current_hw_device] -mem_dev [lindex [get_cfgmem_parts {mt25qu128-spi-x1_x2_x4}] 0]

# 设置属性
set_property PROGRAM.ADDRESS_RANGE {use_file} [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.FILES [list "path/to/backup/BOOT.bin" ] [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.BLANK_CHECK  0 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.ERASE  0 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.CFG_PROGRAM  1 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]
set_property PROGRAM.VERIFY  1 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]

# 关键属性 - 设置起始偏移地址
set_property PROGRAM.START_ADDRESS 0x00800000 [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]

# 执行烧写
program_hw_cfgmem -hw_cfgmem [ get_property PROGRAM.HW_CFGMEM [current_hw_device]]

5.4 网络命令详解

5.4.1 命令格式详细说明

UDP命令包格式如下:

+-------------------------+------------------------+-----------------+
| 命令识别码 (4字节)       | 保留字段 (4字节)        | 数据 (可选)      |
| 0xF1A5_0000/0xF1A5_0001 | 0x00000000             | ...             |
+-------------------------+------------------------+-----------------+

命令包字段说明:

字段长度格式说明
命令识别码4字节十六进制0xF1A5_0000(主程序)
0xF1A5_0001(备份程序)
保留字段4字节十六进制全0,预留扩展
数据(可选)可变-可选,当前版本未使用

命令需要发送到设备的UDP端口(默认5000)。

5.4.2 发送命令的多种方法

1. 使用Python脚本发送命令

以下是一个完整可用的Python脚本,用于发送Flash选择命令:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flash启动选择命令发送工具
用途: 向目标设备发送UDP命令以切换Flash启动区域
作者: FPGA开发团队
日期: 2023-01-01
"""

import socket
import argparse
import sys
import time

# 命令定义
CMD_SELECT_MAIN = b'\xF1\xA5\x00\x00\x00\x00\x00\x00'  # 选择主程序
CMD_SELECT_BACKUP = b'\xF1\xA5\x00\x01\x00\x00\x00\x00'  # 选择备份程序

def send_flash_command(ip_address, port, is_backup=False, verbose=False):
    """
    发送Flash启动选择命令
    
    参数:
        ip_address: 目标设备IP地址
        port: UDP端口号
        is_backup: True=选择备份程序, False=选择主程序
        verbose: 是否显示详细信息
    
    返回:
        成功返回True,失败返回False
    """
    try:
        # 创建UDP套接字
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        sock.settimeout(2.0)  # 设置2秒超时
        
        # 准备命令数据
        if is_backup:
            command = CMD_SELECT_BACKUP
            cmd_name = "备份程序"
        else:
            command = CMD_SELECT_MAIN
            cmd_name = "主程序"
        
        if verbose:
            print(f"正在发送命令: 选择{cmd_name}")
            print(f"目标设备: {ip_address}:{port}")
            print(f"命令数据: {command.hex()}")
        
        # 发送命令
        sock.sendto(command, (ip_address, port))
        print(f"已发送选择{cmd_name}命令到 {ip_address}:{port}")
        
        # 尝试接收响应(如果有)
        try:
            response, addr = sock.recvfrom(1024)
            print(f"收到响应: {response.hex()} 来自 {addr}")
        except socket.timeout:
            if verbose:
                print("未收到响应(正常情况)")
        
        return True
    
    except Exception as e:
        print(f"错误: {e}")
        return False
    
    finally:
        # 关闭套接字
        sock.close()

def main():
    """命令行入口函数"""
    parser = argparse.ArgumentParser(description='Flash启动选择命令发送工具')
    parser.add_argument('ip', help='目标设备IP地址')
    parser.add_argument('-p', '--port', type=int, default=5000, help='UDP端口号(默认:5000)')
    parser.add_argument('-b', '--backup', action='store_true', help='选择备份程序(默认选择主程序)')
    parser.add_argument('-v', '--verbose', action='store_true', help='显示详细信息')
    parser.add_argument('-r', '--reboot', action='store_true', help='发送命令后重启设备(需要实现)')
    
    args = parser.parse_args()
    
    # 发送命令
    success = send_flash_command(args.ip, args.port, args.backup, args.verbose)
    
    # 返回状态码
    sys.exit(0 if success else 1)

if __name__ == '__main__':
    main()

使用方法:

# 选择主程序
python flash_select.py 192.168.1.100

# 选择备份程序
python flash_select.py 192.168.1.100 -b

# 显示详细信息
python flash_select.py 192.168.1.100 -b -v

2. 使用netcat发送命令

对于Linux/macOS用户,可以使用netcat(nc)工具发送命令:

选择主程序命令:

echo -ne '\xF1\xA5\x00\x00\x00\x00\x00\x00' | nc -u 192.168.1.100 5000

选择备份程序命令:

echo -ne '\xF1\xA5\x00\x01\x00\x00\x00\x00' | nc -u 192.168.1.100 5000

3. 使用Packet Sender工具(GUI方式)

对于不熟悉命令行的用户,可以使用Packet Sender工具:

  1. 下载并安装Packet Sender (https://packetsender.com/)
  2. 创建新的UDP数据包
  3. 设置目标IP和端口(默认5000)
  4. 选择"HEX"输入模式
  5. 输入命令数据:
    • 主程序: F1A50000 00000000
    • 备份程序: F1A50001 00000000
  6. 点击"发送"按钮

5.5 完整切换流程演示

以下是切换从主程序到备份程序的完整操作流程演示:

  1. 确认当前启动状态

    • 通过串口终端连接设备(通常为115200波特率)
    • 观察启动日志中的"Boot Mode"信息
    • 确认当前运行的是哪个区域的程序
  2. 发送切换命令

    • 使用上述任一方法发送选择备份程序的命令
    • 命令: 0xF1A5_0001_0000_0000
    • 目标: 设备IP地址,端口5000(默认)
  3. 重启设备

    • 可通过以下方式重启设备:
      • 硬件复位按钮
      • 通过串口发送复位命令
      • 使用网络复位命令(如果实现)
      • 断电重启
  4. 验证切换结果

    • 观察启动日志,确认启动模式变为"Boot Mode: QSPI Flash Backup Region"
    • 验证系统是否正确加载备份程序
    • 检查应用程序特定标识(如版本号、启动信息等)
  5. 切换回主程序(可选)

    • 发送选择主程序命令: 0xF1A5_0000_0000_0000
    • 重启设备
    • 验证系统已切换回主程序

6. 修改与配置说明

6.1 修改的文件清单与功能说明

本项目实现过程中,修改或添加了以下关键文件。下表详细列出了每个文件的修改内容和功能说明:

文件路径修改内容功能说明
AD9361_Prj.srcs/net_src/01_rtl/udp_rx.sv• 添加命令解析逻辑
• 新增Flash选择信号输出
• 定义命令识别码
UDP命令接收与解析模块,识别特定命令并生成控制信号
AD9361_Prj.srcs/net_src/01_rtl/channel_wrapper.v• 添加Flash控制信号接口
• 连接UDP接收模块和顶层模块
UDP数据包处理与转发模块,为UDP接收模块提供接口
AD9361_Prj.srcs/net_src/01_rtl/udp_look_back.v• 添加Flash选择信号连接
• 引出信号到顶层模块
以太网通信顶层模块,管理以太网数据收发
AD9361_Prj.srcs/sources_1/new/AD9361_prj.v• 添加Flash启动逻辑
• 增加选择信号寄存器
• 添加输出引脚接口
项目顶层模块,连接所有子模块并输出控制信号
AD9361_Prj.srcs/constrs_1/new/AD9361_prj.xdc• 添加Flash控制引脚定义
• 设置引脚电气特性
引脚约束文件,定义FPGA引脚分配和电气特性
AD9361_Prj.sdk/FSBl/src/qspi.h• 添加Flash双区域定义
• 定义寄存器地址和偏移量
QSPI Flash头文件,定义Flash访问相关常量
AD9361_Prj.sdk/FSBl/src/qspi.c• 修改访问函数支持双区域
• 添加区域选择逻辑
QSPI Flash访问实现,根据标志选择加载区域
AD9361_Prj.sdk/FSBl/src/main.c• 添加寄存器映射
• 初始化代码
• 启动信息输出
FSBL主程序,初始化系统并启动后续程序

6.2 关键参数配置详解

实际部署时,需要根据具体硬件环境和应用需求调整以下关键参数:

6.2.1 Flash备份区域偏移量

文件AD9361_Prj.sdk/FSBl/src/qspi.h
参数FLASH_BACKUP_OFFSET
默认值0x800000 (8MB)
说明:此参数定义了备份程序在Flash中的起始位置。应根据以下因素调整:

  1. 实际Flash芯片容量:不同设备的Flash大小不同,需确保偏移量不超过Flash容量
  2. 主程序所需存储空间:主程序完整大小,包括FSBL、比特流和应用程序
  3. 备份程序大小:备份程序可能与主程序大小不同,需确保有足够空间存储

计算方法

  • 确保 FLASH_BACKUP_OFFSET > (主程序大小 + 安全裕量)
  • 主程序大小可以通过查看BOOT.bin文件大小估算
  • 建议添加1-2MB安全裕量

实际选择例子

Flash容量主程序大小建议偏移量备注
16MB4MB0x800000 (8MB)标准分配,各占一半
32MB5MB0x800000 (8MB)标准分配,备份区域较大
16MB7MB0xA00000 (10MB)主程序区域较大
8MB3MB0x400000 (4MB)小容量Flash,各占一半

6.2.2 Flash选择寄存器地址

文件AD9361_Prj.sdk/FSBl/src/qspi.h
参数FLASH_SEL_REG_ADDR
默认值0x43C00000
说明:此地址必须映射到系统中的有效GPIO或自定义寄存器,用于存储Flash选择标志。

配置要点

  1. 在Zynq系统中,此地址通常需要映射到AXI GPIO模块的地址
  2. 地址必须在PS可访问的地址空间内(Zynq-7000通常为0x40000000-0x7FFFFFFF)
  3. 不应与其他外设地址冲突
  4. 需要在PS与PL之间建立正确的地址映射

调整方法

  • 使用Vivado的地址编辑器(Address Editor)查看和分配地址
  • 确认地址与硬件设计中的GPIO基地址一致
  • 更新FSBL代码中的地址定义

6.2.3 UDP命令识别码

文件AD9361_Prj.srcs/net_src/01_rtl/udp_rx.sv
参数

  • CMD_FLASH_SEL_MAIN0xF1A5_0000
  • CMD_FLASH_SEL_BACKUP0xF1A5_0001

说明:这些命令码用于识别Flash启动选择命令。选择时应考虑:

  1. 独特性:与系统中其他命令不冲突
  2. 可识别性:容易辨认的模式(如F1A5作为前缀)
  3. 可扩展性:预留空间用于未来添加更多命令

调整建议

  • 保持命令识别码的前缀相同,仅改变低位值表示不同操作
  • 如有多种命令,建议统一命令格式,便于扩展
  • 命令码可根据项目需求自定义,但修改后需要同步更新命令发送工具

6.2.4 Flash控制引脚

文件AD9361_Prj.srcs/constrs_1/new/AD9361_prj.xdc
引脚

  • flash_sel_pin:G15
  • flash_program_pin:G16

说明:这些引脚用于输出Flash选择状态和编程信号。选择时应考虑:

  1. 引脚可用性:确保引脚未被其他功能占用
  2. 电气特性:符合连接设备的电气要求(电压、电流等)
  3. 布线要求:位置便于PCB布线,避免信号完整性问题
  4. 信号稳定性:考虑上拉/下拉电阻配置,确保信号稳定

调整方法

# 示例:修改为不同引脚
set_property PACKAGE_PIN M15 [get_ports flash_sel_pin]
set_property IOSTANDARD LVCMOS33 [get_ports flash_sel_pin]

set_property PACKAGE_PIN N15 [get_ports flash_program_pin]
set_property IOSTANDARD LVCMOS33 [get_ports flash_program_pin]

# 添加上下拉电阻配置
set_property PULLDOWN true [get_ports flash_sel_pin]

6.3 添加更多功能的扩展方法

基于当前设计,可以进行以下功能扩展:

6.3.1 添加状态反馈功能

为了确认命令是否成功接收,可以添加状态反馈:

  1. udp_rx.sv中添加状态反馈信号:

    output reg [7:0] flash_status;  // 状态反馈
    
  2. 在命令处理逻辑中更新状态:

    if (udp_rx_data[0:31] == CMD_FLASH_SEL_MAIN) begin
        flash_sel <= 1'b0;
        flash_sel_valid <= 1'b1;
        flash_status <= 8'h01;  // 成功接收主程序选择命令
    end
    
  3. 添加UDP响应逻辑,将状态发送回客户端

6.3.2 添加多备份区域支持

扩展设计以支持多个备份区域:

  1. qspi.h中定义多个偏移量:

    #define FLASH_BACKUP1_OFFSET 0x800000  // 第一备份区域
    #define FLASH_BACKUP2_OFFSET 0x1000000 // 第二备份区域
    
  2. 扩展Flash选择标志位宽:

    output reg [1:0] flash_sel = 2'b00;  // 2位以支持多达4个区域
    
  3. 更新命令识别码:

    localparam CMD_FLASH_SEL_MAIN = 32'hF1A5_0000;    // 主程序
    localparam CMD_FLASH_SEL_BACKUP1 = 32'hF1A5_0001; // 备份区域1
    localparam CMD_FLASH_SEL_BACKUP2 = 32'hF1A5_0002; // 备份区域2
    

7. 常见问题与故障排除

7.1 启动问题

问题:系统无法从备份区域启动

现象

  • 命令已成功发送,但系统重启后仍从主区域启动
  • FSBL输出中没有显示"Booting from backup flash region"
  • 系统无法正常加载备份程序

可能原因

  • 备份程序未正确烧写到指定偏移地址
  • FSBL未正确读取Flash选择标志
  • Flash选择信号未正确连接
  • 命令接收模块未正确工作

排查步骤

  1. 验证备份程序烧写

    • 使用Flash读取工具确认备份程序已正确烧写
    # Vivado TCL命令读取Flash内容
    read_cfgmem -format bin -interface SPIx4 -size 32 -file backup_verify.bin -start_address 0x800000 -end_address 0x900000
    
    • 确认读取的数据与BOOT.bin文件匹配
  2. 检查FSBL日志

    • 启用FSBL详细调试输出:在fsbl_debug.h中设置FSBL_DEBUG_INFO=1
    • 通过串口监视启动过程,查看读取的选择标志值
    • 检查是否正确读取到GPIO状态
  3. 检查硬件信号

    • 使用示波器或逻辑分析仪检查flash_sel_pin引脚信号
    • 确认信号在命令处理后正确变化,且保持稳定
    • 检查信号从FPGA到GPIO接口的连接
  4. 测试命令接收

    • 添加调试LED指示命令接收状态
    • 使用ILA (Integrated Logic Analyzer)监控UDP接收逻辑
    • 验证UDP数据包是否被正确接收和解析

解决方案

  1. 手动验证启动区域选择

    // 在qspi.c中临时强制使用备份区域
    u32 QspiAccess(u32 SourceAddress, u32 DestinationAddress, u32 LengthBytes)
    {
        // 强制使用备份区域进行测试
        u32 FlashSel = FLASH_SEL_BACKUP;  // 强制选择备份区域
        // 其余代码不变...
    }
    
  2. 验证寄存器映射

    • 在FSBL初始化阶段添加测试代码:
    // 测试写入和读取
    Xil_Out32(FLASH_SEL_REG_ADDR, FLASH_SEL_BACKUP);
    u32 RegVal = Xil_In32(FLASH_SEL_REG_ADDR);
    fsbl_printf(DEBUG_GENERAL, "寄存器测试: 写入=%d, 读取=%d\n", 
                FLASH_SEL_BACKUP, RegVal);
    
  3. 修复偏移量计算

    • 确保SrcAddr计算正确:
    // 正确的偏移量计算
    if (FlashSel == FLASH_SEL_BACKUP) {
        SrcAddr = SourceAddress + FLASH_BACKUP_OFFSET;
        fsbl_printf(DEBUG_INFO, "计算后的地址: 0x%08x\n", SrcAddr);
    }
    

问题:切换命令没有效果

现象

  • 发送UDP命令后,没有任何响应或状态变化
  • 系统重启后启动区域未改变
  • 没有错误消息或异常现象

可能原因

  • UDP命令格式错误
  • 目标IP地址或端口错误
  • 网络连接问题
  • 命令解析逻辑错误
  • 防火墙或网络设备阻止UDP包

排查工具

  • Wireshark: 网络数据包分析
  • netcat/nmap: 网络连接测试
  • LED指示灯: 硬件状态显示
  • ILA: FPGA内部逻辑分析

排查步骤

  1. 验证网络连接

    # 测试网络连通性
    ping 192.168.1.100
    
    # 测试UDP端口可达性
    nmap -sU -p 5000 192.168.1.100
    
  2. 使用Wireshark捕获数据包

    • 监听设备网口,筛选UDP协议和目标端口
    • 确认命令已发送并到达设备
    • 检查数据包内容是否正确
  3. 添加硬件调试接口

    • 配置LED显示命令接收状态
    // 在顶层模块中添加
    assign debug_led[0] = flash_sel;         // 显示当前选择状态
    assign debug_led[1] = flash_sel_valid;   // 命令有效指示
    
  4. 实现简单回显测试

    • 在UDP处理模块中添加回显功能,接收到命令后将内容发送回发送方
    • 验证UDP接收和处理功能正常工作

解决方案

  1. 修复命令格式

    • 确保命令字节顺序正确(注意大小端顺序)
    • 使用十六进制编辑器构建正确的UDP数据包
  2. 网络设置调整

    • 检查设备IP配置和子网设置
    • 关闭防火墙或添加例外规则
    • 尝试在同一子网中测试
  3. 简化测试

    • 临时修改命令解析逻辑,识别任何UDP包
    // 简化测试 - 任何UDP包都触发状态变化
    if (udp_rx_cnt > 0) begin
        flash_sel <= ~flash_sel;  // 切换状态
        flash_sel_valid <= 1'b1;
    end
    

7.2 Flash操作问题

问题:Flash编程失败

现象

  • Vivado报错:Programming failed
  • 验证失败,数据不匹配
  • 程序无法烧写到指定地址

可能原因

  • Flash保护机制启用
  • Flash写入时序不正确
  • 编程工具与Flash型号不匹配
  • Flash硬件连接问题
  • Flash已损坏或老化

排查步骤

  1. 检查Flash状态和保护

    • 读取Flash状态寄存器查看保护状态
    # Vivado TCL命令读取Flash状态
    create_hw_cfgmem -hw_device [current_hw_device] -mem_dev [get_cfgmem_parts {mt25qu128-spi-x1_x2_x4}]
    get_property STATUS [get_hw_cfgmem]
    
    • 查看Flash制造商ID和设备ID,确认型号正确
  2. 尝试不同的编程设置

    • 降低编程速度
    • 修改编程算法
    • 尝试不同的编程模式(SPIx1/SPIx4等)
  3. 硬件检查

    • 测量Flash供电电压是否正常(通常3.3V)
    • 检查JTAG信号质量和连接稳定性
    • 测试Flash引脚连接(CS/CLK/DI/DO等)

解决方案

  1. 解除Flash保护

    # 解除写保护的Vivado TCL命令示例
    create_hw_cfgmem -hw_device [current_hw_device] -mem_dev [get_cfgmem_parts {mt25qu128-spi-x1_x2_x4}]
    set_property PROGRAM.ERASE 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]]
    set_property PROGRAM.CFG_PROGRAM 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]]
    set_property PROGRAM.VERIFY 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]]
    create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]]
    program_hw_devices [current_hw_device]
    
  2. 使用分步编程

    • 先执行擦除操作
    • 确认擦除成功后再执行编程
    • 最后执行验证
  3. 使用替代工具

    • 尝试使用XSPI Flash工具
    • 使用第三方Flash编程器
    • 通过FSBL或U-Boot的Flash实用程序编程

问题:程序在备份区域无法正常运行

现象

  • 系统成功从备份区域启动,但程序行为异常
  • 出现意外的重启或死机
  • 特定功能无法正常工作

可能原因

  • 备份程序编译时未考虑偏移地址
  • 程序中硬编码了Flash地址
  • 备份区域Flash质量问题
  • 程序依赖特定内存地址或硬件配置

排查步骤

  1. 检查程序编译

    • 确认程序是否使用位置无关代码(PIC)
    • 检查链接脚本中的地址分配
    • 确保程序不直接访问绝对地址
  2. Flash内容验证

    • 读取备份区域内容并与原始BOOT.bin比较
    • 检查是否有数据损坏或不完整
  3. 添加调试信息

    • 在应用程序中添加启动阶段日志,显示当前运行的区域
    • 记录关键操作的执行情况和参数

解决方案

  1. 修复位置依赖

    • 修改应用程序,使用相对地址而非绝对地址
    • 更新链接脚本,适应不同的加载地址
    // 使用相对地址访问
    volatile uint32_t *base_addr = (volatile uint32_t *)(获取当前基地址());
    
  2. 重新烧写备份程序

    • 确保使用正确编译的位置无关程序
    • 校验烧写过程和结果
  3. 添加运行时检测

    • 在程序中添加代码检测当前运行区域
    // 检测当前运行区域
    bool is_running_from_backup = (CURRENT_EXECUTION_ADDR >= BACKUP_REGION_START);
    printf("当前运行区域: %s\n", is_running_from_backup ? "备份" : "主要");
    

7.3 硬件连接问题

问题:Flash选择信号不稳定

现象

  • 选择状态随机变化
  • 系统有时从主区域启动,有时从备份区域启动
  • 命令处理行为不一致

可能原因

  • 信号未正确上拉/下拉
  • 引脚浮空
  • 信号干扰
  • 时钟域问题
  • 系统掉电时状态丢失

排查步骤

  1. 信号稳定性测试

    • 使用示波器测量信号电平和噪声
    • 观察信号在不同操作条件下的稳定性
    • 测试温度变化、电源波动等情况下的信号质量
  2. 检查上拉/下拉配置

    • 验证引脚的上拉/下拉配置是否正确
    • 检查外部电阻值是否合适
  3. 时钟域分析

    • 检查信号跨时钟域传输是否正确处理
    • 使用ILA观察同步逻辑行为

解决方案

  1. 改进信号稳定性

    • 添加适当的上拉/下拉电阻
    # 在约束文件中添加
    set_property PULLDOWN true [get_ports flash_sel_pin]
    
    • 增加去耦电容减少电源噪声
    • 优化PCB布线,减少干扰
  2. 使用寄存器存储状态

    • 实现非易失性存储(例如使用EEPROM)
    • 使用带电池的RTC保持状态
    • 冗余存储选择状态,采用多数表决
  3. 实现健壮的同步逻辑

    // 双触发器同步器
    reg flash_sel_meta;
    reg flash_sel_sync;
    
    always @(posedge clk) begin
      if (!rst_n) begin
        flash_sel_meta <= 1'b0;
        flash_sel_sync <= 1'b0;
      end else begin
        flash_sel_meta <= flash_sel_async;
        flash_sel_sync <= flash_sel_meta;
      end
    end
    

8. 技术术语表

术语全称说明
QSPIQuad Serial Peripheral Interface四线串行外设接口,一种高速Flash接口协议,比标准SPI提供更高的数据传输速率
FSBLFirst Stage Boot Loader第一阶段引导加载程序,Zynq系统中负责初始化硬件并加载后续程序
UDPUser Datagram Protocol用户数据报协议,一种无连接的网络传输协议,无需建立连接即可发送数据包
Zynq-Xilinx公司的SoC(System-on-Chip)芯片系列,集成ARM处理器和FPGA
PSProcessing SystemZynq中的处理系统部分,基于ARM Cortex-A9处理器核心
PLProgrammable Logic可编程逻辑部分,即FPGA部分,用于实现自定义硬件功能
FlashFlash Memory闪存,一种非易失性存储器,用于存储启动程序和配置数据
冗余Redundancy系统设计中的备份机制,用于提高可靠性和容错能力
比特流BitstreamFPGA配置文件,包含PL部分的硬件配置信息
AD9361-Analog Devices公司的高性能射频收发器芯片,用于无线通信应用
GPIOGeneral Purpose Input/Output通用输入/输出端口,可编程控制的数字信号接口
BOOT.binBoot BinaryZynq启动文件,包含FSBL、比特流和应用程序
ILAIntegrated Logic Analyzer集成逻辑分析仪,Vivado中用于调试FPGA内部信号的工具
AXIAdvanced eXtensible Interface先进可扩展接口,ARM定义的高性能总线协议
TCLTool Command Language工具命令语言,Vivado中用于自动化操作的脚本语言

9. 附录:项目文件结构

完整的项目文件结构及主要文件说明:

AD9361_Prj/
├── AD9361_Prj.srcs/                     # 源代码目录
│   ├── constrs_1/                       # 约束文件目录
│   │   └── new/
│   │       └── AD9361_prj.xdc           # 引脚约束文件
│   │
│   ├── net_src/                         # 网络相关源代码
│   │   └── 01_rtl/
│   │       ├── udp_look_back.v          # UDP顶层模块
│   │       ├── channel_wrapper.v        # 通道包装模块
│   │       ├── udp_rx.sv                # UDP接收模块(包含命令解析)
│   │       └── udp_tx.v                 # UDP发送模块
│   │
│   └── sources_1/                       # 主源代码目录
│       └── new/
│           └── AD9361_prj.v             # 顶层设计文件
│
├── AD9361_Prj.sdk/                      # SDK工程目录
│   ├── FSBl/                            # FSBL项目
│   │   └── src/
│   │       ├── qspi.h                   # QSPI头文件(添加Flash区域定义)
│   │       ├── qspi.c                   # QSPI实现(修改Flash访问逻辑)
│   │       ├── main.c                   # FSBL主程序(初始化和启动)
│   │       └── ... (其他FSBL文件)
│   │
│   ├── AD9361_prj/                      # 应用程序项目
│   │   └── src/
│   │       └── ... (应用程序源代码)
│   │
│   └── AD9361_prj_bsp/                  # 板级支持包
│
├── AD9361_Prj.xpr                       # Vivado项目文件
├── 双冗余.md                            # 本说明文档
└── tools/                               # 辅助工具(可选)
    ├── flash_select.py                  # Flash选择命令发送工具
    └── README.md                        # 工具使用说明

9.1 关键文件详细说明

9.1.1 顶层设计文件(AD9361_prj.v)

顶层设计文件集成了所有系统组件,包括PS接口、网络模块和Flash控制逻辑。该文件是理解系统整体结构的入口点。

9.1.2 UDP接收模块(udp_rx.sv)

UDP接收模块负责解析网络命令,是实现远程控制功能的核心。该模块定义了命令格式并生成Flash选择信号。

9.1.3 QSPI访问函数(qspi.c)

QSPI访问函数实现了从Flash不同区域读取数据的逻辑,是双冗余启动的关键部分。该函数根据选择标志决定从哪个区域加载程序。

9.1.4 FSBL主程序(main.c)

FSBL主程序负责系统初始化和启动流程,包括读取Flash选择标志和显示启动信息。了解这个文件有助于掌握系统启动过程。

9.2 使用示例脚本

为便于测试和使用,可以在tools目录下创建以下示例脚本:

9.2.1 Flash选择命令发送工具(flash_select.py)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Flash启动选择命令发送工具
"""
import socket
import argparse
import sys

def send_flash_command(ip_address, port, is_backup=False, verbose=False):
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        if is_backup:
            command = b'\xF1\xA5\x00\x01\x00\x00\x00\x00'
            cmd_name = "备份程序"
        else:
            command = b'\xF1\xA5\x00\x00\x00\x00\x00\x00'
            cmd_name = "主程序"
        
        sock.sendto(command, (ip_address, port))
        print(f"已发送选择{cmd_name}命令到 {ip_address}:{port}")
        
        return True
    except Exception as e:
        print(f"错误: {e}")
        return False
    finally:
        sock.close()

def main():
    parser = argparse.ArgumentParser(description='Flash启动选择命令发送工具')
    parser.add_argument('ip', help='目标设备IP地址')
    parser.add_argument('-p', '--port', type=int, default=5000, help='UDP端口号(默认:5000)')
    parser.add_argument('-b', '--backup', action='store_true', help='选择备份程序(默认选择主程序)')
    parser.add_argument('-v', '--verbose', action='store_true', help='显示详细信息')
    
    args = parser.parse_args()
    success = send_flash_command(args.ip, args.port, args.backup, args.verbose)
    sys.exit(0 if success else 1)

if __name__ == '__main__':
    main()

使用方法:

# 选择主程序
python flash_select.py 192.168.1.100

# 选择备份程序
python flash_select.py 192.168.1.100 -b

# 显示详细信息
python flash_select.py 192.168.1.100 -b -v

2. 使用netcat发送命令

对于Linux/macOS用户,可以使用netcat(nc)工具发送命令:

选择主程序命令:

echo -ne '\xF1\xA5\x00\x00\x00\x00\x00\x00' | nc -u 192.168.1.100 5000

选择备份程序命令:

echo -ne '\xF1\xA5\x00\x01\x00\x00\x00\x00' | nc -u 192.168.1.100 5000

3. 使用Packet Sender工具(GUI方式)

对于不熟悉命令行的用户,可以使用Packet Sender工具:

  1. 下载并安装Packet Sender (https://packetsender.com/)
  2. 创建新的UDP数据包
  3. 设置目标IP和端口(默认5000)
  4. 选择"HEX"输入模式
  5. 输入命令数据:
    • 主程序: F1A50000 00000000
    • 备份程序: F1A50001 00000000
  6. 点击"发送"按钮

9.3 相关资源链接

闲来无事,记录琐事