3、了解目标开发板:以QEMU模拟的ARM Versatile Express开发板为例
好,咱们进入第三章。这一章,咱们得把目标开发板摸透。
很多新手移植Buildroot,上来就改配置、编内核,结果板子起不来。为什么?说白了,你连板子的“脾气”都没摸清。我刚开始做嵌入式那会儿,也犯过这毛病。后来带团队,我要求每个新人必须先花一天时间,把开发板的硬件手册翻烂。
今天,咱们以QEMU模拟的ARM Versatile Express开发板为例。别小看它是模拟的,它的硬件架构和真实板卡几乎一致。你把它搞懂了,换任何一款Cortex-A系列板子,思路都一样。
3.1 CPU架构:ARM Cortex-A9
Versatile Express(简称VE)板,QEMU默认模拟的是Cortex-A9 MPCore。这是个啥水平?
- 核心数:最多4核,QEMU默认给1~4核都行。我个人习惯用单核先调通,再开SMP。
- 指令集:ARMv7-A架构,支持ARM、Thumb、Thumb-2指令集。注意,它不支持AArch64,那是ARMv8的事。
- 缓存:L1指令/数据缓存各32KB,L2缓存统一1MB(由PL310控制器管理)。
- 内存管理:标配MMU,支持页表映射。没有MMU,Linux跑不起来。
重要:在Buildroot中配置目标架构时,必须选择 ARM (little endian),目标变体选 cortex-a9。选错了,编译出来的内核根本跑不了。
我记得有一次,同事把目标架构选成了ARMv5,结果内核在启动时直接panic。查了半天,发现是编译器生成的指令集不兼容。嗯,这种坑踩过一次就记住了。
3.2 内存布局:从0x00000000开始
内存布局,是移植过程中最容易出问题的地方。你想想看,内核、设备树、根文件系统,都得放在正确的位置上。
QEMU模拟的VE板,内存布局是这样的:
| 起始地址 | 大小 | 用途 |
|---|---|---|
| 0x00000000 | 64MB | 第一个内存块(默认) |
| 0x04000000 | 64MB | 第二个内存块(可选) |
| 0x10000000 | — | 外设内存映射区 |
| 0x1e000000 | — | PCIe配置空间 |
这里有个关键点:内核的加载地址。QEMU默认把内核加载到 0x60010000(这是QEMU内部约定)。但如果你用U-Boot引导,就得按U-Boot的规则来。
我的建议:刚开始移植,别折腾U-Boot。直接用QEMU的 -kernel 参数加载内核,省心。等Buildroot跑通了,再回头搞bootloader。
我曾经在一个项目里,因为内存地址配错了,内核解压后把自己覆盖了。那感觉,就像你刚写完代码,一编译发现硬盘满了——欲哭无泪。
3.3 外设接口:该有的都有
VE板的外设,虽然不如真实板卡丰富,但该有的都有。咱们挑几个关键的说说。
3.3.1 串口(UART)
这是嵌入式开发的“生命线”。VE板用的是PL011 UART,地址在 0x10009000。内核启动时,所有打印信息都从这里出来。
在Buildroot中,你不需要额外配置。内核默认把PL011作为控制台。但如果你自己写bootloader,记得初始化这个串口。
注意:QEMU默认把串口输出重定向到终端。如果你用 -nographic 参数,串口会映射到stdio。这时候,你按Ctrl+A然后按X,可以退出QEMU。别问我怎么知道的——我第一次用的时候,愣是不知道怎么退出,直接关了终端。
3.3.2 网络(以太网)
VE板模拟了SMSC9118以太网控制器。地址在 0x4e000000。这个网卡很常见,Linux内核自带驱动。
在Buildroot中,你只需要确保内核配置里选中了 SMSC911X 驱动。然后QEMU启动时加上 -netdev user,id=net0 -device smsc9118,netdev=net0,网络就通了。
我个人习惯用user模式网络,简单。但如果你需要更复杂的网络拓扑,可以用tap模式。
3.3.3 存储(SD卡/MMC)
VE板模拟了PL181 MMC控制器。你可以把根文件系统放在SD卡镜像里。QEMU用 -sd 参数挂载。
这里有个坑:PL181的驱动在Linux内核里默认是编译成模块的。如果你忘了把它编进内核,根文件系统就挂不上。我曾经因为这个,折腾了整整一个下午。
3.3.4 其他外设
- 定时器:SP804双定时器,内核用它做时钟源。
- 中断控制器:GIC-400,支持SPI和PPI中断。
- 显示:CLCD控制器,可以输出到QEMU的图形窗口。
- 键盘/鼠标:通过QEMU的输入子系统模拟。
3.4 设备树:板子的“身份证”
现代ARM Linux,都靠设备树(Device Tree)来描述硬件。VE板的设备树文件在Linux内核源码里:
arch/arm/boot/dts/vexpress-v2p-ca9.dts
这个文件描述了所有外设的地址、中断号、时钟等信息。Buildroot编译内核时,会自动生成对应的 .dtb 文件。
你可以打开这个文件看看。里面定义了内存大小、串口地址、网卡信息等等。如果你要移植到真实板卡,改的就是这个文件。
核心要点:设备树里的 reg 属性,必须和硬件手册一致。差一个字节,外设就起不来。我见过有人把 0x10009000 写成了 0x10009001,结果串口死活不输出。嗯,这种错误,查起来真要命。
3.5 启动流程:从QEMU到Linux
咱们简单过一遍启动流程。你心里有个数,后面移植时就知道每一步在干什么。
- QEMU启动:模拟CPU上电,从
0x00000000开始执行。 - Bootloader(可选):如果用U-Boot,它会初始化硬件,加载内核和设备树。
- 内核解压:内核自解压,然后跳转到入口点。
- 内核初始化:解析设备树,注册外设驱动,挂载根文件系统。
- 启动用户空间:执行
/sbin/init,启动Buildroot构建的根文件系统。
在QEMU里,如果你用 -kernel 参数,QEMU会跳过bootloader,直接加载内核。这对调试来说,省了不少事。
3.6 本章小结
好,咱们把VE板摸了一遍。总结几个关键点:
- CPU:Cortex-A9,ARMv7-A,选对Buildroot的架构配置。
- 内存:从0x00000000开始,内核加载地址要记牢。
- 外设:串口PL011、网卡SMSC9118、MMC控制器PL181,驱动都得编进内核。
- 设备树:板子的硬件描述,移植时改的就是它。
下一章,咱们开始动手搭建Buildroot环境。到时候,你会用到今天学的这些知识。别急,一步一步来。