简介针对 Zynq 7020 平台搭配复旦微 FM25F32 闪存的烧写配置资源面向在 Vivado 开发套件中无法直接支持国产 Nor Flash 芯片的嵌入式工程师。该问题源于官方工具没有收录复旦微器件型号自带烧写工具无法识别作者改用自编测试程序完成引导程序与启动镜像的烧写并实际验证上电启动成功为国产化存储替代提供了可复现的完整工程方案。压缩包共 438 个文件大小约 2.27MB内容以 C 源程序与头文件分别 151 个与 165 个、编译生成的中间目标文件58 个以及 makefile 构建脚本为主同时包含生成好的二进制启动镜像、说明文档及 SDK 工程描述文件目录组织清晰方便直接查看源码、核对编译选项与重演烧写流程。资源不仅给出可用的烧写程序还展现出 QSPI 驱动初始化、Flash 擦除写入、FSBL 生成和启动验证等关键环节的处理思路。目前已有 2479 人学习下载适合对 Zynq 启动流程、国产 Flash 适配或裸机开发有实战需求的软硬件工程师。1. 为什么 xc7z020 的 QSPI 烧写要单独写配置程序从一次启动黑屏说起调试基于 Zynq-7000 的板子时SD 卡启动一切正常切到 QSPI 启动后上电跑不起来串口卡在 FSBL 阶段。查了半天发现板子上的 Flash 是某国产厂商的 FM25F32而 Xilinx 的默认 FSBL 根本不认这颗芯片的 JEDEC ID——烧写程序一报错启动镜像根本没进 QSPI。这就是为什么 xc7z020 搭配 FM25F32 时需要一份专门针对这颗 Flash 的烧写配置程序。它能绕过默认 Flash 列表的限制直接通过 QSPI 控制器发送命令完成擦除、编程、校验让 BOOT.bin 真正躺在 QSPI 里。适合做国产化替换、量产调试或硬件验证的工程师。2. FM25F32 与 Zynq QSPI 控制器适配逻辑和硬件选型要点2.1 FM25F32 的关键参数与兼容性判断FM25F32 是一颗 32Mbit 的串行 NOR Flash采用标准 SPI/Dual SPI/Quad SPI 接口供电 3.3V页编程大小 256 字节扇区擦除 4KB块擦除 64KB。这些基本规格与常见的 W25Q32 几乎一致所以很多工程师想当然地认为可以直接用 Xilinx 的 Flash 模板替代。但实际经验是国产 Flash 的 JEDEC ID、状态寄存器位定义、甚至部分命令码可能存在差异。Xilinx FSBL 在启动时会读取 Flash ID 并与内部表比对不匹配就直接停在读取阶段导致上电启动失败。参数FM25F32常见替代 W25Q32容量32Mbit (4MB)32Mbit页大小256B256B扇区擦除4KB4KBJEDEC ID需确认0xEF4016状态寄存器位定义可能与标准有差异标准所以在做适配时不要只看容量和封装先通过 QSPI 控制器读一次 JEDEC ID把返回值记录下来这是后面所有适配的基础。2.2 Zynq QSPI 控制器与 FM25F32 的硬件连接Zynq-7000 的 QSPI 控制器是硬核 IP挂载在 APU 外设总线上支持最多两片 QSPI Flash。常见的 xc7z020 板级连接方式是把 QSPI 引脚分配到 MIO 1 到 MIO 6对应时钟、片选、DI、DO、WP、HOLD。硬件上需要注意以下几点。第一FM25F32 的 HOLD 引脚和 WP 引脚默认要拉高否则片选有效时 Flash 可能进入 HOLD 或写保护状态导致烧写时命令不响应。第二QSPI 数据线 D0/D1 在 SPI 模式下是单向的但升级到 Quad 模式时 D2/D3 也要正确连接且上电默认是标准 SPI 模式启动阶段不会自动切到 Quad。第三时钟线 MIO 2 上串联电阻阻抗匹配如果板子走线较长建议把 QSPI 时钟分频值调低避免信号过冲。用硬件管理器读不到 ID 时先用万用表量 MIO 1、MIO 2 的电压片选在空闲时应该是高电平如果量到低电平说明 MIO 复用没配好或芯片损坏。2.3 为什么默认烧写工具不行必须自定义配置程序Vivado/SDK 里自带的program_flash工具支持一部分 Flash但它的支持列表主要是主流厂商型号。FM25F32 这类国产芯片经常不在列表里直接调用会收到Unrecognized Flash ID的报错。即便你强行指定-flash_type为某个相近型号也可能因为状态寄存器轮询机制不同出现擦除超时、写使能不成功等玄学问题。自定义烧写配置程序的本质是把 QSPI 控制器初始化和 Flash 操作命令全部接管过来。不依赖 FSBL 的 Flash 表而是自己定义 ID 匹配规则和命令序列。这样做的好处是烧写环节可控排错路径明确量产时也能通过命令行参数切换 Flash 型号。我一般会把这套程序独立成一个工程不混在应用代码里方便在调试时单独下载到 DDR 运行。3. 搭建烧写环境从 FSBL 生成到 BOOT.bin 分区表3.1 用 Vivado 生成 FSBL 的常规路径在 SDK 里创建 FSBL 工程时选择Zynq FSBL模板BSP 设置中需要把qspi驱动勾选上。默认 FSBL 会编译出一个在 SD 卡或 JTAG 下加载的启动引导程序但它在读取 QSPI 之前会校验 Flash ID。我们要做的事情就是在 BSP 的xqspipsu_g.c或者 Flash 驱动层加入对 FM25F32 ID 的信任。常见做法是修改XQspiPsu_FlashId结构体数组把 FM25F32 的 JEDEC ID 添加进去。如果不改 FSBL那么烧写程序本身也可以绕过这一步因为烧写程序就是用来向 Flash 里写入镜像的它并不需要 FSBL 先能识别。所以在调试阶段可以先不修改 FSBL而是通过 JTAG 把烧写程序加载到 DDR 里执行由烧写程序完成 QSPI 的擦写。3.2 BOOT.bin 生成与分区表设置BOOT.bin 是 Zynq 上电后从 QSPI 读取的启动镜像它包含 FSBL、FPGA bitstream、应用程序或者裸机程序。使用 Bootgen 工具生成时重点在于-flash_type和地址分配。常见命令如下bootgen -image boot.bif -o BOOT.bin -w on -flash_type qspi-x4-single其中boot.bif内容大致如下the_ROM_image: { [bootloader] fsbl.elf bitstream.bit app.elf [offset0x200000] app2.bin }重点说明两个参数qspi-x4-single表示按单 QSPI Flash四线数据模式生成镜像头[offset0x200000]指定第二个镜像在 Flash 中的偏移地址。Zynq 启动时 BootROM 只会自动加载 FSBL 到 OCMFSBL 再根据分区表读取后续内容。所以分区表偏移必须与烧写时实际写入地址一致否则启动到一半就飞了。3.3 烧写配置程序的工程结构一套完整的烧写配置程序我一般会按下面分层组织模块职责qspi_init.c初始化 MIO、QSPI 控制器时钟和模式flash_cmd.c封装 Flash 命令读ID、写使能、擦除、编程flash_ops.c高层操作扇区擦除、页编程、整片校验bootimg.c解析 BOOT.bin 头计算写入地址和长度main.c命令行解析、进度打印、错误处理这里的关键是 flash_cmd 层要做到和具体型号解耦。FM25F32 和 W25Q32 大部分命令一样但万一有差异只需要改 flash_cmd.c 里的宏定义而不影响上层校验逻辑。4. 烧写配置程序核心实现命令序列、状态机与关键参数4.1 FM25F32 的基础命令集QSPI Flash 的烧写本质是往寄存器里写命令。FM25F32 常用的命令码如下表命令十六进制码说明Read ID0x9F读取 3 字节 JEDEC IDWrite Enable0x06必须擦除/编程前发Read Status Register0x05等待 BIT0 变为 0Sector Erase0x204KB 扇区擦除Page Program0x02按页写入最多 256BRead Data0x03普通读数据Write Disable0x04保护状态锁定这些命令和标准 SPI NOR 兼容但要注意FM25F32 可能有自己的状态寄存器位表示忙状态轮询时读取 0x05 的 BIT0 即可如果一直为 1说明 Flash 内部烧录电压异常或芯片损坏。4.2 烧写状态机设计擦除-编程-校验在编写程序时我用一个状态机把烧写流程拆成四步初始化、擦除、编程、校验。擦除阶段按扇区遍历编程阶段按页遍历。下面是简化后的核心代码// 等待 Flash 空闲 static int qspi_wait_busy(void) { uint32_t timeout 0xFFFFFF; while ((qspi_read_status() 0x01) timeout--) ; return timeout 0 ? 0 : -1; } // 扇区擦除每次擦除一个 4KB 扇区 static int qspi_erase_sector(uint32_t addr) { qspi_send_cmd(WRITE_ENABLE); // 先发写使能 qspi_set_address(addr); // 设置扇区地址 qspi_send_cmd(SECTOR_ERASE); // 发 0x20 return qspi_wait_busy(); // 等待操作完成 } // 页编程每次最多写 256B static int qspi_page_program(uint32_t addr, uint8_t *buf, int len) { qspi_send_cmd(WRITE_ENABLE); qspi_set_address(addr); qspi_send_cmd(PAGE_PROGRAM); for (int i 0; i len; i) qspi_send_byte(buf[i]); // 逐字节发送 return qspi_wait_busy(); }逻辑说明擦除和编程前必须先发送写使能否则 Flash 会忽略后续命令这是最常见的翻车原因。qspi_wait_busy()通过查询状态寄存器 BIT0 判断内部操作是否结束循环里加一个超时保护避免 Flash 异常时程序卡死。参数方面addr必须是扇区边界对齐len最大 256 字节跨页写入时要拆成多次页编程否则数据会回卷到页开头覆盖旧内容。4.3 关键参数地址映射、超时与时钟分频烧写程序的鲁棒性取决于几个参数。第一Flash 地址按字节寻址FM25F32 容量 4MB所以地址宽度 24 位足够。第二擦除等待时间建议设置 30ms 以上超时实测有些国产 Flash 在电压偏低时擦除时间会拉到 50ms。第三QSPI 控制器时钟分频Zynq QSPI 参考时钟是 200MHz如果 Flash 最高支持 100MHz分频值设为 2如果走线质量差直接分频到 4运行在 50MHz 更安全。写一段时钟初始化的示意代码// QSPI 参考时钟 200MHz目标 50MHz XQspiPsu_SetClkPrescaler(QspiInstance, 4);这里的 4 表示把参考时钟除以 4得到 50MHz。烧写时时钟高低直接决定成功率量产测试中我发现 50MHz 比 100MHz 在国产 Flash 上可靠得多后者的写入校验偶发翻车。5. 避坑指南FM25F32 烧写常见问题与排查方法5.1 问题一读不到 JEDEC ID返回全是 0xFF现象调用读 ID 命令后返回三个字节都是 FF。原因MIO 引脚复用没配好或者片选没有被正确拉低。也可能是 Flash 电源没有上电检查电源轨电压。解决先检查 Vivado 里的 MIO 设置确认 QSPI 的 MIO 1-6 被分配出来再用示波器量片选信号在命令发送期间应该有低电平脉冲。如果芯片之前被误设置为 QPI 模式发送 0x66 后跟 0x99 可以复位到标准 SPI 模式。5.2 问题二擦除成功但写入后读回来数据错误现象校验时读出数据与源镜像不一致往往某一段全是 0x00 或 0xFF。原因页编程跨越了 256 字节页边界或者写使能没有在每页编程前单独发送。解决严格按页边界拆分写入每写入一页就重新发送 0x06 写使能。还可以在编程前循环调用读状态寄存器确保上次操作完成再启动下一次。5.3 问题三烧写后上电启动依然失败现象BOOT.bin 已经烧写成功校验也通过但切换 QSPI 启动模式后串口无输出。原因启动模式引脚 MIO[7:5] 没有拨到 QSPI 位置或者 FSBL 已经执行但读取 Flash 失败。解决先确认启动模式引脚电平对照 Zynq TRM 检查电平组合。如果引脚没问题用 JTAG 连上后看 FSBL 打印信息多半是 FSBL 的 Flash ID 匹配问题按照上一章的方法修改 BSP。5.4 问题四高时钟下烧写或读取不稳定现象烧写时进度条卡住或者校验失败提示地址超时。原因时钟频率过高信号劣化导致误码。解决把 QSPI 时钟分频值从 2 改到 4或根据 Flash 手册降速。这个方法在 PCB 走线超过 5cm 的板子上特别有效。我一般量产时统一使用 50MHz保证不同批次板卡的兼容性。6. 进阶把烧写配置程序扩展成量产烧写工具6.1 从裸机程序到命令行烧写工具如果只是调试一颗板子用 JTAG 加载烧写程序就够了。但量产时需要用命令行走一次完整烧写方便自动化脚本调用。我的做法是在 main.c 里用命令行参数指定镜像路径、烧写地址、是否校验输出一段机器可读的日志。例如qspi_program.exe --image BOOT.bin --addr 0x0 --verify程序解析参数后通过 JTAG 或 UART 和板卡通信完事返回 0 或错误码。6.2 烧写时写入序列号和 MAC 地址量产烧写还有一个常见需求在烧写系统镜像的同时把每台设备的唯一序列号写入 Flash 的某个固定地址。烧写程序可以在校验完镜像后单独往地址 0x3F0000 写入 4KB 的配置块。这里要注意写入序列号前必须先擦除该扇区否则新旧数据会交错。写入后回读校验防止序列号重复。6.3 校验策略全量比对还是 CRC 摘要小容量 Flash 全量比对也就 4MB时间成本不高所以量产时我坚持逐字节比对。如果追求速度可以把镜像预计算 CRC32烧写完成后读回整片算 CRC。但注意 CRC 只能发现错误不能定位错误地址所以调试阶段还是全量比对更直观。这套烧写配置程序我用在实际项目上已有大半年从单板调试到小批量生产它解决了默认工具不识别国产 Flash 的死结。后来我每次拿到一款新板子都会先跑一遍读 ID再决定要不要在 FSBL 里加 Flash 型号。从那以后QSPI 启动不稳定这件事几乎再没找上门过。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?