当前位置: 首页 > news >正文

U-Boot移植过程中的关键目录文件解析

本文以NXP的i.mx6ul为例说明

默认的defconfig

uboot 的移植并不是说我们完完全全的从零开始将 uboot 移植到我们现在所使用的开发板或者开发平台上。这个对于我们来说基本是不可能的,这个工作一般是半导体厂商做的,半导体厂商负责将 uboot 移植到他们的芯片上,因此半导体厂商都会自己做一个开发板,这个开发板就叫做原厂开发板,比如大家学习 STM32的时候听说过的discover 开发板就是ST自己做的。

半导体厂商会将 uboot 移植到他们自己的原厂开发板上,测试好以后就会将这个 uboot 发布出去,这就是大家常说的原厂 BSP 包。我们一般做产品的时候就会参考原厂的开发板做硬件,然后在原厂提供的 BSP 包上做修改,将 uboot 或者 linux kernel 移植到我们的硬件上。这个就是uboot 移植的一般流程:

①、在 uboot 中找到参考的开发平台,一般是原厂的开发板。

②、参考原厂开发板移植 uboot 到我们所使用的开发板上。

正点原子的 I.MX6ULL 开发板参考的是 NXP 官方的 I.MX6ULL EVK 开发板做的硬件,因此我们在移植 uboot 的时候就可以以 NXP 官方的 I.MX6ULL EVK 开发板为蓝本。

本章我们是将 NXP 官方的 uboot 移植到正点原子的 I.MX6ULL 开发板上,NXP 官方的uboot 放到了开发板光盘中,路径为:1、例程源码->4、NXP 官方原版 Uboot 和 Linux->ubootimx-rel_imx_4.1.15_2.1.0_ga.tar.bz2。将 uboot-imx-rel_imx_4.1.15_2.1.0_ga.tar.bz2 发送到 Ubuntu中并解压,然后创建 VSCode 工程。

在移植之前,我们先编译一下 NXP 官方 I.MX6ULL EVK 开发板对应的 uboot,首先是配置uboot,configs 目录下有很多跟 I.MX6UL/6ULL 有关的配置如图 33.1.1.1 所示,

从图 33.1.1.1 可以看出有很多的默认配置文件,其中以 mx6ul 开头的是 I.MX6UL 芯片的,mx6ull 开头的是 I.MX6ULL 开发板的。I.MX6UL/6ULL 有 9x9mm 和 14x14mm 两种尺寸的,所以我们可以看到会有mx6ull_9x9和mx6ull_14x14开头的默认配置文件。我们使用的是14x14mm的芯片,所以关注 mx6ull_14x14 开头的默认配置文件。正点原子的 I.MX6ULL 有 EMMC 和NAND 两个版本的,因此我们最终只需要关注 mx6ull_14x14_evk_emmc_defconfig 和mx6ull_14x14_evk_nand_defconfig 这两个配置文件就行了。本章我们讲解 EMMC 版本的移植(NAND 版本移植很多类似),所以使用 mx6ull_14x14_evk_emmc_defconfig 作为默认配置文件。

mx6ull_14x14_evk_defconfig和

mx6ull_14x14_evk_emmc_defconfig和

mx6ull_14x14_evk_nand_defconfig和

mx6ull_14x14_ddr3_arm2_defconfig这些都有啥区别?

这些都是 NXP i.MX6ULL 14x14 系列处理器的 Linux 内核配置文件(defconfig),主要区别在于针对的硬件存储介质、内存类型和板卡版本的适配,具体差异如下:

1. mx6ull_14x14_evk_defconfig

  • 定位:基础通用配置,适用于 i.MX6ULL 14x14 评估板(EVK,Evaluation Kit)的默认通用场景
  • 核心特点:包含 i.MX6ULL 芯片的核心功能支持(如 CPU、基础外设、接口等),但不绑定特定存储介质(不专门针对 eMMC、NAND 或 SD 卡优化)。
  • 适用场景:作为基础模板,或用于硬件配置简单、存储介质不固定的自定义板卡,需根据实际存储类型手动补充配置。

2. mx6ull_14x14_evk_emmc_defconfig

  • 定位:在基础版 evk_defconfig 之上,专门针对带 eMMC 存储的评估板优化。
  • 核心差异
    • 启用 eMMC 控制器驱动、分区管理、总线协议(如 MMC/SD 协议)等功能。
    • 适配板载 eMMC 闪存的硬件接口和时序,确保系统能直接从 eMMC 启动和读写。
  • 适用场景:NXP 官方带 eMMC 存储的 i.MX6ULL 14x14 评估板,或硬件上集成 eMMC 的自定义板卡。

3. mx6ull_14x14_evk_nand_defconfig

  • 定位:针对带 NAND 闪存的评估板优化的配置。
  • 核心差异
    • 启用 NAND 控制器驱动、坏块管理、ECC 校验(用于数据纠错)等 NAND 特有的功能。
    • 适配 NAND 闪存的接口时序和存储协议(与 eMMC 完全不同)。
  • 适用场景:板载 NAND 闪存的 i.MX6ULL 14x14 评估板或自定义板卡(NAND 通常用于大容量、低成本存储场景)。

4. mx6ull_14x14_ddr3_arm2_defconfig

  • 定位:针对特定硬件版本和内存类型的配置。
  • 核心差异
    • 内存适配:明确启用 DDR3 内存控制器驱动和时序参数(区别于其他内存类型如 LPDDR2)。
    • 硬件版本arm2 通常指板卡的硬件版本号(如 PCB 版本、外设布局差异),可能对应特定的 CPU 核心配置、扩展接口或外设支持(需结合具体硬件文档确认)。
  • 适用场景:使用 DDR3 内存且硬件版本为 arm2 的 i.MX6ULL 14x14 定制板(非官方通用评估板,更偏向特定硬件设计)。

总结:核心区别维度

配置文件核心差异点典型适用硬件
mx6ull_14x14_evk_defconfig通用基础配置,无特定存储绑定通用评估板或存储介质不固定的场景
mx6ull_14x14_evk_emmc_defconfig适配 eMMC 存储带 eMMC 闪存的评估板 / 自定义板
mx6ull_14x14_evk_nand_defconfig适配 NAND 闪存带 NAND 闪存的评估板 / 自定义板
mx6ull_14x14_ddr3_arm2_defconfig适配 DDR3 内存 + arm2 版本特定硬件版本(arm2)+ DDR3 内存的板卡

选择时需根据板卡的存储介质类型(eMMC/NAND/ 其他)、内存类型(DDR3 / 其他)和硬件版本匹配,确保内核能正确驱动硬件。

看下mx6ull_14x14_evk_emmc_defconfig文件里的内容:

你提供的这段代码是 U-Boot 引导程序的配置项(通常位于 .config 文件或 defconfig 文件中),用于指定 i.MX6ULL 平台的硬件和功能配置。以下是各配置项的含义解析:

  • CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=..."

    • 定义额外的系统配置选项,这里指定了两个关键参数:
      • IMX_CONFIG=board/freescale/mx6ullevk/imximage.cfg:指定 IMX 芯片的镜像配置文件路径,用于生成符合 i.MX6ULL 启动要求的镜像格式(如设置启动地址、分区表等)。
      • MX6ULL_EVK_EMMC_REWORK:表示针对带 eMMC 存储的 MX6ULL EVK 评估板的硬件修订版本(可能涉及硬件接口调整),启用对应的驱动适配。
  • CONFIG_ARM=y

    • 启用 ARM 架构支持,表明该 U-Boot 针对 ARM 处理器编译。
  • CONFIG_ARCH_MX6=y

    • 指定针对 NXP 的 i.MX6 系列处理器(包括 i.MX6ULL)进行架构级适配,启用该系列芯片的核心驱动和功能。
  • CONFIG_TARGET_MX6ULL_14X14_EVK=y

    • 明确目标硬件为 MX6ULL 14x14 评估板(EVK),U-Boot 会加载该板卡的特定配置(如引脚定义、外设布局等)。
  • CONFIG_CMD_GPIO=y

    • 启用 U-Boot 命令行中的 GPIO 相关命令(如 gpio setgpio get 等),允许在 U-Boot 阶段通过命令操作 GPIO 引脚。

这些配置共同决定了 U-Boot 对 MX6ULL 14x14 EVK 评估板(尤其是带 eMMC 存储的版本)的硬件适配,确保引导程序能正确初始化芯片和外设,为后续加载 Linux 内核做准备。

这里面看起来没有具体的配置项,具体的配置项在哪里?

你提到的这些配置项(如 CONFIG_TARGET_MX6ULL_14X14_EVK 等)是 U-Boot 配置的顶层开关,它们的作用是 “启用” 对应的硬件适配逻辑,而具体的硬件细节配置(如引脚定义、外设参数等)则分散在 U-Boot 源码的对应目录中

具体来说,这些配置项对应的详细内容通常存放在以下位置:

1. 板级配置文件(核心硬件细节)

路径:board/freescale/mx6ull_14x14_evk/(或类似路径,根据具体 U-Boot 版本可能略有差异)
这里是该评估板(EVK)的专属配置目录,包含:

  • mx6ull_14x14_evk.c:核心板级初始化代码,定义了 GPIO 引脚复用、外设(如网口、eMMC、NAND)的硬件参数、时钟配置等。
  • imximage.cfg:对应 CONFIG_SYS_EXTRA_OPTIONS 中指定的镜像配置文件,定义了启动镜像的加载地址、分区布局等。
  • Kconfig/Makefile:板级配置的编译选项,决定哪些功能会被编译进 U-Boot。

2. 芯片级配置文件(架构共性配置)

路径:arch/arm/include/asm/arch-mx6/ 或 drivers/

  • 芯片级寄存器定义、通用外设(如 I2C、SPI、USB)的驱动框架,由 CONFIG_ARCH_MX6 启用。
  • 例如 mx6ull.h 中会定义 i.MX6ULL 芯片的寄存器地址、时钟树等底层硬件信息。

3. 全局配置依赖(通过 Kconfig 关联)

U-Boot 的配置系统通过 Kconfig 文件形成依赖关系:

  • 当你启用 CONFIG_TARGET_MX6ULL_14X14_EVK 时,会自动引入该板卡对应的 Kconfig 中定义的子配置(如默认启用 eMMC 驱动、网口驱动等)。
  • 这些子配置可能对应 drivers/mmc/(eMMC 驱动)、drivers/net/(网口驱动)等目录中的具体实现。

如何查看完整配置?

如果想看到当前配置的所有具体项,可以通过以下命令生成完整的 .config 文件:

# 在 U-Boot 源码根目录执行
make mx6ull_14x14_evk_defconfig  # 加载目标配置
cat .config                       # 查看所有生效的配置项(包括自动启用的子项)

.config 文件中会列出所有被启用的 CONFIG_XXX 项,其中大部分是通过顶层开关(如你提到的那些配置)自动关联的具体功能 / 硬件配置。

比如:

简单说:你看到的是 “开关”,而 “开关控制的具体硬件逻辑” 存放在板级目录和芯片驱动目录中。

配置头文件mx6ullevk.h

mx6ullevk.h 是 NXP i.MX6ULL 系列处理器的 EVK 评估板(Evaluation Kit)专用头文件,主要用于 U-Boot 或 Linux 内核中定义该评估板的硬件相关常量、宏和结构体,是板级配置的核心文件之一。

其典型路径(以 U-Boot 为例)为:
include/configs/mx6ullevk.h

该文件的内容如下所示,已经做了一些基本的注释:

/** Copyright (C) 2016 Freescale Semiconductor, Inc.** Configuration settings for the Freescale i.MX6UL 14x14 EVK board.** SPDX-License-Identifier:	GPL-2.0+*/
#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H#include <asm/arch/imx-regs.h>
#include <linux/sizes.h>
#include "mx6_common.h"
#include <asm/imx-common/gpio.h>//启用 “插件模式” 支持
/* uncomment for PLUGIN mode support */
/* #define CONFIG_USE_PLUGIN */			//启用 “安全启动”(需手动取消注释),启用后需配置 CONFIG_CSF_SIZE (安全配置文件大小,默认 0x4000 字节)。
/* uncomment for SECURE mode support */
/* #define CONFIG_SECURE_BOOT */		#ifdef CONFIG_SECURE_BOOT
#ifndef CONFIG_CSF_SIZE
#define CONFIG_CSF_SIZE 0x4000 
#endif
#endif//判断当前目标板是否为 i.MX6ULL 9x9 EVK
#define is_mx6ull_9x9_evk()	CONFIG_IS_ENABLED(TARGET_MX6ULL_9X9_EVK) //i.MX6UL/ULL 开发板的内存大小因封装不同有差异,配置直接影响 U-Boot 对内存的管理:
//启用 CONFIG_BOOTARGS_CMA_SIZE="cma=96M "(为内核分配 96MB 连续内存)。
#ifdef CONFIG_TARGET_MX6ULL_9X9_EVK
#define PHYS_SDRAM_SIZE		SZ_256M
#define CONFIG_BOOTARGS_CMA_SIZE   "cma=96M " 
#else
#define PHYS_SDRAM_SIZE		SZ_512M
#define CONFIG_BOOTARGS_CMA_SIZE   ""
/* DCDC used on 14x14 EVK, no PMIC */
//禁用 CONFIG_LDO_BYPASS_CHECK(14x14 板用 DCDC 电源,无 PMIC 低压差稳压器)。
#undef CONFIG_LDO_BYPASS_CHECK
#endif//默认禁用 SPL(Secondary Program Loader,二级引导加载程序)
//需手动启用 CONFIG_SPL_LIBCOMMON_SUPPORT/CONFIG_SPL_MMC_SUPPORT 并包含 imx6_spl.h。
/* SPL options */
/* We default not support SPL* #define CONFIG_SPL_LIBCOMMON_SUPPORT* #define CONFIG_SPL_MMC_SUPPORT* #include "imx6_spl.h"
*///启用 U-Boot 运行时环境变量的动态配置能力 
//允许 U-Boot 在运行过程中(而非仅编译时)通过环境变量(如 bootcmd、bootargs)修改自身的核心配置
//无需重新编译 U-Boot 镜像。
#define CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG//启用 U-Boot 启动时的 CPU 信息打印,让开发者在串口终端中直观看到当前硬件的 CPU 型号、架构、主频等关键参数
#define CONFIG_DISPLAY_CPUINFO
//启用 U-Boot 启动时的开发板信息打印,展示当前适配的开发板型号、版本、硬件特征等
//与 CONFIG_DISPLAY_CPUINFO 配合,形成完整的 “硬件身份标识”。
#define CONFIG_DISPLAY_BOARDINFO//定义 U-Boot 中 malloc() 动态内存分配池的大小,此处配置为 16MB(16 * SZ_1M,SZ_1M 是 U-Boot 定义的宏,代表 1MB)。
/* Size of malloc() pool */
#define CONFIG_SYS_MALLOC_LEN		(16 * SZ_1M)//启用 板级早期初始化函数 board_early_init_f(),该函数在 U-Boot 早期阶段(内存初始化之前)执行。
#define CONFIG_BOARD_EARLY_INIT_F
//启用 板级晚期初始化函数 board_late_init(),该函数在 U-Boot 完成大部分初始化(内存、存储、外设等)后执行。
#define CONFIG_BOARD_LATE_INIT//CONFIG_MXC_UART:启用 NXP(原 Freescale)i.MX 系列芯片的 UART 串口驱动,支持通过串口进行控制台交互。
//CONFIG_MXC_UART_BASE UART1_BASE:
//指定默认串口的硬件基地址为 UART1(UART1_BASE 是芯片手册定义的宏,对应 UART1 控制器的物理地址,如 i.MX6ULL 中为 0x02020000)。
#define CONFIG_MXC_UART
#define CONFIG_MXC_UART_BASE		UART1_BASE/*
这段代码是 U-Boot 中针对 i.MX6UL/ULL 开发板 MMC/SD 卡控制器(USDHC) 的核心配置,
核心逻辑是 “根据硬件冲突(NAND 与 USDHC2 引脚复用)动态调整 MMC 控制器数量”,
同时定义控制器的硬件基地址。
*/
/* MMC Configs */
//USDHC(Ultra Secured Digital Host Controller)是 NXP 为 i.MX 芯片设计的 MMC/SD/SDHC 卡控制器,
//负责与 MMC 卡、SD 卡等存储设备通信,是开发板加载镜像(内核、设备树)的核心外设。
//若开发板仅使用 NAND/QSPI 存储,无需 MMC 功能,可通过关闭 CONFIG_FSL_USDHC 宏节省编译体积,避免无用驱动加载。
#ifdef CONFIG_FSL_USDHC
//指定 U-Boot 默认使用的 USDHC 控制器硬件基地址为 USDHC2_BASE_ADDR(即 USDHC2 控制器的物理地址,i.MX6UL/ULL 中固定为 0x02194000)。
//ESDHC 与 USDHC 的关系:ESDHC(Enhanced Secure Digital Host Controller)是 USDHC 的前身,
//U-Boot 中部分老代码仍沿用 ESDHC 命名,此处实际指向的是 USDHC 控制器。
//为什么默认用 USDHC2?
//i.MX6UL/ULL 开发板(如 14x14 EVK)的硬件设计中,USDHC2 通常连接 “板载 MMC 卡槽”,是用户最常用的存储接口,因此优先配置为默认控制器。
#define CONFIG_SYS_FSL_ESDHC_ADDR	USDHC2_BASE_ADDR//这是这段配置的核心逻辑,解决的是 “NAND 闪存与 USDHC2 控制器引脚复用冲突” 的硬件问题:
//(1)冲突背景
//i.MX6UL/ULL 芯片的部分引脚存在功能复用:同一组引脚既可以分配给 USDHC2(用于 MMC 卡),也可以分配给 GPMI NAND 控制器(用于 NAND 闪存)。
//由于硬件物理引脚无法同时实现两个功能,因此当开发板使用 NAND 闪存时,必须禁用 USDHC2;反之,不使用 NAND 时,可同时启用 USDHC1 和 USDHC2。
/* NAND pin conflicts with usdhc2 */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_SYS_FSL_USDHC_NUM	1
#else
#define CONFIG_SYS_FSL_USDHC_NUM	2
#endif
#endif/* I2C configs */
#define CONFIG_CMD_I2C
#ifdef CONFIG_CMD_I2C
#define CONFIG_SYS_I2C
#define CONFIG_SYS_I2C_MXC
#define CONFIG_SYS_I2C_MXC_I2C1		/* enable I2C bus 1 */
#define CONFIG_SYS_I2C_MXC_I2C2		/* enable I2C bus 2 */
#define CONFIG_SYS_I2C_SPEED		100000//PMIC(Power Management IC,电源管理芯片)负责为 CPU 和外设提供稳定电源
//此处针对 i.MX6ULL 9x9 EVK 开发板的 PFUZE3000 芯片配置:
/* PMIC only for 9X9 EVK */
#define CONFIG_POWER
#define CONFIG_POWER_I2C
#define CONFIG_POWER_PFUZE3000
#define CONFIG_POWER_PFUZE3000_I2C_ADDR  0x08
#endif//定义从 MMC/SD 卡加载镜像(如内核、设备树)时默认使用的 分区编号 为 1(分区编号从 0 开始)。
//背景:MMC 设备通常分为多个分区(类似电脑硬盘的 C 盘、D 盘),U-Boot 需明确从哪个分区读取启动文件。
//作用:默认从第 1 分区加载镜像(通常第 0 分区为 boot 分区,第 1 分区为系统分区,具体需与实际烧录方案匹配)。
#define CONFIG_SYS_MMC_IMG_LOAD_PART	1/*
定义 生产模式(MFG Tool)下 NAND 闪存的分区表,仅当启用 NAND 启动(CONFIG_SYS_BOOT_NAND)时生效,用于工厂烧录或批量生产场景。
分区解析(mtdparts=gpmi-nand:...)
gpmi-nand 是 i.MX6UL/ULL 的 NAND 控制器名称,后续为分区划分:
64m(boot):64MB 分区,存放 U-Boot 镜像和启动相关文件(boot 分区)。
16m(kernel):16MB 分区,存放 Linux 内核镜像(zImage 或 uImage)。
16m(dtb):16MB 分区,存放设备树文件(.dtb)。
1m(misc):1MB 分区,用于存放杂项数据(如出厂配置、校准信息)。
-(rootfs):剩余全部空间,作为根文件系统分区(通常使用 UBIFS 格式)。
为什么需要生产模式分区?
生产模式下,工厂通过 MFG Tool 批量烧录镜像,固定的分区表可确保每个设备的存储布局一致,简化自动化烧录流程;
非 NAND 启动时(如 MMC 启动),该分区表无效(定义为空字符串)。
*/
#ifdef CONFIG_SYS_BOOT_NAND
#define CONFIG_MFG_NAND_PARTITION "mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs) "
#else
#define CONFIG_MFG_NAND_PARTITION ""
#endif/*
这段代码是 U-Boot 中环境变量系统的核心配置,
通过 CONFIG_MFG_ENV_SETTINGS 和 CONFIG_EXTRA_ENV_SETTINGS 定义了开发板在不同场景下(生产模式、NAND 启动、MMC / 网络启动)的启动参数、流程和硬件配置,
是 U-Boot 实现 “灵活引导” 的关键
*/
//这部分是工厂生产 / 烧录阶段专用配置,
//核心作用是通过 mfgtool_args 定义生产模式的内核启动参数(如 USB 模拟 U 盘、临时根文件系统),
//并通过 bootcmd_mfg 定义生产模式的启动流程,确保批量生产时的一致性。
#define CONFIG_MFG_ENV_SETTINGS \"mfgtool_args=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"rdinit=/linuxrc " \"g_mass_storage.stall=0 g_mass_storage.removable=1 " \"g_mass_storage.file=/fat g_mass_storage.ro=1 " \"g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF "\"g_mass_storage.iSerialNumber=\"\" "\CONFIG_MFG_NAND_PARTITION \"clk_ignore_unused "\"\0" \"initrd_addr=0x83800000\0" \"initrd_high=0xffffffff\0" \"bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};\0" \/*
CONFIG_EXTRA_ENV_SETTINGS 是 U-Boot 最核心的环境变量集合,定义了开发板在正常使用时的启动参数、硬件配置和引导逻辑。
根据启动介质(NAND 闪存 / 其他介质如 MMC / 网络)分为两种配置
*/
#if defined(CONFIG_SYS_BOOT_NAND)
#define CONFIG_EXTRA_ENV_SETTINGS \CONFIG_MFG_ENV_SETTINGS \"panel=TFT43AB\0" \"fdt_addr=0x83000000\0" \"fdt_high=0xffffffff\0"	  \"console=ttymxc0\0" \"bootargs=console=ttymxc0,115200 ubi.mtd=4 "  \"root=ubi0:rootfs rootfstype=ubifs "		     \CONFIG_BOOTARGS_CMA_SIZE \"mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs)\0"\"bootcmd=nand read ${loadaddr} 0x4000000 0x800000;"\"nand read ${fdt_addr} 0x5000000 0x100000;"\"bootz ${loadaddr} - ${fdt_addr}\0"#else
//当开发板从 MMC/SD 卡、QSPI 或网络启动时,环境变量更复杂,支持多种启动方式切换
#define CONFIG_EXTRA_ENV_SETTINGS \CONFIG_MFG_ENV_SETTINGS \"script=boot.scr\0" \"image=zImage\0" \"console=ttymxc0\0" \"fdt_high=0xffffffff\0" \"initrd_high=0xffffffff\0" \"fdt_file=undefined\0" \"fdt_addr=0x83000000\0" \"boot_fdt=try\0" \"ip_dyn=yes\0" \"panel=TFT43AB\0" \"mmcdev="__stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \"mmcpart=" __stringify(CONFIG_SYS_MMC_IMG_LOAD_PART) "\0" \"mmcroot=" CONFIG_MMCROOT " rootwait rw\0" \"mmcautodetect=yes\0" \"mmcargs=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"root=${mmcroot}\0" \"loadbootscript=" \"fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \"bootscript=echo Running bootscript from mmc ...; " \"source\0" \"loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \"loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}\0" \"mmcboot=echo Booting from mmc ...; " \"run mmcargs; " \"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \"if run loadfdt; then " \"bootz ${loadaddr} - ${fdt_addr}; " \"else " \"if test ${boot_fdt} = try; then " \"bootz; " \"else " \"echo WARN: Cannot load the DT; " \"fi; " \"fi; " \"else " \"bootz; " \"fi;\0" \"netargs=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"root=/dev/nfs " \"ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp\0" \"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} ${image}; " \"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \"if ${get_cmd} ${fdt_addr} ${fdt_file}; then " \"bootz ${loadaddr} - ${fdt_addr}; " \"else " \"if test ${boot_fdt} = try; then " \"bootz; " \"else " \"echo WARN: Cannot load the DT; " \"fi; " \"fi; " \"else " \"bootz; " \"fi;\0" \"findfdt="\"if test $fdt_file = undefined; then " \"if test $board_name = EVK && test $board_rev = 9X9; then " \"setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \"if test $board_name = EVK && test $board_rev = 14X14; then " \"setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \"if test $fdt_file = undefined; then " \"echo WARNING: Could not determine dtb to use; fi; " \"fi;\0" \//这是 U-Boot 上电后自动执行的主启动命令
#define CONFIG_BOOTCOMMAND \"run findfdt;" \"mmc dev ${mmcdev};" \"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 run netboot; fi"
#endif//启用 U-Boot 的 内存测试命令 memtest,允许在启动阶段或通过串口终端手动执行内存完整性测试(如检测坏块、短路等硬件问题)。
/* Miscellaneous configurable options */
#define CONFIG_CMD_MEMTEST
#define CONFIG_SYS_MEMTEST_START	0x80000000
#define CONFIG_SYS_MEMTEST_END		(CONFIG_SYS_MEMTEST_START + 0x8000000)//定义 U-Boot 从存储设备(如 MMC、NAND)加载内核镜像到内存中的 默认地址,
//其值由 CONFIG_LOADADDR 决定(通常在 mx6_common.h 中定义为 0x80800000)。
//为什么是 0x80800000?
//i.MX6UL/ULL 的 SDRAM 起始地址为 0x80000000,前 8MB(0x80000000~0x80800000)通常预留为 U-Boot 自身运行空间,避免内核加载覆盖 U-Boot 代码。
//内核加载地址需与设备树中定义的内核入口地址匹配,确保 CPU 能正确跳转到内核执行。
#define CONFIG_SYS_LOAD_ADDR		CONFIG_LOADADDR
//定义 U-Boot 内部 系统时钟的频率为 1000Hz(即每秒产生 1000 个时钟滴答)。
//作用
//用于 U-Boot 的延时函数(如 udelay()、mdelay())和定时器功能(如倒计时自动启动 bootdelay)。
//1000Hz 意味着每个时钟滴答为 1ms,便于实现毫秒级精度的延时控制(如 mdelay(100) 表示延时 100ms)。
#define CONFIG_SYS_HZ			1000//定义 U-Boot 运行时的 栈空间大小为 128KB(SZ_128K 是 U-Boot 定义的宏,等价于 128 * 1024 字节)。
//作用
//栈是函数调用、局部变量存储的关键内存区域,栈空间不足会导致 栈溢出(表现为程序崩溃、行为异常)。
//128KB 是针对 i.MX6UL/ULL 这类中端嵌入式芯片的合理配置,既能满足 U-Boot 内部函数调用需求(如解析设备树、处理命令),又不会过度占用内存。
#define CONFIG_STACKSIZE		SZ_128K//这段这段代码段定义了 U-Boot 中 物理内存映射 的核心参数,用于描述开发板的物理内存布局(如 SDRAM 位置、初始化 RAM 地址等)
/* Physical Memory Map */
#define CONFIG_NR_DRAM_BANKS		1
//功能:定义 物理 SDRAM 的基地址 为 MMDC0_ARB_BASE_ADDR。
//细节: MMDC0_ARB_BASE_ADDR 是 i.MX6UL/ULL 芯片手册中定义的宏,对应内存控制器 0(MMDC0)管理的 SDRAM 起始物理地址,
//固定为 0x80000000(这是 ARM 架构中典型的外部内存起始地址)。
//作用:标识 SDRAM 在物理地址空间中的起始位置,是 U-Boot 访问内存的基础。
#define PHYS_SDRAM			MMDC0_ARB_BASE_ADDR//定义 U-Boot 识别的 SDRAM 逻辑基地址,与物理基地址 PHYS_SDRAM 保持一致(即 0x80000000)。
//作用:U-Boot 内部通过此地址访问 SDRAM,确保软件逻辑与硬件物理地址对应(对于无 MMU 的系统,逻辑地址直接映射物理地址)
#define CONFIG_SYS_SDRAM_BASE		PHYS_SDRAM
//定义 初始化阶段使用的内部 RAM(IRAM)基地址 为 IRAM_BASE_ADDR。
//细节:IRAM(Internal RAM)是芯片内部集成的小容量 RAM(非外部 SDRAM),i.MX6UL/ULL 的 IRAM 基地址固定为 0x00900000,容量通常为 128KB~256KB。
//作用:SDRAM 初始化前,U-Boot 无法使用外部内存,需依赖 IRAM 完成早期启动(如初始化时钟、内存控制器),此参数指定了这段临时内存的起始位置。
#define CONFIG_SYS_INIT_RAM_ADDR	IRAM_BASE_ADDR
//定义 内部 RAM(IRAM)的大小 为 IRAM_SIZE(由芯片手册定义,如 i.MX6ULL 的 IRAM 大小为 128KB)。
#define CONFIG_SYS_INIT_RAM_SIZE	IRAM_SIZE//计算 U-Boot 早期启动阶段的 栈指针(Stack Pointer)初始地址
#define CONFIG_SYS_INIT_SP_OFFSET \(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)//声明当前系统 不使用传统并行 Flash 芯片(如 NOR Flash)。
//背景:i.MX6UL/ULL 开发板通常采用更先进的存储介质(QSPI 闪存、NAND 闪存、MMC/SD 卡),而非早期的并行 Flash,因此禁用传统 Flash 支持以简化驱动和配置。
//作用:告知 U-Boot 无需加载并行 Flash 驱动,避免资源浪费和潜在的硬件冲突。
/* FLASH and environment organization */
#define CONFIG_SYS_NO_FLASH//根据不同的启动介质(CONFIG_SYS_BOOT_QSPI/CONFIG_SYS_BOOT_NAND)自动选择对应的存储驱动和环境变量存储位置
#ifdef CONFIG_SYS_BOOT_QSPI
#define CONFIG_FSL_QSPI
#define CONFIG_ENV_IS_IN_SPI_FLASH
#elif defined CONFIG_SYS_BOOT_NAND
#define CONFIG_SYS_USE_NAND
#define CONFIG_ENV_IS_IN_NAND
#else
#define CONFIG_FSL_QSPI
#define CONFIG_ENV_IS_IN_MMC
#endif//定义 U-Boot 环境变量(如 bootcmd、bootargs)存储在 第 1 个 MMC 设备 上
//MMC 设备编号从 0 开始,0 对应 USDHC1,1 对应 USDHC2
#define CONFIG_SYS_MMC_ENV_DEV		1   /* USDHC2 */
//定义环境变量存储在 MMC 设备的 第 0 个分区 上
#define CONFIG_SYS_MMC_ENV_PART		0	/* user area */
//定义 Linux 内核启动时使用的 根文件系统(rootfs)路径 为 /dev/mmcblk1p2
#define CONFIG_MMCROOT			"/dev/mmcblk1p2"  /* USDHC2 *///启用 U-Boot 的 bmode 命令,用于查询或设置开发板的 启动模式(Boot Mode)。
//启动模式含义:
//i.MX6UL/ULL 芯片支持多种启动介质(如 MMC、NAND、QSPI、USB),启动模式由硬件引脚(BOOT_MODE[1:0])或软件配置决定。bmode 命令可用于:
//  查看当前启动模式(如 bmode);
//  临时设置下次启动的模式(部分硬件支持)。
//作用:方便开发者在调试阶段快速确认或切换启动介质,无需手动修改硬件引脚。
#define CONFIG_CMD_BMODE#ifdef CONFIG_FSL_QSPI
#define CONFIG_QSPI_BASE		QSPI0_BASE_ADDR
#define CONFIG_QSPI_MEMMAP_BASE		QSPI0_AMBA_BASE#define CONFIG_CMD_SF
#define CONFIG_SPI_FLASH
#define CONFIG_SPI_FLASH_BAR
#define CONFIG_SF_DEFAULT_BUS		0
#define CONFIG_SF_DEFAULT_CS		0
#define CONFIG_SF_DEFAULT_SPEED	40000000
#define CONFIG_SF_DEFAULT_MODE		SPI_MODE_0
#define CONFIG_SPI_FLASH_STMICRO
#endif/* NAND stuff */
#ifdef CONFIG_SYS_USE_NAND
#define CONFIG_CMD_NAND
#define CONFIG_CMD_NAND_TRIMFFS#define CONFIG_NAND_MXS
#define CONFIG_SYS_MAX_NAND_DEVICE	1
#define CONFIG_SYS_NAND_BASE		0x40000000
#define CONFIG_SYS_NAND_5_ADDR_CYCLE
#define CONFIG_SYS_NAND_ONFI_DETECTION/* DMA stuff, needed for GPMI/MXS NAND support */
#define CONFIG_APBH_DMA
#define CONFIG_APBH_DMA_BURST
#define CONFIG_APBH_DMA_BURST8
#endif/*
这段代码是 U-Boot 中 环境变量(Environment Variables)存储参数 的核心配置,
根据环境变量的不同存储介质(MMC/SD、SPI Flash、NAND),定义了环境变量的 大小、偏移地址、扇区大小 等关键参数,
确保 U-Boot 能正确读写环境变量(如 bootcmd、bootargs)
*/
#define CONFIG_ENV_SIZE			SZ_8K
#if defined(CONFIG_ENV_IS_IN_MMC)
#define CONFIG_ENV_OFFSET		(12 * SZ_64K)
#elif defined(CONFIG_ENV_IS_IN_SPI_FLASH)
#define CONFIG_ENV_OFFSET		(768 * 1024)
#define CONFIG_ENV_SECT_SIZE		(64 * 1024)
#define CONFIG_ENV_SPI_BUS		CONFIG_SF_DEFAULT_BUS
#define CONFIG_ENV_SPI_CS		CONFIG_SF_DEFAULT_CS
#define CONFIG_ENV_SPI_MODE		CONFIG_SF_DEFAULT_MODE
#define CONFIG_ENV_SPI_MAX_HZ		CONFIG_SF_DEFAULT_SPEED
#elif defined(CONFIG_ENV_IS_IN_NAND)
#undef CONFIG_ENV_SIZE
#define CONFIG_ENV_OFFSET		(60 << 20)
#define CONFIG_ENV_SECT_SIZE		(128 << 10)
#define CONFIG_ENV_SIZE			CONFIG_ENV_SECT_SIZE
#endif/* USB Configs */
#define CONFIG_CMD_USB
#ifdef CONFIG_CMD_USB
#define CONFIG_USB_EHCI
#define CONFIG_USB_EHCI_MX6
#define CONFIG_USB_STORAGE
#define CONFIG_EHCI_HCD_INIT_AFTER_RESET
#define CONFIG_USB_HOST_ETHER
#define CONFIG_USB_ETHER_ASIX
#define CONFIG_MXC_USB_PORTSC  (PORT_PTS_UTMI | PORT_PTS_PTW)
#define CONFIG_MXC_USB_FLAGS   0
#define CONFIG_USB_MAX_CONTROLLER_COUNT 2
#endif/*
这段代码是 U-Boot 中网络功能的核心配置,
主要针对 i.MX6UL/ULL 开发板的以太网控制器(FEC)进行配置,
包括启用网络命令、指定以太网硬件参数、PHY 芯片类型等,
确保 U-Boot 能通过网络进行通信(如 ping、DHCP、下载镜像)
*/
#ifdef CONFIG_CMD_NET
#define CONFIG_CMD_PING
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_MII
//FEC 是什么:FEC(Fast Ethernet Controller)是 i.MX 芯片内置的快速以太网控制器,支持 10/100Mbps 速率,通过 MII/RMII 接口与外部 PHY 芯片连接。
//CONFIG_FEC_ENET_DEV=1:i.MX6UL/ULL 通常有 2 个 FEC 控制器(ENET1 和 ENET2),此处指定默认使用 ENET2(第 1 个,编号从 0 开始)。
#define CONFIG_FEC_MXC
#define CONFIG_MII
#define CONFIG_FEC_ENET_DEV		1//根据 CONFIG_FEC_ENET_DEV 的值(0 或 1),配置对应以太网控制器的基地址、PHY 地址和接口类型
#if (CONFIG_FEC_ENET_DEV == 0)
#define IMX_FEC_BASE			ENET_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR          0x2
#define CONFIG_FEC_XCV_TYPE             RMII
#elif (CONFIG_FEC_ENET_DEV == 1)
#define IMX_FEC_BASE			ENET2_BASE_ADDR
#define CONFIG_FEC_MXC_PHYADDR		0x1
#define CONFIG_FEC_XCV_TYPE		RMII
#endif
#define CONFIG_ETHPRIME			"FEC"#define CONFIG_PHYLIB
#define CONFIG_PHY_MICREL
#endif#define CONFIG_IMX_THERMAL#ifndef CONFIG_SPL_BUILD
#define CONFIG_VIDEO
#ifdef CONFIG_VIDEO
#define CONFIG_CFB_CONSOLE
#define CONFIG_VIDEO_MXS
#define CONFIG_VIDEO_LOGO
#define CONFIG_VIDEO_SW_CURSOR
#define CONFIG_VGA_AS_SINGLE_DEVICE
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
#define CONFIG_SPLASH_SCREEN
#define CONFIG_SPLASH_SCREEN_ALIGN
#define CONFIG_CMD_BMP
#define CONFIG_BMP_16BPP
#define CONFIG_VIDEO_BMP_RLE8
#define CONFIG_VIDEO_BMP_LOGO
#define CONFIG_IMX_VIDEO_SKIP
#endif
#endif#define CONFIG_IOMUX_LPSR#if defined(CONFIG_ANDROID_SUPPORT)
#include "mx6ullevk_android.h"
#endif#endif

mx6ullevk.h 是 MX6ULL EVK 评估板的 “硬件配置字典”,集中定义了板载 CPU、内存、外设的硬件参数和 U-Boot 功能开关。编译时,这些配置会被纳入 U-Boot 源码,确保生成的镜像能准确适配该评估板的硬件环境。

如果需要修改评估板的硬件配置(如更换内存容量、调整默认启动方式),通常需要修改该文件中的对应宏定义。

接下来对一些重点内容做个备注。

SPL是啥?

在嵌入式系统(尤其是 U-Boot 引导程序)中,SPL(Secondary Program Loader,二级程序加载器) 是一个小型的引导程序,主要用于解决硬件初始化和主引导程序(如 U-Boot 主体)加载的问题。

SPL 的核心作用

  1. 硬件最小化初始化
    嵌入式芯片(如 ARM 架构的 SoC)上电后,首先执行内部 ROM 中的代码(称为 Primary Program Loader,一级程序加载器),但 ROM 代码通常功能有限,只能完成最基础的初始化(如关闭看门狗、配置时钟和内存控制器等)。
    SPL 则在此基础上进一步初始化关键硬件(如 SDRAM、存储控制器),为加载更大的主程序(如 U-Boot 主体)准备好运行环境。

  2. 加载主引导程序
    由于 ROM 空间有限(通常只有几 KB 到几十 KB),无法直接加载完整的 U-Boot(通常几百 KB 到几 MB)。
    SPL 作为 “中间层”,会从存储设备(如 MMC 卡、NAND 闪存、SPI 闪存)中读取 U-Boot 主体镜像,并将其加载到 SDRAM 中运行。

  3. 适配硬件限制
    某些芯片的启动流程要求 “第一阶段引导程序” 必须小于特定尺寸(如 32KB),而完整 U-Boot 往往超过这个限制。SPL 设计得非常精简(通常几十 KB),正好满足这种硬件约束。

SPL 与 U-Boot 的关系

  • SPL 是 U-Boot 的一部分:在编译 U-Boot 时,可通过配置(如 CONFIG_SPL 相关宏)生成 SPL 镜像,与 U-Boot 主体镜像分开存储。
  • 启动流程
    上电 → ROM 代码执行 → 加载并运行 SPL → SPL 初始化硬件 → 加载 U-Boot 主体 → 运行 U-Boot 主体 → 加载内核。

为什么需要 SPL?

  • 硬件限制:解决 ROM 空间不足、无法直接加载大程序的问题。
  • 功能分层:将 “最小化初始化” 和 “完整引导功能” 分离,简化代码维护。
  • 灵活性:SPL 可根据硬件特性定制(如不同存储介质的驱动),为主程序提供统一的运行环境。

在你提供的 mx6ullevk_config.h 中,默认注释了 SPL 相关配置(/* #define CONFIG_SPL_LIBCOMMON_SUPPORT */),说明该开发板默认不启用 SPL。若需启用,需取消注释并配置相应的存储介质支持(如 CONFIG_SPL_MMC_SUPPORT 表示从 MMC 卡加载 U-Boot 主体)。

看这一段关于环境变量的配置

/*
这段代码是 U-Boot 中环境变量系统的核心配置,
通过 CONFIG_MFG_ENV_SETTINGS 和 CONFIG_EXTRA_ENV_SETTINGS 定义了开发板在不同场景下(生产模式、NAND 启动、MMC / 网络启动)的启动参数、流程和硬件配置,
是 U-Boot 实现 “灵活引导” 的关键
*/
//这部分是工厂生产 / 烧录阶段专用配置,
//核心作用是通过 mfgtool_args 定义生产模式的内核启动参数(如 USB 模拟 U 盘、临时根文件系统),
//并通过 bootcmd_mfg 定义生产模式的启动流程,确保批量生产时的一致性。
#define CONFIG_MFG_ENV_SETTINGS \"mfgtool_args=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"rdinit=/linuxrc " \"g_mass_storage.stall=0 g_mass_storage.removable=1 " \"g_mass_storage.file=/fat g_mass_storage.ro=1 " \"g_mass_storage.idVendor=0x066F g_mass_storage.idProduct=0x37FF "\"g_mass_storage.iSerialNumber=\"\" "\CONFIG_MFG_NAND_PARTITION \"clk_ignore_unused "\"\0" \"initrd_addr=0x83800000\0" \"initrd_high=0xffffffff\0" \"bootcmd_mfg=run mfgtool_args;bootz ${loadaddr} ${initrd_addr} ${fdt_addr};\0" \/*
CONFIG_EXTRA_ENV_SETTINGS 是 U-Boot 最核心的环境变量集合,定义了开发板在正常使用时的启动参数、硬件配置和引导逻辑。
根据启动介质(NAND 闪存 / 其他介质如 MMC / 网络)分为两种配置
*/
#if defined(CONFIG_SYS_BOOT_NAND)
#define CONFIG_EXTRA_ENV_SETTINGS \CONFIG_MFG_ENV_SETTINGS \"panel=TFT43AB\0" \"fdt_addr=0x83000000\0" \"fdt_high=0xffffffff\0"	  \"console=ttymxc0\0" \"bootargs=console=ttymxc0,115200 ubi.mtd=4 "  \"root=ubi0:rootfs rootfstype=ubifs "		     \CONFIG_BOOTARGS_CMA_SIZE \"mtdparts=gpmi-nand:64m(boot),16m(kernel),16m(dtb),1m(misc),-(rootfs)\0"\"bootcmd=nand read ${loadaddr} 0x4000000 0x800000;"\"nand read ${fdt_addr} 0x5000000 0x100000;"\"bootz ${loadaddr} - ${fdt_addr}\0"#else
//当开发板从 MMC/SD 卡、QSPI 或网络启动时,环境变量更复杂,支持多种启动方式切换
#define CONFIG_EXTRA_ENV_SETTINGS \CONFIG_MFG_ENV_SETTINGS \"script=boot.scr\0" \"image=zImage\0" \"console=ttymxc0\0" \"fdt_high=0xffffffff\0" \"initrd_high=0xffffffff\0" \"fdt_file=undefined\0" \"fdt_addr=0x83000000\0" \"boot_fdt=try\0" \"ip_dyn=yes\0" \"panel=TFT43AB\0" \"mmcdev="__stringify(CONFIG_SYS_MMC_ENV_DEV)"\0" \"mmcpart=" __stringify(CONFIG_SYS_MMC_IMG_LOAD_PART) "\0" \"mmcroot=" CONFIG_MMCROOT " rootwait rw\0" \"mmcautodetect=yes\0" \"mmcargs=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"root=${mmcroot}\0" \"loadbootscript=" \"fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};\0" \"bootscript=echo Running bootscript from mmc ...; " \"source\0" \"loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}\0" \"loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr} ${fdt_file}\0" \"mmcboot=echo Booting from mmc ...; " \"run mmcargs; " \"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \"if run loadfdt; then " \"bootz ${loadaddr} - ${fdt_addr}; " \"else " \"if test ${boot_fdt} = try; then " \"bootz; " \"else " \"echo WARN: Cannot load the DT; " \"fi; " \"fi; " \"else " \"bootz; " \"fi;\0" \"netargs=setenv bootargs console=${console},${baudrate} " \CONFIG_BOOTARGS_CMA_SIZE \"root=/dev/nfs " \"ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp\0" \"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} ${image}; " \"if test ${boot_fdt} = yes || test ${boot_fdt} = try; then " \"if ${get_cmd} ${fdt_addr} ${fdt_file}; then " \"bootz ${loadaddr} - ${fdt_addr}; " \"else " \"if test ${boot_fdt} = try; then " \"bootz; " \"else " \"echo WARN: Cannot load the DT; " \"fi; " \"fi; " \"else " \"bootz; " \"fi;\0" \"findfdt="\"if test $fdt_file = undefined; then " \"if test $board_name = EVK && test $board_rev = 9X9; then " \"setenv fdt_file imx6ull-9x9-evk.dtb; fi; " \"if test $board_name = EVK && test $board_rev = 14X14; then " \"setenv fdt_file imx6ull-14x14-evk.dtb; fi; " \"if test $fdt_file = undefined; then " \"echo WARNING: Could not determine dtb to use; fi; " \"fi;\0" \//这是 U-Boot 上电后自动执行的主启动命令
#define CONFIG_BOOTCOMMAND \"run findfdt;" \"mmc dev ${mmcdev};" \"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 run netboot; fi"
#endif

这段代码是 U-Boot 中环境变量系统的核心配置,通过 CONFIG_MFG_ENV_SETTINGS 和 CONFIG_EXTRA_ENV_SETTINGS 定义了开发板在不同场景下(生产模式、NAND 启动、MMC / 网络启动)的启动参数、流程和硬件配置,是 U-Boot 实现 “灵活引导” 的关键。以下分模块解析:

一、CONFIG_MFG_ENV_SETTINGS:生产模式环境变量(已详细解析,此处总结)

这部分是工厂生产 / 烧录阶段专用配置,核心作用是通过 mfgtool_args 定义生产模式的内核启动参数(如 USB 模拟 U 盘、临时根文件系统),并通过 bootcmd_mfg 定义生产模式的启动流程,确保批量生产时的一致性。

二、CONFIG_EXTRA_ENV_SETTINGS:通用环境变量(分启动介质适配)

这是 U-Boot 最核心的环境变量集合,定义了开发板在正常使用时的启动参数、硬件配置和引导逻辑。根据启动介质(NAND 闪存 / 其他介质如 MMC / 网络)分为两种配置:

场景 1:NAND 启动(#if defined(CONFIG_SYS_BOOT_NAND)

当开发板从 NAND 闪存启动时,环境变量配置如下:

环境变量功能说明
panel=TFT43AB定义默认屏幕型号为 TFT43AB(4.3 英寸 TFT 屏),供显示驱动使用。
fdt_addr=0x83000000设备树(.dtb)在内存中的加载地址(固定为 0x83000000)。
fdt_high=0xffffffff限制设备树使用的内存上限(避免占用内核空间)。
console=ttymxc0定义默认控制台为 UART1(设备节点 ttymxc0)。
bootargs(内核启动参数)
console=ttymxc0,115200:内核控制台参数(串口 + 波特率);
ubi.mtd=4:指定 UBI 卷在 NAND 的第 4 分区(rootfs 分区);
root=ubi0:rootfs rootfstype=ubifs:根文件系统为 UBIFS 格式(适配 NAND 特性);
mtdparts=...:NAND 分区表(与生产模式一致,确保内核与 U-Boot 分区定义匹配)。
bootcmd(U-Boot 启动命令)NAND 启动的核心流程:
1. nand read ${loadaddr} 0x4000000 0x800000:从 NAND 地址 0x4000000(kernel 分区)读取 8MB 内核到内存 loadaddr
2. nand read ${fdt_addr} 0x5000000 0x100000:从 NAND 地址 0x5000000(dtb 分区)读取 1MB 设备树到 fdt_addr
3. bootz ${loadaddr} - ${fdt_addr}:启动内核(无 initrd,指定设备树地址)。

场景 2:非 NAND 启动(默认,MMC/SD 或网络启动)

当开发板从 MMC/SD 卡、QSPI 或网络启动时,环境变量更复杂,支持多种启动方式切换:

(1)基础配置变量

环境变量功能说明
script=boot.scr启动脚本文件名(boot.scr 是 U-Boot 脚本的二进制格式,可自定义启动逻辑)。
image=zImage内核镜像文件名(i.MX 系列常用压缩内核 zImage)。
fdt_file=undefined设备树文件名(默认未定义,通过 findfdt 自动识别)。
boot_fdt=try设备树加载策略:try 表示 “尝试加载,失败则忽略”(兼容无设备树的启动)。
ip_dyn=yes网络配置策略:默认使用 DHCP 动态获取 IP。
mmcdev=...默认 MMC 设备编号(由 CONFIG_SYS_MMC_ENV_DEV 定义,通常为 1,对应 USDHC2)。
mmcpart=...默认 MMC 分区编号(由 CONFIG_SYS_MMC_IMG_LOAD_PART 定义,通常为 1)。
mmcroot=...MMC 根文件系统路径(/dev/mmcblk1p2,即 MMC1 的第 2 分区)。

(2)启动参数与命令(核心逻辑)

  • mmcargs 与 mmcboot:从 MMC 启动

    • mmcargs:定义 MMC 启动时的内核参数(console + root 路径)。
    • mmcboot:MMC 启动流程:
      run mmcargs;  # 加载 MMC 启动参数
      if 尝试加载设备树且成功:bootz 内核地址 - 设备树地址;  # 带设备树启动
      else if 策略为 "try":bootz 内核地址;  # 无设备树启动(兼容旧内核)
      else:提示 "无法加载设备树"
      
  • netargs 与 netboot:从网络启动

    • netargs:定义网络启动时的内核参数(根文件系统为 NFS 共享目录)。
    • netboot:网络启动流程:
      run netargs;  # 加载网络启动参数
      if 动态 IP: 使用 dhcp 获取内核/设备树;
      else: 使用 tftp 下载内核/设备树;
      加载完成后,同 MMC 启动逻辑启动内核(带/不带设备树)
      
  • findfdt:自动匹配设备树

    根据板型自动选择设备树文件:

    if 板型为 EVK 且版本为 9X9: 使用 imx6ull-9x9-evk.dtb;
    if 板型为 EVK 且版本为 14X14: 使用 imx6ull-14x14-evk.dtb;
    

    避免手动指定设备树,适配不同硬件版本。

  • loadbootscript 与 bootscript:通过脚本启动

    优先加载 boot.scr 脚本(若存在),执行脚本中定义的自定义启动逻辑(灵活适配复杂场景)。

(3)CONFIG_BOOTCOMMAND:U-Boot 默认启动入口

这是 U-Boot 上电后自动执行的主启动命令,逻辑如下:

run findfdt;  # 第一步:自动匹配设备树
mmc dev ${mmcdev};  # 初始化默认 MMC 设备
if MMC 设备存在且扫描成功:if 能加载启动脚本 boot.scr:执行脚本(优先级最高);else if 能加载内核镜像 zImage:执行 MMC 启动流程;else:尝试网络启动;
else:直接尝试网络启动;

优先级:启动脚本(boot.scr) > MMC 启动 > 网络启动,兼顾灵活性和可靠性。

三、核心逻辑总结

  1. 场景适配:通过条件编译区分 NAND / 非 NAND 启动,为不同存储介质定制启动流程。
  2. 优先级设计:正常启动时,脚本启动 > MMC 启动 > 网络启动;生产模式则使用独立的 bootcmd_mfg
  3. 自动化:通过 findfdt 自动匹配设备树,mmcautodetect 自动检测 MMC 设备,减少手动配置。
  4. 兼容性boot_fdt=try 支持带 / 不带设备树的启动方式,兼容不同版本内核。

这些环境变量是 U-Boot “智能引导” 的灵魂,确保开发板能在不同硬件配置、不同存储介质、不同使用场景下稳定启动,是嵌入式系统灵活性的核心体现。

为什么不使用设备树,而是要使用mx6ullevk.h来定义硬件信息?

在 U-Boot 中使用 mx6ullevk.h 这类头文件定义硬件信息,而不直接依赖设备树(Device Tree),主要与 U-Boot 的设计目标、历史发展以及功能定位 有关。具体原因如下:

1. U-Boot 与设备树的定位差异

  • U-Boot 的核心目标:快速完成硬件初始化(如 DDR、串口、存储、网口等),为内核启动提供基础环境。它的生命周期很短(仅在启动阶段运行),不需要像内核那样支持复杂的硬件动态适配。
  • 设备树的核心目标:为 Linux 内核提供统一的硬件描述方式,解决 “同一款内核适配多款硬件” 的问题,避免内核代码中充斥大量板级条件编译。

U-Boot 作为 “引导程序”,更注重初始化效率而非通用性,因此早期设计中采用了更直接的头文件配置方式。

2. 历史发展原因:设备树在 U-Boot 中应用较晚

  • 设备树(Device Tree)是在 Linux 内核 3.x 版本后才逐渐普及的硬件描述方式,而 U-Boot 的历史比设备树更悠久。
  • 早期 U-Boot 为了简化设计,直接通过头文件(如 mx6ullevk.h)硬编码板级硬件参数(如内存地址、外设寄存器地址等),这种方式实现简单、执行高效,符合引导程序的需求。
  • 虽然现代 U-Boot 已经支持设备树,但出于兼容性和初始化效率考虑,仍保留了头文件配置的方式,尤其是在早期初始化阶段(如 DDR 初始化、引脚复用等)。

3. U-Boot 初始化阶段的限制

U-Boot 的启动过程分为多个阶段,其中早期阶段(如 board_init_f 运行在 SRAM 中,此时 DDR 内存尚未初始化,设备树(通常存储在内存或存储设备中)无法被访问。因此:

  • 早期关键硬件(如 DDR、时钟、串口)的初始化必须依赖编译时已知的硬编码参数(来自头文件),无法等到设备树加载后再获取配置。
  • mx6ullevk.h 中定义的内存起始地址、控制器寄存器地址等参数,是 DDR 初始化的前提,必须在编译时确定。

4. U-Boot 对设备树的实际使用方式

现代 U-Boot 并非完全不使用设备树,而是两者配合

  • 头文件(mx6ullevk.h:负责早期初始化必需的硬件参数(如内存大小、核心外设寄存器地址、时钟配置等),确保 U-Boot 能完成基本启动。
  • 设备树(.dtb:在 U-Boot 后期阶段(内存可用后)被加载,用于:
    • 向 Linux 内核传递完整的硬件描述(替代内核中的板级代码)。
    • U-Boot 自身的部分外设驱动(如网口、USB)也会从设备树中获取配置(如引脚、中断号),但这是在早期初始化完成之后。

5. 头文件配置的优势:简单、高效、可控

对于 U-Boot 而言,头文件硬编码的方式有明显优势:

  • 执行效率高:直接通过宏定义访问硬件参数,无需解析设备树的复杂逻辑,适合启动阶段的性能需求。
  • 实现简单:避免了设备树解析的代码复杂性,降低 U-Boot 自身的开发和维护成本。
  • 确定性强:硬件参数在编译时固定,初始化过程可预测,减少启动阶段的不确定性(对嵌入式设备至关重要)。

总结

mx6ullevk.h 这类头文件是 U-Boot 为满足早期初始化效率历史兼容性而设计的硬件配置方式,负责提供启动必需的 “基础硬件参数”;而设备树则在 U-Boot 后期和 Linux 内核中发挥作用,提供更灵活的硬件描述。两者并非对立关系,而是在不同阶段分工协作,共同完成从硬件上电到内核启动的全过程。

随着 U-Boot 对设备树支持的完善,越来越多的外设配置已迁移到设备树中,但核心的早期初始化参数仍依赖头文件,这是由 U-Boot 作为引导程序的本质需求决定的。

板级目录mx6ullevk

uboot 中每个板子都有一个对应的文件夹来存放板级文件,比如开发板上外设驱动文件等等。NXP 的 I.MX 系列芯片的所有板级文件夹都存放在 board/freescale 目录下,在这个目录下有个名为 mx6ullevk 的文件夹,这个文件夹就是 NXP 官方 I.MX6ULL EVK 开发板的板级文件夹。

其中有两个关键文件imximage.cfg和mx6ullevk.c

接下来分别看看

imximage.cfg

先看这个文件的内容:

/** Copyright (C) 2016 Freescale Semiconductor, Inc.** SPDX-License-Identifier:	GPL-2.0+** Refer docs/README.imxmage for more details about how-to configure* and create imximage boot image** The syntax is taken as close as possible with the kwbimage*/#define __ASSEMBLY__
#include <config.h>/* image version */IMAGE_VERSION 2/** Boot Device : one of* spi/sd/nand/onenand, qspi/nor*/#ifdef CONFIG_SYS_BOOT_QSPI
BOOT_FROM	qspi
#elif defined(CONFIG_SYS_BOOT_EIMNOR)
BOOT_FROM	nor
#else
BOOT_FROM	sd
#endif#ifdef CONFIG_USE_PLUGIN
/*PLUGIN    plugin-binary-file    IRAM_FREE_START_ADDR*/
PLUGIN	board/freescale/mx6ullevk/plugin.bin 0x00907000
#else#ifdef CONFIG_SECURE_BOOT
CSF CONFIG_CSF_SIZE
#endif/** Device Configuration Data (DCD)** Each entry must have the format:* Addr-type           Address        Value** where:*	Addr-type register length (1,2 or 4 bytes)*	Address	  absolute address of the register*	value	  value to be stored in the register*//* Enable all clocks */
DATA 4 0x020c4068 0xffffffff
DATA 4 0x020c406c 0xffffffff
DATA 4 0x020c4070 0xffffffff
DATA 4 0x020c4074 0xffffffff
DATA 4 0x020c4078 0xffffffff
DATA 4 0x020c407c 0xffffffff
DATA 4 0x020c4080 0xffffffffDATA 4 0x020E04B4 0x000C0000
DATA 4 0x020E04AC 0x00000000
DATA 4 0x020E027C 0x00000030
DATA 4 0x020E0250 0x00000030
DATA 4 0x020E024C 0x00000030
DATA 4 0x020E0490 0x00000030
DATA 4 0x020E0288 0x000C0030
DATA 4 0x020E0270 0x00000000
DATA 4 0x020E0260 0x00000030
DATA 4 0x020E0264 0x00000030
DATA 4 0x020E04A0 0x00000030
DATA 4 0x020E0494 0x00020000
DATA 4 0x020E0280 0x00000030
DATA 4 0x020E0284 0x00000030
DATA 4 0x020E04B0 0x00020000
DATA 4 0x020E0498 0x00000030
DATA 4 0x020E04A4 0x00000030
DATA 4 0x020E0244 0x00000030
DATA 4 0x020E0248 0x00000030
DATA 4 0x021B001C 0x00008000
DATA 4 0x021B0800 0xA1390003
DATA 4 0x021B080C 0x00000004
DATA 4 0x021B083C 0x41640158
DATA 4 0x021B0848 0x40403237
DATA 4 0x021B0850 0x40403C33
DATA 4 0x021B081C 0x33333333
DATA 4 0x021B0820 0x33333333
DATA 4 0x021B082C 0xf3333333
DATA 4 0x021B0830 0xf3333333
DATA 4 0x021B08C0 0x00944009
DATA 4 0x021B08b8 0x00000800
DATA 4 0x021B0004 0x0002002D
DATA 4 0x021B0008 0x1B333030
DATA 4 0x021B000C 0x676B52F3
DATA 4 0x021B0010 0xB66D0B63
DATA 4 0x021B0014 0x01FF00DB
DATA 4 0x021B0018 0x00201740
DATA 4 0x021B001C 0x00008000
DATA 4 0x021B002C 0x000026D2
DATA 4 0x021B0030 0x006B1023
DATA 4 0x021B0040 0x0000004F
DATA 4 0x021B0000 0x84180000
DATA 4 0x021B0890 0x00400000
DATA 4 0x021B001C 0x02008032
DATA 4 0x021B001C 0x00008033
DATA 4 0x021B001C 0x00048031
DATA 4 0x021B001C 0x15208030
DATA 4 0x021B001C 0x04008040
DATA 4 0x021B0020 0x00000800
DATA 4 0x021B0818 0x00000227
DATA 4 0x021B0004 0x0002552D
DATA 4 0x021B0404 0x00011006
DATA 4 0x021B001C 0x00000000#endif

这段代码是 NXP i.MX6ULL 芯片专用的 U-Boot 启动镜像配置文件(通常命名为 imximage.cfg 或类似名称),用于生成符合 i.MX 系列芯片启动规范的 IMX 格式镜像。其核心作用是定义镜像版本、启动介质、安全配置,以及最关键的 DCD(Device Configuration Data,设备配置数据)—— 通过直接写入芯片寄存器,在 U-Boot 主体代码执行前完成 硬件最小化初始化(如时钟使能、内存控制器配置)。以下分模块详细解析:

一、文件头部:版权与功能说明

/** Copyright (C) 2016 Freescale Semiconductor, Inc.  // 版权归属(NXP 前身)** SPDX-License-Identifier:	GPL-2.0+  // 开源协议(GPLv2)** Refer docs/README.imxmage for more details about how-to configure* and create imximage boot image  // 参考文档(IMX 镜像配置指南)** The syntax is taken as close as possible with the kwbimage  // 语法参考 kwbimage(早期镜像格式)*/
  • 明确文件用途:用于生成 i.MX 芯片可识别的启动镜像,需遵循 NXP 制定的硬件启动规范。

二、基础配置:镜像版本与启动介质

1. #define __ASSEMBLY__ 与 #include <config.h>

  • __ASSEMBLY__:告知编译器 “当前文件包含汇编相关语法”(DCD 部分本质是寄存器操作,需按汇编格式解析)。
  • #include <config.h>:引入 U-Boot 全局配置文件(如之前分析的 mx6ullevk_defconfig),使后续条件编译(如 CONFIG_SYS_BOOT_QSPI)生效。

2. IMAGE_VERSION 2

  • 定义 IMX 镜像版本号为 2(NXP 对启动镜像格式的版本划分,不同版本对应不同的芯片启动逻辑,i.MX6ULL 需使用版本 2)。

3. BOOT_FROM:指定默认启动介质

根据 U-Boot 全局配置,动态选择启动介质(与之前分析的 CONFIG_SYS_BOOT_* 宏联动):

#ifdef CONFIG_SYS_BOOT_QSPI    // 若启用 QSPI 启动
BOOT_FROM	qspi             // 从 QSPI 闪存启动
#elif defined(CONFIG_SYS_BOOT_EIMNOR)  // 若启用 EIM NOR 启动
BOOT_FROM	nor              // 从 NOR 闪存启动
#else                          // 默认(未启用上述启动方式)
BOOT_FROM	sd               // 从 SD/MMC 卡启动
#endif
  • 作用:告知芯片 ROM 代码(一级引导程序)从哪个介质加载当前 IMX 镜像,是启动流程的 “入口指引”。

三、安全与插件配置(可选)

1. CONFIG_USE_PLUGIN:插件模式(少见)

#ifdef CONFIG_USE_PLUGIN
/*PLUGIN    plugin-binary-file    IRAM_FREE_START_ADDR*/
PLUGIN	board/freescale/mx6ullevk/plugin.bin 0x00907000
#else
// ... 正常初始化逻辑
#endif
  • 插件模式:用于加载额外的小型初始化程序(plugin.bin)到 IRAM(内部 RAM,地址 0x00907000),通常用于复杂硬件的预初始化(如安全加密芯片),多数普通开发场景不启用。

2. CONFIG_SECURE_BOOT:安全启动(可选)

#ifdef CONFIG_SECURE_BOOT
CSF CONFIG_CSF_SIZE  // CSF(Command Sequence File):安全启动命令序列
#endif
  • 安全启动:若启用,会在镜像中嵌入 CSF 数据(包含签名、加密密钥等),芯片 ROM 代码会验证 CSF 合法性,防止未授权镜像启动(用于商业产品的防篡改);普通开发场景通常关闭。

四、核心:DCD(Device Configuration Data)硬件初始化

DCD 是该文件最关键的部分,通过 DATA 指令直接向芯片物理寄存器写入值,在 U-Boot 主体代码执行前(甚至内存初始化前)完成 硬件最小化配置,确保后续启动流程有稳定的硬件环境。

DCD 语法规则

每条 DATA 指令格式:DATA [寄存器长度] [寄存器物理地址] [写入值]

  • 寄存器长度:4 表示 32 位寄存器(i.MX6ULL 多数控制寄存器为 32 位);
  • 寄存器地址:芯片手册中定义的物理地址(如 0x020c4068 是时钟控制寄存器);
  • 写入值:要配置到寄存器的数值(对应硬件功能开关)。

关键 DCD 配置解析(按功能分类)

1. 时钟使能(所有外设时钟打开)

/* Enable all clocks */
DATA 4 0x020c4068 0xffffffff  // CCM_CCGR0:使能所有时钟域 0 的外设时钟
DATA 4 0x020c406c 0xffffffff  // CCM_CCGR1:使能所有时钟域 1 的外设时钟
DATA 4 0x020c4070 0xffffffff  // CCM_CCGR2:使能所有时钟域 2 的外设时钟
// ... 后续 4 条类似指令:使能 CCM_CCGR3~CCGR6 的所有时钟
  • 背景:i.MX6ULL 芯片上电后,多数外设时钟默认关闭(节能),需通过 CCM(Clock Controller Module)寄存器手动使能;
  • 作用:打开所有外设时钟(如 MMC、UART、FEC 网络),为后续初始化(如读取 MMC 中的 U-Boot 主体)提供时钟支持。

2. 内存控制器(SDRAM)配置

// 以下指令均为 MMC(内存控制器)寄存器配置,以 0x021B 开头(i.MX6ULL 内存控制器基地址)
DATA 4 0x021B001C 0x00008000  // MMC_CTL0:配置内存控制器控制位
DATA 4 0x021B0800 0xA1390003  // MMC_MDDR_CFG:配置 DDR 内存时序参数
DATA 4 0x021B080C 0x00000004  // MMC_DDR_CFG:配置 DDR 模式
DATA 4 0x021B083C 0x41640158  // MMC_TIMING_CFG_3:配置内存时序(刷新周期、延迟等)
// ... 后续多条指令:配置内存地址映射、读写时序、电源管理等
  • 核心作用:SDRAM 上电后处于未初始化状态,无法直接使用;通过这些指令配置内存控制器的时序、模式、地址映射,使 SDRAM 成为可用的内存空间(为加载 U-Boot 主体、内核提供内存支持)。

3. 外设引脚与控制配置

// 以下指令为其他外设控制寄存器(如 GPIO、UART 引脚复用)
DATA 4 0x020E04B4 0x000C0000  // IOMUXC 寄存器:配置引脚复用(如 UART 引脚)
DATA 4 0x020E04AC 0x00000000  // IOMUXC 寄存器:配置引脚电气属性(如拉电阻)
DATA 4 0x020E027C 0x00000030  // IOMUXC 寄存器:配置 MMC 引脚复用
// ... 后续多条指令:配置 MMC、UART、网络等外设的引脚复用和电气属性
  • 作用:i.MX6ULL 引脚支持多功能复用(如同一引脚可作 GPIO 或 UART _TX),通过 IOMUXC(引脚复用控制器)寄存器配置,确保关键外设(如 MMC 卡、调试串口)的引脚功能正确。

五、文件作用与启动流程关联

IMX 镜像(由该文件生成)在 i.MX6ULL 启动流程中的角色:

  1. 芯片上电:首先执行芯片内部 ROM 代码(一级引导程序);
  2. ROM 代码:根据 BOOT_FROM 配置,从指定介质(SD/QSPI/NOR)读取 IMX 镜像;
  3. 执行 DCD:ROM 代码解析镜像中的 DCD 数据,按 DATA 指令初始化硬件(时钟、SDRAM、引脚);
  4. 加载 U-Boot:DCD 完成后,ROM 代码将 IMX 镜像中的 U-Boot 主体加载到 SDRAM,跳转到 U-Boot 入口执行;
  5. 后续启动:U-Boot 主体执行,继续初始化外设、加载内核。

总结

该文件的本质是 i.MX6ULL 启动的 “硬件初始化脚本”,核心价值在于:

  1. 填补 ROM 代码功能空白:芯片 ROM 代码仅能完成最基础的启动介质读取,DCD 则补充了时钟、内存、引脚等关键初始化,为 U-Boot 主体执行铺路;
  2. 硬件定制化入口:不同开发板的 SDRAM 时序、引脚复用可能不同,只需修改 DCD 中的寄存器地址和数值,即可适配不同硬件(无需改动 U-Boot 核心代码);
  3. 符合 NXP 启动规范:只有包含正确 DCD 的 IMX 镜像,才能被 i.MX6ULL 的 ROM 代码识别并执行,是启动的 “合法性凭证”。

对于开发者而言,若需修改 i.MX6ULL EVK 的硬件初始化(如更换 SDRAM、调整引脚),核心就是修改该文件中 DCD 部分的寄存器配置,且必须严格参考 i.MX6ULL 芯片手册中的寄存器定义(地址、位含义),否则会导致启动失败。

mx6ullevk.c

mx6ullevk.c 是 U-Boot 中 NXP i.MX6ULL EVK 开发板的板级初始化核心文件,位于 board/freescale/mx6ullevk/ 目录下。其核心作用是为该开发板提供 硬件专属的初始化逻辑,涵盖从 “早期硬件准备” 到 “晚期外设配置” 的全流程,是 U-Boot 与 i.MX6ULL EVK 硬件交互的 “直接入口”。

核心定位

i.MX6ULL EVK 开发板的硬件资源(如引脚分配、外设型号、存储接口)具有唯一性,而 U-Boot 核心代码是通用的 ——mx6ullevk.c 就是 “通用 U-Boot 适配特定硬件” 的桥梁:通过实现 U-Boot 定义的板级初始化接口(如 board_init()board_late_init()),将硬件细节(如 “哪组 GPIO 控制 LED”“网络 PHY 芯片地址是多少”)注入 U-Boot 启动流程,确保 U-Boot 能识别并控制板载外设。

典型代码结构与核心函数解析

mx6ullevk.c 的代码逻辑严格遵循 U-Boot 板级驱动框架,核心函数按 “启动阶段” 划分,以下是最关键的函数及功能:

1. board_init():早期板级初始化(启动关键)

执行时机:U-Boot 启动流程的早期(汇编阶段结束后,C 语言阶段开始时,SDRAM 未初始化前)。
核心作用:完成 “最小化硬件准备”,为后续内存初始化、外设配置铺路,主要工作包括:

  • 禁用看门狗:防止芯片因初始化耗时过长被复位(i.MX6ULL 看门狗默认上电使能);
    void board_init(void) {// 禁用看门狗(通过写入 WDOG 控制寄存器)struct wdog_regs *wdog = (struct wdog_regs *)WDOG1_BASE_ADDR;wdog->wcr = 0x00000000;  // 清除 WDOG 使能位
    }
    
  • 配置 GPIO 引脚复用:初始化关键外设的引脚功能(如调试串口 UART1、MMC 卡接口的引脚);
  • 设置早期时钟:为核心外设(如 UART、MMC)配置稳定的时钟源(依赖 i.MX6ULL 的 CCM 时钟控制器);
  • 初始化板级全局数据:设置 gd(global data,U-Boot 全局数据结构)中的板型信息(如 board_idboard_rev),供后续函数使用。

2. board_late_init():晚期板级初始化(外设完善)

执行时机:SDRAM 初始化完成后,U-Boot 命令解析器启动前。
核心作用:在 “核心硬件(内存、串口)就绪” 后,完善板载外设的配置,主要工作包括:

  • 初始化网络外设:配置以太网 PHY 芯片(如 Micrel KSZ8081)的地址、速率协商模式;
    int board_late_init(void) {// 配置网络 PHY 芯片(假设 PHY 地址为 0x01)struct phy_device *phy = phy_find_by_mask(PHY_ADDR, PHY_MASK);if (phy) {phy_config(phy, PHY_INTERFACE_MODE_RMII);  // 配置 RMII 接口模式}// 初始化板载 LED(示例:GPIO1_0 控制 LED)gpio_request(GPIO1_0, "led");gpio_direction_output(GPIO1_0, 1);  // 点亮 LEDreturn 0;
    }
    
  • 初始化显示 / USB 等扩展外设:若 EVK 板载 LCD 屏幕、USB 控制器,在此处配置引脚和驱动参数;
  • 动态调整环境变量:根据硬件版本自动修正环境变量(如根据板型设置 fdt_file 为 imx6ull-9x9-evk.dtb);
  • 初始化板载传感器 / 按键:配置板载按键(如复位键、用户键)的 GPIO 中断,或初始化 I2C 传感器(如温度传感器)。

3. checkboard():板型识别与信息打印

执行时机:U-Boot 启动时,在 board_init() 后执行。
核心作用:识别当前板型版本(如 i.MX6ULL 9x9 EVK 还是 14x14 EVK),并打印板级信息(供开发者调试确认):

int checkboard(void) {// 读取板载版本电阻(通过 GPIO 检测)u32 board_rev = gpio_get_value(GPIO2_10) | (gpio_get_value(GPIO2_11) << 1);// 打印板型信息printf("Board: MX6ULL EVK (Rev: %d.%d)\n", (board_rev >> 1) & 0x1, board_rev & 0x1);return 0;
}

输出示例:启动日志中会显示 Board: MX6ULL EVK (Rev: 1.0),帮助开发者确认硬件版本是否匹配。

4. sdram_init():SDRAM 初始化(部分版本包含)

执行时机board_init() 之后,SDRAM 首次使用前。
核心作用:配置 i.MX6ULL 的内存控制器(MMDC),初始化板载 SDRAM(如 256MB/512MB DDR3),主要工作包括:

  • 写入 SDRAM 时序参数(如行地址、列地址宽度,刷新周期);
  • 配置内存控制器的地址映射(如 SDRAM 物理地址范围 0x80000000~0x8FFFFFFF);
  • 检测 SDRAM 容量并更新 U-Boot 全局数据(gd->ram_size)。

注意:部分 U-Boot 版本会将 SDRAM 初始化逻辑整合到 cpu/armv7/mx6/sdram.c 中,mx6ullevk.c 仅通过宏定义传递板级参数(如 SDRAM 容量、时序)。

5. get_board_rev():获取硬件版本

执行时机:贯穿 U-Boot 启动流程,按需调用。
核心作用:读取板载硬件版本标识(如版本电阻、EEPROM 中的版本信息),返回版本号(如 0x01 表示 Rev 1.0),供其他函数(如 board_late_init()findfdt())适配不同硬件版本的配置

u32 get_board_rev(void) {// 从 EEPROM 读取版本信息(若板载 EEPROM)if (eeprom_read(EEPROM_ADDR, &board_rev, sizeof(board_rev)) == 0) {return board_rev;}// 若无 EEPROM,通过 GPIO 检测版本电阻return gpio_get_value(GPIO2_10) | (gpio_get_value(GPIO2_11) << 1);
}

关键数据结构与宏定义

mx6ullevk.c 中会定义与硬件强相关的宏和数据结构,用于简化代码并明确硬件参数:

  • 硬件基地址宏:如 UART1 基地址 #define UART1_BASE 0x02020000、GPIO 基地址 #define GPIO1_BASE 0x0209C000
  • 引脚定义宏:如 LED 控制引脚 #define LED_GPIO GPIO1_0、网络 PHY 复位引脚 #define PHY_RST_GPIO GPIO2_5
  • 外设参数宏:如网络 PHY 地址 #define PHY_ADDR 0x01、SDRAM 容量 #define PHYS_SDRAM_SIZE 0x10000000(256MB);
  • 寄存器结构体:如看门狗寄存器结构体 struct wdog_regs、GPIO 寄存器结构体 struct gpio_regs,用于便捷访问硬件寄存器。

与其他文件的关联

mx6ullevk.c 并非孤立存在,而是与 U-Boot 其他模块紧密协作:

  1. 依赖 config.h:通过 #include <config.h> 引入板级配置(如 CONFIG_SYS_BOOT_MMCCONFIG_FEC_MXC),决定初始化逻辑的分支(如 “是否初始化网络”);
  2. 调用芯片级驱动:通过 imx6ull_* 系列函数(如 imx6ull_clock_init()imx6ull_mmc_init())调用 i.MX6ULL 芯片级驱动,避免重复实现底层硬件操作;
  3. 配合设备树:初始化逻辑中会读取设备树(.dtb)中的硬件描述(如引脚分配、外设地址),或向设备树注入板级参数(如硬件版本),供 Linux 内核使用。

开发调试场景

在实际开发中,mx6ullevk.c 是最常修改的文件之一,典型场景包括:

  • 新增外设适配:若在 EVK 板上新增 I2C 传感器,需在 board_late_init() 中添加 I2C 初始化和传感器探测代码;
  • 硬件参数修正:若更换 SDRAM 型号(如从 256MB 改为 512MB),需修改 sdram_init() 中的时序参数和容量宏;
  • 调试硬件问题:若网络无法启动,可在 board_late_init() 中添加 PHY 寄存器读取代码(如 phy_read(phy, 0x01)),排查 PHY 芯片是否正常响应;
  • 定制板型适配:基于 EVK 做定制板时,需修改 board_init() 中的 GPIO 复用、时钟配置,确保与定制板的硬件一致。

总结

mx6ullevk.c 是 i.MX6ULL EVK 开发板在 U-Boot 中的 “硬件身份证”,其核心价值在于:

  1. 封装硬件细节:将复杂的硬件初始化逻辑(如寄存器配置、引脚复用)封装为 U-Boot 标准接口,避免通用代码与硬件强耦合;
  2. 保障启动稳定:通过早期初始化(禁用看门狗、配置时钟)确保 U-Boot 能安全过渡到 C 语言阶段,通过晚期初始化完善外设功能;
  3. 支持灵活定制:开发者可通过修改该文件快速适配硬件变更,无需改动 U-Boot 核心代码,大幅提升开发效率。

对于嵌入式开发者而言,理解 mx6ullevk.c 的逻辑是掌握 “U-Boot 适配硬件” 的关键,也是后续定制化开发(如做自己的 i.MX6ULL 板)的基础。

http://www.xdnf.cn/news/1401823.html

相关文章:

  • fastdds qos:LifespanQosPolicy
  • 【C++】类和对象(终章)
  • 第二十六天-待机唤醒实验
  • 信息系统架构
  • v-model ,在 vue3和 vue2中的区别
  • Linux(1)|入门的开始:Linux基本指令
  • 认识Redis
  • IDM手机端,速度能提高6倍!
  • CPU的MBR寄存器和MDR寄存器
  • 联合体和枚举——嵌入式学习笔记
  • Linux IO复用
  • 优选算法:二分查找
  • 数据库攻略:“CMU 15-445”Project0:C++ Primer(2024 Fall)
  • 《Java反射与动态代理:从原理到实践》
  • SpringBoot整合Actuator实现健康检查
  • MIT 6.5840 (Spring, 2024) 通关指南——Lab 1: MapReduce
  • GitHub 热榜项目 - 日榜(2025-08-30)
  • 基于Ubuntu本地GitLab 搭建 Git 服务器
  • 解构机器学习:如何从零开始设计一个学习系统?
  • 【LeetCode】大厂面试算法真题回忆(121) —— 经典屏保
  • 并发编程——09 CountDownLatch源码分析
  • Spring Boot 后端接收多个文件的方法
  • 项目管理常用的方法有哪些
  • 三菱 PLC的中断指令/中断指针
  • 构建现代化的“历史上的今天“网站:从API到精美UI的全栈实践
  • 北方苍鹰优化算法优化的最小二乘支持向量机NGO-LSSVM多输入多输出回归预测【MATLAB】
  • 2025年06月 Scratch 图形化(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • Robolectric如何启动一个Activity
  • 倾斜摄影是选择RGB图像还是多光谱影响进行操作?
  • Transformer:从入门到精通