简介针对Xilinx Zynq平台在Vivado SDK中不支持国产复旦微FM25F32 QSPI Flash、导致启动镜像无法烧写的问题这套程序包给出了完整的实测验证方案。设计基于XC7Z020 FPGA与FM25F32 NOR Flash通过自编烧写测试程序成功写入uboot与FSBL并完成启动加载验证可帮助从事国产化替代或Zynq二次开发的工程师快速绕开SDK工具链限制。压缩包共438个文件约2.27MB以C语言头文件与源文件h/c为主配合Makefile、SDK工程文件、链接脚本及编译生成的o、a、elf、bin等中间产物方便对照工程配置与编译流程。包内还包含项目说明文档与BOOT.bin等关键文件覆盖从驱动适配、镜像生成到烧写验证的完整链路。目前已有2481人学习对正在适配复旦微Flash或研究Zynq QSPI启动机制的开发者具有直接参考价值。1. 开场为什么在xc7z020上烧FM25F32会卡住做了三块Zynq xc7z020的板子QSPI Flash烧写这一步几乎没人一次过。常见动作是手里拿着ID被Vivado SDK默认收录的那几颗Flash去套国产FM25F32结果SDK的Program Flash要么报Unknown Flash ID要么烧完校验全Pass拨到QSPI启动却整屏黑匣子。这问题之所以难缠不是Flash型号冷门而是QSPI启动链一共四段硬件接线、FSBL里的ID识别、Boot Image分区、启动模式拨码任何一处没对齐都会静默失败。这篇是我把FM25F32挂到xc7z020后从建工程、打BOOT.BIN到U-Boot烧写、串口验证的完整记录适合手里有Zynq板子、正在被QSPI Flash烧写搞到头大的开发者抄作业。2. 硬件接线与Vivado工程把FM25F32挂到xc7z020的QSPI控制器上2.1 确认链路FM25F32的引脚与MIO分配FM25F32是3.3V供电的QSPI NOR Flash容量32Mbit也就是4MB支持标准SPI和四线QSPI两种模式。Zynq xc7z020的QSPI控制器接口固定在MIO[5:0]上CPU侧不需要额外分配引脚这省了事也意味着没得选——硬件设计时只能按Zynq的固定映射来接对应关系如下表Zynq MIOQSPI信号FM25F32引脚功能MIO0QSPI_SCKSCK时钟MIO1QSPI_CSCS片选MIO2QSPI_DQ0DI数据输入MIO3QSPI_DQ1DO数据输出MIO4QSPI_DQ2WP/HOLD复用MIO5QSPI_DQ3HOLD/WP复用这里要特别盯住FM25F32的WP和HOLD两个脚。在标准SPI单线模式下WP不接可能还能勉强跑起来一旦把FM25F32切到四线QSPI模式WP就变成了DQ2HOLD变成了DQ3。这两个脚如果悬空或被拉低四根数据线里有两根的电平状态不确定读出来的数据按照字节移位错乱表现就是Flash内容能识别、但启动时指令全乱。所以FM25F32的WP和HOLD必须通过10K电阻上拉到3.3V这是烧写前第一道门槛板上这一步错了后面软件怎么改都救不回来。供电和电平也要顺手核一遍。FM25F32是3.3V器件xc7z020的QSPI IO口也工作在3.3V电平匹配没有问题。但如果你的板子把Flash放在了1.8V域或者中间加了电平转换芯片就要看转换芯片的OE方向和输出速率QSPI时钟25MHz下转换芯片选型不当会导致信号沿变缓烧写时偶发校验失败。我一般把电源和上拉电阻放在PCB检查清单最前面比软件配置更早确认。2.2 在Vivado Block Design里使能QSPI并锁定SDR模式打开Vivado工程后进IP Integrator在Block Design里双击Zynq7 Processing System左侧导航走Peripheral I/O Pins → MIO Configurations展开Quad SPI Flash列勾上QSPI。勾选后会自动把MIO0到MIO5分配成QSPI的一组引脚同时出现QSPI时钟、片选和数据脚的配置项。保存设计后Generate Output Products再Generate Bitstream之前记得在Address Editor里看一眼QSPI控制器的寄存器基地址默认落在0xE000D000线性映射地址从0xFC000000开始后面验证阶段会用到这个线性映射地址。这里有两个配置项值得细看。一个是Flash Speed生成FSBL时会按这个频率初始化QSPI控制器我习惯设成25MHz。FM25F32的标称时钟余量通常够跑更高频率Zynq QSPI控制器在SDR模式下跑到50MHz也很稳但烧写阶段我不追速度25MHz启动稳定性比省那几十毫秒重要。另一个是Flash型号模式在QSPI IP配置里可以选Single和Dual。FM25F32单颗只占一个片选CS0选Single如果板子上为了扩大容量放了两颗Flash做双片选再选Dual。这个选项直接决定FSBL里QSPI驱动初始化几个片选选错会导致片选地址计算偏移验证时的现象是Flash内容能读出来但首地址数据全空。关于DDR模式要单独说一句。Zynq QSPI控制器支持SDR和DDR两种读时序DDR模式下控制器用0xEB这类命令让Flash在时钟上下沿都输出数据带宽翻倍。但很多通用SPI NOR Flash只保证SDR命令集的兼容性对DDR命令的支持要看具体数据手册。FM25F32这类国产Flash我的做法是强制走SDR不在DDR上赌兼容性。把QSPI配置里的Read/Write模式保持SDRU-Boot里也不要开DDR读后面避坑章节5.2会再展开这个坑。2.3 编译FSBL让启动代码认FM25F32的IDFSBL是Zynq启动的第一段代码上电后由固化ROM从Flash加载执行QSPI控制器初始化和Flash ID识别都由它完成。在Vivado SDK里右键平台工程选择Source → Download Sources把FSBL源码下载到本地在src目录下能找到QSPI驱动的Flash参数表。不同版本SDK把这表放在不同文件常见命名是ps7_qspi.c也有可能放在flash_list.h里。表里每一项记录Flash容量、扇区大小和JEDEC ID。JEDEC ID通过0x9F命令读出一般两到三字节包含厂商ID和型号ID。FM25F32如果不在SDK的这张表内用Program Flash烧写时大概率报Unknown Flash IDU-Boot的sf probe也可能读到一组不匹配的ID然后拒绝挂载。处理办法有两个一是从驱动源码里找到那张结构体数组把FM25F32实际读出的ID填进去重新编译FSBL优点是SDK烧写流程原样可用缺点是每换一颗不同型号Flash都要改一次驱动源码二是干脆绕开SDK的Flash识别后续用U-Boot的sf命令直接烧写。我最终落在第二种方案上。原因很简单U-Boot的QSPI驱动对厂商ID没那么敏感只要擦除和编程命令集兼容就能直接操作FM25F32的命令集和主流SPI NOR Flash基本一致适配成本最低。如果你必须留着SDK烧写路径那就按第一种方案改表改完重新编译FSBL并重新打包整个BOOT.BIN别只更新FSBL文件而忘了重新生成镜像。3. 生成BOOT.BINFSBL、bitstream和应用一次打包3.1 组装镜像需要的三份文件想让FM25F32烧完就能引导Flash里不能只有bitstream。Zynq上电后从QSPI Flash读到的第一段必须是FSBLFSBL初始化DDR和外设后把bitstream加载进PL也就是设计里的FPGA配置程序再由FSBL引导应用或U-Boot。所以最终烧进Flash的是一个三合一镜像BOOT.BIN三个分区缺一不可。准备文件时按下表对齐文件来源作用fsbl.elfSDK新建Application工程模板选Zynq FSBL启动第一段初始化DDR、QSPI、PLL等你的工程.bitVivado里Implementation → Generate Bitstream生成PL侧的配置程序u-boot.elf 或 app.elfU-Boot构建产物或你自己的C工程编译产物ARM侧的运行程序fsbl.elf编译时建议选Release配置Debug版本镜像体积大挤占FM25F32本就紧张的4MB空间。bit文件在Vivado里生成后不要改名字打包工具按照分区顺序读取文件路径里不要带中文和空格。如果目标系统跑Linux第三份文件就是u-boot.elf只跑裸机的话就是自己的app.elf。先分别确认这三个文件都真实存在再进打包步骤。3.2 Create Boot Image的参数设置Vivado SDK菜单里选Xilinx → Create Boot Image打开Boot Image Editor。窗口左侧的Boot image partitions列表就是分区配置区依次添加三个分区。第一步添加FSBLPartition Type选bootloaderOffset写0x0它必须位于Flash的0地址因为Zynq固化ROM上电后固定从0地址读启动代码。第二步添加bitstreamPartition Type选datafileOffset手动填0x100000也就是1MB偏移处。第三步添加app或u-bootOffset填0x2000002MB偏移处。这里有个参数取舍值得说。SDK提供一个Auto offset按钮会自动给每个分区安排地址我基本不用手动固定地址能让我在调试时直接用U-Boot的sf read按已知偏移读数据做对比。Zynq的QSPI控制器把Flash映射到线性地址空间分区偏移同时决定启动时加载地址手动固定就少了一层黑匣子。偏移设置有个约束QSPI NOR Flash的扇区一般是4KB0x100000和0x200000都是4KB的整数倍天然对齐不需要额外做边界修正。如果改了偏移值务必在BIF文件里确认分区顺序和Offset一一对应。打包前最后一道检查是容量。FM25F32只有4MB地址范围0x0到0x3FFFFF三个分区文件大小加起来不能超过这个范围。fsbl.elf一般在200KB以内一个典型bitstream在200KB到900KB之间u-boot.elf大概1MB上下。如果发现总大小逼近3.5MB就要考虑压缩bitstream或裁剪PL设计而不是继续加大偏移。拿2MB处的app分区来说它往后最多到0x3FFFFF只剩约2MB可用u-boot如果开了大量配置功能很容易超。3.3 分区与加载地址对照生成BOOT.BIN之前把这张分区表打印出来贴在显示器边上。后面凡是遇到“烧写成功但起不来”第一步就是核对Flash 0地址上是不是FSBL、0x100000是不是bitstream、0x200000是不是u-boot。三段错位是QSPI启动最常见的隐形故障FM25F32本身没问题背锅的是打包参数。分区Flash偏移典型大小启动时加载到FSBL0x000000128KB-256KBOCMbitstream0x100000≤900KBPL配置引擎u-boot/app0x200000≤2MBDDRBoot Image Editor里Output Format选BIN生成之后用文本编辑器打开BOOT.BIN的BIF描述文件重点看三个partition的offset字段是否和上表一致。不一致就回到分区列表重新排然后重新生成。镜像到这一步就绪可以进烧写环节。4. 烧写FM25F32SDK Program Flash与U-Boot两条路径4.1 方式一SDK Program Flash最直观的方式是Vivado SDK里Xilinx → Program Flash。接线是JTAG或USB下载线连到板子先在Hardware Manager里确认能看到xc7z020器件。这里有个细节容易被忽略Hardware Manager里的JTAG时钟速度要手动降到10到30MHz。在线调试时高速JTAG没问题烧Flash时数据吞吐大JTAG链路不稳定会出现TDO stuck或者校验中途断开的玄学错误降速后基本绝迹。Program Flash对话框里的关键参数这样填Flash Type选qspi-x4-single这个名字要和FSBL的QSPI配置严格一致。FSBL里配的是Single加四线数据这里就选x4-single选错会在烧写阶段报控制模式不匹配。Image File选刚生成的BOOT.BINOffset填0x0勾上Erase和Verify。点Program后SDK会先把FSBL通过JTAG加载进内存运行由FSBL里的QSPI驱动执行实际擦写动作日志窗口能看到擦除和编程的进度百分比。这一步最怕报Unknown Flash ID。见到这个错误说明SDK的驱动没有收录FM25F32的ID处理路径在2.3已经说了要么改驱动源码加ID重新编译FSBL要么切到U-Boot方式。如果你手里已经有能启动到U-Boot的环境我强烈建议直接切U-Boot少改一套代码。要是必须走SDK且不想动驱动还有一个办法是把Image File换成MCS或BIT并使用Indirect Programming模式但那个流程绕一圈还是绕不开ID识别不如U-Boot干脆。4.2 方式二U-Boot用sf命令烧写当板子已经能从SD卡或QSPI启动到U-Boot时烧FM25F32完全不用依赖SDK的Flash列表。U-Boot命令行里操作即可# 探测QSPI Flash U-Boot# sf probe 0:0 0 0 # 擦除整片4MB U-Boot# sf erase 0x0 0x400000 # 从SD卡FAT分区加载BOOT.BIN到内存 U-Boot# fatload mmc 0 0x08000000 BOOT.BIN # 把内存中的镜像写入Flash起始地址 U-Boot# sf write 0x08000000 0x0 0x400000sf probe的三个参数是总线号、片选号和时钟频率。0:0表示控制器0的片选0上那颗Flash也就是CS0第二个0代表时钟频率填0U-Boot会使用驱动默认值第三个0是SPI模式。这样写能避开对不同Flash时序参数的硬编码兼容性最好。probe成功后U-Boot可能把FM25F32识别成ID表中某颗容量一致的常见Flash型号这不影响使用擦除和编程命令都是标准的0x02、0x20、0xD8命令集兼容就能直接操作。sf erase擦除整片4MB如果控制器不支持整片擦除命令会自动退化成逐扇区循环擦除只是耗时长一点。fatload把SD卡FAT分区根目录下的BOOT.BIN读到DDR地址0x08000000mmc 0对应SD卡控制器0x08000000是Zynq上常用的DDR加载地址只要这个地址不在正在运行代码的区域内就安全。最后sf write从内存向Flash起始地址写入4MB写完后U-Boot默认会做回读校验看到校验通过信息就代表写入成功。如果不想一次写满4MB可以把0x400000改成BOOT.BIN实际字节数擦除时也改成对齐到扇区的同样大小。4.3 校验不能只信烧写日志SDK的Program Flash勾了Verify会自动校验U-Boot的sf write也自带校验但这两者的校验都只是“写完回读比对”不保证拔电后能启动。我每次烧完还会强制做一次回读看Flash首地址内容# 回读Flash前2MB到内存 U-Boot# sf read 0x08000000 0x0 0x200000 # 打印内存头部256字节十六进制 U-Boot# md.b 0x08000000 0x100md.b把内存里的内容打印成十六进制前0x100字节应该是FSBL的头部。正常的启动代码开头是一段ARM异常向量表能看到一堆跳转指令而不是整片FF或者全零。如果全是0xFF说明擦除后没写进去常见原因是擦除时片选或地址没对齐如果内容看起来像乱码但并非全FF则要怀疑数据线连接或SDR模式配置。除了sf read回读Zynq还有一个更直接的内存映射校验法QSPI控制器把Flash线性映射到0xFC000000地址直接用md.b 0xFC000000 0x100读前256字节。这一步不经过sf命令硬件上Flash像一块只读内存挂在总线上读出来的内容和sf read一致说明从Flash到控制器的整个硬件链路都是通的。两条校验路径我一般都会走一遍U-Boot的sf read负责验证逻辑数据0xFC000000线性读负责验证硬件映射。5. 避坑FM25F32在Zynq QSPI烧写中的5个典型问题5.1 Unknown Flash IDSDK不认这颗Flash现象SDK的Program Flash点击Program后日志框几秒内弹出Unknown Flash ID或者Unsupported flash烧写流程直接终止。原因SDK驱动里维护了一张Flash ID表FM25F32的JEDEC ID不在表中。Zynq QSPI控制器本身能正常通信是软件层的ID校验拒绝了它并非硬件损坏。解决能进U-Boot就用U-Boot的sf命令烧写绕开SDK的ID表必须用SDK的话在FSBL驱动源码里找到Flash信息结构体数组把FM25F32实际读出的JEDEC ID填进去注意结构体里一般有厂商ID和型号ID两个字段改完重新编译FSBL并重新生成整个BOOT.BIN。改表方案我留作备用日常烧写直接U-Boot更快。5.2 烧写日志正常拔电重启却整屏白现象烧写日志显示写入成功Verify也通过但拨到QSPI启动后串口没有任何输出就像Flash是空的。原因两个原因占九成。一是启动模式拨码根本没切到QSPI档Zynq复位时采样到的启动模式还是SD卡或JTAGFlash内容根本没被执行二是FSBL里QSPI被配成了DDR读模式而FM25F32对DDR读命令的兼容性没有保证FSBL执行时读回来的指令是乱的自然起不来。解决第一步查开发板启动模式拨码Zynq xc7z020的QSPI启动对应MIO[4:0]采样值11010具体拨到哪个位置以板卡手册为准。第二步回Vivado把QSPI读写模式锁成SDR重新生成FSBL并重新打包。这两个方向排查完白屏问题基本都能落地。5.3 擦除命令超时状态寄存器轮询卡死现象U-Boot下sf erase执行到一半长时间不动最后报write timeout或者erase error有时连续擦多个扇区时第二个扇区就卡住。原因FM25F32擦除时状态寄存器bit0的翻转节奏和常见Flash有差异U-Boot的轮询超时时间按标准Flash算慢一点的Flash就触顶另一个高频原因是WP引脚被拉低芯片处于写保护状态擦除命令直接被拒绝。解决先用万用表量FM25F32的WP引脚必须为高电平这是写操作的前提。电平正常还超时就把整片擦除改成逐扇区擦每次只擦4KB块擦完读取状态寄存器确认空闲再擦下一块。U-Boot里可以写个短循环脚本执行sf erase 0x0 0x1000地址循环累加直到擦完整个4MB慢一些但稳定。5.4 bitstream加载到一半失败PLL或DNA报错现象FSBL阶段串口能看到打印说明Flash和DDR已经工作但bitstream加载到大约一半时设备宕掉JTAG日志里出现PL初始化失败或DNA读取错误。原因QSPI时钟配得太高。FM25F32在较高时钟下的读时序参数不够稳定FSBL通过DMA把bitstream从Flash读到PL配置引擎时高速数据流下偶发位错导致配置数据校验不过。解决回到Vivado的Zynq配置界面把Flash Speed改成25MHz重新生成FSBL重新打包BOOT.BIN再执行第4章的烧写流程。QSPI Flash时钟不是越快越好25MHz是xc7z020 QSPI上最保守也最可靠的档位牺牲少许加载时间换启动成功率值得。5.5 容量错觉4MB地址算错现象BOOT.BIN超过4MB时也能烧“成功”但启动时FSBL读到被截断的数据串口打印乱码或者U-Boot里sf erase直接报size out of range。原因FM25F32容量32Mbit换算过来就是4MB地址范围0x0到0x3FFFFF。BOOT.BIN整体大小超过4MB或者某个分区偏移超过0x400000都超出Flash可寻址空间超出部分自然写不进去。解决打包前把三个分区文件大小相加确认总和小于0x400000。分区偏移保持FSBL在0x0、bitstream在0x100000、app在0x200000的约定别为了给某个分区让路把偏移改到0x400000以外。真要超容先裁剪PL设计或压缩bitstream改偏移只是自欺欺人。6. 验证跑通拨QSPI启动用串口日志确认FSBL到应用烧写完的镜像到底能不能用唯一标准是冷启动。把开发板断电启动模式拨到QSPI档对应Zynq xc7z020的MIO[4:0]采样值11010。JTAG线如果没拔也先断开因为JTAG适配器会抢占启动流程干扰上电自检。串口按115200 8N1接好打开终端再给板子上电。上电瞬间的日志顺序比内容更重要因为它能定位问题出在哪一段。正常流程下第一屏是FSBL打印典型内容是Boot Device: QSPI跟着是DDR初始化信息。接着如果BOOT.BIN里带了bitstream会出现PL配置完成的信息然后进入U-Boot或裸机应用自己的输出。我习惯用一组日志节点做通过性检查FSBL: Boot from QUAD-SPI DDR initialization complete PL Configuration Done U-Boot banner / 应用自己的启动打印看到这四行说明FM25F32的ID识别、SDR时序、分区偏移和启动模式全对上了。只看到FSBL打印但卡在DDR或PL回看5.4的时钟问题连FSBL打印都没有基本是启动模式拨码或Flash首地址数据问题先回查拨码和CS焊接。串口一个字不出时别急着动Flash先拿万用表量MIO1引脚在拨到QSPI档后是不是正确拉成启动电平这一步经常省掉一整个下午的无效烧写。还有一个更狠的验证是二次上电。第一次上电成功不算数断电等五秒再上电串口必须再次完整打印同样的日志。QSPI Flash存在读时序的偶发问题冷启动两次都成功才说明FM25F32的时序裕量在xc7z020上真正够用。同一个BOOT.BIN连续三次上电都稳定我才会把这颗板子从烧写台放走。从那以后我每烧一颗FM25F32都强制走这道流程打包时核对三段分区大小、烧完用sf read回读前256字节、然后断电冷启动两次看串口日志。这套流程帮我挡掉了至少三次只看校验日志而放过启动问题的翻车现场也希望帮你少走这几步冤枉路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?