The BASEPRI register is one of the system control registers in ARM Cortex-M processors that is used to set the baseline priority mask. This allows priority levels below the baseline priority to be ignored during exception processing.
What is BASEPRI?
BASEPRI stands for Base Priority Mask Register. It is a 32-bit register that sets the minimum priority level for exception processing in the Cortex-M processor. This register acts as a mask to filter out priority levels below the baseline priority set in BASEPRI.
Setting a value in the BASEPRI register prevents interrupts and exceptions with a priority less than or equal to this value from interrupting the processor. This allows you to create critical sections in your code that cannot be interrupted by low priority exceptions.
Key Features of BASEPRI
- It sets the baseline priority mask for exception processing
- Priority levels below the BASEPRI value are ignored and disabled from interrupting the processor
- Used to create critical code sections that are interrupt-safe
- Writeable in Privileged and Unprivileged modes
- Requires only 1 instruction to set priority mask
How BASEPRI Works
The Cortex-M processor has a concept of priority levels for exceptions and interrupts. The highest priority is level 0 and priority increases with higher numeric values. For example, priority level 3 is lower priority than level 1.
BASEPRI allows you to set a baseline priority level. All priority levels at or below the BASEPRI value get masked and disabled from interrupting the processor. So if BASEPRI is set to 5, then priority levels 5 and below (i.e. levels 6,7,8..) get masked.
This creates a form of priority ceiling where exceptions below that BASEPRI value cannot disrupt the critical section of code that needs uninterrupted execution. Once the critical section is complete, the BASEPRI value can be cleared to re-enable lower priority interrupts.
How BASEPRI Interacts with Exceptions
When an exception occurs in the Cortex-M processor, its priority level gets compared with BASEPRI. If the exception priority is higher than BASEPRI, it gets serviced normally by the processor. But if the exception priority is less than or equal to BASEPRI, it gets ignored and remains pending.
For example, if BASEPRI is 7, an exception with priority 5 gets ignored since it falls under the BASEPRI mask. But an exception with priority 3 would still get serviced since it is above the masking threshold.
This allows BASEPRI to act like a gatekeeper, filtering out lower priority exceptions until it is explicitly cleared by software. Setting BASEPRI provides a safe, atomic method of creating critical sections without having to deal with enabling/disabling interrupts.
Using BASEPRI in Code
Using BASEPRI in your code involves just a single instruction to set the base priority level before entering a critical section, and another instruction to clear it after exiting the critical section.
Here is an example code snippet using BASEPRI: /* Set BASEPRI mask to priority level 0 */ MOV BASEPRI, #0 /* Critical section starts */ /* Code that needs uninterrupted execution */ /* Critical section ends */ /* Clear BASEPRI to re-enable lower priority interrupts */ MOV BASEPRI, #0
The first instruction sets the BASEPRI mask to 0, which is the max priority level. This prevents any lower priority exceptions from interrupting the processor.
After the critical section is complete, we simply clear BASEPRI back to 0 to remove the masking and re-enable lower priority interrupts to occur normally.
Using Functions to Set/Clear BASEPRI
Most ARM compilers provide built-in functions to set and clear BASEPRI for easier usage in your code.
For example, ARM Compiler 6 provides these functions: /* Set BASEPRI priority mask */ __set_BASEPRI(basePriValue); /* Clear BASEPRI priority mask */ __reset_BASEPRI();
This allows you to avoid dealing with the actual BASEPRI register and bitmasks. The compiler handles those details internally.
So you can just call __set_BASEPRI() before your critical section, and __reset_BASEPRI() after it to restore the previous BASEPRI state. This provides a clean abstraction when using BASEPRI in your code.
Using BASEPRI Safely
There are some guidelines you should follow when using BASEPRI to ensure safe and correct usage:
- Only increase BASEPRI to enter critical section, never decrease it.
- Restore BASEPRI to its original level after exiting critical section
- Avoid nesting critical sections with BASEPRI
- Use compiler intrinsic functions where possible
- Keep critical sections short and deterministic
Increasing BASEPRI prevents lower priority interrupts, while decreasing it could accidentally unmask interrupts you did not intend to unmask. So only restore BASEPRI back to the original level on exiting the critical section.
Nesting critical sections can cause issues with priority levels. So avoid having nested BASEPRI usage if possible.
Using compiler intrinsic functions reduces the chance of error compared to direct BASEPRI register access. And keeping critical sections short and deterministic improves real-time behavior.
BASEPRI vs Disable Interrupts
The traditional way of making critical sections in embedded C code is by globally disabling and re-enabling interrupts. For example: /* Disable interrupts */ __disable_irq(); /* Critical section */ /* Enable interrupts */ __enable_irq();
This completely disables all interrupts and exceptions on the processor. The downside is that it affects even very high priority interrupts that you may not want to disable.
BASEPRI provides a more selective method of masking only lower priority exceptions. High priority exceptions can still occur while BASEPRI is set. This allows for critical sections without completely disabling all interrupts.
Additionally, BASEPRI requires only 1 instruction to set priority mask vs 2 instructions for disabling/enabling interrupts. So it provides a quicker and more efficient way to create interrupt-safe critical code sections.
Here are some common use cases where setting BASEPRI is useful:
Protecting Memory or Peripheral Access
When reading or writing critical data that could be corrupted by an interrupt handler, BASEPRI can be set prevent concurrent access to that memory/peripheral region.
For example, when updating a set of sensor registers that could be modified by the sensor ISR as well. Setting BASEPRI ensures atomic access to the sensor peripherals while updating the registers.
Data Structure Manipulation
Linked lists, queues, buffers etc need atomic manipulation of pointers and indices to prevent corruption. Setting BASEPRI during manipulation of such data structures prevents concurrent interrupt handlers from corrupting them.
Critical Code Sequences
Certain algorithms like PID control require uninterrupted execution for several iterations to ensure control loop stability. BASEPRI can be used to mask lower priority interrupts during such critical sequences.
During context switching of threads in an RTOS, BASEPRI can be set to prevent a higher priority task from pre-empting during the switch. This prevents concurrency issues during the save/restore of thread contexts.
So in summary, anytime you need to prevent concurrent access or require atomic uninterrupted execution, BASEPRI provides an efficient way to create critical sections in your embedded software.
To conclude, the BASEPRI register is a useful tool in ARM Cortex-M processors to set a base priority threshold for exception processing. Setting BASEPRI allows you to mask interrupts below a certain priority level, creating critical code sections that are immune to lower priority concurrent interrupts or exceptions.
BASEPRI provides a quick, deterministic and atomic method of creating interrupt-safe critical regions without globally disabling all interrupts. This allows high priority interrupts to still occur while lower priority events remain masked by BASEPRI priority ceiling.
By using compiler intrinsic functions, BASEPRI can be easily integrated into any embedded application that requires critical sections that cannot be disrupted by external influences like interrupts handlers or multi-threading. Overall, BASEPRI gives developers fine-grained control over interrupt masking in Cortex-M processors.