一、Cortex-M3/M4 通用寄存器
1、我们首先来了解一下M3/M4的寄存器,M4比M3多了一个浮点单元FPU。其他的部分基本和M3是一样的。
2、Cortex-M3/M4系列处理器拥有通用寄存器R0-R15以及一些特殊功能的寄存器。
3、R0‐ R12 是最“通用目的”的。
4、但是绝大多数的 16 位指令只能使用 R0‐ R7(低组寄存器),而 32 位的 Thumb‐ 2 指令则可以访问所有通用寄存器。这里提到Thumb-2指令,我简单地介绍一下。
Thumb-2是一种精简指令集(RISC)架构,最初由ARM公司开发,并广泛用于ARM处理器上。Thumb-2指令集是ARM指令集的一个扩展,旨在提高代码密度和执行效率。
Thumb-2指令集提供了两种指令集状态:Thumb状态和Thumb-2状态。在Thumb状态下,指令长度为16位,相对于32位的ARM指令来说,代码密度更高。在Thumb-2状态下,指令长度可以是16位或32位,这样就可以运行更复杂的指令,提高程序的执行效率。
Thumb-2指令集具有以下特点:
- 兼容性:Thumb-2指令集能够与之前的Thumb指令集和ARM指令集兼容,既可以在Thumb状态下执行16位指令,也可以在Thumb-2状态下执行32位指令。
- 代码密度:Thumb-2指令集通过使用16位指令来减小代码大小,从而提高代码密度。这对于存储空间受限或带宽有限的嵌入式系统尤为重要。
- 执行效率:Thumb-2指令集在Thumb-2状态下可以执行更复杂的指令,包括对大多数ARM指令的支持,从而提高执行效率和系统性能。
总而言之,Thumb-2指令集是ARM处理器上一种具有代码密度和执行效率优势的指令集,适用于各种嵌入式系统和移动设备应用。
5、特殊功能寄存器有预定义的功能,而且必须通过专用的指令来访问, Cortex-M3/M4 的通用寄存器如图所示。
基于上图,我将逐一讲解各种寄存器的作用。
1)通用目的寄存器 R0-R7
R0‐ R7 也被称为低组寄存器。所有指令都能访问它们。它们的字长全是 32 位,复位后的初始值是不可预料的。
2)通用目的寄存器 R8-R12
R8‐ R12 也被称为高组寄存器。这是因为只有很少的 16 位 Thumb 指令能访问它们,32位的指令则不受限制。它们也是 32 位字长,且复位后的初始值是不可预料的。
3)堆栈指针 R13
R13 是堆栈指针。在 CM3/CM4 处理器内核中共有两个堆栈指针,于是也就支持两个堆栈。当引用 R13(或写作 SP)时,你引用到的是当前正在使用的那一个,另一个必须用特殊的指令来访问( MRS,MSR 指令)。这两个堆栈指针分别是:
(1)、主堆栈指针(MSP),或写作 SP_main。这是缺省的堆栈指针,它由 OS 内核、异常服务例程以及所有需要特权访问的应用程序代码来使用。
(2)、进程堆栈指针(PSP),或写作 SP_process。用于常规的应用程序代码(不处于异常用例程中时)。要注意的是,并不是每个应用都必须用齐两个堆栈指针。简单的应用程序只使MSP 就够了。堆栈指针用于访问堆栈,并且 PUSH 指令和 POP 指令默认使用 SP。
4)连接寄存器 R14
1、R 1 4 是连接寄存器( L R)。在一个汇编程序中,你可以把它写作 b o t h LR 和 R14。
2、LR 用于在调用子程序时存储返回地址。例如,当你在使用 BL(分支并连接, Branch and Link)指令时,就自动填充 LR 的值。尽管 PC 的 LSB 总是 0(因为代码至少是字对齐的), LR 的 LSB 却是可读可写的。这是历史遗留的产物。在以前,由位 0 来指示 ARM/Thumb 状态。因为其它有些 ARM 处理器支持 ARM 和 Thumb 状态并存,为了方便汇编程序移植, CM3/CM4 需要允许 LSB 可读可写。
5)程序计数器 R15
1、R15 是程序计数器,在汇编代码中你也可以使用名字“PC”来访问它。
2、因为 CM3/CM4内部使用了指令流水线,读 PC 时返回的值是当前指令的地址+4。
例如:
ox1000: MOV R0, PC ; R0 = 0x1004
3、如果向 PC 中写数据,就会引起一次程序的分支(但是不更新 LR 寄存器)。CM3/CM4中的指令至少是半字对齐的,所以 PC 的 LSB 总是读回 0。
4、在分支时,无论是直接写 PC 的值还是使用分支指令,都必须保证加载到 PC 的数值是奇数(即 LSB=1),用以表明这是在 Thumb 状态下执行。倘若写了 0,则视为企图转入 ARM 模式,将产生一个 fault异常。
6)特殊功能寄存器组
1、程序状态寄存器组( PSRs 或者 xPSR)
2、中断屏蔽寄存器组( PRIMASK, FAULTMASK,以及 BASEPRI)
3、控制寄存器( CONTROL)
注意:它们只能被专用的 MSR 和 MRS 指令访问,而且它们也没有存储器地址。
MRS
MSR<gp_reg>,
<special_reg>,<special_reg>
<gp_reg>; 读特殊功能寄存器的值到通用寄存器
; 写通用寄存器的值到特殊功能寄存器
7)程序状态寄存器(PSRs 或曰 PSR)
1、应用程序 PSR( APSR)
2、中断号 PSR( IPSR)
3、执行 PSR( EP SR)
说明:通过 M RS / M S R 指令,这 3 个 PSR s 既可以单独访问,也可以组合访问( 2 个组合, 3 个组合都可以)。当使用三合一的方式访问时,应使用名字“ xPSR”或者“ PSR”。三个寄存器的各个位的含义如下图。
PRIMASK,FAULTMASK 和 BASEPRI
这三个寄存器很重要,这三个寄存器用于控制异常的使能和除能,这三个寄存器的介绍如下图:
说明:对于时间‐关键任务而言, PRIMASK 和 BASEPRI 对于暂时关闭中断是非常重要的。 而
FAULTMASK 则可以被 OS 用于暂时关闭 fault 处理机能,这种处理在某个任务崩溃时可能需要。因为在任务崩溃时,常常伴随着一大堆 faults。在系统料理“后事”时,通常不再需要响应这些 fault——人死帐清。总之 FAULTMASK 就是专门留给 OS 用的。
要访问 PRIMASK, FAULTMASK 以及 BASEPRI,同样要使用 MRS/MSR 指令,如:
MRS R0, MRS R0, MRS R0, MSR BASEPRI, MSR FAULTMASK, MSR PRIMASK, | BASEPRI FAULTMASK PRIMASK R0 R0 R0 | ; 读取 BASEPRI 到 R0 中 ; 同上 ; 同上 ; 写入 R0 到 BASEPRI 中 ; 同上 ; 同上 |
只有在特权级下,才允许访问这 3 个寄存器, 为了快速地开关中断, CM3/CM4 还专门设置了一条 CPS 指令,有 4 种用法,这四种用法非常重要,我们在移植 UCOS 操作系统的时候就是用这下面的方法来开关中断的。
CPSID I ;PRIMASK=1, ; 关中断 CPSIE I ;PRIMASK=0, ; 开中断 CPSID F ;FAULTMASK=1, ; 关异常 CPSIE F ;FAULTMASK=0 ; 开异常
8)控制寄存器(CONTROL)
CONTROL 寄存器用于定义特权级别和堆栈指针的使用, CONTROL 寄存器如下表 所示,注
意 CONTROL[2]只有 Cortex-M4 才有。
CONTROL[2]
在 Cortex-M4 中有 FPU 单元,如果我们使用了 FPU,那么在处理异常时就需要保存 FPU 环境, 此位用来指示是否需要保存浮点环境。
CONTROL[1]
在 Cortex‐ M3 的 handler 模式中, CONTROL[1]总是 0。在线程模式中则可以为 0 或1。仅当处于特权级的线程模式下,此位才可写,其它场合下禁止写此位。改变处理器的模式也有其它的方式:在异常返回时,通过修改 LR 的位 2,也能实现模式切换。
CONTROL[0]
仅当在特权级下操作时才允许写该位。一旦进入了用户级,唯一返回特权级的途径,就是触发一个(软)中断,再由服务例程改写该位。 CONTROL 寄存器也是通过 MRS 和 MSR 指令来操作的:
MRS R0, CONTROL MSR CONTROL, R0
9)EXC_RETURN
在进入异常服务程序后, L R 的值被自动更新为特殊的 EXC_RETURN,对于 Cortex-M4
来说这是一个高 27 位全为 1 的值(M3 是高 28 位都为 1)。 M4 中[4:0]位有意义,在 M3 中[3:0]
有意义,并且和 M4 中的相同, EXC_RETURN 位段如表 2.1.4 所示。
注意! EXC_RETURN 的 bit4 非常重要,我们可以根据此位的值来获知硬件会对哪些寄存器进
行自动压入栈和出栈处理。
二、操作模式和特权级别
1、Cortex-M3/CM4 处理器支持两种处理器的操作模式,还支持两级特权操作。
2、两种操作模式分别为: 处理者模式 (handler mode)和线程模式(thread mode)。引入两个模式的本意,是用于区别普通应用程序的代码和异常服务例程的代码——包括中断服务例程的代码。
3、Cortex-M3/M4 的另一个侧面则是特权的分级——特权级和用户级。这可以提供一种存储器访问的保护机制,使得普通的用户程序代码不能意外地,甚至是恶意地执行涉及到要害的操作。处
理器支持两种特权级,如表 2.2.1 所示,这也是一个基本的安全模型。
说明:在 CM3/CM4 运行主应用程序时(线程模式),既可以使用特权级,也可以使用用户级;但是异常服务例程必须在特权级下执行。复位后,处理器默认进入线程模式,特权级访问。在特权级下,程序可以访问所有范围的存储器(如果有 MPU,还要在 MPU 规定的禁地之外),并且可以执行所有指令。
在特权级下的程序可以为所欲为,但也可能会把自己给玩进去——切换到用户级。一旦进入用户级,再想回来就得走“法律程序”了——用户级的程序不能简简单单地试图改写CONTROL 寄存器就回到特权级, 事实上,从用户级到特权级的唯一途径就是异常:如果在程序执行过程中触发了一个异常,处理器总是先切换入特权级,并且在异常服务例程执行完毕退出时,返回先前的状态。