1)实验平台:正点原子MPSoC开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=692450874670
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html
第十一章U-Boot使用实验
在移植linux内核之前,我们肯定要先了解U-Boot。因为U-boot是我们的开发板加载引导启动linux内核的必要工具。本章我们讲解U-Boot是什么、有何作用,有哪些命令以及如何通过U-boot加载引导启动linux内核。
11.1U-Boot简介
对于计算机系统而言,从开机上电到操作系统启动需要一个引导过程,这个引导过程由引导程序指定。引导程序是系统加电启动运行的第一段软件代码。在PC体系结构中,引导程序由主板上的BlOS和位于硬盘MBR中的启动代码组成。系统上电后,首先运行BlOS,在完成硬件检测和资源分配后,将硬盘MBR中的引导程序读到系统的RAM中,然后将控制权交给引导程序。引导程序的主要运行任务就是将内核映像从硬盘读到RAM中,然后跳转到内核的入口点去运行,也即开始启动操作系统。嵌入式Linux系统同样离不开引导程序,这个引导程序一般我们叫作启动加载程序(Bootloader)。
Bootloader是在操作系统运行之前执行的一段小程序。通过这段小程序,可以初始化硬件设备、建立内存空间的映射表,从而建立适当的系统软硬件环境,为最终调用操作系统内核做好准备。也就是说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux内核从flash(SD、eMMC、NAND、NOR FLASH等)拷贝到DDR中,最后启动Linux内核。当然了,bootloader的实际工作要复杂的多,但是它最主要的工作就是启动Linux内核。
对于嵌入式系统,bootloader是基于特定硬件平台实现的。因此,几乎不可能为所有的嵌入式系统建立一个通用的bootloader,不同的处理器架构都有不同的bootloader。Bootloader不仅依赖于CPU的体系结构,而且依赖于嵌入式系统板级设备的配置。对于两块不同的嵌入式开发板而言,即使它们使用同一种处理器,要想让运行在一块板子上的bootloader程序也能运行在另一块板子上,一般需要修改bootloader的源程序。庆幸的是,大部分bootloader仍具有很多共性,某些bootloader能够支持多种体系结构的嵌入式系统。
现成的bootloader软件有很多,比如U-Boot、vivi、RedBoot等等,其中以U-Boot使用最为广泛,为了方便书写,本书会将U-Boot写为uboot。特别说明的是对于ZYNQ MPSoC而言,在引导过程中,先运行FSBL来设置PS,然后运行U-Boot用于加载Linux内核映像并引导Linux,所以uboot对于ZYNQ MPSoC而言是第二阶段引导程序,FSBL是第一阶段引导程序。
uboot的全称是Universal Boot Loader,uboot是一个遵循GPL协议的开源软件。uboot是一个裸机代码,可以看作是一个裸机综合项目。现在的uboot已经支持液晶屏、网络、USB等高级功能。uboot官网地址:http://www.denx.de/wiki/U-Boot/,如下图所示:
图 11.1.1 uboot官网
我们可以在uboot官网下载uboot源码,点击上图中左侧Topics中的“Source Code”,打开如下图所示界面:
图 11.1.2 uboot源码界面
点击上图中的“HTTPS”,进入其服务器即可看到uboot源码,如下图所示:
图 11.1.3 uboot源码
上图显示的就是uboot原汁原味的源码文件,目前最新的版本是2022.04。但是我们一般不会直接用uboot官方的U-Boot源码的。uboot官方的uboot源码是给半导体厂商准备的,半导体厂商会下载uboot官方的uboot源码,然后将自家相应的芯片移植进去。也就是说半导体厂商会自己维护一个为其设计的芯片定制的uboot版本。既然是定制的,那么肯定对自家的芯片支持会很全,虽然uboot官网的源码中一般也会支持他们的芯片,但是绝对是没有半导体厂商自己维护的uboot全面。
Xilinx维护的uboot版本可在网站https://github.com/Xilinx/u-boot-xlnx查看,下载地址为:https://github.com/Xilinx/u-boot-xlnx/releases,下载界面如下图所示:
图 11.1.4 Xilinx官方uboot下载界面
点击上图中箭头所指的zip或tar.gz就可以下载,xilinx-v2019.2对应的就是2019.2的版本,与我们使用的petalinux版本相对应,如果不对应可能会出现某些问题。图 11.1.4中的uboot基本支持了Xilinx当前所有可以跑Linux的芯片,而且支持各种启动方式,比如SD卡、eMMC、NAND、NOR FLASH等等,这些都是uboot官方所不支持的。但是图 11.1.4中的uboot是针对Xilinx自家评估板的,如果是我们自己做的板子就需要修改Xilinx官方的uboot,使其支持我们自己做的板子,正点原子的DFZU2EG_4EV MPSoC开发板就是自己做的板子,虽然大部分都参考了Xilinx官方的ZYNQ MPSoC开发板,但是还是有很多不同的地方,所以需要修改Xilinx官方的uboot,使其适配正点原子的ZYNQ MPSoC开发板。所以当我们拿到开发板以后,是有三种uboot的,这三种uboot的区别如下表所示:
种类 描述
uboot官方的uboot代码 由uboot官方维护开发的uboot版本,版本更新快,基本包含所有常用的芯片
半导体厂商的uboot代码 半导体厂商维护的一个uboot,专门针对自家的芯片,在对自家芯片支持上要比uboot官方的好
开发板厂商的uboot代码 开发板厂商在半导体厂商提供的uboot基础上加入了对自家开发板的支持
表 11.1.2三种uboot的区别
那么这三种uboot该如何选择呢?首先uboot官方的基本是不会用的,因为支持太弱了。最常用的就是半导体厂商或者开发板厂商的uboot,如果你用的半导体厂商的评估板,那么就使用半导体厂商的uboot,如果你是购买的第三方开发板,比如正点原子的ZYNQ MPSoC开发板,那么就使用正点原子提供的uboot源码(也是在半导体厂商的uboot上修改的)。当然了,你也可以在购买了第三方开发板以后使用半导体厂商提供的uboot,只不过有些外设驱动可能不支持,需要自己移植,这个就是我们常说的uboot移植。
注:上面介绍的是一般情况,对于Xilinx厂商的芯片而言有些区别,因为Xilinx提供了Petalinux开发工具。在第六章Petalinux设计流程实战中,我们并没有设置uboot,而是使用默认的官方的uboot,也可以正常运行在我们的ZYNQ MPSoC开发板上。这是因为Petalinux开发工具会根据xsa文件自动分析ZYNQ MPSoC的配置,从而自动配置uboot,相当于自动移植了uboot,严谨一点,应该称为自动适配。这种自动适配主要是使uboot能够在开发板上正常运行,基本能满足我们的使用。所以我们的ZYNQ MPSoC开发板没有提供移植的uboot,也就是没有上面所说的开发板厂商的uboot代码,使用Petalinux自带的uboot源码就可以了。
11.2Petalinux配置和编译U-Boot
进入到第六章Petalinux设计流程实战中创建的petalinux工程目录下,然后按照6.3.2小节设置Petalinux环境变量。
uboot的功能有很多,虽然默认的功能一般而言已经够用了,但在项目开发过程中,可能会使用到uboot平时不常用的功能,这时就需要配置uboot以使能相应功能。在Petalinux中配置uboot的方法很简单,在搭建好的petalinux工程目录下,执行如下命令即可进入uboot图形配置界面:
petalinux-config -c u-boot
执行后会在终端中打开另一个配置uboot的标签页,如下图所示:
图 11.2.1 uboot配置界面
如果有配置uboot的需求可以自行配置。笔者一般使用默认的配置,除了配置项“(4) delay in seconds before automatically booting”外,没有遇到额外配置的情况,所以就不对这些配置项进行介绍了。这里说一下配置项“(4) delay in seconds before automatically booting”的含义,该配置是用来设置启动uboot后,延时多长时间自动启动linux,默认为4秒,如果读者觉得太长的话,可以重新设置,笔者此处使用默认的设置。
配置完成后,保存配置退出。此处笔者由于没有进行配置,直接退出。对于Petalinux19.2版本而言,执行上面的配置操作后,会在当前Petalinux工程的components/plnx_workspace/sources/目录下生成uboot的源码,如下图所示:
图 11.2.2 uboot源码
现在编译uboot。编译uboot的命令很简单,执行如下命令即可编译uboot:
petalinux-build -c u-boot
也可以只使用“petalinux-build”命令,用来编译fsbl、uboot、设备树、Linux内核和根文件系统等,如果是重新创建Petalinux工程,推荐使用“petalinux-build”命令,不带“-c u-boot”参数。
命令执行完以后uboot也就编译成功了,如下图所示:
图 11.2.3 编译U-Boot
注:有一个警告,Petalinux版本的问题,不影响使用。
编译完成以后会在当前Petalinux工程目录的images/linux目录下生成u-boot.elf文件,如下图所示:
图 11.2.4 u-boot镜像文件
11.3U-Boot烧写与启动
uboot编译好以后就可以烧写到板子上使用了。我们将u-boot.elf文件打包到ZYNQ MPSoC的启动文件BOOT.BIN中,在终端中输入如下命令:
petalinux-package --boot --fsbl --fpga --u-boot --force
执行结果如下图所示:
图 11.3.1 打包生成BOOT.BIN文件
生成BOOT.BIN文件后,插上SD启动,将该Petalinux工程image/linux目录下的BOOT.BIN文件拷贝到SD卡的boot分区也即FAT32分区。
拷贝完成后将SD卡插到MPSoc开发板上,启动模式设置从SD卡启动,使用USB线将开发板的串口和电脑连接,如果有条件可以用以太网线将PS以太网口连接到能够联网的路由器。打开串口上位机如putty,设置好串口参数并打开,最后复位开发板。在串口上位机上出现“Hit any key to stop autoboot: ”倒计时的时候按下键盘上的回车键,默认是4秒倒计时,在4秒倒计时结束以后如果没有按下回车键的话uboot就会使用默认参数来启动Linux内核了。如果在4秒倒计时结束之前按下回车键,那么就会进入uboot的命令行模式,如下图所示:
图 11.3.2 uboot启动过程
从上图可以看出,当进入到uboot的命令行模式以后,左侧会出现一个“ZynqMP>”标志。启动的时候会输出一些信息,这些信息如下所示:
1 Xilinx Zynq MP First Stage Boot Loader
2 Release 2019.2 Mar 14 2022 - 01:27:14
3 NOTICE: ATF running on XCZU4EV/silicon v4/RTL5.1 at 0xfffea000
4 NOTICE: BL31: Secure code at 0x0
5 NOTICE: BL31: Non secure code at 0x8000000
6 NOTICE: BL31: v2.0(release):xilinx-v2019.1-12-g713dace9
7 NOTICE: BL31: Built : 10:53:49, Mar 14 2022
8 PMUFW: v1.1
9
10
11 U-Boot 2019.01 (Mar 29 2022 - 03:16:53 +0000)
12
13 Model: Alientek Zynq MpSoc Development Board
14 Board: Xilinx ZynqMP
15 DRAM: 2 GiB
16 EL Level: EL2
17 Chip ID: zu4ev
18 MMC: mmc@ff160000: 0, mmc@ff170000: 1
19 Loading Environment from SPI Flash... SF: Detected n25q256a with page size 256 Bytes, erase size 64 KiB, total 32 MiB
20 OK
21 In: serial@ff000000
22 Out: serial@ff000000
23 Err: serial@ff000000
24 Model: Alientek Zynq MpSoc Development Board
25 Board: Xilinx ZynqMP
26 Net: ZYNQ GEM: ff0b0000, phyaddr 4, interface gmii
27 eth0: ethernet@ff0b0000ZYNQ GEM: ff0e0000, phyaddr 7, interface rgmii-id
28
29 Warning: ethernet@ff0e0000 (eth1) using random MAC address - 46:15:4f:73:5b:11
30 , eth1: ethernet@ff0e0000
31 Hit any key to stop autoboot: 0
32 ZynqMP>
下面我们简单的了解一下这些信息。
第1~8行是First Stage Boot Loader启动输出的信息。第3行可以看到当前使用的ZYNQ芯片是XCZU4EV。
从第11行起是uboot启动输出的信息。
第11行是uboot版本号和编译时间,可以看出,当前的uboot版本号是2019.01,编译时间是2022年3月29日3点16分。
第13~17行是开发板相关信息,可以看出当前使用的开发板名称为“Alientek Zynq MpSoc Development Board”,核心板为“Xilinx ZynqMP”,核心板的芯片id为zu4ev。
第15行提示当前板子的DRAM(内存)为2GiB。
第18提示当前有两个MMC/SD卡控制器:mmc@ff160000和mmc@ff170000。
第19行从SPI(QSPI)加载环境变量;检测到开发板使用的QSPI为n25q256a,大小为32MiB。
第21~23是标准输入、标准输出和标准错误所使用的终端,这里都使用串口(serial)作为终端。
第26~30行是网口信息,提示我们当前有两个网口eth0和eth1,其中eth0的phy地址为4,eth1的phy地址为7。
第31行是倒计时提示,默认倒计时4秒,倒计时结束之前按下回车键就会进入Uboot命令行模式。如果在倒计时结束以后没有按下回车键,那么Linux内核就会启动,Linux内核一旦启动,uboot就会寿终正寝。这个值与我们在11.2节介绍的配置项“(4) delay in seconds before automatically booting”有关。
uboot是来干活的,我们现在已经进入uboot的命令行模式了,进入命令行模式以后就可以给uboot发号施令了。当然了,不能随便发号施令,得看看uboot支持哪些命令,然后使用这些uboot所支持的命令来做一些工作。下一节讲解uboot命令的使用。
11.4U-Boot命令的使用
进入uboot的命令行模式以后输入“help”或者“?”,然后按下回车即可查看当前uboot所支持的命令,如图 11.4.1所示。该图只是uboot的一部分命令,并不是uboot所支持的所有命令。前面说过uboot是可配置的,需要什么命令就使能什么命令,所以该图中的命令是正点原子提供的uboot中使能的命令,uboot支持的命令还有很多,而且也可以在uboot中自定义命令。这些命令后面都跟有命令说明,用于描述此命令的作用,但是命令具体怎么用呢?我们输入“help(或?) 命令名”就可以获取可用命令的完整列表以及查看对应命令的详细用法。
图 11.4.1 uboot命令列表
以“bootm”这个命令为例,我们输入如下命令即可查看“bootm”这个命令的用法:
? bootm 或 help bootm
结果如下图所示:
图 11.4.2 bootm命令使用说明
上图详细的列出了“bootm”这个命令的用法,其它的命令也可以使用此方法查询具体的使用方法。接下来我们学习一下一些常用的uboot命令。
11.4.1信息查询命令
常用的和信息查询有关的命令有3个:bdinfo、printenv和version。先来看一下bdinfo命令,此命令用于查看板子信息,直接输入“bdinfo”即可,结果如下图所示:
图 11.4.3 bdinfo命令
从上图中可以得出DRAM的起始地址和大小,UART波特率、sp(堆栈指针)起始地址等信息。
命令“printenv”用于输出环境变量信息,uboot也支持TAB键自动补全功能,输入“print”然后按下TAB键就会自动补全命令,直接输入“print”也可以。输入“print”,然后按下回车键,环境变量如下图所示:
图 11.4.4 printenv命令结果
在上图中有很多的环境变量,比如baudrate、board_name、boot_img、bootcmd等等。uboot中的环境变量都是字符串,既然叫做环境变量,那么它的作用就和“变量”一样。比如bootdelay这个环境变量就表示uboot启动延时时间,默认bootdelay=4,也就默认延时4秒。前面说的4秒倒计时就是由bootdelay定义的,如果将bootdelay改为5的话就会倒计时5s了。uboot中的环境变量是可以修改的,有专门的命令来修改环境变量的值,稍后我们会讲解。
命令version用于查看uboot的版本号,输入“version”,uboot版本号如下图所示:
图 11.4.5 version命令结果
从上图可以看出,当前uboot版本号为2019.01,2021年11月3日编译的,编译器为aarch64-xilinx-linux-gcc等信息。
11.4.2环境变量操作命令
1、修改环境变量
环境变量的操作涉及到两个命令:setenv和saveenv,命令setenv用于设置或者修改环境变量的值。命令saveenv用于保存修改后的环境变量,一般环境变量是存放在外部flash中的,uboot启动的时候会将环境变量从flash读取到DRAM中。所以使用命令setenv修改的是DRAM中的环境变量值,修改以后要使用saveenv命令将修改后的环境变量保存到flash中,否则的话uboot下一次重启会继续使用以前的环境变量值。
命令setenv使用起来很简单,格式为:
setenv 命令 值
或
setenv 命令‘值1 值2 值3’
比如我们要将环境变量bootdelay该为5,就可以使用如下所示命令:
setenv bootdelay 5
saveenv
上述命令执行过程如下图所示:
图 11.4.6环境变量修改
在上图中,当我们使用命令saveenv保存修改后的环境变量的话会有保存过程提示信息,根据提示可以看出环境变量保存到了SPI flash中,也就是开发板上的QSPI Flah–w25q256中。
修改bootdelay以后,重启开发板,uboot就是变为5秒倒计时,如下图所示:
图 11.4.7 5秒倒计时
2、新建环境变量
命令setenv也可以用于新建命令,用法和修改环境变量一样,比如我们新建一个环境变量author,author的值为“alientek”,那么就可以使用如下命令:
setenv author alientek
saveenv
新建命令author完成以后重启uboot,然后使用命令printenv查看当前环境变量,如下图所示:
图 11.4.8环境变量
从上图可以看到新建的环境变量:author,其值为:alientek。
3、删除环境变量
既然可以新建环境变量,那么就可以删除环境变量,删除环境变量也是使用命令setenv,要删除一个环境变量只要给这个环境变量赋空值即可,比如我们删除掉上面新建的author这个环境变量,命令如下:
setenv author
saveenv
上面命令中通过setenv给author赋空值,也就是什么都不写来删除环境变量author。重启uboot就会发现环境变量author没有了。
11.4.3内存操作命令
内存操作命令就是用于直接对DRAM进行读写操作的,常用的内存操作命令有md、nm、mm、mw、cp和cmp。我们依次来看一下这些命令都是做什么的。
1、md命令
md命令用于显示内存值,格式如下:
md [.b, .w, .l .q] address [# of objects]
命令中的[.b .w .l .q]分别对应byte、word、long、qword,也就是分别以1个字节、2个字节、4个字节和8个字节来显示内存值。address就是要查看的内存起始地址,[# of objects]表示要查看的数据长度,这个数据长度单位不是字节,而是跟你所选择的显示格式有关。比如你设置要查看的内存长度为20(十六进制为0x14),如果显示格式为.b的话那就表示20个字节;如果显示格式为.w的话就表示20个word,也就是202=40个字节;如果显示格式为.l的话就表示20个long,也就是204=80个字节。另外要注意:
uboot命令中的数字都是十六进制的!不是十进制的!
比如你想查看以0X8000000开始的20个字节的内存值,显示格式为.b的话,应该使用如下所示命令:
md.b 8000000 14
而不是:
md.b 8000000 20
上面说了,uboot命令里面的数字都是十六进制的,所以可以不用写“0x”前缀,十进制的20其十六进制为0x14,所以命令md后面的个数应该是14,如果写成20的话就表示查看32(十六进制为0x20)个字节的数据。分析下面四个命令的区别:
md.b 8000000 10
md.w 8000000 10
md.l 8000000 10
md.q 8000000 10
上面这四个命令都是查看以0X8000000为起始地址的内存数据,第一个命令以.b格式显示,长度为0x10,也就是16个字节;第二个命令以.w格式显示,长度为0x10,也就是162=32个字节;第二个命令以.l格式显示,长度为0x10,也就是164=64个字节;最后一个命令以.q格式显示,长度也是0x10,也就是16*8=128个字节。这四个命令的执行结果如下图所示:
图 11.4.9 md命令使用示例
2、nm命令
nm命令用于修改指定地址的内存值,命令格式如下:
nm [.b, .w, .l .q] address
nm命令同样可以以.b、.w、.l和.q来指定操作格式,比如现在以.l格式修改0x8000000地址的数据为0x12345678。输入命令:
nm.l 8000000
输入上述命令以后如下图所示:
图 11.4.10 nm命令
在上图中,8000000表示现在要修改的内存地址,1400000a表示地址0x8000000现在的数据,?后面就可以输入要修改后的数据0x12345678,输入完成以后按下回车,然后再输入‘q’即可退出,如下图所示:
图 11.4.11修改内存数据
修改完成以后再使用命令md来查看一下有没有修改成功(命令md.l 8000000 1),如下图所示:
图 11.4.12查看修改后的值
从上图可以看出,此时地址0X8000000的值变为了0x12345678。
3、mm命令
mm命令也是修改指定地址内存值的,使用mm修改内存值的时候地址会自增,而使用命令nm的话地址不会自增。比如以.l格式修改从地址0x8000000开始的连续3个内存块(3*4=12个字节)的数据为0x05050505,操作如下图所示(命令mm.l 8000000):
图 11.4.13命令mm
从上图可以看出,修改了地址0X8000000、0X8000004和0X8000008的内容为0x05050505。使用命令md查看修改后的值(命令md.l 8000000 3),结果如下图所示:
图 11.4.14查看修改后的内存数据
从上图可以看出内存数据修改成功。
4、mw命令
命令mw用于使用一个指定的数据填充一段内存,命令格式如下:
mw [.b, .w, .l, .q] address value [count]
mw命令同样可以以.b、.w、.l和.q来指定操作格式,address表示要填充的内存起始地址,value为要填充的数据,count是填充的长度。比如使用.l格式将以0X8000000为起始地址的0x10个内存块(0x10 * 4=64字节)填充为0X0A0A0A0A,然后使用命令md来查看,命令如下:
mw.l 8000000 0A0A0A0A 10
md.l 8000000 10
结果如下图所示:
图 11.4.15查看修改后的内存数据
从上图可以看出内存数据修改成功。
5、cp命令
cp是数据拷贝命令,用于将DRAM中的数据从一段内存拷贝到另一段内存中。命令格式如下:
cp [.b, .w, .l, .q] source target count
cp命令同样可以以.b、.w、.l和.q来指定操作格式,source为源地址,target为目的地址,count为拷贝的长度。我们使用.l格式将0x8000000处的地址拷贝到0X8000100处,长度为0x10个内存块(0x10 * 4=64个字节),命令如下所示:
md.l 8000000 10 #查看地址0x8000000处的内容
md.l 8000100 10 #查看地址0x8000100处的内容
cp.l 8000000 8000100 10 #复制数据
md.l 8000100 10 #查看地址0x8000100处的内容
结果如下图所示:
图 11.4.16 cp命令操作结果
在上图中,先使用md.l命令打印出地址0x8000000和0x8000100处的数据,然后使用命令cp.l将0x8000000处的数据拷贝到0x8000100处。最后使用命令md.l查看0x8000100处的数据有没有变化,检查拷贝是否成功。
6、cmp命令
cmp是比较命令,用于比较两段内存的数据是否相等,命令格式如下:
cmp [.b, .w, .l, .q] addr1 addr2 count
cmp命令同样可以以.b、.w、.l和.q来指定操作格式,addr1为第一段内存首地址,addr2为第二段内存首地址,count为要比较的长度。我们使用.l格式来比较0x8000000和0X8000100这两个地址数据是否相等,比较长度为0x10个内存块(16 * 4=64个字节),命令如下所示:
cmp.l 8000000 8000100 10
结果如下图所示:
图 11.4.17 cmp命令比较结果
从上图可以看出两段内存的数据相等。我们再随便挑两段内存比较一下,比如地址0x8002000和0x8003000,长度为0X10,比较结果如下图所示(命令cmp.l 8002000 8003000 10):
图 11.4.18 cmp命令比较结果
从上图可以看出,0x8002000处的数据和0x8003000处的数据就不一样。
11.4.4网络操作命令
uboot是支持网络的,而且网络功能是必不可少的,因为在移植linux kernel的时候需要使用到uboot的网络功能做调试。uboot支持大量的网络相关命令,比如dhcp、ping、nfs和tftpboot,我们接下来依次学习一下这几个和网络有关的命令。
开发板有两个网口:PS_ETH和PL_ETH,在使用uboot的网络功能之前先用网线将开发板的以太网PS_ETH或者PL_ETH接口和电脑或者路由器连接起来。建议开发板和主机PC都连接到同一个路由器上。
连接好后,如何在uboot中选择正确的网口呢?
uboot对网口phy芯片的操作有两个命令——mdio和mii。mdio命令可用于列出当前可用的phy芯片,以及读写phy芯片的寄存器,该命令的详细用法说明如下:
图 11.4.19 mdio用法说明
一般我们用mdio命令获取当前可用的phy芯片,也就是mdio总线。输入“mdio list”,结果如下图所示:
图 11.4.20获取当前可用的phy芯片
可以看到,当前开发板有两个可用的phy芯片,eth0的phy地址为4,eth1的phy地址为7。phy地址为7的是开发板上的PS网口(PS_ETH),phy地址为4的是开发板上的PL网口(PL_ETH)。
获取到phy地址及其对应的eth*后,就可以用mii命令进行切换,选择正确的网口。
mii命令与mdio命令相似,也可用于列出当前可用的phy芯片,以及读写phy芯片的寄存器,该命令的详细用法说明如下:
图 11.4.21 mii用法说明
一般我们用mii命令对多网口进行网口切换。
先用mii获取当前可用的phy芯片。输入“mii device”,结果如下图所示:
图 11.4.22获取当前可用的phy芯片
显示有mii器件eth1和eth0,当前使用的器件为eth0。
命令“mii info”可显示mii phy的信息,如下图所示:
图 11.4.23 mii phy的信息
显示“PHY 0x04”为“10baseT,HDX”,也就是地址为4的PHY芯片通信速度为10Mb,半双工,“PHY 0x00”也是如此,不过笔者好奇的是,哪来的地址为0的PHY芯片。从前面mdio list得到的只有phy地址为4和7的,没有0,不知道是uboot的bug还是笔者理解的不对。
出现上面通信速度为10baseT的,先看看与开发板连接的路由器或者电脑是否也是千兆的,如果是的话,那应该是当前使用的phy芯片不对,需要切换到正确的phy芯片上。前面“mii device”显示当前使用的是eth0,既然不对我们将其切换到eth1,命令如下:
mii device eth1
mii info
结果如下图所示:
图 11.4.24 切换到eth1
显示“PHY 0x07”为“1000baseT,FDX”,也就是地址为7的PHY芯片通信速度为1000Mb,全双工。笔者当前使用的也的确是PHY地址为7的网口——PS_ETH。
切换到正确的phy芯片之后,就可以进行下面的操作了。
注:mdio和mii命令的读写phy寄存器功能在调试phy芯片时很有用,特别是在自己画板时使用的phy芯片不受uboot支持,或延时不对,导致链路不通时。不过现在uboot对市面上常用的phy芯片支持很好,哪怕用的是国产的phy芯片,也基本做到完美支持,当然了这也于国产phy芯片一般兼容于市面上常用的phy芯片有关,所以导致了mdio和mii命令现在用的不多,常见的用法也就是上面介绍的切换phy芯片。
与网络相关的环境变量如下表所示:
表 11.4.1 网络相关环境变量
环境变量 描述
ipaddr 开发板ip地址,可以不设置,使用dhcp命令来从路由器获取IP地址
ethaddr 开发板的MAC地址,一定要设置
gatewayip 网关地址
netmask 子网掩码
serverip TFTP服务器IP地址,使用tftp相关命令要用到,用于调试代码
这里重点说一下serverip,也就是TFTP服务器的IP地址,一般我们用Ubuntu主机作为TFTP服务器,所以也就是Ubuntu主机的IP地址。
连接网线,开发板上电后uboot默认通过dhcp获取网络ip地址(与路由器连接时有效),若与电脑直连,可以在进入uboot命令行模式后通过以下命令手动设置:
setenv ipaddr 192.168.2.123
setenv ethaddr 00:0a:35:00:1e:53
setenv gatewayip 192.168.2.1
setenv netmask 255.255.255.0
setenv serverip 192.168.2.134
saveenv
注意,连接到电脑的以太网接口需要同时设置电脑以太网适配器的Ipv4属性。另外网络地址环境变量的设置要根据自己的实际情况,确保Ubuntu主机和开发板的IP地址在同一个网段内,比如笔者现在的开发板和电脑都在192.168.2.0这个网段内,所以设置开发板的IP地址为192.168.2.123,笔者的Ubuntu主机的地址为192.168.2.134,因此serverip就是192.168.2.134。设置电脑的以太网和Ubuntu主机网络的方法可参考7.7.3小节。
ethaddr为网络MAC地址,是一个48bit的地址,如果在同一个网段内有多个开发板的话一定要保证每个开发板的ethaddr是不同的,否则通信会有问题。设置好网络相关的环境变量以后就可以使用网络相关命令了。
与路由器连接时需要设置serverip,也就是Ubuntu主机的IP地址(如笔者的为192.168.2.134):
setenv serverip 192.168.2.134
saveenv
1、ping命令
开发板的网络能否使用,是否可以和服务器(Ubuntu主机)进行通信,通过ping命令就可以验证,直接ping服务器的IP地址即可,比如笔者的服务器IP地址为192.168.1.,命令如下:
ping 192.168.2.134
结果如下图所示:
图 11.4.25 ping命令
从上图可以看出,192.168.2.134这个主机存在,说明ping成功,uboot的网络工作正常。
注意:如果是通过网线直接将开发板和电脑连接,ping不通的情况,除了以上设置不正确外,还有可能是电脑的防火墙导致的。可以关闭防火墙或者允许文件和打印共享应用通过防火墙(针对Windows系统)。
2、dhcp命令
dhcp用于从路由器获取IP地址,前提是开发板连接到路由器,如果开发板是和电脑直连的,那么dhcp命令就会失效。直接输入dhcp命令即可通过路由器获取到IP地址,如下图所示:
图 11.4.26 dhcp命令
从上图可以看出,开发板通过dhcp获取到的IP地址为192.168.2.181,和我们手动设置的不同,这不影响使用,因为没有连接路由器,不参与局域网,所以手动设置的IP地址只在开发板和电脑之间有效。
3、nfs命令
nfs也就是网络文件系统,通过nfs可以在计算机之间通过网络来分享资源,比如我们将linux镜像和设备树文件放到Ubuntu中,然后在uboot中使用nfs命令将Ubuntu中的linux镜像和设备树下载到开发板的DRAM中。这样做的目的是为了方便调试linux镜像和设备树,也就是网络调试,使用网络调试是Linux开发中最常用的调试方法。原因是嵌入式linux开发不像单片机开发,可以直接通过JLINK或STLink等仿真器将代码直接烧写到单片机内部的flash中,嵌入式Linux通常是烧写到SD卡、eMMC、QSPI Flash等外置flash中,但是嵌入式Linux开发也没有MDK、IAR这样的IDE,更没有烧写算法,因此不可能通过点击一个“download”按钮就将固件烧写到外部flash中。这个时候网络调试的优势就显现出来了,可以通过网络将编译好的linux镜像和设备树文件下载到DRAM中,然后就可以直接运行。
我们一般使用uboot中的nfs命令将Ubuntu中的文件下载到开发板的DRAM中,在使用之前需要开启Ubuntu主机的NFS服务,并且要新建一个NFS使用的目录,以后所有要通过NFS访问的文件都需要放到这个NFS目录中。Ubuntu的NFS服务开启我们在4.4.1小节已经详细讲解过了,包括NFS文件目录的创建,如果忘记的话可以去查看一下该小节。笔者设置的/home/shang/workspace/nfs这个目录为笔者的NFS文件目录。uboot中的nfs命令格式如下所示:
nfs [loadAddress] [[hostIPaddr:]bootfilename]
loadAddress是要加载到的DRAM地址,[[hostIPaddr:]bootfilename]是要下载的文件地址。这里我们将当前Petalinux工程目录images/linux下的内核镜像文件Image复制到NFS目录下,比如笔者放到/home/shang/workspace/nfs这个目录下,完成后的NFS目录如下图所示:
图 11.4.27 NFS目录中的Image文件
准备好以后就可以使用nfs命令来将Image下载到开发板DRAM的0x00200000地址处。在串口终端中输入如下命令:
nfs 200000 192.168.2.134:/home/shang/workspace/nfs/Image
命令中的“200000”表示Image加载到DRAM中的地址,“192.168.2.134:/home/shang/workspace/nfs/Image”表示Image在192.168.2.134这个主机中,路径为/home/shang/workspace/nfs/Image。下载过程如下图所示:
图 11.4.28 nfs命令下载Image过程
在上图中会以“#”提示下载过程。如果出现“ERROR: `serverip’ not set”的错误,就设置serverip为Ubuntu主机的IP地址。笔者的Ubuntu主机的IP地址为192.168.2.134,因此使用命令“setenv serverip 192.168.2.134”设置serverip为192.168.2.134。下载完成以后会提示下载的数据大小,这里下载的18082304字节,而Image的大小就是18082304字节,如下图所示:
图 11.4.29 zImage大小
下载完成以后查看0x00200000地址处的数据,使用命令md.b来查看前0x100个字节的数据(命令md.b 200000 100),如下图所示:
图 11.4.30下载的数据
在Ubuntu虚拟机中,使用od命令或xxd命令来查看Image,检查一下前面的数据是否和上图中的一致,命令如下:
od -tx1 -vN 0x100 Image
或
xxd -g 1 -l 0x100 Image
结果如下图所示:
图 11.4.31 winhex查看Image
可以看出图 11.4.30和图 11.4.31的前0x100个字节的数据一致,说明nfs命令下载到的zImage是正确的。
4、tftpboot命令
tftpboot命令的作用和nfs命令一样,都是用于通过网络下载文件到DRAM中,只是tftpboot命令使用的是TFTP协议,Ubuntu主机作为TFTP服务器。
在Ubuntu虚拟机中,将Image镜像文件拷贝到TFTP服务器使用的tftpboot文件夹中。这一步我们在6.3.8节编译工程完成后Petalinux工具已经为我们做好了,从下图我们可以看到Image已经存在于/tftpboot目录下:
万事俱备,只剩验证了,uboot中的tftp命令格式如下:
tftpboot [loadAddress] [[hostIPaddr:]bootfilename]
看起来和nfs命令格式一样的,loadAddress是文件在DRAM中的存放地址,[[hostIPaddr:]bootfilename]是要从Ubuntu中下载的文件。但是和nfs命令的区别在于,tftpboot命令不需要输入文件在Ubuntu中的完整路径,只需要输入文件名即可。比如我们现在将tftpboot文件夹里面的Image文件下载到开发板DRAM的0x00000000地址处,命令如下:
tftpboot 0 Image
下载过程如下图所示:
图 11.4.32 tftp命令下载过程
从上图可以看出,Image下载成功了,网速为3 MiB/s,文件大小为18082304字节。同样的,可以使用md.b命令来查看前100个字节的数据是否和上图中的相等。有时候使用tftpboot命令从Ubuntu中下载文件的时候会出现如“TFTP error: ‘Permission denied’ (0)”这样的错误提示,提示没有权限,出现这个错误一般有两个原因:
①、在Ubuntu中创建tftpboot目录的时候没有给予tftboot相应的权限。
②、tftpboot目录中要下载的文件没有给予相应的权限。
针对上述两个问题,使用命令“chmod 777 xxx”来给予权限,其中“xxx”就是要给予权限的文件或文件夹。有时候使用tftpboot命令会出现“Retry count exceeded; starting again”的提示,这是因为网络不稳定造成的,重新执行命令即可。
好了,uboot中关于网络的命令就讲解到这里,我们最常用的就是ping、nfs和tftpboot这三个命令。使用ping命令来查看网络的连接状态,使用nfs和tftp命令来从Ubuntu主机中下载文件。
11.4.5eMMC和SD卡操作命令
uboot支持eMMC和SD卡,因此也要提供eMMC和SD卡的操作命令。一般认为eMMC和SD卡是同一个东西,所以没有特殊说明,本教程统一使用MMC来代指eMMC和SD卡。uboot中常用于操作MMC设备的命令为“mmc”。
mmc是一系列的命令,其后可以跟不同的参数,输入“?mmc”即可查看mmc有关的命令,如下图所示:
图 11.4.33 mmc命令
从上图可以看出,mmc后面跟不同的参数可以实现不同的功能,部分命令如下表所示:
表 11.4.2 mmc命令
命令 描述
mmc info 输出MMC设备信息
mmc read 读取MMC中的数据
mmc wirte 向MMC设备写入数据
mmc erase 擦除MMC中的数据
mmc rescan 重新扫描MMC设备
mmc part 列出MMC设备的分区
mmc dev 切换MMC设备
mmc list 列出当前所有有效的MMC设备
mmc hwpartition 设置MMC设备的分区
mmc setdsr 设置DSR寄存器的值
1、mmc rescan命令
mmc rescan命令用于扫描当前开发板上所有的MMC设备,包括eMMC和SD卡,输入命令“mmc rescan”即可。
2、mmc list命令
mmc list命令用于来查看当前开发板一共有几个MMC设备,输入“mmc list”,结果如下图所示:
图 11.4.34扫描MMC设备
可以看出当前开发板有两个MMC设备:mmc@ff160000: 0 (eMMC)和mmc@ff170000: 1 (SD),mmc@ff160000: 0 (eMMC)是eMMC,mmc@ff170000: 1 (SD)是SD卡。
3、mmc info命令
mmc info命令用于输出当前选中的mmc info设备的信息,输入命令“mmc info”即可,如下图所示:
图 11.4.35 mmc info命令
从上图可以看出,当前选中的MMC设备是版本号为5.1,容量为7.3GiB的eMMC,速度为52MHz,使用4位宽的总线。还有一个与mmc info命令相同功能的命令:mmcinfo,“mmc”和“info”之间没有空格。默认会将SD卡设置为当前MMC设备。
4、mmc dev命令
mmc dev命令用于切换当前MMC设备,命令格式如下:
mmc dev [dev] [part]
[dev]用来设置要切换的MMC设备号,[part]是分区号。如果不写分区号的话默认为分区0。使用如下命令切换到eMMC:
mmc dev 1 //切换到SD卡,0为eMMC,1为SD卡
结果如下图所示:
图 11.4.36切换到SD卡
从上图可以看出,切换到eMMC成功,mmc1为当前的MMC设备,输入命令“mmc info”即可查看eMMC的信息,结果如下图所示:
图 11.4.37 eMMC信息
从上图可以看出当前SD卡版本为 3.0,容量为 14.8GiB,速度为50000000Hz=50MHz,4 位宽的总线。
5、mmc part命令
有时候SD卡或者eMMC会有多个分区,可以使用命令“mmc part”来查看其分区,比如查看SD卡的分区情况,输入如下命令:
mmc dev 1 //切换到SD卡
mmc part //查看SD卡分区
结果如下图所示:
图 11.4.38查看SD卡分区
从上图中可以看出,此时SD卡有两个分区,扇区2048196608为第一个分区,扇区19865630916608为第二个分区。
6、mmc read命令
mmc read命令用于读取mmc设备的数据,命令格式如下:
mmc read addr blk# cnt
addr是数据读取到DRAM中的地址,blk是要读取的块起始地址(十六进制),一个块是512字节,这里的块和扇区是一个意思,在MMC设备中我们通常说扇区,cnt是要读取的块数量(十六进制)。比如从SD卡的第2048(0x800)个块开始,读取16(0x10)个块的数据到DRAM的0x00000000地址处,命令如下:
mmc dev 1 0 //切换到MMC分区0
mmc read 00000000 800 10 //读取数据
结果如下图所示:
图 11.4.39 mmc read命令
7、mmc write命令
要将数据写到MMC设备里面,可以使用命令“mmc write”,格式如下:
mmc write addr blk# cnt
addr是要写入MMC中的数据在DRAM中的起始地址,blk是要写入MMC的块起始地址(十六进制),cnt是要写入的块大小,一个块为512字节。注意千万不要写SD卡或者eMMC的前两个块(扇区),里面保存着分区表信息。
8、mmc erase命令
如果要擦除MMC设备的指定块就是用命令“mmc erase”,命令格式如下:
mmc erase blk# cnt
blk为要擦除的起始块,cnt是要擦除的数量。没事不要用mmc erase来擦除MMC设备。
关于MMC设备相关的命令就讲解到这里,表 11.4.5.1中还有一些跟MMC设备操作有关的命令,但是很少用到,这里就不讲解了,感兴趣的可以上网查一下,或者在uboot中查看这些命令的使用方法。
11.4.6FAT格式文件系统操作命令
有时候需要在uboot中对SD卡或者eMMC中存储的文件进行操作,这时候就要用到文件操作命令,跟文件操作相关的命令有:fatinfo、fatls、fstype、fatload和fatwrite,但是这些文件操作命令只支持FAT格式的文件系统。
1、fatinfo命令
fatinfo命令用于查询指定MMC指定分区的文件系统信息,格式如下:
fatinfo [<dev[:part]>]
interface表示接口,比如mmc,dev是查询的设备号,part是要查询的分区。比如我们要查询SD卡分区1的文件系统信息,命令如下:
fatinfo mmc 1:1
结果如下图所示:
图 11.4.40 SD卡分区1文件系统信息
从上图可以看出,SD卡分区1的文件系统为FAT32格式的。
2、fatls命令
fatls命令用于查询FAT格式设备的目录和文件信息,命令格式如下:
fatls [<dev[:part]>] [directory]
interface是要查询的接口,比如mmc,dev是要查询的设备号,part是要查询的分区,directory是要查询的目录。比如查询SD卡分区1中的所有的目录和文件,输入命令:
fatls mmc 1:1
结果如下图所示:
图 11.4.41 SD卡分区1文件查询
从上图可以看出,SD卡的分区1中存放着2个文件:BOOT.BIN和image.ub,没有目录。其中BOOT.BIN文件是我们拷贝到SD卡的FAT32分区中的ZYNQ MPSoC启动文件。
3、fstype命令
fstype用于查看MMC设备某个分区的文件系统格式,命令格式如下:
fstype :
在6.3.10小节制作SD启动卡时我们将SD卡分成两个分区,我们来查看一下这两个分区的文件系统格式,输入命令:
fstype mmc 1:1
fstype mmc 1:2
结果如下图所示:
图 11.4.42 fstype命令
从上图可以看出,分区1的格式为fat,分区1用于存放BOOT.BIN等启动镜像文件。分区2的格式为ext4,用于存放Linux的根文件系统。
4、fatload命令
fatload命令用于将指定的文件读取到DRAM中,命令格式如下:
fatload [<dev[:part]> [ [ [bytes [pos]]]]]
interface为接口,比如mmc,dev是设备号,part是分区,addr是保存在DRAM中的起始地址,filename是要读取的文件名字。bytes表示读取多少字节的数据,如果bytes为0或者省略的话表示读取整个文件。pos是要读的文件相对于文件首地址的偏移,如果为0或者省略的话表示从文件首地址开始读取。我们将SD卡分区1中的BOOT.BIN文件读取到DRAM中的0X00000000地址处,命令如下:
fatload mmc 1:1 00000000 BOOT.BIN
操作过程如下图所示:
图 11.4.43读取过程
从上图可以看出在756ms内读取了8911864个字节的数据,速度为11.2MiB/s。