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 set
、gpio 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 的核心作用
硬件最小化初始化
嵌入式芯片(如 ARM 架构的 SoC)上电后,首先执行内部 ROM 中的代码(称为 Primary Program Loader,一级程序加载器),但 ROM 代码通常功能有限,只能完成最基础的初始化(如关闭看门狗、配置时钟和内存控制器等)。
SPL 则在此基础上进一步初始化关键硬件(如 SDRAM、存储控制器),为加载更大的主程序(如 U-Boot 主体)准备好运行环境。加载主引导程序
由于 ROM 空间有限(通常只有几 KB 到几十 KB),无法直接加载完整的 U-Boot(通常几百 KB 到几 MB)。
SPL 作为 “中间层”,会从存储设备(如 MMC 卡、NAND 闪存、SPI 闪存)中读取 U-Boot 主体镜像,并将其加载到 SDRAM 中运行。适配硬件限制
某些芯片的启动流程要求 “第一阶段引导程序” 必须小于特定尺寸(如 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 启动 > 网络启动,兼顾灵活性和可靠性。三、核心逻辑总结
- 场景适配:通过条件编译区分 NAND / 非 NAND 启动,为不同存储介质定制启动流程。
- 优先级设计:正常启动时,脚本启动 > MMC 启动 > 网络启动;生产模式则使用独立的
bootcmd_mfg
。- 自动化:通过
findfdt
自动匹配设备树,mmcautodetect
自动检测 MMC 设备,减少手动配置。- 兼容性:
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 启动流程中的角色:
- 芯片上电:首先执行芯片内部 ROM 代码(一级引导程序);
- ROM 代码:根据
BOOT_FROM
配置,从指定介质(SD/QSPI/NOR)读取 IMX 镜像;- 执行 DCD:ROM 代码解析镜像中的 DCD 数据,按
DATA
指令初始化硬件(时钟、SDRAM、引脚);- 加载 U-Boot:DCD 完成后,ROM 代码将 IMX 镜像中的 U-Boot 主体加载到 SDRAM,跳转到 U-Boot 入口执行;
- 后续启动:U-Boot 主体执行,继续初始化外设、加载内核。
总结
该文件的本质是 i.MX6ULL 启动的 “硬件初始化脚本”,核心价值在于:
- 填补 ROM 代码功能空白:芯片 ROM 代码仅能完成最基础的启动介质读取,DCD 则补充了时钟、内存、引脚等关键初始化,为 U-Boot 主体执行铺路;
- 硬件定制化入口:不同开发板的 SDRAM 时序、引脚复用可能不同,只需修改 DCD 中的寄存器地址和数值,即可适配不同硬件(无需改动 U-Boot 核心代码);
- 符合 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_id
、board_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 其他模块紧密协作:
- 依赖
config.h
:通过#include <config.h>
引入板级配置(如CONFIG_SYS_BOOT_MMC
、CONFIG_FEC_MXC
),决定初始化逻辑的分支(如 “是否初始化网络”);- 调用芯片级驱动:通过
imx6ull_*
系列函数(如imx6ull_clock_init()
、imx6ull_mmc_init()
)调用 i.MX6ULL 芯片级驱动,避免重复实现底层硬件操作;- 配合设备树:初始化逻辑中会读取设备树(
.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 中的 “硬件身份证”,其核心价值在于:
- 封装硬件细节:将复杂的硬件初始化逻辑(如寄存器配置、引脚复用)封装为 U-Boot 标准接口,避免通用代码与硬件强耦合;
- 保障启动稳定:通过早期初始化(禁用看门狗、配置时钟)确保 U-Boot 能安全过渡到 C 语言阶段,通过晚期初始化完善外设功能;
- 支持灵活定制:开发者可通过修改该文件快速适配硬件变更,无需改动 U-Boot 核心代码,大幅提升开发效率。
对于嵌入式开发者而言,理解
mx6ullevk.c
的逻辑是掌握 “U-Boot 适配硬件” 的关键,也是后续定制化开发(如做自己的 i.MX6ULL 板)的基础。