嵌入式Linux新板子bring-up实战:RK3506B从零到驱动验证
导读:新板子回来怎么让它跑起来?本文以Rockchip RK3506B工业平台为例,覆盖从USB烧录、DDR初始化、U-Boot移植到内核启动的完整bring-up链路,附每一步关键命令和常见踩坑排雷。
一、原理简析
bring-up是什么?
嵌入式Linux新板子bring-up,本质是让软件逐层认识硬件的过程。芯片上电后,依次经历五个阶段,每一层都把下一层所需的环境准备好:
| 阶段 | 谁在工作 | 核心任务 | 运行位置 |
|---|---|---|---|
| ① ROM代码 | 芯片内置BootROM | 从存储介质加载SPL | 芯片内部ROM |
| ② SPL/TPL | U-Boot第一阶段 | 初始化DDR,加载完整U-Boot | 芯片内部SRAM |
| ③ U-Boot | 完整Bootloader | 初始化外设,加载内核 | DDR |
| ④ 内核 | Linux Kernel | 驱动全部硬件,挂载根文件系统 | DDR |
| ⑤ 用户空间 | init + 应用程序 | 业务逻辑运行 | DDR |
Rockchip启动链
Rockchip平台有一条明确的启动链,这也是bring-up的检查清单:
关键认知:bring-up不是调试bug,而是验证硬件设计。软件没反应,优先查供电、时钟、复位,再查代码。
RK3506B平台概览
| 属性 | 说明 |
|---|---|
| CPU | 3×Cortex-A7 @ 1.5GHz + 1×Cortex-M0 @ 200MHz(多核异构) |
| 架构 | ARMv7-A 32bit |
| 制程 | 22nm |
| 内存 | 16bit DDR2/DDR3/DDR3L,最大512MB |
| 存储 | eMMC / NAND Flash / SPI Nor |
| 网络 | 双路RMII 10/100M以太网 |
| 工业总线 | 双CAN-FD、6×UART、3×SPI、3×I2C、DSMC/FLEXBUS |
| SDK | Buildroot 2024.02 + Linux 6.1 + U-Boot 2017.09 |
| 实时支持 | PREEMPT_RT / Xenomai 3 |
二、启动流程与分区布局
Rockchip启动模式
Rockchip芯片支持多种启动模式,通过硬件引脚或eFuse配置:
分区布局
Rockchip使用GPT分区表,bring-up阶段需要知道每个镜像烧到哪个位置:
| 分区名 | 起始扇区(512B) | 大小 | 内容 |
|---|---|---|---|
| loader1 | 0x40 (64) | 2.5MB | idbloader.img = TPL/SPL合并镜像 |
| loader2 | 0x4000 (16384) | 4MB | u-boot.itb (U-Boot + ATF/Trust) |
| trust | 0x6000 (24576) | 4MB | trust.img (ATF/TEE,仅miniloader方案使用) |
| boot | 0x8000 (32768) | 112MB | boot.img (内核+设备树+extlinux.conf) |
| rootfs | 0x40000 (262144) | 剩余空间 | rootfs.img |
分区表来自Rockchip官方wiki Partitions 和 Boot_option。实际分区大小可通过
parameter_gpt.txt自定义。
U-Boot两种启动方案对比
Rockchip平台U-Boot有两套方案,bring-up时需要明确用的是哪套:
| 维度 | U-Boot SPL/TPL方案 | Rockchip Miniloader方案 |
|---|---|---|
| DDR初始化 | rkbin预编译DDR bin + U-Boot TPL加载 | 预编译DDR bin + miniloader(全部闭源) |
| 源码可见 | U-Boot部分开源,DDR bin闭源 | 全部闭源 |
| idbloader内容 | TPL(DDR bin) + SPL(U-Boot编译) | DDR bin + miniloader |
| loader2内容 | u-boot.itb (含U-Boot+trust) | uboot.img (单独) |
| trust分区 | 不需要(含在u-boot.itb内) | 需要单独trust.img @ 0x6000 |
| 主线上游 | 部分支持(U-Boot主线) | 不支持 |
| RK3506B | ✅ SDK默认方案 | ❌ 旧方案 |
RK3506B SDK使用SPL/TPL方案:
rkbin/目录提供DDR初始化二进制 + U-Boot编译生成SPL。烧录时db命令将loader下载到SRAM执行DDR初始化,之后通过wl分别写入各分区镜像。
三、关键速查表
bring-up五阶段对照表
| 阶段 | 成功标志 | 失败表现 | 首要排查项 |
|---|---|---|---|
| ① BootROM | USB设备识别(2207:xxxx) | 电脑无任何反应 | 供电、BOOT引脚电平 |
| ② SPL/DDR | 串口打印"DDR init done" | 串口无任何输出 | DDR型号与bin是否匹配 |
| ③ U-Boot | 串口出现=>交互提示符 |
SPL后无输出或卡死 | 波特率、串口引脚mux |
| ④ 内核 | 打印"VFS: Mounted root" | Starting kernel后卡死 | 设备树、root=参数 |
| ⑤ 驱动验证 | 各外设功能正常 | 设备文件缺失、通信异常 | 逐个外设排查dts配置 |
烧录工具对比
| 工具 | 平台 | 适用场景 |
|---|---|---|
| RKDevTool | Windows(GUI) | 量产烧录、新手友好 |
| rkdeveloptool | Linux(命令行) | 开发调试、CI集成 |
| upgrade_tool | Linux(命令行) | 官方闭源工具、功能最全 |
| rkflash.sh | Linux(脚本) | SDK一键烧录 |
# rkdeveloptool 常用命令速查(SPL方案,RK3506B默认)
rkdeveloptool list # 列出USB设备
rkdeveloptool db rk3506_loader.bin # 下载Boot代码到SRAM(DDR初始化)
rkdeveloptool wl 0x40 idbloader.img # 写入idbloader到loader1分区
rkdeveloptool wl 0x4000 u-boot.itb # 写入U-Boot+Trust到loader2分区
rkdeveloptool wl 0x8000 boot.img # 写入内核到boot分区
rkdeveloptool wl 0x40000 rootfs.img # 写入根文件系统
rkdeveloptool rd # 复位设备
# 注意:ul命令仅miniloader方案使用,SPL方案不需要
四、实战步骤
第一步:SDK环境搭建
# ===== 宿主机:Ubuntu 22.04 LTS 推荐 =====
sudo apt update
sudo apt install -y build-essential git make gcc g++ \
python3 bc unzip dosfstools mtools parted \
libncurses-dev libssl-dev bison flex p7zip-full \
libudev-dev libusb-1.0-0-dev dh-autoreconf repo
# ===== 获取RK3506B SDK =====
mkdir -p ~/proj/rk3506 && cd ~/proj/rk3506
# 方式一:从SDK基础包解压
7z x rk3506_linux_release_v1.0.0a.7z.001
cd rk3506_sdk
# 方式二:repo同步(如有账号)
repo init -u <manifest_url> -b linux-6.1-stan-rkr4.1
repo sync -j16
SDK目录结构速览:
~/proj/rk3506/rk3506_sdk/
├── build.sh -> device/rockchip/common/scripts/build.sh # 编译入口
├── rkflash.sh -> device/rockchip/common/scripts/rkflash.sh # 烧录入口
├── u-boot/ # U-Boot 2017.09源码
├── kernel/ # Linux 6.1内核源码
├── buildroot/ # Buildroot 2024.02根文件系统
├── rkbin/ # Rockchip闭源二进制(RK3506_DDR_bin等)
├── device/rockchip/rk3506/ # 板级配置文件
├── prebuilts/ # 交叉编译工具链(arm-linux-gnueabihf-)
└── docs/ # 本地开发文档
第二步:USB烧录
RK3506B的MaskROM模式进入方式:
# ===== 进入烧录模式 =====
# 方法:按住BOOT/KEY2键 → 按复位键 → 松开复位 → 松开BOOT
# 或:按住USER1/KEY3键同上操作(部分板卡KEY3=Loader模式)
# ===== 确认设备识别 =====
# 在Linux开发机上
lsusb | grep 2207
# 输出: Bus 003 Device 005: ID 2207:3506 Fuzhou Rockchip Electronics Company
rkdeveloptool list
# 输出: DevNo=1 Vid=0x2207,Pid=0x3506, LocationID=xxx Maskrom
完整烧录流程:
RK3506B使用SPL/TPL方案,烧录命令如下(与miniloader方案不同,不需要ul和trust.img):
# ===== SPL/TPL方案:rkdeveloptool(RK3506B SDK默认)=====
# 编译安装 rkdeveloptool
git clone https://github.com/rockchip-linux/rkdeveloptool.git
cd rkdeveloptool
autoreconf -i && ./configure && make && sudo make install
# Step1: db命令下载loader到SRAM,初始化DDR
rkdeveloptool db rk3506_loader_v1.00.bin
# Step2: 写入idbloader(TPL+SPL)到loader1分区(LBA 0x40)
rkdeveloptool wl 0x40 idbloader.img
# Step3: 写入u-boot.itb(U-Boot+trust)到loader2分区(LBA 0x4000)
rkdeveloptool wl 0x4000 u-boot.itb
# Step4: 写入boot.img(内核+设备树)到LBA 0x8000
rkdeveloptool wl 0x8000 boot.img
# Step5: 写入根文件系统到LBA 0x40000
rkdeveloptool wl 0x40000 rootfs.img
# Step6: 复位设备启动
rkdeveloptool rd
# ===== Miniloader方案(旧方案,仅对比参考)=====
# rkdeveloptool db rkxx_loader_vx.xx.bin
# rkdeveloptool ul rkxx_loader_vx.xx.bin # ul = 写入loader到idb区域
# rkdeveloptool wl 0x4000 uboot.img # 注意:uboot.img ≠ u-boot.itb
# rkdeveloptool wl 0x6000 trust.img # miniloader需要单独的trust分区
# rkdeveloptool wl 0x8000 boot.img
# rkdeveloptool wl 0x40000 rootfs.img
# rkdeveloptool rd
# ===== 方案三:rkflash.sh(SDK自带一键脚本,推荐量产)=====
sudo ./rkflash.sh
重要区分:
rkdeveloptool db用于下载loader到SRAM做DDR初始化(两个方案都用)。ul命令仅miniloader方案使用,等价于wl 0x40 idbloader.img。SPL方案直接用wl 0x40 idbloader.img+wl 0x4000 u-boot.itb,不需要ul和单独的trust.img。参考Rockchip Boot_option
第三步:SPL阶段——DDR bring-up
SPL是整个bring-up最难的环节。成功时串口输出(以RK3506B SDK为例):
U-Boot SPL 2017.09 (Mar 19 2025 - 10:00:00 +0800)
DDRINFO: start DDR init
DDRINFO: ddr_rate = 1056MHz
DDRINFO: ddr_type = DDR3
DDRINFO: ddr_size = 256MB
DDRINFO: ddr init done
Trying to boot from MMC1
DDR适配三步法:
DDR验证命令:
# U-Boot命令行下快速验证
=> mw.l 0x80000000 0xdeadbeef 1 # 写入测试数据
=> md.l 0x80000000 1 # 读回验证
80000000: deadbeef # 一致则DDR读写正常
=> mtest 0x80000000 0x81000000 # 测试前16MB(快速检查)
# 通过标准: 无 "FAIL" 错误输出
# 全量DDR测试(需要较长时间)
=> mtest 0x80000000 0x90000000 # 256MB全量测试
# Linux下memtester(系统启动后)
memtester 200M 10 # 200MB跑10轮
第四步:U-Boot启动与调试
DDR跑通后,U-Boot正常启动日志:
U-Boot 2017.09 (Jan 15 2026 - 10:00:00 +0800)
CPU: rk3506 (3*Cortex-A7)
DRAM: 256 MiB
MMC: mmc@ff490000: 0, mmc@ff4b0000: 1
In: serial@ff0d0000
Out: serial@ff0d0000
Err: serial@ff0d0000
Model: Rockchip RK3506B EVB
Net: eth0: ethernet@ff010000, eth1: ethernet@ff011000
Hit any key to stop autoboot: 0
=>
U-Boot下常用调试命令:
# 查看环境变量
=> printenv bootargs
# 查看eMMC信息
=> mmc info
# 网络加载内核(TFTP调试利器)
=> setenv serverip 192.168.1.100
=> setenv ipaddr 192.168.1.200
=> tftpboot 0x81000000 zImage
=> bootz 0x81000000
# 手动挂载根文件系统启动
=> setenv bootargs 'console=ttyS0,115200 earlycon root=/dev/mmcblk0p5 rw rootwait'
=> boot
第五步:内核启动
正确的内核启动日志:
[ 0.000000] Booting Linux on physical CPU 0x0
[ 0.000000] Linux version 6.1.99 (builduser@buildhost)
[ 0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7)
[ 0.100000] Memory: 256MB total
[ 2.500000] VFS: Mounted root (ubifs filesystem) on device 0:14
rk3506 login:
内核卡死排查矩阵:
| 卡住位置 | 典型日志 | 优先排查 |
|---|---|---|
| 无任何内核输出 | Starting kernel ... 后无下文 |
设备树uart节点status、earlycon=参数地址 |
| 停在内存初始化 | Memory policy: ... 后停 |
kernel与SPL的DDR容量配置是否一致 |
| 停在VFS阶段 | VFS: Cannot open root device |
bootargs中root=路径 |
| 停在驱动加载 | 某驱动probe后无输出 | initcall_debug参数,逐个禁用可疑驱动 |
设备树检查要点:
# 确认dts中关键节点
chosen {
stdout-path = "serial0:115200n8"; # 确保调试串口
};
# bootargs中追加earlycon
earlycon=uart8250,mmio32,0xff0d0000 # 提前输出内核日志
第六步:驱动逐项验证
内核跑起来后,按外设类型逐项验证:
# ===== GPIO =====
cat /sys/kernel/debug/gpio
echo 24 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio24/direction
echo 1 > /sys/class/gpio/gpio24/value
# ===== I2C =====
i2cdetect -y 1 # 扫描I2C-1上的设备
i2cget -y 1 0x48 0x00 # 读传感器寄存器
# ===== 以太网 =====
ifconfig eth0 192.168.1.200 up
ping 192.168.1.1
# ===== CAN =====
ip link set can0 type can bitrate 500000
ip link set can0 up
candump can0 # 接收CAN数据
# ===== WiFi/BT =====
ifconfig wlan0 up
hciconfig hci0 up
# ===== eMMC/SD =====
mount /dev/mmcblk0p5 /mnt
dd if=/dev/zero of=/mnt/test.bin bs=1M count=10
五、常见问题解决
SPL/DDR阶段
| 现象 | 根因 | 解决 |
|---|---|---|
| 串口完全无输出 | DDR初始化卡死,SPL未执行 | ① 确认BOOT引脚电平正确 ② 检查各路供电(1.1V/1.35V/1.8V/3.3V) ③ 示波器测时钟晶振是否起振 |
DDR init done打印后无反应 |
SPL加载U-Boot失败 | 检查存储介质(eMMC/NAND)焊接、loader2分区位置 |
| 烧录成功但不起动 | SPL的DDR bin与颗粒不匹配 | 从SDK rkbin/换用正确颗粒的DDR bin;检查Rockchip_Support_List_DDR_Ver2.63.pdf确认颗粒是否在支持列表 |
| 烧录中途报错 | USB传输中断或DDR缓存出错 | rkdeveloptool db后等2秒再执行wl;换USB线或端口;确认进入MaskROM模式 |
U-Boot阶段
| 现象 | 根因 | 解决 |
|---|---|---|
| 串口乱码 | 波特率或时钟配置错误 | RK3506B默认波特率115200(部分老版本SDK用1500000),确认后用对应波特率连接 |
=>提示符下输入无响应 |
串口流控问题 | 终端软件关闭硬件流控(RTS/CTS) |
mmc init failed |
eMMC未识别 | 检查eMMC供电、CMD/DATA线焊接、dts中mmc节点 |
Net: No ethernet found |
以太网PHY未识别 | 检查PHY的reset引脚、MDIO地址、dts中phy-handle |
内核阶段
| 现象 | 根因 | 解决 |
|---|---|---|
Starting kernel ... 后无输出 |
内核与设备树不匹配或earlycon地址错 | 用SDK自带内核镜像先验证;检查UART物理地址是否与dts一致 |
Kernel panic - not syncing: VFS |
rootfs路径或文件系统类型错误 | 确认root=/dev/mmcblk0p5分区号,确认文件系统类型(ubifs/ext4) |
| 内核启动到一半panic | 驱动probe某外设时崩溃 | 在设备树中status = "disabled"逐个禁用可疑外设核验 |
| 启动后内存不足 | DDR容量配置偏小 | 检查SPL中DDR容量配置和设备树memory节点是否一致 |
六、总结
推荐bring-up顺序
经验总结
串口是唯一眼睛——DDR没通之前,除了串口你什么也看不见。first thing first: 让串口出字。
一次只改一个变量——先用参考板验证SDK镜像没问题,再换到新板子上。镜像和硬件分开排查。
DDR是最难的坎——SPL打印"DDR init done"才是真正的bring-up第一关。优先复用同颗粒的DDR bin,不要从零调时序。
稳定压倒一切——DDR通了马上跑
memtester大范围长时间测试。不稳定就降频,性能后面再说。