Cortex-M3作为ARMv7-M架构的代表性处理器,其指令集设计充分考虑了嵌入式系统的特殊需求。与传统的ARM架构相比,M3指令集采用了更精简的Thumb-2技术,完美平衡了代码密度和执行效率。在实际开发中,理解这些指令的工作原理对优化性能至关重要。
指令集主要分为内存访问和数据处理两大类。内存访问指令负责在寄存器和存储器之间传输数据,而数据处理指令则完成算术逻辑运算。特别值得注意的是条件执行机制,它通过IT指令块实现类似if-then的条件判断,避免了频繁的分支跳转,这在实时系统中能显著提升性能。
LDR和STR是使用频率最高的内存访问指令,基本语法格式为:
armasm复制LDR{type}{cond} Rt, [Rn {, #offset}] ; 加载指令
STR{type}{cond} Rt, [Rn {, #offset}] ; 存储指令
其中type字段决定了操作的数据宽度和符号扩展方式:
立即数偏移寻址是最常用的模式,偏移量范围根据数据类型有所不同:
实际应用示例:
armasm复制LDRB R0, [R1, #0x12] ; 从地址R1+0x12加载无符号字节到R0
STRH R2, [R3, #-4]! ; 预索引:存储R2的低半字到R3-4,并更新R3
LDR.W R4, [R5], #8 ; 后索引:从R5加载字到R4,然后R5+=8
这种模式允许第二个寄存器作为基址的偏移量,并可选择移位:
armasm复制LDR R0, [R1, R2, LSL #2] ; 地址=R1 + R2*4
STR R3, [R4, R5] ; 地址=R4 + R5
重要提示:Rn不能是PC,Rm不能是SP或PC。当Rt是PC时,加载的值最低位必须为1。
ADR和LDR指令支持PC相对寻址,这对位置无关代码(PIC)非常有用:
armasm复制ADR R1, data_table ; 获取data_table的PC相对地址
LDR R0, =0x12345678 ; 通过文字池加载大立即数
PC相对偏移范围:
LDM/STM指令可高效批量传输数据,特别适合上下文保存和恢复:
armasm复制STMDB SP!, {R0-R3, LR} ; 压栈多个寄存器(满递减栈)
LDMIA SP!, {R0-R3, PC} ; 出栈并返回
栈操作专用指令PUSH/POP是LDM/STM的语法糖:
armasm复制PUSH {R4-R7, LR} ; 等同于 STMDB SP!, {R4-R7, LR}
POP {R4-R7, PC} ; 等同于 LDMIA SP!, {R4-R7, PC}
在多核/多线程环境中,LDREX和STREX实现了原子操作:
armasm复制retry:
LDREX R0, [R1] ; 独占加载
ADD R0, R0, #1 ; 修改值
STREX R2, R0, [R1] ; 尝试独占存储
CMP R2, #0 ; 检查是否成功
BNE retry ; 失败则重试
CLREX指令用于异常处理中清除独占标记,防止死锁。
基本算术指令支持灵活的第二个操作数(Operand2):
armasm复制ADD{S}{cond} Rd, Rn, Operand2 ; Rd = Rn + Operand2
SUB{S}{cond} Rd, Rn, #imm12 ; Rd = Rn - imm12
RSB R0, R1, #0 ; R0 = 0 - R1 (取反)
ADC R2, R3, R4 ; R2 = R3 + R4 + C (带进位加)
特殊指令ADDW/SUBW支持12位立即数:
armasm复制ADDW R0, R1, #0xFFF ; R0 = R1 + 4095
SUBW R2, R3, #0x1FF ; R2 = R3 - 511
逻辑指令支持位操作:
armasm复制AND R0, R1, #0xFF ; 掩码操作:取R1低8位
ORR R2, R3, R4 ; 按位或
BIC R5, R6, #0x0F ; 清除低4位
EOR R7, R8, R9 ; 按位异或
移位指令支持多种模式:
armasm复制LSL R0, R1, #4 ; 逻辑左移4位
ASR R2, R3, #1 ; 算术右移1位(保持符号)
ROR R4, R5, #8 ; 循环右移8位
RRX R6, R7 ; 带扩展位的循环右移1位
特殊位操作指令:
armasm复制RBIT R0, R1 ; 位序反转
REV R2, R3 ; 字节序反转(32位)
REV16 R4, R5 ; 每个半字内字节反转
REVSH R6, R7 ; 低半字字节反转并符号扩展
CLZ R8, R9 ; 计算前导零个数
Cortex-M3通过IT指令实现条件执行,最多支持4条条件指令:
armasm复制CMP R0, #10
ITTEE GT ; IF-THEN-THEN-ELSE-ELSE
MOVGT R1, #1 ; R0>10时执行
ADDGT R2, R3 ; R0>10时执行
MOVLE R1, #0 ; R0<=10时执行
SUBLE R2, R3 ; R0<=10时执行
Thumb-2指令集混合了16位和32位指令,通常汇编器会自动选择最优编码。但有时需要手动指定:
armasm复制ADDS.W R0, R1, R2 ; 强制32位编码
B.N label ; 强制16位短跳转
.W后缀在以下情况特别有用:
对齐访问:字操作地址应4字节对齐,半字应2字节对齐。非对齐访问会导致性能损失或异常。
批量传输:使用LDM/STM代替多个LDR/STR,可减少指令数和总线周期。
预加载技巧:
armasm复制PLD [R0, #64] ; 预取R0+64处数据到缓存
armasm复制ADD R0, R1, R2, LSL #2 ; R0 = R1 + R2*4
armasm复制CMP R0, #0
ITT NE
ADDNE R1, R2
SUBNE R3, R4
armasm复制copy_loop:
LDMIA R0!, {R2-R5} ; 一次加载4个字
STMIA R1!, {R2-R5} ; 存储到目标地址
SUBS R6, R6, #16 ; 计数器减16
BGT copy_loop ; 循环直到完成
armasm复制; 计算32位数中1的个数
count_ones:
MOV R1, #0 ; 计数器清零
loop:
RBIT R2, R0 ; 位反转
CLZ R2, R2 ; 计算前导零
CMP R2, #32 ; 检查是否为零
BEQ done
ADD R1, R1, #1 ; 计数器加1
LSL R0, R0, R2 ; 左移跳过已计数的零
LSL R0, R0, #1 ; 移出已计数的1
B loop
done:
BX LR
armasm复制atomic_inc:
LDREX R1, [R0] ; 独占加载当前值
ADD R1, R1, #1 ; 增加值
STREX R2, R1, [R0] ; 尝试独占存储
CMP R2, #0 ; 检查是否成功
BNE atomic_inc ; 失败则重试
DMB ; 数据内存屏障
BX LR
掌握Cortex-M3指令集的这些精髓,开发者能够编写出既高效又紧凑的嵌入式代码。在实际项目中,建议结合具体硬件特性(如存储器延迟、总线架构等)进行针对性优化,并充分利用条件执行和指令宽度控制等特性来提升关键代码段的性能。