
简介针对Xilinx公司Zynq-7000系列现场可编程门阵列在国产化替代中开发环境无法识别复旦微电子Nor型QSPI闪存FM25F32的问题这套基于XC7Z020器件的验证工程给出了完整解决思路。作者利用自编烧写测试程序绕过开发套件自带工具的器件限制成功将二级引导加载程序与U-Boot写入闪存并完成启动加载验证。压缩包共四百三十八个文件以一百五十一个C源码文件和一百六十五个头文件为主体辅以编译脚本、链接脚本、汇编文件及BOOT.bin等镜像文件整体大小仅二点二七兆字节结构清晰便于按目录查阅。其中涉及QSPI驱动、缓存管理、以太网控制器等示例代码可深入理解闪存初始化、扇区擦除、写入状态轮询等底层实现。目前已有两千四百七十三人学习使用适合从事Zynq国产化存储与启动方案开发的硬件工程师参考。 做Zynq开发的人应该都有体会Vivado里跑通逻辑、写好PS端程序只是第一步真正让板卡“上电就能跑”的烧写配置环节才是最容易卡壳的地方。尤其当手里拿的不是常规的W25Q系列而是国产复旦微的FM25F32时很多现成的教程和脚本就不灵了——Flash ID对不上、擦除时序不兼容、启动模式配置错误任何一个细节出问题都会让你怀疑人生。这篇文章打算完整梳理一遍“Zynq xc7z020 复旦微FM25F32”这套组合的烧写配置全过程。从器件选型思路、硬件连接要点到三条不同的烧写路径SDK、U-Boot、Vivado Hardware Manager怎么选怎么用再到我实际调试中踩过的几个坑一次性讲清楚。内容面向正在做Zynq平台开发、尤其是需要国产化Flash替代方案的硬件和嵌入式工程师也适合刚接触Zynq启动镜像制作的朋友作为实操参考。1. 项目背景与整体思路拆解1.1 为什么选用QSPI Flash作为启动介质Zynq xc7z020支持从多种介质启动包括SD卡、QSPI Flash、NOR Flash、NAND Flash等。其中QSPI Flash是兼顾成本、性能和可靠性的均衡选择单颗芯片体积小、引脚少、不需要额外的控制器PS端的QSPI控制器直接挂在APB总线上读速度够用而且不像SD卡那样存在文件系统损坏的风险也不像NAND那样需要坏块管理和ECC。对于裸机程序或者轻量级Linux系统来说QSPI Flash完全够用。FM25F32的容量是32Mbit4MB如果只放BOOT.bin——也就是FSBL bitstream 应用elf的打包体——空间绰绰有余。即使要放一个小型内核和设备树4MB也足够支撑一个精简的启动方案。当然如果你打算在QSPI上挂一个完整的根文件系统这个容量就捉襟见肘了这是后面选型时需要提前想清楚的问题。1.2 复旦微FM25F32与Zynq xc7z020的搭配思路复旦微FM25F32是国产QSPI NOR Flash封装和引脚定义上对标市面上主流的W25Q32系列指令集也基本兼容——这意味着硬件上可以直接替换PCB不用改。但在软件层面就不能简单“拿来主义”了Flash的JEDEC ID不同时序参数也有差异如果直接套用针对W25Q32编写的初始化代码或U-Boot驱动可能在识别阶段就直接报错。我这次的板卡设计初衷很明确处理器用Xilinx Zynq xc7z020国产化板卡上这颗料很常见存储介质选用复旦微FM25F32目标是实现上电后从QSPI Flash自动加载FPGA配置并启动PS端程序。整套方案的难点不在于某一个环节而在于把Zynq的BootROM引导流程、FSBL对QSPI的初始化、以及复旦微Flash的时序特性三者对齐——这三个环节任何一处不匹配启动就会静默失败。2. 硬件连接与启动模式配置2.1 启动模式引脚设置Zynq上电后BootROM会读取MIO[6:2]的电平状态来决定从哪个介质引导。从QSPI Flash启动需要把这几个引脚配置成对应的电平组合具体编码在UG865中有详细表格我这里只强调几个实操中容易忽略的细节。首先是MIO的上下拉电阻。很多开发板为了兼容多种启动方式会在MIO[6:2]上预留0欧电阻的焊接位你在量产板卡上必须确认这些电阻的实际焊接状态和启动模式要求一致否则就会出现“明明BootROM在跑却怎么也进不了QSPI引导流程”的诡异现象。其次是MIO[6:2]和QSPI用到的MIO[1:0]、MIO[3:2]在部分模式下会有功能复用务必确认PS端的MIO分配没有冲突。我习惯在硬件投板前就画一张启动模式配置检查表启动介质、MIO[6:2]电平值、对应模式编码、板上实际电阻状态逐项打勾确认。这个习惯帮我挡掉过至少两次量产阶段的低级错误——焊接工人把0欧电阻贴错位置导致整批板卡启动不了排查起来非常痛苦。2.2 FM25F32关键电气参数与连接要点FM25F32供电电压为2.7V~3.6V和Zynq PS端的MIO Bank电压通常是3.3V可以直连。QSPI接口的信号线包括C时钟、D0~D3数据、CS_N片选和WR_N/VIO等其中数据线在Quad模式下是双向的Zynq QSPI控制器会统一管理方向和时序硬件上不需要额外加方向切换逻辑。实际布线时注意几点时钟线C尽量短保持阻抗连续四根数据线长度尽量等长CS_N到Flash的距离不要太远否则在高速读模式下可能出现片选不够快导致误触发电源引脚旁边放一个100nF去耦电容位置越靠近Flash的VCC脚越好。这些虽然是PCB设计的基本功但在高速QSPI读取时布局不当会直接影响时序裕量我在测试中就遇到过因为D0线走线过长导致4-bit模式读数据偶发错误的情况。2.3 QSPI Flash容量与地址映射Zynq QSPI控制器支持单颗Flash也支持两片Flash堆叠Stacked或并行Parallel模式。FM25F32是单颗4MB容量工作在Single模式时地址范围是0xFC000000~0xFC3FFFFF这个地址映射关系在FSBL和U-Boot中都会被用到。如果你后续要换用更大容量的Flash比如16MB甚至32MB需要注意两点一是Zynq的线性地址映射空间是否足够覆盖完整容量二是FSBL里对Flash容量大小的编译配置要及时更新。很多人在换Flash后出现“高地址读不到数据”的问题往往就是忽略了第二个点。3. 烧写配置的三条实现路径3.1 路径一SDK的QSPI Flash ProgrammerVivado SDK里提供了一个图形化的QSPI Flash Programmer工具其实就是用FSBL来驱动QSPI控制器把指定的镜像文件写到Flash里。这是最直接的方式适合量少、调试阶段的烧写。操作步骤并不复杂先在Xilinx菜单里选择Program Flash然后选择BOOT.bin或单独的bitstream文件配置好Flash类型这时候FM25F32往往不在下拉列表中需要选一个兼容型号比如Spansion或Micron的同类Quad Flash、地址偏移和校验选项点击烧写即可。但这套工具对Flash型号的识别比较死板经常会因为JEDEC ID不匹配而拒绝继续或者写入后校验失败。我使用SDK烧写器时踩过最典型的坑是烧写过程正常但校验报错而且错误地址是随机的。后来定位到原因是SDK里选择的Flash时序参数是按照别的型号来的FM25F32的写使能或状态寄存器轮询时序与之有微妙差异导致偶发写入失败。解决方法是手动修改FSDL中QSPI初始化部分的时序配置。3.2 路径二U-Boot环境下的烧写如果板卡上已经有一套能跑起来的U-Boot我强烈推荐用U-Boot的sf命令来烧写Flash这是量产阶段效率最高的方式。U-Boot的QSPI驱动通常已经适配了各种常见Flash如果FM25F32不能直接被识别可以通过修改驱动程序中的JEDEC ID表来快速添加支持。U-Boot下的烧写流程是先通过tftp把镜像文件下载到内存再操作Flash擦除和写入。关键命令就是那么几条# 初始化QSPI控制器 sf probe # 擦除从0x0开始、大小为0x1E0000的区域1.875MB按自己的镜像大小调整 sf erase 0x0 0x1E0000 # 把DDR中0x8000000地址的镜像写入Flash # 0x1000000是1MB按实际文件长度修改 sf write 0x8000000 0x0 0x1000000 # 读取Flash内容到内存用于校验 sf read 0x9000000 0x0 0x1000000 # 对比两个内存区域的内容 cmp.b 0x8000000 0x9000000 0x1000000这套流程看着简单但有几个细节决定成败擦除的时候Flash的Sector和Block边界要搞清楚FM25F32支持4KB Sector擦除和32KB/64KB Block擦除如果你的擦除起始地址不在Block边界上某些型号的Flash会直接报参数错误写入长度如果不是Page256字节对齐末尾部分可能会写入失败。另外U-Boot下载镜像到DDR时注意不要覆盖U-Boot自身运行所在的地址区域。3.3 路径三Vivado Hardware Manager间接烧写当板卡上没有可用的U-BootSDK的Programmer又识别不了Flash时还有一条路使用Vivado Hardware Manager通过JTAG先把一个临时FSBL加载到Zynq的DDR中运行再利用这个FSBL间接操作QSPI Flash。具体做法是在Vivado Hardware Manager里连接目标板卡选择“Add Configuration Memory Device”手动添加FM25F32如果列表中没有就选兼容型号添加然后加载bootloader镜像到DDR执行。这个路径成功的关键是FSBL必须包含QSPI驱动而这需要你在SDK创建FSBL工程时把QSPI相关的选项勾上。这条路径我没有在项目量产中使用但在调试早期、板卡上还没有任何启动镜像的时候它就是唯一的烧录手段。流程稍微繁琐但是可靠。4. 实操过程实录从镜像生成到启动验证4.1 制作BOOT.bin无论后续用哪种方式烧写第一步都是生成BOOT.bin。这个文件的结构很清晰头部是BootROM能解析的启动镜像头里面包含了镜像的各分区信息后面依次是FSBL、FPGA bitstream和PS端应用程序。在Vivado里先综合、实现、生成bitstream然后在SDK里用bitstream文件创建一个FSBL工程并编译最后在Xilinx Tools的Create Boot Image工具里指定分区第一个分区放FSBL第二个分区放FPGA bitstream第三个分区放应用elf或者U-Boot镜像。分区顺序不能搞反BootROM会先加载FSBL到OCM执行FSBL再去配置FPGA并跳转到下一个分区。如果使用命令行方式生成BOOT.binBIF文件的写法如下the_ROM_image: { [bootloader] ./fsbl.elf ./system.bit ./u-boot.elf }生成的BOOT.bin不要超过FM25F32一半的容量留出足够的冗余空间。我在实际项目里还习惯把BOOT.bin用十六进制工具打开看一眼头部确认偏移地址和分区表正常——这招帮我发现过一次bitstream分区偏移错乱的问题。4.2 FM25F32写入操作实录假设你已经通过U-Boot的tftp命令把BOOT.bin下载到内存地址0x8000000接下来的操作顺序是先probe确认Flash能被正常识别再erase把目标区域清理干净紧接着write把数据写入最后一定要做一次read和compare校验。Zynq sf probe Zynq sf erase 0x0 0x400000 Zynq sf write 0x8000000 0x0 0x400000 Zynq sf read 0x9000000 0x0 0x400000 Zynq cmp.b 0x8000000 0x9000000 0x400000注意erase命令的第三个参数是长度而不是结束地址很多人在这里把0x3FFFFF写成0x400000导致实际擦除的区域比预期少1字节最后一位的数据就可能写不进去。虽然这是个小问题但在排查时容易被忽略。U-Boot下烧写的一个额外好处是sf命令自带Flash ID识别信息你可以通过probe的输出来确认控制器和Flash之间的通信是否正常。如果输出里显示unknown ID那就说明时序或ID表需要调整。4.3 启动模式切换与上电验证烧写完成后先把板卡断电把启动模式引脚从JTAG模式切换到QSPI模式或者直接调整对应拨码开关重新上电。如果一切正常串口终端里能看到BootROM加载FSBL的打印信息接着是FPGA配置完成然后进入U-Boot或直接启动裸机程序。我建议在切换启动模式后第一次上电时用示波器抓一下QSPI时钟和CS_N信号确认BootROM确实在尝试访问Flash。很多时候问题不是Flash里没数据而是BootROM压根没走到QSPI那条路上去——这时候检查启动模式引脚比反复烧写Flash更有价值。5. 常见问题与排查技巧实录5.1 典型故障速查表现象可能原因排查思路上电后串口无任何输出启动模式配置错误、BootROM未正确加载检查MIO[6:2]电平、确认JTAG模式是否误开sf probe返回unknown ID驱动ID表缺少FM25F32手动添加JEDEC ID或改用兼容型号配置烧写过程成功但校验失败时序参数不匹配、擦除不完整调整FSBL/U-Boot中的Quad时序配置重新擦除烧写完成但上电启动失败BOOT.bin分区错误、bitstream偏移不对检查BIF文件分区顺序用十六进制工具验证头部程序运行到一半卡死Flash容量配置不对、地址越界检查FSBL中Flash大小映射缩小镜像占用QSPI读取速度很慢Quad模式没使能、工作在SPI模式确认FSBL和U-Boot中QSPI模式都设置为Quad5.2 复旦微FM25F32的差异化适配如果说W25Q32是市场上最常用的QSPI Flash那FM25F32就是它的国产化替身。两者指令集高度兼容但FM25F32在状态寄存器的某些行为上有自己的逻辑。最典型的差异是写保护位的默认状态FM25F32出厂时部分扇区的写保护可能处于开启状态如果不预先解除写保护烧写时会出现“写入命令发出去了但Flash数据没变”的诡异现象。解决的办法是在U-Boot中手动查询状态寄存器# 读取状态寄存器值 sf read 0x9000000 0x0 0x10 md.b 0x9000000如果发现SR的低位存在保护标记就发送写状态寄存器指令先关闭所有保护区域再执行擦写操作。U-Boot环境里有些版本的sf命令没有提供直接写状态寄存器的接口这时可以通过md/mw命令直接向QSPI控制器的寄存器写入指令序列或者更省事的办法是在FSBL初始化代码里加一小段操作上电时自动解除写保护。5.3 一个实战排查案例我调试这套系统时遇到过一个很隐蔽的问题单独在SDK里下载FPGA bitstream一切正常单独在U-Boot里sf write也一切正常但把BOOT.bin烧到Flash、重新上电后FPGA迟迟没有配置成功。反复看FSBL日志都没有异常直到用示波器抓Flash的D0、D1、D2、D3时才发现问题——在Quad模式下部分数据线电平异常。原因是BOOT.bin中的bitstream被FSBL读出来之后还需要进行设备配置而Zynq的这个配置过程会频繁读取Flash。STM32裸机程序在DDR初始化之前运行完全没问题因为OCM够用但FPGA bitstream的体积一旦超过OCM容量FSBL就会启用DMA从Flash搬运数据到DDR。问题就出在DMA传输的时序和FM25F32读操作的状态机在某些边界条件下没有完全对齐导致偶发的读数据错误。这个问题的解决方式比较讨巧在FSBL的QSPI初始化代码里把DMA传输的突发长度调低一些牺牲一点读取速度换取稳定性。虽然听起来像是绕过问题而不是解决问题但在量产场景下稳定性和交付优先级高于极限性能。写在最后的经验和建议这次把Zynq xc7z020和复旦微FM25F32搭在一起调试最大的体会是国产化替换不只是把芯片焊上去那么简单IP核适配、驱动兼容、时序调优每一步都得亲自验证。FM25F32相比W25Q32在绝大多数方面可以无缝替换但细节上的差异——JEDEC ID、状态寄存器行为、写保护逻辑——才是真正决定项目进度的拦路虎。如果你也准备用FM25F32替代常规QSPI Flash我建议在产品开发初期就尽早搭建烧写和启动验证环境而不是等量产前再切换Flash型号。另外U-Boot环境下先验证sf probe是否能正确识别Flash这是一个成本最低但信息量很大的测试能识别说明硬件连接和基本时序没问题不能识别尽早排查还能省下后面大量的调试时间。最后分享一个小习惯每次烧写结束后我都会把Flash里的内容完整读回来与原始镜像做一个二进制对比。这一步虽然会多花几十秒但能避免把“烧写失败”误判成“程序bug”在调试链条里少走不少弯路。希望这篇实战记录能帮你把Zynq和FM25F32这套组合快速跑通。本文还有配套的精品资源点击获取