The Cortex-M processor implements a scheme called “priority masking” to prevent lower priority interrupts from interrupting higher priority code sequences. This is achieved by setting the BASEPRI register to mask interrupts below a certain priority level. For example, setting BASEPRI to 16 prevents interrupts with priority lower than 16 from triggering. This allows critical sections of code to run atomically without being interrupted by lower priority interrupts.
1. Interrupt Priority in Cortex-M
In Cortex-M, interrupts are assigned a priority from 0 (highest) to 255 (lowest). The NVIC interrupt controller uses this priority to decide which interrupt to service first when multiple interrupts are pending at the same time. Higher priority interrupts always preempt lower priority ones.
By default, all interrupts have priority 0. This means they will all be treated equally by the processor. To prevent unwanted preemption, different priority levels should be assigned to different interrupts using the NVIC set priority register.
1.1 Default Priority Levels
On reset, the Cortex-M processor configures interrupt priority levels as follows:
- All NVIC interrupts at priority 0
- SysTick exception at priority 1
- PendSV and DebugMonitor exceptions at priority -2
- SVC exception at priority -1 (highest priority)
This ensures SVC interrupts are always serviced first, followed by PendSV, SysTick and finally external NVIC interrupts.
1.2 Changing Priority Levels
The application can change priority levels dynamically by programming the NVIC priority registers. For example: NVIC_SetPriority(EXTI0_IRQn, 5); //set EXTI0 priority to 5 NVIC_SetPriority(TIM2_IRQn, 3); //TIM2 priority to 3
This will make TIM2 interrupt preempt EXTI0 interrupt due to its higher priority level.
2. Priority Masking and BASEPRI
The Cortex-M processor implements priority masking to prevent interrupts below a certain level from triggering. This is accomplished using the BASEPRI register.
2.1 How BASEPRI Works
BASEPRI can be set to a value between 0 and 255. This masks interrupts with a priority lower than the value in BASEPRI. For example: BASEPRI = 16; //will mask interrupts with priority 16 and below
Setting BASEPRI does not prevent exceptions like SVCall or PendSV from triggering. It only masks external interrupts from the NVIC.
2.2 Using BASEPRI for Critical Sections
A common use case of BASEPRI is to protect critical sections of code from preemption by lower priority interrupts. For example: void critical_function() { //disable interrupts below priority 2 __disable_irq(); BASEPRI = 2 << (8 – __NVIC_PRIO_BITS); //critical section __enable_irq(); //restore BASEPRI BASEPRI = 0; }
This prevents any interrupt with priority lower than 2 from disrupting the critical section. Common critical sections include modifying multi-byte variables, accessing hardware registers, and updating non-atomic flags.
3. Preventing Interrupt Nesting
By default, Cortex-M allows interrupts to nest or preempt each other. For example, a higher priority interrupt can preempt a lower priority ISR that is already in progress. This nested execution can be prevented by masking lower priority interrupts.
3.1 Issues with Nested Interrupts
Allowing nested interrupts can lead to several issues, including:
- Stack overflow if too many levels of nesting occur
- Priority inversion when a high priority interrupt gets blocked by lower priority ISRs
- Deadlock due to complex nested execution flows
- Difficulty reasoning about concurrent flows in the application
For these reasons, deeply nested interrupts are often best avoided in embedded applications.
3.2 Preventing Nesting with BASEPRI
To prevent interrupt nesting, the BASEPRI register can be used on entry to ISR routines to mask lower priority interrupts. For example: void TIM2_IRQHandler() { //disable interrupts below priority 4 __disable_irq(); BASEPRI = 4 << (8 – __NVIC_PRIO_BITS); //ISR handling TIM2->SR = 0; //clear interrupt flags __enable_irq(); //restore BASEPRI BASEPRI = 0; }
This prevents any interrupt with priority lower than 4 from nesting into the TIM2_IRQHandler.
A similar scheme can be implemented in a HAL driver or middleware to prevent nesting globally across all external interrupt handlers.
3.3 Configuring Max Nesting Level
The NVIC provides configuration options to limit the nesting depth:
- NVIC_SetPriorityGrouping() – Sets max preemption levels from 0 to 7
- SCB_AIRCR.PRIGROUP – Sets priority grouping in exception handlers
This can help prevent stack overflows and reduce nested interrupt latency. But using BASEPRI masking gives the flexibility to allow selective nesting if needed.
4. Use Cases for BASEPRI Register
Some common use cases of BASEPRI priority masking include:
- Implementing critical sections – Prevent disruption of critical code sequences
- Data structure protection – Guard multi-byte variables or data structures
- Accessing hardware – Prevent concurrent access to hardware registers
- Entering low power modes – Mask interrupts temporarily to enter low power modes atomically
BASEPRI can also be used with OS kernels and RTOS to disable preemption of thread level code for short durations.
5. Choosing Priority Levels
Some tips for assigning priority levels in Cortex-M applications:
- Higher priority for time critical interrupts like timer ticks
- Higher priority for high frequency interrupts
- Similar priorities for related peripherals (DMA channels, ADC sampling)
- Lower priorities for infrequent interrupts like GPIO
- Separate priorities for groups with different latency requirements
Higher priority interrupts should use lower BASEPRI levels for masking. This ensures lower priority ISRs are masked first.
Tuning the priorities and playing with BASEPRI levels can help achieve the right real-time response in complex applications.
6. Limitations of BASEPRI
While useful, BASEPRI masking has some limitations:
- Only prevents NVIC interrupts, not internal exceptions
- Only stops preemption, not concurrent execution of same priority interrupts
- Requires manual programming before and after critical sections
- Does not disable already pending higher priority interrupts
- Can increase interrupt latencies by disabling preemption
Proper design is required to account for these limitations in the application’s use of BASEPRI.
7. Conclusion
The BASEPRI register provides an efficient mechanism for preventing interrupt nesting and lower priority preemption in Cortex-M applications. When used judiciously, it can help build robust real-time embedded software.
Priority levels, BASEPRI values and nesting behavior should be carefully tuned for the needs of the application. This will lead to responsive interrupt handling while also preventing unintended concurrency issues.
BASEPRI forms an important tool in the Cortex-M developer’s toolkit for managing interrupts and writing predictable software on ARM microcontrollers.