基于ZCU104的Petalinux定制:从XSA到启动镜像的完整构建流程
1. 环境准备:搭建你的Petalinux“厨房”
拿到一块ZCU104开发板,想让它跑起来一个为你硬件量身定制的Linux系统,感觉是不是有点无从下手?别慌,这个过程就像做一道大餐,你得先把厨房(开发环境)收拾利索,把食材(工具链)备齐。我刚开始玩Zynq MPSoC的时候,也在这第一步上折腾了好久,今天就把我踩过的坑和总结的经验,掰开揉碎了讲给你听。
首先,你得有个“厨房”——也就是一台运行Linux的电脑。我强烈推荐使用Ubuntu 18.04 LTS,这是Xilinx官方文档里反复验证过的版本,兼容性最好,能帮你避开一堆莫名其妙的依赖库问题。用虚拟机(比如VMware或VirtualBox)安装一个是最省事的方案,记得分配足够的资源,建议CPU给4核以上,内存至少8GB,硬盘空间留出100GB以上,因为后续编译过程会产生大量中间文件。我试过在资源紧张的虚拟机上编译,那速度真是“感人”,等得花儿都谢了。
厨房有了,接下来就是安装“厨具套装”——Petalinux。这里有个关键点:Vivado、Petalinux和BSP(板级支持包)的版本必须严格匹配。比如你Vivado用的是2020.2,那Petalinux和BSP也得是2020.2。混用版本是新手最常见的“翻车”现场,轻则编译报错,重则生成的镜像根本无法启动。去Xilinx官网下载对应版本的Petalinux安装包,通常是一个很大的.run文件。下载前需要注册账号,这个步骤不能省。
安装前,有一堆系统依赖包需要搞定。别嫌麻烦,一个一个装,这是地基。打开终端,执行下面这条“万能”命令,它基本涵盖了Petalinux所需的所有依赖:
sudo apt-get install -y gcc git make net-tools libncurses5-dev tftpd zlib1g-dev libssl-dev flex bison libselinux1 gnupg wget diffstat chrpath socat xterm autoconf libtool tar unzip texinfo zlib1g-dev gcc-multilib build-essential zlib1g:i386 screen pax gzip gawk装完之后,有个小细节要注意:Petalinux要求系统的/bin/sh链接到bash,而不是dash(Ubuntu默认可能是dash)。检查一下:
ls -l /bin/sh如果显示链接到dash,就用下面命令改过来,在弹出的对话框里选择“否”:
sudo dpkg-reconfigure dash现在可以安装Petalinux了。我习惯把它安装到/opt目录下,结构清晰。先创建目录并修改权限(记得把yourusername换成你的实际用户名):
sudo mkdir -p /opt/pkg/petalinux/2020.2 sudo chown -R yourusername:yourusername /opt然后运行安装程序,指向刚才创建的目录:
./petalinux-v2020.2-final-installer.run --dir /opt/pkg/petalinux/2020.2安装过程会弹出一大堆许可协议,一路按回车直到最后输入y同意即可。安装时间取决于你的机器性能,喝杯咖啡等等。
安装完成后,每次打开新终端,都需要“激活”Petalinux环境。为了省事,我们把它设为别名。编辑你的~/.bashrc文件,在末尾加一行:
alias sptl='source /opt/pkg/petalinux/2020.2/settings.sh'保存后,执行source ~/.bashrc让配置生效。以后只要在终端里输入sptl,就进入了Petalinux的工作环境。至此,你的“厨房”就准备妥当了,锅碗瓢盆、油盐酱醋一应俱全,可以开始我们的“烹饪”之旅了。
2. 工程创建与硬件导入:给Linux系统一张“硬件蓝图”
环境好了,我们正式开工。第一步是创建一个Petalinux工程,并告诉它:“嘿,这是我们的硬件长什么样。” 这个“硬件说明书”就是Vivado导出的XSA(Xilinx Support Archive)文件。我建议你建立一个清晰的工作目录,比如在用户目录下建一个workspace,里面再分门别类放好各种文件,这样以后找东西不抓瞎。
mkdir -p ~/workspace/{BSP, XSA, projects}BSP:存放从官网下载的板级支持包(.bsp文件)。对于ZCU104,一定要下载和你Petalinux版本匹配的BSP,它包含了针对这块板子的基础配置,能省去大量手动配置的功夫。XSA:把你从Vivado里File -> Export -> Export Hardware...(记得勾选Include bitstream)生成的.xsa文件放进来。这个文件包含了你的PL(可编程逻辑)设计信息、PS(处理器系统)配置以及比特流。projects:用来存放后续创建的Petalinux工程。
进入工作目录,并激活Petalinux环境:
cd ~/workspace sptl接下来,使用BSP作为模板创建工程。这就像用预制好的面团做披萨底,比自己从头和面要快得多,也更容易成功:
petalinux-create -t project -n my_zcu104_linux -s ./BSP/xilinx-zcu104-v2020.2-final.bsp命令执行后,会在当前目录下生成一个名为my_zcu104_linux的文件夹,这就是你的Petalinux工程目录。
现在,要把我们自定义的硬件信息“注入”到这个工程里。进入工程目录,执行配置命令,并指定XSA文件的路径:
cd my_zcu104_linux petalinux-config --get-hw-description ../XSA/design_1_wrapper.xsa这个命令会启动一个基于文本界面的配置菜单。第一次运行时,它会解析XSA文件,自动识别出处理器型号(如Zynq UltraScale+ MPSoC)、内存大小、外设等信息,并加载到工程配置中。这里我们首先要做的是一个关键选择:系统从哪里启动?
对于ZCU104评估板,我们通常从SD卡启动。在配置菜单中,按如下路径导航:
Image Packaging Configuration ---> Root filesystem type (INITRAMFS) ---> (X) SD card选择SD card,然后按右方向键选择Save,回车确认保存配置文件名为.config,最后选择Exit退出。这一步决定了最终生成的根文件系统镜像格式,以及启动脚本中寻找根文件系统的位置。如果选错,系统启动时会在最后一步卡住,提示找不到根文件系统,我早期就因为这个坑折腾了半天。
配置完成后,你会发现在工程目录下生成了一个components/plnx_workspace/目录,里面包含了从XSA解析出的设备树源文件等。这意味着Petalinux已经“认识”了你的硬件。这一步是后续所有软件配置的基础,相当于给即将诞生的Linux系统绘制了一张准确的“硬件地图”。
3. 深度定制:内核、根文件系统与关键的内存保留配置
基础工程搭好了,但这是个“通用”系统。我们的硬件设计里,PS(ARM处理器)和PL(FPGA逻辑)之间是需要频繁交换数据的,这就需要对系统进行深度定制。核心任务有三件:配置Linux内核、定制根文件系统,以及最最重要的一步——为PS-PL数据交互预留专用的内存区域。
先说说内核配置。虽然BSP提供了默认配置,但了解如何微调很有必要。运行:
petalinux-config -c kernel这会进入内核的详细配置菜单,里面有成千上万个选项。对于初学者,大部分保持默认即可。但有一个选项强烈建议你检查,它关系到PS能否直接访问物理内存:
Kernel hacking ---> [*] Filter access to /dev/mem确保这个选项没有被选中(即括号内是空格,不是*或M)。如果被选中,内核会阻止通过/dev/mem设备访问所有物理内存,这会导致我们后续在PS端用mmap映射PL器件地址或保留内存时失败。BSP默认配置通常是关掉的,但检查一下更放心。检查完毕后,保存退出。
接下来是根文件系统配置。这里可以添加你需要的软件包,比如调试工具gdb、strace,网络工具iperf,或者python3等。命令是:
petalinux-config -c rootfs菜单里分为Filesystem Packages多个子类,你可以按需勾选。初期为了简化,可以全部保持默认。等系统跑起来后,如果发现缺少某个命令或库,再回来添加也不迟。
现在来到整个定制流程的灵魂所在——设备树(Device Tree)修改。设备树是Linux内核用来描述硬件拓扑结构的一种数据格式。我们要在这里告诉内核:“有一块内存区域是保留给PS和PL进行数据共享的,你别拿去干别的了。”
为什么这步如此关键?想象一下,PS和PL是工厂里的两个车间,它们需要共享一个仓库来传递零件。这个仓库就是一段物理内存(DDR中的一部分)。如果Linux内核不知道这个仓库被预定了,它可能把这里当成普通内存,分配给其他程序使用。结果就是,PS往里面写数据,PL来读,却发现里面全是别的程序的“垃圾数据”,或者更糟,PL写入的数据被其他进程覆盖,导致系统崩溃。
具体怎么做呢?在Petalinux工程里,自定义设备树配置放在project-spec/meta-user/recipes-bsp/device-tree/files/目录下。我们需要修改或创建system-user.dtsi或pl-custom.dtsi文件。根据我的实战经验,在Petalinux 2020.2及以后版本,更稳妥的做法是修改pl-custom.dtsi。
假设我们在Vivado设计中,为数据交互预留的DDR地址是从0x1000_0000开始,大小为0x0DF9_E000(约224MB)。那么pl-custom.dtsi文件内容应该如下:
/ { reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; buffer@0x10000000 { no-map; reg = <0x0 0x10000000 0x0 0x0DF9E000>; }; }; };我来解释一下关键字段:
reserved-memory:这是一个节点,声明了系统中所有需要保留的内存区域。buffer@0x10000000:我们自定义的保留区域节点名,地址作为名字的一部分便于识别。no-map:这个属性非常重要!它告诉内核,在系统启动的早期阶段,不要为这块内存创建页表映射。也就是说,Linux的内存管理系统完全看不到这块内存,从而保证了它的独占性。reg = <0x0 0x10000000 0x0 0x0DF9E000>:定义内存区域。前两个数字是起始地址的高32位和低32位(对于Zynq UltraScale+,地址是64位的,所以这里高32位是0x0)。后两个数字是区域大小的高32位和低32位。这里的意思就是从0x1000_0000开始,分配0x0DF9_E000字节的内存。
修改保存后,Petalinux在编译时会自动将这个文件的内容与从XSA解析出的基础设备树合并。这样,编译出来的Linux内核在启动时,就会乖乖地把这块内存圈起来,留给我们PS和PL的数据交互专用。这一步配置是否正确,直接决定了后续应用程序和FPGA逻辑能否正常通信,可以说是“失之毫厘,谬以千里”。
4. 系统构建与镜像打包:从源码到可启动的SD卡
所有配置都敲定了,接下来就是激动人心的编译构建环节。这个过程有点像把准备好的食材下锅烹饪,最终做出一盘菜。你只需要输入一个命令:
petalinux-build然后,就可以去休息一下了。首次完整构建耗时较长,在我的台式机(i7,32GB内存)上大概需要30分钟到1个小时,具体取决于你选择的软件包数量和机器性能。终端里会飞速滚动各种编译信息,只要没有红色的Error提示,就安心等待。这个过程会依次编译U-Boot(启动引导程序)、Linux内核、设备树二进制文件(.dtb)以及根文件系统。
构建完成后,所有生成的镜像文件都存放在images/linux/目录下。但这时候它们还是分散的,我们需要把它们“打包”成一个或几个可以直接烧录到SD卡的文件。这就需要用到打包命令:
petalinux-package --boot --fsbl images/linux/zynqmp_fsbl.elf --pmufw images/linux/pmufw.elf --fpga images/linux/system.bit --u-boot images/linux/u-boot.elf --force让我拆解一下这个命令的每个部分:
--boot:告诉工具我们要生成启动镜像。--fsbl:指定First Stage Bootloader的ELF文件。FSBL是上电后第一个运行的软件,负责初始化PS、加载PL比特流等。--pmufw:指定Platform Management Unit Firmware的ELF文件。PMU负责电源管理、系统监控等。--fpga:指定包含你PL设计的比特流文件(.bit)。这个文件决定了你的FPGA逻辑功能。--u-boot:指定第二阶段的引导程序U-Boot的ELF文件。--force:如果输出文件已存在则强制覆盖。
执行这个命令后,它主要做两件事:
- 将
fsbl.elf、pmufw.elf、system.bit和u-boot.elf四个文件,按照Zynq MPSoC启动要求的特定格式,拼接生成一个BOOT.BIN文件。这个文件会被启动ROM首先加载。 - 将Linux内核镜像(
Image)、设备树二进制文件(system.dtb)以及根文件系统镜像(可能是rootfs.cpio.gz或rootfs.ext4,取决于你的配置)打包生成一个image.ub文件。这是一种FIT(Flattened Image Tree)格式的镜像,U-Boot能够识别并从中加载内核和根文件系统。
打包完成后,images/linux/目录下我们最关心的就是这三个文件:
BOOT.BIN:包含所有启动阶段固件和FPGA比特流的第一启动镜像。image.ub:包含Linux内核、设备树和根文件系统的系统镜像。boot.scr:U-Boot的启动脚本文件,告诉U-Boot从哪里加载image.ub等参数。
最后一步,就是把这“三剑客”拷贝到SD卡。准备一张FAT32格式的SD卡(容量8GB以上足够),插入电脑。将上述三个文件直接复制到SD卡的根目录(注意不是任何文件夹内)。对于ZCU104,SD卡插槽在板子侧面,将制作好的SD卡插入,上电,如果一切顺利,你应该能在串口终端里看到U-Boot的启动信息,接着是Linux内核解压、启动,最后出现登录提示符。那一刻的成就感,绝对是驱动你继续深入探索嵌入式Linux世界的最佳燃料。
