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 独立启动。

Zynq 启动链与 BOOT.BIN 组成 启动介质 QSPI / SD / NAND MIO[5:2] 选择 BootROM 芯片固化 读 Boot Header FSBL 初始化 DDR / 时钟 配置 PL (PCAP) 搬移第二阶段 第二阶段程序 裸机 ELF 或 U-Boot → Linux 内核 / 应用 BOOT.BIN(bootgen 打包) [bootloader] fsbl.elf PL 比特流 system.bit 第二阶段 u-boot.elf / 裸机 ELF 关键点: • FSBL 是唯一能配置 PL 的阶段,PL 比特流必须由 FSBL 写入 PCAP • BOOT.BIN 必须按 bootgen 格式打包,直接拷贝 ELF 无法启动 • 启动模式引脚决定介质,调试时常用 JTAG 或 SD 卡

二、裸机运行环境

裸机(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 的配置与构建流程封装成命令行工具。典型流程如下:

  1. 用 petalinux-create -t project 创建工程;
  2. 用 petalinux-config --get-hw-description 导入 Vivado 导出的 .xsa,自动生成设备树与硬件配置;
  3. 用 petalinux-config -c kernel 裁剪内核,petalinux-config -c rootfs 选择 rootfs 包;
  4. 用 petalinux-build 编译,petalinux-package --boot 生成 BOOT.BIN 与 image.ub。

驱动加载有两种方式:编译进内核(built-in)或编译成模块(.ko)后动态加载。对于加速器驱动,若希望开机即用,可设为 built-in;若需灵活调试,可编译为模块,用 insmod 或 modprobe 加载。加载后通过 dmesg 查看 probe 是否成功,通过 ls /dev 或 ls /sys/class 确认设备节点是否生成。

设备树中的 compatible 字符串必须与驱动中的 of_device_id 表完全一致,否则 probe 不会被调用,这是初学者最常见的“驱动不工作”原因之一。

五、运行时环境搭建要点

无论裸机还是 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 或自定义驱动读写同一寄存器,确认软硬件路径一致。这样就把“启动链—设备树—驱动—运行时”串成了一条可验证的闭环。