在特权级模式下,用户可以访问和配置系统控制寄存器,比如NVIC中断控制器。然而,如果是在非特权级模式下,系统控制寄存器是不允许访问的,一旦访问将导致硬件异常。
Unprivileged:
非特权级,起到保护用户任务的作用,防止用户可以在任意任务中访问和修改系统寄存器,操作不当会造成系统崩溃。
Privileged:
特权级,这种模式下用户可以在任意任务中对系统控制寄存器的访问和修改。
对于Cortex-M3或者M4内核来说,所有的核心外设寄存器都是只能在特权级下才可以访问。
核心外设主要是MPU,NVIC,SCB和STK四个单元。除了核心外设寄存器以外,M3/M4内核的特殊功能寄存器也是不能在非特权级下访问的,特殊功能寄存器主要包括以下寄存器:
1. 程序状态寄存器组(PSRs或曰xPSR)
2. 中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
3. 控制寄存器(CONTROL)
对于参考手册上面所说的SPI,USART,USB等所有外设寄存器都是可以在非特权级下进行访问的。
在用户级下,不能访问系统控制空间(SCS,包含配置寄存器及调试组件的寄存器),且禁止使用MSR访问特殊功能寄存器(APSR除外),如果访问,则产生fault。
基于以上原因,从特权级转到非特权级后,不能直接通过写“控制寄存器”——因为不支持在非特权级下访问;因此只能通过特殊方式触发异常,从而进入特权级。
主要有以下两种方法访问核心外设:
1. 使用SVC(Supervisor Call)软中断。
2. 在初始化和开启多任务操作系统前做核心外设的初始化。
Cortex-M3/M4中的特殊功能寄存器包括:
1. 程序状态寄存器组(PSRs或曰xPSR)
2. 中断屏蔽寄存器组(PRIMASK, FAULTMASK,以及BASEPRI)
3. 控制寄存器(CONTROL)
其中控制寄存器CONTROL是用来设置特权级和非特权级切换的,CONTROL寄存器定义如下:
CONTROL[0]
仅当在特权级下操作时才允许写该位。一旦进入了用户级,唯一返回特权级的途径,就是触发一个软中断,再由服务例程改写该位。
CONTROL寄存器也是通过MRS和MSR指令来操作的:
MRS R0, CONTROL
MSR CONTROL, R0
Cortex-M3/M4支持两种操作模式,两种操作模式分别是:
Handlermode,中断模式,简单的说就是指的异常服务程序是处在中断模式。
Threadmode,线程模式,简单的说就是异常服务程序以外的程序都是处在线程模式。
中断和异常的区别:
在 ARM 编程领域中,凡是打断程序顺序执行的事件,都被称为异常(exception)。除了外部中断外,当有指令执行了“非法操作”, 或者访问被禁的内存区间,因各种错误产生的 fault, 以及不可屏蔽中断发生时,都会打断程序的执行,这些情况统称为异常。在不严格的上下文中,异常与中断也可以混用。另外,程序代码也可以主动请求进入异常状态的(常用于系统调用)。
当处理器处在线程状态下时,既可以使用特权级,也可以使用用户级;
另一方面,handler模式总是特权级的。
在系统复位后,处理器进入线程模式+特权级。
-
在特权级下的代码可以通过置位CONTROL[0]来进入用户级。而不管是任何原因产生了任何异常,处理器都将以特权级来运行其服务例程,异常返回后,系统将回到产生异常时所处的级别。
-
用户级下的代码不能再试图修改CONTROL[0]来回到特权级。它必须通过一个异常handler,由那个异常handler来修改CONTROL[0],才能在返回到线程模式后拿到特权级。
为了避免系统堆栈因应用程序的错误使用而毁坏,我们可以给应用程序专门配一个堆栈,不让它共享操作系统内核的堆栈。在这个管理制度下,运行在线程模式的用户代码使用PSP,而异常服务例程则使用MSP。这两个堆栈指针的切换是智能全自动的,就在异常服务的始末由硬件处理。
MSP和PSP 的含义是Main_Stack_Pointer 和 Process_Stack_Pointer,在逻辑地址上他们都是R13.
Cortex-M3内核有两个堆栈指针:MSP-主堆栈指针和PSP-进程堆栈指针,在任何一个时刻只能有一个堆栈指针起作用.
MSP:主堆栈指针,当程序复位后(开始运行后),一直到第一次任务切换完成前,使用的都是MSP,即:main函数运行时用的是MSP,运行OSStartHighRdy,运行PendSV程序,用的都是MSP。当main函数开始运行前,启动文件会给这个函数分配一个堆栈空间,像ucos给任务分配堆栈空间一样,用于保存main函数运行过程中变量的保存。此时MSP就指向了该堆栈的首地址。
PSP: 进程堆栈指针,切换任务之后PendSV服务程序中有ORR LR, LR, #0x04这句,意思就是PendSV中断返回后使用的PSP指针,此时PSP已经指向了所运行任务的堆栈,所以返回后就可以就接着该任务继续运行下去了。
由于任何一个时刻都只能使用一个堆栈指针(SP),所以,如果在某一个时刻,需要读取或者改变另外一个堆栈指针的内容就得使用特定的指令:MSR 和 MRS.