第3章 U-Boot移植实战:瑞芯微平台启动流程、U-Boot配置与编译、DDR与时钟初始化调试

好,咱们直接进入正题。这一章讲的是U-Boot移植,说白了就是让板子先跑起来。很多刚接触瑞芯微平台的朋友,一上来就被各种配置项和编译报错搞懵了。我刚开始做RK3399项目时也踩过不少坑,今天就把这些经验掰开揉碎讲给你听。

3.1 瑞芯微平台启动流程

先搞清楚一件事:芯片上电后到底干了什么?瑞芯微的启动流程分几个阶段,每个阶段都有明确分工。

核心启动链路:

  • BootROM:芯片内部固化的只读代码,上电后第一个执行
  • DDR初始化:在SPL阶段完成,没有DDR什么都干不了
  • SPL (Secondary Program Loader):从存储介质加载U-Boot
  • U-Boot proper:完整的U-Boot,负责加载内核

我画了一张流程图,把整个启动过程串起来,你一看就明白。

瑞芯微平台启动流程 BootROM 检测启动介质 (eMMC/SD/NAND) SPL (DDR初始化) U-Boot proper 加载Linux内核 注意:DDR初始化在SPL阶段完成,这是移植中最容易出问题的环节

嗯,这里要注意。BootROM是芯片出厂就写死的,你改不了。我们能动手的只有SPL和U-Boot proper。瑞芯微的SPL里集成了DDR初始化代码,这部分跟硬件设计强相关。

3.2 U-Boot配置与编译

拿到一块新板子,第一步是选对配置文件。瑞芯微的U-Boot仓库里,配置文件命名很有规律。

芯片型号 配置文件 适用场景
RK3568 evb-rk3568_defconfig 官方评估板
RK3588 evb-rk3588_defconfig 官方评估板
RK3399 evb-rk3399_defconfig 官方评估板

我个人习惯,拿到新板子先拿官方评估板的配置编译一遍,确保工具链和环境没问题。然后再根据自己板子的差异去改。

编译步骤(以RK3568为例):

# 设置交叉编译工具链
export CROSS_COMPILE=aarch64-linux-gnu-

# 清理
make distclean

# 加载配置
make evb-rk3568_defconfig

# 配置(可选)
make menuconfig

# 编译
make -j$(nproc)

编译完会生成几个关键文件:u-boot.bin、spl/u-boot-spl.bin、u-boot.itb。瑞芯微平台用的是FIT (Flattened Image Tree) 格式,把SPL和U-Boot proper打包在一起。

避坑指南:我曾经因为工具链版本不对,编译出来的U-Boot死活跑不起来。瑞芯微官方推荐用gcc 10.3或11.2,太新或太旧都可能出问题。建议直接用SDK里自带的工具链。

3.3 DDR初始化调试

DDR初始化是移植中最头疼的部分,没有之一。为什么?因为DDR没跑起来,你连串口打印都看不到。

瑞芯微的DDR初始化代码在drivers/ram/rockchip/目录下。每个芯片对应一个文件,比如RK3568的是sdram_rk3568.c。这里面的参数跟硬件设计一一对应:

  • DDR类型:DDR3、DDR4、LPDDR4、LPDDR4X
  • 频率:比如800MHz、1066MHz、1600MHz
  • 时序参数:tCL、tRCD、tRP、tRAS等
  • 容量:单颗颗粒大小、通道数

我建议你先把DDR频率降到最低,比如400MHz。等系统跑起来了,再慢慢往上调。这样做的好处是,如果DDR有问题,你能更快定位是硬件问题还是参数问题。

调试DDR的常用手段:

  1. 串口打印:在SPL里加printch(),看代码执行到哪一步
  2. LED指示灯:在关键位置点灯,确认代码流程
  3. 逻辑分析仪:抓DDR的CLK、CMD、DQ信号,看时序对不对
  4. 对比法:拿官方评估板的参数做基准,逐项对比

我记得有一次调RK3588的板子,DDR死活初始化不过。折腾了两天,最后发现是原理图上DDR的VREF电压接错了。嗯,这种硬件问题,软件再调也没用。

3.4 时钟初始化调试

时钟是另一个容易出坑的地方。瑞芯微的时钟树很复杂,PLL、分频、门控,一环扣一环。

时钟初始化主要在arch/arm/mach-rockchip/目录下。每个芯片有自己的时钟驱动,比如clk_rk3568.c。你需要关注几个关键时钟:

时钟名称 典型频率 作用
ARMCLK 1.8GHz / 2.0GHz CPU核心时钟
DDRCLK 800MHz / 1600MHz DDR控制器时钟
ACLK 400MHz / 500MHz 总线时钟
UARTCLK 24MHz / 48MHz 串口时钟

调试时钟时,我一般先确认UART时钟。因为串口是我们跟板子沟通的唯一窗口。如果串口没输出,先查UART时钟对不对。

时钟调试小技巧:

  • 用示波器量PLL的输出引脚,确认PLL是否锁定
  • 在代码里加debug_uart打印,看时钟配置是否生效
  • 对比官方SDK的时钟配置,逐项检查寄存器值

你想想看,如果ARMCLK没配好,CPU跑在错误频率上,系统可能直接死机。我遇到过最离谱的一次,是PLL的反馈分频系数算错了,CPU频率只有标称值的十分之一。板子虽然能跑,但慢得像蜗牛。

3.5 实战:从零开始移植U-Boot

好,理论讲完了,咱们来点实际的。假设你拿到一块基于RK3568的自研板子,该怎么移植?

移植步骤:

  1. 复制配置文件:cp configs/evb-rk3568_defconfig configs/myboard-rk3568_defconfig
  2. 修改设备树:在arch/arm/dts/下创建myboard-rk3568.dts,修改DDR容量、型号、GPIO定义
  3. 调整DDR参数:在drivers/ram/rockchip/sdram_rk3568.c里修改时序参数
  4. 配置串口:确认UART2的引脚复用和时钟
  5. 编译烧录:make myboard-rk3568_defconfig && make -j8

第一次烧录后,如果串口有输出,恭喜你,最难的DDR和时钟已经过了。如果没输出,别慌,按前面说的调试手段一步步查。

我曾经踩过的坑:有一次我改了设备树里的DDR容量,但忘了改对应的配置宏。结果U-Boot读到的容量跟实际硬件不匹配,系统只认到一半内存。这种低级错误,检查一遍就能避免。

最后说一句,U-Boot移植是个细致活。别指望一次成功,多试几次,多对比官方代码,慢慢就摸到门道了。