☰
Petalinux工程骨架详解:从XSA到BOOT.BIN的嵌入式Linux构建
2026/10/5 3:52:03 网站建设 项目流程

1. 先把 petalinux 工程骨架这块拼图摆正

如果你刚接触 Zynq 这类带 FPGA 的嵌入式平台,想用 petalinux 给板卡做一套 Linux 系统,第一反应大概率是找一份教程,敲几条命令,生成 BOOT.BIN,烧进 SD 卡,完事。我最早也这么想,结果折腾了整整一个周末,卡在最基本的三件事上:设备树到底怎么改、boot.scr 和 image.ub 分别负责什么、以及 SD 卡里每个文件为什么非放那个位置不可。后来回头看,这些问题不是因为工具复杂,而是因为我一开始没有把 petalinux 工程骨架这件事弄明白。这篇文章就把我反复踩坑之后整理出来的东西写下来,作为这个系列的第 1 篇,目标读者是三类人:用过 Vivado 但没碰过 petalinux 的人、被各种启动报错吓退的新手、以及想从“会敲命令”升级到“知道每步在干什么”的人。

1.1 很多人学 petalinux 的第一道坎:不知道整套流程在干什么

大多数教程都会告诉你运行petalinux-create、petalinux-config、petelinux-build、petalinux-package这几条命令,但很少有人讲清楚这几条命令背后到底发生了什么。结果就是你照着敲完了,板子没起来,不知道去查哪里;就算板子起来了,换一块板子、换一个外设,又不会了。

我后来把整个流程重新拆了一遍,发现 petalinux 做的事情其实是一个“硬件描述 -> 软件组件 -> 可启动镜像”的三级转换:

  • 第一级,Vivado 导出的.xsa文件,里面包含硬件平台信息,比如 CPU 型号、DDR 地址、外设地址、时钟频率,以及可选的 FPGA bitstream。
  • 第二级,petalinux 拿到.xsa之后,生成对应的设备树源文件、U-Boot 配置、内核配置和根文件系统骨架。
  • 第三级,petalinux-build和petalinux-package把这些东西编译、组装成 BOOT.BIN、boot.scr、image.ub 这一套启动文件。

所以 petalinux 不是简单帮你“编一个 Linux 内核”,它更像一条流水线:输入是硬件工程师给的 XSA,输出是嵌入式 Linux 工程师可以直接烧到板子上的镜像。

1.2 一套嵌入式 Linux 方案的完整组成

在 Zynq 平台上,一套能跑起来的嵌入式 Linux 方案至少包含下面几个部分:

层次组件作用来源
FPGA 逻辑bitstream (.bit)配置 PL 侧的硬件逻辑,比如 AXI GPIO、DMA、AD9361 接口Vivado 生成
启动引导FSBL + U-Boot初始化 DDR、时钟、MIO,加载 bitstream,引导内核FSBL 由 Vivado 生成,U-Boot 由 petalinux 编译
内核kernel Image / uImage负责进程调度、内存管理、驱动框架petalinux 编译
设备树.dtb告诉内核“硬件长什么样、地址在哪里、中断是几号”petalinux 的 DTG 根据 XSA 生成
根文件系统rootfs提供 /bin、/etc、/lib 这些用户态环境petalinux 生成

这里最容易忽略的是设备树。很多刚从单片机转过来的朋友习惯把所有硬件信息写在代码里,但 Linux 内核不一样,它面对的是千奇百怪的板卡,不可能每块板子都编译一个专用内核,所以采用设备树这种“数据驱动”的方式。设备树里写清楚外设基地址、中断号、引脚复用,内核再去匹配对应驱动。

1.3 petalinux 在中间扮演什么角色

把上面五层放在一起看,petalinux 的定位就很清晰了:它是连接硬件工程师交付物和嵌入式 Linux 工程师日常工作的“组装车间”。没有 petalinux,你也能手动下载 U-Boot 源码、Linux 内核源码、Buildroot,然后一个一个交叉编译,再自己拼启动镜像。这样做不是不行,但你得同时维护 U-Boot、内核、设备树、rootfs 四套东西的版本关系,第一次搞十有八九会乱。

petalinux 把这些事情封装成了 Yocto 层的 recipe,并提供了一套相对统一的命令行接口。它的好处是:你只要维护一个project-spec目录下的配置,就能反复构建出可复现的启动镜像。缺点是:Yocto 构建系统本身很复杂,命令被封裝得越简单,出问题时越难排查。所以我一直建议学 petalinux 的人不要只背命令,而是要把每一层对应到实际文件上,不然遇到“编译过了但启动不了”这种问题会很痛苦。

2. 从 Vivado 到 petalinux:用最简单工程串起整条链路

2.1 我建议的第一个工程只保留这些东西

很多人一上来就想把完整板卡支持、AD9361、DMA、Ethernet 全部塞进去,结果系统起不来根本不知道是硬件问题、设备树问题还是 rootfs 问题。我做第一块板子的经验是:先做一个“最小可启动系统”,能打印 U-Boot 日志、能进 Linux 命令行就算赢。

在 Vivado 里,这个最小工程只需要三样东西:

  • Zynq PS,也就是processing_system7_0,开好 UART、SD、QSPI 这三个最基本的 MIO 配置。
  • AXI GPIO,用来点一颗 LED,验证 PL 和 PS 之间的 AXI 通路。
  • 一个简单的 Block Design,把 PS 和 AXI GPIO 连起来,跑综合、实现,生成 bitstream。

这里有一个新手很容易踩的坑:导出.xsa的时候忘记勾选Include bitstream。如果你不勾,后面 petalinux 打包 BOOT.BIN 时就没有 FPGA 配置文件,PL 侧永远是空的,AXI GPIO 自然不工作。

正确的导出方式,在 Vivado 的 Tcl Console 里执行:

write_hw_platform -force -include_bit ./system.xsa

或者在 GUI 菜单里点击File -> Export Hardware,务必确认勾选Include bitstream。导出的system.xsa就是 petalinux 的输入。

2.2 为什么需要 XSA、bitstream 和设备树“三方对账”

把 Vivado 工程和 petalinux 工程放在一起看,你会发现它们之间有一个三角关系:

  • XSA 描述硬件架构,告诉 petalinux 有哪些外设、地址是多少。
  • Bitstream 描述 FPGA 逻辑,它会在启动早期被 FSBL 加载进 PL。
  • 设备树描述内核视角的硬件视图,告诉内核哪个地址对应哪个驱动。

这三者必须保持一致。最常见的翻车现场是:你改了 Vivado Block Design,给某外设换了基地址,然后只重新导出了 XSA,忘了重新生成 bitstream;或者只重新生成了 bitstream,没有重新导入 petalinux。结果就是设备树里的地址和实际 PL 逻辑对不上,驱动 probe 失败,日志里出现一堆乱码。

所以我的习惯是:每次改动 Vivado 硬件后,统一执行一次“重新综合实现 -> 导出带 bitstream 的 XSA -> 在 petalinux 工程里重新导入 XSA -> 重新 build”。这个过程看起来啰嗦,但能省下大量排错时间。

3. petalinux 命令的“入坑顺序”:先创建,再逐层配置

3.1 工程创建与硬件描述导入

创建 petalinux 工程本身不复杂,但有几个前置条件容易被忽略。我建议先确认这几件事:

  • petalinux 安装路径下没有中文和空格。
  • 当前终端用的是 bash,不是 sh 或者 dash。
  • 已经执行过安装目录下的settings.sh环境脚本。
  • 磁盘剩余空间至少留 80GB,整个构建过程会消耗大量空间。

确认完环境之后,按下面步骤操作:

source /opt/petalinux/2025.1/settings.sh petalinux-create -t project -n zynq-led --template zynq cd zynq-led petalinux-config --get-hw-description=../hardware/system.xsa

--template zynq的意思是创建一个 Zynq-7000 系列的工程模板。如果你的芯片是 Zynq UltraScale+ MPSoC,这里要改成zynqMP;如果是 Versal,要改成versal。选错模板后面会有一堆奇怪的错误,比如找不到 PMU 固件、U-Boot 配置不匹配等。

执行完最后一条命令后,会进入一个菜单配置界面。这里大多数人就直接按 F1 保存退出了,但我建议第一次老老实实把菜单翻一遍,至少要知道下面这些选项在哪里。

3.2 进入配置菜单后,先看这几个关键项

petalinux-config打开的是基于 Kconfig 的菜单界面,和 Linux 内核的menuconfig很像。新手一进去容易懵,我先帮你划几个重点:

  • Subsystem AUTO Hardware Settings -> Serial console:查看串口控制台参数,Zynq 默认通常是ttyPS0,波特率 115200。
  • Subsystem AUTO Hardware Settings -> Advanced bootable images storage Settings:选择 boot 镜像从哪里读取,可以是 SD、QSPI、EMMC。
  • Image Packaging Configuration -> Root filesystem type:选择根文件系统类型,这里决定后面 SD 卡怎么分区、image.ub 里装什么。
  • u-Boot Configuration -> boot script:控制是否生成 boot.scr,以及启动命令的默认配置。

菜单里按/可以搜索符号,按?可以看当前项的帮助说明。改完配置不要直接关终端,要一路按Esc退到最底层,选择保存,否则你的修改不会生效。

3.3 Rootfs 类型的选择,直接影响 SD 卡内容

这是第一课里最值得展开讲的地方。很多教程没区分根文件系统类型,导致有人把 ext4 的 rootfs 要求硬套在 initramfs 上,被人说“你怎么不把根文件系统分区拷进去”。

先看这张表:

模式根文件系统在哪里适用场景SD 卡内容
initramfs打进 image.ub第一版验证、临时测试BOOT.BIN + boot.scr + image.ub
ext4 / SD独立 ext4 分区需要持久化存储、跑实际应用上述三件套 + rootfs 分区
WIC 整卡镜像整个 SD 卡镜像量产、发布、复制给同事用 dd 直接写整卡

如果你是第一次做最小系统验证,我建议先选 initramfs。好处是 SD 卡只需要一个 FAT32 分区,把三个文件丢进去就能启动,不用纠结分区表错乱、rootfs 没格式化等问题。坏处是根文件系统在内存里,重启后修改不保留,不适合实际产品。

如果你选择 ext4 / SD 模式,那么 SD 卡需要两个分区:第一个 FAT32 分区放 BOOT.BIN、boot.scr、image.ub;第二个 ext4 分区放 rootfs。这个分区结构一定要记住,后续所有启动问题里,至少有三成是分区结构不对。

3.4 构建、打包、制作 SD 卡三步走

当硬件配置和 menuconfig 都设置好之后,就可以开始构建和打包了:

petalinux-build petalinux-package --boot --fsbl images/linux/zynq_fsbl.elf \ --fpga images/linux/system.bit --u-boot

第一条命令会把内核、U-Boot、设备树、rootfs 全部编译出来。第二条命令把这些文件按启动顺序打包成BOOT.BIN。

构建完成后,images/linux目录下会多出几个关键文件:

  • BOOT.BIN:启动镜像,包含 FSBL、bitstream、U-Boot。
  • boot.scr:U-Boot 启动脚本,告诉 U-Boot 去哪里找 image.ub。
  • image.ub:FIT 格式镜像,通常包含内核和设备树,如果配置了 initramfs,也会包含根文件系统。

制作 SD 卡,我的做法是先用lsblk确认 SD 卡设备名,然后分区、格式化、拷贝:

sudo fdisk /dev/sdX # 分区1: 512M, 类型 c (W95 FAT32) # 分区2: 剩余空间, 类型 83 (Linux) sudo mkfs.vfat -F 32 -n BOOT /dev/sdX1 sudo mkfs.ext4 -L rootfs /dev/sdX2 sudo mount /dev/sdX1 /mnt/boot sudo cp images/linux/BOOT.BIN images/linux/boot.scr images/linux/image.ub /mnt/boot/ sudo umount /mnt/boot

如果你选择的是 initramfs 模式,只需要准备一个 FAT32 分区就够了,第二个 ext4 分区可以不要。但请注意,U-Boot 启动脚本里的 bootcmd 是根据 rootfs 类型生成的,你改了 rootfs 类型之后,最好重新执行一次petalinux-package生成新的 boot.scr,不要自己手动改 bootcmd。

4. 最容易翻车的文件:设备树、boot.scr 和 SD 卡分区

4.1 设备树不是“配置文件”,是“硬件对账表”

我见过不少新手把设备树当成配置文件随手改,结果改出来的东西既没有语法错误,也编译过去了,但内核就是认不到外设。原因很简单:设备树的本质是一份“硬件对账表”,它必须和 Vivado 里实际生成的地址、中断号、时钟频率完全一致。

在 petalinux 工程里,用户自定义设备树主要改这个文件:

project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi

这个文件会被自动 include 到整棵设备树里。你想追加一个 AXI GPIO 节点,或者改某个已有节点的属性,都可以写在这里。

举个例子:

#include <dt-bindings/gpio/gpio.h> &axi_gpio_0 { compatible = "xlnx,axi-gpio-2.0"; xlnx,gpio-width = <8>; };

编译设备树可以用:

petalinux-build -c device-tree

这条命令很快,只重新生成设备树,不重新编译内核,适合调试设备树时反复试。

这里有个红线:不要直接去改project-spec下自动生成的pl.dtsi或system.dts。因为每次重新导入 XSA,这些文件都会被覆盖。你把改动写在这里面,等于白改。所有用户自定义改动都应该放到system-user.dtsi或对应的 bbappend 里。

4.2 我见过最多的三个启动失败原因

第一课先帮你把最容易踩的三个坑列出来,后面排错会省很多事:

现象根因处理方式
上电后串口完全没有输出BOOT.BIN 没放到 SD 卡第一个 FAT32 分区,或者分区类型不对重新分区,第一个分区必须是 FAT32,文件放在分区根目录
U-Boot 能启动,但停在Zynq>提示符找不到 boot.scr 或 image.ub,U-Boot 进入了命令模式确认 boot.scr 和 image.ub 都在 FAT32 分区根目录,文件名大小写一致
内核启动后报VFS: Unable to mount root fs根文件系统类型选错,或内核 bootargs 里的 root 分区不存在检查 menuconfig 里的 rootfs 类型;如果用 ext4,确认 ext4 分区格式化且 bootargs 指向正确设备

第二个坑特别典型。U-Boot 进入命令行之后不会告诉你“文件找不到”,它只是跳过了 boot.scr,然后停在提示符等你输入命令。这时候你可以先检查环境变量:

printenv bootcmd printenv bootargs

然后手动执行一遍启动流程,看看卡在哪一步:

ls mmc 0:1 load mmc 0:1 0x2000000 image.ub bootm 0x2000000

这样能快速判断是文件路径问题、FIT 镜像格式问题,还是 rootfs 问题。

4.3 设备树错误排查方法

设备树出问题,日志通常不会直接告诉你“设备树第几行错”,而是驱动 probe 失败、failed to get clock、no node found这类模糊信息。排查时我一般按这个顺序来:

  1. 先确认生成的 DTB 里有没有你要的节点。用fdtdump或dtc把 DTB 反编译出来,搜索外设名字:
fdtdump images/linux/system.dtb | grep -A 20 "axi_gpio"
  1. 再确认节点属性值对不对。重点看reg、interrupts、clocks这三项,它们必须和 Vivado 的 Address Editor 里显示的一致。

  2. 最后去看 Linux 内核日志里对应驱动的报错信息:

dmesg | grep -i gpio dmesg | grep -i ad9361

设备树调试不建议靠猜,把 DTB 反编译出来,和 Vivado 里的硬件信息一列一列对,通常很快能找到问题。

5. 实战心得:版本匹配、增量构建、AD9361 设备树迁移

5.1 版本没对齐,是百分之六十奇葩报错的根因

这是我用了几年 petalinux 之后最深的体会。Vivado、Vitis、petalinux 这三个工具的版本必须严格对应。比如你用 Vivado 2025.1 导出的 XSA,丢给 petalinux 2024.2 去导入,大概率会出现设备树生成错误、U-Boot 源码不兼容、FSBL 版本不匹配等一堆奇怪问题。这些问题有时候报错信息很直白,有时候完全不报错,但最终的启动镜像就是跑不起来。

所以我现在的习惯是:

  • 建一个版本清单,写清楚 Vivado 版本、petalinux 版本、内核版本、U-Boot 版本。
  • 所有工具链尽量用同一年份的 release。
  • XSA 文件名里带上日期和 Vivado 版本号,比如system_20250110_v2025.1.xsa。
  • petalinux 工程根目录放一个 README,记录这次构建用的 XSA 是哪一个。

这套习惯看起来很基础,但能避免很多“明明代码没变,为什么换台电脑就编不过”的诡异问题。

5.2 一线工程师应该掌握的几个增量构建命令

petalinux-build全量构建一次可能要几个小时,不可能每次改一行代码就全量编译。下面这几个命令我几乎天天用:

# 只重编设备树,适合改 dtsi 后快速验证 petalinux-build -c device-tree # 只重编内核 petalinux-build -c kernel # 只重编 U-Boot petalinux-build -c u-boot # 导入新 XSA 后,需要清理旧配置再重新生成 petalinux-config --get-hw-description=/path/to/system.xsa --silentconfig petalinux-build -x mrproper petalinux-build

注意最后那个mrproper,它会把大部分编译产物清掉,所以不要随便执行。只有当你改了硬件描述、换了 XSA,发现增量编译结果出现诡异问题时,才需要做一次彻底清理。

另外一个小技巧:build 完之后,images/linux下会保留上一次的镜像。如果你想确认当前这个 image.ub 是什么时候编的,可以直接看文件时间戳。我每次打包之前都会检查时间戳,避免把旧镜像误当成新镜像烧到板子上。

5.3 如何把 AD9361 原有设备树移到新 petalinux 工程里

你搜到的热搜词里有“如何将 ad9361 原有设备树移到新建 petalinux 工程里”,这个需求很常见。很多人手里有一套老工程,里面关于 AD9361 的设备树已经调通了,现在想移植到新的 petalinux 工程里,省得重新调。

直接复制粘贴肯定不行,因为老工程里的设备树可能依赖某些 include 路径、宏定义、或者自定义驱动节点。我的建议是按下面两步走:

第一步,把老工程里的ad9361.dtsi或相关节点清理干净,放进新工程的project-spec/meta-user/recipes-bsp/device-tree/files/目录下。然后在同目录下的device-tree.bbappend里加上:

FILESEXTRAPATHS:prepend := "${THISDIR}/files:" SRC_URI += "file://ad9361.dtsi"

这样 petalinux 构建设备树时,就会把这个文件加入搜索路径。

第二步,在system-user.dtsi里 include 并覆盖需要的节点:

/include/ "ad9361.dtsi" &axi_ad9361 { /* 新工程里,这里可能要改时钟、GPIO 引脚或 SPI 片选 */ };

这里要注意,AD9361 的设备树节点通常不只是“把一个 dtsi 复制过来”那么简单。它依赖内核里的 IIO 驱动,依赖 FPGA 里的 AXI 接口地址,依赖时钟树配置。如果你新工程里的 PL 地址和原来的不一样,设备树里的reg属性必须同步修改。

还有一种更稳妥的做法是直接在system-user.dtsi里用${SOMETHING}这样的宏,去引用 XSA 生成的硬件节点,而不是写死地址。但这对初学者来说理解门槛较高,所以如果你只是先跑通,复制过来改地址是最直接的。

迁移方式优点缺点
直接 include 原 dtsi快,改动少如果原 dtsi 依赖老工程路径,可能编译报错
放到 files 目录 + SRC_URI干净、可复现、不影响原文件需要理解一点 Yocto 的 bbappend 机制
在新工程里重新写节点最干净,完全和当前硬件匹配工作量大,需要非常熟悉 AD9361 的寄存器配置

我个人推荐第二种。它既能保留老工程调试好的内容,又不会污染自动生成的文件,后续升级也方便。

6. 第一版系统起来之后,还要做什么

6.1 系统启动后先检查这三件事

当你第一次看到 Linux 登录提示符出现时,先别急着高兴,至少做三项检查,确认这套系统是真的可用,而不是“恰好能启动”。

第一,检查设备树到底有没有被内核正确解析:

cat /proc/device-tree/model

如果打印出来的是你板子的型号名称,说明设备树加载正常。如果这个文件不存在或者内容乱码,说明设备树没配对。

第二,检查你关心的外设节点有没有被驱动绑定。以 AXI GPIO 为例:

ls /sys/class/gpio/ cat /proc/device-tree/axi/gpio@40000000/status

如果能在/sys/class/gpio下看到对应的 gpiochip,说明驱动 probe 成功。

第三,检查启动日志里有没有被忽略的严重错误:

dmesg | grep -i error dmesg | grep -i fail

不是说有 error 就一定不能用,但你要知道每个 error 来自哪里。比如 SD 卡驱动的 error 可能只是 probe 顺序问题,而 DMA 驱动的 error 很可能影响实际业务。

6.2 把常用命令固化成脚本,别靠记忆力

我发现很多初学者喜欢一条一条敲命令,敲完就忘,下次重新配环境又要从头查。我的建议是,一旦板子能正常启动,立刻把整个构建流程写成一个脚本保存下来。

下面这个脚本是我常用的模板:

#!/usr/bin/env bash set -e XSA=${1:-system.xsa} source /opt/petalinux/2025.1/settings.sh petalinux-config --get-hw-description=$XSA --silentconfig petalinux-build petalinux-package --boot \ --fsbl images/linux/zynq_fsbl.elf \ --fpga images/linux/system.bit \ --u-boot BOOT_DIR=/tftpboot/zynq-led mkdir -p $BOOT_DIR cp -v images/linux/BOOT.BIN images/linux/boot.scr images/linux/image.ub $BOOT_DIR/

这个脚本的作用不只是省时间,它还保证了每一次构建用的流程是一致的。你不会因为某次忘了重新导入 XSA,而拿到一个和硬件不匹配的旧镜像。后续就算换一台装机环境,只要把脚本拷过去,改一下 petalinux 安装路径,就能跑出一套可复现的启动镜像。

这一篇先讲到这里。下一篇文章我打算拿一块具体的 Zynq 板子,完整走一遍“Vivado 点灯工程 -> petalinux 最小系统 -> 启动到命令行”的过程,再把过程中遇到的报错和排查思路贴出来。如果你在迁移 AD9361 设备树或者制作 SD 卡时遇到过什么典型问题,欢迎留言,我会把高频问题补充到后续的排错清单里。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询