红叶何时落水
Cortex-M3 是一个 32 位处理器内核。
CM3 采用了哈佛结构,拥有独立的指令总线和数据总线,
CM3 处理器拥有 R0-R15 的寄存器组,而非RSIC-V中的32个,
并且0号寄存器不保持0
R13寄存器用于存放堆栈指针,msp,psp;
msp 异常程序的堆栈指针
psp 用户自定义的非异常程序的指针
非当前堆栈指针必须用特殊的指令来访问(MRS,MSR指令)。
R14 储存PC+4 减少堆栈的读取时间,一般读内存要三个指令周期 入栈的程序返回地址就是R14的值
R15 储存PC 读 PC 时返回的值是当前指令的地址+4
PC 的 LSB 总是读回 0。然而,在分支时,无论是直接写 PC 的值还是使用分支指令,都必须保证加载到 PC 的数值是奇数(即 LSB=1),用以表明这是在 Thumb 状态下执行。
R0-R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位,复位后的初始值是不可预料的。
R8-R12 也被称为高组寄存器。
特殊功能寄存器组只能被 MSR/MRS 指令访问
MRS <gp_reg>, <special_reg> ;读特殊功能寄存器的值到通用寄存器(p75)
MSR <special_reg>, <gp_reg> ;写通用寄存器的值到特殊功能寄存器
当前优先级被存储在 xPSR 的专用字段中。
APSR用于标识ALU的溢出或进位,可以用作条件跳转指令的判断条件(p70)
32 位带 S 后缀的算术逻辑指令
16 位算术逻辑指令
比较指令(如,CMP/CMN)和测试指令(如 TST/TEQ进行相关运算,但是不将运算值保存,只用它来改变标志位)
直接写 PSR/APSR (MSR 指令)
以上四种方式可以改变APSR
屏蔽寄存器组 (特权级下,才允许访问)
PRIMASK 置 1 就关掉所有可屏蔽的异常(NMI 和硬 fault 可以响应);缺省值是 0,表示没有关中断
AULTMASK 同PRIMASK 但是置1时,只有NMI可以响应
BASEPRI 最多9位 所有优先级号大于等于此值的中断都被关
特权级和用户级。这可以提供一种存储器访问的保护机制
CONTROL寄存器来配置特权级和用户级以及进程模式,只能在异常程序中配置
复位后,处理器默认进入线程模式,特权极访问;
CONTROL[1] 特权级的线程模式下,此位才可写
0=选择主堆栈指针 MSP
1=选择进程堆栈指针 PSP
CONTROL[0] 特权级下操作时才允许写该位
用户级下的代码不能再试图修改 CONTROL[0]来回到特权级。它必须通过一个异常 handler,由那个异常
handler 来修改 CONTROL[0],才能在返回到线程模式后拿到特权级
CM3 的所有中断机制都由 NVIC 实现。除了支持 240 条中断之外,NVIC 还支持 16-4-1=11 个
内部异常源,可以实现 fault 管理机制。结果,CM3 就有了 256 个预定义的异常类型
使用双堆栈的条件下,线程模式响应中断时,进入中断入栈时使用psp,然后在中断中使用msp,结束该中断时,切换到psp来出栈
使用MRS,MSR指令来读写sp值
启动过程
调用复位例程之前初始化 MSP 是必需的,因为可能第 1 条指令还没来得及执行,就发生了 NMI 或是其它 fault。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。
汇编基础
32 位 Thumb-2 指令也可以按半字对齐
立即数必须以“#”开头
MOV R0, #0x12 ; R0 0x12
注意:常数定义必须顶格写
NVIC_IRQ_SETEN0 EQU 0xE000E100 ;相当于#define NVIC_IRQ_SETEN0 0xE000E100
DCB 来定义一串字节常数,字节常数还允许以字符串的形式来表达
HELLO_TEXT DCB ”Hello\n”,0
DCD 来定义一串 32 位整数 分配地址
MY_NUMBER DCD 0x12345678
16位
B 无条件转移
B<cond> 条件转移
BL 转移并连接。用于呼叫一个子程序,返回地址被存储在 LR 中
LDR 从存储器中加载字到一个寄存器中
LDR Rd, [Rn, #offset];
STR 把一个寄存器按字存储到存储器中
STR Rd, [Rn, #offset];
LDMIA 加载多个字,并且在加载后自增基址寄存器
LDMIA Rd!, {寄存器列表} 从 Rd 处读取多个字,并依次送到寄存器列表中的寄存器。每读一个字后 Rd 自增一次,16 位宽度
STMIA 存储多个字,并且在存储后自增基址寄存器
STMIA Rd!, {寄存器列表} 依次存储寄存器列表中各寄存器的值到 Rd 给出的地址。每存一个字后 Rd 自增一次,16 位宽度
NOP 无操作
SVC 系统服务调用
CPSIE 使能 PRIMASK(CPSIE i)/ FAULTMASK(CPSIE f)——清 0 相应的位
CPSID 除能 PRIMASK(CPSID i)/ FAULTMASK(CPSID f)——置位相应的位
32位
LDM 从一片连续的地址空间中加载若干个字,并选中相同数目的寄存器放进去
STM 存储若干寄存器中的字到一片连续的地址空间中,占用相同数目的字
LDR.W R0, [R1, #20]! ;预索引 R1 = R1 + 20;
STR.W R0, [R1], #-12 ;后索引 该指令是把 R0 的值存储到地址 R1 处的。在存储完毕后, R1 = R1+(-12)
16 位指令 MOV 支持 8 位立即数加载
MOV R0, #0x12
32 位指令 MOVW 和 MOVT 可以支持 16 位立即数加载
伪指令 用于加载32位立即数
LDR, r0, =0x12345678
对于 LDR,如果汇编器发现要产生立即数是一个程序地址,它会自动地把 LSB 置位,例如:
LDR r0, =address1 ; R0= 0x4000 | 1
ADR 指令则永远是“憨厚”的,它决不会擅自修改 LSB。例如:
ADR r0, address1 ; R0= 0x4000。注意:没有“=”号
RBIT.W Rd, Rn 位反转,按位旋转 180 度(可以用作快速FFT中的反转优化)
B Label ;跳转到 Label 处对应的地址
BX reg ;跳转到由寄存器 reg 给出的地址
BL Label ;跳转到 Label 对应的地址,并且把跳转前的下条指令地址保存到 LR
BLX reg ;跳转到由寄存器 reg 给出的地址,并根据 REG 的 LSB 切换处理器状态;还要把转移前的下条指令地址保存到 LR
使用x后缀指令,要记得最低位置1,以示Thumb状态,为0的话是arm状态cm3不支持,会有fault;
IF-THEN(IT)指令围起一个块,里面最多有 4 条指令,它里面的指令可以条件执行(用于优化三元运算符以及小逻辑的if语句)
if (R0==R1)
{
R3 = R4 + R5;
R3 = R3 / 2;
}
else
{
R3 = R6 + R7;
R3 = R3 / 2;
}
CMP R0, R1 ; 比较 R0 和 R1
ITTEE
ADDEQ R3, R4, R5 ; 相等时加法
EQ ; 如果 R0 == R1, Then-Then-Else-Else
ASREQ R3, R3, #1 ; 相等时算术右移
ADDNE R3, R6, R7 ; 不等时加法
ASRNE R3, R3, #1 ; 不等时算术右移