3. U-Boot 源码结构分析:顶层目录结构及关键目录作用
拿到一份 U-Boot 源码,第一反应是什么?我当年第一次解压出来,看到几十个文件夹,说实话有点懵。但别怕,它的目录设计其实很有章法。说白了,U-Boot 的源码结构就是一套「分层 + 板级隔离」的体系。搞懂了它,你以后移植、调试、甚至自己写 bootloader 都会顺手很多。
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 引脚)。
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 源码目录分层
下面这张图帮你理清各个目录之间的关系:
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/ 这四个核心目录的定位。其他目录,用到的时候再查。随着你调试的板子越来越多,这些目录结构会自然印在脑子里。