Interrupt prioritization

From SEGGER Wiki
Revision as of 15:02, 2 December 2021 by Martin (talk | contribs)
Jump to: navigation, search

Most CPUs support interrupts with different priorities. Utilizing these, developers may determine the order of execution when two or more interrupts occur simultaneously. The number of available interrupt priority levels depends on the CPU and/or the implemented interrupt controller.

Introduction

With embOS, different levels of interrupts are grouped into one of two categories:

  • High priority interrupts are called zero-latency interrupts within the embOS documentation.
  • Low priority interrupts are called embOS interrupts within the embOS documentation.

Generally, the differences between embOS interrupts and zero-latency interrupts are as follows:

embOS interrupts Zero-latency interrupts
May call any embOS API function that is legal for ISRs Must not call any embOS API function
Must call OS_INT_Enter() or OS_INT_EnterNestable() as first embOS API function in the ISR Must not call OS_INT_Enter() or OS_INT_EnterNestable()
Must call OS_INT_Leave() or OS_INT_LeaveNestable() as last embOS API function in the ISR Must not call OS_INT_Leave() or OS_INT_LeaveNestable()
Can temporarily be disabled by embOS and thus may be affected by additional latency Are never disabled by embOS and thus are not affected by additional latency
Typically include the lower half of all available priorities Typcially include the upper half of all available priorities

This means that zero-latency interrupts can interrupt the operating system at any time, even in critical sections such as the modification of RTOS-maintained linked lists.

Note: Debug builds of embOS will check the priority of interrupts and call OS_Error() with the error code 166 (OS_ERR_ISR_PRIORITY_INVALID, previously OS_ERR_CPU_STATE_ISR_ILLEGAL) in case an embOS API call was made from a zero-latency interrupt.

Interrupt priority levels with embOS and ARMv7-A / ARMv7-R

ARMv7-A/R offers two types of interrupts: FIQs for fast, low latency interrupt handling and IRQs, for more general interrupts.
With embOS, all IRQs are considered embOS interrupts while all FIQs are considered zero-latency interrupts.

Interrupt priority levels with embOS and ARMv6/7-M

With its architectural specification, ARMv7-M supports up to 256 interrupt priorities, i.e. the values 0 to 255 (where 0 indicates the highest and 255 the lowest possible priority).
The threshold between embOS interrupts and zero-latency interrupts by default is configured at 128 (where 0 to 127 are considered zero-latency interrupts, while 128 to 255 are considered embOS interrupts).

Most ARMv7-M devices, however, do not implement all 256 priority levels, but for example 16 or 32 priority levels only. This is done by omitting the least significant bits of the respective priority register.
Consequently, with the given examples, the remaining most significant bits may hold priority values of 0 to 15, or 0 to 31, respectively.
With the given examples, the threshold between embOS interrupts and zero-latency interrupts is at 8 or 16, respectively.

Note: Since at reset all priorities are initialized with 0, applications need to explicitly configure the priority of interrupts that should be used as embOS interrupts.

With embOS, there's two different options to configure the priority of interrupts:

  • embOS API functions
  • CMSIS API functions

When using CMSIS API (i.e. NVIC_SetPriority()), the implemented priority levels may be used as-is, for the CMSIS API accounts for the actual number of implemented interrupt levels for the chosen device (i.e., it shifts the desired value into the appropriate position of the priority register).

Contrary, when using embOS API (i.e. OS_ARM_ISRSetPrio()), the values 0 to 255 must be used in all cases, since embOS is agnostic of the actual number of implemented interrupt levels for the chosen device. This means that embOS API will write to the least significant bits of the priority register, too. However, since writes to all un-implemented bits are ignored, this approach gives the exact same results as when using the CMSIS API.

For illustration, the following table holds examples for calls to OS_ARM_ISRSetPrio() and NVIC_SetPriority() with different numbers of implemented priority levels:

highest priority for a zero-latency interrupt lowest priority for a zero-latency interrupt highest priority for an embOS interrupt lowest priority for an embOS interrupt
2 implemented levels[1]
(1 implemented bit)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 1); NVIC_SetPriority(*, 1);
4 implemented levels[2]
(2 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 1); NVIC_SetPriority(*, 2); NVIC_SetPriority(*, 3);
8 implemented levels
(3 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 3); NVIC_SetPriority(*, 4); NVIC_SetPriority(*, 7);
16 implemented levels
(4 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 7); NVIC_SetPriority(*, 8); NVIC_SetPriority(*, 15);
32 implemented levels
(5 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 15); NVIC_SetPriority(*, 16); NVIC_SetPriority(*, 31);
64 implemented levels
(6 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 31); NVIC_SetPriority(*, 32); NVIC_SetPriority(*, 63);
128 implemented levels
(7 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 63); NVIC_SetPriority(*, 64); NVIC_SetPriority(*, 127);
256 implemented levels
(8 implemented bits)
with embOS API OS_ARM_ISRSetPrio(*, 0); OS_ARM_ISRSetPrio(*, 127); OS_ARM_ISRSetPrio(*, 128); OS_ARM_ISRSetPrio(*, 255);
with CMSIS API NVIC_SetPriority(*, 0); NVIC_SetPriority(*, 127); NVIC_SetPriority(*, 128); NVIC_SetPriority(*, 255);
  1. Not allowed by ARM standard.
  2. Only with ARMv6, not allowed with ARMv7 by ARM standard.