ARM内核与寄存器详解
目录
ARM架构概述
ARM(Advanced RISC Microprocessor)是一种RISC(精简指令集计算机)处理器架构,最初由Acorn计算机公司设计,现在由ARM公司开发和授权。由于其低功耗和高性能特性,ARM处理器广泛应用于移动设备、嵌入式系统和物联网设备。
ARM架构已经发展了多个版本,从ARMv1到最新的ARMv9,不同版本引入了不同的功能和改进。主要的架构系列包括:
- Cortex-A系列:应用处理器,用于高性能系统
- Cortex-R系列:实时处理器,用于需要快速响应的系统
- Cortex-M系列:微控制器,用于低功耗嵌入式设备
ARM处理器模式
ARM处理器有多种操作模式,每种模式有不同的寄存器可见性和权限级别:
模式 | 描述 | 进入方式 |
---|---|---|
用户模式 | 普通程序执行 | 程序正常运行 |
系统模式 | 特权操作系统任务 | 软件切换 |
管理模式 | 系统保护模式 | 软件中断 |
中止模式 | 处理内存访问违例 | 数据或指令预取中止 |
未定义模式 | 处理未定义指令 | 遇到未定义指令 |
快速中断模式 | 高优先级中断处理 | FIQ中断 |
中断模式 | 中断处理 | IRQ中断 |
Cortex-M3内核的处理器模式
注意:Cortex-M3内核采用了简化的处理器模式系统,与传统ARM架构不同。Cortex-M3主要有两种运行模式:
- 线程模式(Thread Mode) - 用于运行应用程序代码
- 处理器模式(Handler Mode) - 用于处理所有异常
Cortex-M3还引入了特权级别的概念:
- 特权访问 - 可访问所有系统资源
- 非特权访问 - 受限制的资源访问
以下是Cortex-M3中不同情况的模式对应关系:
传统ARM模式 | Cortex-M3对应情况 | 实际例子 |
---|---|---|
用户模式 | 线程模式(非特权) | 运行普通应用程序代码,如循环计算任务 |
系统模式 | 线程模式(特权) | 操作系统执行特权操作,如初始化外设、配置MPU |
管理模式 | 通过SVC异常进入Handler模式 | 应用程序调用SVC 指令请求操作系统服务,如SVC #0 切换任务 |
中止模式 | MemManage异常进入Handler模式 | 程序访问MPU禁止的内存区域;设置了只读区域但尝试写入 |
未定义模式 | UsageFault异常进入Handler模式 | 执行了未支持的浮点指令;除零操作;未对齐的内存访问 |
快速中断模式 | 没有直接对应,所有中断进入Handler模式 | EXTI外部中断;高优先级的ADC转换完成中断 |
中断模式 | 没有直接对应,所有中断进入Handler模式 | UART接收完成中断;SysTick系统定时器中断 |
Cortex-M3异常处理示例:
-
硬故障(HardFault)例子:
- 程序尝试执行未映射内存区域的代码
- 访问了未对齐的内存地址
- 在中断处理过程中出现嵌套错误
-
SVC(管理模式)例子:
c// 通过SVC调用请求操作系统服务 __asm("SVC #42"); // 调用42号系统服务,例如RTOS切换任务
-
BusFault例子:
c// 访问无效的外设地址 volatile uint32_t *ptr = (uint32_t *)0x60000000; // 假设这是无效总线地址 *ptr = 0x1234; // 触发BusFault异常
-
使用故障例子:
c// 未对齐的访问(如果启用了对齐检查) volatile uint32_t *ptr = (uint32_t *)0x20000001; // 非4字节对齐地址 *ptr = 0x1234; // 触发UsageFault异常
Cortex-A系列处理器模式
Cortex-A系列处理器保留了传统ARM架构的7种处理器模式,并增加了安全扩展和虚拟化扩展模式。以下是Cortex-A核在不同情况下的模式例子:
模式 | Cortex-A对应情况 | 实际例子 |
---|---|---|
用户模式 | 低特权应用程序 | 手机上运行的普通APP;Linux用户空间程序 |
系统模式 | 高特权操作系统代码 | 操作系统内核执行特权任务;驱动程序操作硬件 |
管理模式 | 系统调用处理 | 应用程序通过SWI /SVC 请求系统服务;Android的Binder调用 |
中止模式 | 内存访问违例处理 | 程序访问受MMU保护的内存区域;Linux中的段错误(SIGSEGV) |
未定义模式 | 处理未识别指令 | 程序执行平台不支持的指令;尝试执行NEON指令但硬件不支持 |
快速中断模式 | 高优先级外设中断 | DMA传输完成;高速存储器控制器中断;显示刷新中断 |
中断模式 | 普通外设中断处理 | 触摸屏输入;按键中断;传感器数据就绪中断 |
监视模式 | TrustZone安全世界 | 安全认证;指纹处理;支付系统隔离 |
虚拟机模式 | 虚拟化支持(ARMv7-A) | 虚拟机管理程序;Docker容器环境切换 |
Cortex-A与Cortex-M主要区别:
-
处理器模式实现:
- Cortex-A: 完整的7种模式+扩展模式,通过CPSR控制
- Cortex-M: 简化为线程模式和处理器模式两种,通过异常入口管理
-
特权级别:
- Cortex-A: 通过处理器模式区分特权
- Cortex-M: 明确的特权/非特权状态区分
-
异常处理:
- Cortex-A: 使用向量表+模式切换
- Cortex-M: 统一的异常机制,自动保存/恢复上下文
Cortex-A异常处理示例:
c
// Cortex-A中的异常向量表设置
// 通常在汇编启动文件中定义
void vectors(void) __attribute__((section("vectors")));
void vectors(void) {
asm("b reset_handler"); // 复位处理
asm("b undefined_handler"); // 未定义指令
asm("b svc_handler"); // 软件中断(SVC)
asm("b prefetch_handler"); // 取指中止
asm("b data_handler"); // 数据中止
asm("b unused_handler"); // 未使用
asm("b irq_handler"); // 中断
asm("b fiq_handler"); // 快速中断
}
// SVC示例 - Linux系统调用
int main() {
int result;
// 调用write系统调用(4号)
asm("mov r0, #1"); // 文件描述符1(stdout)
asm("ldr r1, =message"); // 消息缓冲区
asm("mov r2, #13"); // 消息长度
asm("mov r7, #4"); // write系统调用号
asm("swi #0"); // 执行系统调用
asm("mov %0, r0" : "=r" (result));
return 0;
}
ARM寄存器集
通用寄存器
ARM架构提供16个32位通用寄存器(R0-R15),其中R13-R15有特殊用途:
寄存器 | 别名 | 描述 | 使用约定 |
---|---|---|---|
R0 | - | 通用寄存器 | 第一个函数参数,函数返回值 |
R1-R3 | - | 通用寄存器 | 函数参数 |
R4-R11 | - | 通用寄存器 | 需在函数调用间保存 |
R12 | IP | 程序内部暂存寄存器 | 过程调用中临时使用 |
R13 | SP | 堆栈指针 | 指向当前堆栈顶部 |
R14 | LR | 链接寄存器 | 保存子程序返回地址 |
R15 | PC | 程序计数器 | 指向当前执行指令 |
程序计数器(PC)
PC寄存器(R15)保存当前执行指令的地址。在ARM状态下,PC指向当前指令地址+8;在Thumb状态下,PC指向当前指令地址+4。
使用方法:
- 读取PC获得当前指令附近的地址
- 向PC写入值实现跳转
assembly
MOV R0, PC @ 获取当前PC值
MOV PC, LR @ 从子程序返回
链接寄存器(LR)
LR寄存器(R14)用于存储子程序返回地址。当执行BL(分支并链接)指令时,返回地址被自动保存到LR中。
使用方法:
- 调用子程序前保存LR(如果子程序内还会调用其他函数)
- 子程序返回时将LR的值复制到PC
assembly
PUSH {LR} @ 保存返回地址
BL subroutine @ 调用子程序
POP {PC} @ 恢复返回地址并返回
堆栈指针(SP)
SP寄存器(R13)指向当前堆栈顶部。ARM通常采用满递减(Full Descending)堆栈,即SP指向最后一个已入栈的数据项。
使用方法:
- PUSH操作前先减少SP,再存储
- POP操作先加载,再增加SP
assembly
PUSH {R0-R3} @ 将R0-R3压入堆栈
POP {R0-R3} @ 从堆栈弹出到R0-R3
状态寄存器(CPSR/SPSR)
当前程序状态寄存器(CPSR)和保存的程序状态寄存器(SPSR)包含处理器状态信息。
CPSR字段:
- 条件标志(N,Z,C,V):用于条件执行
- 控制位:处理器模式、中断禁用标志、指令集状态等
条件标志:
- N(负数):结果为负
- Z(零):结果为零
- C(进位):产生进位
- V(溢出):有符号溢出
assembly
CMP R0, R1 @ 比较R0和R1,设置条件标志
ADDPL R0, R0, #1 @ 如果结果非负(N=0)则执行加法
协处理器寄存器
ARM架构支持协处理器扩展,包括CP15系统控制协处理器。CP15寄存器控制缓存、MMU、系统控制和配置。
访问方法:
assembly
MRC p15, 0, R0, c1, c0, 0 @ 读取CP15 c1寄存器到R0
MCR p15, 0, R0, c1, c0, 0 @ 写入R0到CP15 c1寄存器
NEON和VFP寄存器
现代ARM架构包含NEON和VFP(向量浮点)扩展,提供额外的寄存器用于SIMD和浮点运算:
- 32个64位寄存器(D0-D31)
- 也可视为16个128位寄存器(Q0-Q15)
assembly
VMOV.F32 S0, #1.0 @ 加载浮点常量到S0
VADD.F32 S0, S0, S1 @ 浮点加法
VLDM R0, {D0-D3} @ 加载多个64位寄存器
寄存器使用规范
ARM架构定义了AAPCS(ARM架构过程调用标准)规范:
- R0-R3:参数传递和结果返回,调用者保存
- R4-R11:局部变量,被调用者保存
- R12(IP):内部过程调用暂存,调用者保存
- R13(SP):堆栈指针,被调用者保存
- R14(LR):链接寄存器,被调用者保存
- R15(PC):程序计数器
参数传递机制
当函数参数超过4个时,ARM采用以下策略:
- 前4个参数通过R0-R3寄存器传递
- 额外的参数通过栈传递,从右到左入栈
- 参数在栈上按照4字节对齐
- 被调用者负责从栈上获取额外参数
示例:调用有6个参数的函数func(a, b, c, d, e, f)
assembly
MOV R0, #1 @ 第一个参数 a
MOV R1, #2 @ 第二个参数 b
MOV R2, #3 @ 第三个参数 c
MOV R3, #4 @ 第四个参数 d
PUSH {R5, R6} @ 将第五和第六个参数入栈 (f先入栈,e后入栈)
MOV R5, #6 @ 第六个参数 f (先入栈)
MOV R6, #5 @ 第五个参数 e (后入栈)
PUSH {R5, R6}
BL func @ 调用函数
ADD SP, SP, #8 @ 调用完成后恢复栈(清理参数)
对于返回值:
- 32位或更小的返回值保存在R0中
- 64位返回值使用R0和R1
- 更大的结构体通过引用返回,调用者提供内存地址作为隐式第一个参数
常见ARM指令与寄存器操作
数据处理指令:
assembly
MOV R0, R1 @ R0 = R1
ADD R0, R1, R2 @ R0 = R1 + R2
SUB R0, R1, #1 @ R0 = R1 - 1
AND R0, R1, #0xFF @ R0 = R1 & 0xFF
内存访问指令:
assembly
LDR R0, [R1] @ 从R1指向的地址加载到R0
STR R0, [R1, #4] @ 存储R0到R1+4指向的地址
LDMIA R1!, {R0-R4} @ 多寄存器加载,递增后更新R1
STMDB R13!, {R0-R3} @ 多寄存器存储,递减前更新R13
分支指令:
assembly
B label @ 无条件分支
BL function @ 分支并链接(调用子程序)
BX LR @ 分支并切换状态(常用于返回)
CMP R0, #0 @ 比较R0与0
BEQ zero_label @ 相等时分支
条件执行:
assembly
ADDEQ R0, R0, R1 @ 当Z=1时执行加法
MOVNE R0, #0 @ 当Z=0时执行赋值