欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > 名人名企 > 深入理解 Cortex-M3 特殊寄存器

深入理解 Cortex-M3 特殊寄存器

2025/5/9 10:33:19 来源:https://blog.csdn.net/Cris_Jay/article/details/147752293  浏览:    关键词:深入理解 Cortex-M3 特殊寄存器

在上一篇文章中分享了 Cortex-M3 内核寄存器组的相关知识,实际上除了内核寄存器组外,CM3 处理器中还存在多个特殊寄存器,它们分别为 程序状态寄存器中断/异常屏蔽寄存器 和 控制寄存器

图片

需要注意的是,特殊寄存器未经过存储器映射,即没有对应的存储器地址,也只能使用专门的 MSR 和 MRS 等特殊寄存器访问指令来进行访问:

MRS <reg> <special_reg>  ;将特殊寄存器读入寄存器
MSR <special_reg> <reg>  ;写入特殊寄存器

CMSIS-Core 也提供了几个用于访问特殊寄存器的 C 函数,其本质也是以上两个指令的封装。如在 gcc 环境下(cmsis_gcc.h),对 CONTROL 寄存器的操作如下:

__STATIC_FORCEINLINE uint32_t __get_CONTROL(void)
{uint32_t result;__ASM volatile ("MRS %0, control" : "=r" (result) );return(result);
}__STATIC_FORCEINLINE void __set_CONTROL(uint32_t control)
{__ASM volatile ("MSR control, %0" : : "r" (control) : "memory");
}

接下来我们看一下这些特殊寄存器的具体含义。

程序状态寄存器(PSRs 或 xPSR)

程序状态寄存器包含以下三个状态寄存器:

  • 应用 PSR(APSR)

  • 执行 PSR(EPSR)

  • 中断 PSR(IPSR)

通过上面提到的 MRS 和 MSR 指令,这三个 PSRs 可以单独访问:

MRS r0, APSR  ;将应用状态读入 R0
MRS r0, IPSR  ;将中断/异常状态读入 R0
MSR APSR, R0  ;写应用状态

也可以组合访问(两个组合或三个组合都可以)。当使用三合一方式访问时,应使用 “xPSR” 或 “PSR” 这两个名字。

MRS r0, PSR  ;读组合程序状态字
MSR PSR, r0  ;写组合程序状态字

需要注意的是,软件代码无法直接使用 MRS (读出为 0)或 MSR 直接访问 EPSR。同时 IPSR 为只读,可以从组合 PSR 中读出。

这三个寄存器的位域结构如下:

图片

组合形式:

图片

其中每个位域字段的含义如下:

  • N:负标志。

  • Z:零标志。

  • C:进位(或非借位)标志。

  • V:溢出标志。

  • Q:饱和标志(ARMv6-M 中不存在)。

  • ICI/IT:中断继续指令状态位(ICI),用于条件执行的 IF-THEN 指令状态位(ARMv6-M 中不存在)。

  • T:Thumb 状态,总是 1,尝试清除此位会引起错误异常。

  • Exception Number:表示处理器正在处理的异常对应的编号。

PRIMASK, FAULTMASK 和 BASEPRO

PRIMASK、FAULTMASK 和 BASEMASK 寄存器都用于异常或中断的屏蔽,每个异常(包括中断)都有一个优先等级,数值越小优先级越高,反之数值越大优先级越低。以上三个特殊寄存器可以基于优先级屏蔽异常,只有在特权访问等级才能对它们进行操作(非特权状态下的写操作会被忽略,而读取则会返回 0)。它们的默认值都为 0,即不屏蔽任何异常或中断。这些寄存器的编程模型如下图所示:

图片

PRIMASK 寄存器是位宽为 1 的中断屏蔽寄存器。在置位时,它会阻止不可屏蔽中断(NMI)和 HardFault 异常之外的所有异常(包括中断)。实际上,它的原理是将当前异常优先级提升为 0,这也是可编程异常/中断的最高优先级。PRIMASK 最常见的用途是在一些时间要求很严格的进程中禁止所有中断,在该进程完成后,需要将 PRIMASK 清除以重新使能中断。

FAULTMASK 和 PRIMASK 非常相似,不过它还能屏蔽 HardFault 异常,它实际上是将异常优先级提升到了 -1。错误处理代码可以使用 FAULTMASK 以免在错误处理期间再次触发其他错误(只有几种)。例如, FAULTMASK 可用于旁路 MPU 或屏蔽总线错误(这些都是可配置的),这样,错误处理代码执行修复措施也就更容易了。与 PRIMASK 不同, FAULTMASK 在异常返回时会被自动清除。

BASEPRI 会根据优先级屏蔽异常或中断。BASEPRI 的宽度取决于设计中实际实现的优先级数量,这通常是由微控制器供应商决定的。大多数 Cortex-M3 或 Cortex-M4 微控制器都有 8 个或 16 个可编程的异常优先级,此时 BASEPRI 的宽度就相应地为 3 位或者 4 位。BASEPRI 为 0 时不会起作用,当被设置为非 0 数值时,他就会屏蔽具有相同或更低优先级的异常(包括中断),而更高优先级的则仍然会被处理器接受。

CMSIS-Core 提供了多个 C 函数用于访问 PRIMASK、FAULTMASK、及 BASEPRI 寄存器。(这些寄存器只能在特权等级下访问)

x = __get_BASEPRI(); // 读 BASEPRI 寄存器
x = __get_PRIMARK(); // 读 PRIMASK 寄存器
x = __get_FAULTMASK(); // 读 FAULTMASK 寄存器
__set_BASEPRI(x); // 设置 BASEPRI
__set_PRIMASK(x); // 设置 PRIMASK
__set_FAULTMASK(x); // 设置 FAULTMASK
__disable_irq(); // 设置 PRIMASK, 禁用 IRQ
__enable_irq(); // 清除 PRIMASK, 使能 IRQ

同时也可以使用汇编代码访问这些寄存器:

MRS r0, BASEPRI ; 将 BASEPRI 寄存器的值读入 R0
MRS r0, PRIMASK ; 将 PRIMASK 寄存器的值读入 R0
MRS r0, FAULTMASK ; 将 FAULTMASK 寄存器的值读入 R0
MSR BASEPRI, r0 ; 将 R0 寄存器的值写入 BASEPRI 
MSR PRIMASK, r0 ; 将 R0 寄存器的值写入 PRIMASK 
MSR FAULTMASK, r0 ; 将 R0 寄存器的值写入 FAULTMASK

此外,利用修改处理器状态(CPS)指令,可以非常方便地设置或清除 PRIMASK 和 FAULTMASK 的值:

CPSIE i ; 使能 interrupt (清除 PRIMASK)
CPSID i ; 禁用 interrupt (设置 PRIMASK)
CPSIE f ; 使能 interrupt (清除 FAULTMASK)
CPSID f ; 禁用 interrupt (设置 FAULTMASK)

CONTROL 寄存器

CONTROL 寄存器中包含了如下两个主要信息:

  • 栈指针的选择(主栈指针 MSP 和 进程栈指针 PSP)。

  • 线程模式的访问等级(特权级和非特权级)。

其编程模型如下:

图片

具体的位域描述为:

位     描述

CONTROL[1]

(SPSEL)

定义栈指针的选择 

0=选择主栈指针 MSP(复位后缺省值) 

1=选择进程栈指针 PSP 

在线程或基础级(没有在响应异常),可以使用 PSP。在 handler 模式下, 只允许使用 MSP,所以此时不得往该位写 1。

CONTROL[0]

(nPRIV)

定义线程模式中的特权等级

0=特权级的线程模式 

1=用户级的线程模式 

Handler 模式永远都是特权级的。

复位后,CONTROL 寄存器默认为 0,这意味着处理器此时处于线程模式,具有特权访问权限并且使用主栈指针。通过写 CONTROL 寄存器,特权线程模式的程序可以切换栈指针的选择或进入非特权访问等级,如下图所示:

图片

不过,nPRIV(CONTROL[0])置位后,运行在线程模式的程序就不能访问 CONTROL 寄存器了。

运行在非特权等级的程序一般情况下无法再切换回特权访问等级,这样就提供了一个基本安全的模型。例如,嵌入式系统中可能会具有运行在非特权等级且不受信任的应用,这些应用的访问权限就需要受到限制,以免不可靠的程序引起系统的崩溃。

若需要在线程模式切换回特权访问等级,则需要借助于异常机制。在异常处理期间,处理程序可以清除 nPRIV 位。在返回到线程模式后,处理器就会进入特权访问等级。

图片

若使用嵌入式 OS,每次上下文切换时都可以重新编程 CONTROL 寄存器,以满足应用间不同特权访问等级的需要。

nPRIV 和 SPSEL 的设置有 4 种组合方式,其中 3 种在实际应用中较为常见:

nPRIVSPSEL应用场景
00简单应用,整个应用运行在特权访问等级,主程序和中断处理只会使用一个栈,即主栈 MSP
01具有嵌入式 OS 的应用,当前执行的任务运行在特权级线程模式,当前任务选择使用进程栈指针 PSP,而 MSP 则用于 OS 内核以及异常处理
11具有嵌入式 OS 的应用,当前执行的任务运行在非特权级线程模式,当前任务选择使用进程栈指针 PSP,而 MSP 则用于 OS 内核以及异常处理
10线程模式运行在非特权访问等级,且使用 MSP,在 Handler 模式下可以观察到,而在用户任务中一般不会使用,这是因为在多数嵌入式 OS 中,应用任务的栈和 OS 内核以及异常处理使用的栈是相互独立的

对于未使用嵌入式 OS 的多数简单应用,无须修改 CONTROL 寄存器的数值,整个应用可以运行在特权访问等级并且只使用 MSP:

图片

要利用 C 语言访问 CONTROL 寄存器,可以使用符合 CMSIS 的设备驱动库提供的以下函数:

x = __get_CONTROL(); // 读取当前 CONTROL 寄存器的值
__set_CONTROL(x); // 设置 CONTROL 寄存器的值为 x

如果使用汇编,则可以借助于 MRS 和 MSR 指令:

MRS r0, CONTROL   ;将 CONTROL 寄存器的值读到 r0
MSR CONTROL, r0   ;将 r0 中的值写到 CONTROL 寄存器

最后,你可以通过检查 CONTROL 和 IPSR 的数值来确定当前是否为特权等级:

int in_privileged(void) {if (__get_IPSR() != 0)return 1;  // Trueelse if ((__get_CONTROL() & 0x1) == 0)return 1;  // Trueelsereturn 0;  // False
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词