第4章 U-Boot移植实战:板级配置创建、设备树修改、DDR初始化调试、网络驱动移植

U-Boot移植,说白了就是让Bootloader在你的板子上跑起来。很多新手一上来就盯着代码改,结果越改越乱。我个人的习惯是——先搞清楚硬件,再看代码,最后动手改。这一章我们就把四个核心环节拆开揉碎了讲。

4.1 板级配置创建

U-Boot的板级配置,核心就是两个文件:Kconfig 和 Makefile。但真正决定板子行为的,是 include/configs/ 下的头文件。

4.1.1 从参考板开始

别从零写。找个硬件最接近的参考板,复制一份。我在项目中遇到过,有人非要从头写配置,结果折腾了两周,还不如直接改参考板来得快。

具体步骤:

  1. 在 board/ 下创建你的板子目录,复制参考板的文件
  2. 在 include/configs/ 下创建板级头文件
  3. 修改 arch/arm/mach-xxx/Kconfig,添加你的板子选项
  4. 修改顶层 boards.cfg 或 Makefile 添加编译目标

关键配置项清单

配置项说明常见坑
CONFIG_SYS_TEXT_BASEU-Boot加载地址与DDR初始化地址冲突
CONFIG_SYS_INIT_SP_OFFSET栈指针偏移太小会导致栈溢出
CONFIG_NR_DRAM_BANKSDDR bank数量与实际硬件不匹配
CONFIG_SYS_MALLOC_LEN动态内存池大小网络功能需要更大

4.1.2 板级头文件编写要点

头文件里最烦人的就是宏定义。我建议你按功能模块分组:

  • 内存相关:DDR地址范围、大小、时序参数
  • 时钟相关:CPU频率、总线频率、外设时钟
  • 外设相关:串口、网卡、存储设备的基地址
  • 环境变量:默认bootargs、bootcmd

我的小技巧:把参考板的头文件和你的板子原理图放在一起对照看。每个宏定义都能在原理图上找到对应引脚或寄存器,这样不容易漏。

4.2 设备树修改

U-Boot从2016年开始全面拥抱设备树。说白了,设备树就是硬件的“身份证”,告诉U-Boot你的板子上有什么、在哪里。

4.2.1 设备树文件结构

一个典型的设备树文件包含:

/dts-v1/;
#include "soc.dtsi"

/ {
    model = "My Industrial Board";
    compatible = "mycompany,imx6ul-board";

    chosen {
        stdout-path = &uart1;
    };

    memory {
        device_type = "memory";
        reg = <0x80000000 0x10000000>;  /* 256MB DDR */
    };
};

4.2.2 常见修改场景

我在实际项目中,设备树修改最多的几个地方:

  • 内存节点:DDR容量和地址必须与硬件一致
  • 串口节点:调试串口的引脚复用和波特率
  • 网卡节点:PHY地址、复位引脚、时钟配置
  • GPIO节点:LED、按键、控制信号

注意:设备树的 compatible 字符串必须与驱动中的匹配表一致。我曾经因为拼写错误,查了整整一天才发现是少了个逗号。

4.3 DDR初始化调试

DDR初始化是U-Boot移植中最容易翻车的环节。为什么?因为DDR一旦没配好,整个系统都跑不起来,连打印都看不到。

4.3.1 DDR初始化流程

U-Boot的DDR初始化大致分三步:

  1. 控制器配置:设置DDR控制器的工作模式、时序参数
  2. PHY训练:调整DQS/DQ的延迟,保证数据采样正确
  3. 校准测试:读写验证,确认DDR工作正常

调试三板斧

问题现象可能原因排查方法
U-Boot完全无输出DDR初始化失败,代码卡死检查时钟配置、电源时序
偶尔启动成功时序参数临界,温度影响放宽时序,增加余量
运行中随机死机DDR稳定性问题跑memtest,检查布线

4.3.2 实战调试技巧

我记得有一次,板子在低温下死活起不来。用示波器抓DDR时钟,发现频率偏了。后来查出来是PLL配置里有个分频系数写错了。

调试建议:

  • 先用低速模式验证基本读写
  • 逐步提高频率,找到稳定边界
  • 用U-Boot自带的 md 和 mw 命令手动测试
  • 实在不行,用JTAG单步跟踪

避坑指南:DDR的时序参数不要照搬参考板。不同PCB布局、不同批次的内存颗粒,时序都会有差异。我曾经因为偷懒直接复制参数,结果批量生产时翻车了。

4.4 网络驱动移植

网络功能在工业场景下太重要了——远程升级、NFS挂载根文件系统、网络调试,都离不开它。

4.4.1 网络驱动架构

U-Boot的网络驱动分三层:

  • MAC层:控制器的DMA、描述符管理
  • PHY层:物理层芯片的配置和管理
  • 协议层:ARP、IP、UDP等协议栈

移植的重点在MAC和PHY层。协议层基本不用动。

4.4.2 PHY驱动移植

PHY芯片的移植相对简单。大多数PHY都兼容通用驱动。你需要做的:

  1. 在设备树中指定PHY的地址和类型
  2. 配置PHY的复位引脚(如果有)
  3. 设置PHY的工作模式(千兆/百兆、全双工/半双工)
/* 设备树中的PHY节点示例 */
&fec1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_enet1>;
    phy-mode = "rmii";
    phy-handle = <&phy0>;
    status = "okay";

    mdio {
        #address-cells = <1>;
        #size-cells = <0>;

        phy0: ethernet-phy@0 {
            reg = <0>;
            reset-gpios = <&gpio5 0 GPIO_ACTIVE_LOW>;
            reset-delay-us = <10000>;
        };
    };
};

4.4.3 MAC驱动调试

MAC驱动的问题通常出在DMA描述符和中断处理上。我遇到过最头疼的问题——网卡能发不能收。查了三天,发现是DMA描述符的地址对齐有问题。

调试步骤:

  • 先用 ping 测试基本连通性
  • 用 tftp 测试大文件传输
  • 抓包分析,确认PHY是否正常工作
  • 检查MAC寄存器的状态位

重要提醒:网络驱动的调试一定要有串口输出配合。没有串口日志,你根本不知道网卡初始化到哪一步卡住了。

4.5 本章知识体系

下面这张图把U-Boot移植的四个核心环节串起来了。你可以看到,板级配置是基础,设备树是桥梁,DDR和网络是两大难点。

U-Boot移植知识体系 U-Boot移植核心环节 U-Boot移植 板级配置创建 设备树修改 DDR初始化调试 网络驱动移植 板级配置 Kconfig 头文件宏定义 设备树 dts/dtsi 节点修改 DDR调试 时序参数 PHY训练 网络驱动 MAC/PHY DMA调试 核心原则:先配板级,再改设备树,最后调DDR和网络 每个环节都要验证通过,再进入下一步

4.6 本章小结

U-Boot移植没有捷径,但有套路。板级配置是地基,设备树是蓝图,DDR和网络是两大硬骨头。我做了这么多年移植,最大的体会就是——别怕踩坑,每个坑都是经验。

嗯,这一章的内容就到这。记住,调试的时候保持耐心,一个参数一个参数地试,总会找到问题的。