3. i.MX启动流程与Bootloader移植

i.MX系列处理器的启动过程与通用ARM SoC有本质区别:芯片上电后并非直接执行外部Flash中的代码,而是由固化在片内ROM中的Boot ROM代码接管,按照预设的启动设备顺序依次尝试加载镜像。理解这一机制,是进行U-Boot移植和板级适配的前提。本节以i.MX6ULL/i.MX8M Mini工业网关平台为背景,梳理从Boot ROM到U-Boot、再到内核的完整链路,并给出可操作的移植与验证方法。

一、i.MX启动模式与Boot ROM加载机制

i.MX的启动模式由芯片的BOOT_MODE[1:0]引脚在上电复位时采样决定,常见取值包括内部Boot(从eFUSE或GPIO配置的启动设备加载)、串行下载(Serial Download,用于量产烧录和救砖)以及内部保留模式。以i.MX6ULL为例,当BOOT_MODE为内部Boot时,Boot ROM会读取eFUSE或GPIO配置的BOOT_CFG寄存器,得到启动设备优先级列表,典型顺序为:SD/eMMC → NAND → NOR → USB串行下载。

Boot ROM对启动设备的要求非常具体:它不识别文件系统,只读取设备固定偏移处的原始数据。对于SD/eMMC,Boot ROM读取LBA 2(偏移0x400字节)开始的第一个扇区,检查其中是否包含合法的IVT(Image Vector Table)和Boot Data结构。IVT是一个固定格式的头部,包含镜像入口地址、DCD(Device Configuration Data)指针、Boot Data指针以及自校验的CSF(Command Sequence File,用于HAB安全启动)指针。Boot Data则描述镜像在内存中的加载地址和总长度。

DCD是i.MX启动流程中最容易被忽视却最关键的部分。它是一段由Boot ROM解释执行的寄存器配置脚本,用于在加载U-Boot之前完成DDR控制器的初始化。因为U-Boot的代码段通常链接到DDR地址空间,如果DDR未初始化,Boot ROM将镜像拷贝到DDR后无法执行。DCD由一系列写命令组成,格式为“写寄存器地址 + 值”,例如配置IOMUXC的DDR引脚复用、MMDC的时序参数、PHY的校准寄存器等。DCD通常由NXP提供的DDR Stress Test工具或i.MX DDR Tool生成,再嵌入到U-Boot的SPL或镜像头部中。

i.MX6ULL 启动链路与镜像结构 Boot ROM 采样BOOT_MODE 读取BOOT_CFG 启动设备优先级 SD/eMMC → NAND → NOR → USB Serial Download 镜像头部结构(偏移0x400) IVT Boot Data DCD CSF DCD 执行:DDR初始化 • IOMUXC DDR引脚复用配置 • MMDC时序参数(tRCD/tRP/tRAS等) • DDR PHY校准与ZQ校准 U-Boot 加载与执行 从DDR入口地址跳转,启动SPL/Proper 初始化串口、时钟、外设,加载内核 启动设备选择判断 SD/eMMC 可启动? NAND 可启动? NOR 可启动? USB 串行下载 任一设备命中即加载,全部失败则进入USB下载 关键点:Boot ROM不识别文件系统,只读取固定偏移的原始镜像;DCD必须在U-Boot代码执行前完成DDR初始化

二、U-Boot源码结构与板级配置

U-Boot源码采用分层组织,与i.MX平台相关的关键目录包括:arch/arm/cpu/armv7/mx6/存放i.MX6系列的CPU初始化代码,arch/arm/include/asm/arch-mx6/存放寄存器定义和IVT/DCD相关头文件,board/freescale/或board/nxp/下存放各参考板的板级文件,configs/下存放各板子的defconfig配置文件。移植新板卡时,通常以NXP官方评估板(如mx6ull_14x14_evk)为模板,复制其目录和defconfig,再逐步修改。

板级配置的核心文件包括:mx6ull_xxx_defconfig定义编译时的宏开关,如CONFIG_TARGET_MX6ULL_XXX、CONFIG_DEFAULT_DEVICE_TREE、CONFIG_SYS_MMC_ENV_DEV等;board/nxp/mx6ull_xxx/下的xxx.c实现board_init()、board_mmc_init()、board_eth_init()等板级初始化函数;include/configs/mx6ull_xxx.h定义内存布局、环境变量默认值、启动参数等。设备树文件arch/arm/dts/mx6ull-xxx.dts描述硬件资源,U-Boot和Linux内核共用同一份DTS,但U-Boot阶段只启用必要外设。

以工业网关常见的双网口、eMMC、SPI Flash配置为例,defconfig中需要关注的关键项包括:CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/nxp/mx6ull_xxx/imximage.cfg"指定DCD配置文件路径;CONFIG_BOOTCOMMAND定义默认启动命令;CONFIG_ENV_IS_IN_MMC指定环境变量存储位置。板级C文件中,board_mmc_init()需要根据实际使用的USDHC控制器编号调用fsl_esdhc_initialize(),并配置对应的GPIO复位引脚。

三、DDR初始化与DCD配置实操

DDR初始化是移植中最容易出问题的环节。以i.MX6ULL搭配DDR3L(如MT41K256M16,容量512MB,16位位宽)为例,DCD配置需要完成以下寄存器序列:首先配置IOMUXC的DDR相关引脚为DDR功能,包括DRAM_SDCLK、DRAM_CAS、DRAM_RAS、DRAM_CS、DRAM_WE、DRAM_ADDR[15:0]、DRAM_DATA[15:0]等;然后配置MMDC的MDCTL、MDPDC、MDOTC、MDCFG0/1/2、MDMISC等寄存器,设置时序参数;最后配置MMDC_PHY的MPZQHWCTRL、MPWLGCR等寄存器,执行写电平校准和ZQ校准。

NXP提供的DDR Stress Test工具可以生成校准参数。实际操作中,先在评估板上运行该工具,得到一组可用的MMDC和PHY寄存器值,再将其转换为DCD格式写入imximage.cfg。DCD文件的基本语法为:

# imximage.cfg 片段示例
DATA 4 0x020C4068 0x00C03F3F   # CCM_CCGR0
DATA 4 0x020C406C 0x0030FC03   # CCM_CCGR1
DATA 4 0x020C4070 0x0FFFC000   # CCM_CCGR2
DATA 4 0x020C4074 0x3FF00000   # CCM_CCGR3
DATA 4 0x020C4078 0x00FFF300   # CCM_CCGR4
DATA 4 0x020C407C 0x0F0000C3   # CCM_CCGR5
DATA 4 0x020C4080 0x000003FF   # CCM_CCGR6
# MMDC 初始化
DATA 4 0x021B001C 0x00008000   # MMDC_MDSCR
DATA 4 0x021B000C 0x555A7975   # MMDC_MDCFG0
DATA 4 0x021B0010 0xFF538F64   # MMDC_MDCFG1
DATA 4 0x021B0014 0x01FF00DB   # MMDC_MDCFG2
DATA 4 0x021B0018 0x00011740   # MMDC_MDMISC
DATA 4 0x021B001C 0x00008000   # MMDC_MDSCR
DATA 4 0x021B002C 0x000026D2   # MMDC_MDOR
DATA 4 0x021B0030 0x005B0E21   # MMDC_MDMR
DATA 4 0x021B0008 0x1B333030   # MMDC_MDCTL
DATA 4 0x021B0004 0x0002002D   # MMDC_MDPDC
DATA 4 0x021B0040 0x00000027   # MMDC_MAPSR
DATA 4 0x021B0000 0x831A0000   # MMDC_MDCTL

上述参数仅为示例,实际值必须根据具体DDR颗粒的时序手册和校准结果确定。DCD执行完毕后,Boot ROM会将U-Boot镜像从启动设备拷贝到DDR的链接地址(如0x87800000),然后跳转执行。

四、启动参数设置与烧录验证

U-Boot启动后,通过串口控制台可以设置环境变量。工业网关常用的启动参数包括:bootargs设置内核命令行,如console=ttymxc0,115200 root=/dev/mmcblk1p2 rootwait rw;bootcmd定义自动启动流程,如mmc dev 1; fatload mmc 1:1 0x80800000 zImage; fatload mmc 1:1 0x83000000 imx6ull-xxx.dtb; bootz 0x80800000 - 0x83000000;ipaddr、serverip、gatewayip用于网络调试。环境变量默认值在include/configs/mx6ull_xxx.h中通过CONFIG_EXTRA_ENV_SETTINGS定义,首次启动时若eMMC中无有效环境变量,则使用编译时的默认值。

烧录验证通常使用dd命令或NXP的mfgtool。以SD卡烧录为例,在Linux主机上执行:

# 将U-Boot镜像写入SD卡偏移0x400处
sudo dd if=u-boot.imx of=/dev/sdb bs=512 seek=2 conv=fsync
# 验证写入
sudo dd if=/dev/sdb bs=512 skip=2 count=1 | hexdump -C | head -20

上电后观察串口输出,正常启动应依次看到Boot ROM版本信息、DCD执行提示(部分版本不打印)、U-Boot版本和编译时间、DRAM容量检测结果(如DRAM: 512 MiB)、MMC设备识别信息,最后进入U-Boot命令行或自动启动内核。若卡在SPL阶段无输出,优先检查DCD中DDR时序参数和电源轨;若U-Boot能启动但MMC识别失败,检查设备树中USDHC节点和GPIO复位配置;若内核启动后根文件系统挂载失败,检查bootargs中的root=参数与分区编号是否匹配。

验证DDR初始化是否成功的一个实用方法:在U-Boot命令行执行md 0x80000000 0x100读取DDR起始区域,再执行mw 0x80000000 0x12345678写入测试值,然后md 0x80000000 1回读。若回读值一致,说明DDR基本可用;若出现数据错误或系统挂起,则需重新校准DCD参数。