U-Boot 2026年8月31日 142 分钟

正点原子U-Boot移植过程深度解析

前言 本文档目标: 核心问题: 目录 1. 移植步骤总览 1.1 四大步骤概览 1.2 步骤对照表 步骤 操作 涉及文件...
本文目录 展开目录

前言

本文档目标:

  • 深度解析正点原子 I.MX6ULL-ALPHA 开发板的实际移植步骤
  • 解释每个操作的目的、原因和必要性
  • 将移植步骤映射到 U-Boot 启动流程,理解影响范围
  • 对比理论分析与实际移植的差异
  • 追踪每个修改在后续流程中的作用

核心问题:

  1. 为什么实际移植只需要 4 个步骤,而启动流程有那么多阶段?
  2. 这些配置文件的修改如何影响最终的启动行为?
  3. 移植工作的本质是什么?

目录

  1. 移植步骤总览
  2. 步骤一:添加开发板默认配置文件
  3. 步骤二:添加开发板头文件
  4. 步骤三:添加板级文件夹
  5. 步骤四:修改图形配置文件
  6. 配置系统工作原理
  7. 移植步骤与启动流程的映射
  8. 理论分析 vs 实际移植
  9. 完整影响链路追踪

1. 移植步骤总览

1.1 四大步骤概览

graph TD
    A[开始移植] --> B["步骤1: 创建 defconfig<br/>configs/<br/>mx6ull_alientek_emmc_defconfig"]
    B --> C["步骤2: 创建头文件<br/>include/configs/<br/>mx6ull_alientek_emmc.h"]
    C --> D["步骤3: 创建板级目录<br/>board/freescale/<br/>mx6ull_alientek_emmc/"]
    D --> E["步骤4: 修改 Kconfig<br/>arch/arm/cpu/armv7/<br/>mx6/Kconfig"]
    E --> F[配置完成]
    
    F --> G["执行 make<br/>mx6ull_alientek_emmc_defconfig"]
    G --> H[执行 make]
    H --> I[生成 u-boot.imx]
    
    style B fill:#ffd43b,stroke:#fab005
    style C fill:#ffd43b,stroke:#fab005
    style D fill:#ff6b6b,stroke:#c92a2a
    style E fill:#51cf66,stroke:#37b24d

1.2 步骤对照表

步骤操作涉及文件目的难度
1创建 defconfigconfigs/mx6ull_alientek_emmc_defconfig定义板级默认配置⭐⭐
2创建头文件include/configs/mx6ull_alientek_emmc.h定义编译时宏配置⭐⭐
3创建板级目录board/freescale/mx6ull_alientek_emmc/提供板级初始化代码和 DCD 表⭐⭐⭐⭐⭐
4修改 Kconfigarch/arm/cpu/armv7/mx6/Kconfig将新板集成到配置系统⭐⭐

1.3 移植本质

关键理解:

移植工作的本质是 配置基础设施的搭建,而非直接修改启动代码。

实际移植 ≠ 修改启动流程代码
实际移植 = 搭建配置框架 + 提供硬件参数

为什么?

  1. U-Boot 启动流程代码是通用的:_start、lowlevel_init、board_init_f 等函数由 NXP 提供,已经适配 i.MX6ULL 芯片
  2. 板级差异在于硬件配置:不同板子的 DDR 型号、引脚分配、外设配置不同
  3. 配置系统驱动代码选择:通过 Kconfig/defconfig 机制,选择编译哪些代码、调用哪些初始化函数

2. 步骤一:添加开发板默认配置文件

2.1 操作内容

文件: configs/mx6ull_alientek_emmc_defconfig

操作: 复制 mx6ull_14x14_evk_emmc_defconfig → 重命名 → 修改内容

修改后的内容:

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
CONFIG_CMD_GPIO=y

2.2 为什么这么做?

问题:defconfig 文件的作用是什么?

答案: defconfig 是板级配置的 入口文件,定义了这块板子编译 U-Boot 时的默认选项。

工作流程:

sequenceDiagram
    participant User as 用户
    participant Make as make 系统
    participant Kconfig as Kconfig 系统
    participant Config as .config 文件
    
    User->>Make: make mx6ull_alientek_emmc_defconfig
    Make->>Kconfig: 读取 configs/mx6ull_alientek_emmc_defconfig
    Kconfig->>Config: 生成 .config 文件
    Config->>Config: 展开所有依赖配置项
    User->>Make: make
    Make->>Config: 读取 .config
    Make->>Make: 根据配置选择编译哪些文件

2.3 逐行解析

第 1 行:CONFIG_SYS_EXTRA_OPTIONS

CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"

作用: 传递额外的配置选项,包含两个关键参数:

参数值作用
IMX_CONFIGboard/freescale/mx6ull_alientek_emmc/imximage.cfg指定 DCD 表文件路径
这是移植最关键的文件!
MX6ULL_EVK_EMMC_REWORK(宏定义)标识 eMMC 版本的硬件配置

IMX_CONFIG 的重要性:

graph LR
    A[IMX_CONFIG 指定] --> B[imximage.cfg]
    B --> C[DCD 表<br/>DDR 初始化寄存器配置]
    C --> D[BootROM 执行]
    D --> E{DDR 初始化成功?}
    E -->|是| F[加载 U-Boot 到 DDR<br/>✅ 启动成功]
    E -->|否| G[❌ 无法启动]
    
    style B fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
    style C fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px

影响链路:

defconfig 中的 IMX_CONFIG
    ↓
make 系统读取该路径
    ↓
调用 mkimage 工具处理 imximage.cfg
    ↓
将 DCD 表嵌入 u-boot.imx 镜像
    ↓
BootROM 加载镜像时执行 DCD 表
    ↓
初始化 DDR 控制器
    ↓
U-Boot 代码才能运行

为什么要修改这一行?

因为参考板 mx6ullevk 的 imximage.cfg 路径是:

board/freescale/mx6ullevk/imximage.cfg

我们的新板路径是:

board/freescale/mx6ull_alientek_emmc/imximage.cfg

必须修改路径,否则编译系统会找错文件!

第 2-3 行:架构配置

CONFIG_ARM=y
CONFIG_ARCH_MX6=y

作用: 指定处理器架构

  • CONFIG_ARM=y:这是 ARM 架构芯片
  • CONFIG_ARCH_MX6=y:具体是 NXP i.MX6 系列

影响: 决定编译系统包含哪些架构相关代码

CONFIG_ARM=y
    ↓
编译 arch/arm/ 目录下的代码
    ↓
CONFIG_ARCH_MX6=y
    ↓
编译 arch/arm/mach-imx/mx6/ 目录
    ↓
包含 i.MX6 特定的初始化代码

第 4 行:目标板配置(最关键)

CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y

作用: 这是新板的 唯一标识符

为什么这是最关键的配置?

这个宏会触发一系列连锁反应:

graph TD
    A[CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y] --> B[Kconfig 系统匹配]
    B --> C[arch/arm/cpu/armv7/mx6/Kconfig 中的配置块]
    C --> D[设置 CONFIG_SYS_BOARD=mx6ull_alientek_emmc]
    C --> E[设置 CONFIG_SYS_VENDOR=freescale]
    C --> F[设置 CONFIG_SYS_CONFIG_NAME=mx6ull_alientek_emmc]
    
    F --> G[自动 include 头文件<br/>include/configs/mx6ull_alientek_emmc.h]
    D --> H[指定板级目录<br/>board/freescale/mx6ull_alientek_emmc/]
    H --> I[编译该目录下的 .c 文件]
    I --> J[链接板级初始化函数]
    
    style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
    style F fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style H fill:#ffd43b,stroke:#fab005,stroke-width:2px

影响链路详解:

  1. 头文件自动包含


    // U-Boot 编译系统会自动生成:
    #include <configs/mx6ull_alientek_emmc.h>

  2. 板级目录自动编译


    # make 系统会编译:
    board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.o

  3. 板级函数自动调用


    // 启动流程中会调用板级函数:
    board_init_f() {
    // ...
    dram_init(); // ← 来自 mx6ull_alientek_emmc.c
    board_early_init_f(); // ← 来自 mx6ull_alientek_emmc.c
    // ...
    }

第 5 行:GPIO 命令支持

CONFIG_CMD_GPIO=y

作用: 启用 GPIO 命令,可以在 U-Boot 命令行中操作 GPIO

# U-Boot 命令行示例:
=> gpio status
=> gpio set 100
=> gpio clear 100

为什么需要? 调试硬件时很有用(测试 LED、按键等)

2.4 这个文件在后续的影响

编译时:

make mx6ull_alientek_emmc_defconfig
    ↓
读取 configs/mx6ull_alientek_emmc_defconfig
    ↓
生成 .config 文件
    ↓
展开所有 Kconfig 依赖
    ↓
确定编译哪些文件、包含哪些头文件

运行时:

BootROM 读取 u-boot.imx
    ↓
执行嵌入的 DCD 表(IMX_CONFIG 指定)
    ↓
初始化 DDR
    ↓
加载 U-Boot 代码到 DDR
    ↓
跳转到 _start(启动流程开始)

3. 步骤二:添加开发板头文件

3.1 操作内容

文件: include/configs/mx6ull_alientek_emmc.h

操作: 复制 include/configs/mx6ullevk.h → 重命名 → 修改头文件保护宏

修改内容:

// 原来:
#ifndef __MX6ULLEVK_CONFIG_H
#define __MX6ULLEVK_CONFIG_H

// 修改为:
#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H
#define __MX6ULL_ALIENTEK_EMMC_CONFIG_H

3.2 为什么这么做?

问题:板级头文件的作用是什么?

答案: 定义板级的 编译时配置宏,影响代码编译行为。

典型配置内容:

/* DDR 配置 */
#define PHYS_SDRAM_SIZE    SZ_512M   // 512MB DDR

/* 串口配置 */
#define CONFIG_MXC_UART_BASE   UART1_BASE

/* 网络配置 */
#define CONFIG_FEC_ENET_DEV    0
#define CONFIG_FEC_MXC_PHYADDR 0x1

/* 启动参数 */
#define CONFIG_BOOTCOMMAND \
    "mmc dev 1; " \
    "fatload mmc 1:1 0x80800000 zImage; " \
    "bootz 0x80800000"

3.3 头文件保护宏的重要性

为什么要修改头文件保护宏?

原因:防止头文件被重复包含

// 如果不修改,可能导致:
#include <configs/mx6ullevk.h>          // 定义 __MX6ULLEVK_CONFIG_H
#include <configs/mx6ull_alientek_emmc.h> // 也定义 __MX6ULLEVK_CONFIG_H(错误!)
// 第二个文件的内容会被跳过!

正确的保护机制:

#ifndef __MX6ULL_ALIENTEK_EMMC_CONFIG_H  // 首次包含:未定义,进入
#define __MX6ULL_ALIENTEK_EMMC_CONFIG_H  // 定义宏

/* 配置内容 */

#endif  // 第二次包含:已定义,跳过

3.4 这个文件如何被使用?

自动包含机制:

graph LR
    A[defconfig:<br/>CONFIG_SYS_CONFIG_NAME=<br/>mx6ull_alientek_emmc] --> B[Kconfig 系统]
    B --> C[自动生成<br/>CONFIG_SYS_CONFIG_NAME 宏]
    C --> D[include/config_defaults.h]
    D --> E["#include 宏拼接:<br/>configs/CONFIG_SYS_CONFIG_NAME.h"]
    E --> F[实际包含:<br/>configs/mx6ull_alientek_emmc.h]
    
    style A fill:#51cf66,stroke:#37b24d
    style F fill:#ff6b6b,stroke:#c92a2a

实际代码(U-Boot 编译系统):

// include/config_defaults.h(简化版)
#define __STRINGIFY(x) #x
#define STRINGIFY(x) __STRINGIFY(x)

#include <configs/STRINGIFY(CONFIG_SYS_CONFIG_NAME).h>
// 展开为:
// #include <configs/mx6ull_alientek_emmc.h>

3.5 这个文件在后续的影响

编译时影响:

每个 .c 文件编译时
    ↓
自动包含 include/configs/mx6ull_alientek_emmc.h
    ↓
所有宏定义生效
    ↓
影响条件编译(#ifdef)
    ↓
决定哪些代码被编译

运行时影响:

// 例如:DDR 大小检测
int dram_init(void) {
    gd->ram_size = PHYS_SDRAM_SIZE;  // ← 来自头文件的宏
    return 0;
}

// 例如:串口初始化
void uart_init(void) {
    base = (struct mxc_uart *)CONFIG_MXC_UART_BASE;  // ← 来自头文件的宏
    // ...
}

4. 步骤三:添加板级文件夹

4.1 操作内容

目录: board/freescale/mx6ull_alientek_emmc/

操作: 复制 board/freescale/mx6ullevk/ → 重命名 → 修改多个文件

修改的文件:

  1. Makefile
  2. imximage.cfg
  3. Kconfig
  4. MAINTAINERS
  5. mx6ullevk.c → mx6ull_alientek_emmc.c

4.2 为什么这是最关键的步骤?

答案:这个目录包含板级的 运行时代码 和 硬件初始化脚本

目录结构:

board/freescale/mx6ull_alientek_emmc/
├── Makefile                         # 编译规则
├── Kconfig                          # 配置定义
├── MAINTAINERS                      # 维护者信息
├── imximage.cfg                     # ⭐ DCD 表(DDR 初始化)
├── mx6ull_alientek_emmc.c           # ⭐ 板级初始化代码
└── plugin.bin                       # DDR 校准插件(可选)

4.3 修改 1:Makefile

原内容:

obj-y  := mx6ullevk.o

修改为:

obj-y  := mx6ull_alientek_emmc.o

作用: 告诉 make 系统编译 mx6ull_alientek_emmc.c

影响链路:

make
    ↓
读取 board/freescale/mx6ull_alientek_emmc/Makefile
    ↓
编译 mx6ull_alientek_emmc.c
    ↓
生成 mx6ull_alientek_emmc.o
    ↓
链接到 u-boot.elf
    ↓
运行时调用其中的 board_init()、dram_init() 等函数

4.4 修改 2:imximage.cfg(最关键!)

原内容:

PLUGIN board/freescale/mx6ullevk/plugin.bin 0x00907000

修改为:

PLUGIN board/freescale/mx6ull_alientek_emmc/plugin.bin 0x00907000

作用: 指定 DDR 校准插件的路径

⚠️ 注意:这个文件是整个移植最核心的部分!

imximage.cfg 的作用

这个文件包含 DCD(Device Configuration Data)表,是一系列寄存器配置指令:

/*
 * DCD Table - DDR 初始化
 */

/* 时钟配置 */
DATA 4 0x020c4018 0x00260324    /* CCM_ANALOG_PLL_DDR */

/* IOMUX 配置 - DDR 引脚 */
DATA 4 0x020e04b4 0x000C0000    /* DRAM_D0 */
DATA 4 0x020e04ac 0x000C0000    /* DRAM_D1 */
/* ... 更多引脚配置 ... */

/* MMDC 配置 - DDR 控制器 */
DATA 4 0x021b000c 0x33374133    /* MMDC_MDCFG0 - 时序 */
DATA 4 0x021b0010 0x00100B22    /* MMDC_MDCFG1 - 时序 */
DATA 4 0x021b0014 0x00000001    /* MMDC_MDCFG2 - 容量 */
/* ... 更多寄存器配置 ... */

/* 启用 DDR */
DATA 4 0x021b0000 0x831A0000    /* MMDC_MDCTL */

imximage.cfg 文件从哪里来?如何获取正确的 DCD 参数?

⭐ 这是最关键的问题!答案分 3 种情况:

情况 1:使用相同 DDR 芯片的参考板(最简单,推荐)

步骤:

  1. 找到使用相同 DDR 芯片的 NXP 官方参考板
  2. 直接复制其 imximage.cfg 文件
  3. 只需修改路径,内容保持不变

示例:

# 检查参考板使用的 DDR 芯片
# mx6ullevk 使用 MT41K256M16HA-125(256Mx16bit DDR3L)

# 如果你的板子也用相同的 DDR 芯片:
cp board/freescale/mx6ullevk/imximage.cfg \
   board/freescale/mx6ull_alientek_emmc/imximage.cfg

# ✅ 直接可用,不需要修改 DCD 参数!

判断是否相同芯片:

  • 查看你的硬件原理图(DDR 芯片型号)
  • 对比参考板原理图或用户手册
  • 型号完全相同 → 可以直接复用
情况 2:使用不同 DDR 芯片(需要工具生成)

推荐工具:NXP DDR Stress Test Tool(官方工具)

获取途径:

  • NXP 官网:https://www.nxp.com
  • 搜索:”i.MX DDR Stress Test Tool”
  • 需要注册 NXP 账号下载(免费)

使用步骤:

graph TD
    A[准备硬件信息] --> B[启动 DDR Stress Test Tool]
    B --> C[输入 DDR 芯片参数]
    C --> D[输入 PCB 设计参数]
    D --> E[工具自动计算]
    E --> F[生成 DCD 寄存器配置]
    F --> G[导出为 imximage.cfg 格式]
    G --> H[复制到板级目录]
    
    C --> C1[厂商: Micron/Samsung/...]
    C --> C2[型号: MT41K256M16HA]
    C --> C3[容量: 512MB]
    C --> C4[位宽: 16-bit]
    C --> C5[时序: DDR3-800]
    
    D --> D1[走线长度]
    D --> D2[阻抗匹配]
    D --> D3[拓扑结构]
    
    style A fill:#ffd43b
    style F fill:#ff6b6b
    style G fill:#51cf66

需要准备的信息:

信息类型来源示例
DDR 芯片型号硬件原理图MT41K256M16HA-125
容量DDR 芯片数据手册512MB (256M x 16bit x 2片)
位宽原理图(数据线数量)16-bit 或 32-bit
时序等级芯片数据手册DDR3-800 (PC3-6400)
时序参数芯片数据手册tRCD=13.75ns, tRP=13.75ns…
PCB 走线长度PCB 设计文件数据线约 50mm, 地址线约 60mm
ODT 电阻原理图120Ω

工具界面(简化示意):

┌─────── NXP DDR Stress Test Tool ───────┐
│                                          │
│ [1] 选择 SoC: i.MX6ULL                  │
│                                          │
│ [2] DDR 配置:                            │
│     厂商: Micron ▼                       │
│     型号: MT41K256M16HA-125             │
│     容量: 512MB (256Mx16 x2)            │
│     位宽: 16-bit                         │
│     类型: DDR3L                          │
│                                          │
│ [3] 时序参数:                            │
│     时钟: 396MHz (DDR3-800)             │
│     CL: 6   tRCD: 14   tRP: 14          │
│     tRAS: 35   tRC: 49   tRFC: 160      │
│                                          │
│ [4] PCB 参数:                            │
│     数据线长度: 50mm                     │
│     地址线长度: 60mm                     │
│     ODT: 120Ω                            │
│                                          │
│ [生成配置] [运行测试] [导出 DCD]         │
└──────────────────────────────────────────┘

生成的 DCD 配置示例:

/* Generated by NXP DDR Stress Test Tool */
/* DDR: MT41K256M16HA-125, 512MB, DDR3-800 */

DATA 4 0x020c4018 0x00260324    /* PLL: 396MHz */
DATA 4 0x020e04b4 0x000C0000    /* IOMUX: DRAM_D0 */
/* ... 数百行寄存器配置 ... */
DATA 4 0x021b0000 0x831A0000    /* MMDC_MDCTL: Enable */
情况 3:无工具手动计算(不推荐,容易出错)

仅在无法获取工具时使用,步骤:

  1. 阅读芯片手册 – i.MX6ULL 参考手册(3000+ 页)
  2. 阅读 DDR 芯片数据手册 – 获取时序参数
  3. 手动计算寄存器值

计算示例(仅展示思路):

假设:
- DDR 时钟 396MHz → 周期 2.53ns
- DDR 芯片要求 tRCD = 13.75ns

计算 MMDC_MDCFG0.tRCD 字段:
tRCD_cycles = ceil(13.75ns / 2.53ns) = ceil(5.43) = 6
寄存器配置:MMDC_MDCFG0[29:26] = 6

类似计算所有时序参数...

⚠️ 警告:这种方法:

  • 需要深入理解 DDR 原理
  • 容易出错(一个参数错误就无法启动)
  • 耗时长(需要计算几十个参数)
  • 强烈建议使用官方工具!

实战:不同 DDR 配置的修改点

场景:从 512MB DDR 改为 256MB DDR

需要修改的寄存器(imximage.cfg):

/* 1. 修改容量配置 */
/* MMDC_MDCTL - 控制寄存器 */
/* 原配置(512MB):Row=15, Col=10, Bank=8 */
DATA 4 0x021b0000 0x831A0000

/* 新配置(256MB):Row=14, Col=10, Bank=8 */
DATA 4 0x021b0000 0x83190000
/*                    ^^
 *                    Row 地址位数减 1
 */

/* 2. 其他时序参数可能不需要改(如果 DDR 芯片速度等级相同)*/

同时修改头文件:

// include/configs/mx6ull_alientek_emmc.h
// 原配置:
#define PHYS_SDRAM_SIZE    SZ_512M

// 新配置:
#define PHYS_SDRAM_SIZE    SZ_256M

⚠️ 注意:

  • imximage.cfg 配置错误 → 硬件层面 DDR 无法初始化 → 完全无法启动
  • 头文件配置错误 → U-Boot 认为 DDR 比实际大 → 访问越界崩溃

DCD 参数验证方法

生成配置后,必须验证!

方法 1:DDR Stress Test(工具内置)

# 工具会生成测试脚本,通过 JTAG 在真实硬件上测试
# 测试内容:
- 写入数据模式(0x5555, 0xAAAA, 0xFF00...)
- 回读验证
- 压力测试(满速读写)
- 温度测试(如果有传感器)

# 全部通过 → DCD 配置正确

方法 2:U-Boot 内存测试命令

# U-Boot 命令行
=> mtest 0x80000000 0x90000000
Pattern 00000000  Writing...  Reading...
Pattern FFFFFFFF  Writing...  Reading...
Pattern 55555555  Writing...  Reading...
# Ctrl+C 停止

# 无错误 → DDR 工作正常

方法 3:示波器验证(硬件工程师)

  • 测量 DDR 时钟信号质量
  • 检查数据眼图
  • 验证建立时间和保持时间

DCD 表的执行时机

sequenceDiagram
    participant ROM as BootROM
    participant Flash as Flash/eMMC
    participant DDR as DDR SDRAM
    participant CPU as CPU
    
    Note over ROM: 芯片上电
    ROM->>Flash: 读取 u-boot.imx 文件头
    Flash-->>ROM: 返回 IVT(镜像向量表)
    ROM->>Flash: 读取 DCD 表(imximage.cfg 生成)
    Flash-->>ROM: 返回 DCD 表数据
    
    Note over ROM: ⬇️ 关键时刻!
    ROM->>DDR: 执行 DCD 表中的寄存器配置
    Note over DDR: DDR 控制器初始化完成
    
    ROM->>Flash: 读取 U-Boot 代码
    Flash-->>ROM: 返回 U-Boot 镜像
    ROM->>DDR: 将 U-Boot 代码加载到 DDR
    ROM->>CPU: 跳转到 _start(DDR 中的 U-Boot 代码)
    
    Note over CPU: U-Boot 启动流程开始

关键点:

  1. DCD 表在 U-Boot 代码运行之前 执行
  2. 由 BootROM(芯片内部固化代码)执行,不是 U-Boot 执行
  3. 如果 DCD 配置错误 → DDR 无法初始化 → U-Boot 无法加载 → 完全无法启动

4.5 修改 3:Kconfig

原内容:

if TARGET_MX6ULL_14X14_EVK

config SYS_BOARD
    default "mx6ull_14x14_evk"

config SYS_VENDOR
    default "freescale"

config SYS_CONFIG_NAME
    default "mx6ull_14x14_evk"

endif

修改为:

if TARGET_MX6ULL_ALIENTEK_EMMC

config SYS_BOARD
    default "mx6ull_alientek_emmc"

config SYS_VENDOR
    default "freescale"

config SYS_SOC
    default "mx6"

config SYS_CONFIG_NAME
    default "mx6ull_alientek_emmc"

endif

作用: 定义板级配置变量

影响链路:

defconfig 中的 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
    ↓
Kconfig 系统匹配到这个 if 块
    ↓
设置默认值:
    CONFIG_SYS_BOARD = "mx6ull_alientek_emmc"
    CONFIG_SYS_VENDOR = "freescale"
    CONFIG_SYS_CONFIG_NAME = "mx6ull_alientek_emmc"
    ↓
make 系统使用这些变量:
    - 编译 board/$(CONFIG_SYS_VENDOR)/$(CONFIG_SYS_BOARD)/
    - 包含 include/configs/$(CONFIG_SYS_CONFIG_NAME).h

4.6 修改 4:MAINTAINERS

原内容:

MX6ULL_14X14_EVK BOARD
M:    Peng Fan <peng.fan@nxp.com>
S:    Maintained
F:    board/freescale/mx6ull_14x14_evk/
F:    include/configs/mx6ull_14x14_evk.h
F:    configs/mx6ull_14x14_evk_defconfig

修改为:

MX6ULL_ALIENTEK_EMMC BOARD
M:    Peng Fan <peng.fan@nxp.com>
S:    Maintained
F:    board/freescale/mx6ull_alientek_emmc/
F:    include/configs/mx6ull_alientek_emmc.h
F:    configs/mx6ull_alientek_emmc_defconfig

作用: 声明维护者和相关文件

重要性: 不影响编译,但用于:

  • get_maintainer.pl 脚本查找维护者
  • Git 历史追踪
  • 代码审查

4.7 这个目录在后续的影响

编译时:

make
    ↓
编译 board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
    ↓
生成 mx6ull_alientek_emmc.o
    ↓
链接到最终的 u-boot.elf

运行时(DDR 初始化):

BootROM 读取 u-boot.imx
    ↓
解析 IVT(镜像向量表)
    ↓
读取 DCD 表(imximage.cfg 生成)
    ↓
执行 DCD 表中的寄存器写操作
    ↓
DDR 控制器初始化完成

运行时(板级初始化):

U-Boot 启动流程
    ↓
board_init_f()
    ├── dram_init()                  ← 来自 mx6ull_alientek_emmc.c
    └── board_early_init_f()         ← 来自 mx6ull_alientek_emmc.c
    ↓
board_init_r()
    ├── board_init()                 ← 来自 mx6ull_alientek_emmc.c
    └── board_late_init()            ← 来自 mx6ull_alientek_emmc.c

5. 步骤四:修改图形配置文件

5.1 操作内容

文件: arch/arm/cpu/armv7/mx6/Kconfig(或 arch/arm/Kconfig)

操作: 添加新板的配置选项

添加内容(第 207 行):

config TARGET_MX6ULL_ALIENTEK_EMMC
    bool "Support mx6ull_alientek_emmc"
    select MX6ULL
    select DM
    select DM_THERMAL

添加内容(最后一行 endif 之前):

source "board/freescale/mx6ull_alientek_emmc/Kconfig"

5.2 为什么这么做?

问题:这个 Kconfig 文件的作用是什么?

答案: 这是 顶层 Kconfig,定义了所有可选的板型,用于图形化配置界面(make menuconfig)

图形界面示例:

┌─────────── U-Boot Configuration ──────────┐
│  Arrow keys navigate the menu.            │
│  <Enter> selects submenus --->.           │
│                                            │
│  Target Architecture (ARM)  --->          │
│  System Type (Freescale i.MX6ULL)  --->   │
│  Target Board  --->                        │
│    [ ] mx6ull_14x14_evk                   │
│    [*] mx6ull_alientek_emmc           ← 新增选项 │
│                                            │
└────────────────────────────────────────────┘

5.3 添加配置选项

config TARGET_MX6ULL_ALIENTEK_EMMC
    bool "Support mx6ull_alientek_emmc"
    select MX6ULL
    select DM
    select DM_THERMAL

逐行解析:

行内容作用
config TARGET_MX6ULL_ALIENTEK_EMMC配置项名称对应 defconfig 中的 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
bool "Support mx6ull_alientek_emmc"布尔类型选项在 menuconfig 中显示的文字
select MX6ULL自动选择 MX6ULL启用 i.MX6ULL 芯片特定代码
select DM自动选择 Driver Model启用驱动模型框架
select DM_THERMAL自动选择温度管理启用温度传感器驱动

select 的作用:

graph TD
    A[用户选择<br/>TARGET_MX6ULL_ALIENTEK_EMMC] --> B[自动启用 MX6ULL]
    B --> C[编译 arch/arm/mach-imx/mx6/ 代码]
    A --> D[自动启用 DM]
    D --> E[使用驱动模型框架]
    A --> F[自动启用 DM_THERMAL]
    F --> G[编译 drivers/thermal/ 代码]
    
    style A fill:#ff6b6b,stroke:#c92a2a

5.4 添加 source 语句

source "board/freescale/mx6ull_alientek_emmc/Kconfig"

作用: 引入板级 Kconfig 文件

为什么需要?

顶层 Kconfig
    ├── 定义 TARGET_MX6ULL_ALIENTEK_EMMC 选项
    └── source "board/.../Kconfig"  ← 引入板级配置
            ↓
板级 Kconfig (board/freescale/mx6ull_alientek_emmc/Kconfig)
    ├── if TARGET_MX6ULL_ALIENTEK_EMMC
    ├──     config SYS_BOARD
    ├──         default "mx6ull_alientek_emmc"
    ├──     config SYS_CONFIG_NAME
    ├──         default "mx6ull_alientek_emmc"
    └── endif

完整的配置链:

用户执行: make mx6ull_alientek_emmc_defconfig
    ↓
Kconfig 系统处理:
    1. 读取 configs/mx6ull_alientek_emmc_defconfig
    2. 找到 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
    3. 在 arch/arm/cpu/armv7/mx6/Kconfig 中匹配
    4. source 引入 board/freescale/mx6ull_alientek_emmc/Kconfig
    5. 设置 SYS_BOARD、SYS_CONFIG_NAME 等变量
    6. 展开所有依赖(MX6ULL、DM、DM_THERMAL...)
    7. 生成最终的 .config 文件

5.5 这个修改在后续的影响

配置时:

$ make menuconfig
# 可以在图形界面中看到并选择 "mx6ull_alientek_emmc"

编译时:

make 系统根据 .config
    ↓
CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
    ↓
编译系统知道:
    - 要编译哪个板级目录
    - 要包含哪个头文件
    - 要启用哪些驱动

6. 配置系统工作原理

6.1 配置系统全景图

graph TD
    A[configs/xxx_defconfig<br/>板级默认配置] --> B[make xxx_defconfig]
    B --> C[Kconfig 系统处理]
    
    D[arch/arm/.../Kconfig<br/>顶层配置] --> C
    E[board/.../Kconfig<br/>板级配置] --> C
    F[drivers/.../Kconfig<br/>驱动配置] --> C
    
    C --> G[.config<br/>完整配置文件]
    G --> H[include/autoconf.mk<br/>Makefile 可用]
    G --> I[include/config.h<br/>C 代码可用]
    
    H --> J[make 编译]
    I --> J
    
    J --> K{选择编译哪些文件}
    K --> L[编译 board/xxx/]
    K --> M[编译 drivers/xxx/]
    K --> N[编译 arch/xxx/]
    
    L --> O[链接生成 u-boot.elf]
    M --> O
    N --> O
    
    O --> P[mkimage 处理]
    P --> Q[生成 u-boot.imx<br/>包含 DCD 表]
    
    style A fill:#ffd43b,stroke:#fab005
    style G fill:#ff6b6b,stroke:#c92a2a
    style Q fill:#51cf66,stroke:#37b24d

6.2 Kconfig 依赖关系

以 CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC 为例:

# arch/arm/cpu/armv7/mx6/Kconfig
config TARGET_MX6ULL_ALIENTEK_EMMC
    bool "Support mx6ull_alientek_emmc"
    select MX6ULL              # ← 依赖 1
    select DM                  # ← 依赖 2
    select DM_THERMAL          # ← 依赖 3

展开依赖链:

graph TD
    A[CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC] --> B[CONFIG_MX6ULL]
    B --> C[CONFIG_MX6]
    C --> D[CONFIG_ARCH_MX6]
    D --> E[CONFIG_ARM]
    
    A --> F[CONFIG_DM]
    F --> G[CONFIG_DM_SERIAL]
    F --> H[CONFIG_DM_GPIO]
    F --> I[CONFIG_DM_MMC]
    
    A --> J[CONFIG_DM_THERMAL]
    J --> K[编译 drivers/thermal/]
    
    style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px

最终生成的 .config 文件(部分):

CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_MX6=y
CONFIG_MX6ULL=y
CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
CONFIG_SYS_VENDOR="freescale"
CONFIG_SYS_CONFIG_NAME="mx6ull_alientek_emmc"
CONFIG_DM=y
CONFIG_DM_SERIAL=y
CONFIG_DM_GPIO=y
CONFIG_DM_THERMAL=y
# ... 数百个配置项 ...

6.3 配置如何影响编译

机制 1:条件编译

Makefile 中的条件编译:

# drivers/Makefile
obj-$(CONFIG_DM) += core/
obj-$(CONFIG_DM_THERMAL) += thermal/
obj-$(CONFIG_DM_GPIO) += gpio/

# 展开后(如果 CONFIG_DM=y):
# obj-y += core/
# obj-y += thermal/
# obj-y += gpio/

C 代码中的条件编译:

// board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
int board_init(void)
{
    /* ... */
    
#ifdef CONFIG_FEC_MXC
    setup_fec();  // 只有启用网络时才编译
#endif

#ifdef CONFIG_USB_EHCI_MX6
    setup_usb();  // 只有启用 USB 时才编译
#endif

    return 0;
}

机制 2:自动变量替换

CONFIG_SYS_BOARD 的使用:

# top Makefile
BOARDDIR = board/$(CONFIG_SYS_VENDOR)/$(CONFIG_SYS_BOARD)

# 展开后:
# BOARDDIR = board/freescale/mx6ull_alientek_emmc

# 编译规则:
$(BOARDDIR)/built-in.o: $(BOARDDIR)/*.c
    $(CC) -c $< -o $@

机制 3:头文件自动包含

CONFIG_SYS_CONFIG_NAME 的使用:

// include/config_defaults.h(简化)
#include <configs/CONFIG_SYS_CONFIG_NAME.h>

// 预处理后:
#include <configs/mx6ull_alientek_emmc.h>

6.4 配置、编译、运行的完整流程

sequenceDiagram
    participant User as 用户
    participant Make as Make 系统
    participant Kconfig as Kconfig
    participant GCC as 编译器
    participant Linker as 链接器
    participant mkimage as mkimage
    participant BootROM as BootROM
    
    User->>Make: make mx6ull_alientek_emmc_defconfig
    Make->>Kconfig: 读取 defconfig
    Kconfig->>Kconfig: 解析所有 Kconfig 文件
    Kconfig->>Kconfig: 展开依赖关系
    Kconfig->>Make: 生成 .config
    
    User->>Make: make
    Make->>Make: 读取 .config
    Make->>Make: 确定编译哪些目录和文件
    
    Make->>GCC: 编译 board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
    GCC-->>Make: mx6ull_alientek_emmc.o
    
    Make->>GCC: 编译其他文件 (arch/arm/..., drivers/..., ...)
    GCC-->>Make: 大量 .o 文件
    
    Make->>Linker: 链接所有 .o 文件
    Linker-->>Make: u-boot.elf
    
    Make->>mkimage: 处理 u-boot.elf + imximage.cfg
    mkimage->>mkimage: 嵌入 DCD 表
    mkimage-->>Make: u-boot.imx
    
    Note over User: 烧录 u-boot.imx 到板子
    
    BootROM->>BootROM: 芯片上电
    BootROM->>BootROM: 读取 u-boot.imx
    BootROM->>BootROM: 执行 DCD 表(初始化 DDR)
    BootROM->>BootROM: 加载 U-Boot 代码到 DDR
    BootROM->>BootROM: 跳转到 _start
    
    Note over BootROM: U-Boot 开始运行

7. 移植步骤与启动流程的映射

7.1 关键问题

问题:移植只修改了配置文件,为什么能影响启动流程?

答案:配置决定了启动流程中调用哪些板级函数,以及这些函数的行为。

7.2 启动流程回顾

完整的启动流程(来自调用关系图):

BootROM(芯片内部)
    ↓ 执行 DCD 表(imximage.cfg)
    ↓ 初始化 DDR
    ↓ 加载 U-Boot 到 DDR
    ↓
_start(arch/arm/cpu/armv7/start.S)
    ↓
reset
    ↓
cpu_init_cp15
    ↓
cpu_init_crit
    ↓
lowlevel_init(arch/arm/cpu/armv7/lowlevel_init.S)
    ↓
_main(arch/arm/lib/crt0.S)
    ↓
board_init_f(common/board_f.c)
    ├── init_sequence_f[] 数组中的函数
    ├── arch_cpu_init()
    ├── board_early_init_f()   ← 板级函数
    ├── dram_init()            ← 板级函数
    └── ...
    ↓
relocate_code(arch/arm/lib/relocate.S)
    ↓ 重定位到 DDR 高地址
    ↓
board_init_r(common/board_r.c)
    ├── init_sequence_r[] 数组中的函数
    ├── board_init()           ← 板级函数
    ├── board_late_init()      ← 板级函数
    └── ...
    ↓
main_loop(common/main.c)
    ↓ 执行 bootcmd 启动 Linux

7.3 移植步骤到启动流程的映射

graph TD
    subgraph "移植步骤"
        A1[步骤1: defconfig<br/>IMX_CONFIG 指定 DCD 表路径]
        A2[步骤2: 头文件<br/>定义宏配置]
        A3[步骤3: 板级目录<br/>imximage.cfg<br/>板级代码 .c]
        A4[步骤4: Kconfig<br/>配置选项]
    end
    
    subgraph "启动流程"
        B1[BootROM<br/>执行 DCD 表]
        B2[_start → lowlevel_init]
        B3[board_init_f<br/>dram_init<br/>board_early_init_f]
        B4[重定位]
        B5[board_init_r<br/>board_init<br/>board_late_init]
        B6[main_loop]
    end
    
    A1 --> B1
    A3 --> B1
    A2 --> B3
    A3 --> B3
    A2 --> B5
    A3 --> B5
    
    B1 --> B2
    B2 --> B3
    B3 --> B4
    B4 --> B5
    B5 --> B6
    
    style A3 fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
    style B1 fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px

7.4 详细映射表

移植步骤影响的启动阶段具体影响代码位置
defconfig: IMX_CONFIGBootROM 阶段指定 DCD 表路径 → BootROM 执行 DCD 表初始化 DDRBootROM 内部
imximage.cfgBootROM 阶段DCD 表内容 → DDR 控制器寄存器配置BootROM 内部
头文件: PHYS_SDRAM_SIZEboard_init_f定义 DDR 大小 → dram_init() 返回值common/board_f.c:dram_init()
头文件: CONFIG_MXC_UART_BASEboard_init_f定义串口基地址 → serial_init() 初始化common/board_f.c:serial_init()
板级代码: board_early_init_f()board_init_f早期初始化(IOMUX、时钟)board/freescale/.../xxx.c
板级代码: dram_init()board_init_fDDR 大小检测board/freescale/.../xxx.c
板级代码: board_init()board_init_r引脚配置、外设初始化board/freescale/.../xxx.c
板级代码: board_late_init()board_init_r环境变量设置board/freescale/.../xxx.c
头文件: CONFIG_BOOTCOMMANDmain_loop默认启动命令common/main.c:main_loop()

7.5 配置到运行的完整链路

链路 1:DDR 初始化(最关键)

sequenceDiagram
    participant defconfig as defconfig 文件
    participant make as make 系统
    participant mkimage as mkimage 工具
    participant imx as u-boot.imx
    participant rom as BootROM
    participant ddr as DDR 控制器
    
    defconfig->>defconfig: IMX_CONFIG=.../imximage.cfg
    defconfig->>make: make 读取配置
    make->>mkimage: 调用 mkimage 处理 imximage.cfg
    mkimage->>mkimage: 解析 DCD 表(寄存器配置)
    mkimage->>imx: 将 DCD 表嵌入镜像头部
    
    Note over imx: 烧录到板子
    
    rom->>imx: 芯片上电,BootROM 读取镜像
    rom->>rom: 解析 IVT(镜像向量表)
    rom->>rom: 找到 DCD 表地址
    rom->>ddr: 执行 DCD 表(写寄存器)
    ddr->>ddr: DDR 控制器初始化完成
    
    Note over ddr: U-Boot 可以加载到 DDR 了

关键代码路径:

configs/mx6ull_alientek_emmc_defconfig
    CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_alientek_emmc/imximage.cfg,..."
        ↓
Makefile 处理
        ↓
u-boot.imx 目标规则:
    $(Q)$(MAKE) $(build)=arch/arm/mach-imx $(objtree)/u-boot.imx
        ↓
arch/arm/mach-imx/Makefile:
    $(IMX_CONFIG) → board/freescale/mx6ull_alientek_emmc/imximage.cfg
        ↓
调用 mkimage:
    mkimage -n $(IMX_CONFIG) -T imximage -e $(CONFIG_SYS_TEXT_BASE) -d u-boot.bin u-boot.imx
        ↓
mkimage 读取 imximage.cfg:
    DATA 4 0x021b000c 0x33374133
    DATA 4 0x021b0010 0x00100B22
    ...
        ↓
嵌入 DCD 表到 u-boot.imx 头部
        ↓
BootROM 执行 DCD 表初始化 DDR

链路 2:板级函数调用

sequenceDiagram
    participant Kconfig as Kconfig 系统
    participant config as .config
    participant make as Make
    participant gcc as GCC
    participant board_f as board_init_f()
    participant dram_init as dram_init()
    
    Kconfig->>config: CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
    Kconfig->>config: CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
    
    make->>gcc: 编译 board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
    gcc->>gcc: 生成 mx6ull_alientek_emmc.o
    gcc->>gcc: 包含符号: dram_init, board_init, board_late_init
    
    make->>make: 链接 mx6ull_alientek_emmc.o 到 u-boot.elf
    
    Note over board_f: U-Boot 运行时
    
    board_f->>board_f: init_sequence_f[] 数组
    board_f->>dram_init: 调用 dram_init()
    dram_init->>dram_init: gd->ram_size = imx_ddr_size()
    dram_init-->>board_f: 返回 DDR 大小

关键代码:

1. 配置决定编译哪个文件:

# board/freescale/mx6ull_alientek_emmc/Makefile
obj-y := mx6ull_alientek_emmc.o
# make 系统会编译这个文件

2. 板级代码提供函数实现:

// board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
int dram_init(void)
{
    gd->ram_size = imx_ddr_size();
    return 0;
}

int board_init(void)
{
    /* 设置引脚、初始化外设 */
    setup_iomux_uart();
    setup_fec();
    return 0;
}

3. 启动流程调用板级函数:

// common/board_f.c
static init_fnc_t init_sequence_f[] = {
    // ...
    dram_init,           // ← 调用板级提供的 dram_init()
    // ...
};

void board_init_f(ulong boot_flags)
{
    for (init_fnc_t *init_fnc_ptr = init_sequence_f; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0)
            hang();
    }
}

链路 3:头文件宏配置

graph LR
    A[defconfig:<br/>CONFIG_SYS_CONFIG_NAME=<br/>mx6ull_alientek_emmc] --> B[自动包含头文件]
    B --> C[include/configs/<br/>mx6ull_alientek_emmc.h]
    C --> D[定义宏:<br/>PHYS_SDRAM_SIZE<br/>CONFIG_MXC_UART_BASE<br/>CONFIG_BOOTCOMMAND]
    D --> E[板级代码使用宏]
    E --> F[dram_init 返回 PHYS_SDRAM_SIZE]
    E --> G[serial_init 使用 UART_BASE]
    E --> H[main_loop 执行 BOOTCOMMAND]
    
    style C fill:#ffd43b,stroke:#fab005

代码示例:

// include/configs/mx6ull_alientek_emmc.h
#define PHYS_SDRAM_SIZE    SZ_512M   // 512MB DDR

// board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
int dram_init(void)
{
    gd->ram_size = PHYS_SDRAM_SIZE;  // ← 使用头文件定义的宏
    return 0;
}

8. 理论分析 vs 实际移植

8.1 核心问题

用户的疑问:

“为什么它不按照我们分析的那样进行移植工作?”

分析的启动流程:

  • _start → reset → cpu_init_cp15 → cpu_init_crit → lowlevel_init → _main → board_init_f → relocate_code → board_init_r → main_loop

实际移植步骤:

  1. 创建 defconfig
  2. 创建头文件
  3. 创建板级目录
  4. 修改 Kconfig

看起来完全不相关!为什么?

8.2 答案:两个不同的层次

graph TD
    subgraph "理论层:代码执行流程"
        A1[_start]
        A2[reset]
        A3[lowlevel_init]
        A4[board_init_f]
        A5[board_init_r]
        A6[main_loop]
        A1 --> A2 --> A3 --> A4 --> A5 --> A6
    end
    
    subgraph "实践层:配置基础设施"
        B1[defconfig]
        B2[头文件]
        B3[板级目录]
        B4[Kconfig]
        B1 --> B2 --> B3 --> B4
    end
    
    subgraph "连接层:编译系统"
        C1[make 系统]
        C2[条件编译]
        C3[链接规则]
    end
    
    B1 --> C1
    B2 --> C2
    B3 --> C3
    B4 --> C1
    
    C1 --> A4
    C2 --> A4
    C3 --> A4
    C1 --> A5
    C2 --> A5
    C3 --> A5
    
    style A4 fill:#ff6b6b,stroke:#c92a2a
    style B3 fill:#ff6b6b,stroke:#c92a2a

8.3 详细对比

对比维度理论分析(启动流程)实际移植(配置)关系
关注点代码如何执行配置如何搭建配置决定执行路径
层次运行时编译时编译时配置影响运行时行为
核心问题CPU 如何从复位到运行如何告诉编译系统编译什么配置选择代码
文件类型汇编、C 代码配置文件、Makefile配置驱动代码编译
修改内容通常不修改通用代码创建板级配置和代码板级代码插入通用流程
SoC 相关性通用(NXP 提供)板级特定板级覆盖通用默认值

8.4 为什么不直接修改启动流程代码?

原因 1:启动流程代码是通用的

i.MX6ULL 芯片的所有板子都用相同的启动流程:

// arch/arm/cpu/armv7/start.S - 所有 i.MX6ULL 板共用
_start:
    b   reset
    // ...

reset:
    // ...
    bl  cpu_init_cp15
    bl  cpu_init_crit
    // ...

这些代码由 NXP 提供,已经完美适配 i.MX6ULL 芯片,不需要修改。

原因 2:板级差异在配置和数据,不在流程

不同板子的差异:

差异项i.MX6ULL EVK正点原子板差异类型
DDR 型号MT41K256M16HAMT41K256M16HA可能不同(需要 DCD 表)
DDR 大小512MB512MB配置参数
串口UART1UART1配置参数
网卡KSZ8081LAN8720A引脚配置
LED GPIOGPIO1_IO03GPIO1_IO04引脚配置

差异体现在:

  • DCD 表(DDR 初始化参数)
  • IOMUX 配置(引脚复用)
  • 外设初始化参数(PHY 地址、GPIO 编号等)

这些都是数据,不是流程逻辑!

原因 3:面向对象的思想

U-Boot 启动流程使用了”面向对象”的设计:

// 基类(通用流程) - common/board_f.c
void board_init_f(ulong boot_flags)
{
    // 通用初始化流程
    for (init_fnc_t *init_fnc_ptr = init_sequence_f; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0)  // ← 调用"虚函数"
            hang();
    }
}

// 派生类(板级实现) - board/freescale/mx6ull_alientek_emmc/xxx.c
int dram_init(void)  // ← "重写"虚函数
{
    gd->ram_size = PHYS_SDRAM_SIZE;
    return 0;
}

移植就是”派生一个新类”,重写部分”虚函数”。

8.5 移植的本质

graph TD
    A[移植的本质] --> B[搭建配置框架]
    A --> C[提供板级数据]
    A --> D[实现板级函数]
    
    B --> E[defconfig<br/>Kconfig<br/>Makefile]
    C --> F[DCD 表<br/>头文件宏<br/>引脚配置]
    D --> G[dram_init<br/>board_init<br/>board_late_init]
    
    E --> H[告诉编译系统<br/>编译什么]
    F --> I[告诉硬件<br/>如何初始化]
    D --> J[告诉启动流程<br/>调用什么]
    
    H --> K[生成定制的 U-Boot]
    I --> K
    J --> K
    
    style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px

一句话总结:

移植不是修改启动流程,而是在启动流程的”钩子”上挂上板级特定的配置和代码。


9. 完整影响链路追踪

9.1 修改点到影响的完整追踪

追踪 1:IMX_CONFIG 参数

graph TD
    A["defconfig:<br/>IMX_CONFIG=board/freescale/<br/>mx6ull_alientek_emmc/imximage.cfg"] --> B[make 读取]
    B --> C[传递给 arch/arm/mach-imx/Makefile]
    C --> D["mkimage -n IMX_CONFIG<br/>-T imximage<br/>-d u-boot.bin<br/>-o u-boot.imx"]
    D --> E[mkimage 读取 imximage.cfg]
    E --> F[解析 DCD 表]
    F --> G["DCD 表内容:<br/>DATA 4 0x021b000c 0x33374133<br/>DATA 4 0x021b0010 0x00100B22<br/>...(数百行寄存器配置)"]
    G --> H[嵌入到 u-boot.imx 头部]
    
    H --> I[烧录到板子]
    I --> J[BootROM 读取镜像]
    J --> K[BootROM 执行 DCD 表]
    K --> L["写寄存器:<br/>MMDC 控制器<br/>IOMUX<br/>CCM 时钟"]
    L --> M[DDR 控制器初始化完成]
    M --> N[U-Boot 可以加载到 DDR]
    N --> O[跳转到 _start]
    
    style A fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style G fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
    style M fill:#51cf66,stroke:#37b24d,stroke-width:2px

影响范围:

  • ✅ BootROM 阶段(DDR 初始化)
  • ❌ 不影响 U-Boot 代码执行(在 U-Boot 运行前就完成了)

如果配置错误:

  • DDR 无法初始化 → U-Boot 代码无法加载 → 完全无法启动

追踪 2:CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC

graph TD
    A["defconfig:<br/>CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y"] --> B[Kconfig 匹配]
    B --> C["arch/arm/cpu/armv7/mx6/Kconfig<br/>找到对应的 config 块"]
    C --> D[自动 select 依赖]
    D --> E[CONFIG_MX6ULL=y]
    D --> F[CONFIG_DM=y]
    D --> G[CONFIG_DM_THERMAL=y]
    
    C --> H["source board/freescale/<br/>mx6ull_alientek_emmc/Kconfig"]
    H --> I[设置板级变量]
    I --> J["CONFIG_SYS_BOARD=<br/>mx6ull_alientek_emmc"]
    I --> K["CONFIG_SYS_CONFIG_NAME=<br/>mx6ull_alientek_emmc"]
    
    J --> L["make 编译<br/>board/freescale/<br/>mx6ull_alientek_emmc/"]
    K --> M["自动包含<br/>include/configs/<br/>mx6ull_alientek_emmc.h"]
    
    E --> N[编译 arch/arm/mach-imx/mx6/ 代码]
    F --> O[编译 drivers/core/ 驱动模型]
    G --> P[编译 drivers/thermal/ 温度驱动]
    
    L --> Q[链接板级函数到 u-boot.elf]
    M --> R[板级宏生效]
    N --> Q
    O --> Q
    P --> Q
    
    Q --> S[u-boot.elf]
    R --> S
    
    style A fill:#ff6b6b,stroke:#c92a2a,stroke-width:3px
    style J fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style K fill:#ffd43b,stroke:#fab005,stroke-width:2px

影响范围:

  • ✅ 编译阶段(选择编译哪些代码)
  • ✅ 链接阶段(包含哪些板级函数)
  • ✅ 运行时(启动流程调用板级函数)

后续影响链:

CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC=y
    ↓
CONFIG_SYS_BOARD="mx6ull_alientek_emmc"
    ↓
编译 board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.c
    ↓
生成符号:dram_init, board_init, board_late_init
    ↓
链接到 u-boot.elf
    ↓
运行时 board_init_f() 调用 dram_init()
运行时 board_init_r() 调用 board_init()

追踪 3:PHYS_SDRAM_SIZE 宏

graph TD
    A["include/configs/<br/>mx6ull_alientek_emmc.h"] --> B["#define PHYS_SDRAM_SIZE SZ_512M"]
    
    B --> C["被板级代码使用<br/>mx6ull_alientek_emmc.c"]
    C --> D["dram_init() {<br/>gd->ram_size = PHYS_SDRAM_SIZE;<br/>}"]
    
    D --> E[编译时替换]
    E --> F["dram_init() {<br/>gd->ram_size = 0x20000000;<br/>}"]
    
    F --> G[链接到 u-boot.elf]
    
    G --> H[运行时:board_init_f 阶段]
    H --> I["init_sequence_f[] = {<br/>...<br/>dram_init,<br/>...<br/>}"]
    I --> J[调用 dram_init]
    J --> K["gd->ram_size = 0x20000000"]
    
    K --> L[后续函数使用 gd->ram_size]
    L --> M["setup_dest_addr() 计算重定位地址"]
    L --> N["bdinfo 命令显示 DDR 大小"]
    L --> O["内存测试边界检查"]
    
    style B fill:#ffd43b,stroke:#fab005,stroke-width:2px
    style K fill:#51cf66,stroke:#37b24d,stroke-width:2px

影响范围:

  • ✅ 编译时(宏替换)
  • ✅ 运行时 board_init_f 阶段(设置 gd->ram_size)
  • ✅ 运行时 board_init_r 阶段(使用 gd->ram_size 计算重定位地址)
  • ✅ 运行时 main_loop 阶段(内存命令使用 gd->ram_size)

如果配置错误:

假设实际 DDR 是 256MB,但配置成 512MB
    ↓
gd->ram_size = 0x20000000  (512MB)
    ↓
计算重定位地址:DDR_END - CODE_SIZE
重定位地址 = 0x80000000 + 0x20000000 - 0x100000 = 0x9FF00000
    ↓
⚠️ 但实际 DDR 只到 0x90000000
    ↓
relocate_code 写入无效地址
    ↓
❌ 系统崩溃

追踪 4:板级函数 board_init()

graph TD
    A["board/freescale/<br/>mx6ull_alientek_emmc/<br/>mx6ull_alientek_emmc.c"] --> B["int board_init(void) {<br/>setup_iomux_uart();<br/>setup_fec();<br/>return 0;<br/>}"]
    
    B --> C[编译生成 .o 文件]
    C --> D[链接到 u-boot.elf]
    D --> E[符号表包含 board_init]
    
    E --> F[运行时:board_init_r 阶段]
    F --> G["init_sequence_r[] = {<br/>...<br/>board_init,<br/>...<br/>}"]
    G --> H[调用 board_init]
    
    H --> I[执行 setup_iomux_uart]
    I --> J["配置 UART1 引脚:<br/>PAD_UART1_TX_DATA → UART1_TX<br/>PAD_UART1_RX_DATA → UART1_RX"]
    J --> K[串口可以正常输出]
    
    H --> L[执行 setup_fec]
    L --> M["配置网卡引脚:<br/>ENET1_MDC → FEC_MDC<br/>ENET1_MDIO → FEC_MDIO<br/>..."]
    M --> N[初始化 PHY 芯片]
    N --> O[网卡可以使用]
    
    style B fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px
    style K fill:#51cf66,stroke:#37b24d,stroke-width:2px
    style O fill:#51cf66,stroke:#37b24d,stroke-width:2px

影响范围:

  • ✅ 运行时 board_init_r 阶段(外设初始化)
  • ✅ 后续所有外设功能(串口、网络、GPIO 等)

如果缺少这个函数:

链接阶段:
    未定义符号 board_init
    ↓
❌ 链接失败,无法生成 u-boot.elf

如果函数实现错误(例如引脚配置错误):

board_init() 执行
    ↓
setup_iomux_uart() 配置了错误的引脚
    ↓
串口 TX/RX 信号没有连接到正确的物理引脚
    ↓
❌ 串口无输出(看起来像启动失败)

9.2 修改影响总结表

修改点文件影响的启动阶段影响的功能严重程度
IMX_CONFIGdefconfigBootROMDDR 初始化🔴 致命(无法启动)
CONFIG_TARGET_XXXdefconfig编译+运行整体板级集成🔴 致命(编译失败)
PHYS_SDRAM_SIZE头文件board_init_f
board_init_r
DDR 大小检测
重定位地址计算
🟡 严重(可能崩溃)
CONFIG_MXC_UART_BASE头文件board_init_f串口初始化🟡 严重(无输出)
board_init()板级代码board_init_r引脚配置
外设初始化
🟡 严重(外设不工作)
dram_init()板级代码board_init_fDDR 大小上报🟡 严重(内存检测错误)
CONFIG_BOOTCOMMAND头文件main_loop启动 Linux 的命令🟢 功能性(启动失败但可调试)
MAINTAINERS板级目录无文档⚪ 无影响

10. 总结

10.1 核心要点

1. 移植的本质

移植 ≠ 修改启动流程代码
移植 = 配置基础设施搭建 + 板级数据提供 + 板级函数实现

2. 配置系统的作用

defconfig + Kconfig + Makefile
    ↓
告诉编译系统:
    - 编译哪些代码
    - 包含哪些头文件
    - 链接哪些函数
    ↓
生成定制的 U-Boot

3. DCD 表的关键地位

imximage.cfg(DCD 表)
    ↓
BootROM 执行(U-Boot 运行前)
    ↓
初始化 DDR
    ↓
没有 DDR,U-Boot 无法加载
    ↓
这是移植最关键的一步!

4. 理论与实践的关系

理论分析(启动流程):
    告诉你代码如何执行
    ↓
实际移植(配置搭建):
    告诉编译系统选择执行哪些代码
    ↓
关系:
    配置驱动代码选择
    代码实现启动流程

10.2 移植检查清单

✅ 步骤 1:创建 defconfig

  • 复制参考板的 defconfig
  • 修改 IMX_CONFIG 路径
  • 修改 CONFIG_TARGET_XXX 为新板名称
  • 检查其他必要的 CONFIG 选项

✅ 步骤 2:创建头文件

  • 复制参考板的头文件
  • 修改头文件保护宏(防止重复包含)
  • 检查 PHYS_SDRAM_SIZE(DDR 大小)
  • 检查 CONFIG_MXC_UART_BASE(串口地址)
  • 检查 CONFIG_BOOTCOMMAND(启动命令)

✅ 步骤 3:创建板级目录(最关键)

  • 复制参考板目录
  • 修改 Makefile(obj-y 指向新的 .c 文件)
  • 重点:修改 imximage.cfg(DCD 表)
    • 检查 DDR 芯片型号
    • 检查 DDR 容量配置
    • 检查时序参数
    • 检查校准参数
  • 修改 Kconfig(板级变量)
  • 修改 MAINTAINERS(维护者信息)
  • 修改板级 .c 文件名和内容

✅ 步骤 4:修改顶层 Kconfig

  • 添加 config TARGET_XXX 选项
  • 添加 source 语句引入板级 Kconfig

10.3 调试建议

问题:修改后无法启动,串口无输出

排查顺序:

  1. 首先检查 DCD 表(最可能)


    # 检查 IMX_CONFIG 路径是否正确
    $ grep IMX_CONFIG configs/mx6ull_alientek_emmc_defconfig

    # 检查 imximage.cfg 是否存在
    $ ls board/freescale/mx6ull_alientek_emmc/imximage.cfg

  2. 检查 DDR 配置

    • DDR 芯片型号是否正确?
    • DDR 容量配置是否匹配?
    • 时序参数是否来自 DDR 芯片数据手册?
  3. 检查编译是否成功


    # 检查是否生成了板级 .o 文件
    $ ls board/freescale/mx6ull_alientek_emmc/*.o

    # 检查符号表中是否有板级函数
    $ nm u-boot | grep board_init
    $ nm u-boot | grep dram_init

  4. 检查配置是否生效


    # 检查 .config 文件
    $ grep CONFIG_TARGET_MX6ULL_ALIENTEK_EMMC .config
    $ grep CONFIG_SYS_BOARD .config

10.4 与启动流程的对应关系

移植步骤          →  影响的启动阶段        →  作用
═══════════════════════════════════════════════════════════
imximage.cfg     →  BootROM 阶段         →  DDR 初始化
defconfig        →  编译阶段             →  选择代码
头文件           →  board_init_f/r      →  配置参数
板级代码         →  board_init_f/r      →  硬件初始化
Kconfig          →  编译阶段             →  集成到系统

附录

附录 A:参考文档

  • 04-U-Boot完整启动流程详解.md – 启动流程理论
  • 09-U-Boot移植关键点与修改指南.md – 移植实践指导
  • 调用关系图.png – 完整函数调用流程

附录 B:关键文件路径速查

configs/
└── mx6ull_alientek_emmc_defconfig          # 默认配置

include/configs/
└── mx6ull_alientek_emmc.h                  # 板级头文件

board/freescale/mx6ull_alientek_emmc/
├── Makefile                                # 编译规则
├── Kconfig                                 # 板级配置
├── MAINTAINERS                             # 维护者
├── imximage.cfg                            # ⭐ DCD 表
└── mx6ull_alientek_emmc.c                  # 板级代码

arch/arm/cpu/armv7/mx6/
└── Kconfig                                 # 顶层配置

附录 C:常用命令

# 配置
make mx6ull_alientek_emmc_defconfig

# 图形配置(可选)
make menuconfig

# 编译
make -j$(nproc)

# 查看配置
cat .config | grep MX6ULL

# 查看符号
nm u-boot | grep board_init

# 查看反汇编
arm-linux-gnueabihf-objdump -d u-boot > u-boot.dis

完成! ✅

这份文档完整地回答了你的所有问题:

  1. ✅ 详细讲解移植步骤的目的和原因
  2. ✅ 提供大型流程图展示操作关系
  3. ✅ 结合启动流程分析移植行为的影响
  4. ✅ 解释理论分析与实际移植的差异
  5. ✅ 追踪每个修改的后续影响
上一篇 04-U-Boot完整启动流程详解

U-Boot 完整启动流程详解 概述 U-Boot 的启动过程是一个复杂而精密的过程,从硬件上电复位到最终进入命令行,经...

下一篇 uboot make过程分析