ARM 架构基础:Cortex-M 与指令集
ARM 架构基础:Cortex-M 与指令集
ARM 架构是嵌入式系统中最主流的处理器架构,拥有高性能、低功耗的特性。其中,Cortex-M 系列是专门为微控制器和实时控制系统设计的 32 位 RISC 处理器核心,广泛应用于 IoT、汽车电子、工业控制和消费类产品中。理解 Cortex-M 的体系结构和指令集,是掌握嵌入式底层开发的前提。
Cortex-M 处理器家族概览
Cortex-M 系列按性能和应用场景分为多个子系列,它们共享统一的 Thumb/Thumb-2 指令集和 NVIC 中断控制器,但功能特性逐步升级。
| 系列 | 核心特点 | 典型应用 |
|---|---|---|
| Cortex-M0 / M0+ | 极小面积、极低功耗,最小指令集 | 传感器节点、简单外设 |
| Cortex-M3 | 平衡性能与功耗,带硬件除法和 MPU | 通用嵌入式控制 |
| Cortex-M4 | 在 M3 基础增加 DSP 扩展与 FPU | 数字信号处理、电机控制 |
| Cortex-M7 | 六级流水线、双发射,带 FPU 和 Cache | 高性能实时控制 |
| Cortex-M23 / M33 | ARMv8-M 架构,TrustZone 安全 | 安全物联网设备 |
所有 Cortex-M 处理器都只支持 Thumb/Thumb-2 指令集,无需切换 ARM 状态,这使得代码密度高且执行效率优秀。
编程模型
操作模式与特权级别
Cortex-M 有两种操作模式,两种特权级别:
- 线程模式(Thread mode):执行普通应用程序代码,可运行在特权级或用户级。
- 处理模式(Handler mode):执行异常/中断服务程序,始终运行在特权级。
特权级别分为 特权级(Privileged) 和 用户级(Unprivileged)。用户级代码对某些寄存器和存储器区域受限,是安全应用的基础。
核心寄存器组
Cortex-M 拥有 16 个通用寄存器 (R0~R15) 和若干特殊功能寄存器。
- R0 ~ R12:通用数据寄存器,32 位,其中 R0~R7 为低组寄存器,几乎所有指令均可直接访问;R8~R12 为高组寄存器,部分 16 位 Thumb 指令不可直接访问。
- R13 (SP):堆栈指针。它有两个实体:主堆栈指针 MSP(复位后默认使用,用于异常处理)和进程堆栈指针 PSP(用于用户线程)。当前处于哪种模式由 CONTROL 寄存器决定。
- R14 (LR):链接寄存器,保存子程序或异常返回地址。
- R15 (PC):程序计数器,指向当前指令地址。直接修改 PC 可实现跳转,读取 PC 可以获得下一条指令地址(因流水线,值为当前地址 + 4)。
- xPSR:程序状态寄存器,分为 APSR(应用 PSR)、IPSR(中断号)、EPSR(执行 PSR)。常用标志 N(负数)、Z(零)、C(进位/借位)、V(溢出)在 APSR 中。
- CONTROL 寄存器:控制堆栈指针选择和特权级。
- PRIMASK:全局中断屏蔽寄存器,置 1 时屏蔽所有可配置优先级的中断(NMI 和 HardFault 除外)。
存储器映射采用统一编址
Cortex-M 使用 32 位地址总线,可寻址 4GB 存储空间。存储器映射由架构定义,规范了代码区、SRAM、外设、PPB 等区域。例如,0x00000000 起始存放初始 SP 和向量表;0x20000000 起始为片上 SRAM;0x40000000 起始外设区;0xE000E000 起始为系统控制块(SCB)和 NVIC。所有 I/O 寄存器映射到存储器地址,通过 LDR/STR 访问,没有专用 I/O 指令。
ARM 指令集:Thumb 与 Thumb-2
早期的 ARM 处理器支持 32 位 ARM 指令集和 16 位 Thumb 指令集,需要通过状态切换。Cortex-M 系列只实现了 Thumb-2 技术,它混合了 16 位和 32 位指令,无需状态切换。Thumb-2 既保留了高代码密度的 16 位指令,又增加了功能强大的 32 位指令,可以完成复杂操作而无需拆分成多条指令。
关键特性:
- 大多数 16 位指令只能访问 R0-R7,而 32 位指令可访问全部寄存器。
- 32 位指令可包含内置移位、扩展操作数,实现单指令完成乘加、加载/存储带更新等操作。
- 指令编码长度可通过上半核的半字自动识别,处理器自动解析,编程时无需关心。
Cortex-M 常用指令详解
以下指令均属于 Thumb-2 指令子集,适用于所有 Cortex-M 核心(部分高级指令需要特定架构版本,会特别说明)。
数据传送指令
| 指令 | 功能说明 | 示例 |
|---|---|---|
MOV Rd, #imm |
立即数传送到寄存器,仅限 8 位立即数? 32 位 MOV 可支持 16 位或更多 | MOV R0, #0x55 |
MOV Rd, Rm |
寄存器间传送 | MOV R1, R0 |
LDR Rd, [Rn, #offset] |
从存储器加载字,基址 Rn 加偏移 | LDR R0, [R1, #4] |
STR Rm, [Rn, #offset] |
将寄存器存储到存储器 | STR R0, [R1, #0x10] |
LDRH/LDRB/STRH/STRB |
加载/存储半字或字节 | LDRH R2, [R3, #2] |
LDM/STM |
多寄存器加载/存储 | LDMIA R0!, {R1-R3} |
注意:LDR/STR 的立即数偏移范围因指令长度而异,32 位指令支持更大立即数。R0! 表示回写,即基址更新。
数据处理指令
| 指令 | 功能说明 | 示例 |
|---|---|---|
ADD Rd, Rn, Rm |
加法 | ADD R0, R1, R2 |
ADD Rd, Rn, #imm3 |
加法,3位立即数 | ADD R0, R1, #5 |
SUB Rd, Rn, Rm |
减法 | SUB R0, R1, R2 |
AND/ORR/EOR |
按位与/或/异或 | AND R0, R1, R2 |
MVN Rd, Rm |
取反传送 | MVN R0, R1 |
LSL/LSR/ASR/ROR |
移位操作,可结合MOV使用 | LSL R0, R1, #3 |
SXTx/UXTx |
符号/零扩展 (32位指令) | SXTB R0, R1 |
比较与测试指令
这些指令不修改目标寄存器,仅更新 APSR 标志位。
CMP Rn, Rm或CMP Rn, #imm:计算 Rn - Rm,更新 N、Z、C、V。CMN Rn, Rm:计算 Rn + Rm,更新标志。TST Rn, Rm:按位与,更新 N、Z。TEQ Rn, Rm:按位异或,更新 N、Z。
通常跟随条件执行的分支指令,但 Cortex-M 大部分指令不直接支持条件码(只有条件分支和 IT 块)。可以利用与标志位配合的分支指令如 BEQ、BNE。
分支指令
| 指令 | 说明 | 示例 |
|---|---|---|
B label |
直接跳转,范围 ±16MB(32位指令更大) | B main_loop |
BL label |
带链接跳转,将返回地址存入 LR,用于函数调用 | BL func |
BX Rm |
跳转到寄存器所存地址,可切换 ARM/Thumb(Cortex-M 中保持 Thumb) | BX LR 用于返回 |
BLX Rm |
带链接的间接跳转 | BLX R3 |
CBZ/CBNZ |
比较为零/非零并跳转(16位指令) | CBZ R0, label |
条件分支则根据标志位:BEQ(等于),BNE(不等),BCS(进位置1),BCC(进位清0),BMI(负数),BPL(非负),BVS(溢出),BVC(无溢出),BHI(高于),BLS(低于或相同)等。
乘法和除法指令
Cortex-M3/M4/M7 提供硬件乘法和除法。
MUL Rd, Rn, Rm:32 位乘法,结果取低 32 位。MLS Rd, Rn, Rm, Ra:乘减,Rd = Ra - Rn*Rm。MLA Rd, Rn, Rm, Ra:乘加,Rd = Ra + Rn*Rm。UMULL/SMULL:长乘法,64 位结果。SDIV/UDIV:有符号/无符号除法。
Cortex-M4/M7 的 DSP 指令还包括单周期 16 位双乘法、饱和算术、SIMD 加/减等,需要汇编中显式指定。
位操作与比特带
Cortex-M3 及以上支持位操作,主要通过比特带(Bit Band)实现。存储器中有两个 1MB 的比特带区域(SRAM 和外设),每个字映射到一个别名区,对别名区的一个字的写入转换为对目标比特的原子置位/清零。汇编指令层面还可使用 BFC/BFI、UBFX/SBFX 等 32 位指令进行位域操作。
BFC Rd, #lsb, #width:将 Rd 的指定范围清零。BFI Rd, Rn, #lsb, #width:将 Rn 低 width 位插入 Rd 的指定位置。UBFX Rd, Rn, #lsb, #width:无符号提取位域并零扩展到 32 位。
状态寄存器访问与修饰指令
MRS Rd, <psr>:将特殊寄存器(如 PRIMASK、CONTROL)读取到通用寄存器。MSR <psr>, Rn:将通用寄存器的值写入特殊寄存器,常用于屏蔽中断或切换堆栈指针。CPSID I/CPSIE I:快速屏蔽/开启中断(写 PRIMASK)。WFI/WFE:等待中断/事件,进入低功耗状态。ISB/DSB/DMB:指令/数据/存储器屏障,保证流水线和存储器访问顺序,在操作寄存器前可能需要 DSB。
寻址模式
Cortex-M 指令支持多种灵活的寻址方式,使得结构化数据操作非常高效。
- 立即数偏移寻址:
LDR R0, [R1, #4],偏移量直接内嵌。 - 寄存器偏移寻址:
LDR R0, [R1, R2],R2 作为偏移。 - 前索引寻址:
LDR R0, [R1, #4]!,先将 R1=R1+4,再加载到 R0,等效于 *++ptr。 - 后索引寻址:
LDR R0, [R1], #4,先加载 R0 = *R1,再 R1=R1+4,等效于 *ptr++。 - PC 相对寻址:
LDR R0, [PC, #offset],常用于加载文本池中的常量。 - SP 相对寻址:
LDR R0, [SP, #offset],访问堆栈局部变量。
这些寻址模式也适用于 STR 和 LDRH/LDRB 等指令。
异常与中断模型
Cortex-M 使用统一的向量异常处理机制,由嵌套向量中断控制器 NVIC 管理。
- 异常向量表:位于存储器开始处(通常 0x00000000),每个表项为 4 字节,存放入口地址。前 16 个异常为系统异常(复位、NMI、HardFault、SVCall、PendSV、SysTick 等),之后是外部中断 IRQ0~IRQn。
- 异常进入:硬件自动压栈(依次压入 xPSR, PC, LR, R12, R3, R2, R1, R0),保存返回地址到 LR(特殊值 EXC_RETURN),从向量表加载对应处理程序地址到 PC,切换到 Handler 模式,使用 MSP。
- 异常返回:通过将 EXC_RETURN 写入 PC 触发返回,硬件自动出栈,恢复状态。通常用
BX LR或POP {PC}返回。 - 尾链优化:当一个异常结束时,若有待处理异常,可直接切换到新的异常处理,省去多余的压栈出栈。
- NVIC 优先级:支持最多 256 级优先级,高优先级可抢占低优先级,相同优先级则使用尾链。
在编程时,可以用 C 语言的 __attribute__((naked)) 或嵌入汇编进行异常处理,也可直接编写 void SysTick_Handler(void) 函数,编译器会自动生成返回指令。
汇编编程基础示例
以下是一个简单的汇编程序,实现 LED 闪烁控制,假设外设地址已知。
.syntax unified
.cpu cortex-m3
.thumb
.global _start
_start:
LDR R0, =0x40021018 @ 加载 RCC 时钟使能寄存器地址
LDR R1, [R0]
ORR R1, R1, #0x10 @ 使能 GPIO 端口 B 时钟
STR R1, [R0]
LDR R0, =0x40010C04 @ GPIOB 端口配置寄存器低地址 (CRL)
LDR R1, [R0]
BIC R1, R1, #0xF0 @ 清除 PB1 配置位
ORR R1, R1, #0x20 @ 设置为推挽输出,50MHz
STR R1, [R0]
loop:
LDR R0, =0x40010C0C @ GPIOB 输出数据寄存器 (ODR)
LDR R1, [R0]
EOR R1, R1, #0x02 @ 翻转 PB1
STR R1, [R0]
LDR R2, =0x100000 @ 简单延时循环
delay:
SUBS R2, R2, #1
BNE delay
B loop
上述代码使用 LDR 伪指令加载地址常量,ORR 置位,EOR 翻转位,实现了基础的 GPIO 控制。
总结
Cortex-M 架构通过统一的 Thumb-2 指令集、精简高效的寄存器模型和实时优化的异常处理机制,为嵌入式系统提供了强大的处理能力。掌握其编程模型、常用指令、寻址方式以及异常处理流程,是进行嵌入式固件开发或系统优化的基石。后续可结合具体芯片参考手册,深入实践外设驱动和实时任务设计。