ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

ZYNQ Multiboot机制详解:软件复位与程序跳转实现在线升级与回滚

ZYNQ Multiboot机制详解:软件复位与程序跳转实现在线升级与回滚 做ZYNQ这行的都知道程序跑死人容易但要让系统在跑飞、死机、升级失败之后还能自己“翻回来”才是最考验设计功底的部分。这篇文章想聊的就是ZYNQ里“软件复位重启”和“程序跳转”这一整套玩法核心就是Xilinx的Multiboot机制。不管你是要做Bootloader在线升级还是想让系统死机后能自动回滚到出厂固件都绕不开这几个知识点BootROM怎么从固定地址读镜像、FSBL怎么改Multiboot寄存器、软复位之后到底从哪里启动。我尽量把这里面的原理、寄存器、代码和踩坑经历一次性讲透。这篇文章适合正在做ZYNQ裸机开发、嵌入式Linux启动定制、以及远程升级方案的工程师。内容会涉及FSBL、U-Boot、PetaLinux、QSPI/NAND/SD启动也会把Multiboot回滚的实现方式拆开讲。读完你至少能自己搭一套“双镜像在线升级”的框架而不是停留在跑通Demo的阶段。1. 先说清楚ZYNQ的启动流程和Multiboot到底解决什么问题1.1 从BootROM到FSBLZYNQ上电后的第一段旅程ZYNQ-7000上电后片内的ARM Cortex-A9双核还处于复位状态真正第一个执行代码的是片内BootROM。BootROM翻译过来就是“只读启动程序”它固化在芯片里用户改不了。它的任务是根据MIO引脚上的启动模式选择信号去QSPI Flash、NAND、NOR、SD卡或JTAG接口上找一份合法的启动镜像。对QSPI和NAND这种串行启动设备而言BootROM会去设备起始地址扫描一段数据判断开头有没有合法的Boot Header。Boot Header里的第一个字段是魔术字固定为0xAA995566。如果这个魔术字对不上BootROM会继续往下一个搜索地址找直到找到合法镜像或者搜索失败。这个“自动搜索”的行为是整个Multiboot机制能够成立的最底层基础。找到合法Boot Header后BootROM会解析后面跟着的寄存器初始化表、分区信息表然后加载FSBLFirst Stage Boot Loader到片上存储器执行。FSBL承担两件事一是初始化PS端的DDR、PLL、MIO、CLK把整个软硬件环境带起来二是读取启动镜像头部里的分区表把PL bitstream、U-Boot或者裸机APP依次加载到指定位置最后跳转过去。所以你看ZYNQ的启动不是“点一下电源就运行Linux”这么简单它是一层套一层的接力跑。BootROM传给FSBLFSBL传给U-BootU-Boot再传内核中间任何一环出了问题系统就会卡住。而Multiboot这个机制就是让我们能在这条接力链的起点处做文章让BootROM不去默认地址找镜像而是跳到我们指定的另一个地址去启动另一份镜像。1.2 Multiboot、软件复位、程序跳转三者的关系Multiboot这个词在Xilinx官方文档中的含义是“多启动”。它不是一个软件而是一套由BootROM硬件行为、寄存器配置、镜像布局共同组成的机制。核心思想是系统复位后BootROM并不是死板地只读Flash的0地址它会先检查一个叫做MULTIBOOT的寄存器如果这个寄存器里被写入了合法的Magic Number和偏移BootROM就会从那个偏移位置去读取镜像。软件复位和程序跳转这两件事和Multiboot是天然绑定的。比如你做在线升级新固件下载到了Flash的0x200000偏移处想让设备下次启动时从新固件跑就必须做两步操作第一步把0x200000这个偏移按指定格式写入MULTIBOOT寄存器第二步触发一次软件复位。复位的目的是让BootROM重新开始干活重新读取MULTIBOOT寄存器的值并从新偏移启动。所以软件复位在这里更像是一个“触发器”程序跳转的目标位置则通过MULTIBOOT寄存器提前设定好。很多初学者会把“程序跳转”理解成普通的函数调用或者跳转指令在ZYNQ的Bootloader场景里这是个误区。我们说的跳转是“换一个镜像跑”是整机级别的重启而不是在同一个程序里跳到某个地址继续跑。搞清楚了这一点后面理解看门狗恢复、回滚机制就容易多了。2. Multiboot的硬件机制BootROM、Boot Header和那几个关键寄存器2.1 Boot Header里藏着什么0xAA995566后面的内容ZYNQ的启动镜像不只是一段纯粹的机器码最前面有一块固定长度的头部叫做Boot Header前0x8C0字节左右都有固定格式。第一个word是0xAA995566叫Magic Word。后面的字段包括镜像长度、源地址、目标地址、执行地址、偏移值、寄存器初始化表、安全标志、分区头信息等。BootROM拿到镜像后会按这些字段完成三件事一是把FSBL从源地址搬到RAM目标地址二是根据需要初始化一部分PL侧的寄存器三是根据数据头里的信息决定跳转地址和执行方式。因此在做Multiboot时我们不能只关注“在哪个偏移放了镜像”还要确保每份镜像的Boot Header都是通过Bootgen正确生成的里面各个字段对齐、长度计算正确。否则即使BootROM找到了镜像也可能加载失败表现为启动卡死或者进入不可预料的异常状态。一个容易踩坑的地方是镜像偏移对齐。BootROM搜索镜像的粒度并不是1字节QSPI的搜索步长通常是32字节的倍数这也是为什么Multiboot寄存器的偏移值要按照32字节为单位折算。NAND启动的搜索步长还会更大通常以块对齐。如果镜像放在一个不是32字节对齐的偏移上写再多的寄存器也没用。2.2 MULTIBOOT、REBOOT_STATUS和复位生成器软件跳转的三块跳板Xilinx在PS端的DevCDevice Configuration模块里提供了两个关键寄存器REBOOT_STATUS地址是0xF8007028MULTIBOOT地址是0xF800702C。这两个寄存器的行为比较特殊它们不是普通的用户寄存器BootROM会参与读写。MULTIBOOT寄存器的用法是系统复位后BootROM检查这个寄存器的值看高16位是不是0xBEEF如果是就取低16位的值乘以32作为本次启动所需镜像的Flash偏移地址。写成C语言就是#define MULTIBOOT_REG 0xF800702C #define REBOOT_STATUS_REG 0xF8007028 void set_multiboot_offset(unsigned int byte_offset) { unsigned int reg_val (0xBEEF 16) | (byte_offset 5); Xil_Out32(MULTIBOOT_REG, reg_val); }注意这里的(byte_offset 5)相当于除以32。如果你想把启动位置切到Flash的0x200000偏移那低16位算出来是0x10000整体写进寄存器的值就是0xBEEF0000 | 0x10000 0xBEEF10000不是要按16位截断实际是0xBEEF0000 | 0x00010000这里推演时要特别留神。正确的操作是把低16位填充成偏移除以32的值由于偏移0x200000除以32等于0x10000已经超过16位范围这本身就说明该偏移对Multiboot来说是不合法的偏移量不能超过2MB。Multiboot能覆盖的Flash偏移上限是0x1FFFFF2MB减1。更大范围的情况往往需要先从一个小的跳板镜像再由跳板镜像做二次跳转这个后面讲在线升级架构时再说。REBOOT_STATUS寄存器的作用是记录复位状态和BootROM的尝试次数。FSBL可以在启动时读取它判断当前是从正常启动路径来的还是因为上一次启动失败被BootROM重新捞回来的。一旦检测到多次启动失败FSBL就应该执行回滚策略把MULTIBOOT寄存器重新指回出厂Golden分区避免设备反复在损坏的Update分区上死循环。第三块跳板是PS端的复位发生器。ZYNQ的SLCRSystem Level Control Register模块负责产生系统级复位。触发软件复位前需要先往SLCR的解锁寄存器写入解锁密钥0xDF0D然后往PSS_RST_CTRL寄存器写入软复位标志。示例代码如下#define SLCR_UNLOCK_REG 0xF8000008 #define SLCR_LOCK_REG 0xF800000C #define PSS_RST_CTRL_REG 0xF8000200 #define SLCR_UNLOCK_KEY 0xDF0D void soft_reset_ps(void) { Xil_Out32(SLCR_UNLOCK_REG, SLCR_UNLOCK_KEY); Xil_Out32(PSS_RST_CTRL_REG, 0x1); while (1) { /* 等待复位到来 */ } }写这个寄存器前不解锁SLCR是没效果的很多人第一次调跳转时只写了复位寄存器系统没反应折腾半天才发现少了解锁这一步。2.3 Flash启动布局设计Golden与Update分区怎么摆Multiboot应用得最多的场景就是双镜像在线升级。一般把Flash从物理上分成两个大区Golden区出厂固件区和Update区升级固件区。Golden区放在低地址Update区放在高地址。分区设计要满足几个原则Golden区必须包含完整的FSBL、Bitstream和U-Boot保证设备出厂后第一次上电就能用。Update区大小至少等于Golden区否则新固件放不下。分区之间至少留一点空白防止镜像长度计算错误时发生越界覆盖。如果Flash空间充足建议在Update区后面再留一个备份区用于存放升级前未覆盖的旧固件。基于2MB的Multiboot偏移限制如果分区分得比较大比如Update区放在8MB偏移就不能直接靠BootROM一步跳到Update区。实践中常见的做法是设置一个位于2MB以内的Bootloader Agent跳板程序它本身是一份很小的镜像启动后负责再检查Update分区的合法性然后往MULTIBOOT寄存器写入更大的偏移再次复位跳转。这样虽然多了一次复位但突破了2MB的寻址限制。3. 软件复位与程序跳转的六种实现方式3.1 最直接的FSBL层跳转改MULTIBOOT寄存器再软复位如果跳转动作发生在FSBL阶段最简单可靠的方式就是先写MULTIBOOT寄存器再写复位寄存器。这个方案在裸机Bootloader里非常常用因为此时操作系统还没起来APU处于单核裸跑状态不用担心其他核的干扰。FSBL源码中要做的事情可以抽象成下面几个步骤/* 1. 根据需要跳转的Flash偏移设置Multiboot */ set_multiboot_offset(0x80000); /* 2. 关闭D-Cache避免复位瞬间脏数据回写异常 */ Xil_DCacheDisable(); Xil_ICacheDisable(); /* 3. 清空并禁止中断 */ Xil_Out32(0xF8F00214, 0x1); /* 屏蔽全局中断的一种方式 */ /* 4. 触发软复位 */ soft_reset_ps();需要注意的是触发软复位之前最好把D-Cache关掉否则在复位瞬间Cache里未写回的数据可能会造成启动后新镜像运行环境不干净。FSBL链路上还有一个细节很多老工程师会在跳转前把MULTIBOOT寄存器再清除一次防止下次意外上电时又从旧路径启动但这个操作也要具体场景具体分析如果你的跳板逻辑依赖复位后的寄存器状态清之前要算好逻辑顺序。3.2 利用看门狗复位实现异常恢复在设备运行过程中突然死机CPU已经没法执行正常的跳转代码了这时候该用什么方式切换启动路径答案就是看门狗Watchdog Timer。ZYNQ-7000的PS端有专用的看门狗定时器超时后会产生复位信号让系统整体回到BootROM。看门狗的坑在于它本身不懂什么Multiboot只是完成“复位”这个动作。复位之后BootROM还会不会按你的意愿去启动Update镜像取决于复位前MULTIBOOT寄存器里的值。所以设计看门狗方案时需要把MULTIBOOT寄存器的写入时机提前。举个例子你希望这次运行如果死机重启后就进入诊断程序那么在正常运行时就应该先把MULTIBOOT指向诊断分区再去喂看门狗。一旦死机看门狗复位BootROM自动进入诊断模式。Xilinx官方提供过基于REBOOT_STATUS计数回滚的参考设计思路是FSBL启动时检查REBOOT_STATUS的值如果连续重启次数超过阈值就认为当前镜像有问题自动清除Multiboot偏移回滚到Golden镜像。这套逻辑配合看门狗才能做到“死机后不用人工干预自动恢复出厂状态”。网上很多人吐槽“单片机死机后软件看门狗需要多次复位才能起来”多半就是踩了这个坑——只写了看门狗复位没在FSBL里做复位原因判断结果反复在坏固件里重启。3.3 U-Boot命令行里的跳转玩法如果系统已经跑到U-Boot阶段跳转选择就灵活多了。U-Boot本身支持从Flash加载多个镜像环境变量里可以配置不同的启动命令。一个常见做法是使用U-Boot的bootm命令直接从内存地址启动FIT格式镜像setenv loadaddr 0x2000000 setenv bootargs consolettyPS0,115200 root/dev/mmcblk0p2 rw fatload mmc 0:1 ${loadaddr} image.ub bootm ${loadaddr}如果要实现多个分区切换可以通过修改U-Boot的环境变量bootcmd实现。比如设定两套启动命令一套从QSPI读取一套从SD读取通过按键或外部引脚选择。U-Boot阶段跳转的优点是调试方便不用重新编译FSBL缺点是U-Boot本身要依赖FSBL已经初始化DDR和时钟如果Flash里的Bitstream加载有问题U-Boot可能根本起不来。U-Boot还支持go命令直接从内存某个地址执行裸机程序。这个命令适合在调试阶段快速跳转到某个测试程序但正式产品不建议依赖它因为go跳转后U-Boot占用的外设和中断环境没有完全清理裸机程序很容易踩到残留状态。3.4 Linux用户态触发跳转升级在线升级的场景里大部分时候应用跑在Linux系统里升级动作需要由用户的应用程序触发。此时可以写一个简单的Linux内核模块把物理地址映射到内核虚拟地址然后操作MULTIBOOT寄存器和SLCR复位寄存器。内核模块的核心逻辑如下#include linux/module.h #include linux/io.h #include linux/fs.h #include linux/uaccess.h #define MULTIBOOT_REG 0xF800702C #define SLCR_UNLOCK 0xF8000008 #define PSS_RST_CTRL 0xF8000200 static void __iomem *multiboot_base; static void __iomem *slcr_base; static int trigger_jump(char *str) { unsigned int offset; void __iomem *reg; kstrtou32(str, 0, offset); multiboot_base ioremap(MULTIBOOT_ADDR, 4); slcr_base ioremap(SLCR_UNLOCK, 0x200); /* 1. 设置multiboot目标 */ reg multiboot_base; iowrite32((0xBEEF 16) | (offset 5), reg); /* 2. 解锁SLCR */ reg slcr_base; iowrite32(0xDF0D, reg); iowrite32(0x1, reg (PSS_RST_CTRL - SLCR_UNLOCK)); return 0; }这类模块在正式产品里需要注意一定不能随意暴露给普通用户最好通过文件节点的权限控制只允许特定进程写入。复位操作一旦触发整个系统立即重启任何未保存的数据都会丢失所以应用层必须在上层做好升级状态记录和文件系统同步比如把“升级准备完成”的标志写入一个单独的存储分区重启后再由Bootloader读取。3.5 PLFPGA侧发起复位跳转有些系统希望由PL逻辑来控制整个升级流程比如PL里跑了一个软核处理器或者PL检测到外部信号后要求PS切换镜像。ZYNQ提供了多种PL到PS的交互途径常见的有AXI GP接口和EMIO。思路比较简单FSBL在启动时把PS端的关键寄存器映射地址通过AXI接口开放给PLPL可以往这些地址写入复位命令。更常见的做法是PL先把升级目标偏移写入一个固定的BRAM或寄存器然后通过中断通知PSPS收到中断后再执行正常的Multiboot跳转。这种方式的好处是职责清晰PL只负责决策PS负责执行复位。使用EMIO引脚也可以实现类似效果。例如PL输出一个脉冲给PS的一个MIO复用引脚PS端中断或轮询检测到这个脉冲后启动软复位流程。这种方案简单但要注意信号抖动和电平匹配实际项目里建议加一个简单的按键消抖逻辑防止把毛刺当成跳转信号。3.6 裸机程序中的纯函数跳转不加复位直接跳如果只是想在同一个程序里从一个裸机APP跳到另一个裸机APP且目标程序已经加载在DDR里可以不使用复位直接修改栈指针和返回地址跳过去。原理是把目标程序的入口地址写到PC把目标程序约定的栈地址写到SP中断向量表也需要切到目标程序的向量表基址。但ZYNQ的裸机跳转远没有单片机那么“简单”。DDR里的缓存一致性、MMU表、中断控制器状态都需要处理稍微一个地方没清理干净目标程序跑起来就是各种诡异异常。我自己做过的项目里除非是两个经过严格约定的裸机程序之间跳转否则不推荐这种方式。从Bootloader跳转到U-Boot时FSBL内部用的就是类似机制但那是经过了仔细设计的跳转流程。4. Bootloader与在线升级系统的完整设计4.1 双镜像在线升级的总体架构一个完整的ZYNQ在线升级系统建议按下面这张逻辑链路来设计出厂固件Golden位于Flash低地址常态应用Update位于Flash高地址或另一个Flash设备上每次启动时FSBL检查升级标志和复位原因如果存在合法Update标志则先校验Update镜像的CRC和Boot Header成功则加载Update失败则回退Golden。我实际项目里实践过的一种稳定架构是三级分区分区0Golden一级镜像包含FSBLBITU-Boot固定0偏移。分区1Jump Agent放在1MB偏移以内是一个极小的裸机程序负责读取分区2里的镜像头并检查分区信息。分区2Update主镜像可以是多Mbytes的完整Linux Image偏移超过2MB也没关系。在线升级时应用程序先把新的分区2写入预留的升级缓冲区域写完后在Jump Agent能读到的位置写一个“升级请求”标志然后软复位。BootROM从Golden启动FSBL识别到升级请求后不直接加载U-Boot而是跳转到Jump Agent由Jump Agent去校验和跳转。这套架构的好处是把复杂逻辑集中在Jump Agent里Golden镜像保持最小、最稳定即使Update分区写坏了也不影响Golden回滚。4.2 制作boot.bin、image.ub与FSBL配置制作Multiboot镜像离不开Bootgen工具。通常先用Vivado/Vitis编译出fsbl.elf和硬件描述文件然后使用Bootgen按BIF文件规则打包。一个典型的BIF文件长这样the_ROM_image: { [bootloader] zynq_fsbl.elf [destination_cpu cortex-a9] u-boot.elf }如果要把PL比特流一起打进BOOT.BIN则在中间加上bitstreamthe_ROM_image: { [bootloader] zynq_fsbl.elf [destination_cpu cortex-a9] system.bit [destination_cpu cortex-a9] u-boot.elf }在Vitis旧版SDK中创建启动镜像时Bootgen会自动为镜像添加Boot Header。但要注意多份镜像各自需要独立的Boot Header也就是说Golden和Update必须分别通过Bootgen打包不能把两个镜像简单拼接在一起然后指望BootROM自己去区分。PetaLinux环境下的流程稍微不一样。PetaLinux构建完成后在images/linux目录下会生成zynq_fsbl.elf、u-boot.elf、image.ub等文件。执行下面的命令可以打包BOOT.BINpetalinux-package --boot --format BIN --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot --output boot.binimage.ub和boot.scr则通常放在SD卡的FAT分区或写进Flash。PetaLinux制作SD卡启动盘时我记得的典型步骤是整个SD卡分成两个分区第一个分区格式化为FAT32存放BOOT.BIN、boot.scr、image.ub第二个分区格式化为ext4放rootfs。很多新手制作SD卡失败无非是分区表类型不对、FAT分区没激活、或者文件系统没有及时sync就拔卡这些问题排查起来都很费时间。4.3 烧写流程和分区管理烧写QSPI或NAND时最稳妥的做法是先用Vivado Hardware Manager通过JTAG加载FSBL再通过Xilinx的Flash Programmer把镜像写入对应Flash偏移或者直接在U-Boot下用sf和nand命令烧写。U-Boot下烧写QSPI的命令示例# 先把要写入的镜像载入内存 tftp 0x2000000 boot.bin # 擦除QSPI偏移0的区域长度按需设定 sf probe sf erase 0x0 0x800000 sf write 0x2000000 0x0 0x800000这里最容易犯的错误是擦除长度没算好。QSPI Flash的扇区大小通常是4KB擦除时如果长度不是扇区的整数倍部分扇区会残留旧数据导致最终镜像在Flash里“拼接”出错启动时出现莫名其妙的CRC错误或Header错位。NAND Flash还有坏块管理的问题U-Boot下烧写NAND时要确认使用的命令是否带坏块检查逻辑否则写进坏块的数据会在启动时静默丢失。另外烧写完以后千万别急着断电建议先读回校验一遍。把Flash读回内存和原始bin文件做哈希对比确认等于再断电。这个步骤在量产导入时尤其重要能避免一批设备带着坏镜像出厂。5. 常见问题与排查技巧实录5.1 Valid FSBL file is required到底卡在哪有段时间总有朋友问我在Vivado/Vitis的Flash Programmer里烧写时报了这么一句A valid FSBL file is required for flash operation。这个报错看起来像是“找不到FSBL文件”实际是烧写工具需要依赖FSBL来初始化DDR和Flash控制器。出现这个报错先检查三件事一是当前工程里有没有编译生成fsbl.elf如果工程只导入了硬件描述文件而没有创建FSBL应用肯定会缺二是fsbl.elf对应的器件型号是否和当前FPGA器件一致复制过来用很容易忽略版本匹配三是Flash Programmer界面的FSBL路径是否正确新版Vitis里路径变了很多老用户想当然地填了个空路径。如果只是单纯想把JEDEC或BIN文件写进Flash完全可以在U-Boot下用命令烧写不依赖JTAG的Flash Programmer。调试阶段我觉得这种方式反而更高效不用每次都打开Vivado图形界面。5.2 跳转后程序跑飞一步步查什么Multiboot设置正确、复位也触发了但新镜像跑起来后就是各种异常先不要怀疑是Multiboot机制的问题而是要按照“启动链”逐层排查。第一步确认镜像自己单独从JTAG或者单分区启动时能正常工作。如果单独启动都有问题那就是镜像本身不合格和Multiboot无关。第二步打印BootROM的搜索偏移。通过在FSBL里读MULTIBOOT寄存器确认BootROM实际是从哪个偏移读到镜像的避免“你以为写的是A分区实际上跳到B分区”的乌龙。第三步检查DDR地址冲突。FSBL把U-Boot加载到DDR但新镜像的入口地址是否和旧镜像加载地址重叠目标程序运行时是否会覆盖自己正在运行的代码段这些都需要在链接脚本里确认。第四步检查Cache和MMU。跳转前建议关闭I-Cache和D-Cache等目标程序自己做好初始化后再重新使能。一个真实的案例某项目把U-Boot从0x08000000加载改为0x10000000结果每次跳转都起不来。查到最后发现FSBL里有一段代码在初始化后继续使用旧的物理地址映射MMU没刷TLB新地址被缓存命中到旧数据整个执行流全部错乱。刷一次TLB、重映射以后问题就消失了。5.3 看门狗需要多次复位才能恢复的坑前面提到的“看门狗需要多次复位”现象在ZYNQ平台上很典型。根源往往不是看门狗本身而是FSBL对复位原因处理得不够细致。推荐的做法是在FSBL启动代码最开始就读取REBOOT_STATUS寄存器把复位原因和计数信息保存到一个全局变量里。逻辑可以这样定如果复位原因是看门狗超时且当前启动计数小于3则正常加载Update分区如果启动计数已经等于3说明Update分区大概率已经损坏这时把MULTIBOOT寄存器清零强制切回Golden分区。下面是伪代码unsigned int status Xil_In32(REBOOT_STATUS_REG); if ((status 0x7) 3) { /* 连续启动失败回滚 */ Xil_Out32(MULTIBOOT_REG, 0x0); }很多设计失败在“过度信任Update分区”。一旦看门狗复位后FSBL又去加载同一个损坏镜像于是看门狗再次超时再次复位无限循环。要跳出循环必须让FSBL具备“失败计数”的能力哪怕只是简单的三次回滚规则都能让设备在几分钟内自动恢复正常。5.4 PetaLinux 2025.1制作SD卡启动卡的细节PetaLinux 2025.1对ZYNQ-7000的支持依旧沿用旧版本的镜像打包逻辑但不少命令输出路径有些变化。制作SD卡时我一般按下面的顺序操作# 构建工程 petalinux-build # 打包BOOT.BIN petalinux-package --boot --format BIN \ --fsbl images/linux/zynq_fsbl.elf \ --fpga images/linux/system.bit \ --u-boot # 检查镜像目录 ls images/linux/生成的BOOT.BIN注意大小写拷到SD卡的FAT分区boot.scr和image.ub也一起拷进去。rootfs解压到第二个ext4分区。实际工程中踩过两个坑第一个是SD卡分区工具选了MBR但FAT分区没标记为active导致U-Boot找不到boot.scr第二个是FAT分区格式化时簇大小选太大导致BOOT.BIN虽然拷进去了但U-Boot读取时出现文件系统错误。建议FAT分区不要小于512MB格式化时使用默认簇大小即可不要为了省空间改装小簇。5.5 NAND Flash选型与BootROM兼容性ZYNQ启动时对NAND Flash并不是全兼容BootROM内部有一份设备ID列表只有ID匹配的NAND设备才会被识别和读取。很多工程师觉得“Linux内核支持什么NANDBootROM就支持什么NAND”这是不对的。Linux支持是一回事BootROM能不能识别是另一回事。选型时直接查UG585的BootROM支持列表重点关注NAND的页大小、块大小、地址周期数、ECC要求等参数。如果选型已经定死了但BootROM不识别解决思路有两个一是用QSPI先启动FSBLFSBL初始化NAND控制器后再从NAND加载应用镜像二是在U-Boot里增加NAND驱动支持通过命令手动读取NAND。这两个方案都需要增加一层逻辑项目周期上要提前留量。NAND烧写时还要注意ECC算法和坏块管理策略这部分单独就能写很长这里只说一个最容易踩的坑很多Flash Programmer默认使用线性地址写NAND完全不管坏块导致量产中出现部分设备启动异常。成熟的方案是让FSBL或U-Boot里的NAND驱动支持坏块跳过像JFFS2/UBIFS文件系统那样在存储层保证数据落盘安全。6. 实际项目中的一点经验总结做ZYNQ升级和复位这一整套方案我个人最大的体会是不要一开始就把逻辑塞进一个复杂的“超级Bootloader”而是把启动链路拆成最小可用单元一层一层验证。先让Golden单分区能跑再把Update分区加上最后才做看门狗回滚。任何一步没验证透就叠加上去后面排查问题会非常痛苦。还有一个容易被忽略的细节是镜像版本记录。建议在Boot Header之外的自定义保留字段里写一个版本号每次生成镜像时更新。FSBL启动时把版本号打出来配合显示在设备外壳上的版本标签能在现场维护时快速判断设备到底跑的是哪份固件。没有这个记录回滚排查时靠猜效率实在太低。最后再分享一个小技巧在办公室里调试Multiboot时别一上来就做远程升级全流程先在U-Boot里手动执行sf write、再从内存跳转一步步确认每一个环节。等手动路径全部验证通过再把操作自动化变成Linux应用脚本。这样可以节省大量打板、重新上电的时间调试节奏会舒服很多。
返回列表