
1. 为什么选 Petalinux 而不是手工拼装 Linux先搞清它的工作边界做 ZYNQ 开发的人应该都有过这种感觉Vivado 里搭个 Block Design 导个 bit 文件跑个裸机 helloworld 其实不难难的是把 Linux 拉起来。你要自己交叉编译 U-Boot手动写设备树折腾内核根文件系统三样东西之间还得版本对上。这里面任何一个环节出错表现出来都是启动阶段莫名其妙的卡死或者串口没有输出排查起来相当痛苦。我第一次在 ZYNQ 上做 Linux 项目时就是手工拼的先是 U-Boot 源码编译然后是内核设备树的适配前前后后花了两个星期最后还是靠论坛里零散的帖子才把系统拉起来。后来换到 Petalinux第一次完整跑通整个流程只用了半天。不是说 Petalinux 有多智能而是它把嵌入式 Linux 定制里最繁琐的“把硬件描述转成软件配置”这一层自动化了。Petalinux 本质上是一套以 Yocto 为基础的嵌入式 Linux 开发工具链它的核心工作逻辑是从 Vivado 导出的 XSA 文件硬件描述包里提取外设信息自动生成 FSBLFirst Stage Boot Loader、设备树源文件、U-Boot 配置再配合内核、rootfs最终打包出可启动的 BOOT.BIN 和 image.ub。你不需要手工去写那些 PS 端外设的设备树节点比如 UART、SD 控制器、以太网Petalinux 会根据 XSA 里的配置自动生成。不过要明确一个边界Petalinux 不是给你做好了一个能用的 Linux 发行版它给你的是“生成系统的手段和框架”。你仍然需要告诉它你要什么内核功能、要什么 rootfs 组件、要哪些用户程序。它的价值在于把整条链路的版本依赖关系替你管好了让你从“拼装工人”变成“配置决策者”。本文面向的读者是手里有一块 ZYNQ-7000 系列板卡7020、7010 都适用想用 Petalinux 2023.1 配合 Vivado 2023.1 定制一套 Linux 系统并且希望在遇到报错时能自己定位问题而不是每碰一个障碍就去群里问。我会按实际操作的顺序来讲从环境准备到最终启动验证都会覆盖中间穿插我踩过的坑和排查思路。2. 环境准备与 Petalinux 2023.1 安装版本匹配是第一道门槛2.1 版本对应关系Petalinux、Vivado、Ubuntu 三者必须匹配Petalinux 的版本号是跟着 Vivado 走的2023.1 版本就对应 Vivado 2023.1。这里有个很容易被忽视的点Petalinux 在解析 XSA 文件时会校验 Vivado 版本信息如果你用 Vivado 2022.2 导出 XSA然后用 Petalinux 2023.1 去导入很可能会报类似 Hardware Description 版本不兼容的错误。所以第一步先把版本绑死Vivado 2023.1 Petalinux 2023.1。另一个问题是宿主机 Ubuntu 版本。Petalinux 2023.1 官方支持 Ubuntu 18.04、20.04、22.0422.04 是后来补充支持的。我自己实测下来22.04 能用但需要额外装一些兼容库20.04 最稳。如果你是新手直接用 Ubuntu 20.04 LTS 可以省掉很多版本兼容的麻烦。磁盘空间和内存也别忽视。完整构建一次 ZYNQ 的工程Petalinux 安装本身占 30~50GB安装时可以选择组件构建过程中产生的临时文件和缓存还要额外 30GB 以上。建议给虚拟机或宿主机至少预分配 120GB 磁盘空间内存 8GB 起步16GB 更舒服。我用 8GB 内存构建过能跑但比较煎熬特别是同时开浏览器查资料的时候。2.2 依赖包安装与常见环境问题安装 Petalinux 之前先把依赖库装齐。Ubuntu 20.04 上最小安装集合如下sudo apt-get update sudo apt-get install -y \ build-essential git autoconf automake libtool \ gcc-multilib g-multilib lib32z1 lib32ncurses6 \ lib32stdc6 libssl-dev libgmp-dev libmpc-dev \ libglib2.0-dev libgtk2.0-dev libx11-dev \ libxext-dev libxrender-dev libjpeg-dev \ libncurses5-dev libncursesw5-dev \ net-tools tftpd-hpa xinetc expect \ iproute2 iputils-ping curl wget \ python3 python3-pip python3-venv \ chrpath diffstat cpio file texinfo \ gawk python3-distutils unzip注意 Ubuntu 22.04 上没有 lib32ncurses6 这个包只有 lib32ncurses-dev而且 python2 相关的工具链问题更多一些。所以我在 2.1 里强调 20.04 更稳是有实际理由的。装完依赖后检查两件事。第一件事确认默认 shell。Petalinux 的安装脚本要求 /bin/sh 指向 bash。Ubuntu 默认是 dash需要改sudo dpkg-reconfigure dash弹窗里选 No也就是保持 /bin/sh 指向 bash。如果不改安装脚本会在某个环节莫名退出报错信息还很模糊很容易把人带偏。所以我建议在一开始就处理掉。第二件事检查是否有 tftp 服务需要的用户。Petalinux 在生成 U-Boot 后可能需要 tftp 传递镜像安装脚本会自动创建 tftp 用户和目录但如果你之前装过别的 tftp 服务导致冲突安装也会中断。保险起见先确认没有残留服务。2.3 安装 Petalinux 与工程目录规划去赛灵思官网下载 Petalinux 2023.1 安装器文件名一般是 petalinux-v2023.1-installer.run。安装前想清楚你要装到哪个目录这个目录有讲究路径里不能有空格不能是 root 用户的家目录也不建议放在 Windows 共享或 NTFS 挂载分区上——Petalinux 的构建过程对文件权限和符号链接极其敏感跨文件系统很容易出奇怪的问题。我习惯装在 /opt/petalinux 下然后给当前用户赋权限sudo mkdir -p /opt/petalinux sudo chown -R $USER:$USER /opt/petalinux然后执行安装./petalinux-v2023.1-installer.run --dir /opt/petalinux安装器会让你选择要装的组件默认全选即可。如果只做 ZYNQ-7000 的 Linux 定制不必纠结组件选择全选最省心磁盘空间够的情况下没必要省那几十 GB。安装完成后设置环境变量source /opt/petalinux/settings.sh这个命令每次开新终端都要执行所以我建议写进 ~/.bashrcecho source /opt/petalinux/settings.sh ~/.bashrc source ~/.bashrcsource 之后可以用 which petalinux-create 验证环境是否生效。这条命令每条新终端都必须 source报 petalinux-create: command not found 的人九成都是省了这一步。3. 从 Vivado 导出 XSA 到创建 Petalinux 工程定制的真正起点3.1 Vivado 侧的硬件准备XSA 里必须包含 bitstreamPetalinux 定制的起点不是 Linux 命令而是 Vivado 里的硬件设计。你需要在 Vivado 2023.1 里完成 Block Design配置好 Zynq PS 的 DDR、UART、SD、以太网等外设然后综合实现生成 bitstream最后 File Export Hardware 导出 XSA。这里有一个我见过无数人踩的坑Export Hardware 的时候对话框里有一个 Include bitstream 选项默认是不勾选的。如果你不勾选导出的 XSA 里只有硬件描述信息没有 PL 侧的 bit 流。Petalinux 导入这样的 XSA 不会报错但后续你生成的 BOOT.BIN 里就不会包含 PL 配置上电后 FPGA 逻辑不工作外设当然也起不来。我之前帮人排查过一个 ZYNQ 板卡的问题Linux 启动了串口能登入但 AXI GPIO 控制的 LED 死活不亮查设备树节点也是正常的最后发现就是导出 XSA 时没勾选 bitstream。这类问题表面现象和根因离得很远排查起来特别耗时。所以养成一个习惯只要有 PL 逻辑导出 XSA 永远勾选 Include bitstream。导出命令行的方式也可以如果你们项目有自动化构建的要求write_hw_platform -include_bit -file ./system.xsa导出完成后你其实可以把 .xsa 文件当 zip 解压开来看看里面的内容。它会包含 hardware_description.bit、ps7_init_gpl.c/h、xparameters.h 等文件。Petalinux 解析 XSA 时主要靠前两个文件生成 FSBL 相关的初始化代码和 bit 流。3.2 创建 Petalinux 工程模板选择与硬件描述导入拿到 XSA 后回到终端创建 Petalinux 工程。petalinux-create -t project -n zynq_linux --template zynq这里的 --template 参数要注意ZYNQ-7000 用 zynqZynq UltraScale MPSoC 用 zynqMPVersal 用 versal。选错了模板构建出来的内核和 FSBL 会和你实际的芯片不匹配启动阶段大概率卡死。工程创建完成后进入目录导入 XSAcd zynq_linux petalinux-config --get-hw-description/path/to/your/xsa这个命令会把 XSA 里面的硬件配置解析出来生成对应的设备树、U-Boot 配置和内核配置模板。执行的时候会弹出一个图形化配置界面基于 Kconfig这是 Petalinux 核心的配置入口。界面里能改的东西很多但新手只需要关注几个关键项Subsystem AUTO Hardware Settings这里能看到从 XSA 解析出来的 PS 外设信息DDR 类型、UART 地址、SD 控制器等。一般不需要手动改除非你做的是特殊的硬件设计。Advanced Bootable Images Settings U-Boot ConfigurationU-Boot 的配置默认就行除非你有重定向启动的需求。Image Packaging Configuration Root filesystem type选 INITRAMFS 还是 EXT4。SD 卡启动通常用 EXT4固化到 QSPI Flash 用 INITRAMFS 更常见。这个决定后续 rootfs 怎么打包。保存退出后Petalinux 会自动完成第一次配置解析。这一步如果报错大概率是前面的环境问题比如 /bin/sh 没改、磁盘空间不足等。3.3 工程目录结构知道东西都放在了哪里创建完工程后pedalinux 的目录结构有几个关键位置你一定要知道后面排查问题全靠它们zynq_linux/ ├── images/linux/ # 构建输出 ├── project-spec/ │ ├── meta-user/ # 用户自定义层 │ │ └── recipes-bsp/ │ │ └── device-tree/ # 设备树源文件 │ ├── meta-plnx-generated/ # Petalinux 自动生成层 │ └── configs/ # 配置文件 ├── build/ # 构建过程文件Yocto 工作区 └── components/ # 源码组件目录日常开发中你手动修改的绝大多数文件都在 project-spec/meta-user/ 下面。Petalinux 的哲学是“自动生成的部分不要手动改手动改的部分集中在 meta-user 里”这样即使重新导入 XSA你的自定义配置也不会被冲掉。有一个很实用的经验如果你在 project-spec/meta-user 外面修改了自动生成的文件重新 petalinux-build 时这些修改很可能被覆盖。所以所有定制内容都要落实到 meta-user 层。4. 设备树与内核定制自己加的硬件逻辑如何接入 Linux4.1 设备树文件的组织方式system-user.dtsi 是唯一的入口ZYNQ 的设备树在 Petalinux 里是自动生成的但你完全可以在 system-user.dtsi 里追加或覆盖节点。这个文件位于project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi模板大概长这样/include/ system-conf.dtsi / { chosen { bootargs consolettyPS0,115200 earlyprintk root/dev/mmcblk0p2 rw rootwait; }; };注意第一行 include 了 system-conf.dtsi这个文件是 Petalinux 根据 XSA 自动生成的 PS 外设配置包含 UART、SD、以太网等节点的基本信息。你不需要重复定义这些节点否则会出现 duplicate node 冲突编译直接失败。我之前帮一个客户看问题他们自己在 system-user.dtsi 里写了一遍完整的内存节点和 UART 节点结果和生成的 pcw.dtsi 里的节点冲突编译时报“Error: duplicate node name” 一堆错误。实际上正确的做法是只写你需要覆盖的属性或者新增的 PL 外设节点不要整个节点都重写。4.2 添加自定义 PL 外设一个 AXI GPIO 的完整示例假设你在 Block Design 里添加了一个 AXI GPIO地址是 0x40000000你想在 Linux 里通过 /sys/class/gpio 来控制它。在 system-user.dtsi 中新增节点/include/ system-conf.dtsi / { axi_gpio_0: gpio40000000 { compatible xlnx,xps-gpio-1.00.a; reg 0x40000000 0x10000; #gpio-cells 2; gpio-controller; }; };然后重新编译设备树petalinux-build -c device-tree编译完成后新的设备树文件会输出到 images/linux/system.dtb。你可以用 fdtdump 或者 dtc 反编译确认节点是否打进去了dtc -I dtb -O dts -o system.dts system.dtb grep -A 5 gpio40000000 system.dts看到节点存在后还要确认内核里打开了 GPIO 子系统对应的驱动配置。很多人在这里会卡住设备树节点明明加了/sys/class/gpio 下看不到设备。原因多半是内核配置里没有使能 GPIO_SYSFS 或者对应驱动没编进去。避免这个问题最简单的办法是在配置内核时打开相关选项具体操作见下一小节。4.3 内核配置petalinux-config -c kernel 的正确打开方式内核配置入口petalinux-config -c kernel这条命令会进入内核的 menuconfig 界面基于当前 XSA 生成的默认配置之上叠加你的修改。对于 ZYNQ-7000 平台常用的几个配置项位置Device Drivers - GPIO Support - /sys/class/gpio/... (sysfs interface) - Serial drivers - Xilinx UART - Network device support - Ethernet driver support - Xilinx Gigabit Ethernet修改完保存退出Petalinux 会把 config 碎片写入工程配置下次完整构建时生效。如果你只是改了内核配置而不涉及设备树也可以单独构建内核petalinux-build -c kernel这里要提醒一个经验如果你之前从未构建过内核直接 -c kernel 会因为缺少依赖而失败建议先跑一次完整的 petalinux-build 让整个流程跑通再去做增量定制。首次构建大约需要 20~40 分钟取决于机器性能这是正常的。还有一个内核定制里常见的问题是版本漂移。Petalinux 2023.1 默认内核是 6.1Yocto 的 linux-yocto 层会按固定的 SRCPISource Revision拉取源码。如果你在网上看了一些“手动替换内核源码”的教程跟着做了之后版本源改变了有可能导致编译失败或者启动时内核 panic。建议优先使用 Petalinux 自带的内核不要轻易替换。4.4 如何通过 Petalinux 编译外部设备树和内核一个实操补充这个问题在嵌入式 Linux 社区里经常被问到。部分场景下你需要维护一套不在 Petalinux 工程内的外部设备树源码或内核源码比如公司的内核仓库是独立维护的不希望把主线的代码直接塞进 Petalinux 的 components 里。我实际用过的可行方案有两种。第一种外部设备树。把你要参与编译的 dts/dtsi 文件放到一个自定义目录比如 /home/user/external_dts然后在 system-user.dtsi 里用绝对路径 include 外部文件/include/ /home/user/external_dts/my_pl_overlay.dtsi再执行petalinux-build -c device-tree如果路径正确外部 dtsi 会被一起编译进 system.dtb。这个方案适合只追加少量自定义节点的场景。第二种外部内核源码。这个方法稍微麻烦一点需要动到 bbappend 文件。在 meta-user 层下创建project-spec/meta-user/recipes-kernel/linux/linux-xlnx_%.bbappend内容指定外部源码位置FILESEXTRAPATHS_prepend : ${THISDIR}/files: SRC_URI_append file://defconfig KERNEL_SRC_PATH /home/user/external_kernel_source do_configure_prepend() { cp -r ${KERNEL_SRC_PATH}/* ${S}/ }然后重新构建内核petalinux-build -c kernel注意这种方式的坑在于它会把你指定的外部源码拷进 Petalinux 的源码目录然后在此基础上生成配置和编译。如果外部源码本身不是完整的 Linux 源码树比如只有 patch这个方案会失败。我更推荐用 Yocto 标准的 externalsrc 机制来管理外部内核源码在工程配置里加echo INHERIT externalsrc project-spec/meta-user/conf/user.conf echo EXTERNALSRC_pn-linux-xlnx /home/user/external_kernel_source project-spec/meta-user/conf/user.conf然后用 petalinux-build -c kernel 就能直接用外部源码构建不需要把源码物理拷贝进去。这种方式更干净也能够跟踪源码目录里的修改。5. Rootfs 定制与应用程序集成让系统不只是能启动5.1 rootfs 类型怎么选INITRAMFS 还是 EXT4很多初学者在配置 rootfs 时会纠结选哪种类型。这个选择其实取决于你的启动介质和产品需求。INITRAMFS 是把根文件系统打包成一个 cpio 归档和内核一起放进 BOOT.BIN 或者 image.ub 里启动时内核把它解压到内存中作为根文件系统。优点是启动介质只需要一个分区对 QSPI Flash 启动很友好缺点是所有对 rootfs 的修改都是临时的断电即失。EXT4 是把 rootfs 做成一个 ext4 格式的镜像文件烧写到 SD 卡或 eMMC 的独立分区中启动时内核挂载该分区作为根文件系统。优点是修改持久适合开发和调试缺点是需要额外的存储分区不适合 QSPI Flash 这种小容量介质。在 Petalinux 里切换 rootfs 类型petalinux-config进入 Image Packaging Configuration Root filesystem type选 EXT4SD 卡场景或 INITRAMFSQSPI 场景。如果是 SD 卡启动还有一个细节EXT4 rootfs 镜像生成后并不是直接 dd 到 SD 卡而是在 SD 卡上已经手动分好区、格式化好 rootfs 分区后把镜像解包进分区里。命令是sudo mkfs.ext4 /dev/sdX2 sudo mkdir -p /mnt/rootfs sudo mount /dev/sdX2 /mnt/rootfs sudo tar -xzf /path/to/images/linux/rootfs.tar.gz -C /mnt/rootfs sudo umount /mnt/rootfs注意 Petalinux 构建 EXT4 类型的 rootfs 时会在 images/linux/ 下生成 rootfs.tar.gz 和 rootfs.ext4 两个文件前者是解包用的后者是直接整分区烧写用的。我一般用 rootfs.tar.gz 解包到已有分区的方案因为可以保留分区原有的一些配置。5.2 meta-user 层集成自研应用程序Recipe 的写法一个很常见的需求是我需要让 Linux 启动后自动跑我们自己写的可执行程序。Petalinux 的标准做法是在 meta-user 层添加一个应用 recipe。步骤很简单。假设你的程序是 /home/user/myapp/ 下编译出来的可执行文件 myapp先在工程目录下创建 recipe 结构project-spec/meta-user/recipes-apps/myapp/ ├── files/ │ └── myapp └── myapp.bbmyapp.bb 内容SUMMARY My custom application SECTION applications LICENSE MIT LIC_FILES_CHKSUM file://${COMMON_LICENSE_DIR}/MIT;md50835ade698e0bcf8506ecda2f7b4f302 SRC_URI file://myapp S ${WORKDIR} do_install() { install -d ${D}${bindir} install -m 0755 ${WORKDIR}/myapp ${D}${bindir}/myapp }然后在 Petalinux 的 rootfs 配置里勾选这个 recipepetalinux-config -c rootfs进入 user packages 分类勾选 myapp保存退出。构建时它会自动编译并把它安装到 rootfs 的 /usr/bin 目录。如果你希望开机自启还需要加一个 init 脚本或者 systemd service。Petalinux 2023.1 默认使用 systemd所以写一个 service 文件最干净project-spec/meta-user/recipes-apps/myapp/files/myapp.service内容[Unit] DescriptionMy custom application Afternetwork.target [Service] ExecStart/usr/bin/myapp Restartalways [Install] WantedBymulti-user.target然后改 myapp.bb 把 service 也安装进去并在 SYSTEMD_SERVICE 变量里声明inherit systemd SRC_URI file://myapp file://myapp.service do_install() { install -d ${D}${bindir} install -m 0755 ${WORKDIR}/myapp ${D}${bindir}/myapp install -d ${D}${systemd_unitdir}/system install -m 0644 ${WORKDIR}/myapp.service ${D}${systemd_unitdir}/system/myapp.service } SYSTEMD_SERVICE_${PN} myapp.service重新构建后系统启动时会自动拉起 myapp而且因为配了 Restartalways程序崩溃后 systemd 会自动重启它这个特性在嵌入式产品里非常实用。5.3 交叉编译链的使用脱离 Petalinux 命令行也能编应用除了用 recipe 让 Petalinux 在构建时编译应用还有一种开发模式日常迭代调试时直接用交叉编译工具链编好可执行文件scp 到板子上运行快速验证最后再把稳定版本集成进 recipe 走完整构建。Petalinux 提供 SDK 工具链导出功能petalinux-build --sdk petalinux-package --sysroot前者会生成 .sh 安装脚本安装后里面带有完整的交叉编译工具链和系统库。安装完成后 sourcesource /opt/petalinux_sdk/environment-setup-cortexa9t2hf-neon-xilinx-linux-gnueabi之后就可以直接$CC -o myapp myapp.c注意环境变量里的 CC 已经配置成了交叉编译器直接用 $CC 而不是 gcc。这里顺带提一下热词里“qt zynq serialport 库编译”的问题。在 ZYNQ 上跑 Qt 应用如果用 Petalinux 提供的 sysroot 交叉编译 Qt SerialPort 模块注意在 rootfs 配置里勾选 qtbase 和 qtserialport 相关的包然后 SDK 安装时确保包含这些开发库。否则交叉编译时会出现找不到 QSerialPort 头文件的错误。SDK 的 --sysroot 参数在 environment-setup 脚本里已经预设好了直接编译即可。5.4 sstate 缓存第二次构建快十倍的关键Petalinux 构建慢的一个主要原因是每次清空 build 目录后所有东西都要重新编译。如果你要经常做 rootfs 或内核配置的迭代强烈建议在工程配置里启用 sstate 缓存。在 project-spec/meta-user/conf/petalinuxbsp.conf 里添加INHERIT sstate-mirrors SSTATE_DIR /opt/sstate-cache同时把构建输出的 sstate 目录保留住。启用后只有改动到的包会被重新编译其他包直接复用缓存第二次构建的时间能从 30 分钟缩短到 3~5 分钟效果非常明显。有一个我踩过的小坑SSTATE_DIR 指向的目录如果是在 Windows 共享或网络盘上会因为文件锁问题导致 sstate 写入失败然后整个构建报一个很隐晦的错误。所以 sstate 缓存目录用本地磁盘就对了。6. 常见报错排查从安装到编译的完整故障链路这一章我按报错发生的时间顺序整理从安装阶段、工程创建阶段、构建阶段到打包启动阶段每个报错我都会还原现场、给排查思路和解决办法。6.1 安装阶段“source 后 petalinux-create 找不到命令”现象在任意目录执行 petalinux-create提示 command not found。这个错误九成九是环境变量没有加载。验证方法ls /opt/petalinux/settings.sh echo $PETALINUX如果 PETALINUX 变量为空说明 settings.sh 没有 source或者 source 的文件路径不对。解决source /opt/petalinux/settings.sh但还有一种更隐蔽的情况你 source 了PETALINUX 变量也设置了但 petalinux-create 还是找不到。这时候检查你的安装路径是不是有空格比如 /home/user/my petalinux 这种路径。Petalinux 的工具链大量脚本用空格做分隔符路径里出现空格会导致所有命令都失效。解决方法是把安装目录改到无空格路径重新安装。6.2 安装阶段“ERROR: Must be in the host group” 或 “Sorry, this platform is not supported”这个报错通常发生在 Ubuntu 版本不被支持时。Petalinux 2023.1 安装器有平台检测逻辑如果检测到 /etc/os-release 里写的版本不在支持列表里就会直接拒绝安装。排查思路是确认你的系统版本cat /etc/os-release如果版本号没问题但仍然报 not supported检查一下系统是不是从低版本升级上来的比如从 20.04 升级到 22.04。升级过的系统里 /etc/os-release 可能写的是新版本但内核版本或者 glibc 仍然是旧的安装器检测兼容性时会判断失败。一个临时绕过的手段是用--platform参数强制指定比如./petalinux-v2023.1-installer.run --dir /opt/petalinux --platform ubuntu-22.04但我不建议新手用这种绕过方式因为后续构建大概率还是会遇到版本不匹配的编译错误。更靠谱的做法是干净装一个官方支持版本的 Ubuntu 虚拟机一劳永逸。6.3 工程创建阶段导入 XSA 时报“Hardware Description 版本不匹配”现象petalinux-config --get-hw-description 之后终端输出类似于[ERROR] Hardware description does not match with the Petalinux version根因很简单Vivado 导出的 XSA 版本和 Petalinux 版本不一致。例如你用了 Vivado 2022.2 导出 XSA但 Petalinux 是 2023.1。排查链路也很直接到 Vivado 的安装目录下确认版本或者重新用匹配的 Vivado 版本导出一份 XSA。如果项目硬件设计是别人用别的版本 Vivado 做的你没有对应的版本那就需要把项目里 Block Design 的 tcl 脚本拿过来在你本机对应版本的 Vivado 里重新生成工程、重新综合导出。这个坑提醒大家一个项目管理的原则硬件设计工具版本和软件工具版本要提前对齐并且写进项目文档。不然等到联调阶段才发现版本不匹配重新综合一次可能要花好几个小时。6.4 构建阶段“ERROR: bitstream not found in XSA”这个报错在第一次 petalinux-build 时经常出现。字面意思很清楚Petalinux 在 XSA 里没找到 bit 流文件。排查链路如下第一步确认 XSA 是否真的包含 bitstream。直接解压看unzip -l system.xsa | grep bit如果发现没有 .bit 文件那问题就发生在 Vivado 导出阶段。回到 VivadoFile Export Hardware确认勾选 Include bitstream重新导出。第二步如果你的工程已经创建完了修改 XSA 后需要重新导回petalinux-config --get-hw-description/path/to/new/system.xsa然后重新构建。注意这个操作会刷新自动生成的设备树配置如果你之前手动改过 meta-user 下的文件确认它们还在通常不会被覆盖。6.5 构建阶段报 Python 相关的 UnicodeDecodeError 或 ModuleNotFoundErrorPetalinux 的 Yocto 构建流程重度依赖 Python 3.8~3.10 之间的具体行为如果你系统里默认的 python3 版本太高比如 Ubuntu 24.04 自带的 3.12或者系统里同时存在多版本 Python构建时会出现各种 ImportError 或编码错误。我遇到的一个典型案例UnicodeDecodeError: ascii codec cant decode byte 0xe8 in position 9: ordinal not in range(128)这一般是因为某个源码包里包含非 ASCII 字符的文件名或字符串而系统 locale 没设好。排查和解决export LC_ALLen_US.UTF-8 export LANGen_US.UTF-8另一个常见的是缺少模块比如ModuleNotFoundError: No module named jinja2这个多半不是真的缺而是 Petalinux 使用了它自己打包的 python 环境但当前 shell 的 PYTHONPATH 污染了。排查方法echo $PYTHONPATH如果输出非空unset 它再重新构建unset PYTHONPATH之前有个人把自定义的 PYTHONPATH 写进了 .bashrc导致 Petalinux 构建一直报错花了两天才定位到。6.6 构建阶段网络相关的下载失败源码包拉不下来Petalinux 构建过程中会从网上下载一些源码包和依赖。如果你的构建环境处于内网或者网络策略严格构建可能在 fetch 阶段卡很久然后报错ERROR: xxx-1.0-r0 do_fetch: Network Error: Connection timed out排查链路第一步确认哪些包下载失败。看报错信息里的包名去 /opt/petalinux 对应的 downloads 目录确认缓存。第二步建立本地缓存或镜像。Petalinux 支持离线构建前提是你提前把所有需要的源码包放进 downloads 目录。最笨但有效的方式在有网络的机器上先跑一次构建让它把源码包全部缓存到 /opt/petalinux/downloads然后把这个目录拷贝到内网机器上。工程配置里指向这个目录DL_DIR /opt/petalinux/downloads在 project-spec/meta-user/conf/petalinuxbsp.conf 里加上这一行之后构建就不会再外网下载。还有一点sstate 缓存也能帮忙。如果内网机器之前构建过相同版本的另一个工程把 sstate 目录共享过去大部分包直接复用根本不需要重新下载。6.7 打包阶段petalinux-package --boot 生成 BOOT.BIN 失败构建完成后生成启动镜像petalinux-package --boot --fsbl --fpga --u-boot --force这里的参数解释一下--fsbl 表示包含第一级引导从 XSA 编译出的 FSBL--fpga 表示包含 PL bit 流--u-boot 表示包含 U-Boot SSBL。这三样缺一不可。如果你漏了 --fpga生成的 BOOT.BIN 不包含 PL 配置上电后 FPGA 逻辑不工作如果漏了 --fsbl生成的 BOOT.BIN 根本没法启动连 U-Boot 都到不了。常见的报错是ERROR: Failed to boot. Check FSBL and bitstream.排查链路确认 images/linux/ 下有没有对应的输入文件——system.bit、image.ub、u-boot.elf、zynq_fsbl.elf。如果没有 u-boot.elf 或 zynq_fsbl.elf说明前面的构建没有完整成功先回去检查 petalinux-build 是否有 warning 级别的错误被忽略了。一个我反复见到的低级错误直接把 images/linux/ 下已生成的 BOOT.BIN 当作输入文件之一传给 petalinux-package——这不是它的输入是它的输出。把 BOOT.BIN 删掉或者换个输出目录再打包。另外如果你用 --force 重新打包它会覆盖旧文件。这个过程如果失败最常被忽略的原因其实是磁盘空间不足。打包时会把临时文件写到 /tmp 或者工程目录的 build 目录下空间不够时中途退出报错信息还特别长容易被淹没。看到打包失败且日志提到 space 或 write 字样先df -h检查。7. 烧写与启动从 BOOT.BIN 到串口登录的全链路验证7.1 SD 卡启动分区、镜像摆放与启动模式开关SD 卡启动是 ZYNQ 开发阶段最常用的方式也是调试效率最高的方式。因为 Linux 修改完 rootfs 可以直接替换文件不需要重新烧写整个 Flash。SD 卡需要两个分区分区文件系统大小内容分区1FAT32500MB 左右BOOT.BIN、image.ub、boot.scr分区2ext4剩余空间rootfs解包 rootfs.tar.gz分区操作用 gparted 或者 fdisk 都行关键点在于分区1必须是 FAT32而且文件系统标签不要有特殊字符。为什么 BOOT.BIN 必须放在 FAT32 上因为 ZYNQ 的 BootROM 只能从 FAT32 分区读取启动镜像这是芯片硬件固化好的行为没法改。镜像摆放完成后把板卡的启动模式拨码开关设为 SD 启动。ZYNQ-7000 的启动模式由 MIO[5:4] 引脚的电平决定拨码开关一般标着 SD/Flash/JTAG 三种模式。参照你板卡原理图或丝印说明设置。上电后串口连接调试终端波特率 1152008N1。正常启动时你会依次看到 BootROM 加载 FSBL、FSBL 加载 U-Boot、U-Boot 加载内核最后进入 Linux 登录提示。7.2 QSPI Flash 烧写JTAG 固化时到底要不要先初始化 DDR关于热词里提到的“zynq 7020 使用 JTAG 固化 flash 时必须使用 DDR 吗”这个问题要从启动流程说起。ZYNQ QSPI Flash 启动的完整链路是BootROM 从 QSPI 的启动镜像区读取 FSBL第一级引导加载到芯片内部的 OC0M片上 256KB SRAM里执行FSBL 运行后负责初始化 DDR、读取 bit 流配置 PL、加载 U-Boot 到 DDR 中执行U-Boot 再接管后续引导。关键点在于BootROM 和 FSBL 前期的执行过程都不依赖 DDR。所以如果你的 BOOT.BIN 比较小可以直接被 BootROM 完整加载到 OCM 中执行那么整个固化过程并不强制要求先初始化 DDR。那为什么有些人说必须先用 DDR因为默认情况下面FSBL 在设计时被配置为“加载 U-Boot 到 DDR 地址并跳转”。如果你的 BOOT.BIN 里 FSBL、bitstream、U-Boot 加起来的大小超过了 OCM 容量256KBBootROM 只能把 FSBL 自身加载进去接下来 FSBL 必须从 Flash 继续读取剩余内容而 FSBL 内部逻辑默认会把后续镜像放到 DDR 里。此时如果 DDR 还没初始化好加载就会失败。所以结论是是否必须用 DDR取决于你的 BOOT.BIN 大小和 FSBL 的加载策略。开发阶段我们通常不用关注这个细节因为 QSPI 烧写一般用 Vivado Hardware Manager 的 JTAG 路径或者 Petalinux 的烧写命令它们会先把镜像读入主机端缓存再逐段编程不经过 DDR。用 Petalinux 烧写 QSPI Flash 的命令petalinux-package --boot --fsbl --fpga --u-boot --force petalinux-boot --jtag --prebuilt 3或者直接用 Vivado Hardware Manager通过 JTAG 连接板卡在 Flash Programming 界面选择 BOOT.BIN 烧写。这个方法我在 7020 板卡上用过很多次稳定可靠。烧写完成后的启动验证和 SD 卡类似只是需要把拨码开关切到 QSPI 启动模式。7.3 串口启动日志怎么看一个正常的 log 长什么样很多初学者拿到一块板子串口没有输出或者输出卡住不知道从哪里查起。我建议先记住一个正常的 ZYNQ Linux 启动日志应该包含哪几个阶段第一阶段BootROM一般没有输出但如果是 SD 启动且镜像加载失败会有错误码输出。这个阶段极短常驻现象是串口一开始什么动静都没有。第二阶段FSBL初始化了 UART输出类似Xilinx Zynq FSBL 2023.1 Silicon version: 3如果你看不到这段说明 FSBL 没有正确执行。常见原因是启动模式拨错、镜像损坏、时钟配置错误。优先检查拨码开关和镜像文件。第三阶段U-BootU-Boot 2023.01-gxxxx DRAM: 1 GiB MMC: mmce0100000: 0 Loading environment from FAT...这里能看到 DDR 大小是否和硬件匹配MMC 控制器是否识别到 SD 卡。如果卡在这里多半是设备树里 SD 控制器配置有问题或者 SD 卡分区格式不对。第四阶段内核Starting kernel ... [ 0.000000] Booting Linux on physical CPU 0x0然后是一大堆内核初始化日志。最后是[ OK ] Started System Logging Service. Zynq login:看到 login 提示系统就起来了。用户名为 root默认无密码直接回车进入。记住这个四阶段结构排查时对照“卡在哪个阶段”就能快速缩小范围。比如能进 U-Boot 但起不了内核问题大概率在内核镜像或设备树能起内核但挂载不了 rootfs问题大概率在 bootargs 里的 root 参数或分区格式。7.4 常见启动问题的快速定位对照表现象可能原因排查动作串口完全无输出启动模式错误 / 镜像未找到 / 串口接线错误检查拨码、串口连接、BOOT.BIN 存在性FSBL 阶段卡死FSBL 读取外部镜像失败 / DDR 时序配置错误检查 XSA 中 DDR 型号是否与实际芯片一致U-Boot 阶段 SD 卡找不到分区格式不对 / 设备树 SD 节点异常确认分区1为 FAT32检查 dts 中 sd 节点内核启动后卡在 rootfs 挂载bootargs 里 root 分区号不对检查 /dev/mmcblk0p2 与 bootargs 是否一致启动到 login 但 GPIO 设备不可用PL bit 流没加载 / 设备树节点缺失 / 驱动未编译先确认 BOOT.BIN 中包含 FPGA bit这套对照表基本覆盖了我这些年遇到的大部分启动故障场景。实际排查时我习惯先复现问题把完整串口日志用 Tera Term 或 minicom 保存下来然后按上面的阶段定位。日志是排查启动问题最好的线索空口描述“不起来”没用看日志里的最后一行输出往往就直接指向根因了。8. 构建加速、版本管理与后续扩展把这套流程用得更顺的几条经验到这里一套完整的 ZYNQ Petalinux 2023.1 定制流程已经跑通了。最后分享几个我在实际项目里积累的经验算不上完整的教程章节但都是让这套流程更顺手的细节。第一构建加速。前面提过 sstate 缓存这里再强调下它的正确用法。如果你同时维护多个 Petalinux 工程比如一个 7020 和一个 7010 的板卡两个工程共用同一个 SSTATE_DIR 是安全的因为 sstate 是按任务和包内容做哈希匹配的相同部分直接复用不冲突。实际项目中我通常把 sstate 目录放在一块 SSD 上效果比机械硬盘明显好很多。另外petalinux-build 支持并行编译参数用petalinux-build -j 4或者更多能显著缩短构建时间。我的经验是 -j 8 在 8 核机器上收益最大再往上提升不明显了。第二工程版本管理。Petalinux 工程里有很多文件是构建过程自动生成的不适合全部纳入 Git 版本管理。我一般只把 project-spec/meta-user、project-spec/configs 和 images/linux 下的预编译镜像纳入仓库其余目录加 .gitignore。这样团队成员拉取代码后执行 petalinux-build 就能完整重现构建不会把几 GB 的中间产物都塞进仓库。第三从“能启动”到“能交付”你需要额外关注的东西。一套 Linux 能登录只是起点产品化还要考虑文件系统只读化防止意外篡改、应用日志落盘和轮转、看门狗机制、系统升级方案。Petalinux 2023.1 的 rootfs 定制能力足够支撑这些需求核心思路都是通过 meta-user 层添加对应的配置和模块思路和前面添加 myapp 的方式一致。举个升级方案的例子我最近一个项目用的是 AB 分区方案Flash 里放两套镜像和两份 rootfsU-Boot 里根据环境变量决定启动哪一套。升级时把新镜像写入非激活分区写入成功后切换启动标志如果新系统启动失败则 U-Boot 自动回滚到旧分区。这套逻辑在 Petalinux 里实现不复杂U-Boot 的 distro boot 机制天然支持关键是提前把分区布局设计好。第四遇到问题时的信息收集方式。我帮人远程排查 Petalinux 问题时最痛苦的就是对方只发一句“构建失败了”却不给完整日志。Petalinux 构建日志的位置build/tmp/work/xxx/yyy/temp/log.do_build或者直接看终端完整输出的末尾部分。下次你在群里问报错问题时先把最后 20 行日志贴出来别人一眼就能看出问题在哪比你描述半天现象高效得多。这套流程我前前后后带过好几个项目组落地最常见的现象就是第一遍跑的时候各种报错但把环境问题和版本匹配问题解决干净之后后续再做其他板卡或新功能时非常顺。Petalinux 真正节省的时间不在于第一次构建而在于你修改硬件配置后重新生成系统的那个过程。传统手工方式改一个外设可能要重新生成设备树、改 U-Boot 配置、重新编译内核两次修改之间隔的时间越长忘记的细节越多。Petalinux 把这一切收敛成了“重新导入 XSA、重新构建”两个动作这才是它最大的价值所在。