4. Zynq启动链与BOOT.BIN构建

Zynq-7000 的启动过程与纯 ARM 处理器有本质区别:上电后最先运行的并不是 U-Boot,而是片内 BootROM。BootROM 根据 MIO[5:2] 等启动模式引脚决定从 QSPI、SD、NAND 还是 JTAG 取镜像,然后从启动设备中读取一个被称为 BOOT.BIN 的引导镜像。BOOT.BIN 不是单一程序,而是一个由 bootgen 按 BIF 描述文件拼装出来的多分区容器,里面通常依次包含 FSBL、可选的 PMU 固件(PMUFW)、ATF(ARM Trusted Firmware)、U-Boot 以及 PL 的 bitstream。理解这条链的先后顺序、各自职责和打包约束,是 Zynq Linux BSP 工程能否顺利启动的关键。

启动链上各阶段的职责

BootROM 是固化在芯片内的只读代码,它只做最基础的工作:采样启动模式、初始化必要的时钟与 MIO、从所选启动设备读取 Boot Header,然后根据头部信息把第一个分区(通常是 FSBL)搬到 OCM(On-Chip Memory)并跳转执行。BootROM 本身不识别文件系统,它按裸扇区或裸地址读取,因此 BOOT.BIN 在 SD 卡上必须放在未分区区域或 FAT 分区起始处,在 QSPI 上必须从 0x000000 开始烧写。

FSBL(First Stage Boot Loader)是启动链中第一个由用户提供的程序,由 Xilinx 提供的 FSBL 源码经 Vitis 或 SDK 编译得到。它承担四件事:完成 PS 侧的 DDR、时钟、MIO 等初始化;按 BIF 描述依次把后续分区搬到 DDR 指定地址;若镜像中包含 bitstream,则调用 PCAP 接口完成 PL 配置;最后把控制权交给 ATF 或 U-Boot。FSBL 是唯一能直接操作 PCAP 完成 PL 配置的阶段,因此 bitstream 在 BOOT.BIN 中的位置必须排在 FSBL 之后、U-Boot 之前。

PMUFW(Platform Management Unit Firmware)运行在 PMU 这个独立微控制器上,负责电源域、隔离、复位和部分时钟管理。当设计中使用 PMU 相关功能(如动态电源管理、PL 电源域控制)时,PMUFW 必须作为独立分区打包进 BOOT.BIN,通常紧跟在 FSBL 之后。若镜像缺少 PMUFW 而 U-Boot 或 ATF 又依赖 PMU 服务,启动会在早期挂起,表现为串口无输出或停在 “PMU firmware not found”。

ATF(ARM Trusted Firmware)在 Zynq 上以 BL31 形式存在,提供 PSCI 服务,负责 CPU 热插拔、二级 CPU 上电和系统级电源管理。Linux 内核通过 PSCI 调用启动从核,因此多核 SMP 配置下 ATF 是必需的。ATF 通常由 FSBL 加载到 DDR,再由 U-Boot 在启动内核前保留并跳转。U-Boot 则是大家最熟悉的阶段,负责加载内核、设备树和根文件系统,最终引导 Linux。

BIF 描述与 bootgen 打包

BOOT.BIN 的生成依赖 BIF(Boot Image Format)文件。BIF 用简洁的语法描述每个分区的源文件、目标地址和属性。下面是一个典型的 BIF 示例,包含 FSBL、PMUFW、ATF、U-Boot 和 bitstream:

// zynq_boot.bif
the_ROM_image:
{
    [bootloader] fsbl.elf
    pmufw.elf
    bl31.elf
    u-boot.elf
    system.bit
}

其中 [bootloader] 标记告诉 bootgen 第一个分区是 FSBL,BootROM 会据此识别。各分区的加载地址由 ELF 文件自身的段地址决定,bootgen 会解析 ELF 并生成对应的分区头。bitstream 分区会被 FSBL 识别为 PL 配置数据,通过 PCAP 写入。若使用加密或认证启动,还需在 BIF 中加入 [auth_params]、[encryption] 等属性并配合密钥文件。

打包命令非常直接:

bootgen -image zynq_boot.bif -arch zynq -o BOOT.BIN -w on

-arch zynq 指定目标架构,-w on 表示覆盖已有输出。执行后 bootgen 会打印每个分区的偏移、大小和校验信息,这些信息是排查启动问题的第一手依据。若某个 ELF 的入口地址与 FSBL 预期不符,bootgen 会报错或产生无法启动的镜像,因此务必核对各阶段的链接地址。

QSPI 与 SD 启动验证

SD 启动验证最方便:把 BOOT.BIN 复制到 FAT32 分区根目录,插入卡槽,设置 MIO[5:2] 为 SD 模式,上电后串口应依次打印 FSBL、PMUFW、ATF、U-Boot 的版本信息。若卡在 FSBL 之前,优先检查 BOOT.BIN 是否放在分区起始处以及启动模式引脚是否正确。QSPI 启动则需要先用 JTAG 或 U-Boot 的 sf 命令把 BOOT.BIN 烧写到 Flash 的 0x000000 偏移,再切换启动模式复位。QSPI 验证时常见问题是烧写偏移错误或 Flash 型号与 QSPI 控制器时序不匹配,表现为 BootROM 读不到有效头部。

验证顺序建议:先用 JTAG 加载 FSBL 确认 DDR 初始化正常,再验证 SD 启动,最后固化到 QSPI。这样能把问题定位在单一环节,避免同时引入多个变量。
Zynq-7000 启动链与 BOOT.BIN 分区布局 BootROM 片内固化 · 采样MIO FSBL DDR/时钟初始化 PMUFW 电源/隔离管理 ATF (BL31) PSCI · 多核启动 U-Boot 加载内核/设备树 Linux Kernel 根文件系统 Bitstream PCAP 配置 PL PCAP BOOT.BIN 分区容器(bootgen 按 BIF 拼装) FSBL bootloader PMUFW pmufw.elf ATF bl31.elf U-Boot u-boot.elf Bitstream system.bit offset 0x0 紧随FSBL DDR加载 DDR加载 PCAP配置 bootgen -image zynq_boot.bif -arch zynq -o BOOT.BIN -w on 启动模式 MIO[5:2] 选择 SD QSPI 分区顺序约束:FSBL 必须为首个分区;bitstream 必须在 FSBL 之后、U-Boot 之前

常见启动故障与定位思路

启动失败时,串口输出是最重要的线索。若完全无输出,问题通常在 BootROM 阶段:启动模式引脚错误、BOOT.BIN 未放在正确偏移、Flash 未烧写或 QSPI 时序不匹配。若打印了 FSBL 版本后停止,多半是 DDR 初始化失败或后续分区地址冲突,可用 JTAG 连接后查看 FSBL 的 Status 变量。若 FSBL 通过但 U-Boot 未启动,检查 ATF 是否缺失、PMUFW 是否被正确加载。若 U-Boot 启动但内核挂起,则问题已转移到内核与设备树,与 BOOT.BIN 无关。

一个实用的排查习惯是:每次只改动一个分区或一个配置项,重新打包并记录 bootgen 输出的分区偏移表。把偏移表与 FSBL 源码中的加载地址对照,能快速发现地址重叠或越界。对于 QSPI 启动,还可以用 U-Boot 的 sf probe 和 sf read 命令读回 Flash 内容,与本地 BOOT.BIN 做二进制比对,确认烧写完整。

bitstream 分区会显著增大 BOOT.BIN 体积。若 QSPI 容量紧张,可考虑把 bitstream 放到 U-Boot 阶段由 fpga loadb 单独加载,而不是打包进 BOOT.BIN。两种方式对启动时间和 Flash 占用的影响不同,需按产品需求权衡。