从 Vivado 到 Linux 启动:PetaLinux 嵌入式开发完整实战笔记(一)
如果你手头正好有一块 AMD/Xilinx 的 Zynq 或者 Versal 开发板,想在上面跑一个完整的 Linux 系统,那 PetaLinux 绝对是你绕不开的工具链。不少刚接触嵌入式 Linux 的朋友,一上来就被术语砸晕——什么 FSBL、U-Boot、设备树、image.ub、boot.scr,再加上 PetaLinux 那套petalinux-create、petalinux-config、petalinux-build的命令流程,第一次跑通时“运气好”能启动,运气不好就是各种黑屏、卡死、找不到根文件系统。这篇文章我打算从实际开发的角度,把用 PetaLinux 做嵌入式设计开发的完整流程、关键步骤和坑点拆开讲清楚,这篇是系列第一篇,先聚焦在环境搭建、工程创建、配置编译和启动镜像生成这几块,后续再深入设备树修改、驱动集成和调试技巧。
这篇文章适合谁看?已经会用 Vivado 做硬件设计、但还不太熟悉 PetaLinux 流程的工程师,也适合刚入手 Zynq 开发板、准备跑通第一个 Linux 系统的新手。我会尽量把“为什么要这么做”和“这么做背后的原理”讲透,而不只是给一串命令让你复制粘贴。
1. 整体设计思路与 PetaLinux 的定位
1.1 PetaLinux 到底解决了什么问题
很多人在接触 PetaLinux 之前,已经在用 Yocto 或者 Buildroot 交叉编译 Linux 系统,为什么还要单独学一个 PetaLinux?简单说,PetaLinux 就是 AMD 官方基于 Yocto 定制的一套嵌入式 Linux 开发工具,它的核心价值在于:把“硬件描述文件 → 引导程序 → 内核 → 根文件系统 → 启动镜像”这条链路完全打通了。
传统方式下,你要手工管理内核源码、u-boot 源码、设备树源文件,还要自己交叉编译 FSBL(First Stage Bootloader,第一阶段引导程序),手动写 U-Boot 的环境变量,再准备根文件系统。这一套流程里任何一个环节版本对不上,或者配置项漏了,启动就会失败,而且排查起来极其痛苦。PetaLinux 把这些步骤统一到一个工程里,输入硬件描述文件(XSA),输出可直接烧录的 BOOT.BIN、image.ub 和启动脚本,整个过程相对标准化,而且它知道 AMD 自家硬件有哪些坑。
我从实际体验来说,PetaLinux 有点像嵌入式 Linux 里的“一站式解决方案”,它确实有一些学习曲线,也不像 Buildroot 那么灵活轻量,但它和 Vivado/Vitis 的结合是最紧密的。尤其是你以后要加自研 IP、改 PL 侧逻辑、调 PS-PL 接口,PetaLinux 能根据硬件配置自动生成配套的设备树和驱动程序框架,这一块是其他通用构建工具替代不了的。
1.2 开发流程全景图
在动手敲命令之前,脑子里一定要有整条链路的画面。PetaLinux 开发大致分这么几个阶段:
第一,硬件设计阶段。在 Vivado 里完成 Block Design,配置好 PS 侧的 DDR、MIO、UART、SD 控制器等外设,加上 PL 侧需要的内容,综合实现后导出 XSA 文件。这个阶段决定了 Linux 能看到哪些硬件资源。
第二,PetaLinux 工程创建阶段。拿到 XSA 之后,用petalinux-create创建工程,用petalinux-config --get-hw-description导入硬件描述。PetaLinux 会自动解析出你的硬件配置,生成初始的设备树、内核配置和 U-Boot 配置。
第三,系统配置阶段。你可以在 menuconfig 界面里勾选内核驱动、rootfs 组件、U-Boot 环境变量、设备树插件等。比如你要用某个 WiFi 模块、某个触摸屏芯片,就在这一层把对应驱动选上。
第四,编译打包阶段。运行petalinux-build编译整个系统,再通过petalinux-package --boot生成 BOOT.BIN,最后整合出 image.ub。这个阶段就是把前面所有配置变成可以烧录的文件。
第五,部署启动阶段。把生成的文件拷贝到 SD 卡或 QSPI Flash,开发板上电,通过串口观察启动日志,能顺利进入 Linux shell 说明链路打通了。
这五个阶段里,每个阶段都有各自的坑。最常见的问题集中在第四个和第五个,编译时缺依赖、打包时选错分区格式、启动时 U-Boot 找不到内核,这些我都会在后面的章节里展开说。
1.3 版本选型和开发环境建议
选版本这件事,很多人不重视,结果折腾了好几天。PetaLinux 对操作系统版本很挑剔,它要求特定的 Ubuntu/CentOS 版本对应特定的 PetaLinux 版本。比如 PetaLinux 2023.1 官方支持 Ubuntu 18.04/20.04 和 CentOS 7.8/8.2,你拿 Ubuntu 22.04 去装,大概率会遇到依赖库缺失或者 Bash 版本不兼容的问题,不是不能解决,但会浪费大量时间在对齐环境上。
我的建议是,直接用虚拟机装一个官方支持的 Ubuntu 版本,比如用 PetaLinux 2024.1 配 Ubuntu 22.04,或者用 2023.1 配 Ubuntu 20.04,都是成熟组合。有朋友说虚拟机性能不够,编译太慢,这个确实存在。我的经验是给虚拟机分配至少 8 核 CPU、16GB 内存、100GB 磁盘空间,首次全量编译 PetaLinux 工程大约需要 30 分钟到 1 小时,和装了双系统的时间差距不算太大。如果你条件允许,用 WSL2 也能跑,但要注意 USB 设备直通和串口访问会比较麻烦,调试阶段还是完整的虚拟机或者 Linux 实体机更顺手。
另外提醒一句,PetaLinux 安装路径不要放在中文目录和带空格的目录下,安装用户最好也别用 root 用户日常操作。虽然 root 也能用,但某些脚本对权限的判断有点怪,容易出现“文件明明在却报找不到”的问题。
2. 环境搭建与 PetaLinux 安装实操
2.1 安装前的系统准备清单
环境搭建这部分,我按自己踩坑排雷后总结的最简路径来写。首先是磁盘空间,PetaLinux 安装包本身就有 10GB 左右,解压后还要额外占用空间,你还要建工程、编译,建议磁盘剩 100GB 以上。然后是软件依赖,PetaLinux 官网的安装文档列了一长串依赖库,但实际装的时候只需要把常用的开发库装齐就行,我用的是下面这条命令,基本覆盖了绝大多数依赖:
sudo apt update sudo apt install -y gcc git make net-tools libncurses5-dev \ libncursesw5-dev libssl-dev libtinfo-dev zlib1g-dev \ build-essential u-boot-tools device-tree-compiler \ python3 python3-pip python3-venv python3-setuptools \ libegl1-mesa libgl1-mesa-glx libmagic1 iproute2 gawk \ xz-utils cpio rsync bc flex bison libyaml-dev \ libgnutls28-dev libc6-dev libstdc++6注意:不同版本的 PetaLinux 依赖会有细微差异,如果安装后运行
petalinux-version报缺少某些 .so 文件,用apt-file或者直接搜包名补装就行,通常缺什么补什么,别一上来就重装整个系统。
还有一个比较隐蔽的坑是/bin/sh指向的问题。Ubuntu 默认把/bin/sh指向dash,但 PetaLinux 的很多脚本是为bash写的,运行某些配置工具时会出现语法错误。建议执行下面这条命令,把默认 shell 改回 bash:
sudo dpkg-reconfigure dash在弹出的界面里选择“No”,也就是不把 dash 作为默认 shell。这个改动不会影响系统正常使用,但能让 PetaLinux 的脚本少抽风。
2.2 安装 PetaLinux 的完整过程
从官网下载 PetaLinux 安装包后,首先给它加执行权限。这里建议把安装包装在一个专门的目录下,比如~/Downloads/petalinux,然后解压或者直接运行安装器。以 2024.1 版本为例,它一般是一个.run文件,执行:
chmod +x petalinux-v2024.1-*.run ./petalinux-v2024.1-*.run --dir ~/petalinux-install安装器会问你接受协议、选择安装路径,过程中会自动检查依赖,如果有缺的会提示。安装完成后,还要设置环境变量,把 PetaLinux 的工具路径加进去。我习惯在~/.bashrc里加一行:
source ~/petalinux-install/settings.sh然后用source ~/.bashrc或者新开终端,运行petalinux-version --verbose验证是否安装成功。如果能看到版本信息和构建日期,说明安装这步已经完成了。
这里我必须多说一句,很多人装完之后直接就开始建工程,结果在编译时发现各种工具链找不到。这通常是因为没有 source 环境变量,或者每次开新终端都要重新 source。建议在~/.bashrc里加生效语句,免去每次手动 source 的麻烦。
2.3 网络与 repo 镜像配置
国内用户用 PetaLinux 另一个比较头疼的问题是网络。PetaLinux 底层基于 Yocto,编译过程中会从网上拉取一些开源包的源码,如果你的网络不够稳定,编译往往会在某个依赖上下载失败。这里提供一个我实测有效的办法,配置 Yocto 的本地下载缓存。在 PetaLinux 工程目录下有一个build/conf/local.conf文件(没有就先运行一次petalinux-build再查看),可以在里面加:
BB_NO_NETWORK = "0" DL_DIR = "/opt/yocto-downloads" SSTATE_DIR = "/opt/yocto-sstate-cache"把DL_DIR指向一个专门的下载目录,这样每次编译时用到的源码包都会缓存下来,第二次编译或者重新建工程不会重复下载。SSTATE_DIR是编译缓存目录,能大幅减少重复编译的时间。如果你在公司内网或者有代理环境,可能还需要设置http_proxy等环境变量,这个根据自己的网络情况来配置。
编译源包的过程中,最常见的错误是 Git 克隆失败或者 tarball 下载超时,这种多数是网络波动导致的。我一般用petalinux-build -x mrproper清理之后重新编译,或者把失败的包手动下载到DL_DIR指定目录,让编译流程不用再从外网拉取。
3. 创建 PetaLinux 工程与硬件描述导入
3.1 从 Vivado 导出 XSA 的关键细节
PetaLinux 工程的核心输入是 XSA 文件,这个文件包含了硬件平台的全部描述信息。很多人第一次接触这个流程,不知道 XSA 怎么生成,或者在 Vivado 里点了一圈没找到导出入口。实际上操作很简单:在 Vivado 里完成综合实现之后,菜单栏选择 File → Export Hardware,勾选“Include bitstream”,确认导出的文件名和位置。这里必须注意,如果 PetaLinux 需要配置 PL 侧的 FPGA 逻辑,就一定要包含 bitstream;如果你只是纯 PS 跑 Linux,bitstream 可有可无,但后续如果要挂 PL 驱动,还是建议勾上。
导出 XSA 还有一个细节:Vivado 版本和 PetaLinux 版本最好保持一致。比如说你用 Vivado 2024.1 生成的 XSA,最好就用 PetaLinux 2024.1 去导入,版本跨太大可能会出现设备树生成格式不兼容的问题。虽然不是绝对不能用,但没必要给自己添堵。
拿到 XSA 后,推荐建一个专门的硬件描述目录,比如~/project/xsa/,把 XSA 文件放进去,命名成容易识别的形式,比如zcu104_base.xsa。这样后续创建工程时引用路径清晰,也方便切换不同硬件版本做对比测试。
3.2 petalinux-create 与工程目录结构
创建 PetaLinux 工程的方式有两种:直接从 XSA 创建模板工程,或者从 BSP 创建。这里先用最常见的方式,从 XSA 创建:
mkdir -p ~/project/petalinux cd ~/project/petalinux petalinux-create -t project --name zcu104_linux --template zynq--template参数根据你的处理器系列选择,Zynq-7000 用zynq,Zynq UltraScale+ MPSoC 用zynqMP,Versal 用versal。命令执行完成后,会在当前目录下生成一个名为zcu104_linux的文件夹,里面就是完整的 PetaLinux 工程骨架。
工程目录里有几个关键文件夹:images是编译输出的镜像文件目录,components是各种源码组件的存放位置,project-spec里是工程配置和设备树等核心文件。我建议你先跑一下tree -L 2 -d看看目录结构,记住project-spec/meta-user这个位置,后面所有自定义的设备树修改、驱动文件添加都在这里。
创建完成后,接下来就是导入硬件描述:
cd zcu104_linux petalinux-config --get-hw-description=~/project/xsa/这个命令不加=也行,路径写对了同样能识别。执行后会进入配置界面,这里不要乱动,先按方向键移动到 Exit 退出,保存默认配置即可。PetaLinux 会解析 XSA 文件,自动生成基础的系统配置、设备树源文件和 U-Boot 配置。
3.3 配置界面里的关键选项解读
petalinux-config打开的是一个菜单配置界面,和 Linux 内核的 menuconfig 风格一致。对于初学者来说,这里面容易踩坑的选项主要有三个,我详细说说。
第一是 Boot 设备配置。进入 Subsystem AUTO Hardware Settings → Advanced bootable images storage Settings,这里可以选择启动镜像的存储介质:SD 卡、QSPI Flash、NAND 等。如果你用 SD 卡启动,就选 sd;如果用 QSPI Flash 启动,选 qspi。选错的话,生成的 U-Boot 会从错误的设备加载内核,启动时会卡在Wrong image format或者No boot file found。
第二是 U-Boot 环境变量配置。进入 U-Boot Configuration,可以设置 bootargs、bootcmd 等参数。很多人喜欢在这里手工加 console=ttyPS0,115200 之类的参数,其实 PetaLinux 默认已经帮你配好了,不建议手动改,除非你明确知道自己在做什么。我的经验是,保持默认,等启动问题出现后再针对性调试。
第三是 rootfs 文件系统类型。在 Image Packaging Configuration 里可以选 rootfs 的打包方式,常见的有 INITRAMFS、EXT4、UBIFS 等。SD 卡启动最常用的是 EXT4,把 rootfs 放到 SD 卡的 ext4 分区;开发调试阶段用 INITRAMFS 也很方便,系统直接跑在内存里,不依赖 SD 卡根文件系统分区,不过每次改 rootfs 都要重新编译打包,镜像我一般调试阶段用 INITRAMFS,稳定后用 EXT4。
这一层的核心原则是:没有明确需求之前,配置能不改就不改。PetaLinux 默认生成的配置已经针对你的硬件做了大量优化,乱改反而容易引入问题。
4. 内核配置、rootfs 定制与设备树
4.1 内核 menuconfig 的常用配置方法
导入硬件描述以后,PetaLinux 已经为你生成了一个精简但可用的内核配置。实际开发中,我们经常需要按需修改内核配置,这时就用到petalinux-config -c kernel:
petalinux-config -c kernel运行后同样进入 menuconfig 界面,这里对应的是 Linux 内核的 Kconfig。比如你想启用某个 USB 转串口驱动、某个 GPIO 按键驱动,或者把某个驱动编成模块,都在这里操作。操作方法和普通 Linux 内核编译完全一致,按空格切换 [*]、[ ] 或 [M]。
我建议开发初期不要改动内核配置,先用默认配置跑通启动流程,确认一切正常后再按需裁剪或新增驱动。否则很容易出现“改了配置编译不过”或者“启动时驱动加载报错”的连锁问题,排查起来比较烧脑。如果确实需要确认某个驱动是否已经启用,可以在配置界面按/搜索符号名,比如你想查 I2C 驱动,输入CONFIG_I2C就能看到当前状态和所在位置。
4.2 定制 rootfs:添加你需要的应用和库
rootfs 定制是 PetaLinux 一个很实用的功能。比如你要在板子上运行一个自己编译的 hello_world 程序,或者要装一个 dropbear(SSH 服务)、一个 python3 运行时,都可以通过petalinux-config -c rootfs来完成:
petalinux-config -c rootfs这里的配置界面和 Yocto 的 image feature 类似,你可以勾选各种软件包。第一次打开你会看到很多分类:Filesystem Packages、Apps、Image Features 等。比如我想在系统里支持 SSH,就进入 Filesystem Packages → misc → ssh,勾选 dropbear;想要 python3,就去 Languages → python3 下勾选对应模块。
等 rootfs 配置好以后,你可以把自己编译好的应用放到工程目录下的某个自定义目录,然后在 rootfs 配置里添加一个自定义的应用程序组件,或者更简单一点的做法,编译完以后直接把二进制丢到根文件系统的某个路径下,比如/usr/bin,再重新打包 rootfs。PetaLinux 为这种情况提供了一个专门的变量和目录,叫project-spec/meta-user/recipes-apps,你可以照着模板写一个简单的 recipe,把自己的应用集成进去。这个后续我会单独写一篇细讲,当前阶段先用默认 rootfs 跑通系统即可。
4.3 设备树文件的位置与基础修改逻辑
设备树是嵌入式 Linux 开发中最重要的配置文件之一,它描述了硬件平台上有哪些外设、地址、中断、引脚复用等信息。PetaLinux 中,与硬件描述相关的设备树源文件在导入 XSA 后会自动生成,但你需要改写的通常是用户设备树文件,位于project-spec/meta-user/recipes-bsp/device-tree/files/,比如system-user.dtsi。
这个文件用于叠加自定义的设备树设置,比如你要禁用某个默认启动的串口、修改某个外设的时钟频率、为某个自研 IP 添加设备节点,都可以在这个 dtsi 文件里修改。它的语法和标准设备树完全一致,只是通过 include 的方式和自动生成的基础 dts 合并。我的建议是不要直接改 PetaLinux 自动生成的system.dts,而是把自定义内容写进system-user.dtsi,这样当硬件描述里面有更新时,重新导入 XSA 后你的修改不会丢失。
举个例子,如果你想给 SPI 控制器添加一个外设子节点,可以在system-user.dtsi里添加类似内容:
&spi0 { status = "okay"; my_device@0 { compatible = "vendor,my-device"; reg = <0x0>; spi-max-frequency = <1000000>; }; };这样修改之后,重新编译时 PetaLinux 会自动把基础设备树和你的叠加层合并,生成的 dtb 会包含你的自定义节点。注意缩进和语法,一个;漏掉或者括号不匹配,编译设备树时会直接报错,而且错误信息有时不够直观,需要仔细排查。
4.4 修改设备树后如何验证
设备树编译成 dtb 后,会打包在 image.ub 里。验证修改是否生效,最直接的方法是启动 Linux 后在/proc/device-tree目录下查找对应节点:
ls /proc/device-tree/ cat /proc/device-tree/spi0/status如果能看你的自定义节点,说明设备树叠加成功了。如果找不到,先检查编译输出,看 dtb 是否重新生成,然后确认是否在 U-Boot 阶段加载了新的 dtb。一个常见的情况是 U-Boot 默认从某个固定地址加载 dtb,如果你只更新了 image.ub 而没有更新 boot.scr,U-Boot 环境变量里指定的加载地址和文件名不匹配,系统会认为设备树加载失败,直接使用内置默认设备树,导致你的修改看起来“没生效”。
5. 编译工程与生成 BOOT.BIN、image.ub、boot.scr
5.1 全量编译与增量编译的正确姿势
工程配置都改好之后,就该进入编译环节了。编译 PetaLinux 工程最常用的命令是:
petalinux-build这条命令会编译整个工程,包括 FSBL、PMUFW(针对 MPSoC/Versal)、U-Boot、设备树、内核、rootfs,最后生成镜像。首次编译时间很长,二十分钟到一小时都是正常的。后续修改了内核配置,可以用petalinux-build -c kernel只编译内核;修改了 rootfs 配置,用petalinux-build -c rootfs;修改了设备树,用petalinux-build -c device-tree。这种增量编译方式能节省大量时间,但要注意,根文件系统打包这一层有时会依赖某些组件,如果改了内核二进制,最后最好再整体跑一次petalinux-build,确保镜像完整。
编译过程中的日志默认打印在终端上,如果想保留日志方便排查错误,可以用:
petalinux-build 2>&1 | tee build.log出错了先看日志尾部的错误信息,大多数情况下错误都是缺依赖、网络下载失败或者磁盘空间不足。如果日志里出现ERROR: Task failed,可以进入对应组件的源码路径查看具体的编译错误。
5.2 生成 BOOT.BIN 的完整参数说明
BOOT.BIN 是 Zynq 系列启动时第一个被加载的镜像,它包含 FSBL、PMUFW(如果平台支持)、ATF、U-Boot 这几个部分。生成命令是:
petalinux-package --boot --format BIN --fsbl images/linux/zynq_fsbl.elf \ --pmufw images/linux/pmufw.elf --atf images/linux/bl31.elf \ --u-boot images/linux/u-boot.elf --output images/linux/BOOT.BIN不同平台的参数略有差别。Zynq-7000 不需要--pmufw和--atf,命令可以简化成:
petalinux-package --boot --format BIN --fsbl images/linux/zynq_fsbl.elf \ --u-boot images/linux/u-boot.elf --output images/linux/BOOT.BIN这里提醒一下,--fsbl指定的路径一定要存在,如果只单独编译过内核而没有完整编译,FSBL 可能没有生成。第一次操作时,建议先完整跑一次petalinux-build,再执行打包命令。
打包完成后,images/linux/目录下会有 BOOT.BIN、image.ub、boot.scr 等一堆文件,这些就是后续烧录 SD 卡的全部内容。
5.3 boot.scr 的作用与手动创建方法
boot.scr是 U-Boot 的启动脚本,它是由boot.cmd或者boot.txt通过mkimage工具生成的。这个文件的作用是告诉 U-Boot 如何加载内核和设备树:从哪个设备、哪个分区、哪个文件名加载,加载到哪个内存地址,然后用什么命令启动。
PetaLinux 在打包时一般会自动生成boot.scr,但有时候你改了 U-Boot 环境变量或者分区布局,默认的 boot.scr 就不适用了。这种情况下可以手写一个启动脚本:
echo "Loading kernel image.ub..." load mmc 0:1 0x10000000 image.ub setenv bootargs console=ttyPS0,115200 bootm 0x10000000保存为boot.cmd,然后用下面的命令生成boot.scr:
mkimage -c none -A arm64 -T script -d boot.cmd boot.scr如果是 Zynq-7000(32 位 ARM),架构参数要改成-A arm。手动生成 boot.scr 后,把它和 BOOT.BIN、image.ub 一起放进 SD 卡启动分区即可。这里我个人的习惯是,除非确实需要修改,否则都用 PetaLinux 自动生成的 boot.scr,手动改容易忽略一些默认参数,比如 fdt 加载地址,导致启动不了。
5.4 BOOT.BIN 与 U-Boot 启动流程的对应关系
把 BOOT.BIN 的生成流程理清楚之后,你会发现 Zynq 的启动流程其实是分阶段进行的。首先是 BootROM 从启动介质中读取 BOOT.BIN,然后加载 FSBL。FSBL 的主要工作是初始化 DDR、配置 PS 侧的时钟和引脚,然后根据 BOOT.BIN 里的配置加载 PMUFW(MPSoC/Versal 平台)、ATF 和 U-Boot。
U-Boot 运行起来之后,会读取 boot.scr 定义的启动命令,从你指定的存储介质中加载 kernel 的 image.ub,同时加载设备树,然后跳转到内核入口。整个流程中任何一个环节出错,串口上都会有对应的错误信息,比如Wrong image format for U-Boot表示 U-Boot 镜像格式不对,Bad Linux ARM64 Image magic表示内核镜像格式不对,这些都是排查启动问题的线索。
从这个流程可以看出,BOOT.BIN、image.ub、boot.scr 三者的关系是一环扣一环的。如果换了 U-Boot 但没换 image.ub,或者换了 image.ub 但 boot.scr 里加载地址不对,都会导致启动失败。所以我强烈建议,每次编译打包之后,把 images/linux/ 下的 BOOT.BIN、image.ub、boot.scr 三个文件作为一个整体更新到 SD 卡,不要只替换其中一个。
6. 制作可启动 SD 卡与上电验证
6.1 SD 卡分区的正确姿势
从 SD 卡启动 Zynq Linux 需要一个启动分区和一个根文件系统分区。启动分区必须是 FAT32 格式,里面放 BOOT.BIN、image.ub、boot.scr;根文件系统分区可以是 ext4 格式,里面存放 rootfs。步骤很简单,但有几个容易踩的坑。
首先用lsblk确认 SD 卡的设备名,假设是/dev/sdb,千万不要搞错,否则会把宿主机的硬盘格式化,那就悲剧了。然后用fdisk分区:
sudo fdisk /dev/sdb在 fdisk 交互界面里,输入o创建新的分区表,然后n新建分区,第一个分区大小设为 1GB(或者 512MB 也行),类型选择t并输入c,设为 W95 FAT32 (LBA)。再n新建第二个分区,剩余空间全部分给 rootfs。最后w写入并退出。
分区完成后,格式化两个分区:
sudo mkfs.vfat -F 32 /dev/sdb1 sudo mkfs.ext4 /dev/sdb2这里有个经验之谈:SD 卡作为启动介质,分区表格式传统上用 MBR 就好,不要弄成 GPT,Zynq 的 BootROM 对 GPT 支持有限,会找不到启动镜像。
6.2 拷贝启动文件与 rootfs 的解包
格式化完成后,把自动生成的启动文件拷贝到第一个分区:
sudo mount /dev/sdb1 /mnt/boot sudo cp images/linux/BOOT.BIN /mnt/boot/ sudo cp images/linux/image.ub /mnt/boot/ sudo cp images/linux/boot.scr /mnt/boot/ sudo umount /mnt/boot然后准备 rootfs。PetaLinux 生成的 rootfs 有两种形态,一种是在 images/linux/rootfs.tar.gz,一种是直接以目录形式存在于 build/tmp/rootfs 下。我们用 tar 包来解压到 ext4 分区比较常见:
sudo mount /dev/sdb2 /mnt/rootfs sudo tar -xzf images/linux/rootfs.tar.gz -C /mnt/rootfs sudo umount /mnt/rootfs解压完成后,可以用sync确保数据落盘,然后安全弹出 SD 卡,插到开发板上电。如果一切正常,串口终端上会在几十秒内看到 U-Boot 的启动日志,然后进入内核启动流程,最后出现 Linux 登录提示符。
6.3 串口工具与启动日志观察技巧
观察启动日志最常用的工具是 minicom 和 screen。以 minicom 为例,先确认串口设备名,一般 USB 转串口是/dev/ttyUSB0,开发板上自带的串口可能是/dev/ttyS0或者/dev/ttyAMA0,然后配置波特率,Zynq 默认通常是 115200:
sudo minicom -D /dev/ttyUSB0 -b 115200启动时如果卡在某一行,先看卡住的位置,不同位置对应不同阶段的问题。卡在 U-Boot 阶段,日志会停在Hit any key to stop autoboot或加载文件的提示处;卡在内核阶段,日志末尾可能是Kernel panic - not syncing: VFS: Unable to mount root fs,说明根文件系统有问题。启动日志是最直接的线索,建议完整保存下来,排错时逐行分析。
另外有一个小心得:开发调试阶段,建议在 U-Boot 启动阶段按任意键进入 U-Boot shell,执行printenv检查 bootargs 和 bootcmd 的值,确认加载的文件名和地址正确,再执行boot手动启动。这样可以绕开 boot.scr 的干扰,快速定位问题。
7. 常见启动问题与排查技巧实录
7.1 启动卡死或反复重启
启动时反复重启,最典型的原因有三个。第一个是 FSBL 阶段 DDR 初始化失败,这通常是因为硬件描述里的 DDR 型号、速率和实际板子不一致,需要回到 Vivado 里核对 DDR IP 配置。第二个是 PMUFW 或 ATF 缺失导致后续启动阶段失败,Zynq UltraScale+ 平台如果 BOOT.BIN 里没打包 pmufw.elf 和 bl31.elf,系统会在启动早期卡住。第三个是电源问题,开发板供电不足,大电流阶段会自动复位。
排查方法很简单:先看日志尾部输出到哪一行。如果日志在 FSBL 阶段就断了,优先怀疑 DDR 配置;如果日志到了 U-Boot 但加载 image.ub 时重启,优先怀疑镜像或供电。
7.2 找不到根文件系统(VFS panic)
Kernel panic - not syncing: VFS: Unable to mount root fs是出现频率最高的错误之一,几乎每个新手都会遇到。这个错误说明内核本身已经运行起来了,但找不到你指定的根文件系统。可能的原因有两个:bootargs 里 root= 参数指定的设备名不对,或者对应的分区/文件系统格式不匹配。
排查时先确认 rootfs 是打在 initramfs 里还是在外置分区。如果是在外置分区,确认 SD 卡的分区格式和 bootargs 里的参数一致。比如 bootargs 里写的是root=/dev/mmcblk0p2 rw rootfstype=ext4,那 SD 卡的第二个分区就必须是 ext4 格式,并且 rootfs 已经解压进去了。
7.3 设备树相关报错与处理
启动时设备树相关的报错,常见的有FDT: Failed to process reserved-memory和ERROR: reserving fdt memory failed。这类报错通常是因为 U-Boot 加载设备树的内存地址与内核镜像的内存地址重叠了。解决方法是通过 U-Boot 环境变量调整加载地址,或者检查设备树里 reserved-memory 节点是否和 DDR 实际布局冲突。
还有一个很隐蔽的情况是设备树编译时没有把某些节点编进去。你改了system-user.dtsi,但 PetaLinux 因为某些原因没有把修改纳入最终编译,导致启动后看到的设备树还是没有你的节点。这时可以先清理设备树编译缓存再重新编译:
petalinux-build -c device-tree -x distclean petalinux-build -c device-tree然后再整体打包,问题一般能解决。
7.4 启动问题排查速查表
下表整理了一些我在实际调试中遇到过的高频问题、原因和解决方向,方便你快速定位。
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| BootROM 阶段卡住,串口无输出 | SD 卡分区表/启动文件缺失 | 确认 BOOT.BIN 已拷入 FAT32 分区,分区表为 MBR |
| U-Boot 打印后停在加载文件 | image.ub 或 boot.scr 加载地址错误 | 进 U-Boot shell 执行 printenv 查看 bootcmd |
| 启动到内核时报错,日志末尾有 “Kernel panic” | root 参数或 rootfs 挂载失败 | 核对 bootargs 中 root= 与 SD 卡分区是否一致 |
| 加载驱动时提示无法识别外设 | 设备树节点缺失或 compatible 不匹配 | 查看 /proc/device-tree 确认节点是否存在 |
| 网络不通,eth0 不存在 | 内核缺少网卡驱动或设备树未配置 | 检查 kernel menuconfig 和 dtsi 中网卡节点 |
| 系统启动极慢 | SD 卡速度慢或 rootfs 频繁 I/O | 换高速卡,或者用 initramfs 方式启动 |
排查问题别急着反复烧卡,先把日志保存下来,对照表格逐项排查,效率会高很多。
8. 从开发到部署的几点个人体会
一路走下来,PetaLinux 给我的感受是:它确实把嵌入式 Linux 开发的门槛降低了,但并没有把复杂度消灭,而是把复杂度转移到了配置层面。你用 Yocto 手工搭平台时,复杂度在 recipe 和 layer 的编写上;你用 PetaLinux 时,复杂度在理解它生成的那些默认配置上。所以学习 PetaLinux,不要只停留在敲命令的层面,一定要弄懂它帮你做了什么,目录结构背后的逻辑是什么,生成的 BOOT.BIN 里各段的作用是什么。这些知识是相通的,哪怕以后换了别的构建工具,底层原理还是那套。
我个人的建议是,新接触 PetaLinux 的朋友,不要一上来就追求大而全的定制,先老老实实把默认流程跑通,从 Vivado 出 XSA,到创建工程、编译、烧 SD 卡、看到登录提示符。这个过程能帮你建立对整个启动链路的感觉。然后再逐步尝试修改设备树、添加驱动、定制 rootfs,一次只改一个变量,保持对其他配置的“不变”,这样出了问题你能快速判断是哪一步引起的。
在实际开发中,PetaLinux 还有一个让人又爱又恨的特点:它生成的整条工具链相对闭合,如果你按官方给出的路径走,体验很顺;但如果你想做一些“非常规”的定制,比如自己写一个外部模块、集成一个非标准的 BSP,就需要对 Yocto 底层机制有足够理解。这时候多翻翻 PetaLinux 的文档和 Yocto 的 Mega Manual,比在网上漫无目的地搜错误日志更有用。文档虽然啰嗦,但确实是排查问题最可靠的依据。
下一篇我准备写设备树修改和自定义 IP 驱动的集成,那部分会更贴近实际项目场景。你在照着这篇文章做的过程里遇到任何问题,欢迎在评论里带上启动日志一起讨论,我看到都会回复。嵌入式 Linux 这条路上,踩坑是常态,关键是踩完坑能总结出点东西来,那就值了。