本帖最后由 正点原子运营 于 2020-12-18 12:18 编辑
1)实验平台:正点原子领航者ZYNQ开发板
2)平台购买地址:https://item.taobao.com/item.htm?&id=606160108761
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-301505-1-1.html
4)对正点原子FPGA感兴趣的同学可以加群讨论:712557122
5)关注正点原子公众号,获取最新资料更新
第十八章 另一种方式编译ZYNQ镜像
本篇是ARM Linux驱动开发篇中的第一章,本章跟驱动开发并没有什么关系,由于前面我们一直都是使用xilinx的petalinux工具编译镜像文件,例如包括u-boot、linux内核、设备树、ZYNQPL端的bitstream文件以及fsbl等,虽然petalinux功能上比较全面,但是在编译速度上太慢了!完全是在浪费时间,相信大家在使用petalinux的时候已经感受到了它给你带来的无奈之处;为此笔者专门去研究了一下,那本章笔者就带大家通过另外一种比较快的方式编译各种镜像文件。 本章采用分步式的方式编译启动开发板所需要的各种镜像文件,虽然步骤比较繁琐,但灵活性比较高;虽然本章使用的是另一种方式编译镜像,但还是得需要用到petalinux提供的一些工具,所以大家一定要安装petalinux。
1.1 由hdf文件得到bit文件.bit文件是zynq pl端所需要用到的bitstream文件,例如我们使用petalinux-build命令完成工程编译之后会在images/linux目录会产生一个system.bit文件,这个就是我们这里说到bitstream文件,.bit可以通过.hdf硬件描述符文件得到,怎么得到呢?下面给大家说来。 将ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7010\3_Embedded_Linux\vivado_pro\Navigator_7010\Navigator_7010.sdk文件夹拷贝到Ubuntu系统目录下,例如/home/zynq/hdf/目录,大家根据自己的情况选择目录,本实验以领航者7010为例,如果大家用的是领航者7020,则选择ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7020\3_Embedded_Linux\vivado_pro\Navigator_7020\Navigator_7020.sdk文件夹。 在Navigator_7010.sdk目录下有一个.hdf文件,如下所示:
图 31.1.1 hdf文件 接下来使用source命令执行petalinux的环境配置脚本settings.sh,如下所示: source /home/zynq/petalinux/v2018.3/settings.sh
图 31.1.2 运行settings.sh脚本文件 /home/zynq/petalinux/v2018.3是笔者的petalinux安装目录,完成环境配置之后执行hsi命令进入到hsi命令模式,如下所示: hsi
图 31.1.3 hsi命令行 执行命令之后就可以进入到hsi命令行模式下了,hsi命令行模式就跟我们的linux命令行模式差不多,都可以执行命令,只不过执行的命令不同;hsi命令是petalinux工具提供的,所以一定安装petalinux才行;在这个模式下可以执行一些命令,例如执行下面这条命令可以打开硬件设计文件hdf得到bit文件: open_hw_design/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.hdf
图 31.1.4 打开hdf文件 Open_hw_design命令紧跟着的是hdf文件路径,命令执行成功之后会在hdf文件所在目录下产生bit文件以及ps7_init命名的.c文件和.h文件,如下所示(在hsi命令行模式下执行exit命令退出该模式): exit
图 31.1.5 退出hsi命令行模式
图31.1.6 产生bit文件及其它文件 从上图中可以看到在hdf文件所在目录下产生了design_navigator_7010_wrapper.bit文件以及.c和.h文件。 1.2 自动生成设备树文件Petalinux可以根据hdf文件描述的硬件信息自动配置U-Boot和内核所需的设备树文件,那么我们也可以通过hsi命令来生成;首先需要导入xilinx的device-tree仓库,在我们提供的资料包中已经给大家准备好了,路径为:ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7010\3_Embedded_Linux\misc\device-tree-xlnx-xilinx-v2018.3.tar.gz,大家也可以通过 https://github.com/Xilinx/device-tree-xlnx/releases网址进行下载,选择2018.3版本.tar.gz压缩格式文件,如下所示:
图31.2.1 设备树仓库下载 Xilinx device-tree描述了设备与设备树之间的匹配关系,将下载好或是资料包中的device-tree-xlnx-xilinx-v2018.3.tar.gz压缩文件拷贝到Ubuntu系统目录下,例如/home/zynq/device-tree,大家根据自己的情况选择,然后将其解压当前目录,解压之后会产生一个device-tree-xlnx-xilinx-v2018.3文件夹,如下所示:
图31.2.2 device-tree目录 那么device-tree-xlnx-xilinx-v2018.3目录就是我们所需要的device-tree仓库。接下来我们执行hsi命令进入到hsi命令行模式,执行下面这些命令: open_hw_design/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.hdf set_repo_path/home/zynq/device-tree/device-tree-xlnx-xilinx-v2018.3 create_sw_design device-tree -os device_tree -procps7_cortexa9_0 generate_target -dir /home/zynq/linux/dts
图31.2.3 生成设备树文件 首先也是通过open_hw_design命令打开hdf文件,然后使用set_repo_path命令设置device-tree仓库所在路径,例如/home/zynq/device-tree/device-tree-xlnx-xilinx-v2018.3;使用create_sw_design创建软件设计,” -os device_tree”指定了软件设计的类型,我们这里使用device-tree表示它是一个设备树类型,” -procps7_cortexa9_0”指定处理器的名称,在hsi命令模式下使用”xxxx -help”可以查看xxxx命令的用法说明,这里就不给大家列举了;最后使用generate_target命令去产生目标,也就是我们的设备树文件,” -dir /home/zynq/linux/dts”指定路径。 此时我们可以退出hsi命令行模式,进入到/home/zynq/linux/dts目录下,可以看到自动配置产生的.dts和.dtsi文件,如下所示:
图31.2.4 生成设备树文件 Linux设备树是是一个很重要的知识点,将会在后面给大家详细说明,本章先不讲,这些文件后面我们会用到。 1.3 创建、编译fsbl工程fsbl(First StageBoot Loader)是ZYNQ第一启动引导加载代码,它的主要任务就是初始化DDR,并将用户代码(例如U-Boot、bit文件或者是SDK裸机代码)从存储设备中拷贝到DDR中运行。在使用petalinux-build命令编译完成之后,在images/linux目录下有一个zynq_fsbl.elf文件,这个就是fsbl编译之后产生了elf格式文件,同样也可以使用hsi命令来创建fsbl工程并进行编译。 首先我们需要导入Xilinx embeddedsw存储库,该embeddedsw仓库用于裸机应用,例如fsbl、pmu固件、plm等,在我们提供的资料包中已经给大家准备好了,路径:ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7010\3_Embedded_Linux\misc\embeddedsw-xilinx-v2018.3.tar.gz,大家也可以从 https://github.com/Xilinx/embeddedsw/releases网址进行下载,选择2018.3版本.tar.gz压缩格式,如下所示:
图31.3.1 下载embeddedsw仓库 将下载好或是资料包中的embeddedsw-xilinx-v2018.3.tar.gz压缩文件拷贝到Ubuntu系统目录下,例如/home/zynq/embeddedsw,大家根据自己的情况选择;然后将其解压当前目录,解压之后会产生一个embeddedsw-xilinx-v2018.3文件夹,如下所示:
图31.3.2 embeddedsw目录 在Ubuntu中执行下面这条命令,建立一个软链接文件gmake,链接到make: sudo ln -s /usr/bin/make /usr/bin/gmake
图31.3.3 建立gmake软链接文件 因为在编译的过程中会用到gmake,其实gmake就是GNU make的缩写,Linux系统环境下的make就是GNU make;之所有有gmake,是因为在别的平台上,make一般被占用,GNU make只好叫gmake了;比如在进行编译时要使用make命令,但如果在非GNU系统中运行,必须使用GNU make,而不是使用系统自带的make版本,这时要用gmake代替make进行编译。 完成上面的工作之后,我们就可以进行fsbl的创建与编译了,执行hsi命令进入hsi命令行模式,执行下面这些命令: - open_hw_design/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.hdf
- set hwdsgn design_navigator_7010_wrapper // 定义hwdsgn变量
- set_repo_path/home/zynq/embeddedsw/embeddedsw-xilinx-v2018.3 // 设置embeddedsw仓库路径
- set fsbl_dir /home/zynq/linux/fsbl // 定义fsbl_dir变量
- generate_app -hw $hwdsgn -os standalone -procps7_cortexa9_0 -app zynq_fsbl -compile -sw fsbl -dir $fsbl_dir
复制代码
图31.3.4 编译fsbl-1
图31.3.5 编译fsbl-2 首先还是使用open_hw_design命令打开hdf硬件描述符文件;使用set_repo_path命令设置embeddedsw仓库所在路径,例如/home/zynq/embeddedsw/embeddedsw-xilinx-v2018.3;set命令用于定义变量,我们使用set命令定义了一个hwdsgn变量等于design_navigator_7010_wrapper,当执行generate_app命令的时候使用该变量,定义了一个fsbl_dir变量定义了用于存放fsbl工程的路径;最后使用generate_app命令产生和编译fsbl,”-hw $hwdsgn”指定硬件设计的名字,就是hdf文件去掉后缀即可,”-os standalone”指定我们的app是一个无操作系统的裸机程序,”-proc ps7_cortexa9_0”指定处理器,”-app zynq_fsbl”指定app的名字(这个不能改),”-compile”表示要编译生成的源文件(如果不加的话则表示仅产生fsbl工程源码并不进行编译),”-sw fsbl”指定本软件设计的名字(这个名字可以自己定),”-dir$fsbl_dir”我们的fsbl工程存放的路径,笔者这里以/home/zynq/linux/fsbl为例,大家根据自己的情况设定。 整个过程完成之后,我们可以执行exit命令退出hsi命令行模式了,此时/home/zynq/linux/fsbl目录下的内容如下所示:
图31.3.6 fsbl工程源码目录 /home/zynq/linux/fsbl就是我们通过hsi命令生成的fsbl对应的源码目录,这个就像是在Windows下使用SDK软件创建了一个FSBL工程一样,上图中的executable.elf文件fsbl工程编译之后得到elf格式文件;得到fsbl源码工程之后,我们就可以直接在fsbl工程目录进行编译了,例如,在/home/zynq/linux/fsbl目录下执行make clean命令对fsbl工程目录进行清理,如下: make clean
图31.3.7 清理工程 我们可以修改Makefile文件,将编译产生的fsbl镜像文件名字改为zynq_fsbl.elf,在fsbl工程目录下,打开Makefile文件,修改EXEC变量,如下所示: vi Makefile
图31.3.8 修改EXEC变量 将该变量由executable.elf修改为zynq_fsbl.elf,除此之外,我们还可以看到变量CC := arm-none-eabi-gcc,这指定了编译fsbl时使用的交叉编译工具,这个不用改,修改完成之后保存退出即可!我们直接在该目录下执行make命令即可编译,如下: make
图31.3.9 make编译fsbl工程 编译成功之后产生的镜像文件就成了fsbl_zynq.elf,如下所示:
图31.3.10 fsbl镜像文件 1.4 编译U-Boot在<第十四章 U-Boot移植>中,已经让大家把xilinx官方的U-Boot拷贝到Ubuntu系统中了,并对他们进行了修改,添加了一些自己定义的文件, 本篇教程不使用前面移植后的U-Boot,我们用一份新的xilinx官方2018.3版本( 这个版本是xilinx设定的版本,其U-Boot版本为2018.01)的U-Boot源码,源码已经提供给大家了,路径为:ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7010\3_Embedded_Linux\资源文件\uboot\u-boot-xlnx-xilinx-v2018.3.tar.gz,大家也可以通过 https://github.com/Xilinx/u-boot-xlnx/releases网址进行下载。 首先将U-Boot源码压缩包文件拷贝到Ubuntu系统目录中,例如/home/zynq/linux/u-boot目录,并将其解压到当前目录,解压之后会产生一个u-boot-xlnx-xilinx-v2018.3文件夹,进入到该目录下,就可以看到U-boot源码目录下的文件和文件夹了,如下所示:
图31.4.1 U-Boot源码目录 在前面给大家讲过,使用"petalinux-config -c u-boot"命令可以对U-Boot进行配置、使用"petalinux-build-c u-boot"命令可以编译u-boot,那么这些方法都是使用petalinux工具时的做法,这里教大家如何在U-Boot源码目录下配置、编译而不通过petalinux-xxx命令; 关于U-Boot我们已经在前面给大家进行了详细的讲解,包括Makefile文件、各个文件目录的作用以及源码的分析、U-Boot配置、U-Boot常用命令等等,这里就不再讲解。 1、添加设备树文件 关于设备树的概念现在不给大家讲,后面会说。除了Linux内核支持设备树之外,我们使用的U-Boot也是支持设备树的;在31.2小节中我们使用hsi命令生成了一些dts和dtsi的设备树源文件,现在我们要用到它们了,这些设备树文件都是根据我们的hdf硬件设计而自动生成的,如果我们使用U-Boot中自带的设备树文件则不能满足我们的情况。 将前面生成的设备树文件(笔者这里生成的设备树文件在/home/zynq/linux/dts目录下)pcw.dtsi、pl.dtsi、system-top.dts以及zynq-7000.dtsi四个文件拷贝到U-Boot源码目录下的arch/arm/dts目录下,拷贝过去之后进入到arch/arm/dts目录下查看,如下所示:
图31.4.2 拷贝设备树文件到U-Boot 接下来我们需要对system-top.dts文件进行一个简单地修改,打开system-top.dts文件,修改之前内容如下: 示例代码20.4.1 system-top.dts修改之前
- /*
- * CAUTION: This file is automaticallygenerated by Xilinx.
- * Version: HSI
- * Today is: Mon Mar 16 02:51:23 2020
- */
- /dts-v1/;
- #include "zynq-7000.dtsi"
- #include "pl.dtsi"
- #include "pcw.dtsi"
- / {
- chosen {
- bootargs = "earlycon";
- stdout-path = "serial0:115200n8";
- };
- aliases {
- ethernet0 = &gem0;
- i2c0 = &i2c_2;
- i2c1 = &i2c0;
- i2c2 = &i2c1;
- serial0 = &uart0;
- serial1 = &uart1;
- spi0 = &qspi;
- };
- memory {
- device_type = "memory";
- reg = <0x0 0x20000000>;
- };
- };
复制代码这里我们对该文件进行一个简单地修改,修改之后如下所示: 示例代码20.4.2 system-top.dts修改之后
- /*
- * CAUTION: This file is automaticallygenerated by Xilinx.
- * Version: HSI
- * Today is: Mon Mar 16 02:51:23 2020
- */
- /dts-v1/;
- #include "zynq-7000.dtsi"
- #include "pl.dtsi"
- #include "pcw.dtsi"
- / {
- model = "AlientekZYNQ Development Board";
- chosen {
- bootargs = "console=ttyPS0,115200earlyprintk root=/dev/mmcblk0p2 rw rootwait";
- stdout-path = "serial0:115200n8";
- };
- aliases {
- ethernet0 = &gem0;
- i2c0 = &i2c_2;
- i2c1 = &i2c0;
- i2c2 = &i2c1;
- serial0 = &uart0;
- serial1 = &uart1;
- spi0 = &qspi;
- };
- memory {
- device_type = "memory";
- reg = <0x0 0x20000000>;
- };
- };
- &gem0 {
- local-mac-address= [00 0a 35 00 1e 53];
- };
- &qspi {
- #address-cells= <1>;
- #size-cells= <0>;
- flash0:flash@0 {
- compatible = "n25q512a","micron,m25p80";
- reg = <0x0>;
- #address-cells= <1>;
- #size-cells= <1>;
- spi-max-frequency= <50000000>;
- partition@0x00000000 {
- label = "boot";
- reg = <0x00000000 0x00500000>;
- };
- partition@0x00500000 {
- label = "bootenv";
- reg = <0x00500000 0x00020000>;
- };
- partition@0x00520000 {
- label = "kernel";
- reg = <0x00520000 0x00a80000>;
- };
- partition@0x00fa0000 {
- label = "spare";
- reg = <0x00fa0000 0x00000000>;
- };
- };
- };
复制代码修改完成之后保存退出即可! 以上主要修改了bootargs属性、添加了model属性、网口0的MAC地址以及给qspi进行了分区操作。修改完成之后,我们还需要将system-top.dts文件添加到arch/arm/dts/Makefile文件中,找到dtb-$(CONFIG_ARCH_ZYNQ)这里,将system-top.dtb(.dts编译之后得到的就是.dtb文件)添加上去,这样当CONFIG_ARCH_ZYNQ宏被选中的时候,system-top.dts才会被编译进去,如下所示:
图31.4.3 添加system-top.dtb文件 注意在zynq-zybo.dtb后面加上一个反斜杠”\”,跟上面的保持一致。 添加好之后保存退出文件。 2、修改zynq-common.h和zynq_zc70x.h配置文件 include/configs/目录下有很多的配置文件,该目录下的配置文件中有很多宏定义,这些宏定义将会在编译U-Boot源码时会被用到;前面我们使用petalinux工具的时候并没有去配置U-Boot、内核等,原因在于petalinux确实比较智能,它能够根据hdf文件记录的信息自动配置,例如一些时钟频率、外设时钟、环境变量偏移量等。 zynq-common.h和zynq_zc70x.h都在include/configs目录下,zynq-common.h是xilinx提供的用于配置zynq系列处理器的通用配置文件,zynq-common.h头文件被zynq_zc70x.h包含,下面我们要对这两个头文件进行一些简单地修改! 首先对zynq_zc70x.h文件进行修改,修改之前如下所示: 示例代码20.4.3 zynq_zc70x.h修改之前
- /*
- * (C) Copyright 2013 Xilinx, Inc.
- *
- * Configuration settings for the Xilinx ZynqZC702 and ZC706 boards
- * See zynq-common.h for Zynq common configs
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
- #ifndef__CONFIG_ZYNQ_ZC70X_H
- #define__CONFIG_ZYNQ_ZC70X_H
- #defineCONFIG_ZYNQ_I2C0
- #defineCONFIG_ZYNQ_EEPROM
- #include <configs/zynq-common.h>
- #endif /*__CONFIG_ZYNQ_ZC70X_H */
复制代码这里我们要把CONFIG_ZYNQ_I2C0和CONFIG_ZYNQ_EEPROM宏定义给去掉,原因就是我不想在U-Boot中使用I2C设备,直接给去掉!修改之后如下: 示例代码20.4.4 zynq_zc70x.h修改之后
- /*
- *(C) Copyright 2013 Xilinx, Inc.
- *
- *Configuration settings for the Xilinx Zynq ZC702 and ZC706 boards
- *See zynq-common.h for Zynq common configs
- *
- *SPDX-License-Identifier: GPL-2.0+
- */
-
- #ifndef__CONFIG_ZYNQ_ZC70X_H
- #define__CONFIG_ZYNQ_ZC70X_H
- #include <configs/zynq-common.h>
- #endif /*__CONFIG_ZYNQ_ZC70X_H */
复制代码接下来修改zynq-common.h文件,打开该文件,修改CONFIG_CPU_FREQ_HZ(ARM主频),将频率由800000000修改为666666687(如果是7020核心板则修改为766666687): 示例代码20.4.5 zynq-common.h修改内容-1
- /*CPU clock */
- #ifndefCONFIG_CPU_FREQ_HZ
- # defineCONFIG_CPU_FREQ_HZ 666666687
- #endif
复制代码接下来修改CONFIG_SF_DEFAULT_SPEED(默认的QSPI速率),由30000000改为50000000,如下所示: 示例代码20.4.6 zynq-common.h修改内容-2
- /*QSPI */
- #ifdefCONFIG_ZYNQ_QSPI
- # defineCONFIG_SF_DEFAULT_SPEED 50000000
- #endif
复制代码修改CONFIG_ENV_SIZE(环境变量大小),由(128 << 10)修改为0x20000: 示例代码20.4.7 zynq-common.h修改内容-3
- /*Total Size of Environment Sector */
- #ifdef CONFIG_ENV_IS_IN_EEPROM
- #define CONFIG_ENV_SIZE CONFIG_SYS_EEPROM_SIZE
- #define CONFIG_EXTRA_ENV_SETTINGS
- #else
- #define CONFIG_ENV_SIZE 0x20000
- #endif
复制代码修改CONFIG_ENV_OFFSET(环境变量存储位置偏移量),由0xE0000修改为0x500000: 示例代码20.4.8 zynq-common.h修改内容-4
- /*cc108 requires to be 0xF00000 to have boot.bin with bitstream included */
- #ifndef CONFIG_ENV_OFFSET
- # define CONFIG_ENV_OFFSET 0x500000
- #endif
- #endif
复制代码需要注意CONFIG_ENV_SIZE和CONFIG_ENV_OFFSET这两个是U-Boot环境变量存储有关的,在下面menuconfig配置U-Boot的时候我们会将环境变量存储在QSPI中,CONFIG_ENV_SIZE指定了环境变量分区的大小,CONFIG_ENV_OFFSET指定环境变量分区的偏移量,这些都与QSPI的分区表有关! 修改完成之后保存退出即可,接下来就可以正式使用make命令进行配置和编译了。 3、defconfig配置 在U-Boot的源码目录下,执行下面这条命令对xilinx官方的U-Boot进行defconfig配置,如下所示: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-zynq_zc702_defconfig
图31.4.4 defconfig配置 “ARCH=arm”指定了u-boot运行的机器架构,“CROSS_COMPILE=arm-linux-gnueabihf-”指定了交叉编译工具的前缀,交叉编译工具我们使用的是xilinx官方提供的,在我们安装petalinux的时候也安装了,在petalinux安装目录下的tools/linux-i386目录,如下所示:
图 31.4.5 交叉编译工具链安装目录 gcc-arm-linux-gnueabi和gcc-arm-none-eabi两个目录下安装的是两种不同的交叉编译工具链(其它那几个目录安装的也是交叉编译工具链,对于ZYNQ 7010/7020来说用不上),两者之间的区别大家可以简单地理解为gcc-arm-linux-gnueabi一般用于编译linux系统的源码,例如ARM Linux内核源码、ARM Linux应用程序等,而gcc-arm-none-eabi一般用于编译ARM裸机程序,例如我们前面编译fsbl代码的时候用的就是它;这么一说的话,那我们编译U-Boot应该用gcc-arm-none-eabi咯,但这里我们用的是gcc-arm-linux-gnueabi,主要笔者看到xilinx wiki文档中用的也是这个,那就这个吧。 交叉编译命令就在安装目录下的bin文件夹中,例如:
图31.4.6 交叉编译命令 CROSS_COMPILE指定的就是这些命令的前缀,我们在前面source petalinux目录下的settings.sh脚本时已经将它们的路径导出到环境变量了,所以不用加全路径。 zynq_zc702_defconfig是xilinx为7010/7020添加的配置文件,所以我们也是使用这个! 4、menuconfig图形化配置 U-Boot图形化配置前面已经给大家讲过了,在U-Boot源码目录下,执行下面这条命令进入到menuconfig图形化配置界面: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-menuconfig
图31.4.7 menuconfig配置 各个配置项就不给大家一一讲解了,相信大家通过前面的学习已经对U-Boot的menuconfig图形化配置有一定的认识和理解了,接下来我直接把配置列出来: Boot media ---> Support for booting from SD/EMMC // 选择 SPL / TPL ---> [ ] Enable SPL // 取消 Command lineinterface ---> Memory commands ---> [ ] eeprom - EEPROM subsystem // 取消 Device access commands ---> [ ] i2c // 取消 Device TreeControl ---> (system-top) Default Device Tree for DTcontrol // 将设备树设置为system-top(也就是system-top.dts) Device Drivers ---> Serial drivers ---> [ ] Enable an early debug UART fordebugging // 取消 配置完成之后保存退出,执行下面这条命进行编译: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all-j10
图31.4.8 编译U-Boot 编译完成之后将会在当前目录下生成一个u-boot文件、u-boot.bin文件以及其它一些文件,如下所示:
图31.4.9 编译完成 u-boot文件是elf格式的镜像文件,而u-boot.bin则是纯二进制格式的镜像文件。 1.5 编译kernel同样 本篇教程不使用前面移植后的Linux内核源码,我们用一份新的xilinx官方2018.3版本( 这个版本是xilinx设定的版本,其linux版本为4.14.0)的内核源码,源码已经提供给大家了,路径为:ZYNQ开发板资料盘(A盘)\4_SourceCode\ZYNQ_7010\3_Embedded_Linux\资源文件\kernel\linux-xlnx-xilinx-v2018.3.tar.gz,大家也可以通过 https://github.com/Xilinx/linux-xlnx/releases网址进行下载。 将内核源码压缩包文件拷贝到Ubuntu系统目录中,例如/home/zynq/linux/kernel目录,并将其解压到当前目录,解压之后会产生一个linux-xlnx-xilinx-v2018.3文件夹,进入到该目录下,就可以看到内核源码目录下的文件和文件夹了,如下所示:
图31.5.1 内核源码目录 本小节我们也是直接在内核源码目录下进行编译,而不使用petalinux工具,其实这些知识前面都已经给大家介绍过了。 1、添加设备树文件 这里我们直接将前面U-Boot源码arch/arm/dts目录下的那四个设备树文件(pl.dtsi、pcw.dtsi、system-top.dts和zynq-7000.dtsi)直接拷贝到内核源码目录下的arch/arm/boot/dts目录中,不用进行修改。同样我们也需要修改arch/arm/boot/dts目录下的Makefile文件,将设备树添加上去,如下:
图31.5.2 Makefile中添加设备树 修改完成之后保存退出即可! 2、defconfig配置 在内核源码目录下执行下面这条命令对内核进行defconfig配置: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-xilinx_zynq_defconfig
图31.5.3 defconfig配置 命令这里就不给大家解释了,前面我们已经解释过,内核的配置、编译方式基本跟U-Boot是一样的,需要注意的是命令中间的空格不要漏掉了,因为以前发现很多初学者老是会漏掉这些空格,这样就导致命令执行失败。 3、menuconfig配置 这里我们暂时就不进行配置,以后有需要的时候再配,先保持默认! 4、编译内核 执行下面这条命令编译内核源码: make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-zImage -j10
图31.5.4 编译内核
图31.5.5 编译完成 编译完成之后会在arch/arm/boot/目录下生成一个名为zImage的内核镜像文件,后面我们在用。 5、编译设备树 内核与u-boot不同的是,U-Boot的设备树默认是与内核一起编译的,并且产生的dtb文件已经与U-Boot镜像文件集成在一起了;在内核里边我们需要单独编译出设备树的dtb文件,前面已经将我们所需要的设备树文件拷贝到内核的arch/arm/boot/dts目录下了,接下来执行这条命令编译system-top.dtb文件: - make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-system-top.dtb -j10
复制代码
图31.5.6 编译设备树 编译成功之后会在arch/arm/boot/dts目录下生成system-top.dtb文件。 1.6 编译rootfs根文件系统我们直接使用petalinux进行编译即可,首先进入petalinux的安装目录,使用petalinux-create命令新建一个petalinux工程,如下: - petalinux-create --type project --template zynq--name ALIENTEK-ZYNQ
复制代码
图31.6.1 新建petalinux工程 进入到ALIENTEK-ZYNQ工程目录下,执行petalinux-config --get-hw命令导入hdf硬件描述符文件: - cd ALIENTEK-ZYNQ
- petalinux-config --get-hw-description/home/zynq/hdf/Navigator_7010.sdk
复制代码
图31.6.2 导入hdf文件 这里用的hdf文件就是31.1小节中使用的,在弹出来的配置界面中,我们不用进行任何配置,直接保存退出即可!等待它配置完成。 接下来直接编译根文件系统: petalinux-build -c rootfs
图31.6.3 编译根文件系统 等待其编译完成,完成之后产生的根文件系统压缩包在images/linux目录下,如下所示:
图31.6.4 产生的根文件系统压缩包 因为我们要使用SD卡启动,并且SD卡会有一个EXT4格式的分区专门存放根文件系统,所以我们要使用压缩格式的根文件系统,例如rootfs.tar.gz或rootfs.tar.bz2。 1.7 启动开发板经过上面一系列的过程之后,我们就已经得到了启动开发板的所有所需的镜像文件了。例如我们使用petalinux工具会生成image.ub和BOOT.BIN文件,使用这两个文件我们就可以启动开发板了,但是这里我们不这样做。 前面给大家介绍过image.ub这个文件的本质,那么它其实是多个文件组合在一起的,包括内核镜像、dtb以及根文件系统,这样的做法有一个弊端,不够灵活,一个变了整个image.ub文件就得重新制作一遍,很麻烦。同样BOOT.BIN文件是fsbl镜像、u-boot镜像以及pl端bit文件集合在一起的,那么既然是集合在一起的,那么我们单独使用也是可以的嘛。 所以我们这里的思路就是将内核镜像文件zImage、内核设备树文件以及根文件系统从image.ub文件中分离出来;而将bit文件从BOOT.BIN文件中分离出来,这样做之后我们的SD卡中将会存在5部分内容:zImage、dtb、rootfs、bit以及BOOT.BIN(fsbl镜像与u-boot镜像的集合体),明确之后我们将他们拷贝到我们的SD卡中。 1、制作SD启动卡 SD启动卡的制作方式在17.2.10小节当中已经给大家介绍过了,这里不再细说,笔者直接将一张16G的TF卡格式化了2个分区,FAT32和EXT4,如下所示:
图31.7.1 SD启动卡分区信息 2、拷贝镜像到FAT分区 将前面过程当中生成的各种镜像文件拷贝到SD启动卡的FAT分区,包括zImage(内核镜像,内核源码目录arch/arm/boot/zImage)、system-top.dtb(内核设备树dtb文件,内核源码目录arch/arm/boot/dts/system-top.dtb)、design_navigator_7010_wrapper.bit(pl端bitstream文件,/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.bit)。大家根据自己前面步骤当中文件存放的目录去找到相应的这些镜像文件。 接下来我们需要将BOOT.BIN文件拷贝到FAT分区,但是前面的过程中我们并没有生成BOOT.BIN文件,这里就需要使用前面生成的u-boot(U-Boot elf格式镜像,U-Boot源码目录u-boot)和zynq_fsbl.elf(fsbl elf格式镜像,/home/zynq/linux/fsbl/zynq_fsbl.elf)来制作BOOT.BIN文件。 首先我们进入到U-Boot源码目录下,将生成的u-boot文件重命名为u-boot.elf,如下所示: mv u-boot u-boot.elf
图31.7.2 u-boot重命名 为啥要重命名呢?因为笔者发现使用petalinux-package命令制作BOOT.BIN文件的时候,如果elf格式文件没有带.elf后缀名会识别不了。接下来我们就可以用petalinux-package命令去生成BOOT.BIN文件了,在U-Boot源码目录下执行下面这条命令: petalinux-package --boot -p/home/zynq/petalinux/v2018.3/ALIENTEK-ZYNQ --fsbl/home/zynq/linux/fsbl/zynq_fsbl.elf --u-boot ./u-boot.elf -o ./BOOT.BIN --force
图31.7.3 生成BOOT.BIN文件 petalinux-package命令前面已经给大家介绍过了,”-p”选项指定一个petalinux工程,如果直接在petalinux工程目录下运行该命令则不需要用”-p”指定;”--fsbl”指定fsbl的elf格式镜像;” --u-boot”指定U-Boot的elf格式镜像文件,大家根据自己的路径情况进行修改;”-o”选项指定输出文件的名字以及存放的路径,例如这里是将BOOT.BIN文件存放在当前目录下,命令执行完成之后就会在当前目录下生成一个BOOT.BIN文件,如上图所示。我们使用ls查看下它的大小:
图31.7.4 BOOT.BIN文件大小 我们发现它只有600多K,但是u-boot.elf文件可是有4M多的,而zynq_fsbl.elf文件也有600多K,为啥合成为BOOT.BIN文件之后只有600多K了呢?原因在于格式问题,elf格式文件中除了机器码之外,还包含了很多额外的信息,例如符号表、调试信息、重定位表、运行地址和加载地址等,信息量很大,所以导致elf文件很大,例如linux下的应用程序就是elf格式的,程序加载运行的时候会去解析,但是裸机下不能用elf格式,包括内核镜像和U-Boot镜像等都不能用elf格式;而bin文件则是最纯粹的二进制机器代码,没有任何多余的信息,所以文件小。 使用petalinux-package命令生成的BOOT.BIN文件的时候,它会解析我们给定的elf格式文件,获取它的运行地址、加载地址以及真正的二进制机器代码,然后再把它俩集合起来,所以BOOT.BIN文件就是一个.bin格式的文件,所以它小,相信说到这里大家应该明白了!需要注意我们生成的BOOT.BIN文件中并不包含bit文件,前面已经跟大家说过了。 之后将BOOT.BIN文件也拷贝到SD启动卡的FAT分区,那么此时来看看我们的FAT分区有哪些文件:
图31.7.5 FAT分区文件列表 为了方便、好看,笔者将design_navigator_7010_wrapper.bit文件和system-top.dtb文件进行了重命名,分别为system.bit和system.dtb,如下:
图31.7.6 文件重命名 3、将根文件系统解压到EXT4分区 接下来我们需要将31.6小节编译的根文件系统压缩包文件解压到SD启动卡的EXT4分区,这里笔者使用rootfs.tar.gz压缩包文件,进入到rootfs.tar.gz压缩包文件所在目录,执行解压命令:
图31.7.7 解压rootfs.tar.gz /media/zynq/ext4是笔者的SD启动卡对应的EXT4分区的挂载点,注意解压的时候需要使用前面要加sudo,也就是要以root用户的身份进行解压。解压完成之后执行sync命令将数据同步到SD卡中,之后卸载SD启动卡,卸载成功之后拔掉它!
图31.7.8 卸载SD启动卡 4、启动开发板 将我们做好的启动卡插入开发板,连接电源、串口启动,打印信息如下所示:
图31.7.9 开发板启动 在U-Boot启动2秒倒计时之前,按回车或者是空格键停止启动,进入到U-Boot的命令行模式,因为现在不能直接启动,我们需要对U-Boot环境变量进行修改,在U-Boot命令行下执行下面这些命令设置环境变量,如下所示:
图31.7.10 设置环境变量 示例代码20.7.1 U-Boot环境变量设置 - Zynq> setenv bitstream_load_address 0x100000
- Zynq> setenv bitstream_image system.bit
- Zynq> setenv bitstream_size 0x300000
- Zynq> setenv kernel_image zImage
- Zynq> setenv devicetree_image system.dtb
- Zynq>
- Zynq> setenv sdboot 'if mmcinfo; then run uenvboot; echo Copying Linux from SD to RAM... && load mmc 0 ${bitstream_load_address} ${bitstream_image} && fpga loadb 0 ${bitstream_load_address} ${bitstream_size} && load mmc 0 ${kernel_load_address} ${kernel_image} && load mmc 0 ${devicetree_load_address} ${devicetree_image} && bootz ${kernel_load_address} - ${devicetree_load_address}; fi'
复制代码
关于U-Boot命令的使用在前面已经给大家讲解过,不过这里也给大家简单地说明一下我们设置的这些环境变量的作用: 首先第一条命令我们设置了bitstream_load_address变量存放bitstream文件从SD卡中拷贝到内存中的地址;第二条命设置了bitstream_image变量等于SD卡中bitstream文件的名字,也就是system.bit;第三条命设置了bitstream_size变量等于bitstream文件的大小;第四条命令设置了kernel_image变量等于SD卡中内核镜像的名字,也就是zImage;第五条命令设置了devicetree_image变量等于SD卡中内核设备树dtb文件的名字,也就是system.dtb;kernel_image和devicetree_image我们的这个U-Boot中默认是有的,只不过它们的内容并不是我们SD中的文件名,所以需要重新设定。 最后一条命令设置了sdboot变量,其实这个变量在我们使用的这个U-Boot中也是存在的,这里也是对他重新定义,这个变量的内容很长,我们可以把它的内容整理一下,如下: 示例代码20.7.2 sdboot变量内容整理 - ifmmcinfo; then
- run uenvboot;
- echo Copying Linux from SD to RAM... && // 打印字符串
- load mmc 0 ${bitstream_load_address} ${bitstream_image} && // 从SD卡拷贝bitstream文件到内存
- fpga loadb 0 ${bitstream_load_address} ${bitstream_size} && // 从内存中加载bitstream数据到FPGA
- load mmc 0 ${kernel_load_address} ${kernel_image} && // 从SD卡拷贝内核镜像到内存
- load mmc 0 ${devicetree_load_address} ${devicetree_image} &&// 从SD卡拷贝设备树到内存
- bootz ${kernel_load_address} - ${devicetree_load_address}; // bootz启动内核
- fi
复制代码“&&”符号其实就是C语言中”与”,跟C语言里面的作用一毛一样,${xxx}就是变量的引用,if … fi这个就是if条件判断语句了,fi表示条件判断的结束,U-Boot里边的这种语法很简单地,就不给大家细说了,如果不明白可以在网上找找资料。 前面跟大家讲过,U-Boot中的bootcmd变量的作用,U-Boot启动内核或在命令行下执行boot命令时其实就是去执行bootcmd,那我们来看看bootcmd的内容是什么:
图31.7.11 bootcmd的内容
图31.7.12 modeboot的内容 所以从上面可以知道,bootcmd的内容其实就是运行sdboot,也就是上面定义的那个。 变量设置完成之后,执行saveenv保存环境变量到QSPI Flash中,那么下次就不用再设置了
图31.7.13 保存环境变量 保存完成执行boot命令启动内核:
图31.7.14 启动内核 启动完成之后登陆系统(用户名和密码都是root):
图 31.7.15 登陆系统 后面我们的驱动开发篇将以本章使用的U-Boot源码、内核源码等进行开发工作! 1.8 tcl脚本前面我们在hsi终端下通过执行相应的命令去获取bit文件、自动生成设备树以及构建一个fsbl源码等,觉得这样非常的不方便,每次都要先执行hsi命令进入到hsi终端模式,然后在一个一个命令敲,这样感觉非常不好,那可不可以像shell命令一样,创建一个shell脚本之类的呢?其实是可以的,我们可以创建一个后缀名为.tcl的脚本,然后把我们的命令添加上去,然后直接执行这个脚本就行了。 例如我在Ubuntu目录下新建一个hsi_test.tcl文件,内容如下所示: - hsi::open_hw_design/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.hdf
复制代码文件中的hsi: pen_hw_designxxx.hdf就表示执行his的open_hw_design命令,后面跟着的参数就是对应的hdf文件路径了,例如我们将31.2小节中执行的命令写入到hsi_test.tcl脚本中,如下所示: 示例代码20.8.1 tcl脚本示例 hsi_test.tcl内容
- # tclscript test
-
- hsi::open_hw_design/home/zynq/hdf/Navigator_7010.sdk/design_navigator_7010_wrapper.hdf
- hsi::set_repo_path/home/zynq/device-tree/device-tree-xlnx-xilinx-v2018.3
- hsi::create_sw_designdevice-tree -os device_tree -procps7_cortexa9_0
- hsi::generate_target-dir /home/zynq/linux/dts
复制代码第一行#号表示注释。 那我们如何执行hsi_test.tcl脚本呢?执行方法如下所示: xsct hsi_test.tcl
图31.8.1 运行hsi_test.tcl脚本 “xsct xxx.tcl”即可!xsct也是petalinux工具安装的时候自带的,它也是命令行终端,例如运行xsct命令就会进入到xsct终端模式,如下所示:
图31.8.2 xsct终端 例如我们在xsct终端下也可以执行脚本中的命令,例如:
图31.8.3 xsct运行hsi命令 所以当我们执行”xscthsi_test.tcl”命令时其实就是类似于将hsi_test.tcl交给xsct去解析并运行。 关于xsct、hsi以及tcl脚本等更多的内容这里就不讲了,笔者对这些没有什么深入的了解!如果大家感兴趣自己去网上找找相关的资料。
|