对于 Cortex-M 处理器(包括 ARMv6-M 和 ARMv7-M)异常是否能被处理器接受以及何时被处理器接受并执行异常处理,是由异常的优先级和处理器当前的优先级决定的。更高优先级的异常(优先级编号更小)可以抢占低优先级的异常(优先级编号更大),这就是异常/中断嵌套的情形。有些异常(复位,NMI 和 HardFault)具有固定的优先级,其优先级由负数表示,这样,它们的优先级就会比其他的异常要高。其他异常则具有可编程的优先级,范围为 0 ~ 255。
Cortex-M3 和 Cortex-M4 处理器在设计上具有 3 个固定的最高优先级以及 256 个可编程优先级(具有 128 个抢占等级)。可编程优先级的实际数量由芯片设计商决定。多数 Cortex-M3 或 Cortex-M4 芯片支持的优先级较少,如 8/16/32 等。这是因为大量的优先级会增加 NVIC 的复杂度,也会增加功耗且降低速度。多数情况下,应用程序只需少量的编程优先级。因此,芯片设计人员需要基于目标应用的优先级数量定制处理器设计。优先级的减少是通过去除优先级配置寄存器的最低位(LSB)实现的。
中断优先级由优先级寄存器控制,宽度为 3~8 位。例如,若设计中只实现了 3 位优先级,优先级配置寄存器就如下图所示:
对于上图的示例,由于第 0~4 位没有实现,它们读出来总是为 0,对这些位的写操作会被忽略。根据这种设置,可能的优先级为 0x00(高优先级)、0x20、0x40、0x60、0x80、0xA0、0xC0 以及 0xE0(最低)。
类似的,如果设计中实现了 4 位优先级,就会得到 16 个可编程的优先级:
以上两个位宽的优先级实现等级如下图所示:
实际使用的位数越多,可用的优先级就越多。不过,优先级位数增多也意味着芯片内部的逻辑门数增多,进而增加芯片的功耗。对于 ARMv7-M 架构,宽度最少为 3 位( 8 个等级)。对于 Cortex-M3 和 Cortex-M4 处理器,所有优先级寄存器的复位值都为 0 。
上文中提到优先级的减少是通过去除优先级配置寄存器的最低位(LSB)实现的,之所以这里是移除最低位(LSB)而不是最高位(MSB),是因为这样处理可以使 Cortex-M 处理器间的软件移植工作更容易。
若一个在具有 4 位优先级配置寄存器的设备上运行的程序要想移植到一个具有 3 位优先级配置寄存器的设备,如果移除的是 MSB 而不是 LSB,则此时的优先级配置可能会相反。例如,对于某些应用程序,IRQ#0 的优先级为 0x05,而 IRQ#1 的优先级为 0x03,此时 IRQ#1 的优先级更高。但当移除了最高位 bit 2,则 IRQ#0 优先级会变为 0x01,比 IRQ#1 的优先级还要高!
以下是一个具有 3 位、5 位和 8 位优先级寄存器对应的示例:
这里可能会有个疑问:为什么优先级配置寄存器为 8 位宽时,只有 128 个抢占等级?
这是因为 8 位寄存器会被进一步分为两个部分:分组优先级和子优先级(注:早期的技术参考手册会将分组优先级称为抢占优先级)。
利用系统控制块(SCB)中的一个名为优先级分组的配置寄存器(后续文章会讲到),每个具有可编程优先级的优先级配置寄存器可被分为两个部分。上半部分(左边的位)为分组(抢占)优先级,而下半部分(右边的位)则为子优先级,如下图所示:
在处理器已经在运行一个中断处理时能否产生另外一个中断,是由该中断的抢占优先级决定的。子优先级只会用于具有两个相同分组优先级的异常同时产生的情形,此时,具有更高子优先级(数值更少)的异常会被优先处理。
软件可以通过下表所示的 CMSIS-Core 函数来访问优先级组的设置,以及获取其相关信息:
注意,NVIC_DecodePriority 的结果由修改 *pPreemptPriority 以及 *pSubPriority 这两个指针所指向的数值后得到。
由于优先级分组的存在,分组(抢占)优先级的最大宽度为 7,因此也就有了 128 个等级。若优先级分组被设置为 7,所有具有可编程优先级的异常就会处于相同的等级,这些异常间就不会产生抢占。而 HardFault、NMI 和 复位是例外,因为他们的优先级分别为 -1,-2 和 -3,因此可以抢占上述可编程优先级的异常。
在确定实际的分组优先级和子优先级时,必须要考虑以下因素:
-
实际的优先级配置寄存器
-
优先级分组设置
例如,若配置寄存器的宽度为 3 (第 7 ~ 5 位可用)且优先级分组为 5,则会有 4 个分组/抢占优先级(第 7 ~ 6 位),而且每个分组/抢占优先级具有两个子优先级,如下图所示:
按照上图的设置,可用的子优先级就如下图所示:
对于相同的设计,若优先级分组为 0x01,则只会有 8 个分组优先级,且每个抢占等级中没有进一步的子优先级(优先级寄存器的 bit[1:0] 此时总是为 0)。优先级配置寄存器的定义如下图:
其可用等级如下图:
若 Cortex-M3/M4 实现了优先级配置寄存器中的所有 8 位,则它所具有的抢占等级最大只会是 128,此时优先级分组为 0。其优先级域定义如下图所示:
若两个中断同时被确认,且它们拥有相同的分组/抢占优先级和子优先级,则异常编号更小的中断的优先级更高(IRQ#0 的优先级高于 IRQ#1)。
最后要注意,为了避免中断优先级被意外修改,在写入应用中断和复位控制寄存器(地址为 0xE000ED0C)时要非常小心。多数情况下,在配置了优先级分组后,若不是要产生一次复位,就不要再使用这个寄存器了。