The PRIMASK register is one of the special-purpose program status registers in ARM Cortex-M4 cores. It is used to disable or enable interrupts globally. Setting the PRIMASK register to 1 disables interrupts, while clearing it to 0 enables interrupts. Understanding the PRIMASK register is important for writing interrupt service routines and controlling interrupt priorities in Cortex-M4 based microcontrollers.
Purpose of the PRIMASK Register
The PRIMASK register allows developers to disable interrupts globally in a Cortex-M4 system. This is useful when you have a critical section of code that cannot be interrupted. Setting PRIMASK to 1 will disable all interrupts. Clearing PRIMASK to 0 re-enables interrupts.
Some typical uses of PRIMASK include:
- Disabling interrupts during interrupt service routines to prevent nesting of interrupts.
- Protecting access to shared resources like peripherals, memory, or data structures.
- Ensuring atomic read-modify-write operations on variables are not interrupted.
So the PRIMASK register gives developers a tool to disable all interrupts globally when needed. This is more efficient than having to disable interrupts individually in each peripheral.
How the PRIMASK Register Works
The PRIMASK register is a single bit register that controls the priority masking of all exceptions and interrupts. When PRIMASK is set to 1, exceptions with configurable priority like interrupts, PendSV, and SysTick are disabled by raising their priorities to the highest level (level 0).
Setting PRIMASK does not affect NMI, HardFault, and exceptions with fixed higher priority levels. So things like bus faults, usage faults, and debug monitor exceptions will still trigger.
When PRIMASK is 0, the normal priority levels are restored and maskable interrupts will fire as expected. So PRIMASK provides a simple way to globally enable or disable maskable interrupts in Cortex-M4.
Reading and Writing the PRIMASK Register
There are a few ways to read and write to the PRIMASK register in Cortex-M4:
- Using the MRS and MSR instructions to transfer the PRIMASK bit to a general purpose register like R0.
- Using the special register access instructions CPSID i and CPSIE i to set or clear the PRIMASK in one instruction.
- Directly modifying the memory mapped PRIMASK register if available in the MCU.
For example: /* Read PRIMASK into R0 */ MRS R0, PRIMASK /* Set PRIMASK to disable interrupts */ CPSID i /* Clear PRIMASK to enable interrupts */ CPSIE i
Consult the reference manual for your specific Cortex-M4 MCU to find the exact methods available to access PRIMASK.
Using PRIMASK to Protect Critical Sections
A common use case for PRIMASK is to protect a critical section of code that must not be interrupted by exceptions or preempted by higher priority interrupts. This ensures atomic access to shared resources.
For example: /* Critical section start */ CPSID i // Set PRIMASK to disable interrupts // Access shared resource CPSIE i // Clear PRIMASK to re-enable interrupts /* Critical section end */
It is important to keep the critical section short and re-enable interrupts promptly. Leaving interrupts disabled for too long can negatively impact real-time performance.
Using PRIMASK in Interrupt Service Routines
Another common use of PRIMASK is at the start of interrupt service routines. This prevents nested interrupts which could corrupt state or stack. /* ISR start */ CPSID i // Disable interrupt nesting // ISR code CPSIE i // Re-enable interrupts /* ISR end */
Again it is good practice to only disable interrupts briefly in the ISR. Some Cortex-M4 based MCUs also have nested vectored interrupt controllers to handle nested interrupts in hardware.
PRIMASK vs BASEPRI Registers
Both PRIMASK and BASEPRI can be used to mask interrupts in Cortex-M4. However there are some differences:
- PRIMASK globally disables all maskable interrupts.
- BASEPRI only masks interrupts with a priority lower than the value set in BASEPRI.
- PRIMASK disables all preemption. BASEPRI only masks interrupts allowing higher priority exceptions to preempt.
- BASEPRI requires more code to manage priority levels, PRIMASK is a single bit enables/disable.
So PRIMASK gives a simple global interrupt disable while BASEPRI allows finer grained priority masking control.
Saving and Restoring PRIMASK State
When using PRIMASK in interrupt service routines, it is good practice to save the state of PRIMASK on interrupt entry and restore it on exit. This avoids any side effects on the global interrupt enable state: /* ISR start */ MRS R0, PRIMASK // Save current PRIMASK value CPSID i // Disable interrupts // ISR code MSR PRIMASK, R0 // Restore original PRIMASK state /* ISR end */
This makes the ISR stateless and avoids unintentionally re-enabling interrupts too early or too late.
Prioritizing PRIMASK and BASEPRI Usage
As a best practice guideline for Cortex-M4 development:
- Use PRIMASK sparingly only for critical sections and ISR entry/exit.
- Use BASEPRI more broadly for managing interrupt priorities and preemption.
- Minimize the time interrupts are disabled globally with PRIMASK.
Overusing PRIMASK can negatively impact real-time performance. BASEPRI allows better control of interrupt nesting and priority levels.
Configuring PRIMASK Priority in NVIC
The PRIMASK exception type can be configured in the Nested Vectored Interrupt Controller (NVIC) via the SysTick and PendSV priority registers. Setting PRIMASK priority here controls its preemption by other exceptions when enabled.
Lower numeric priority values have higher logical priority. So setting PRIMASK to low priority in NVIC allows critical exceptions to preempt even when interrupts are disabled by PRIMASK.
Conclusion
The PRIMASK register is a simple but powerful tool for globally disabling and enabling interrupts in Cortex-M4 MCUs. Using PRIMASK appropriately can protect critical sections of code and simplify ISR state management. Combined judiciously with BASEPRI for priority control, PRIMASK helps developers optimize interrupt response and real-time performance in Cortex-M4 application.
Understanding the PRIMASK register, its interaction with BASEPRI, NVIC priorities, and exception preemption is key to utilizing it effectively in Cortex-M4 projects.