4. Zynq启动与裸机/ Linux运行环境
Zynq 的启动过程与纯 FPGA 或纯 ARM 处理器都不同:它是一颗把 PS(Processing System,双核 Cortex-A9 及外设)和 PL(Programmable Logic)集成在同一芯片上的 SoC。上电后,PS 先接管启动,再由软件决定何时、以何种方式配置 PL。理解这条启动链,是后续做软硬件协同加速的前提——因为加速器 IP 只有在 PL 被正确配置、驱动被正确加载之后,才能被 Linux 或裸机程序访问。
一、启动链:BootROM → FSBL → 用户程序
Zynq 上电或复位后,最先运行的是芯片内部固化的 BootROM。BootROM 根据启动模式引脚(MIO[5:2] 等)决定从哪里读取启动镜像,常见来源包括 QSPI Flash、SD 卡、NAND、JTAG。它只做一件很有限的事:从所选介质中读取 Boot Header,找到 FSBL(First Stage Boot Loader)并把它搬到 OCM(On-Chip Memory)中执行。
FSBL 是用户可修改的第一段代码,通常由 Xilinx 的 FSBL 模板生成。它承担四项关键任务:初始化 DDR 控制器与时钟、把 PL 的比特流写入 PCAP 完成 PL 配置、把第二阶段程序(裸机 ELF 或 U-Boot)搬到 DDR、最后跳转执行。因此 FSBL 是“PS 启动”和“PL 生效”之间的桥梁。
把 FSBL、PL 比特流、U-Boot(或裸机 ELF)按 Boot Header 规定的格式打包,就得到 BOOT.BIN。它不是一个普通文件,而是带启动头、可被 BootROM 解析的镜像。用 Xilinx 的 bootgen 工具配合 .bif 文件即可生成:
// boot.bif 示例
the_ROM_image:
{
[bootloader] fsbl.elf
system.bit
u-boot.elf
}
// 生成命令
bootgen -image boot.bif -arch zynq -o BOOT.BIN -w on
其中 [bootloader] 标记的必须是 FSBL,system.bit 是 PL 比特流,u-boot.elf 是第二阶段。若做裸机开发,把 u-boot.elf 换成应用 ELF 即可。BOOT.BIN 写入 SD 卡第一个 FAT 分区或烧入 QSPI,即可脱离 JTAG 独立启动。
二、裸机运行环境
裸机(Bare-metal)环境下没有操作系统,程序直接运行在硬件之上。典型工程由 FSBL + 应用 ELF 组成,或直接用 JTAG 加载。裸机访问 PL 中的加速器 IP,通常通过 AXI-Lite 寄存器读写或 AXI-Stream DMA 完成。例如一个自定义 IP 映射到 0x43C00000,裸机代码可直接用指针访问:
#define ACCEL_BASE 0x43C00000
#define CTRL_REG (ACCEL_BASE + 0x00)
#define STATUS_REG (ACCEL_BASE + 0x04)
// 启动加速器
*(volatile uint32_t*)CTRL_REG = 0x1;
// 轮询完成标志
while ((*(volatile uint32_t*)STATUS_REG & 0x2) == 0);
裸机的优点是延迟低、无调度开销,适合确定性要求高的场景;缺点是缺少文件系统、网络协议栈和内存管理,复杂应用开发成本高。
三、Linux 运行环境与设备树
当应用需要网络、文件系统、多任务时,通常选择 Linux。Zynq 上运行 Linux 需要三样东西:内核镜像(zImage 或 Image)、设备树(Device Tree)、根文件系统(rootfs)。U-Boot 负责把这三者从存储介质加载到 DDR 并启动内核。
设备树是 Linux 描述硬件的关键机制。它用文本(.dts/.dtsi)描述 CPU、内存、外设地址、中断、时钟、GPIO 等信息,编译成 .dtb 后由内核解析。对于 PL 中的自定义加速器 IP,必须在设备树中新增节点,否则驱动无法匹配。例如:
// 在 pl.dtsi 中描述自定义加速器
accelerator_0: accelerator@43c00000 {
compatible = "mycompany,accel-1.0";
reg = <0x43c00000 0x10000>;
interrupts = <0 29 4>;
clocks = <&clk_100M>;
};
其中 compatible 是驱动匹配的字符串,reg 是寄存器基址与长度,interrupts 描述中断号与触发类型。内核启动时,平台总线会根据 compatible 找到对应驱动并调用 probe 函数。
四、PetaLinux 与驱动加载
PetaLinux 是 Xilinx 提供的 Linux 开发套件,它把内核、U-Boot、设备树、rootfs 的配置与构建流程封装成命令行工具。典型流程如下:
- 用
petalinux-create -t project创建工程; - 用
petalinux-config --get-hw-description导入 Vivado 导出的 .xsa,自动生成设备树与硬件配置; - 用
petalinux-config -c kernel裁剪内核,petalinux-config -c rootfs选择 rootfs 包; - 用
petalinux-build编译,petalinux-package --boot生成 BOOT.BIN 与 image.ub。
驱动加载有两种方式:编译进内核(built-in)或编译成模块(.ko)后动态加载。对于加速器驱动,若希望开机即用,可设为 built-in;若需灵活调试,可编译为模块,用 insmod 或 modprobe 加载。加载后通过 dmesg 查看 probe 是否成功,通过 ls /dev 或 ls /sys/class 确认设备节点是否生成。
五、运行时环境搭建要点
无论裸机还是 Linux,运行时环境搭建都要关注以下几点:
- DDR 初始化:FSBL 中根据 DDR 型号配置时序参数,参数错误会导致启动失败或运行不稳定;
- 时钟与复位:PL 的时钟来源、复位信号需在 Vivado 中正确连接,并在设备树或裸机代码中使能;
- 中断:Linux 下需确认 GIC 中断号与设备树一致,裸机下需配置 GIC 与中断服务程序;
- 缓存一致性:PS 与 PL 通过 AXI 交互时,DMA 缓冲区需处理 cache 一致性,Linux 下用
dma_alloc_coherent,裸机下用Xil_DCacheFlush等; - 启动介质:量产用 QSPI,调试用 SD 卡或 JTAG,切换时注意启动模式引脚与 BOOT.BIN 位置。
一个可复现的验证方法是:先用 JTAG 加载 FSBL 与裸机程序,确认 PL 配置成功、加速器寄存器可读写;再切换到 SD 卡启动 Linux,用 devmem 或自定义驱动读写同一寄存器,确认软硬件路径一致。这样就把“启动链—设备树—驱动—运行时”串成了一条可验证的闭环。