文章目录
0 写在前面
这是一个系列博客,详细介绍如何在 ZYNQ 与 ZYNQ MP 平台上如何移植 Linux 系统。目前网络上的大部分教程都是全程基于 Petalinux 的开发,虽然这样简化了开发流程,但对于初学者深入理解掌握 Linux 是不利的,所以,有了这个系列的博客,从几乎为 0 开始教大家怎么移植 Linux 系统。
本人的软件与环境版本:
Windows 的 Vivado 与 Vitis 版本:2020.2(前期学习 ZYNQ7020 跟随正点原子安装);
Ubuntu版本:18.04.2;
虚拟机上的 Vivado、Vitis 与 Petalinux 版本:2020.1(前期学习 ZYNQ MP 跟随 Alinx 安装)。
所有相关的文件存放在虚拟机的以下路径:
~/Linux_tp
如果后面涉及到一些命令行操作的文件路径,大家可以参考着修改为自己的路径。
此外,我后续的操作与实测都是黑金(Alinx)基于 ZYNQ MP 系列的 zu3eg 操作,但基本也会对 ZYNQ-7000系列的一些不同做一些说明,如果你使用的是 ZYNQ-7000 系列的板卡,可以自己灵活变通一下。
1 移植回顾与文件剖析
通过前面1~5篇内容,我们移植了 u-boot、Linux内核与根文件系统,并使用 SD 卡的 FAT 分区存放启动所需文件,EXT 分区存放根文件系统(Petalinux文件系统、Debian、Ubuntu)。
在移植过程中的整体结构与涉及文件如下图所示:
其中,我们前面移植时是没有把设备树文件包含在 BOOT.BIN 中,它实际上也是可以被包含在 BOOT.BIN 中加载的。
对于 FAT 分区内的有效文件,就使用的角度来说:
1,bl31.elf 都是完全不会更改的;
2,fsbl.elf、pmufw.elf 在不大幅度改变处理器的启动/使用情况(如后面想从纯 linux 转到 freertos + Linux 双架构)也是不需要更改的;
3,uboot.elf 在确保 u-boot 好用后也不需要更改;
4,FPGA的比特文件 xxx.bit 在每次更新 Vivado 的工程后,都需要使用新生成的比特文件;
5,与 Linux 直接相关的镜像文件 Image 和设备树文件 xxx.dtb 都是在实际开发中可能经常修改的。
而EXT 分区的根文件系统由于需要一直使用,不需要每次都重新解压。
因此,我们可以改变 BOOT.BIN 的包含内容,使得其内全是不太可能变化的内容,再通过 u-boot 去加载所谓可能随着我们学习与开发变化的内容。修改 SD 卡结构为:
FAT 分区放入5个文件:BOOT.BIN、Linux镜像 Image、设备树文件 xxx.dtb、FPGA的比特文件 xxx.bit 、u-boot 的启动脚本 boot.scr;EXT 分区则是根文件系统。
2 准备文件
2.1 BOOT.BIN
2.1.1 使用 Xilinx bootgen工具生成
具体的方法可以参考第三篇:【ZYNQ Linux移植】3-u-boot移植。
主要是需要对 .bif 进行修改,因为我们现在 BOOT.BIN 只包含 zynqmp_fsbl.elf、pmufw.elf、bl31.elf、u-boot.elf 这四个文件了:
//arch = zynqmp; split = false; format = BIN
the_ROM_image:
{
[bootloader]./zynqmp_fsbl.elf
[pmufw_image]./pmufw.elf
[destination_cpu=a53-0,exception_level=el-3]./bl31.elf
[destination_cpu=a53-0,exception_level=el-2]./u-boot.elf
}
这里同样需要注意 arch 芯片架构的问题!
2.1.2 使用Petalinux
先正常编译petalinux的工程:
petalinux-build
在打包 BOOT.BIN 时,需要注意与之前的差别!之前一般使用的是:
petalinux-package --boot --u-boot --fsbl --force(无pl端)
petalinux-package --boot --u-boot --fpga --fsbl --force(有pl端,既包含比特文件)
而现在,我们不再把设备树和比特打包进去,需要使用:
petalinux-package --boot --fsbl --u-boot --dtb no --force
从打包时打印的信息,也可以看出这里打包的 BOOT.BIN 包含的内容:
事实上,这里文件的顺序也是至关重要的!如果是使用 bootgen 生成的话在编写 .bif 时需要特别注意。
2.2 boot.scr
boot.scr 是 u-boot 的脚本文件,如果我们设置 u-boot 的环境变量 boot_cmd 为 run distro_bootcmd 的话,u-boot 会尝试从可能的启动设备上去扫描 boot.scr 这个文件,有的话就会自动执行。
这个文件在执行:petalinux-build
后,就会生成,放在 Petalinux工作目录/images/linux
。如果没有 Petalinux,也可以直接使用以下内容作为 boot.cmd.default。不过我们需要对这个 boot.src 进行一定的修改。先进入那个目录,先使用以下命令创建一个临时的文件:
cp boot.scr boot.cmd.default
如果是自己移植,没有Petalinux,可以自己找个文件夹,创建一个文件,把里面的内容修改为:
# This is a boot script for U-Boot
# Generate boot.scr:
# mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
#
################
for boot_target in ${boot_targets};
do
if test "${boot_target}" = "jtag" ; then
booti 0x00200000 0x04000000 0x00100000
exit;
fi
if test "${boot_target}" = "mmc0" || test "${boot_target}" = "mmc1" ; then
if test -e ${devtype} ${devnum}:${distro_bootpart} /image.ub; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x10000000 image.ub;
bootm 0x10000000;
exit;
fi
if test -e ${devtype} ${devnum}:${distro_bootpart} /Image; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x00200000 Image;;
fi
if test -e ${devtype} ${devnum}:${distro_bootpart} /system-top.dtb; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x00100000 system-top.dtb;
fi
if test -e ${devtype} ${devnum}:${distro_bootpart} /rootfs.cpio.gz.u-boot; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x04000000 rootfs.cpio.gz.u-boot;
booti 0x00200000 0x04000000 0x00100000
exit;
fi
if test -e ${devtype} ${devnum}:${distro_bootpart} /system.bit; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x02000000 system.bit;
fpga loadb 0 ${fileaddr} ${filesize}
fi
booti 0x00200000 - 0x00100000
exit;
fi
if test "${boot_target}" = "xspi0" || test "${boot_target}" = "qspi" || test "${boot_target}" = "qspi0"; then
sf probe 0 0 0;
if test "image.ub" = "image.ub"; then
sf read 0x10000000 0xF00000 0x6400000;
bootm 0x10000000;
exit;
fi
if test "image.ub" = "Image"; then
sf read 0x00200000 0xF00000 0x1D00000;
sf read 0x04000000 0x4000000 0x4000000
booti 0x00200000 0x04000000 0x00100000
exit;
fi
exit;
fi
if test "${boot_target}" = "nand" || test "${boot_target}" = "nand0"; then
nand info
if test "image.ub" = "image.ub"; then
nand read 0x10000000 0x4100000 0x6400000;
bootm 0x10000000;
exit;
fi
if test "image.ub" = "Image"; then
nand read 0x00200000 0x4100000 0x3200000;
nand read 0x04000000 0x7800000 0x3200000;
booti 0x00200000 0x04000000 0x00100000
exit;
fi
fi
done
如果是使用Petalinux的话,就对复制出来的这个文件进行修改,先把头部的第一行去掉。
再找到跟设备树相关的部分,把名称改为 system-top.dtb(因为内核中是让它编译成这个名字,就不用来回改了,这个可以根据自己的情况决定),再在下图位置添加对于 bit 文件的搜索的 u-boot 命令:
if test -e ${devtype} ${devnum}:${distro_bootpart} /system.bit; then
fatload ${devtype} ${devnum}:${distro_bootpart} 0x02000000 system.bit;
fpga loadb 0 ${fileaddr} ${filesize}
fi
这段 u-boot 命令的意思是检查指定的存储设备上是否存在 system.bit 文件,如果有的话,就把它加载到内存 0x02000000,再把内存中的比特流配置到 fpga。
修改后,参照这个文件上面的注释使用 mkimage 重新生成 boot.src:
mkimage -c none -A arm -T script -d boot.cmd.default boot.scr
如果提示没有 mkimage,使用以下命令安装:
sudo apt install u-boot-tools
2.3 Linux 镜像与设备树文件
2.3.1 自己单独移植
在获取文件前,首先需要先完善一下我们之前在移植过程中的设备树文件,如果是使用 Petalinux,就修改对应的 system-user.dtsi,如果是自己单独移植,就修改我们之前移植过程中放在 Linux 内核源码下设备树文件夹的 system-top.dts,把跟节点下 chosen 节点进行修改,主要是为了给 u-boot 传递环境变量的值:
/ {
chosen {
bootargs = "console=ttyPS0,115200 earlycon root=/dev/mmcblk1p2 rw rootwait";
stdout-path = "serial0:115200n8";
};
};
bootargs 指定使用串口 0 (115200波特率)作为控制台,根文件系统的目录为 SD 卡的第二个分区(EXT分区)。需要注意的是,修改完设备树后,还需要按下面步骤重新编译!
设备树其实我们之前在 u-boot 和 Linux 内核移植中都进行了编译,实际上,使用这两个地方获取的设备树都是可以的,但在后续实际的学习与开发中,还是推荐使用 Linux 内核源码包去编译获取设备树,这是由于 u-boot 在初始完成编译,得到 u-boot.elf 后,我们就不需要更改了,设备树由于和 Linux 强相关,常常需要同时修改 Linux 镜像与设备树文件,所以不如直接一次性在编译内核时搞定。
如果是完全编译 Linux内核,可以使用我们在第四篇创建的脚本:
./zynqmp.sh
当然也可以单独只编译设备树:
make dtbs
最后获得的文件位置为:
Image镜像文件:内核源码根目录/arch/arm64/boot
设备树文件 :内核源码根目录/arch/arm64/boot/dts/xilinx
2.3.2 使用Petalinux
首先需要对 Petalinux 的一些设置进行修改。在读取 xsa 时设置,或者使用以下命令打开:
petalinux-config
首先查看系统主存储设备的配置选项,根据需要修改:
-->Subsystem AUTO Hardware Settings
-->SD/SDIO Settings
-->Primary SD/SDIO
把它改为 psu_sd_1
,这个选项指定哪个 SD/SDIO 控制器将被用作系统的主要存储接口,该控制器将用于访问启动设备(如 SD 卡),是系统启动和根文件系统所在的设备。一般的ZYNQ硬件设计里面,SD0 是用作EMMC,SD1才是用作SD卡使用,具体是否需要修改,要根据具体硬件设计以及 Vivado 中 Block Design 中的 ZYNQ 核配置来决定:
修改完后,修改 dtb image settings
,绝对的路径为:
-->Subsystem AUTO Hardware Settings
-->Advanced bootable images storage Settings(确保这个选项勾选,回车进入下一级)
-->dtb image settings
-->image storage media
这样修改,相当于是设置了 dtb 文件将从 SD卡进行加载,而不是从 image.ub 中获取。
注意这也会影响 bootloader (我们用的 U-Boot) 加载 DTB 的方式。
这里还需要注意一个点,在 image storage media 同级目录下,有个image name的选项,它默认是 system.dtb。这个选项是指,后面在系统加载时,会在 SD 卡中搜索 system.dtb 进而加载,所以我们后面放进去的设备树文件,最后也得改成这个名字。
然后,修改 SD 卡相关的选项。
一是启动的文件系统类型,绝对路径为:
-->Image Packaging Configuration
-->Root filesystem type
把它改为 EXT4 ,这是指定根文件系统的文件系统类型。
二是在同级的菜单里,有一个选项叫:Device node of SD device。
把它改为 /dev/mmcblk1p2
。即指定 SD 卡第二个分区作为根文件系统。(一般 SD 卡是第一个分区设置为 FAT 分区用来启动,第二个分区设置为 EXT4 分区,作为根文件系统,具体原因,见:嵌入式Linux开发为什么要设置FAT与EXT两个分区 )。
修改完所有后,保存退出。一定要记得保存!一定要记得保存!一定要记得保存!
在 build 完后(修改了.xsa记得重新加载 .xsa),就可以在以下目录获取镜像与设备树:
petalinux工程目录/images/linux
这里有两种 image,一种是 Image,它与我们单独在第四篇:【ZYNQ Linux移植】4-内核移植 中编译所得的镜像是一致的,其实就是 Petalinux 帮我们编译得到的。
第二种则是 Petalinux 三件套的 image.ub,我们知道使用 Petalinux 的话,一般是在 FAT 分区放置上图的连续三个文件:boot.scr 、BOOT.BIN 、Image.ub。而 Petalinux 在使用时打包 BOOT.BIN 一般是把比特文件包含在内的,也就是说,与我们在第一部分的原始结构图相比,可以推测 Petalinux 的这个 image.ub 是最起码也包含上面的 Image 和设备树文件的。我们也可以通过以下命令来分析这个文件:
mkimage -l image.ub
结果与推测一致:
如果采用 Petalinux 的三件套启动方式,就使用 image.ub,同时也就不需要把设备树文件 system.dtb 放入 FAT 分区了。
2.4 FPGA部分的比特文件
2.4.1 通过 Vitis 获取
这部分在第二篇:【ZYNQ Linux移植】2-获取设备树 中的 2.2 节介绍从 Vitis 获取设备树时同时获取了 xxx.bit。
2.4.2 通过 Petalinux 获取
如果是使用 Petalinux 的话,在加载完 .xsa 并 build 后,可以在以下目录找到 xxx.bit:
petalinux工程目录/project-spect/hw-description
2.4.3 通过 Vivado 获取
当然,对于比特文件,最熟悉的应该是从 Vivado 获取,其生成后的所在路径为:
Vivado工程目录/xxx.runs/impl_x/
2.5 根文件系统
根文件系统直接按照第五篇:【ZYNQ Linux移植】5-根文件系统移植的步骤进行即可,这里没有变化。
如果是使用 Petalinux 提供的最小系统的话,还有一个可以优化的点是让根文件系统自动登录,不再需要我们再每次手动输入用户名和密码。
在 Petalinux 的工程目录下,执行以下命令:
petalinux-config -c rootfs
按以下路径进入:
-->Image Features
-->auto-login
勾选 auto-login
,再保存退出,重新编译 Petalinux!
3 u-boot修改
按照以上步骤准备好所有的文件后,放入 SD 卡的对应分区,使用 SD 卡模式启动开发板,然后快速地按回车键,在u-boot 下修改环境变量。
为了避免大家的不一致性,可以先把所有环境变量恢复到初始值:
env default -a
这里修改的主要是两个变量:bootcmd 与 boot_targets。bootcmd 是 u-boot 启动后就会运行的命令,这里的 “run distro_bootcmd” 是尝试从可能的启动设备上(也就是后面的 boot_targets)扫描 boot.scr 文件,再通过 boot.scr 文件尝试启动。使用以下命令进行修改:
env set bootcmd "run distro_bootcmd"
env set boot_targets "mmc0 mmc1 jtag qspi0 nand0 usb0 usb1 scsi0 pxe dhcp"
关于细致的这两个环境变量为什么要这么修改,可以看我另一篇关于 debug u-boot 的博客:占位。而由于我们在设备树已经对 boot_args 进行了指定,它会在执行 boot.scr 加载完设备树文件后写入,这里就不需要修改了,修改了也是无效的。
最后记得保存到 Flash,然后重新启动即可:
saveenv
reset
我这里使用的是 Debian,reset
后会自动启动系统,最后如下图所示:
4 总结
本篇主要是对前面几篇中 FAT 中的文件进行了重新组织,把原来包含在 BOOT.BIN 中的设备树文件剥离出来,让 BOOT.BIN 只包含基本不会更改的部分:fsbl.elf、pmufw.elf、bl31.elf、u-boot.elf,使用 boot.scr 加载需要经常修改的设备树文件 xxx.dtb、 Image和 xxx. bit。
5 参考资料
1,DeepSeeker相关搜索结果;
2,《正点原子 MPSoC-P5B之嵌入式Linux开发指南_V1.0》