
ZYNQ的启动模式是很多新手入坑时碰到的第一块硬骨头。开发板上明明既有SD卡槽又有QSPI Flash却搞不清两者什么关系怎么让它们各自能启动怎么实现双启动双启动到底有什么用我当年第一次接触ZYNQ时也被这堆概念绕晕过。后来折腾过几个项目、翻了不少手册才彻底理顺所谓启动模式本质就是处理器复位瞬间采样MIO[6:2]五根引脚的电平然后照着这个电平组合从对应设备加载启动镜像。把这句话吃透后面所有配置都是水到渠成的事。这篇文章我就按自己实际动手的流程把SD卡与QSPI Flash双启动模式的硬件配置、镜像制作、烧写验证和常见问题一次讲清楚。1. 动手前的认知ZYNQ启动的本质是引脚电平 镜像1.1 BootROM、FSBL、级联启动各是谁很多新手第一次看ZYNQ启动流程时都会被一堆缩写劝退BootROM、FSBL、SSBL、U-Boot、PL bitstream……其实拆开看并不复杂。ZYNQ芯片内部固化了一段出厂代码叫BootROM上电后处理器会优先从片内的OCMOn-Chip Memory通常256KB运行这段代码。BootROM干的事很纯粹根据采样到的启动引脚电平找到对应的启动设备从里面读出FSBLFirst Stage Boot Loader第一级引导程序放到OCM里执行。FSBL是由用户自己生成的它接管后负责初始化DDR、配置时钟、初始化PS外设、加载PL bitstream然后再去加载第二级引导比如U-Boot或者裸机应用程序。那什么是级联启动简单说就是BootROM只负责从一级设备加载FSBL剩下的bitstream、U-Boot、应用程序全都让FSBL从二级设备去读。这样一来启动镜像就被拆成了两部分放在两个地方。这就是SD卡与QSPI Flash双启动模式在硬件层面的基础逻辑。1.2 MIO[6:2]编码表五个引脚决定启动方式ZYNQ-7000系列的启动模式由MIO[6:2]五根引脚在复位释放瞬间的电平决定注意是采样一次、复位前改无效。这五根引脚分成两组来看MIO[6:5]主启动设备选择Primary Boot DeviceMIO[4:2]级联启动方式Cascaded Boot Options对应关系务必记牢MIO[6:5]主启动设备MIO[4:2]级联方式00NAND Flash111无级联01NOR Flash010SD卡级联到QSPI10SD卡SDIO0100QSPI级联到SD卡11QSPI Flash110通过PCAP级联很多开发板上的拨码开关会把这五根引脚引出来拨到对应位置后按复位键芯片重新采样启动模式就变了。如果你想用SD卡启动MIO[6:5]必须拨成10想用QSPI启动就拨成11。MIO[4:2]在没有特殊需求时保持111就行。1.3 双启动的三种理解别搞混了双启动这个词在不同人口中含义完全不同我见过至少三种拨码切换双启动SD卡和QSPI Flash各存一套完整镜像通过拨码开关手动选择用哪个启动。这种方式最简单也是绝大多数开发调试场景的选择。硬件级联双启动SD卡只放FSBLQSPI Flash里放完整应用镜像上电后BootROM从SD卡加载FSBLFSBL再从QSPI加载bitstream和U-Boot/应用。这种方式适合批量生产、现场升级一个SD卡可以引导多块板子应用更新只烧QSPI。软件层双启动从SD卡启动Linux后通过U-Boot的sf命令或Linux下的工具访问QSPI Flash实现镜像切换、系统升级。严格说这不算启动模式但很多人的需求其实藏在这里。文章后面会把这三种都覆盖到但主线还是前两种因为它们涉及硬件配置新手最容易卡在这里。2. 5分钟硬件级配置从原理图到拨码开关的对应关系2.1 在原理图上按图索骥搜MIO2到MIO6拿到一块新板子第一步不是翻几十页数据手册而是打开原理图直接搜索MIO2、MIO3、MIO4、MIO5、MIO6。有些原理图上这五根引脚连在一起网络名直接叫BOOT_MODE0BOOT_MODE4或者PS_MIO2PS_MIO6。找到之后看它们连到了哪里。常见设计有两种直接连拨码开关开关另一端接GND或3.3V通过上下拉确定电平通过0欧电阻或跳线帽接到电源/GND需要改焊接跳线。如果是0欧电阻方案改起来麻烦一些但大多数学习板、开发板都会预留拨码开关。拨码开关的ON位置对应哪种电平不同板卡不统一务必对照原理图确认开关拨到ON是拉到GND还是拉到3.3V这直接决定你拨码的方向。有个细节值得提醒MIO[6:2]这五根引脚中MIO2和MIO3同时也是SDIO0的CMD和CLK信号复用引脚MIO5和MIO6也复用了其他功能。如果这些引脚被其他外设占用且产生冲突信号可能导致启动模式采样异常。设计PCB时这两组引脚最好不要有强烈干扰源实际调试中如果出现拨码明明对了但启动模式不对要怀疑是否有外设跟MIO[6:2]抢信号。2.2 实现SDQSPI双启动的具体拨码组合以最常见的4位或8位拨码开关为例假设拨码开关ON端接GND即拨到ON为低电平0拨到OFF为上拉到高电平1那么以下组合可以直接照抄目标模式MIO6MIO5MIO4MIO3MIO2拨码状态ON为0SD卡独立启动10111OFF, ON, OFF, OFF, OFFQSPI独立启动11111OFF, OFF, OFF, OFF, OFFSD→QSPI级联10010OFF, ON, ON, OFF, ONQSPI→SD级联11100OFF, OFF, OFF, ON, ON注意这只是ON为0的假设不同板卡可能相反。拿到板子后先看原理图确认关系再对照这个表格换算。有些板子直接把5位拨码单独做成了一个模块丝印上标了MIO6MIO2那就简单了直接按MIO值拨就行。2.3 上电后如何验证当前启动模式很多新手最头疼的问题是我拨了码但不知道有没有生效。其实ZYNQ把采样结果存在一个寄存器里随时可以读出来验证。这个寄存器是SLCR模块里的BOOT_MODE寄存器地址为0xF800025C它复位后的低5位就对应MIO[6:2]的采样值且只读。在U-Boot命令行下验证当前启动模式md.l 0xF800025C 1假设输出为F800025C: 00000012十六进制0x12转换成二进制是10010按低5位拆开就是MIO61、MIO50、MIO40、MIO31、MIO20。对照编码表MIO[6:5]10表示主启动设备是SD卡MIO[4:2]010表示SD卡级联到QSPI说明当前正处于SD→QSPI级联启动模式。如果你用Vitis或Vivado的SDK调试也可以在FSBL代码里加入打印u32 BootMode Xil_In32(XPAR_SLCR_BOOT_MODE_ADDR) 0x1F; xil_printf(Boot Mode: 0x%02x\r\n, BootMode);上电后在串口终端就能看到当前采样的boot mode值。这个验证习惯能帮你排除掉一大半配置了但不起作用的问题。3. 软件镜像准备SD卡和QSPI里到底该放什么硬件拨码搞定只是第一步真正让新手抓狂的是镜像文件不知道怎么生成、该放哪里。3.1 PetaLinux一条命令打包BOOT.BIN如果你用PetaLinux生成SD卡启动镜像非常简单。先正常完成PetaLinux工程构建让images/linux目录下出现zynq_fsbl.elf、system.bit、u-boot.elf这几个关键文件然后执行打包命令petalinux-package --boot --format BIN \ --fsbl images/linux/zynq_fsbl.elf \ --fpga images/linux/system.bit \ --u-boot执行完之后同目录下会生成BOOT.BIN。这个BOOT.BIN本质上是把FSBL、PL bitstream、U-Boot三个部分按固定格式拼在一起的文件BootROM和FSBL会依序解析并加载它们。接下来制作SD卡启动盘使用SD Card Formatter工具格式化SD卡文件系统选FAT32将BOOT.BIN拷贝到SD卡根目录将boot.scrU-Boot启动脚本和image.ub内核设备树打包镜像一并拷贝到根目录安全弹出SD卡插入开发板SDIO0对应的卡槽。image.ub里的ub是U-Boot的通用镜像格式内核和设备树被打包在了一起PetaLinux构建时会自动生成。启动时U-Boot执行boot.scr里的命令从FAT分区找到image.ub加载到DDR并启动内核。3.2 裸机工程怎么生成BOOT.BIN如果你跑的是裸机程序没有Linux生成BOOT.BIN的方式更简单但很多人不熟悉bootgen。在Vitis中新建或导入一个硬件工程后依次完成在xsa文件对应的硬件平台上创建FSBL工程并编译得到zynq_fsbl.elf创建你的裸机应用程序工程并编译得到app.elf写一个BIF文件比如boot.bifthe_ROM_image: { [bootloader] zynq_fsbl.elf app.elf }如果FPGA逻辑里有bitstream在BIF中加一行system.bit即可。然后在Vitis的Xilinx Tools菜单下打开Bootgen或者直接命令行执行bootgen -image boot.bif -o i BOOT.BIN生成的BOOT.BIN丢到SD卡FAT32分区根目录上电就能从SD卡启动裸机程序。裸机方案不需要boot.scr和image.ub因为FSBL加载完app.elf之后就直接把控制权交给应用程序了。3.3 SD卡分区的关键必须是FAT32这里必须单独拎出来说因为这是新手踩得最密集的坑。ZYNQ的BootROM只认FAT12/FAT16/FAT32文件系统它不认识ext4也不认识NTFS。如果你把SD卡整个格式化成ext4之后往里面丢BOOT.BIN上电后BootROM会懵掉串口毫无输出看起来就像板子坏了。我见过不止一个人把SD卡用dd命令写入了PetaLinux的整卡镜像boot分区是FAT32所以没问题但如果自己手动分区时把所有空间都给了ext4那BootROM就找不到了。正确做法是第一个分区必须是FAT32用于放BOOT.BIN、boot.scr、image.ub如果后续要挂载rootfs可以再分第二个ext4分区放rootfs。另外SD卡的容量也有讲究。ZYNQ的BootROM对SDXCexFAT格式支持不友好建议使用32GB以内的SD卡。如果手头只有64GB以上的卡可以用SD Card Formatter的覆盖格式选项格式化成FAT32绝大多数情况下也能用但稳定性不如小容量卡。3.4 QSPI Flash烧写与地址规划QSPI Flash的启动逻辑和SD卡类似BootROM从QSPI首地址开始读取镜像并加载FSBL后面的事由FSBL接管。所以最省事的做法就是把跟SD卡一样的BOOT.BIN原封不动烧到QSPI的起始地址0x0000000。烧写方式我常用三种按场景选第一种Vitis的program_flash命令。在Vitis Terminal里执行program_flash -f BOOT.BIN -offset 0 -flash_type qspi-x4-single -fsbl zynq_fsbl.elf注意-flash_type要和板级QSPI实际支持的模式匹配常见的是qspi-x4-single。如果FSBL里配置的QSPI控制器模式和这里不一致烧进去之后可能启动失败。第二种Vivado Hardware Manager图形化烧写。连接JTAG后用Add Configuration Memory Device功能选好Flash型号和镜像文件直接烧写。适合不熟悉命令行的场景但效率低量产不推荐。第三种U-Boot环境里烧写。SD卡启动Linux或U-Boot之后把BOOT.BIN放到某个存储介质上加载到DDR某个地址然后用sf命令写入sf probe sf erase 0x0 0x2000000 sf write 0x2000000 0x0 0x2000000这里sf write的三个参数分别是DDR源地址、QSPI目的偏移、写入长度。这种方法在量产和现场升级时最灵活也是我项目里最常用的。QSPI Flash的地址规划建议参考这个表前提是Flash容量16MB以上地址范围存放内容说明0x0000000 ~ 0x01FFFFFBOOT.BIN2MB空间FSBLbitstreamU-Boot足够0x0200000 ~ 0x05FFFFFimage.ub内核和设备树预留4MB0x0600000 ~ 0x07FFFFF备份分区存放出厂固件方便回滚实际规划时一定要看你板子的Flash型号和容量。如果BOOT.BIN超过2MB地址要后移烧写时sf erase的长度要覆盖到整个文件别擦一半写一半。4. 双启动实测验证日志里那些信息说明什么4.1 独立启动的串口日志特征先看从SD卡独立启动的日志。正常时串口输出大致长这样Xilinx Zynq ZC706 U-Boot 2024.01 (Jan 12 2025 - 16:20:33 0800) I2C: ready DRAM: 1 GiB MMC: mmce0100000: 0 Net: eth0: ethernete000b000注意看MMC那行出现mmce0100000: 0说明U-Boot已经识别到了SDIO0控制器后续能从MMC设备加载image.ub。如果这里显示No MMC device found基本可以断定要么SD卡没插对卡槽不是SDIO0要么文件系统格式不对。从QSPI独立启动时日志前几行几乎一样因为FSBL和U-Boot都从QSPI加载了看不到MMC设备也正常。但U-Boot里如果配了CONFIG_SYS_BOOTCOUNT_ADDR或QSPI驱动会多出QSPI相关的初始化信息比如SF: Detected n25q128a13 with page size 256 Bytes, erase size 64 KiB, total 16 MiB看到这行说明U-Boot已经通过QSPI控制器识别到了Flash型号和容量。如果这里报SF: Unsupported command或者Failed to get SPI flash说明FSBL里对QSPI控制器的配置和实际板卡不匹配优先检查MIO[8:23]、MIO[1:0]是否被占用以及FSBL的QSPI时钟频率是否超过Flash最高支持频率。4.2 级联启动日志特征如何确认它真的从QSPI加载了后续镜像以SD→QSPI级联为例上电后的流程是BootROM从SD卡读取BOOT.BIN精简版只含FSBLFSBL跑到一定阶段后转去QSPI Flash读取剩余的bitstream和U-Boot。这种模式在日志上有一个很明显的特征不到1秒的FSBL初始化信息之后紧跟着的U-Boot启动时间比SD独立启动稍长且日志中不会出现SD卡设备访问痕迹却会出现QSPI Flash初始化信息。如果你在FSBL源码里加入了自定义打印可以在FSBL读取QSPI之前和之后各打一条日志xil_printf(FSBL: Load image from QSPI...\r\n); // QSPI加载逻辑 xil_printf(FSBL: QSPI image loaded OK!\r\n);出现了这两条日志基本可以确认级联链路是通的。如果没有第二条那说明FSBL根本没走到QSPI读取这一步问题大概率出在BootROM读取BOOT_MODE寄存器后没有进入级联分支也就是MIO[4:2]没拨对。4.3 我的一次真实排查经历为什么级联模式没有触发有次给客户做批量烧录方案因为固件比较大BOOT.BIN超过了8MBSD卡里放精简FSBL、QSPI里放完整镜像的级联模式就成了最优解。第一次上电测试SD卡里的FSBL正常启动但死活不加载QSPI里的镜像串口卡死在FSBL初始化完成之后没有任何报错。我当时的排查链路是这样的第一步用md.l 0xF800025C 1读回BOOT_MODE寄存器发现值是0x12也就是MIO[6:5]10、MIO[4:2]010级联标志确实存在。问题不在硬件采样。第二步怀疑QSPI里的镜像格式不对。我把QSPI读出来跟原始BOOT.BIN对比发现内容一致排除了烧写错误。第三步查FSBL的源码逻辑。这才发现我用的Vitis版本生成的FSBL里默认配置的QSPI加载地址和QSPI控制器的x4模式与我烧写时用的-flash_type qspi-x4-single不匹配。程序里走的还是单线模式读QSPI但Flash实际被配置成了四线模式导致FSBL读出来的数据全是乱码加载自然失败。解决办法很简单重新在Vitis里生成FSBL时把BSP的QSPI配置改成qspi-x4-single重新编译打包烧写级联一次通过。这个案例想说明两件事一是级联启动有问题时先确认寄存器里的值是不是真的进了级联分支二是FSBL里QSPI模式配置和实际烧写方式必须保持一致这是我见过最隐蔽的一个坑。5. 踩坑实录新手最容易翻车的5个典型问题5.1 上电后串口零输出先查这几处串口完全没有反应是新手最崩溃的时刻。按这个顺序排查通常十分钟内能找到原因串口接线和波特率是否对。ZYNQ调试串口一般从MIO14/MIO15引出波特率常见115200。先拿一个已知能跑的镜像排除串口本身的问题。启动模式是否真的采样正确。用万用表量一下MIO2到MIO6的实际电压而不是只看拨码位置。拨码开关氧化、虚焊都会导致电平不对。SD卡是否插在SDIO0对应的卡槽。ZYNQ BootROM只认SDIO0插到SDIO1的卡槽上任何镜像都白搭。SD卡分区和文件系统是否正确。FAT32、BOOT.BIN在根目录这两条缺一不可。镜像本身是否损坏。在电脑上重新拷贝一次拷贝完安全弹出再拔卡Windows下直接拔卡可能只写了缓存没写实体。5.2 FSBL打印正常却卡死的处理顺序FSBL跑起来了说明BootROM和SD/QSPI的基础链路是通的。卡死的位置不同原因也不同。如果卡在DDR初始化阶段优先查DDR颗粒型号和FSBL里DDR配置文件是否匹配PetaLinux构建时选错DDR型号是常见原因。如果卡在bitstream加载阶段先看PL的bit文件是否损坏再看DDR里的加载地址是否被其他镜像占用。如果U-Boot起来了但启动内核时卡在Starting kernel ...优先怀疑设备树里内存节点大小和实际DDR容量不匹配或者FIT镜像里包含了错误的内核地址。这里有个通用建议在Vitis里用SDK的xil_printf在FSBL每个关键步骤后打日志花费半小时加的打印能帮你节省几天的排查时间。5.3 QSPI烧写成功但启动失败地址、格式、模式三连检QSPI烧完上电没反应几乎都能归到这三类地址错烧写偏移不是从0x0开始。有人习惯先烧U-Boot再烧App结果App覆盖了U-Boot或者BOOT.BIN没从0开始导致BootROM读到的是垃圾数据。建议量产烧写脚本里强制从0x0擦除整个flash再写。格式错QSPI里烧的不是BOOT.BIN而是某个单独的ELF文件。BootROM不认ELF它只认带特定头格式的BOOT.BIN这一点跟SD卡完全一样。模式错就是我上一节踩过的那个坑FSBL配置的QSPI控制器模式和烧写/板卡实际模式不一致导致FSBL能识别Flash但读不到正确数据。查FSBL BSP设置和烧写时用的flash_type对应起来。5.4 拨码切换后不生效别忘看复位类型拨码拨了半天上电一看还是原来的启动模式很多人第一反应是拨码方向搞反了。如果方向确认无误还有一个容易被忽略的因素复位类型。ZYNQ有POR复位和PS复位两种。部分板卡的PS_RST按键不会触发重新采样启动引脚你按的如果是这颗复位键拨码改了也不会生效。正确做法是改完拨码后按一次全板上电复位键通常标着POR或PWR_ON或者干脆断电重新上电。另外拨码开关最好在断电状态下拨。带电拨码虽然一般不会烧东西但MIO[6:2]在运行中还承担其他功能相邻引脚上的信号毛刺可能干扰采样结果造成拨了但采样不对的假象。5.5 文件拷进SD卡了却说找不到缓存、分区、大小写SD卡启动时BootROM报找不到BOOT.BIN但文件明明就在卡里。遇到这种情况按三个方向排查第一Windows拷贝缓存问题。拷贝完直接拔卡文件可能还留在写缓存里。务必安全弹出。第二BootROM对FAT32分区表的识别兼容性。用diskpart或第三方工具格式化过的卡有时分区起始位置和扇区对齐不符合BootROM预期。稳妥做法是用SD Card Formatter工具重新格式化。第三文件名大小写。BOOT.BIN在BootROM的查找逻辑里不区分大小写但有些老版本BootROM或者经过修改的第三方FSBL对大小写敏感。统一用大写BOOT.BIN最省心。还有一个不常见但真实存在的坑如果你用的SD卡之前烧录过Raspberry Pi的镜像或者其他Linux整卡镜像分区表里可能存在多个分区或残留的GPT备份头导致BootROM读取时解析错乱。这种情况用SD Card Formatter做一次全卡覆盖格式化就能解决。最后再分享一个我自己的习惯每拿到一块新板子我不会急着跑大项目而是先把boot mode拨码的所有组合都试一遍每次上电后用md.l 0xF800025C 1读寄存器的值并记录下来。这样到真正做双启动切换时哪些组合在板上对应什么模式我心里早就有底了不用临时去翻原理图。这种看似笨拙的做法在后来好几个项目里都帮我省下了大量排查时间。