☰
Zynq-7000 QSPI适配FM25F32:定制烧写配置程序指南
2026/10/15 1:34:37 网站建设 项目流程

简介:针对 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
扇区擦除4KB4KB
JEDEC 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 [offset=0x200000] app2.bin }

重点说明两个参数:qspi-x4-single表示按单 QSPI Flash,四线数据模式生成镜像头;[offset=0x200000]指定第二个镜像在 Flash 中的偏移地址。Zynq 启动时 BootROM 只会自动加载 FSBL 到 OCM,FSBL 再根据分区表读取后续内容。所以分区表偏移必须与烧写时实际写入地址一致,否则启动到一半就飞了。

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 ID
Write Enable0x06必须擦除/编程前发
Read Status Register0x05等待 BIT0 变为 0
Sector Erase0x204KB 扇区擦除
Page Program0x02按页写入,最多 256B
Read 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 启动不稳定这件事几乎再没找上门过。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询