Cortex-M7 中断开关指令总结

1 前言

        PRIMASK, FAULTMASK 以及 BASEPRI是三个可以用于异常使能或除能的特殊功能寄存器,其原理是为系统当前的可执行异常优先级(ExecutionPriority)设置最小值,,大于该值(优先级更小)的异常都会被屏蔽掉,这三个寄存器需要通过通过 MRS/MSR 指令来访问。亦或者,可以使用CPS指令来设置PRIMASK和PRIMASK这两个寄存器。

        此外,关于可执行异常优先级,可以理解为,在无异常处理且程序运行在线程模式的情况下,可执行异常优先级为 (maximum supported exception priority value +1),其中maximum supported exception priority value为最低优先级对应的值,即线程模式对应的优先级低于所有异常。

2. 异常屏蔽寄存器(Exception mask registers)

2.1 可配置优先级异常屏蔽寄存器

         Priority Mask Register,在特权模式下可读写,该寄存器主要是用于屏蔽那些优先级可配置的异常。更直接的来说,除了Reset,NMI和HardFault这三个优先级为固定负数值的大佬外,其余异常的优先级都是可配置的,这些寄存器都可以通过Priority Mask Register来屏蔽。

 图1  Priority Mask Register

        如图1所示,该寄存器只有bit[0]有效,0为无效,置1则会屏蔽所有可配置优先级的异常。实际上,将PRIMASK置1后,其实是将系统支持响应的异常优先级提高到了0,优先级小于0(值比0大)的异常都被屏蔽掉。

 2.2 错误异常屏蔽寄存器

        Fault Mask Register,在特权模式下可读写,该寄存器只有bit[0]有效,0为无效,置1时,除了还可以响应NMI之外,其他所有异常都会被屏蔽。另一方面,CPU在退出除了NMI之外的异常处理程序时,都会将Fault Mask Register清零。

  图2 Fault Mask Register

       实际上,将FAULTMASK置1后,其实是将系统支持响应的异常优先级提高到了-1,优先级小于-1(值比-1大)的异常都被屏蔽掉。

2.3 基础优先级异常屏蔽寄存器

        Base Priority Mask Register,在特权模式下可读写,是根据异常的优先级设置了一条高压线,超过或触及该高压线的中断都会被屏蔽。具体来说,当向该寄存器写入一个大于0的值时(记为BASEPRI),优先级值大于等于BASEPRI的异常都会被屏蔽。由于中断优先级的值越大,优先级越低,所以这个寄存器其实是画了一条线,屠戮了那些低优先级的异常。

        该寄存器的位域如图3所示,只有低8位有效,其他位读时回0,写入会被忽略。当写入值BASEPRI位0时,该寄存器除能,谁也不屏蔽。

图3 Base Priority Mask Register

       例如,cmsis(Cortex Microcontroller Software Interface Standard)中关于该寄存器的标准接口实现如下:

/**
  \brief   Set Base Priority
  \details Assigns the given value to the Base Priority register.
  \param [in]    basePri  Base Priority value to set
 */
__STATIC_FORCEINLINE void __set_BASEPRI(uint32_t basePri)
{
  __ASM volatile ("MSR basepri, %0" : : "r" (basePri) : "memory");
}

/**
  \brief   Set Base Priority (non-secure)
  \details Assigns the given value to the non-secure Base Priority register when in secure state.
  \param [in]    basePri  Base Priority value to set
 */
__STATIC_FORCEINLINE void __TZ_set_BASEPRI_NS(uint32_t basePri)
{
  __ASM volatile ("MSR basepri_ns, %0" : : "r" (basePri) : "memory");
}

/**
  \brief   Set Base Priority with condition
  \details Assigns the given value to the Base Priority register only if BASEPRI masking is disabled,
           or the new value increases the BASEPRI priority level.
  \param [in]    basePri  Base Priority value to set
 */
__STATIC_FORCEINLINE void __set_BASEPRI_MAX(uint32_t basePri)
{
  __ASM volatile ("MSR basepri_max, %0" : : "r" (basePri) : "memory");
}

         类似地,freeRTOS中进入临界区portENTER_CRITICAL的底层实现,也是通过条用vPortRaiseBASEPRI函数,向Base Priority Mask Register写入configMAX_SYSCALL_INTERRUPT_PRIORITY来实现的:

/*-----------------------------------------------------------*/

    portFORCE_INLINE static void vPortRaiseBASEPRI( void )
    {
        uint32_t ulNewBASEPRI;

        __asm volatile
        (
            "	mov %0, %1												\n"\
            "	cpsid i													\n"\
            "	msr basepri, %0											\n"\
            "	isb														\n"\
            "	dsb														\n"\
            "	cpsie i													\n"\
            : "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory"
        );
    }

/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/

    portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue )
    {
        __asm volatile
        (
            "	msr basepri, %0	"::"r" ( ulNewMaskValue ) : "memory"
        );
    }
/*-----------------------------------------------------------*/

         对应地,退出临界区portEXIT_CRITICAL的实现则是通过vPortSetBASEPRI函数向Base Priority Mask Register中写0,清除屏蔽位。

 对于时间-关键任务而言,恰如其分地使用 PRIMASK 和 BASEPRI 来暂时关闭一些中断是非常重要的。而 FAULTMASK 则可以被 OS 用于暂时关闭 fault 处理机能,这种处理在某个任务崩溃时可能需要。因为在任务崩溃时,常常伴随着一大堆 faults。在系统料理“后事”时,通常不再需要响应这些 fault——人死帐清。总之 FAULTMASK 就是专门留给 OS 用的。

                                                                                                 ---引自《Cortex-M3权威指南》

         注意,由于子优先级是用于对于pending状态的异常进行优先级比较,并不会影响active状态的异常(不参与抢占行为的规则定义)。因此,通过置位PRIMASK, FAULTMASK 以及 BASEPRI这三个寄存器,进行优先级提升(Priority boosting)时,只作用于分组抢占优先级(group priority),不影响子优先级。

 3 CPS指令

        CPS,Change Processor State,只可以在特权级模式下使用,用户及模式下该指令无效,且由于该指令不支持条件执行,所以不支持在IT指令块中使用。其语法范式比较固定,主要可以拆解为以下四条:

CPSID i ; Disable interrupts and configurable fault handlers (set PRIMASK)
CPSID f ; Disable interrupts and all fault handlers (set FAULTMASK)
CPSIE i ; Enable interrupts and configurable fault handlers (clear PRIMASK)
CPSIE f ; Enable interrupts and fault handlers (clear FAULTMASK)

        其中,ID应该是interrupt disable的缩写,而IE则对应着interrupt enable;随后的的i和f分别指示PRIMASK和FAULTMASK寄存器。

        可以看到,在vPortRaiseBASEPRI函数中就有对cpsid i和cpsie i指令的应用。

4 总结

        总的来看,中断开关其实是通过控制当前系统可执行异常优先级来实现的。可执行异常优先级(ExecutionPriority)主要由以下几个因素来决定:

        ①  base level of execution priority ,基础可执行优先级,指的是线程运行时的优先级,即 (maximum supported exception priority value +1),为最低;

        ② 处于active状态的异常的优先级,正在执行的异常的优先级,和被抢占的异常(处于active and pending状态)的异常的优先级;

        ③  PRIMASK, FAULTMASK 和 BASEPRI置位,导致的可执行异常优先级提升(priority boosting);

        也就是说,系统的可执行异常优先级最终为以上几种情形中的最高优先级(最小值)。举例来说,当前由A,B,C三个异常,A优先级PRI(A)最高,C优先级最低:

        ① 异常B首先发生,CPU执行到ISR后,异常B状态从pending变为active,可执行异常优先级变为PRI(B)

        ② 异常A随之发生,抢占了B,执行ISR后进入active状态,使得可执行异常优先级被提升为PRI(A),而B的状态变为active and pending;

        ③ 异常C发生,由于其优先级小于当前可执行异常优先级,被迫pending;

        ④ 异常A的ISR中,将A的优先级直接更改为比C还低(这种降低当前执行的异常优先级的行为其实很不好),使得可执行异常优先级随之跌落至当前active的异常中的最高优先级,即PRI(B),此时是不允许B抢占A的,以避免优先级反转的乱象;

        ⑤ 随后,异常B的ISR开始执行,并置位PRIMASK,使得可执行异常优先级变为0;

        ⑥ B的ISR执行完毕后,C的优先级比0低,所以仍处于pending状态;       

  • 35
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值