Course contentsShow
← Embedded Systems & Computer Architecture
Lesson 209 of 9363. Microcontroller Fundamentals and Bare-Metal CFree lesson

Securing Critical Sections with Interrupt Masking

Apply PRIMASK and FAULTMASK registers to protect non-atomic operations from being interrupted in complex bare-metal system designs.

In this lesson, you will learn how to use special processor registers to create "atomic" blocks of code that are immune to interruption, ensuring data integrity in complex systems.

When your mainline code and an Interrupt Service Routine (ISR) share a data structure, you face a race condition. If the processor stops your main loop halfway through updating a multi-byte variable to jump into an ISR, the ISR might read corrupted, "half-updated" data. To prevent this, we must ensure that specific operations are atomic—meaning they cannot be interrupted.

On ARM Cortex-M processors, we control this using the PRIMASK and FAULTMASK registers.

The Mechanism of Masking

  • PRIMASK: When set to 1, this register disables all configurable-priority interrupts. It effectively "mutes" the outside world, allowing your critical code to finish without preemption.
  • FAULTMASK: This is a more aggressive tool. When set, it disables all interrupts plus all configurable fault handlers. This is reserved for extreme cases where you need absolute isolation from the system, such as critical power-management transitions or low-level kernel synchronization.

Implementing a Critical Section

To protect a shared variable, you mask interrupts, perform the operation, and then immediately restore the original state:

// Example: Protecting a 64-bit variable update
uint32_t primask_state = __get_PRIMASK(); // Save current state
__disable_irq();                          // Set PRIMASK = 1

shared_data = new_value;                  // The "Critical Section"

__set_PRIMASK(primask_state);             // Restore original state

It is vital to save the previous state of the mask register before disabling interrupts. If you simply use a global "enable" command at the end of your block, you might accidentally enable interrupts that were meant to remain disabled by a higher-level function that called your code. By saving and restoring the PRIMASK value, you ensure that your critical section behaves safely regardless of the system state that existed before it began.

Key Takeaway: Use PRIMASK to create atomic critical sections by temporarily disabling interrupts, ensuring that shared data remains consistent even when multiple execution contexts compete for access.