i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用。
第四篇 嵌入式Linux系统移植篇
第六十七章 Uboot编译及移植
在之前学习开发板烧写的章节中,我们用到uboot和内核的镜像是怎么做出来的呢,我们在学习移植uboot之前先使用迅为电子移植好的uboot镜像来学习一下uboot的相关知识,本章节我们来学习下基础的知识“什么是uboot”和“uboot中的常用命令”,并且我们下载NXP官方的源码。
67.1 U-Boot介绍
67.1.1 什么是u-boot
uboot是一段裸机代码,它的实现非常复杂,主要是初始化一些硬件,部署整个计算机系统,然后将Linux内核从flash(NAND,NOR FLASH,SD,MMC 等)拷贝到 DDR 中,根据环境变量去启动内核,并向内核传递参数。它的目标就是启动内核,内核启动后它的生命也随之结束。
u-boot是SourceForge上的开源项目,由一个人发起,然后由整个世界所有感兴趣的人共同维护发展而来的一个bootloader,bootloader是用来引导和加载内核,向内核传递参数的,是内核引导程序的统称,bootloader除了u-boot还有bios,LilO,redboot,vivi等。
uboot 的全称是 Universal Boot Loader,uboot 是一个遵循 GPL 协议的开源软件,uboot 是一个裸机代码,可以看作是一个裸机综合例程。现在的 uboot 已经支持液晶屏、网络、USB 等高级功能。uboot 官网为 http://www.denx.de/wiki/U-Boot/,
可以在 uboot 官网下载 uboot 源码,点击图中左侧 Topics 中的“Source Code”,
进入其 FTP 服务器即可看到 uboot 源码
我们可以在uboot官网下载最原始的uboot源码,也就是没有经过修改的,原汁原味的uboot的,但是在实际工作中,我们并不会直接在uboot官网下载uboot源码来移植uboot。为什么呢?因为我们要在对应的平台上来运行uboot,那么这个uboot是不是就要对这个平台支持的非常全面呢,因为uboot本身就是裸机代码,所以想要对某一个平台支持的非常全面,就要对这个平台非常熟悉,比如i.MX 8M Mini,那就要对i.MX 8M Mini这个芯片非常熟悉,那谁对这个芯片非常熟悉呢,当然是半导体厂家呀,所以,uboot官网里面的原汁原味的uboot是给半导体厂家准备的,比如RK,NXP等等。NXP官方的 uboot 基本支持了 NXP 当前所有可以跑 Linux 的芯片,而且支持各种启动方式,比如 EMMC、NAND、NOR FLASH 等等,这些都是 uboot 官方所不支持的。我们如果要移植对芯片支持最全面,最好的uboot的,我们一般是在半导体厂家维护uboot版本的基础上来二次开发自己的uboot,而不是直接在uboot官网下载源码来移植。
在迅为提供的资料里面有两个Linux的BSP源码包,一个是半导体厂商提供的源码包,里面的uboot是未经修改的,一个是迅为提供的BSP源码包,里面的uboot是经过迅为修改后的uboot,可以直接编译在开发板上来运行。当然了,你也可以在购买了第三方开发板以后使用半导体厂商提供的 uboot,只不过有些外设驱动可能不支持,需要自己移植,这个就是我们常说的 uboot 移植。
67.1.2 常用命令
我们先烧迅为做好的uboot(flash.bin)到开发板上,我们先了解与uboot相关的命令。
uboot启动如下图所示:
上电以后,出现 Hit any key to stop autoboot: 0就按下任意键,进入uboot命令行,输入?查看帮助信息,会弹出很多命令和介绍,
上面的那些命令并不是 uboot 所支持的所有命令,前面说过 uboot 是可配置的,需要什么命令就使能什么命令。这些命令后面都跟有命令说明,用于描述此命令的作用,但是命令具体怎么用呢?我们输入“help(或?) 命令名”既可以查看命令的详细用法,以“bootz”这个命令为例,我们输入如下命令即可查看“bootz”这个命令的用法:
=> ? bootz
bootz - boot Linux zImage image from memory
Usage:
bootz [addr [initrd[:size]] [fdt]]
- boot Linux zImage stored in memory
The argument 'initrd' is optional and specifies the address
of the initrd in memory. The optional argument ':size' allows
specifying the size of RAW initrd.
When booting a Linux kernel which requires a flat device-tree
a third argument is required which is the address of the
device-tree blob. To boot that kernel without an initrd image,
use a '-' for the second argument. If you do not pass a third
a bd_info struct will be passed instead
常用的和信息查询有关的命令有 3 个:
bdinfo
printenv
version
先来看一下 bdinfo 命令,此命令用于查看板子信息,直接输入“bdinfo”即可,
从上面中可以得出 DRAM 的起始地址和大小、启动参数保存起始地址、波特率、sp(堆栈指针)起始地址等信息。
命令“printenv”用于输出环境变量信息,uboot 也支持 TAB 键自动补全功能,输入“print”然后按下 TAB 键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键,环境变量如下面所示:
u-boot=> pri
baudrate=115200
boot_fdt=try
bootcmd=mmc dev ${mmcdev}; if mmc rescan; then if run loadbootscript; then run bootscript; else if run loadimage; then run mmcboot; else run netboot; fi; fi; else booti ${loadaddr} - ${fdt_addr}; fi
bootcmd_mfg=run mfgtool_args;if iminfo ${initrd_addr}; then if test ${tee} = yes; then bootm ${tee_addr} ${initrd_addr} ${fdt_addr}; else booti ${loadaddr} ${initrd_addr} ${fdt_addr}; fi; else echo "Run fastboot ..."; fastboot 0; fi;
bootdelay=2
bootscript=echo Running bootscript from mmc ...; source
cnotallow=ttymxc1,115200 earlycnotallow=ec_imx6q,0x30890000,115200
emmc_dev=1
ethprime=FEC
fastboot_dev=mmc1
fdt_addr=0x43000000
fdt_file=itop8mm-evk-7.0.dtb
fdt_high=0xffffffffffffffff
fdtcnotallow=be8f5860
image=Image
initrd_addr=0x43800000
initrd_high=0xffffffffffffffff
jh_clk=
jh_mmcboot=setenv fdt_file fsl-imx8mm-evk-root.dtb;setenv jh_clk clk_ignore_unused; if run loadimage; then run mmcboot; else run jh_netboot; fi;
jh_netboot=setenv fdt_file fsl-imx8mm-evk-root.dtb; setenv jh_clk clk_ignore_unused; run netboot;
kboot=booti
loadaddr=0x40480000
loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}
loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
mfgtool_args=setenv bootargs cnotallow=${console},${baudrate} rdinit=/linuxrc clk_ignore_unused
mmcargs=setenv bootargs ${jh_clk} cnotallow=${console} root=${mmcroot}
mmcautodetect=yes
mmcboot=echo Booting from mmc ...; run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if run loadfdt; then booti ${loadaddr} - ${fdt_addr}; else echo WARN: Cannot load the DT; fi; else echo wait for boot; fi;
mmcdev=1
mmcpart=1
mmcroot=/dev/mmcblk2p2 rootwait rw
netargs=setenv bootargs ${jh_clk} cnotallow=${console} root=/dev/nfs ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
netboot=echo Booting from net ...; run netargs; if test ${ip_dyn} = yes; then setenv get_cmd dhcp; else setenv get_cmd tftp; fi; ${get_cmd} ${loadaddr} ${image}; if test ${boot_fdt} = yes || test ${boot_fdt} = try; then if ${get_cmd} ${fdt_addr} ${fdt_file}; then booti ${loadaddr} - ${fdt_addr}; else echo WARN: Cannot load the DT; fi; else booti; fi;
script=boot.scr
sd_dev=0
soc_type=imx8mm
Environment size: 2333/4092 bytes
有很多的环境变量,比如 baudrate、board_name、board_rec、boot_fdt、bootcmd等等。uboot 中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。
比如 bootdelay 这个环境变量就表示 uboot 启动延时时间,默认bootdelay=3,也就默认延时 3秒。前面说的 3 秒倒计时就是由 bootdelay 定义的,如果将 bootdelay 改为 5 的话就会倒计时 5s了。uboot 中的环境变量是可以修改的,有专门的命令来修改环境变量的值。
setenv:修改环境变量:setenv 环境变量名 环境变量值
删除环境变量:setenv 环境变量名
例如设置自启动倒计时环境变量bootdelay,设置为5秒,默认是2秒,
输入命令setenv bootdelay 10,然后输入printenv查看环境变量,发现环境变量已经改变。
删除环境变量也是使用命令 setenv,要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的bootdelay 环境变量,使用命令 setenv bootdelay删除环境变量,
实验完成后,重新启动开发板恢复环境变量使设置的环境失效,因为环境变量没有保存到flash。
reset:重新启动
在uboot命令行输入reset即可重启开发板,
实验完成后,重新启动开发板恢复环境变量使设置的环境失效,因为环境变量没有保存到flash。
reset:重新启动
在uboot命令行输入reset即可重启开发板,
1.mmc: 在uboot命令行,输入mmc可以查看跟mmc相关的命令
由上图我们可以发现,mmc后面的参数不同,mmc的功能就不一样,例如:
mmc info:打印mmc的信息
mmc list :查看mmc设备
mmc read addr blk# cnt:从eMMC读cnt个块数据到内存addr处;
mmc write addr blk# cnt:把内存addr处的cnt个块数据写到eMMC。
mmc erase blk# cnt:擦除blk#开始的cnt个数据块
其中,addr为内存地址,blk#是mmc的块号,cnt是设备块的个数,块的单位是512字节。
(1)mmc info命令
mmc info命令可以查看设备信息,
通过上图我们可以看出,当前的设备是emmc设备,也就是图中MMC version所表示的信息为emmc的版本为5.1,Capacity所表示的信息为容量大小为14.6 Gib,Bus Width表示带宽为8-bit,Bus Speed表示速度为52000000Hz。
(2)mmc list命令
mmc list查看mmc设备,直接输入mmc list即可,
(3)mmc rescan命令
mmc rescan可以扫描开发板上的emmc设备,直接输入mmc rescan命令即可
(4)mmc dev命令
mmc dev命令可以切换mmc设备,命令格式:mmc dev mmc dev [dev] [part],其中dev是我们要切换到的mmc设备号,part可以不写,不写的话默认分区为0。
我们找一张FAT32的TF卡,查到开发板上的TF卡座子上,一般我们把TF卡也认为是mmc设备。所以也可以用mmc命令来操作。连接好TF卡以后,使用以下命令切换到TF卡
mmc dev 0
其中0为sd卡,1为emmc。
然后我们使用mmc info命令查看是否切换成功,
从上图我们可以看到,sd卡的版本信息为3.0,容量为14.6 GiB,位宽为4-bit。
(5)mmc part命令
mmc part可以用来查看mmc设备的分区,比如我们要查看emmc的分区,我们先切换到emmc设备(如果当前已经是emmc设备则不用切换),命令如下:
mmc dev 1
切换成功如下图所示:
使用命令mmc part查看emmc的分区,
从上图我们可以看出,emmc一共有2个分区,类型为DOS,其中第一个分区用来存放uboot镜像和内核镜像,第二个分区用来存放文件系统
(6)mmc read命令
使用mmc read命令可以读取mmc设备的数据信息,格式为mmc read addr blk# cnt,其中addr是读取到内存中的地址,blk为起始的块地址,一般为十六进制,一块是512字节,cnt是要读取的块的数量。
例如:mmc read 0xa0000000 10 10 ,表示mmc设备的第10块开始,读取10块到内存的0xa0000000地址当中去。结果如下:
5 go命令
go命令可以指定跳到地址处运行,命令格式为 go addr ,其中addr是应用在内存中的首地址。
6 run命令
可以执行环境变量中的命令,这个命令一般用于用户运行自定义的环境变量。
7 ls命令,默认uboot没有这个命令,可以配置上使用。
ls命令可以列出文件的目录,命令格式ls <interface> [<dev[:part]> [directory]]:
例如,查看emmc分区5的信息。分区5为文件系统分区
ls mmc 1:5
其中,1为emmc,5为emmc里面的分区5
查看emmc分区5里面的etc信息,也就是文件系统里面的etc信息,命令如下
ls mmc 1:5 etc
67.2设置交叉编译器
输入以下命令设置交叉编译器,只有设置了交叉编译器,才可以编译源码,否则会报错。
. /opt/fsl-imx-xwayland/4.14-sumo/environment-setup-aarch64-poky-linux
export ARCH=arm64
export CROSS_COMPILE=aarch64-poky-linux-
67.3 获取u-boot-imx 源码并编译
输入以下命令下载u-boot-imx 源码。
git clone https://source.codeaurora.org/external/imx/uboot-imx
进入uboot-imx目录,
cd uboot-imx
查看git分支,
git branch -a
切换分支,输入以下命令:
git checkout imx_v2018.03_4.14.78_1.0.0_ga
同步到当前仓库,输入以下命令:
git pull origin
输入以下命令,开始编译。
make distclean
make imx8mm_ddr4_evk_defconfig
输入以下命令,开始编译。
. /opt/fsl-imx-xwayland/4.14-sumo/environment-setup-aarch64-poky-linux
make -j 8
编译成功后会产生所需的文件:
u-boot-nodtb.bin
spl/u-boot-spl.bin
arch/arm/dts/fsl-imx8mm-ddr4-evk.dtb
67.4 获取 imx-mkimage源码并编译
下载 imx-mkimage源码,输入以下命令:
git clone https://source.codeaurora.org/external/imx/imx-mkimage/ && cd imx-mkimage
查看远程分支,输入以下命令:
git branch -a
切换到与imx_4.14.78_1.0.0_ga版本对应的分支上,输入以下命令:
git checkout imx_4.14.78_1.0.0_ga
同步到当前仓库,输入以下命令:
git pull origin
67.5获取imx-atf源码并编译
退回上一级目录,下载 imx-atf,并同样切换分支
cd ..
git clone https://source.codeaurora.org/external/imx/imx-atf/ && cd imx-atf
切换到与imx_4.14.78_1.0.0_ga版本对应的分支上,输入以下命令:
git checkout imx_4.14.78_1.0.0_ga
同步到当前仓库,输入以下命令:
git pull origin
编译 imx-atf,输入以下命令:
make clean PLAT=imx8mm
LDFLAGS="" make PLAT=imx8mm
编译完成会生成build/imx8mm/release/bl31.bin文件,
67.6 获得firmware-imx源码
返回上一级目录,输入以下命令获得firmware-imx源码
cd .. && mkdir firmware-imx-8.10
cd firmware-imx-8.10
wget http://www.freescale.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.1.bin
解压输入以下命令:
./firmware-imx-8.1.bin --auto-accept
67.7生成flash.bin
使用 imx-mkimage 链接合成所有文件生成最后二进制文件
输入以下命令拷贝uboot-imx中的文件到imx-mkimage目录。
cp uboot-imx/tools/mkimage ./imx-mkimage/iMX8M/mkimage_uboot
cp uboot-imx/arch/arm/dts/fsl-imx8mm-ddr4-evk.dtb ./imx-mkimage/iMX8M/fsl-imx8mm-ddr4-evk.dtb
cp uboot-imx/spl/u-boot-spl.bin ./imx-mkimage/iMX8M/
cp uboot-imx/u-boot-nodtb.bin ./imx-mkimage/iMX8M/
cpfirmware-imx-8.10/firmware-imx-8.1/firmware/ddr/synopsys/ddr4_dmem_1d.bin ./imx-mkimage/iMX8M/
cpfirmware-imx-8.10/firmware-imx-8.1/firmware/ddr/synopsys/ddr4_dmem_2d.bin ./imx-mkimage/iMX8M/
cp firmware-imx-8.10/firmware-imx-8.1/firmware/ddr/synopsys/ddr4_imem_1d.bin ./imx-mkimage/iMX8M/
cp firmware-imx-8.10/firmware-imx-8.1/firmware/ddr/synopsys/ddr4_imem_2d.bin ./imx-mkimage/iMX8M/
cp imx-atf/build/imx8mm/release/bl31.bin ./imx-mkimage/iMX8M/
cd imx-mkimage
make SOC=iMX8MM clean
make SOC=iMX8MM flash_ddr4_evk
编译完会生成flash.bin镜像,
然后我们可以使用TF卡启动uboot,但是不能使用UUU工具烧写,接下来进一步优化源码,请查看第二章。