4. Zynq启动流程与引导镜像
Zynq-7000 的启动过程与纯 ARM 处理器最大的不同,在于它必须先把 PL(可编程逻辑)的配置数据也纳入引导链。上电后,芯片内部固化的 BootROM 先运行,它不依赖任何外部存储器控制器初始化代码,只使用芯片内部的 OCM(On-Chip Memory)和少量外设,从启动模式引脚决定的介质(QSPI、NAND、SD/eMMC、JTAG)中读取引导头,并把第一阶段引导程序 FSBL 载入 OCM 执行。因此,理解 Zynq 启动的关键,是理解“谁加载谁、每一级运行在什么存储空间、PL 配置在哪一级完成”。
启动链的五个阶段
完整的启动链可以概括为:BootROM → FSBL → PL 配置 → SSBL(通常是 U-Boot)→ Linux 内核与根文件系统。BootROM 只做最基础的工作:读取 Boot Header,校验并搬运 FSBL 到 OCM,然后跳转执行。FSBL 是用户可定制的第一阶段引导程序,它承担三项核心任务:初始化 DDR 控制器和时钟、根据比特流文件配置 PL、加载下一阶段镜像(U-Boot 或直接加载内核)。PL 配置既可以由 FSBL 完成,也可以延后到 U-Boot 阶段通过 FPGA 管理器完成,这取决于设计对 PL 就绪时间的要求。SSBL 即 U-Boot,运行在 DDR 中,负责加载 Linux 内核、设备树和根文件系统。最后内核挂载根文件系统,启动用户空间 init 进程。
BOOT.BIN 的组成与生成
BOOT.BIN 是 BootROM 直接读取的镜像,它由 Boot Header、FSBL 可执行文件以及可选的 PL 比特流组成。Boot Header 中记录了 FSBL 的起始地址、长度、校验和以及目标执行地址等关键信息,BootROM 依据这些字段完成搬运和校验。生成 BOOT.BIN 通常使用 Xilinx SDK/Vitis 中的 Create Boot Image 工具,或命令行工具 bootgen。一个典型的 BIF(Boot Image Format)文件如下:
// boot.bif
the_ROM_image:
{
[bootloader] fsbl.elf
system.bit
u-boot.elf
}
执行 bootgen -image boot.bif -o BOOT.BIN -w on 即可生成 BOOT.BIN。其中 [bootloader] 标记的 fsbl.elf 会被 BootROM 首先加载;system.bit 是 PL 配置数据,若不需要 FSBL 配置 PL,可将其移除并在 U-Boot 中通过 fpga load 命令加载;u-boot.elf 作为第三阶段镜像被 FSBL 搬运到 DDR 并跳转执行。需要注意,BIF 中分区的顺序决定了加载顺序,Boot Header 始终位于镜像最前端。
image.ub 的组成与生成
image.ub 是 U-Boot 使用的 FIT(Flattened Image Tree)格式镜像,它把内核、设备树和根文件系统打包在一个文件中,并附带校验信息。FIT 镜像的源描述文件是 .its(Image Tree Source),典型内容如下:
// image.its
/dts-v1/;
/ {
description = "Zynq Linux FIT Image";
images {
kernel {
description = "Linux Kernel";
data = /incbin/("Image");
type = "kernel";
arch = "arm";
os = "linux";
compression = "none";
load = <0x00008000>;
entry = <0x00008000>;
hash { algo = "sha256"; };
};
fdt {
description = "Zynq Device Tree";
data = /incbin/("system.dtb");
type = "flat_dt";
arch = "arm";
compression = "none";
hash { algo = "sha256"; };
};
ramdisk {
description = "Root Filesystem";
data = /incbin/("rootfs.cpio.gz");
type = "ramdisk";
arch = "arm";
os = "linux";
compression = "gzip";
hash { algo = "sha256"; };
};
};
configurations {
default = "conf";
conf {
kernel = "kernel";
fdt = "fdt";
ramdisk = "ramdisk";
};
};
};
使用 mkimage -f image.its image.ub 即可生成 image.ub。U-Boot 启动时通过 bootm 命令解析 FIT 镜像,根据 configurations 节点选择内核、设备树和根文件系统,并完成校验后跳转内核入口。若根文件系统存放在 SD 卡或 eMMC 的独立分区中,则 image.ub 中可只包含内核和设备树,根文件系统由内核命令行 root=/dev/mmcblk0p2 指定。
启动介质与镜像布局
以 SD 卡启动为例,典型的分区布局是:FAT32 分区存放 BOOT.BIN 和 image.ub,ext4 分区存放根文件系统。BootROM 从 SD 卡的 FAT 分区读取 BOOT.BIN,FSBL 配置 PL 并加载 U-Boot,U-Boot 从同一 FAT 分区读取 image.ub,解压内核并挂载 ext4 分区中的根文件系统。若使用 QSPI Flash 启动,BOOT.BIN 通常烧录在 Flash 起始地址,image.ub 可存放在后续偏移地址,U-Boot 通过 sf read 命令读取。启动模式由 MIO[5:2] 引脚在上电复位时采样决定,设计硬件时必须确保这些引脚有明确的上拉或下拉。
ps7_init() 和 FsblHookBeforeBitstreamDownload()。若 PL 配置失败,FSBL 会通过串口输出错误码,常见原因包括比特流文件与器件型号不匹配、PCAP 时钟未使能、或比特流未包含在 BOOT.BIN 中。
常见问题与判断依据
启动失败时,串口输出是首要判断依据。若 BootROM 阶段无任何输出,应检查启动模式引脚、BOOT.BIN 是否烧录正确、Boot Header 校验和是否有效。若 FSBL 输出 “FSBL Status = 0xA001” 之类的错误码,通常指向 DDR 初始化失败或比特流加载失败。若 U-Boot 能启动但无法加载内核,应检查 image.ub 是否存在于 FAT 分区、FIT 镜像的 load 地址是否与内核链接地址一致、设备树是否匹配硬件。若内核启动后卡在 “Waiting for root device”,则需检查根文件系统分区号、文件系统类型以及内核命令行参数是否正确。
掌握这条启动链后,实际开发中就可以按阶段定位问题:BootROM 阶段看启动模式和 BOOT.BIN,FSBL 阶段看 DDR 初始化和 PL 配置,U-Boot 阶段看镜像加载和启动参数,内核阶段看设备树和根文件系统挂载。每一级的输出和错误码都是下一级能否正常执行的前提,逐级排查比盲目重新生成镜像更高效。