-
概述
本文主要记录M4处理器的编程模型,其中会聊到寄存器组,处理器模式,软件执行特权等。
-
处理器模式
-
线程模式:当处理器复位或者异常处理结束后,就会进入线程模式,在线程模式下,主要运行的用户应用软件。
-
处理模式:用来处理各种异常。
-
软件执行特权
-
特权级别:执行在特权级别的软件可以访问系统所有资源和指令
-
非特权级别:顾名思义是一些非特权程序执行级别,这类程序不能访问系统时钟,NVIC或者SCB;会限制使用一些特殊指令,甚至严格控制内存和外设的访问。
从Linux角度看,这两种级别类似于内核态和用户态。
区分这两种软件执行级别的出发点不难看出,就是对关键区域的保护。
那问题来了,怎么控制区分软件执行特权呢?先把该问题暂且搁置起来,我们先聊聊处理器的寄存器组。
-
寄存器组
处理器是对指令的处理,其中主要一个模块是ALU,为了不影响处理速度,需要有一个地方可以快速及时为ALU提供数据源或者保存结果,那这个地方就是寄存器组,先来一张框图,然后一一详细聊聊每个寄存器的用途。
-
R0~R12:13个通用目的寄存器,R0~R7是低寄存器,因为有的16位指令只能访问低寄存器;高寄存器可以被32位指令和几个16位指令访问。
-
R13:被栈指针,物理上其实有两个栈指针:MSP和PSP;通过PUSH和POP指令对栈操作结束后,R13指针会被调整。因为是32位存储系统,SP指针最低两位为0,保存的地址必须32位字节对齐。 带着问题我们深入理解哈这个栈指针:怎么区分这两个寄存器呢?或者为什么有两个栈指针? 因为处理器有处理模式和线程模式,物理上要区分栈指针; 另外通过Control寄存器的bit[1]SPSEL位可以标识具体使用的哪个栈指针: SPSEL=0,标识使用MSP指针。 SPSEL=1,标识使用PSP指针。 最后,系统复位是运行在处理模式,因此MSP的复位值为是从存储器的第一个32位的字中取出来。PSP是不确定。
-
LR:当从子程序、函数调用和异常返回时,用来保存返回地址;当函数A调用另外一个函数B或者子程序时,首先会将LR的数值保存在栈中,结束调用后,把栈中LR的值加载给PC值,程序得以继续执行。 在处理异常期间,LR会被自动更新为EXC_RETURN数值,然后该数值会在异常处理结束时触发异常返回。 一旦复位,LR初始化为0xFFFFFFFF。
-
PC:程序计数器,包含当前程序地址,如果读该寄存器,返回当前指令地址加4,如果写该寄存器则引起程序跳转。有一点需要提醒:因为指令必须是半字或字对齐,因此PC的bit0为0,但是当更新PC时,必须设置bit0为1,表示Thumb状态。在C/C++编程时,编译器会自动将跳转目前的bit0置位。一旦复位,PC初始化为复位向量地址,该值保存在0x00000004地址中。
-
程序状态寄存器PSR:顾名思义,程序在处理器运行的结果如何,就需要该寄存器呈现给用户。PSR属于特殊寄存器,没有经过存储器映射,因此可使用MSR和MRS等特殊寄存器访问指令来进行访问。 运行的程序大概分为:用户应用程序,中断处理程序和异常处理程序。因此PSR也分为:
APSR:应用程序状态寄存器,仅表示前面指令执行状态。寄存器各个字段描述如下图
Bits | 名字 | 功能 |
---|---|---|
31 | N | 负标志 |
30 | Z | 零标志 |
29 | C | 进位标志 |
28 | V | 溢出标志 |
27 | Q | 饱和标志 |
19:16 | GE[3:0] | 大于等于标志 |
IPSR:中断程序状态寄存器。保存当前的中断号,可指示当前执行的异常和中断向量入口。。当异常开始和结束是,处理器会写IPSR,软件只能读该寄存器,写被忽略。
ISR_NUMBER | 功能 |
---|---|
0 | 线程模式 |
1 | Reserved |
2 | NMI |
3 | HardFault |
4 | MemManage |
5 | BusFault |
6 | UsageFault |
.... | |
11 | SVCall |
12 | for debug |
13 | Reserved |
14 | PendSv |
15 | SysTick |
16 | IRQ0 |
当系统复位后,系统处于线程模式,因此ISR_Number写为0。 EPSR:异常程序状态寄存器。EPSR寄存器包含Thumb状态标志位,IF-Then指令和ICI字段。软件直接读写该寄存器返回0,写被忽略。 ICI字段的理解:在LDM、STM,PUSH和POP指令操作时,发生中断,处理暂时停止四个指令执行,然后保存四个指令的操作数到EPSR[15:12]字段。当中断执行结束后,从EPSR[15:12]恢复操作数继续执行四个指令。
-
异常屏蔽寄存器:PRIMASK寄存器,BASEPRI寄存器,FAULTMASK寄存器。
-
PRIMASK寄存器:主要屏蔽除NMI和Hardfault之外的所有的异常。在时间要求严格的进程中禁止所有中断:运行完进程后,重新使能中能。实际是将当前异常优先级提升为0.
-
FAULTMASK寄存器器:屏蔽除NMI外的所有异常,从任何异常处理返回时,处理器自动清零该位。
-
BASEPRI寄存器:设置被屏蔽中断的的优先级。只会处理比该优先级更高的异常。0时,无效。
-
Control寄存器:选择栈指针,线程模式的访问等级,是否使用浮点单元。该寄存器可以用来区分特权模式和非特权模式。回复最初的问题了。
位域 | 功能 |
---|---|
nPRIV | 定义线程模式下的特权级别:0:特权模式;1:非特权模式 |
SPSEL | 定义当前使用的哪个栈指针;0:MSP为当前栈指针;1:PSP为当前栈指针。 |
FPCA | 指示当前是否为浮点指针执行,0:非浮点单元运行。1:浮点单元运行。 |
-
总结:通过本节文章的浅析,主要掌握处理器编程模式中的处理器模式,特权访问模式,以及寄存器组。嵌入式编程晋级必须掌握的知识点。后续章节会重点剖析。