3. U-Boot 源码结构分析:顶层目录结构及关键目录作用

拿到一份 U-Boot 源码,第一反应是什么?我当年第一次解压出来,看到几十个文件夹,说实话有点懵。但别怕,它的目录设计其实很有章法。说白了,U-Boot 的源码结构就是一套「分层 + 板级隔离」的体系。搞懂了它,你以后移植、调试、甚至自己写 bootloader 都会顺手很多。

核心原则:U-Boot 通过目录结构把「通用代码」和「板级/架构相关代码」彻底分开。你改板子,基本只动几个目录;你改协议栈,去另外几个目录。互不干扰。

3.1 顶层目录概览

我们先看顶层。你 ls 一下 U-Boot 根目录,会看到类似这样的布局:

u-boot/
├── arch/          # CPU 架构相关代码(ARM、RISC-V、x86...)
├── board/         # 具体板卡代码(DDR 初始化、板级外设...)
├── common/        # 通用核心代码(命令解析、环境变量、HUSH shell...)
├── drivers/       # 设备驱动(串口、网卡、MMC、SPI、NAND...)
├── include/       # 头文件(配置宏、数据结构、API 声明...)
├── lib/           # 通用库函数(字符串、CRC、加密、压缩...)
├── cmd/           # 命令实现(每个 .c 文件对应一个命令)
├── net/           # 网络协议栈(TFTP、NFS、DHCP、Ping...)
├── fs/            # 文件系统支持(ext4、FAT、UBIFS...)
├── dts/           # 设备树源文件(.dts / .dtsi)
├── tools/         # 主机端工具(mkimage、fdtget、envtools...)
├── configs/       # 默认配置文件(defconfig)
├── scripts/       # 构建脚本(Kconfig、Makefile 辅助)
├── doc/           # 文档
└── examples/      # 示例代码(API 使用示例)

嗯,这里要注意:不是每个目录你都需要精通。作为嵌入式工程师,你日常打交道最多的就是 board/、arch/、common/、drivers/ 和 include/。其他目录,用到时再查就行。

3.2 关键目录深度解析

3.2.1 board/ —— 板级代码的「家」

这个目录存放的是跟具体硬件板卡强相关的代码。每个板子一个子目录,比如 board/ti/am335x/、board/freescale/imx6q_sabresd/。

里面一般有什么?

  • 板级初始化函数:board_init_f()、board_init_r()、board_late_init()
  • DDR 初始化:时序参数、寄存器配置,这块最容易出问题
  • 板级外设配置:GPIO 复用、PHY 地址、LED 定义
  • 低电平驱动:有些板子会在这里放 SPI Flash 或 I2C 的板级适配
我的习惯:拿到一块新板子,我第一件事就是去 board/ 目录下找参考。比如你的芯片是 i.MX6UL,那就先看 board/freescale/mx6ul_14x14_evk/,复制一份改。省时省力。

3.2.2 arch/ —— CPU 架构的「心脏」

这里按 CPU 架构分目录:arch/arm/、arch/riscv/、arch/x86/、arch/mips/ 等等。每个架构下又按 SoC 厂商或系列细分。

以 arch/arm/ 为例:

arch/arm/
├── cpu/          # CPU 核心代码(异常向量、MMU、Cache、SMP...)
│   ├── armv7/
│   ├── armv8/
│   └── ...
├── mach-*/       # SoC 平台代码(时钟、复位、定时器...)
│   ├── mach-imx/
│   ├── mach-sunxi/
│   └── ...
├── lib/          # 架构通用函数(cache 操作、pci 访问...)
├── include/      # 架构头文件(寄存器地址、位定义...)
└── dts/          # 架构级设备树

这里我要强调一点:arch/ 和 board/ 的分工很多人搞混。简单说:arch/ 管的是 CPU 和 SoC 内部的东西(MMU、Cache、中断控制器),board/ 管的是板级的东西(DDR 型号、网卡 PHY、LED 引脚)。

我曾经踩过的坑:有次移植 U-Boot,我在 board/ 里配了 DDR 时序,但死活起不来。查了两天,最后发现是 arch/arm/mach-sunxi/ 里的时钟配置不对,PLL 频率算错了。所以记住:板子起不来,先查 arch/ 的时钟和复位,再查 board/ 的 DDR。

3.2.3 common/ —— 通用核心逻辑

这个目录是 U-Boot 的「大脑」。它不依赖具体板子或架构,提供的是 bootloader 的核心机制:

  • 命令处理框架:cli_hush.c(HUSH shell 解析器)、command.c(命令注册与分发)
  • 环境变量管理:env_common.c、env_flash.c、env_mmc.c 等
  • 启动流程:board_f.c(init sequence F)、board_r.c(init sequence R)
  • 控制台 I/O:console.c、stdio.c
  • 内存管理:malloc_simple.c、lmb.c

你想想看,你在 U-Boot 命令行敲一个 printenv,背后就是 common/ 里的代码在干活。它解析你的输入,找到对应的命令函数,执行,然后输出结果。

3.2.4 drivers/ —— 设备驱动仓库

这个目录很大,按设备类型分子目录:

子目录 包含驱动 典型文件
serial/ 串口驱动 ns16550.c, imx_uart.c
net/ 网卡驱动 fec_mxc.c, dwc_eth_qos.c
mmc/ MMC/SD/SDIO 驱动 fsl_esdhc.c, sunxi_mmc.c
spi/ SPI 控制器驱动 spi_sunxi.c, fsl_qspi.c
nand/ NAND Flash 驱动 mxs_nand.c, sunxi_nand.c
usb/ USB 主机/设备驱动 ehci-sunxi.c, dwc2.c
gpio/ GPIO 控制器驱动 mxc_gpio.c, sunxi_gpio.c
i2c/ I2C 控制器驱动 mvtwsi.c, imx_lpi2c.c

U-Boot 的驱动模型(Driver Model, DM)从 2014 年左右开始引入,现在已经是主流。它把驱动注册、探测、绑定都标准化了。我个人建议新板子直接用 DM,维护起来方便很多。

3.2.5 其他重要目录

include/ —— 头文件集中营。这里有个文件你一定会经常改:include/configs/<board_name>.h。这是板级配置头文件,定义宏如 CONFIG_BOOTCOMMAND、CONFIG_SYS_TEXT_BASE 等。嗯,老版本 U-Boot 的配置主要靠它,新版本逐渐迁移到 Kconfig 了。

cmd/ —— 每个 .c 文件对应一个 U-Boot 命令。比如 cmd/bootm.c 实现 bootm 命令,cmd/nand.c 实现 nand 命令。如果你想添加自定义命令,在这里新建一个 .c 文件就行。

net/ —— 网络协议栈。U-Boot 的网络功能很精简,只实现了 TFTP 客户端、NFS 客户端、DHCP 客户端、Ping 等。没有完整的 TCP/IP 栈,只有 UDP 和 ARP。

3.3 知识结构图:U-Boot 源码目录分层

下面这张图帮你理清各个目录之间的关系:

U-Boot 源码目录分层结构 U-Boot 根目录 arch/ (CPU架构) board/ (板级) drivers/ (驱动) common/ (核心) cpu/ (CPU核心) mach-*/ (SoC平台) board_init_f/r() serial/ net/ mmc/ spi/ nand/ usb/ cli/ env/ console/ 关键依赖关系 • arch/ 提供 CPU/SoC 底层初始化(时钟、MMU、中断) • board/ 依赖 arch/ 的时钟和复位,再完成板级 DDR 和外设初始化 • drivers/ 通过 DM 框架被 common/ 和 cmd/ 调用

3.4 目录选择速查表

最后,我整理了一个速查表。当你遇到具体问题时,可以快速定位该去哪个目录:

遇到问题 优先查看目录 典型文件/子目录
板子起不来,卡在某个阶段 board/ + arch/ board.c, lowlevel_init.S, clock.c
串口没有输出 drivers/serial/ + board/ ns16550.c, board_uart_config
网卡 ping 不通 drivers/net/ + board/ fec_mxc.c, PHY 地址配置
MMC/SD 卡识别不到 drivers/mmc/ + board/ fsl_esdhc.c, 引脚复用配置
环境变量保存失败 common/env_*.c + board/ env_mmc.c, env_flash.c, 分区偏移
想添加新命令 cmd/ + common/ 新建 cmd_xxx.c, 注册 U_BOOT_CMD
想修改默认启动命令 include/configs/ CONFIG_BOOTCOMMAND 宏
设备树编译报错 dts/ + arch/arm/dts/ .dts 文件语法、include 路径
我的建议:别试图一次性记住所有目录。你只需要记住 board/、arch/、common/、drivers/ 这四个核心目录的定位。其他目录,用到的时候再查。随着你调试的板子越来越多,这些目录结构会自然印在脑子里。