2、嵌入式系统基础:ARM体系结构、存储器映射、启动流程、开发板硬件资源分析

嵌入式Linux系统移植的本质,是在特定的硬件平台上构建从引导程序(u-boot)、操作系统内核(Kernel)到根文件系统(rootfs)的完整软件栈。而这一切工作的起点,是对底层硬件资源的深刻理解。本章作为整个课程的地基,将围绕ARM体系结构、存储器映射、典型启动流程以及开发板硬件资源分析四个维度展开,帮助读者建立从芯片手册到板级电路的系统性认知框架。只有清晰掌握CPU如何取指、如何访问外设、如何上电复位,才能在后续移植u-boot和内核时做到“知其然,更知其所以然”。

ARM嵌入式系统启动与存储映射总览 ARM Cortex-A 处理器 内核 | MMU | Cache | GIC Boot ROM (iROM) 固化代码,选择启动设备 启动跳转 DDR SDRAM 主存储器 (0x40000000) 加载运行 SD/eMMC 存储u-boot/内核/rootfs SPI NOR Flash 存储u-boot镜像 NAND Flash 大容量存储 启动设备选择 加载镜像 外设总线与存储器映射 UART (0x10000000) | GPIO (0x11000000) | I2C (0x12000000) | SPI (0x13000000) | USB (0x14000000) 通过AXI/APB/AHB总线桥接,CPU以内存映射方式访问外设寄存器 总线矩阵 启动流程: Boot ROM → 加载u-boot到SRAM/DDR → u-boot初始化DDR → 加载内核 → 挂载rootfs

上图展示了ARM嵌入式系统启动与存储映射的核心链路。从处理器视角看,所有外设和存储器统一编址,CPU通过地址总线访问它们。启动流程则是一个典型的“多级引导”过程:芯片内部固化的Boot ROM首先运行,根据硬件引脚电平或eFuse状态选择启动介质,然后将u-boot镜像从SD卡或Flash中加载到内部SRAM或DDR中执行。理解这张图,是后续配置u-boot环境变量和内核设备树(Device Tree)的基础。

2.1 ARM体系结构核心原理

ARM处理器采用精简指令集(RISC)架构,其核心设计哲学是“简单、高效、低功耗”。在嵌入式Linux领域,我们主要关注Cortex-A系列(应用处理器)和Cortex-R系列(实时处理器),其中Cortex-A系列是运行Linux的主流选择。ARM体系结构的关键特征包括:加载/存储(Load/Store)架构——只有Load和Store指令可以访问内存,所有运算都在寄存器中完成;固定长度指令——ARM模式每条指令32位,Thumb模式16位,便于流水线设计;条件执行——大多数指令可附带条件码,减少分支跳转开销。

从编程模型角度看,ARM处理器提供16个32位通用寄存器(R0-R15),其中R13(SP)为栈指针,R14(LR)为链接寄存器,R15(PC)为程序计数器。此外还有当前程序状态寄存器(CPSR),用于保存条件码标志、中断禁止位、处理器模式等信息。ARM支持多种处理器模式(User、FIQ、IRQ、Supervisor、Abort、Undefined、System),每种模式拥有独立的寄存器组,这是实现操作系统上下文切换和中断处理的硬件基础。

寄存器功能在Linux中的作用
R0-R12通用寄存器函数参数传递、局部变量存储
R13 (SP)栈指针内核栈/用户栈切换
R14 (LR)链接寄存器保存函数返回地址
R15 (PC)程序计数器指令取指地址
CPSR程序状态寄存器中断控制、模式切换、条件标志

在系统移植中,我们需要特别关注MMU(内存管理单元)和Cache。MMU负责虚拟地址到物理地址的转换,并提供了内存访问权限控制。Linux内核依赖MMU实现进程地址空间隔离。Cache则用于缓解CPU与内存之间的速度差异,分为I-Cache(指令缓存)和D-Cache(数据缓存)。在u-boot阶段,通常先关闭MMU和Cache(使用物理地址直接访问),待内核启动时再逐步开启。

2.2 存储器映射与地址空间

ARM处理器使用统一的地址空间,即内存和外设寄存器都映射到同一个32位或64位地址空间中。以典型的32位Cortex-A9为例,地址空间为4GB(0x00000000 - 0xFFFFFFFF)。芯片设计者会将这个空间划分为多个区域:DDR内存通常映射在低地址(如0x40000000),片上SRAM映射在0x10000000附近,外设寄存器则映射在0x10000000以上的高地址区域。这种设计使得CPU访问外设就像访问内存一样简单——只需向特定地址写入或读取数据即可。

在嵌入式Linux中,存储器映射信息通过设备树(Device Tree)文件描述。设备树是一种描述硬件资源的数据结构,它告诉内核“在哪里找到什么设备、如何配置它”。例如,UART控制器的寄存器基地址、中断号、时钟频率等信息都记录在设备树节点中。u-boot在启动内核时,会将设备树二进制文件(DTB)加载到内存中,并将起始地址传递给内核。

关键概念:物理地址(PA)与虚拟地址(VA)。在u-boot阶段,我们直接使用物理地址操作硬件。例如,初始化DDR控制器时,需要向DDR控制器的寄存器物理地址写入配置值。而Linux内核启动后,所有外设驱动都使用虚拟地址访问寄存器,通过ioremap()函数将物理地址映射到内核虚拟地址空间。理解这一区别,对于调试驱动问题至关重要。

典型的存储器映射布局如下表所示(以某Cortex-A9平台为例):

地址范围大小用途
0x00000000 - 0x0000FFFF64KBBoot ROM (iROM)
0x02000000 - 0x0201FFFF128KB内部SRAM
0x10000000 - 0x1000FFFF64KBUART0控制器
0x11000000 - 0x1100FFFF64KBGPIO控制器
0x12000000 - 0x1200FFFF64KBI2C控制器
0x40000000 - 0xBFFFFFFF2GBDDR SDRAM(内存)

在移植过程中,我们需要仔细阅读芯片参考手册中的“Memory Map”章节,确认每个外设的基地址和地址范围。一个常见的错误是外设地址配置错误,导致内核无法访问设备寄存器,表现为驱动初始化失败或系统崩溃。

2.3 典型启动流程分析

嵌入式Linux系统的启动流程可以概括为四个阶段:Boot ROM → u-boot → Linux内核 → rootfs。每个阶段都有明确的职责和交接协议。

  1. Boot ROM阶段:芯片上电后,CPU从复位向量地址(通常是0x00000000)开始执行固化在ROM中的代码。Boot ROM的主要任务是初始化最基本的时钟和存储控制器,然后根据启动引脚(如OM[4:0])或eFuse配置,从SD卡、eMMC、NAND Flash或UART等介质中读取u-boot镜像到内部SRAM中。由于SRAM容量有限(通常只有几十KB到几百KB),Boot ROM只加载u-boot的前一部分(称为SPL或BL1),由SPL完成DDR初始化后再加载完整的u-boot。
  2. u-boot阶段:u-boot是通用的引导加载程序,它负责初始化DDR、串口、Flash等关键外设,然后从存储介质中读取Linux内核镜像(uImage或zImage)和设备树文件(DTB)到DDR中。u-boot还提供交互式命令行,方便开发者调试启动参数。最终,u-boot通过跳转指令将控制权交给内核,并传递启动参数(如内存大小、控制台设备、根文件系统位置等)。
  3. Linux内核阶段:内核启动时,首先进行解压缩(如果是zImage),然后初始化MMU、建立页表、初始化中断控制器、时钟系统等。内核会解析设备树,注册平台设备,并挂载根文件系统。如果根文件系统是initramfs(内嵌在内核中),则直接使用;如果是外部存储设备(如SD卡分区),则需要通过启动参数指定根设备。
  4. rootfs阶段:内核挂载根文件系统后,执行/sbin/init程序(通常是busybox或systemd),init进程负责启动系统服务、配置网络、加载驱动模块等,最终呈现用户态环境。

实战经验:在调试启动流程时,建议在u-boot中开启串口日志(CONFIG_DEBUG_UART),并设置bootdelay=3以便在启动时中断进入命令行。通过printenv查看环境变量,使用mmc read、sf read等命令手动读取存储介质内容,验证镜像是否烧写正确。如果内核启动卡住,可以通过earlyprintk内核参数获取早期启动日志。

2.4 开发板硬件资源分析

在进行系统移植之前,必须对开发板的硬件资源进行全面梳理。以常见的i.MX6ULL开发板为例,我们需要关注以下资源:

  • 处理器:NXP i.MX6ULL,Cortex-A7内核,主频528MHz,支持NEON SIMD指令集。
  • 内存:512MB DDR3L,16位总线宽度,工作频率400MHz。
  • 存储:8GB eMMC、Micro SD卡槽、SPI NOR Flash(可选)。
  • 网络:10/100M以太网(RMII接口),使用LAN8720A PHY芯片。
  • 显示:RGB LCD接口,支持4.3寸/7寸电容触摸屏。
  • 串口:UART1作为调试串口,UART2/3/4扩展。
  • USB:USB OTG和USB Host接口。
  • 其他:GPIO、I2C、SPI、PWM、ADC等通用外设。

分析硬件资源时,需要对照原理图和芯片手册,确认以下关键信息:DDR的型号和时序参数(用于配置u-boot中的DDR初始化代码);eMMC的接口模式(SDIO或MMC);PHY芯片的地址和复位引脚;调试串口的引脚复用(IOMUX配置)。这些信息将直接决定u-boot和内核的设备树配置。

常见误区:很多初学者直接使用开发板厂商提供的默认配置,而不去理解硬件细节。例如,更换了不同型号的DDR芯片后,仍然沿用旧的时序参数,导致系统随机死机或启动失败。正确的做法是:根据DDR芯片的数据手册,计算刷新周期、CAS延迟等参数,并在u-boot的board_init.c中修改对应的寄存器配置。同样,如果PHY芯片的地址不是默认的0x01,需要修改设备树中的phy-handle和reg属性。

硬件资源分析的最终产出是一份“板级配置清单”,包括:内存映射表、中断号分配表、GPIO复用表、时钟树配置。这份清单是编写设备树和u-boot板级文件(board.c)的依据。建议使用表格形式整理,便于后续查阅和团队协作。

资源类型型号/规格关键参数移植关注点
DDR3LNT5CC256M16DP512MB, 16bit, 400MHz时序参数、地址映射
eMMCKLMSGAGEAC8GB, HS200模式SDIO接口配置、分区表
PHYLAN8720ARMII接口, 地址0x00复位GPIO、时钟源
调试串口UART1115200, 8N1IOMUX复用、时钟使能