ARM Cortex-M 体系结构深度解析——寄存器模型、处理器模式、AAPCS 与异常模型

发布时间:2026/7/6 14:44:06

ARM Cortex-M 体系结构深度解析——寄存器模型、处理器模式、AAPCS 与异常模型 一、引言ARM Cortex-M 系列是当前嵌入式 MCU 市场占有率最高的处理器架构。从 STM32F103 的 Cortex-M3 到 STM32F407 的 Cortex-M4F再到 GD32、nRF52、RP2040这些芯片的内核架构高度统一。理解 Cortex-M 体系结构就是掌握嵌入式的内功。本文从以下维度深度解析ARM 公司商业模式与产品线RISC vs CISC为什么 ARM 是嵌入式首选Cortex-M 寄存器模型16 个关键寄存器MSP 与 PSP 双栈指针机制AAPCS 调用约定与栈帧分析异常模型与中断处理位带操作与存储器映射Cortex-M3 vs M4 vs M0 核心差异二、ARM 体系结构基础2.1 ARM 公司的商业模式ARM 不造芯片只卖设计方案IP 授权。这种模式决定了其生态的丰富性ARM 设计内核 → 授权给芯片厂商 → 厂商添加外设 → 生产芯片 ↑ ↑ ARM Cortex-M3 STM32F1 / GD32F103 / APM32 ARM Cortex-M4 STM32F4 / nRF52840 / AT32F4 ARM Cortex-M0 STM32F0 / RP2040 / MM322.2 RISC vs CISC 对比ARM 是典型的 RISC精简指令集计算机架构。特性RISCARMCISCx86指令数量少精选常用指令多几百条复杂指令指令长度固定32位 ARM / 16位 ThumbThumb-2 为 16/32 位混合可变1~15字节执行周期大多数 1 个时钟周期复杂指令需多个周期寻址方式少Load/Store 架构多内存可直接运算寄存器数量多16~31 个少8 个通用寄存器功耗低高编译器依赖高靠编译器优化低硬件完成复杂操作面试必问ARM 是 Load/Store 架构——所有运算只在寄存器之间进行数据必须先从内存 Load 到寄存器运算完再 Store 回内存。不能像 x86 那样直接对内存地址做加法。2.3 Cortex-M/A/R 产品线定位系列定位架构版本关键特征典型产品M0/M0超低功耗 MCUARMv6-M2级流水线无MPUSTM32F0, RP2040M3主流 MCUARMv7-M3级流水线MPU可选STM32F1, GD32F103M4DSPFPUARMv7E-M单精度FPU DSP指令STM32F4, nRF52840M7高性能 MCUARMv7E-M6级流水线ICache/DCacheSTM32H7, i.MX RTM33安全 MCUARMv8-MTrustZone安全扩展STM32L5, nRF5340R5/R52实时核ARMv7-R多核锁步低延迟TMS570汽车级A53/A72应用核ARMv8-AMMU 64位支持RK3588, i.MX8三、Cortex-M 寄存器模型3.1 16 个核心寄存器一览低寄存器Caller-saved被调函数不保护 R0 ──── a1 ──── 函数参数/返回值 R1 ──── a2 ──── 函数参数 R2 ──── a3 ──── 函数参数 R3 ──── a4 ──── 函数参数 ​ 高寄存器Callee-saved被调函数必须保存 R4 ──── v1 ──── 通用 R5 ──── v2 ──── 通用 R6 ──── v3 ──── 通用 R7 ──── v4 ──── 通用 R8 ──── v5 ──── 通用 R9 ──── v6 ──── 通用 R10 ─── v7 ──── 通用 R11 ─── v8 ──── 通用 --- 以上 8 个是 PendSV 手动保存的寄存器 ​ 特殊寄存器 R12 ─── IP ──── 临时链接器使用 R13 ─── SP ──── 栈指针MSP PSP 两个 R14 ─── LR ──── 链接寄存器存返回地址 / EXC_RETURN R15 ─── PC ──── 程序计数器PC 当前地址 4流水线效应3.2 程序状态寄存器 xPSRxPSR 实际上由 3 个独立的寄存器组合而成xPSR32位由 APSR EPSR IPSR 组合而成 31 30 29 28 27 26:25 24 23:20 19:16 15:10 9 8 7:0 ┌────┬────┬────┬────┬──┬───────┬──┬────────┬───────┬───────┬────┬──┬──────────┐ │ N │ Z │ C │ V │ Q │ ICI/IT│ T │ 保留 │ GE │ ICI/IT│ IT │ Exception │ │负数│零 │进位│溢出│饱│中断续 │Thumb│(RAZ) │大于等 │中断续 │IF- │ Number │ │ │标志│标志│标志│和│续指令 │位(1)│ │于标志 │续指令 │Then│(IPSR,低9位)│ └────┴────┴────┴────┴──┴───────┴──┴────────┴───────┴───────┴────┴──┴──────────┘NZCVbit 31~28条件标志位由比较和算术指令设置Qbit 27DSP 饱和标志M4/M7 使用M3 保留ICI/ITbit 26~25 和 bit 15~10中断可继续指令 / If-Then 指令状态位EPSRTbit 24必须为 1——Cortex-M 只支持 Thumb/Thumb-2 指令集GE[3:0]bit 19~16大于等于标志SIMD 指令使用M4/M7IPSRbit 8~0当前异常号Exception Number只读// 读取 xPSR 中的异常号IPSR 位于低 9 位 uint32_t ulActiveIRQ (__get_xPSR() 0x1FF); // 0 线程模式 // 2 NMI // 3 HardFault // 11 SVC系统服务调用 // 14 PendSV可挂起系统调用 // 15 SysTick系统滴答定时器 // 16 外设中断IRQ0 ~ IRQn3.3 MSP 与 PSP为什么要两个栈指针特性MSP主栈指针PSP进程栈指针用途中断处理 OS 内核用户任务复位默认✅ 使用 MSP需要 OS 手动切换到 PSP中断中始终使用 MSP不使用CONTROL.SPSEL0使用 MSP—CONTROL.SPSEL1—使用 PSP裸机程序只用 MSP不使用RTOS 场景中断/内核用 MSP每个任务各有自己的 PSP裸机程序 Thread 模式 ──→ 使用 MSP 中断发生 ──→ 仍使用 MSP在中断中 RTOS 程序 任务 A ──→ 使用 PSP_A独立的栈空间 任务 B ──→ 使用 PSP_B独立的栈空间 触发中断 ──→ 切换回 MSP内核栈 ★ 这就是 RTOS 的栈隔离——无论哪个任务触发中断 压栈都在 MSP 所在的主栈上进行不占用任务栈空间3.4 函数调用时的寄存器使用AAPCSAAPCSARM Architecture Procedure Call Standard定义了 ARM 架构的函数调用规则void func(int a, int b, int c, int d, int e) R0 R1 R2 R3 [栈] 调用 func(1, 2, 3, 4, 5): ① R01, R12, R23, R34 ② 第5个参数 5 推到栈上SP - 4, *SP 5 ③ 执行 BL func → LR 返回地址PC func 入口 被调函数 func: ④ 如果用到 R4-R11 → 先 PUSH {R4-R11, LR} 保存 ⑤ 函数体 ... ⑥ POP {R4-R11, PC} 恢复 返回为什么面试常考 AAPCS因为理解调用约定才能写正确的汇编/内联汇编分析 HardFault 时的栈回溯Stack Unwinding理解 RTOS 任务切换时的上下文保存范围四、异常模型与中断处理4.1 Cortex-M 异常系统Cortex-M3/M4 有15 个系统异常 最多 240 个外部中断异常号 优先级 名称 用途 ────── ────── ──── ──── 1 -3最高 Reset 复位 2 -2 NMI 不可屏蔽中断 3 -1 HardFault 硬错误默认错误处理 4 可配置 MemManage 内存管理错误 5 可配置 BusFault 总线错误 6 可配置 UsageFault 用法错误 7~10 — 保留 11 可配置 SVC **系统服务调用** 12 可配置 DebugMon 调试监视 13 — 保留 14 可配置 PendSV **可挂起系统调用** 15 可配置 SysTick **系统滴答定时器** 16 可配置 外设中断 GPIO/TIM/ADC/USART...4.2 中断处理的硬件自动压栈Cortex-M 最强大的特性之一硬件自动压栈。中断发生时CPU 自动将 8 个寄存器推入当前栈 ​ SP 指向 → ┌──── xPSR ────┐ ← 高地址先压 │ PC │ ← 返回地址 │ LR │ ← EXC_RETURN │ R12 │ │ R3 │ │ R2 │ │ R1 │ │ R0 │ ← 低地址后压 └──────────────┘ SP 指向这里压完 8 个寄存器后 ​ MSP vs PSP 的选择由 EXC_RETURN 决定 0xFFFFFFF9 → 中断前使用 MSP裸机场景 0xFFFFFFFD → 中断前使用 PSPRTOS 场景F103Cortex-M3vs F407Cortex-M4F压栈差异场景压栈大小说明CM3 任何情况32 字节恒定无 FPU简单确定CM4F 不涉及 FPU32 字节和 CM3 一致CM4F 涉及 FPU100 字节额外 68 字节的浮点寄存器CM4F Lazy Stacking32~100 字节按需保存但抖动更大4.3 PendSV 与任务切换RTOS 的任务切换依赖 PendSV 异常优先级最低 ┌─────────────┐ │ SysTick 中断 │ ← 周期性触发默认 1ms └──────┬──────┘ │ ▼ xTaskIncrementTick() → 是否要切换任务 │ 是 ←───┴───→ 否 │ │ ▼ ▼ 触发 PendSV 继续当前任务 │ ▼ PendSV_Handler汇编实现 ① MRS R0, PSP ← 读当前任务的 PSP ② STMDB R0!, {R4-R11} ← 保存 R4~R11手动压栈 ③ STR R0, [TCB] ← 更新 pxTopOfStack ④ vTaskSwitchContext() ← 选择下一个任务 ⑤ LDR R0, [新TCB] ← 读取新任务的堆栈指针 ⑥ LDMIA R0!, {R4-R11} ← 恢复新任务的 R4~R11 ⑦ MSR PSP, R0 ← 更新 PSP ⑧ BX LR ← LR0xFFFFFFFD → 自动出栈 R0~R3/R12/LR/PC/xPSR 为什么只手动保存 R4~R11 → R0~R3/R12/LR/PC/xPSR 由**硬件自动压栈**中断进入时已完成 → R4~R11 是 Callee-saved软件必须手动保存五、存储器映射与位带操作5.1 Cortex-M 4GB 地址空间Cortex-M3/M4 的 4GB 地址空间被预定义为几个大区 0xE0000000 ~ 0xFFFFFFFF ─── 系统外设SCB/NVIC/MPU ─── 512MB 0x60000000 ~ 0x9FFFFFFF ─── 外部 RAM / 外设 ─── 1GB 0x40000000 ~ 0x5FFFFFFF ─── 片上外设寄存器 ─── 512MB 0x20000000 ~ 0x3FFFFFFF ─── SRAM ─── 512MB 0x00000000 ~ 0x1FFFFFFF ─── CodeFlash Boot 区 ─── 512MB5.2 位带操作Bit-bandingCortex-M3/M4 支持位带操作——将1 位映射到1 个字32 位的地址空间通过写一个字来原子性地操作一个 bit。位带区域SRAM: 位带别名区地址映射: 0x20000000.0 ─────→ 0x22000000 可读写 bit 0 0x20000000.1 ─────→ 0x22000004 可读写 bit 1 0x20000000.2 ─────→ 0x22000008 可读写 bit 2 ... 0x200FFFFF.31 ─────→ 0x23FFFFFC 最后一个 bit 计算公式 别名地址 0x22000000 (A - 0x20000000) × 32 B × 4 其中 A 是 SRAM 地址B 是位号0~31STM32F103 寄存器位带操作实战/* GPIOA-ODR 的第 5 位的位带地址 */ // GPIOA-ODR 地址 0x4001080C #define GPIOA_ODR_ADDR 0x4001080CUL #define BITBAND_PERIPH(addr, bit) ((volatile uint32_t *)(0x42000000 \ ((uint32_t)(addr) - 0x40000000) * 32 (bit) * 4)) #define PA5_ODR (*BITBAND_PERIPH(GPIOA_ODR_ADDR, 5)) // 使用位带操作 PA5原子操作比读-改-写快且安全 PA5_ODR 1; // PA5 输出高 PA5_ODR 0; // PA5 输出低 // 类比不使用位带的读-改-写方式 // GPIOA-ODR | (1 5); // 非原子可能被中断打断位带优势不需要关中断就能原子性地修改单个 bit。FreeRTOS 的某些底层操作可以利用位带来保护标志位。六、Cortex-M3 vs M4 vs M0 关键差异特性M0M3M4M7架构版本ARMv6-MARMv7-MARMv7E-MARMv7E-M流水线2级3级3级6级硬件除法❌✅✅✅单周期乘法❌✅✅✅DSP 指令❌❌✅✅FPU❌❌单精度单/双精度位带❌✅✅✅MPU❌✅(可选)✅(可选)✅Cache❌❌❌✅(IC/DC)中断延迟15 cycles12 cycles12 cycles12 cycles代码密度高(Thumb)更高(T2)更高(T2)更高(T2)代表芯片STM32F0STM32F1STM32F4STM32H7七、常见面试追问Q: Cortex-M3 和 Cortex-M4 的区别是什么M4 在 M3 基础上增加了DSP 指令SMUAD、SMLAD——单指令乘加/饱和运算单精度 FPU硬件浮点运算不依赖软件模拟16 位 SIMD 指令一次处理 2 个 16 位数据其他NVIC、MPU、位带、存储器映射完全一致。Q: 为什么 Cortex-M 不能跑 Linux因为 Cortex-M 系列没有 MMU只有可选的 MPU。MMU 提供虚拟地址到物理地址的映射是标准 Linux 进程隔离的基础MPU 只能做内存区域权限保护不能做地址翻译跑标准 Linux 至少需要 Cortex-A 系列或有 MMU 的 R 系列无 MMU 的 Linux 变体如 uClinux理论上可在部分 Cortex-M 上运行但应用范围极窄Q: RISC-V 和 ARM 比怎么样RISC-V 是开源指令集架构ARM 是商业 IP。RISC-V 优势开源免费、模块化可自定义指令扩展、无授权费RISC-V 劣势生态不如 ARM编译器/调试器/RTOS 支持较少目前 RISC-V 在 IoT 领域快速崛起ESP32-C3、GD32V但 ARM 在主流嵌入式占据绝对优势八、总结知识点核心要点寄存器模型16 个核心寄存器R0-R3 传参R4-R11 被调保存R13(SP) 双栈指针MSP vs PSPMSP 给中断/内核PSP 给任务——RTOS 栈隔离的基础AAPCSR0-R3 传参 → 第5个参数上栈 → LR 存返回地址硬件压栈中断自动压 8 个寄存器32 字节PendSV 手动压 R4-R11位带操作1 bit 映射到 1 word原子修改——不用关中断异常号1~15 系统异常16 外设中断PendSV(14) 和 SysTick(15) 是 RTOS 基础M0/M3/M4 差异M0 最简无除法M3 主力带硬件除法M4 加 DSPFPU理解 ARM Cortex-M 体系结构是嵌入式工程师从能用到懂的分水岭。掌握了寄存器模型和异常机制RTOS 的任务切换原理、HardFault 的调试方法、启动代码的执行流程就不再有黑盒。

相关新闻