LV01-02-IMX6ULL-ALPHA开发板体验-02-NXP-mfgtool简介

本文主要是体验正点原子I.MX6U-ALPHA开发板出厂系统——NXP官方烧写工具mfgtool的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
PC端开发环境 Windows Windows11
Ubuntu Ubuntu20.04.6的64位版本(一开始使用的是16.04版本,后来进行了升级)
VMware® Workstation 17 Pro 17.0.0 build-20800274
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
Win32DiskImager Win32DiskImager v1.0
Linux开发板环境 Linux开发板 正点原子 i.MX6ULL Linux 阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
点击查看本文参考资料
分类 网址 说明
官方网站 https://www.arm.com/ ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.nxp.com.cn/ NXP官方网站
https://www.nxpic.org.cn/NXP 官方社区
https://u-boot.readthedocs.io/en/latest/u-boot官网
https://www.kernel.org/linux内核官网
点击查看相关文件下载
分类 网址 说明
NXP https://github.com/nxp-imx NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库
https://elixir.bootlin.com/linux/latest/source 在线阅读linux kernel源码
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga
I.MX6ULL i.MX 6ULL Applications Processors for Industrial Products I.MX6ULL 芯片手册(datasheet,可以在线查看)
i.MX 6ULL Applications ProcessorReference Manual I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网)

一、mfgtool烧写工具

这是一个烧写工具,是NXP专门为自己家的芯片出的一个工具,我们是可以通过脚本来使用这个工具烧写我们所需要的文件到开发板中去,而脚本可以根据我们的需求来写。

1. ALPHA板开发资料中的mfgtool

这个烧写工具,在正点原子提供的资料里边已经有了,我们使用的话就先用正点原子提供的这一个:

image-20230713074739385

2. NXP官方原版mfgtool

我们先对官方原版的mfgtool进行一个了解,方便后边学习定制自己的mfgtool。

2.1 软件下载

MfgTool 工具是 NXP 提供的专门用于给 I.MX 系列 CPU 烧写系统的软件,可以在 NXP 官网下载到,由于正点原子出厂的系统都是基于NXP维护的4.1.15的相关BSP资源开发的,所以这里的工具,我也选择相同的版本:IMX6_L4.1.15_2.0.0_MFG_TOOL (nxp.com.cn)。 下载并解压好的文件如下所示:

image-20241103215738343

解压后有两个.txt 文件和两个.gz 压缩包。 .txt 文档就不去看了,重点是这两个 .gz 压缩包,这两个压缩包的区别在名字上已经写的很详细了。“without-rootfs”和“with-rootfs”,一个是带 rootfs 和一个是不带 rootfs。

2.2 相关介绍

mfg_tools 这个工具本意是给 NXP 自己的开发板设计的烧写软件,所以肯定带有自家开发板对应的 uboot、 linux kernel 和 rootfs 的文件。我们肯定是要烧写文件系统的,所以选择 mfgtools-with-rootfs.tar.gz 这个压缩包,继续对其解压, 解压出一个名为 mfgtools-with-rootfs 的文件夹,此文件夹就包含有我们需要的烧写工具。解压后会包含以下文件:

image-20241103215954270

(1)我们只关心上图中 Profiles 这个文件夹,因为后面要烧写文件就放到这个文件夹中。里面存放着的是其实就是镜像文件,这里面镜像文件分为两种:第一种是作为媒介用途的 镜像,包括uboot、dtb与uImage;第二种是真正烧录到emmc、sdcard或者nand flash的镜像文件。多提一句,之所以存在这两种镜像,是因为MFGTools的烧录 原理是先烧录媒介镜像到ram里面,然后启动,再通过这个启动了的媒介镜像把目标镜像烧录到emmc、sdcard或者nand flash里面。

(2)MfgTool2.exe 就是烧写软件,但是我们不会直接打开这个软件烧写, mfg_tools 不仅能烧写I.MX6U,而且也能给 I.MX7、 I.MX6Q 等芯片烧写,所以在烧写之前必须要进行配置,指定烧写的是什么芯片,烧写到哪里去。

(3)Document文件夹中存放了与该工具相关的文档。

(4)Driver目录为Windows 32位和64位操作系统的驱动,驱动一般都不会出现问题,也就无需理会。

(5)More_scripts目录主要存放一些脚本文件,如果只是单纯使用MFGtool工具 也无需修这些脚本文件,此处暂时无需理会。

(6)还有这些众多的.vbs 文件就是配置脚本,烧写的时候通过双击这些.vbs 文件来打开烧写工具。这些.vbs 烧写脚本既可以根据处理器的不同,由用户自己选择向 I.MX6D、 I.MX6Q、 I.MX6S、 I.MX7、 I.MX6UL 和 I.MX6ULL 等的哪一款芯片烧写系统。也可以根据存储芯片的不同,选择向 EMMC、 NAND 或 QSPI Flash 等的哪一种存储设备烧写,功能非常强大。例如我们现在需要向 I.MX6U 烧写系统 ,那么就需要参考下表所示的 5 个烧写脚本:

脚本文件描述
mfgtool2-yocto-mx-evk-emmc.vbsEMMC 烧写脚本
mfgtool2-yocto-mx-evk-nand.vbsNAND 烧写脚本
mfgtool2-yocto-mx-evk-qspi-nor-n25q256a.vbsQSPI Flash 烧写脚本,型号为 n25q256a
mfgtool2-yocto-mx-evk-sdcard-sd1.vbs如果 SD1 和 SD2 接的 SD 卡,这两个文件分 别向 SD1 和 SD2 上的 SD 卡烧写系统。
mfgtool2-yocto-mx-evk-sdcard-sd2.vbs
其他的.vbs 烧写脚本用不到,因此可以删除掉。 image-20241103221624913

3. mfgtool启动

前边我们做完准备工作后,USB线应该一端接在电脑,另一端接在开发板的USB OTG口,拨码开关应该对应在从USB启动(0100 0000)的状态。

image-20240525164855977

当一切准备就绪以后,按一下开发板的复位键,(如果插了 TF 卡,请弹出 TF 卡,否则电脑不能识别 USB!等识别出来以后再插上 TF 卡! )此时就会进入到 USB 模式,如果是第一次进入 USB 模式的话可能会久一点,这个是免驱的,因此不需要安装驱动。一旦第一次设置好设备以后,后面每次连接都不会有任何提示了。

image-20240525165007553

怎么确认连接成功了呢?我们可以双击打开这个文件:

image-20221023135227789

然后会看到如下弹窗:

image-20221023135252376

上边显示【No Device Connected】就说明USB连接不成功,我们可以重启开发板重试一下,若识别到USB,连接成功的话,如下图:

image-20221023135357169

会显示【符合 HID 标准的供应商定义设备】。这里需要注意,我们需要先拔出TF卡,等识别到USB后再插上TF卡即可。

二、系统烧写原理

1. 烧写流程概述

1.1 两个阶段

开发板连接电脑以后双击“ mfgtool2-yocto-mx-evk-emmc.vbs ”文件打开软件。

image-20221023135529577

然后就会打开mfgtool软件,会有如下弹窗:

image-20221023135644384

【注意】若是使用的是正点原子alpha linux开发板,这里需要先拔掉TF卡,并且将拨码开关拨到从USB启动,否则会识别不到USB。要是有其他的MfgTool2.exe正在运行,这时候就会报Initialize the library failed,error code:29.

如果出现【符合 HID 标准的供应商定义设备】就说明连接正常,可以进行烧写,如果出现其他的字符那么就要检查连接是否正确。点击【Start】按钮即可开始烧写,烧写什么东西呢?肯定是烧写 uboot、 Linux kernel、 .dtb 和 rootfs,那么这四个文件应该放到哪里 MfgTool 才能访问到呢? 我们进入如下目录中 :

1
L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware

该目录中相关文件如下图所示:

image-20221023140005355

文件夹“ OS Firmware ”是存放系统固件的,我们重点需要关注该目录中的 files、 firmware 这两个文件夹,以及 ucl2.xml 这个文件。在具体看这三个文件和文件夹之前,我们先来简单了解一下 MfgTool 烧写的工作流程:

MfgTool 其实是先通过 USB OTG 先将 uboot、 kernel 和.dtb(设备树)这是三个文件下载到开发板的 DDR 中,注意此时不需要下载 rootfs。就相当于直接在开发板的 DDR上启动 Linux 系统,等 Linux 系统启动以后再向 EMMC 中烧写完整的系统,包括 uboot、 linux kernel、 .dtb(设备树)和 rootfs,因此 MfgTool 工作过程主要分两个阶段:

(1)将 firmware 目录中的 uboot、 linux kernel 和.dtb(设备树),通过 USB OTG 将这个文件下载到开发板的 DDR 中,目的就是在 DDR 中启动 Linux 系统,为后面的烧写做准备。这个就对应后面的BurnStarp阶段

(2)经过第(1)步的操作,此时 Linux 系统已经运行起来了,系统运行起来以后就可以很方便的完成对 EMMC 的格式化、分区等操作。 EMMC 分区建立好以后就可以从 files 中读取要烧写的 uboot、 linux kernel、 .dtb(设备树)和 rootfs 这 4 个文件,然后将其烧写到 EMMC 中。其实也不仅限于EMMC,这个烧录的位置由用户指定,可以是sd card、nand flash,emmc等。这个对应后面的Updater阶段

1.2 连接后发生了什么?

img

(1)当i.MX6板子上电后,什么都没有初始化,只有Chip ROM(芯片的内部ROM)可以运行。Chip ROM的默认程序会去初始化USB,并将其配置为HID设备。这就是为什么板子设置为Download模式并上电后在PC端的设备管理器可以看到HID-compliant device连上。

(2)一旦PC机检测到HID设备连上,那么PC(USB Host)就会去枚举设备并建立连接。建立连接后的第一件事就是传送一个mini uboot(firmware)到目标板SoC的Chip RAM(芯片的内部RAM)中然后开始运行mini uboot(firmware)。mini uboot会对目标板进行初始化,主要是初始化 DDR/外部ROM等设备,所以之后DDR及外部ROM已经可以访问了。mini uboot从mini uboot中提取内存初始化代码或数据来执行任务。也可以使用内存初始化脚本来获取初始化数据。

(3) 初始化外部基本接口之后,PC端会将mini kernel(firmware)、mini system(firmware)、dtb(firmware)传送给SoC并放到DDR(外部RAM)指定的地址。然后PC发送命令,例如ucl2.xml中的:

1
<CMD state="BootStrap" type="jump" > Jumping to OS image. </CMD>

PC把这个命令发送给Chip ROM让其跳转到DDR的kernel首地址去执行。

(4)跳转到kernel之后,初始化USB驱动,并枚举为一个MSC设备,此时SoC可以和主机进行高速数据传输。

(5)之后PC机MFGTool工具通过ucl2.xml中的脚本逐条执行剩下的指令直到最后遇到Done指令。

【说明】

(1)上面提到的mini uboot、mini kernel、mini system、dtb文件均在软件目录下的这个文件夹下:

1
>L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware/firmware

(2)mini kernel必须要配置USB、USB大容量存储驱动、SD卡、UTP等。详见

1
>L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Document/V2/Manufacturing Tool V2 Linux or Android Firmware Development Guide V2.docx

(3)mini kernel启动linuxrc作为第一个进程,linuxrc其实是一个脚本,最终通过执行 uuc 来启动UTP传输协议。UTP是作为USB Mass Storage(v4 Kernel)驱动的扩展,所以启动 UTP 必须对 USB 进行复位然后重新枚举。

2. OS Firmware目录说明

2.1 firmware目录

路径为:

1
L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware/firmware

打开 firmware 文件夹,里面有很多的 .imx 结尾的 uboot 文件、一个 zImage 镜像文件、很多.dtb结尾的设备树文件。这些文件都是NXP官方开发板使用的,不同的板子使用不同的文件,其中我们需要关心的只有下表的这三个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树。
这三个文件就是 I.MX6ULL EVK 开发板烧写系统的时候**第一阶段**所需的文件。**如果要烧写我们的系统,就需要用我们编译出来的zImage、 u-boot.imx 和 imx6ull-alienteke-mmc.dtb 这三个文件替换掉表中这三个文件。**但是名字要和表中的一致,因此需要将 u-boot.imx 重命名为 u-boot-imx6ull14x14evk_emmc.imx,将 imx6ull-alientek-emmc.dtb重命名为 zImage-imx6ull-14x14-evk-emmc.dtb。

2.2 files目录

路径为:

1
L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS Firmware/files

第二阶段就是从 files 目录中读取整个系统文件,并将其烧写到 EMMC 中。 files 目录中的文件和firmware 目录中的基本差不多,都是不同板子对应的 uboot、设备树文件,我们只关心下表中的四个文件:

脚本文件描述
zImageNXP 官方 I.MX6ULL EVK 开发板的 Linux 镜像文件。
u-boot-imx6ull14x14evk_emmc.imxNXP 官方 I.MX6ULL EVK 开发板的 uboot 文件。
zImage-imx6ull-14x14-evk-emmc.dtbNXP 官方 I.MX6ULL EVK 开发板的设备树。
rootfs_nogpu.tar.bz2根文件系统,注意和另外一个 rootfs.tar.bz2 根文件系统区分开。 nogpu 表示此根文件系统不包含 GPU 的内容, I.MX6ULL 没有 GPU,因此要使用此根文件系统。
如果要烧写我们自己编译出来的系统,就需要用我们编译出来的 zImage、 u-boot.imx 和imx6ull-alientek-emmc.dtb 和 rootfs 这四个文件替换掉上边这四个文件。

2.3 ucl2.xml 文件

2.3.1 整体框架

files 和 firmware 目录下有众多的 uboot 和设备树,那么烧写的时候究竟选择哪一个呢?这个工作就是由 ucl2.xml 文件来完成的。

点击查看 整体框架
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<UCL>
<CFG>
<!-- 判断向 I.MX 系列的哪个芯片烧写系统 -->
<!-- 中间部分省略...... -->
</CFG>

<LIST name="SDCard" desc="Choose SD Card as media">
<!-- 向 SD 卡烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="eMMC" desc="Choose eMMC as media">
<!-- 向 EMMC 烧写 Linux 系统 -->
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>
<!-- 中间部分省略...... -->
</LIST>

<LIST name="Nor Flash" desc="Choose Nor flash as media">
<!-- 向 Nor Flash 烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="Quad Nor Flash" desc="Choose Quad Nor flash as media">
<!-- 向 Quad Nor Flash 烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="NAND Flash" desc="Choose NAND as media">
<!-- 向 NAND Flash 烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="SDCard-Android" desc="Choose SD Card as media">
<!-- 向 SD 卡烧写 Android 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="eMMC-Android" desc="Choose eMMC as media">
<!-- 向 EMMC 烧写 Android 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="Nand-Android" desc="Choose NAND as media">
<!-- 向 NAND Flash 烧写 Android 系统 -->
<!-- 中间部分省略...... -->
</LIST>

<LIST name="SDCard-Brillo" desc="Choose SD Card as media">
<!-- 向 SD 卡烧写 Brillo 系统 -->
<!-- 中间部分省略...... -->
</LIST>
</UCL>

2.3.2 UCL标签

ucl2.xml 以“<UCL>”开始,以“</UCL>”结束。它表示更新 命令列表(Update Command List,UCL),会被MFGtool工具中的脚本解析,所有的配置都包含在该标签之下。

2.3.3 CFG标签

ucl2.xml 首先会判断当前要向 I.MX 系列的哪个芯片烧写系统,相关代码如下:

1
2
3
4
5
6
7
8
9
10
<CFG>
<STATE name="BootStrap" dev="MX6SL" vid="15A2" pid="0063"/>
<STATE name="BootStrap" dev="MX6D" vid="15A2" pid="0061"/>
<STATE name="BootStrap" dev="MX6Q" vid="15A2" pid="0054"/>
<STATE name="BootStrap" dev="MX6SX" vid="15A2" pid="0071"/>
<STATE name="BootStrap" dev="MX6UL" vid="15A2" pid="007D"/>
<STATE name="BootStrap" dev="MX7D" vid="15A2" pid="0076"/>
<STATE name="BootStrap" dev="MX6ULL" vid="15A2" pid="0080"/>
<STATE name="Updater" dev="MSC" vid="066F" pid="37FF"/>
</CFG>

这个标签之间是配置相关内容,通过读取芯片的 VID 和 PID 即可判断出当前要烧写什么处理器的系统,如果VID=0X15A2,PID=0080,那么就表示要给 I.MX6ULL 烧写系统。

它是MFGtool工具的一些全局配置,如在第一阶段(BootStrap)设备枚举,dev的值可以 为MX6SL、MX6D、MX6Q、MX6SX、MX6UL、MX6ULL、MX7D其中的一个或 多个(因为MFGtool工具可以同时烧录多个开发板),如果USB 的vid为15A2,pid为0080,那么MFGtool工具会将dev识别为MX6ULL,这USB中 的vid与pid是由芯片本身决定的,当dev为MX6ULL时,在后续的烧录任务中会通过ifdev MX6ULL进行选择执行哪些语句,然后将name设置为BootStrap,标识当前处于第 一阶段,不同的阶段执行的处理是不一样的。这个阶段简单来说就是将开发板的USB OTG接口 连接电脑,并且被MFGtool工具识别,才能有接下来的烧录操作。

如果USB中的vid为066F,pid为37FF,则表示进入第二阶段,将dev标识为MSG,而name则被设置为Updater。

简单来说,烧录分为两个阶段,BurnStarp和Updater,通过全局配置设备的vid和pid,来选择操作的设备(开发板)。

2.3.4 LIST标签

1
2
3
4
5
6
7
<LIST name="eMMC" desc="Choose eMMC as media">
<!-- 向 EMMC 烧写 Linux 系统 -->
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>
<!-- 中间部分省略...... -->
</LIST>

“<LIST>”和“</LIST>”之间的是针对不同存储芯片的烧写命令。 这就是前面说的 列表配置,list后面有name、desc等参数,name则表示选择cfg.ini文件中 的list内容中的配置,可以为SDCard、eMMC、NAND Flash等。而desc参数用来说明目的,选择烧录 的位置,如Choose SD Cardas media、Choose eMMC as media、Choose NAND as media等。

2.3.5 CMD标签

1
2
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>

在< LIST > </LIST>标签下有多个<CMD> </CMD>标签,这是命令标签,在不同的 阶段命令是不一样的,MFGtool工具的命令分为主机特定命令(Host Specific Commands)与固件特定命令(Firmware Specific Commands),其中主机特定命令是由MFGtool工具解析和执行,而固件特定命令由 目标设备上的固件运行解析和执行。

命令标签下有多个属性,如state用于表示该命令在哪个阶段被执行,type表示执行命令的类型,body表示命令的参数,flie则是其他参数,如需要烧录哪个文件,最后 的Loading Kernel、Loading U-boot则是命令描述。

2.3.5.1 两种命令
  • 主机特定命令(Host Specific Commands)的命令类型有多种,其他参数也有多种,具体见下表(空余处表示无参数)。

命令类型

命令参数

其他参数

说明

load

file

烧录的镜像文件的路径和文件名

Address

镜像存放的RAM地址

loadSection

ROM代码使用的参数,应设置为“OTH”

setSection

设置为OTH,如果还有其他镜像,设置为APP

HasFlashHeader

镜像如果包含flash header,设为TRUE,否则设置为FALSE

CodeOffset

第一次执行的指令的地址偏移量。注意:该命令仅适用于除i.MX50 HID模式设备之外的Bulk-IO模式i.MX设备。

jump

通知ROM代码跳转到RAM映像运行

boot

保留

flie

加载映像到RAM

if

执行判断

  • 固件特定命令(Firmware Specific Commands)的命令类型有多种,其他参数也有多种:

命令

参数

说明

?

请求以XML格式发送设备标识信息

!

integer

根据参数进行重启操作

$

string

执行shell命令,这是最常用的命令类型

flush

等待所有数据传输完成并处理

ffs

对SD卡进行分区并将引导流闪存到它

read

string

读取参数指定的文件并将其发送给主机。如果没有这样的文件,将返回相应的状态

send

从主机接收文件

selftest

进行自我诊断, 返回通过或适当状态。在当前版本中未实现

save

string

将命令“send”接收的文件保存到指定为参数的文件中。

pipe

string

执行shell命令并从管道的输入端读取数据。 MFGtool工具会将文件发送到管道输出端

wff

准备将固件写入flash

wfs

准备将固件写入SD Card

ffs

将固件写入SD Card

如果命令被命名为”push”即type=”push”,这意味着 命令由目标设备而不是主机解析和执行,主机唯一要做的就是将命令发送到目标设备,通过body进行发 送命令。(固件特定命令已删减,且空余处表示无参数)。如果命令被命名为”push”即type=”push”,这意味着命令由目标设备而不是主机解析和执行,主机唯一要做的就是将命令发送到目标设备,通过body进行发送命令。

2.3.5.2 文件匹配

看上面这条命令:

1
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>

这个文件里的%都是啥?这里其实要用到后面的cfg.ini或者xxx.vbs文件,我们后面再说。

3. xxx.vbs文件

上边确定了处理器以后就要确定向什么存储设备烧写系统,这个时候就要用到 mfgtool2-yocto-mx-evk-emmc.vbs 脚本文件了,此文件内容如下:

1
2
3
Set wshShell = CreateObject("WScript.shell")
wshShell.run "mfgtool2.exe -c ""linux"" -l ""eMMC"" -s ""board=sabresd"" -s ""mmc=1"" -s ""6uluboot=14x14evk"" -s ""6uldtb=14x14-evk"""
Set wshShell = Nothing

第 2 行:就是 “ wshShell.run ” 这一行,这里一行调用了 mfgtool2.exe 这个软件,并且还给出了一系列的参数,其中就有 “ eMMC ” 字样,说明是向 EMMC 烧写系统,要烧写的存储设备就这样确定下来了。“ wshShell.run ”后面还有一堆的其他参数,这些参数都有对应的值,如下所示:

1
2
3
4
board=sabresd
mmc=1
6uluboot=14x14evk
6uldtb=14x14-evk

是不是很眼熟,还记得上面的哪条命令吧:

1
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>

就像这条命令的6uluboot,会被14x14evk取代,所以这里的文件就是firmware/u-boot-imx6ul14x14evk_emmc.imx,即:

image-20241104223009081

也就是说这些参数最终将会被 ucl2.xml 文件所使用,最终用于确定各个文件名。另外这个-l参数,据我猜测是决定LIST标签中的name,这里的name是eMMC,就会对应:

1
2
3
<LIST name="eMMC" desc="Choose eMMC as media">
<!-- 中间部分省略...... -->
</LIST>

4. UICfg.ini文件

1
2
[UICfg]
PortMgrDlg=1

UICfg.ini文件是用来配置同时烧录多少个开发板,即配置其多通道的烧录。我们此处并不量产,默认配置为1。

5. cfg.ini文件

1
L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/cfg.ini
image-20241104074701149

cfg.ini文件主要是用来配置目标芯片类型和板子信息及存储器的方式等内容的,其主要文件内容 包括四部分内容,分别是profiles、platform、list、variable。需要注意的是,这个文件实际是给ucl2.xml文件使用的,用于确认里面%…%之间的值,相当于一个默认的配置文件,它里面的值会被xxx.vbs文件中的命令所取代

  • profiles
1
2
[profiles]
chip = Linux

表示要使用Profiles目录下哪个文件夹的内容进行烧录。如以上配置使用“/profiles/Linux/OS Firmware/ucl2.xml”目录 下的ucl2.xml配置烧录。会被mfgtool2.exe -c ""xxx""覆盖。

  • platform
1
2
[platform]
board = SabreSD

开发板名字,目前没有作用,可以忽略

  • list
1
2
[LIST]
name = SDCard

表示使用“/profiles/CHIP_PROFILE/OS Firmware/ucl2.xml”文件中 的哪个list配置进行烧录,如将name 设置为 SDCard,则使用 ucl2.xml文 件中 SDCard一栏的配置进行烧录。会被mfgtool2.exe -l ""xxx""覆盖。

1
2
3
4
5
6
7
8
<LIST name="SDCard" desc="Choose SD Card as media">
<!-- 向 SD 卡烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>
<LIST name="eMMC" desc="Choose eMMC as media">
<!-- 向 EMMC 烧写 Linux 系统 -->
<!-- 中间部分省略...... -->
</LIST>
  • variable

variable中是一些环境变量,在list列表配置中会引用的环境 变量,会被会被mfgtool2.exe -s ""xxx=variable""覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[variable]
board = sabresd
mmc = 0
sxuboot=sabresd
sxdtb=sdb
7duboot=sabresd
7ddtb=sdb
6uluboot=14x14ddr3arm2
6uldtb=14x14-ddr3-arm2
6ulldtb=14x14-ddr3-arm2
ldo=
plus=
lite=l
initramfs=fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot
seek = 1
sxnor=qspi2
7dnor=qspi1
6ulnor=qspi1
nor_part=0

例如

1
initramfs=fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot

在”/profiles/CHIP_PROFILE/OS Firmware/ucl2.xml”文件中会被引用

1
2
<CMD state="BootStrap" type="load" file="firmware/%initramfs%" address="0x83800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Initramfs.</CMD>

其引用的方式为”%…%”,在两 个百分号(%)之间,通过变量initramfs进行传递。

6. 烧写过程分析

我们接下来实例分析一下点击这个mfgtool2-yocto-mx-evk-emmc.vbs之后会发生什么。

6.1 参数配置

我们看一下这个mfgtool2-yocto-mx-evk-emmc.vbs脚本:

1
2
3
Set wshShell = CreateObject("WScript.shell")
wshShell.run "mfgtool2.exe -c ""linux"" -l ""eMMC"" -s ""board=sabresd"" -s ""mmc=1"" -s ""6uluboot=14x14evk"" -s ""6uldtb=14x14-evk"""
Set wshShell = Nothing

可以得到这些配置项:

1
2
3
4
5
6
board=sabresd
mmc=1
6uluboot=14x14evk
6uldtb=14x14-evk

name=eMMC

我们结合一下cfg.ini,我们会得到传入ucl2.xml 文件的各个参数的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[profiles]
chip = Linux

[platform]
board = SabreSD

[LIST]
name = eMMC

[variable]
board = sabresd
mmc = 1
sxuboot=sabresd
sxdtb=sdb
7duboot=sabresd
7ddtb=sdb
6uluboot=14x14evk
6uldtb=14x14-evk
6ulldtb=14x14-ddr3-arm2
ldo=
plus=
lite=l
initramfs=fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot
seek = 1
sxnor=qspi2
7dnor=qspi1
6ulnor=qspi1
nor_part=0

6.2 CFG

首先进入CFG标签,开始选择器件,这例正点原子alpha开发板使用的是imx6ull,所以这里dev肯定是”MX6ULL”。

1
2
3
4
5
<CFG>
<!-- 其他的省略 -->
<STATE name="BootStrap" dev="MX6ULL" vid="15A2" pid="0080"/>
<STATE name="Updater" dev="MSC" vid="066F" pid="37FF"/>
</CFG>

6.3 LIST

前面根据最终配置参数,我们的name=eMMC,所以会选到这个LIST:

1
2
3
<LIST name="eMMC" desc="Choose eMMC as media">
<!-- 其他的省略 -->
</LIST>

后面我们只需要关注这个标签里的命令即可。

6.4 CMD——BootStrap

6.4.1 BootStrap——uboot

1
2
3
4
5
6
7
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6q%plus%%board%_sd.imx" ifdev="MX6Q">Loading U-boot</CMD>
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6dl%board%_sd.imx" ifdev="MX6D">Loading U-boot</CMD>
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6sx%board%_emmc.imx" ifdev="MX6SX">Loading U-boot</CMD>
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx7d%7duboot%_sd.imx" ifdev="MX7D">Loading U-boot</CMD>
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%6uluboot%_emmc.imx" ifdev="MX6UL">Loading U-boot</CMD>
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%lite%%6uluboot%_emmc.imx" ifdev="MX6ULL">Loading U-boot</CMD>

前面我们知道dev是等于MX6ULL,所以这里只需要关注:

1
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%lite%%6uluboot%_emmc.imx" ifdev="MX6ULL">Loading U-boot</CMD>

%lite%根据前面的配置文件为l,%6uluboot%为14x14evk,所以这里展开就是:

1
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ull14x14evk_emmc.imx" ifdev="MX6ULL">Loading U-boot</CMD>

这个时候就使用的是这个文件:

image-20241104232733823

这个时候,mfgtool就会将这个imx文件下载到DRAM中。

6.4.2 BootStarp——kernel

接下来是内核:

1
2
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x12000000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6Q MX6D">Loading Kernel.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>

不管哪一款芯片,内核镜像的名字都是zimage,只是下载的位置不同,我们这里是MX6ULl,所以是这个:

1
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>

使用的就是这个文件:

image-20241104234003124

6.4.3 BootStrap——Initramfs

1
2
3
4
<CMD state="BootStrap" type="load" file="firmware/%initramfs%" address="0x12C00000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6Q MX6D">Loading Initramfs.</CMD>
<CMD state="BootStrap" type="load" file="firmware/%initramfs%" address="0x83800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Initramfs.</CMD>

这个文件嘛,我也不知道是干啥的,好像是可以删掉,我在正点原子的mfgtool中没看到这个命令,但是这里也分析一下吧。根据前面的配置文件,这里的%initramfs%是:

1
initramfs=fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot

所以这里展开就是:

1
<CMD state="BootStrap" type="load" file="firmware/fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot" address="0x83800000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Initramfs.</CMD>

就是这个文件:

image-20241104234352843

6.4.4 BootStrap——device tree

然后就是设备树啦,我们来看一下 :

1
2
3
4
5
6
7
8
9
10
11
12
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6q%plus%-%board%%ldo%.dtb" address="0x18000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6Q">Loading device tree.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6dl-%board%%ldo%.dtb" address="0x18000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6D">Loading device tree.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6sx-%sxdtb%-emmc.dtb" address="0x83000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SX">Loading device tree.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage-imx7d-%7ddtb%.dtb" address="0x83000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX7D">Loading device tree.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6ul-%6uldtb%-emmc.dtb" address="0x83000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6UL">Loading device tree.</CMD>
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6ul%lite%-%6uldtb%-emmc.dtb" address="0x83000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6ULL">Loading device tree.</CMD>

我们直接看这个MX6ULL:

1
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6ul%lite%-%6uldtb%-emmc.dtb" address="0x83000000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6ULL">Loading device tree.</CMD>

根据前面的配置文件,我们得到%lite%=l,%6uldtb%=14x14-evk,所以这里命令展开就是:

1
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6ull-14x14-evk-emmc.dtb" address="0x83000000" loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6ULL">Loading device tree.</CMD>

就是这个文件:

image-20241104234816747

6.4.5 BootStrap——jump

这里就来到了第一阶段的结尾,则是Jumping to OS image,应该就是去启动这个已经下载到DRAM的Linux系统,从而进入到系统烧写的第二个阶段,在看第二阶段的代码前,先总结一下第一阶段烧写了哪些固件,基于i.mx6ul这款SoC,下载到DRAM的固件如下:

固件 下载文件
uboot firmware/u-boot-imx6ull14x14evk_emmc.imx
kernel firmware/zImage
initramfs firmware/fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot
dtb firmware/zImage-imx6ull-14x14-evk-emmc.dtb

这里结束,我们其实已经在开发板上运行起来了一个linux系统,后续第二阶段就是通过这个linux系统将相关的uboot、kernel、dtb、rootfs等烧写到eMMC中去。

6.5 CMD——Updater

6.5.1 Updater——create partition

一开始先创建分区:

1
2
3
<CMD state="Updater" type="push" body="send" file="mksdcard.sh.tar">Sending partition shell</CMD>
<CMD state="Updater" type="push" body="$ tar xf $FILE "> Partitioning...</CMD>
<CMD state="Updater" type="push" body="$ sh mksdcard.sh /dev/mmcblk%mmc%"> Partitioning...</CMD>

我们看一下这个mksdcard.sh.tar文件里面是什么:

image-20241105000348103

我们打开这个脚本看一眼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh

# partition size in MB
BOOT_ROM_SIZE=10

# wait for the SD/MMC device node ready
while [ ! -e $1 ]
do
sleep 1
echo “wait for $1 appear”
done

# call sfdisk to create partition table
# destroy the partition table
node=$1
dd if=/dev/zero of=${node} bs=1024 count=1

sfdisk --force ${node} << EOF
${BOOT_ROM_SIZE}M,500M,0c
600M,,83
EOF

我们再来看一下%mmc%,根据前面的配置,这个值应该是1,所以最后一个命令就是:

1
<CMD state="Updater" type="push" body="$ sh mksdcard.sh /dev/mmcblk1"> Partitioning...</CMD>

可以看到是通过mksdcard.sh使用sfdisk工具对/dev/mmcblk1进行分区。脚本的命令解析一下就是:

1
2
3
4
5
6
7
8
9
node=/dev/mmcblk1
BOOT_ROM_SIZE=10
# 所以有
dd if=/dev/zero of=/dev/mmcblk1 bs=1024 count=1

sfdisk --force /dev/mmcblk1 << EOF
10M,500M,0c
600M,,83
EOF

6.5.2 Updater——burn uboot

我们来看一下这几条CMD.

  • clear u-boot arg
1
2
<!-- burn uboot -->
<CMD state="Updater" type="push" body="$ dd if=/dev/zero of=/dev/mmcblk%mmc% bs=1k seek=768 conv=fsync count=8">clear u-boot arg</CMD>

首先是清空要烧写uboot的区域,我们前面知道%mmc%=1,所以这里就是:

1
<CMD state="Updater" type="push" body="$ dd if=/dev/zero of=/dev/mmcblk1 bs=1k seek=768 conv=fsync count=8">clear u-boot arg</CMD>
  • access boot partition and send
1
2
3
4
5
6
<CMD state="Updater" type="push" body="$ echo 0 > /sys/block/mmcblk%mmc%boot0/force_ro">access boot partition 1</CMD>
<!-- 中间省略 -->
<CMD state="Updater" type="push" body="send" file="files/u-boot-imx6ul%lite%%6uluboot%_emmc.imx" ifdev="MX6ULL">Sending u-boot.bin</CMD>
<CMD state="Updater" type="push" body="$ dd if=$FILE of=/dev/mmcblk%mmc%boot0 bs=512 seek=2">write U-Boot to sd card</CMD>
<CMD state="Updater" type="push" body="$ echo 1 > /sys/block/mmcblk%mmc%boot0/force_ro"> re-enable read-only access </CMD>
<CMD state="Updater" type="push" body="$ mmc bootpart enable 1 1 /dev/mmcblk%mmc%">enable boot partion 1 to boot</CMD>

这里这几个变量是:

1
2
3
%mmc%=1
%lite%=l
%6uluboot%=14x14evk

变量的值替换后就是:

1
2
3
4
5
6
<CMD state="Updater" type="push" body="$ echo 0 > /sys/block/mmcblk1boot0/force_ro">access boot partition 1</CMD>
<!-- 中间省略 -->
<CMD state="Updater" type="push" body="send" file="files/u-boot-imx6ull14x14evk_emmc.imx" ifdev="MX6ULL">Sending u-boot.bin</CMD>
<CMD state="Updater" type="push" body="$ dd if=$FILE of=/dev/mmcblk1boot0 bs=512 seek=2">write U-Boot to sd card</CMD>
<CMD state="Updater" type="push" body="$ echo 1 > /sys/block/mmcblk1boot0/force_ro"> re-enable read-only access </CMD>
<CMD state="Updater" type="push" body="$ mmc bootpart enable 1 1 /dev/mmcblk1">enable boot partion 1 to boot</CMD>

这里的过程是先发送,再写入,发送的时候是这个目录的文件:

image-20241105065317996

这里要先了解一些eMMC的分区,在eMMC器件里有两种存储器,SRAM(静态随机访问存储器)和NAND闪存。SRAM是位于控制器ASIC里面为了数据暂时保存以及被控制器微处理器快速访问的易失性存储器。NAND阵列是由一个或者多个晶片组成的非易失性存储器,大概的框图如下:

eMMC深入浅出 第二章 eMMC结构 第二节 分区以及存储区域

mmcblk1boot0其实就代表eMMC的Boot Area Partitions(Boot Area包含两个Boot Area Partitions),一般是拿来存储Bootloader,支持SOC从eMMC启动系统。详细的就不再说了,以后学习的时候再详细了解。

6.5.3 Updater——create fat partition

创建一个fat分区:

1
2
3
4
<CMD state="Updater" type="push" body="$ while [ ! -e /dev/mmcblk%mmc%p1 ]; do sleep 1; echo \"waiting...\"; done ">Waiting for the partition ready</CMD>
<CMD state="Updater" type="push" body="$ mkfs.vfat /dev/mmcblk%mmc%p1">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk%mmc%p1"/>
<CMD state="Updater" type="push" body="$ mount -t vfat /dev/mmcblk%mmc%p1 /mnt/mmcblk%mmc%p1"/>

这里吧%mmc%=1替换一下就是:

1
2
3
4
<CMD state="Updater" type="push" body="$ while [ ! -e /dev/mmcblk1p1 ]; do sleep 1; echo \"waiting...\"; done ">Waiting for the partition ready</CMD>
<CMD state="Updater" type="push" body="$ mkfs.vfat /dev/mmcblk1p1">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk1p1"/>
<CMD state="Updater" type="push" body="$ mount -t vfat /dev/mmcblk1p1 /mnt/mmcblk1p1"/>

这里先格式化分区,然后用mount命令挂载。

6.5.4 Updater——burn zImage

1
2
3
<!-- burn zImage -->
<CMD state="Updater" type="push" body="send" file="files/zImage">Sending kernel zImage</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/zImage">write kernel image to sd card</CMD>

这里就是烧写zImage文件,这里替换一下变量值就是:

1
2
3
<!-- burn zImage -->
<CMD state="Updater" type="push" body="send" file="files/zImage">Sending kernel zImage</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk1p1/zImage">write kernel image to sd card</CMD>

就是把zimage烧写到mmcblk1p1这个分区中。这里会先发送然后再拷贝,使用的文件是这个:

image-20241105065429877

6.5.5 Updater——burn dtb

接下来是烧写设备树文件,这里只看MX6ULL的两行命令

1
2
3
<CMD state="Updater" type="push" body="send" file="files/zImage-imx6ul%lite%-%6uldtb%-emmc.dtb" ifdev="MX6ULL">Sending Device Tree file</CMD>

<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/imx6ul%lite%-%6uldtb%.dtb" ifdev="MX6ULL">write device tree to sd card</CMD>

变量值为:

1
2
3
4
mmc = 1
6uluboot=14x14evk
6uldtb=14x14-evk
6ulldtb=14x14-ddr3-arm2

替换后如下所示:

1
2
3
<CMD state="Updater" type="push" body="send" file="files/zImage-imx6ull-14x14-evk-emmc.dtb" ifdev="MX6ULL">Sending Device Tree file</CMD>

<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk1p1/imx6ull-14x14-evk.dtb" ifdev="MX6ULL">write device tree to sd card</CMD>

这里有点奇怪,就是执行cp命令复制过程中把zImage-imx6ull-14x14-evk-emmc.dtb后面的-emmc去掉了,可能是内核中设备树名字的问题,反正这里知道是复制设备树到对应文件中就是了。

6.5.6 Updater——burn m4 demo bins

这个看起来是烧写一些demo文件:

1
2
3
4
5
6
7
8
9
<!-- burn m4 demo bins-->
<CMD state="Updater" type="push" body="send" file="files/imx7d_sabresd_m4_TCM_helloworld.bin" ifdev="MX7D">Sending helloworld demo</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/imx7d_sabresd_m4_TCM_helloworld.bin" ifdev="MX7D">write demo image to eMMC</CMD>
<CMD state="Updater" type="push" body="send" file="files/imx7d_sabresd_m4_TCM_mcctty.bin" ifdev="MX7D">Sending mcctty demo</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/imx7d_sabresd_m4_TCM_mcctty.bin" ifdev="MX7D">write demo image to eMMC</CMD>
<CMD state="Updater" type="push" body="send" file="files/imx7d_sabresd_m4_TCM_Pingpang.bin" ifdev="MX7D">Sending pingpong demo</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/imx7d_sabresd_m4_TCM_Pingpang.bin" ifdev="MX7D">write demo image to eMMC</CMD>

<CMD state="Updater" type="push" body="$ umount /mnt/mmcblk%mmc%p1">Unmounting vfat partition</CMD>

不过其实这里是MX7D才有,MX6ULL没有,这里就不用管了。这里烧写完后面把mmcblk1p1卸载。

6.5.7 Updater——burn rootfs

接下来是烧写根文件系统:

1
2
3
4
5
6
7
8
<CMD state="Updater" type="push" body="$ mkfs.ext3 -F -E nodiscard /dev/mmcblk%mmc%p2">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk%mmc%p2"/>
<CMD state="Updater" type="push" body="$ mount -t ext3 /dev/mmcblk%mmc%p2 /mnt/mmcblk%mmc%p2"/>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk%mmc%p2" file="files/rootfs.tar.bz2" ifdev="MX6SL MX6D MX6Q MX6SX">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk%mmc%p2" file="files/rootfs_nogpu.tar.bz2" ifdev="MX6UL MX7D MX6ULL">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="frf">Finishing rootfs write</CMD>
<CMD state="Updater" type="push" body="$ umount /mnt/mmcblk%mmc%p2">Unmounting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ echo Update Complete!">Done</CMD>

我们根据配置文件对变量进行替换:

1
2
3
4
5
6
7
8
<CMD state="Updater" type="push" body="$ mkfs.ext3 -F -E nodiscard /dev/mmcblk1p2">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk1p2"/>
<CMD state="Updater" type="push" body="$ mount -t ext3 /dev/mmcblk1p2 /mnt/mmcblk1p2"/>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk1p2" file="files/rootfs.tar.bz2" ifdev="MX6SL MX6D MX6Q MX6SX">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk1p2" file="files/rootfs_nogpu.tar.bz2" ifdev="MX6UL MX7D MX6ULL">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="frf">Finishing rootfs write</CMD>
<CMD state="Updater" type="push" body="$ umount /mnt/mmcblk1p2">Unmounting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ echo Update Complete!">Done</CMD>

一开始也是格式化了一个分区/dev/mmcblk1p2,然后对其进行挂载:

1
2
3
<CMD state="Updater" type="push" body="$ mkfs.ext3 -F -E nodiscard /dev/mmcblk1p2">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk1p2"/>
<CMD state="Updater" type="push" body="$ mount -t ext3 /dev/mmcblk1p2 /mnt/mmcblk1p2"/>

接下来是发送和写入根文件系统:

1
2
3
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk1p2" file="files/rootfs.tar.bz2" ifdev="MX6SL MX6D MX6Q MX6SX">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk1p2" file="files/rootfs_nogpu.tar.bz2" ifdev="MX6UL MX7D MX6ULL">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="frf">Finishing rootfs write</CMD>

这里这个pipe tar命令有点没看懂,网上搜了下也没见这么用的,我猜测是pipe是管道,这个命令直接把根文件系统解压后写入mmcblk1p2这个分区,这里我们使用的是MX6ULL,所以这里根文件系统是ootfs_nogpu.tar.bz2:

image-20241105071304417

这个根文件系统包不包含rootfs这一级目录?解压后直接是根文件系统?我们解压看一下,为防止一堆文件出现,我们先创建一个目录:

1
2
3
4
5
cd /d/devSoftware/L4.1.15_2.0.0-ga_mfg-tools/mfgtools-with-rootfs/mfgtools/Profiles/Linux/OS\ Firmware/files
mkdir temp
cp rootfs_nogpu.tar.bz2 temp/
cd temp
tar xf rootfs_nogpu.tar.bz2

这里需要注意,由于是linux根文件系统,这里会存储在大量的软链接,而windows下是没有软链接的概念的,所以这些文件解压时会报错,不用管,我们只是看看顶层目录长啥样而已。然后我们看一下这个目录:

image-20241105071750420

会发现直接就是根文件系统了,所以我么打包的时候也要注意,要保证解压出来的文件不在压缩包名的子目录中。到这里,烧写的第二阶段就结束了,之后就可以将拨码开关拨到eMMC启动,就可以启动系统了。

6.5.8 Updater——总结

第二阶段烧写了哪些文件?

固件 下载文件
uboot files/u-boot-imx6ull14x14evk_emmc.imx
kernel files/zImage
dtb files/zImage-imx6ull-14x14-evk-emmc.dtb
rootfs files/rootfs_nogpu.tar.bz2

三、NXP官方系统烧写

1. 系统烧写到EMMC

我们先来体验一下将NXP官方的镜像烧写到正点原子的开发板中,简单了解一下烧写过程。正点原子的 EMMC 核心版用的也是 512MB 的 DDR3 加 8G 的 EMMC,因此烧写 NXP 官方的系统是没有任何问题的。基本步骤如下:

  • (1)连接好 USB,拨码开关拨到 USB 下载模式。

  • (2)弹出 TF 卡,然后按下开发板复位按键,后续将系统烧写到EMMC中去,所以不需要 TF 卡。

  • (3)打开 MobaXterm,连接开发板,注意,此时需要两根USB线,一个连接在USB OTG,用于烧写程序,另一个连接在串口,用于显示开发板串口的打印信息。

image-20230714181213952
  • (4)双击“mfgtool2-yocto-mx-evk-emmc.vbs”,打开下载软件,如果出现【符合 HID 标准的供应商定义设备】等字样就说明下载软件已经准备就绪。
image-20230714181753981
  • (5)点击【Start】按钮开发烧写 NXP 官方系统,在uboot烧写完成并启动后,串口就会有打印信息输出。烧写过程如图所示:
image-20230714181855110

然后我们可以在 MobaXterm 中看到当前的烧写过程,过程很漫长(主要是烧写根文件系统有大量的文件要拷贝,就很慢),这里就截取了一部分。

image-20230714182112631
  • (6)烧写完成状态如下图所示:
image-20221023145536704
  • (7)然后我们点击【Stop】,最后点击【Exit】退出即可。

2. 重启测试

上边烧写完毕后,我们拔出 USB 线,将开发板上的拨码开关拨到 EMMC 启动模式(1010 0110),然后重启开发板,此时就会从 EMMC 启动。只是启动以后的系统是 NXP 官方给 I.MX6ULL EVK 开发板制作的,这个系统需要输入用户名,用户名为“root”,没有密码,如下图:

image-20230714183618441

在“imx6ul7d login:”后面输入“root”用户名,然后点击回车键即可进入系统中,进入系统以后就可以进行其他操作了。由此可以看出,NXP 官方的系统其实是可以在正点原子的 EMMC 版核心板上运行的 。 正常来讲,若是有显示屏,则也会有一些显示信息,但是可能不清楚,并且位置也不是很对:

image-20230714183832269

四、改造自己的烧写工具?

上边的目录里边有很多我们不需要的文件,看着挺烦人的,我们最少需要哪些文件?这一部分我们以官方的系统为例进行一个学习。我们这里就以EMMC为例进行学习说明,SD卡和NAND也是一样的步骤。

1. 准备工具目录

我们复制一个 mfgtools-with-rootfs 目录,然后重命名:

image-20230715090438259

接着我们删除其中的 mfgtools-with-rootfs-mine/mfgtools/*.vbs 文件,再删除 mfgtools-with-rootfs-mine/mfgtools/Profiles/Linux/OS Firmware目录中的 files 目录、firmware 目录和ulc2.xml文件,这些删除的东西我们后边都会重新创建并修改,最终创建一个自己的mfgtool。

2. 文件准备

2.1 files目录文件

我们的files目录需要那些文件?其实我们只需要四个文件:u-boot、linux内核镜像、dtb设备树和rootfs根文件系统,我们使用的是EMMC的板子,所以我们可以从官方的 mfgtool 中的OS Firmware目录中找到这些文件,我们直接复制到自己的目录中:

image-20230715095029133

2.2 firmware目录文件

firmware目录文件需要u-boot、linux内核和dtb,在这个里边不需要根文件系统,firmware目录中的文件主要是完成烧写的第一个过程。我们也可以从mfgtool 中的OS Firmware目录中找到这些文件,我们直接复制到自己的目录中:

image-20230715095123022

firmware 目录下的 fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot 文件(后边好像用到了,所以这里就一起拷贝一下啦,但是做什么用没有深究)。

2.3 新建 .vbs 文件

vbs文件用于调用mfgtool.exe烧写工具,并传入相关参数的,我们需要重新自己新建一个自己的,我们直接复制 mfgtool2-yocto-mx-evk-emmc.vbs 文件到 mfgtools-with-rootfs-mine/mfgtools目录即可(当然也可以命名为其他),文件内容不要做任何修改, .vbs 文件我们就新建好了。文件内容如下:

1
2
3
Set wshShell = CreateObject("WScript.shell")
wshShell.run "mfgtool2.exe -c ""linux"" -l ""eMMC"" -s ""board=sabresd"" -s ""mmc=1"" -s ""6uluboot=14x14evk"" -s ""6uldtb=14x14-evk"""
Set wshShell = Nothing

在此脚本下,后边我们在ucl2.xml文件中遇到%6uluboot%就会被替换为14x14evk,遇到 %6uldtb%会被替换为14x14-evk,但是后边在这里自定义的可以直接写死,在实际开发需要支持多种芯片的时候可以采用此种方法传递参数,然后我们可以在 ucl2.xml 中通过 %variable_name%来引用。

【注意】这里的其实是修改了 mfgtools-with-rootfs-mine/mfgtools/cfg.ini中的参数,cfg.ini文件中是默认的配置,要修改的话就在脚本中修改,重新传入即可。

2.4 新建 ucl2.xml 文件

ucl2.xml文件位于mfgtools-with-rootfs-mine/mfgtools/Profiles/Linux/OS Firmware目录下,它决定了mfgtool的烧写的详细过程以及烧写的文件。我们新建一个ucl2.xml文件(也可以复制原来的进行修改):

点击查看 ulc2.xml文件内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<!--
* Copyright (C) 2012, Freescale Semiconductor, Inc. All Rights Reserved.
* The CFG element contains a list of recognized usb devices.
* DEV elements provide a name, class, vid and pid for each device.
*
* Each LIST element contains a list of update instructions.
* "Install" - Erase media and install firmware.
* "Update" - Update firmware only.
*
* Each CMD element contains one update instruction of attribute type.
* "pull" - Does UtpRead(body, file) transaction.
* "push" - Does UtpWrite(body, file) transaction.
* "drop" - Does UtpCommand(body) then waits for device to disconnect.
* "boot" - Finds configured device, forces it to "body" device and downloads "file".
* "find" - Waits for "timeout" seconds for the "body" device to connect.
* "show" - Parse and show device info in "file".
-->


<UCL>
<CFG>
<STATE name="BootStrap" dev="MX6ULL" vid="15A2" pid="0080"/>
<STATE name="Updater" dev="MSC" vid="066F" pid="37FF"/>
</CFG>

<LIST name="eMMC" desc="Choose eMMC as media">
<!-- firmware/u-boot-imx6ull14x14evk_emmc.imx (lite="l" 6uluboot="14x14evk") -->
<CMD state="BootStrap" type="boot" body="BootStrap" file ="firmware/u-boot-imx6ul%lite%%6uluboot%_emmc.imx" ifdev="MX6ULL">Loading U-boot</CMD>
<!-- firmware/zImage -->
<CMD state="BootStrap" type="load" file="firmware/zImage" address="0x80800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Kernel.</CMD>
<!-- firmware/fsl-image-mfgtool-initramfs-imx_mfgtools.cpio.gz.u-boot -->
<CMD state="BootStrap" type="load" file="firmware/%initramfs%" address="0x83800000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6SL MX6SX MX7D MX6UL MX6ULL">Loading Initramfs.</CMD>
<!-- firmware/zImage-imx6ull-14x14evk-emmc.dtb (lite="l" 6uldtb="14x14evk") -->
<CMD state="BootStrap" type="load" file="firmware/zImage-imx6ul%lite%-%6uldtb%-emmc.dtb" address="0x83000000"
loadSection="OTH" setSection="OTH" HasFlashHeader="FALSE" ifdev="MX6ULL">Loading device tree.</CMD>

<CMD state="BootStrap" type="jump" > Jumping to OS image. </CMD>

<!-- create partition -->
<CMD state="Updater" type="push" body="send" file="mksdcard.sh.tar">Sending partition shell</CMD>
<CMD state="Updater" type="push" body="$ tar xf $FILE "> Partitioning...</CMD>
<CMD state="Updater" type="push" body="$ sh mksdcard.sh /dev/mmcblk%mmc%"> Partitioning...</CMD>

<!-- burn uboot -->
<CMD state="Updater" type="push" body="$ dd if=/dev/zero of=/dev/mmcblk%mmc% bs=1k seek=768 conv=fsync count=8">clear u-boot arg</CMD>
<!-- access boot partition -->
<!-- files/u-boot-imx6ull14x14evk_emmc.imx (lite="l" 6uluboot="14x14evk") -->
<CMD state="Updater" type="push" body="$ echo 0 > /sys/block/mmcblk%mmc%boot0/force_ro">access boot partition 1</CMD><!-- 取消分区只读保护 -->
<CMD state="Updater" type="push" body="send" file="files/u-boot-imx6ul%lite%%6uluboot%_emmc.imx" ifdev="MX6ULL">Sending u-boot.bin</CMD>
<CMD state="Updater" type="push" body="$ dd if=$FILE of=/dev/mmcblk%mmc%boot0 bs=512 seek=2">write U-Boot to eMMC</CMD>
<CMD state="Updater" type="push" body="$ echo 1 > /sys/block/mmcblk%mmc%boot0/force_ro"> re-enable read-only access </CMD><!-- 开启分区只读保护 -->
<CMD state="Updater" type="push" body="$ mmc bootpart enable 1 1 /dev/mmcblk%mmc%">enable boot partion 1 to boot</CMD>

<!-- create fat partition -->
<CMD state="Updater" type="push" body="$ while [ ! -e /dev/mmcblk%mmc%p1 ]; do sleep 1; echo \"waiting...\"; done ">Waiting for the partition ready</CMD>
<CMD state="Updater" type="push" body="$ mkfs.vfat /dev/mmcblk%mmc%p1">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk%mmc%p1"/>
<CMD state="Updater" type="push" body="$ mount -t vfat /dev/mmcblk%mmc%p1 /mnt/mmcblk%mmc%p1"/>

<!-- burn zImage -->
<!-- files/zImage -->
<CMD state="Updater" type="push" body="send" file="files/zImage">Sending kernel zImage</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/zImage">write kernel image to eMMC</CMD>

<!-- burn dtb -->
<!-- files/zImage-imx6ull-14x14evk-emmc.dtb (lite="l" 6uldtb="14x14-evk") -->
<CMD state="Updater" type="push" body="send" file="files/zImage-imx6ul%lite%-%6uldtb%-emmc.dtb" ifdev="MX6ULL">Sending Device Tree file</CMD>
<CMD state="Updater" type="push" body="$ cp $FILE /mnt/mmcblk%mmc%p1/imx6ul%lite%-%6uldtb%.dtb" ifdev="MX6ULL">write device tree to eMMC</CMD>

<CMD state="Updater" type="push" body="$ sleep 1">delay</CMD>
<CMD state="Updater" type="push" body="$ sync">Sync...</CMD>
<CMD state="Updater" type="push" body="$ umount /mnt/mmcblk%mmc%p1">Unmounting vfat partition</CMD>

<!-- burn rootfs -->
<CMD state="Updater" type="push" body="$ mkfs.ext3 -F -E nodiscard /dev/mmcblk%mmc%p2">Formatting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ mkdir -p /mnt/mmcblk%mmc%p2"/>
<CMD state="Updater" type="push" body="$ mount -t ext3 /dev/mmcblk%mmc%p2 /mnt/mmcblk%mmc%p2"/>
<CMD state="Updater" type="push" body="pipe tar -jxv -C /mnt/mmcblk%mmc%p2" file="files/rootfs_nogpu.tar.bz2" ifdev="MX6UL MX7D MX6ULL">Sending and writting rootfs</CMD>
<CMD state="Updater" type="push" body="frf">Finishing rootfs write</CMD>
<CMD state="Updater" type="push" body="$ umount /mnt/mmcblk%mmc%p2">Unmounting rootfs partition</CMD>
<CMD state="Updater" type="push" body="$ echo Update Complete!">Done</CMD>
</LIST>
</UCL>

【注意】上边的文件中,%mmc%将会被替换为1,lite=”l”(小写的L), 6uldtb=”14x14-evk”,6uluboot=”14x14evk”。

3. 烧写测试

这里就按照之前烧写官方系统的时候的方式就可以了,直接双击 mfgtool2-yocto-mx-evk-emmc.vbs 文件,开发板确保已经连接到电脑,便会打开mfgtool:

image-20221023154920830

然后我们点击【Start 】等待烧写完毕。

4. 重启测试

上边烧写完毕后,我们拔出 USB 线,将开发板上的拨码开关拨到 EMMC 启动模式,然后重启开发板,此时就会从 EMMC 启动。若是烧写成功的话,就会看到与上边NXP官方系统烧写一节的笔记中一样的情况:

image-20230715102654342

能到这一步,说明我们改造的mfgtool可以正常使用。

参考资料

技术分享-NXP MFGTool软件烧录原理 - 飞凌嵌入式行业资讯 - 保定飞凌嵌入式技术有限公司 (forlinx.com)

doc.embedfire.com/linux/imx6/base/zh/latest/building_image/burning_NXP_firmware.html