Cortex-M3处理器的寄存器包括R0~R15和一些特殊的寄存器。其中R0到R12是通用寄存器,但是一些16位的Thumb指令只能访问R0到R7(低寄存器),而32位的Thumb-2指令则可以访问所有这些寄存器。特殊寄存器只能通过特殊访问指令访问。
所有的寄存器如下图所示:
1 核心寄存器
AAPCS
(ARM Architecture Procedure Calling Standard
)定义的寄存器如下:
寄存器 | 别名 | 描述 |
---|---|---|
r15 | PC | Program Counter(Current Instruction) |
r14 | LR | Link Register(Return Address) |
r13 | SP | Stack Pointer |
r12 | IP | Intra-Procedure-call scratch register |
r11 | v8 | Variable-register 8 |
r10 | v7 | Variable-register 7 |
r9 | v6,SB,TR | Variable-register 6 or Platform Register |
r8~r4 | v5~v1 | Variable-register 5 ~ Variable-register 1 |
r3~r0 | a4~a1 | Argument/scratch register 4 ~ Argument/scratch register 1 |
这里主要介绍R13~R15寄存器
1.1 R13(Stack Pointer):MSP和PSP
R13
是堆栈指针,在CM3中,有两个独立的堆栈内存,用户只能访问当前的堆栈,如果要访问另一个需要用特殊寄存器的MRS
和MSR
指令来进行操作。两种堆栈分别为:
- MSP(
Main Stack Pointer
):默认的SP,用在操作系统内核、异常处理函数和所有需要优先权限的访问 - PSP(
Process Stack Pointer
):通常由运行嵌入式操作系统的系统中的线程进程使用(不处于异常处理程序中时)
并不是两个堆栈都必须同时使用,对于简单的应用来说,只需要用到MSP。对于堆栈的访问需要用PUSH
和POP
指令。
PUSH {R0} ; R13=R13-4, then Memory[R13] = R0
POP {R0} ; R0 = Memory[R13], then R13 = R13 + 4
PUSH
和POP
通常用于在子例程开始时将寄存器内容保存到堆栈内存中,然后在子例程结束时从堆栈中恢复寄存器。
subroutine_1
PUSH {R0-R7, R12, R14} ; Save registers
... ; Do your processing
POP {R0-R7, R12, R14} ; Restore registers
BX R14 ; Return to calling function
因为寄存器PUSH
和POP
操作总是按字对齐的(地址必须是0x0,
0x4, 0x8,…),故SP/R13
的低两位被硬件拉低,读取它将总是为0。
1.2 R14(Link Register)
LR用于在调用函数时,保存它下一条指令的PC。
main ; Main program
...
BL function1 ; Call function1 using Branch with Link instruction.
; PC = function1 and
; LR = the next instruction in main
...
function1
... ; Program code for function 1
BX LR ; Return
尽管PC
的第0位总是为0(指令是按字或半字对齐的),但LR
寄存器的第0位是可读写的,这是因为在Thumb
指令集中,第0位用来指示ARM/Thumb
状态。为了允许Thumb-2
的程序能在其它支持Thumb-2
的ARM处理器中运行,所以LSB是可读写的。
1.3 R15(Program Counter)
由于Cortex-M3处理器的流水线特性,当读取PC
时,该值与此时正在执行指令的地址通常会相差4。例如:
0x1000 : MOV R0, PC ; R0 = 0x1004
其他的指令,比如load
指令,由于地址计算中的对齐,PC
的有效值可能不是指令地址加4。但是在执行过程中,PC
值仍然比指令地址提前至少2个字节。
直接向PC
写入数值将触发一个跳转指令,但是LR
寄存器不会更新。但无论是直接向PC
写入值还是使用B
/BL
/BX
等跳转指令,目标地址的LSB都应该设置为1,表示这是一个Thumb state
操作。如果为0,尝试切换到ARM state
则会触发异常。
2 特殊寄存器
Cortex-M3中的特殊寄存器如下:
- Program Status registers (
PSRs
) - Interrupt Mask registers (
PRIMASK
,FAULTMASK
, andBASEPRI
) - Control register (
CONTROL
)
特殊寄存器只能通过MSR
和MRS
指令访问,它们没有内存地址:
MRS <reg>, <special_reg> ; Read special register
MSR <special_reg>, <reg> ; write to special register
2.1 Program Status Register
程序状态寄存器PSRs
被分为三个状态寄存器:
- Application Program Status register (
APSR
) - Interrupt Program Status register (
IPSR
) - Execution Program Status register (
EPSR
)
这三个寄存器可以被同时访问,使用xPSR
关键字表示这三个寄存器。下图所示为寄存器中具体的字段,还有ARM和ARM7 TDMI中xPSR
的对比:
这些位的定义如下所示:
Bit | Decription |
---|---|
N | Negative |
Z | Zero |
C | Carry/borrow |
V | Overflow |
Q | Sticky saturation flag |
ICI/IT | Interrupt-Continuable Instruction (ICI) bits, IF-THEN instruction status bit |
T | Thumb state, always 1; trying to clear this bit will cause a fault exception |
Exception number | Indicates which exception the processor is handling |
其中APSR
是可读写的,而IPSR
和EPSR
是只读的。
MRS r0, APSR ; Read Flag state into R0
MRS r0, IPSR ; Read Exception/Interrupt state
MRS r0, EPSR ; Read Execution state
MSR APSR, r0 ; Write Flag state
MRS r0, PSR ; Read the combined program status word
MSR PSR, r0 ; Write combined program state word
2.2 PRIMASK, FAULTMASK, and BASEPRI Registers
PRIMASK
,FAULTMASK
和BASEPRI
用来关闭异常。
Register Name | Description |
---|---|
PRIMASK | 仅1位。当为1时,表示允许NMI 和Hard fault 异常,而其它所有的中断和异常都会被屏蔽。默认值为0。 |
FAULTMASK | 仅1位。当为1时,表示仅允许NMI 异常,其它所有中断和错误处理异常都会被屏蔽。默认值为0。 |
BASEPRI | 最多8位(取决于芯片应用的优先级位数)。它表示屏蔽优先级的等级,它将屏蔽所有比该优先级相同和更低的中断。默认值为0。 |
PRIMASK
和BASEPRI
寄存器对于在对实时要求高的任务中,用来临时禁用中断非常有用- 而当任务崩溃时,可能会发生许多不同的错误,操作系统可以使用
FAULTMASK
临时禁用异常错误处理程序,这样在内核开始清理这些错误时,不会被其它错误所中断。因此,FAULTMASK
为OS内核提供了处理错误条件的时间。
为了访问这些寄存器,芯片厂商CMSIS一般会在设备的驱动库中提供相应的C函数API:
x = __get_BASEPRI(); // Read BASEPRI register
x = __get_PRIMARK(); // Read PRIMASK register
x = __get_FAULTMASK(); // Read FAULTMASK register
__set_BASEPRI(x); // Set new value for BASEPRI
__set_PRIMASK(x); // Set new value for PRIMASK
__set_FAULTMASK(x); // Set new value for FAULTMASK
__disable_irq(); // Clear PRIMASK, enable IRQ
__enable_irq(); // Set PRIMASK, disable IRQ
以__get_BASEPRI()
为例,其实现为:
__STATIC_FORCEINLINE uint32_t __get_BASEPRI(void)
{
uint32_t result;
__ASM volatile ("MRS %0, basepri" : "=r" (result) );
return(result);
}
当然在汇编中,我们可以直接通过MRS
和MSR
来访问:
MRS r0, BASEPRI ; Read BASEPRI register into R0
MRS r0, PRIMASK ; Read PRIMASK register into R0
MRS r0, FAULTMASK ; Read FAULTMASK register into R0
MSR BASEPRI, r0 ; Write R0 into BASEPRI register
MSR PRIMASK, r0 ; Write R0 into PRIMASK register
MSR FAULTMASK, r0 ; Write R0 into FAULTMASK register
注意,这三个寄存器不能再user mode
下被更改。
2.3 Control Register
控制寄存器用于定义privilege level
和SP
的选择。我们暂时关注该寄存器的低2位:
Bit | Function |
---|---|
1 | Stack status 1 = Alternate stack is used 0 = Default stack( MSP ) is used在 thread mode 下,Alternate stack为PSP ,在handler mode 下没有Alternate stack,该位必须设置为0 |
0 | 0 = Privileged in thread mode 1 = User state in thread mode 在 handler mode 下,总是处于privilege mode |
对于bit0来说,只有当内核处于thread mode
并具有privilege
时,该位才可写。在user state
或handler mode
下,不允许写入此位。除了写入这个寄存器之外,改变这个位的另一种方法是在异常返回时改变LR的第2位。
- 从
user state
切换回privilege state
的唯一方法是触发中断并在异常处理程序中更改此状态。
同样的,CMSIS提供了C函数来访问这个寄存器,当然我们也可以使用汇编来访问。
x = __get_CONTROL(); // Read the current value of CONTROL
__set_CONTROL(x); // Set the CONTROL value to x
MRS r0, CONTROL ; Read CONTROL register into R0
MSR CONTROL, r0 ; Write R0 into CONTROL register