本文转载自:coldnew's blog
在 zybo board 开发记录: Zynq 与 LED 闪烁控制 一文中我们谈到了如何透过 C 语言撰写独立的程序,让它控制 Zynq 的处理器系统 (Processing System, PS) 去闪烁 LED 的亮暗。既然 Zynq 的处理器系统 (Processing System, PS) 使用的是 ARM Cortex-A9 的处理器,那当然也可以让我们跑 Linux 在 Zybo Board 上。
本文将简述如何自行编译 u-boot 以及 Linux Kernel,并搭配 Busybox 制作简单的 RootFS 执行于 Zybo Board 上。
开发目标
在这次的开发中,我们要编译 u-boot、Linux,并使用 Busybox 制作简单的 Rootfs 后,透过制作 SD 卡来让 Zybo Board 透过 SD 卡开机。
要注意到的是,由于我们要透过 SD 卡开机进入到 Linux 系统,因此我们要透过 JP5 去更改开机模式。
了解开机流程
既然我们要让 Zybo board 执行 Linux 系统,就要先来了解一下开机流程,才知道我们大概需要准备哪些东西。从Zynq-7000 All Programmable SoC: Embedded Design Tutorial - A Hands-On Guide to Effective Embedded System Design (UG1165) 可以看到 Zynq-7000 执行 Linux 系统的开机流程图。
也就是说,当开始提供电源给 Zynq 处理器系统 (Processing System, PS) 并完成重置(reset) 后,Zynq 内建的 Boot ROM 会去加载 第一阶段开机程序 (First Stage Boot Loader, FSBL) ,接着加载 比特流 (bitstream) 去初始化整个 可程序逻辑(Programmable Logic, PL) 。 完成后,接下来就是透过 U-Boot 去加载 Linux Kernel、Device Tree 以及 Root File System。
了解了这个,我们就知道我们大概要准备哪些东西了。
设定好环境
在安装玩 Vivado 与 Xilinx SDK 后,实际上包含 Zynq 在用的 ARM toolchain 亦同时被安装到系统中,我们只要使用 source 命令即可让当前的环境知道 xilinx-arm toolchain 的路径。这边以 Viavdo 2016.2 作为范例。
coldnew@gentoo ~ $ source /opt/Xilinx/Vivado/2016.2/settings64.sh
这样就可以获得 arm-xilinx-* toolchain 的命令,实际上有哪些呢? 输入个 arm-xilinx- 按下 TAB 看看
如果你系统上已经有其他的 ARM toolchain 的话,可以考虑跳过这一步骤,接下来要格式化 Micro SD 卡。
格式化 MicroSD 卡
在这次的开发中,我们要设定 MicroSD 卡片成两个分区,第一个是 fat32 格式,第二个则使用 ext4 格式,若不会使用 fdisk 命令的话,可以透过 gparted 来进行格式化,以下是我格式化卡片的范例 (8GB 卡片)。
(实际上在本文的范例中,只会用到第一个分区,第二个分区是为了往后文章要开机到大一点的 rootfs 准备的。)
编译 u-boot
我们首先去 GitHub 下载 DigilentInc 加入 zybo board 后的 u-boot 版本,要注意这边要选择 master-next 分支。
git clone https://github.com/DigilentInc/u-boot-Digilent-Dev.git -b master-next
完成后进入到该文件夹
coldnew@gentoo ~ $ cd u-boot-Digilent-Dev
编译 u-boot,记得指派编译目标为 zynq_zybo_config
coldnew@gentoo ~/u-boot-Digilent-Dev $ CROSS_COMPILE=arm-xilinx-linux-gnueabi- make zynq_zybo_config
coldnew@gentoo ~/u-boot-Digilent-Dev $ CROSS_COMPILE=arm-xilinx-linux-gnueabi- make
编译完成后,注意一下 u-boot 这个档案,他就是我们等等要用到的 u-boot 执行档,不过由于 Xilinx Tool 要找有 .elf 扩展名的档案,因此我们把它复制成 u-boot.elf 。
coldnew@gentoo ~/u-boot-Digilent-Dev $ cp u-boot u-boot.elf
编译 Linux kernel
编译好 u-boot 后,接下来就是编译 Linux Kernel 了,我们一样选择 DigilentInc 加入 zybo board 后的 Linux Kernel 版本,记得要选 master-next 分支。
git clone https://github.com/DigilentInc/Linux-Digilent-Dev.git -b master-next
接着,当然就是编译了,不过在这之前请先确定你有装 u-boot-tools 这套件,我们需要里面的 mkimage 指令,Gentoo Linux 可以直接用以下命令来安装。
coldnew@gentoo ~ $ sudo emerge dev-embedded/u-boot-tools
完成后进入 Linux Kernel 文件夹
coldnew@gentoo ~ $ cd Linux-Digilent-Dev
编译我们需要的 uImage 文件,记得要指定 config 为 xilinx_zynq_defconfig 以及设定 UIMAGE_LOADADDR为 0x8000 。
coldnew@gentoo ~/Linux-Digilent-Dev $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make xilinx_zynq_defconfig
coldnew@gentoo ~/Linux-Digilent-Dev $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make
coldnew@gentoo ~/Linux-Digilent-Dev $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make UIMAGE_LOADADDR=0x8000 uImage
coldnew@gentoo ~/Linux-Digilent-Dev $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make zynq-zybo.dtb
编译完后,我们会需要 arch/arm/boot/uImage 以及 arch/arm/boot/dts/zynq-zybo.dtb 这两个档案,后者就是 device tree 编译出来的数据文件。
由于放入到 SD 卡上的 device tree 文件名为 devicetree.dtb ,因此这边将 zynq-zybo.dtb 改一下名。
coldnew@gentoo ~/Linux-Digilent-Dev $ cp arch/arm/boot/dts/zynq-zybo.dtb devicetree.dtb
如果你想手动修改 Device Tree 并再重新编译的话,也可以这样去产生我们要的 devicetree.dtb 。
coldnew@gentoo ~/Linux-Digilent-Dev $ ./scripts/dtc/dtc -I dts -O dtb -o devicetree.dtb arch/arm/boot/dts/zynq-zybo.dts
编译 BusyBox
Busybox 是一个非常有趣的程序,举凡我们在 Linux 下最常用的命令如 ls、cd 等到 sed、vi 他都具有相对应的简单实现,此外,这些命令实际上都只是一个软连结 (symlink) 连结到名为 busybox 的执行档,也就是说,如果我们将 busybox 进行静态编译 (static link),则制作出来的系统整体大小大约为 2 MB (kernel) + 1.4 MB (busybox),而这个系统却又可以具有许多 UN*X 下的常用命令,也因此 busybox 很常用于空间有限的系统。
我们在这个开发过程中,由于只是验证执行 Linux 系统的功能,因此选用 Busybox 来作为我们的 rootfs。
首先先下载 Busybox 的原始码,这里选用 1_25_stable 这个稳定分支
git clone git://git.busybox.net/busybox -b 1_25_stable
进行我们自己的设定
coldnew@gentoo ~/busybox $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make menuconfig
在进行设定时有以下几点要确实注意,我们要将 busybox 编译为静态链接,并且增加 init 功能,主要设定如下:
Busybox Settings --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)
Init Utilities --->
[*] init
Login/Password Management Utilities --->
[*] getty
Shells --->
[*] ash
设定完成后开始进行编译
coldnew@gentoo ~/busybox $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make
编译完成后透过 make install 命令,会将编译出来的 busybox 与软连结(symlink)产生在 _install 文件夹内
coldnew@Rosia ~/busybox $ ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- make install
建立一些缺少的文件夹 (/dev、/sys …etc)
coldnew@gentoo ~/busybox $ cd _install && mkdir -p proc sys dev etc/init.d root
建立 etc/init.d/rcS 作为启动脚本,并添加以下内容
coldnew@gentoo ~/busybox/_install $
vim etc/init.d/rcS
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
/sbin/mdev -s
将 etc/init.d/rcS 加入可执行权限
coldnew@gentoo ~/busybox/_install $ chmod +x etc/init.d/rcS
建立 etc/inittab ,这会让我们可以透过 UART 登入 zybo board
coldnew@gentoo ~/busybox/_install $
vim etc/inittab
#!/bin/sh
#
Init script
::sysinit:/etc/init.d/rcS
#
Start shell on the serial ports
::respawn:/sbin/getty -L ttyPS0 115200 vt100
#
What to do when restarting the init process
::restart:/sbin/init
#
What to do before rebooting
::shutdown:/bin/umount -a -r
设定默认的 /etc/passwd 档案,我们要让 root 用户登入时不用输入密码
coldnew@gentoo ~/busybox/_install $
vim etc/passwd
root::0:0:root:/root:/bin/sh
建立 /init 并软连结到 /sbin/init ,避免 Linux Kernel 开机时找不到 rootfs 的 init。
coldnew@gentoo ~/busybox/_install $ ln -s /sbin/init init
接下来,由于这次我们只是要开机到 ramdisk 上的 rootfs, 因此将 busybox 做出的 rootfs 打包成 cpio 格式。
coldnew@gentoo ~/busybox/_install $ find . | cpio -H newc -o | gzip -9 > ../uramdisk.cpio.gz
再透过 mkimage 将这个 uramdisk.cpio.gz 档案转成 uboot 用的 uramdisk.image.gz
coldnew@gentoo ~/busybox/_install $ mkimage -A arm -T ramdisk -C gzip -d ../uramdisk.cpio.gz ../uramdisk.image.gz
Image Name:
Created:
Sun Jul 17 19:02:08 2016
Image Type: ARM Linux RAMDisk Image (gzip compressed)
Data Size: 1042106 Bytes = 1017.68 kB = 0.99 MB
Load Address: 00000000
Entry Point: 00000000
在这边的这个 uramdisk.image.gz 就是我们开机会进入到的 rootfs,也是我们等等要放到 SD 卡第一个扇区的档案。
编译比特流 (bitstream)
在 zybo board 开发记录: 升级 Digilent 提供的设计档 一文中,我们提到了怎样升级 Digilent 提供的预先定义好接脚的配置文件 (zybo_base_system) ,这次的项目,我们就直接用这个配置文件案来进行 Linux 开机的动作。
首先你必须根据该篇文章,将你的 Zybo board 配置文件案升级到你用的 Vivado 版本,完成后我们重新建立一个干净的项目。
首先先把先前生成的旧项目清掉:
coldnew@gentoo ~/ZYBO/Projects/linux_bd/proj $ sh cleanup.sh
接下来用 Vivado 2016.2 重新生出新的项目 ~
coldnew@gentoo ~/ZYBO/Projects/linux_bd/proj $ /opt/Xilinx/Vivado/2016.2/bin/vivado -mode batch -source create_project.tcl
完成后,会看到 ZYBO/Projects/linux_bd/proj 目录变成这样:
coldnew@gentoo ~/ZYBO/Projects/linux_bd/proj $
tree -L 1
.
├── cleanup.cmd
├── cleanup.sh
├── create_project.tcl
├── ip_upgrade.log
├── linux_bd.cache
├── linux_bd.hw
├── linux_bd.ip_user_files
├── linux_bd.sim
├── linux_bd.srcs
├── linux_bd.xpr
├── vivado.jou
└── vivado.log
5 directories, 7 files
我们使用 Vivado 打开 linux_bd.xpr 这个专案。
如果你有兴趣看他生出来的 Block Design 是怎样的,也可以切到 Block Design 那页看看
我们直接点选 Program and Debug -> Generate Bitstream 产生我们要的比特流 (bitstream)
建立 FSBL
到此,我们除了 第一阶段开机程序 (First Stage Boot Loader, FSBL) 外,其他的程序都已经编译出执行档了,让我们来处理 FSBL 吧。
首先点选 File -> Export -> Export hardware
记得要勾选 Include bitstream
完成后,执行 Xilinx SDK
透过 File -> New -> Application Project 去建立我们的新项目
设定这个项目为 standalone 的项目
选择样板为 Zynq FSBL
选择我们刚刚建立的 FSBL 项目,按下右键选择 Build Project 进行编译
建立 BOOT.bin
编译完 FSBL 后,选择 Xilinx Tools -> Create Boot Image 去建立我们的 BOOT.bin
在 Boot image partitions 那边,加入我们的 bitstream 以及 u-boot 档案,记得要按照顺序加入。
完成后,点选 Create Image 就会产生我们要的 BOOT.bin 到指定路径。
在这个步骤中,如果你是指令控的话,我们也可以在产生 FSBL.elf 后,建立一个名为 boot.bif 的档案,其内容如下
//arch = zynq; split = false; format = BIN
the_ROM_image:
{
[bootloader]/path/to/fsbl-build/fsbl.elf
/path/to/linux_bd/linux_bd.sdk/linux_bd_wrapper.bit
/path/to/u-boot/u-boot.elf
}
接下来透过 bootgen 这个命令去产生 BOOT.bin
coldnew@gentoo ~ $ bootgen -image boot.bif -w on -o i boot.bin
将档案复制到 Micro SD 卡
好了,我们已经完成了所有准备动作,是时候将档案放到 Micro SD 卡并看看结果了,在本文中我们会将以下几个档案放到 第一个分割区 (fat32) 。
coldnew@gentoo /tmp/sdc1 $
tree -L 1
.
├── BOOT.bin
├── devicetree.dtb
├── uImage
└── uramdisk.image.gz
0 directories, 4 files
也就是说我们的 SD 卡有的东西,要像 The Zynq Book p.439 这张图那样
测试开机与结果
是时候来测试结果了,要注意到你的 Zybo Board 的 JP5 要设定成下面这样,这样给电时,Zynq 才会读取 SD 卡上面的 u-boot 并将比特流 (bitstream) 刻录到 FPGA 中。
插入刚刚建立好的 SD 卡,并提供电源后,我们可以使用可以接收 UART 相关的程序,如 gtkterm、teraterm、screen、emacs 等,启动它并开启 /dev/ttyUSB1 后,设定 baudrate 为 115200 ,就可以看到开机到 rootfs 的状态。
取得程序代码
本文的范例已经放置于 GitHub 上,你可以到以下的 repo 去寻找,具体项目对应的教学名称,则请参考README.md 档案
延伸阅读[1] Zynq-7000 All Programmable SoC: Embedded Design Tutorial - A Hands-On Guide to Effective Embedded System Design (UG1165)
[2] ZYBO Zync-7000 Development Board Work - Booting Linux on the ZYBO
[3] The Zynq Book
评论
查看更多