3. U-Boot 源码结构解析:顶层目录、板级目录、CPU 架构目录的功能划分

拿到 U-Boot 源码包,第一眼看到那一堆文件夹,很多人会懵。我刚开始接触时也一样,感觉像进了迷宫。其实它的结构很有章法,说白了就是三个层次:顶层管全局、板级管具体硬件、架构管 CPU 核心。搞懂这三层,移植工作就成功了一半。

3.1 顶层目录:U-Boot 的“大脑”

顶层目录是 U-Boot 的调度中心。你解压源码后,第一眼看到的那些文件和文件夹,就是它了。我习惯先看 Makefile 和 Kconfig,因为整个编译流程都靠它们串联。

目录/文件 作用 我的经验
Makefile 顶层构建脚本,定义编译规则 移植时经常要改这里添加新板子
Kconfig 配置系统入口,管理菜单选项 用 make menuconfig 就是读它
arch/ CPU 架构相关代码 ARM、RISC-V 都在这里
board/ 板级初始化代码 移植的核心战场
common/ 通用函数库 命令解析、环境变量都在这里
drivers/ 设备驱动 串口、网卡、Flash 驱动
include/ 头文件 配置宏定义、数据结构
lib/ 库函数 字符串操作、CRC 计算等
tools/ 主机端工具 mkimage、fdt 处理工具
我的习惯:拿到一个新板子,先看 board/ 和 include/configs/ 下的文件。这两个地方藏着板子的“身份证”。

3.2 板级目录:你的板子在这里“安家”

板级目录 board/ 是移植工作的主战场。每个板子在这里都有一个专属文件夹,里面放着板子特有的初始化代码。说白了,就是告诉 U-Boot:“我的板子长什么样,怎么启动。”

举个例子,我移植过一块基于 TI AM335x 的板子,它的路径是 board/ti/am335x/。里面通常有:

  • board.c —— 板级初始化函数,比如 board_init()
  • mux.c —— 引脚复用配置
  • Kconfig —— 板级配置选项
  • Makefile —— 编译规则

我记得有一次,客户板子的 DDR 参数和参考设计不一样。我就是在 board.c 里找到 dram_init() 函数,把时序参数改掉才跑起来的。嗯,这里要注意:板级目录只放“这个板子特有”的代码,通用的东西别往里塞。

避坑指南:我曾经犯过一个错误——把 CPU 通用的初始化代码写到了板级目录里。结果换了个同架构的板子,代码全得重写。正确的做法是:CPU 通用的放 arch/,板子特有的放 board/。

3.3 CPU 架构目录:芯片的“内核”

arch/ 目录下按 CPU 架构分家。ARM、x86、RISC-V、MIPS……各占一个子目录。以 ARM 为例,路径是 arch/arm/,里面又细分:

子目录 内容 说明
cpu/ CPU 核心初始化 启动汇编、异常向量表
include/ 架构相关头文件 寄存器定义、内存映射
lib/ 架构通用函数 cache 操作、MMU 设置
mach-*/ SoC 厂商代码 比如 mach-sunxi、mach-imx

你想想看,为什么要把架构代码单独拎出来?因为不同 CPU 的启动流程天差地别。ARM 要关中断、设栈指针、开 MMU;RISC-V 要处理 M 模式和 S 模式的切换。这些代码如果混在板级目录里,移植到另一个板子时根本没法复用。

核心原则:

  • arch/ —— 管“CPU 怎么跑”
  • board/ —— 管“板子长什么样”
  • 顶层 —— 管“怎么编译、怎么配”

3.4 三者的协作关系

光说理论不够,我画张图帮你理清关系。这张图展示了 U-Boot 启动时,三个目录如何配合工作:

U-Boot 源码三层结构协作关系 顶层目录(Makefile / Kconfig) arch/arm/(CPU 架构代码) board/ti/am335x/(板级代码) arch/arm/cpu/start.S board/.../board.c common/board_f.c 启动流程说明: 1. CPU 上电,先执行 arch/arm/cpu/ 下的汇编代码(设置栈、关中断) 2. 跳转到 board/ 下的 C 函数(初始化时钟、DDR、串口) 3. 进入 common/ 的通用初始化流程(环境变量、命令解析)

从这张图能看明白:启动是从架构层开始的。CPU 上电后先跑 arch/arm/cpu/start.S,做完最低限度的初始化,再跳到板级代码。板级代码把 DDR、时钟、串口搞定后,才进入通用的 common/ 流程。

我的经验:调试启动失败时,先看 log 停在哪一层。如果连串口都没打印,问题大概率在 arch/ 或 board/ 的早期初始化。如果打印了但卡在某个命令,那就是 common/ 或 drivers/ 的问题。

3.5 其他重要目录

除了上面三个核心目录,还有几个地方你也会经常打交道:

  • drivers/ —— 设备驱动的大本营。串口、网卡、Flash、USB……按子系统分文件夹。移植时如果板子换了网卡芯片,主要改这里。
  • include/configs/ —— 板级配置头文件。比如 am335x_evm.h,里面定义了内存大小、环境变量分区、默认启动命令。我每次移植第一件事就是复制一个参考板子的 .h 文件,然后改参数。
  • common/ —— 通用代码。命令处理(cmd/)、环境变量(env/)、启动流程(board_f.c、board_r.c)都在这里。这部分一般不用动,除非你要加自定义命令。
  • tools/ —— 主机工具。比如 mkimage 用来给内核镜像加头部,fdtgrep 用来裁剪设备树。交叉编译时这些工具要在 PC 上跑。
注意:别在 include/configs/ 里写死硬件参数。我见过有人把 DDR 时序直接写在 .h 文件里,结果换了个 DDR 型号,改起来痛苦得要命。正确的做法是用设备树(DTB)来描述硬件,U-Boot 从设备树里读参数。

3.6 总结一下

U-Boot 的源码结构,说白了就是“分而治之”:

  • 顶层管编译和配置,是总调度
  • 架构层管 CPU 核心,是“骨架”
  • 板级层管具体硬件,是“血肉”
  • 驱动层和通用层提供“器官”和“功能”

搞懂这个分层逻辑,移植时就知道代码该往哪放。不会把 CPU 初始化和板子初始化混在一起,也不会在通用代码里塞板子特有的 hack。嗯,这就是我多年移植经验的总结——结构清晰了,问题就少了一半。