前言
本文档目标:
- 深度解析正点原子 I.MX6ULL-ALPHA 开发板的实际移植步骤
- 解释每个操作的目的、原因和必要性
- 将移植步骤映射到 U-Boot 启动流程,理解影响范围
- 对比理论分析与实际移植的差异
- 追踪每个修改在后续流程中的作用
核心问题:
- 为什么实际移植只需要 4 个步骤,而启动流程有那么多阶段?
- 这些配置文件的修改如何影响最终的启动行为?
- 移植工作的本质是什么?
目录
- 移植步骤总览
- 步骤一:添加开发板默认配置文件
- 步骤二:添加开发板头文件
- 步骤三:添加板级文件夹
- 步骤四:修改图形配置文件
- 配置系统工作原理
- 移植步骤与启动流程的映射
- 理论分析 vs 实际移植
- 完整影响链路追踪
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 | 创建 defconfig | configs/mx6ull_alientek_emmc_defconfig | 定义板级默认配置 | ⭐⭐ |
| 2 | 创建头文件 | include/configs/mx6ull_alientek_emmc.h | 定义编译时宏配置 | ⭐⭐ |
| 3 | 创建板级目录 | board/freescale/mx6ull_alientek_emmc/ | 提供板级初始化代码和 DCD 表 | ⭐⭐⭐⭐⭐ |
| 4 | 修改 Kconfig | arch/arm/cpu/armv7/mx6/Kconfig | 将新板集成到配置系统 | ⭐⭐ |
1.3 移植本质
关键理解:
移植工作的本质是 配置基础设施的搭建,而非直接修改启动代码。
实际移植 ≠ 修改启动流程代码
实际移植 = 搭建配置框架 + 提供硬件参数
为什么?
- U-Boot 启动流程代码是通用的:
_start、lowlevel_init、board_init_f等函数由 NXP 提供,已经适配 i.MX6ULL 芯片 - 板级差异在于硬件配置:不同板子的 DDR 型号、引脚分配、外设配置不同
- 配置系统驱动代码选择:通过 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_CONFIG | board/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
影响链路详解:
头文件自动包含
// U-Boot 编译系统会自动生成:
#include <configs/mx6ull_alientek_emmc.h>板级目录自动编译
# make 系统会编译:
board/freescale/mx6ull_alientek_emmc/mx6ull_alientek_emmc.o板级函数自动调用
// 启动流程中会调用板级函数:
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/ → 重命名 → 修改多个文件
修改的文件:
Makefileimximage.cfgKconfigMAINTAINERSmx6ullevk.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 芯片的参考板(最简单,推荐)
步骤:
- 找到使用相同 DDR 芯片的 NXP 官方参考板
- 直接复制其
imximage.cfg文件 - 只需修改路径,内容保持不变
示例:
# 检查参考板使用的 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:无工具手动计算(不推荐,容易出错)
仅在无法获取工具时使用,步骤:
- 阅读芯片手册 – i.MX6ULL 参考手册(3000+ 页)
- 阅读 DDR 芯片数据手册 – 获取时序参数
- 手动计算寄存器值
计算示例(仅展示思路):
假设:
- 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 启动流程开始
关键点:
- DCD 表在 U-Boot 代码运行之前 执行
- 由 BootROM(芯片内部固化代码)执行,不是 U-Boot 执行
- 如果 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_CONFIG | BootROM 阶段 | 指定 DCD 表路径 → BootROM 执行 DCD 表初始化 DDR | BootROM 内部 |
| imximage.cfg | BootROM 阶段 | DCD 表内容 → DDR 控制器寄存器配置 | BootROM 内部 |
| 头文件: PHYS_SDRAM_SIZE | board_init_f | 定义 DDR 大小 → dram_init() 返回值 | common/board_f.c:dram_init() |
| 头文件: CONFIG_MXC_UART_BASE | board_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_f | DDR 大小检测 | board/freescale/.../xxx.c |
| 板级代码: board_init() | board_init_r | 引脚配置、外设初始化 | board/freescale/.../xxx.c |
| 板级代码: board_late_init() | board_init_r | 环境变量设置 | board/freescale/.../xxx.c |
| 头文件: CONFIG_BOOTCOMMAND | main_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
实际移植步骤:
- 创建 defconfig
- 创建头文件
- 创建板级目录
- 修改 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 型号 | MT41K256M16HA | MT41K256M16HA | 可能不同(需要 DCD 表) |
| DDR 大小 | 512MB | 512MB | 配置参数 |
| 串口 | UART1 | UART1 | 配置参数 |
| 网卡 | KSZ8081 | LAN8720A | 引脚配置 |
| LED GPIO | GPIO1_IO03 | GPIO1_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_CONFIG | defconfig | BootROM | DDR 初始化 | 🔴 致命(无法启动) |
CONFIG_TARGET_XXX | defconfig | 编译+运行 | 整体板级集成 | 🔴 致命(编译失败) |
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_f | DDR 大小上报 | 🟡 严重(内存检测错误) |
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 调试建议
问题:修改后无法启动,串口无输出
排查顺序:
首先检查 DCD 表(最可能)
# 检查 IMX_CONFIG 路径是否正确
$ grep IMX_CONFIG configs/mx6ull_alientek_emmc_defconfig
# 检查 imximage.cfg 是否存在
$ ls board/freescale/mx6ull_alientek_emmc/imximage.cfg检查 DDR 配置
- DDR 芯片型号是否正确?
- DDR 容量配置是否匹配?
- 时序参数是否来自 DDR 芯片数据手册?
检查编译是否成功
# 检查是否生成了板级 .o 文件
$ ls board/freescale/mx6ull_alientek_emmc/*.o
# 检查符号表中是否有板级函数
$ nm u-boot | grep board_init
$ nm u-boot | grep dram_init检查配置是否生效
# 检查 .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
完成! ✅
这份文档完整地回答了你的所有问题:
- ✅ 详细讲解移植步骤的目的和原因
- ✅ 提供大型流程图展示操作关系
- ✅ 结合启动流程分析移植行为的影响
- ✅ 解释理论分析与实际移植的差异
- ✅ 追踪每个修改的后续影响