ARM 嵌入式开发中,直接读 PC(R15)获取"当前执行指令地址"必出错——执行地址0x08000100时,PC 可能是0x08000104(Cortex-M3/M4)或0x08000108(经典 ARM)。核心原因只有两个:流水线并行执行与架构规范固化,以下聚焦 Cortex-M3/M4讲透关键。
一、先明确两个核心概念
- 执行地址:CPU 当前正在"执行(Execute)"的指令地址(如正在运算的
ADD指令地址) - PC 指针:CPU 下一个要"取指(Fetch)"的指令地址(告诉 CPU 下条指令在哪)
PC 天然指向"执行地址的后面",差异仅在于偏移多少——由流水线和架构决定。
二、根本原因:流水线 + 架构规范
1. 流水线机制:并行执行的必然结果
ARM 用 3 级流水线(取指→译码→执行)实现指令并行:当指令 A(执行地址)在执行时,指令 B 在译码,指令 C 在取指,PC 此时指向指令 C 的地址。
例:经典 ARM(32 位指令)中,执行地址0x00(A)→ PC0x08(C),偏移 +8;但 Cortex-M3/M4 有额外规范。
2. 架构规范:Cortex-M3/M4 的"强制偏移"
Cortex-M3/M4 仅支持 Thumb/Thumb-2 指令集(16/32 位指令),ARMv7-M 架构强制规定:无论指令是 16 位还是 32 位,PC = 执行地址 + 4。
- 执行 16 位指令(地址
0x00)→ PC0x04 - 执行 32 位指令(地址
0x00)→ PC0x04
目的是简化开发:无需判断指令长度,偏移规则统一。
三、Cortex-M3/M4 实战:正确获取执行地址
1. 手动计算:PC - 4
因PC = 执行地址 + 4,减 4 即得真实执行地址:
; 获取当前执行地址,存入R0
GetCurrentAddr:MRS R0, PC ; R0 = PC(执行地址+4)SUB R0, R0, #4 ; R0 = 执行地址(正确)BX LR
2. 用伪指令:避免手动算偏移
日常开发优先用ADR/LDR =label,编译器自动处理 PC 偏移:
ADR R0, DataBuf ; 短距离:自动生成PC相对寻址(修正偏移)
LDR R1, =ConfigAddr ; 长距离:从字面池读地址(无需关心PC)
DataBuf: DCD 0x11223344
ConfigAddr: DCD 0x00001234
四、3 个必避误区
- 误区 1:按指令长度算偏移(16 位 +2、32 位 +4)→ 错!Cortex-M 强制 +4
- 误区 2:混用架构规则(把经典 ARM 的 +8 套到 Cortex-M)→ 错!Cortex-M 只 +4
- 误区 3:手动算偏移不用伪指令→ 错!
ADR/LDR =label更稳定,避免代码修改后偏移失效
五、总结
PC≠执行地址,是 ARM"效率(流水线并行)"与"易用性(架构规范)"的平衡结果。对 Cortex-M3/M4 开发者,只需记住:
PC = 执行地址 + 4- 获取执行地址用
PC-4 - 日常用伪指令处理地址
无需深究流水线细节,按规则用即可。