The key difference between a fault mask and PRIMASK in ARM Cortex CPUs is their purpose and scope. A fault mask is used to disable interrupts globally across the entire system. In contrast, PRIMASK is used to disable interrupts only for the current execution thread in a multi-threaded system.
Purpose of Fault Masks
A fault mask is a global interrupt enable/disable control that affects all execution threads in a system. Setting the fault mask to 1 will disable interrupts globally, while clearing the fault mask to 0 will re-enable interrupts.
The main purposes of the fault mask are:
- To disable interrupts globally during a critical section of code where interrupts must not be allowed.
- To act as a master interrupt enable/disable switch for the whole system.
- To allow atomic operations by disabling preemption during the atomic code sequence.
So in essence, the fault mask provides a way to globally disable and re-enable interrupts across all execution threads. This allows for critical sections of code to run atomically without being preempted by interrupts.
Purpose of PRIMASK
PRIMASK is a interrupt disable control register that is local to each execution thread in a multi-threaded system. Setting PRIMASK to 1 disables interrupts only for the current thread, while other threads can still handle interrupts.
The main purposes of PRIMASK are:
- To disable interrupts for short critical sections within a single thread.
- To allow a thread to atomically read-modify-write a shared resource without interleaving from interrupts.
- To avoid interrupt nesting within a single thread when multiple interrupt priorities are in use.
So PRIMASK allows per-thread interrupt disable rather than global interrupt disable across all threads. This allows critical sections to be protected on a per-thread basis while still allowing interrupt handling concurrently in other threads.
Scope and Effects
The key difference in scope and effect between fault masks and PRIMASK is:
- Fault mask – Global interrupt disable across all execution threads.
- PRIMASK – Local interrupt disable only for the current thread.
When the fault mask is set, interrupts will be disabled across all threads until the fault mask is cleared again. This provides system-wide atomicity but at the cost of increased interrupt latency.
PRIMASK only disables interrupts for the specific thread that sets it. This allows critical sections to be protected in a single thread without incurring interrupt latency across the whole system.
Usages in ARM Cortex CPUs
Here are some typical uses of fault masks and PRIMASK in ARM Cortex CPUs:
Fault Mask Usage
- Disable interrupts during task scheduling or context switching code to avoid race conditions.
- Protect OS kernel data structures that could be accessed from multiple threads.
- Allow atomic read-modify-write operations on global shared data.
- Reduce interrupt latency for time-critical code sequences.
PRIMASK Usage
- Disable interrupts during short critical sections within a single thread.
- Allow atomic access to thread-local or shared data protected by a mutex.
- Avoid nesting of multiple low priority interrupts within a single thread.
- Protect critical sections in libraries that could be called from multiple threads.
Register Implementation
Fault masks and PRIMASK are typically implemented as registers in the system control coprocessor of Cortex CPUs.
The fault mask is often exposed as a single bit in the CPU’s Program Status Register (PSR). Setting this bit masks interrupts globally.
PRIMASK may be implemented as its own control register or single bit field within a system control register. It only affects the current thread’s interrupt enabling.
So while both controls involve setting a single bit, their scope differs significantly – fault mask is global whereas PRIMASK is local to a thread.
Save and Restore on Context Switch
Due to their different scope, fault masks and PRIMASK need to be handled differently when context switching between threads:
- The fault mask should not be changed on a context switch since it controls the global interrupt enable state.
- PRIMASK must be saved and restored along with other thread context when switching between threads. This maintains the interrupt masking state per-thread.
So the system preserves the global interrupt state using the fault mask across context switches. But the per-thread PRIMASK state must be maintained separately for each thread.
Prioritization and Nesting
Fault masks and PRIMASK have different effects on interrupt prioritization and nesting:
- Setting the fault mask prevents all interrupts globally, regardless of priority.
- PRIMASK only stops interrupts of lower priority than the current executing priority within the same thread.
So when using a prioritized interrupt scheme:
- The fault mask disables all interrupts no matter their priority level.
- PRIMASK only masks lower priority interrupts while allowing same/higher priority interrupts to preempt.
Likewise for nesting, the fault mask prevents any nested interrupts. But PRIMASK only stops nested interrupts below the current priority from the same thread.
Summary
In summary:
- Fault masks globally disable interrupts across all threads in the system.
- PRIMASK locally disables interrupts only for the thread that sets it.
- Fault masks provide system-wide atomicity at the cost of interrupt latency.
- PRIMASK allows per-thread atomic sections with lower interrupt latency impact.
- Fault masks must be preserved across context switches. PRIMASK must be saved/restored.
- Fault masks have no concept of priority. PRIMASK respects priority within a thread.
So in ARM Cortex CPUs, fault masks and PRIMASK provide related but distinct mechanisms for controlling interrupt enables during critical sections – one global and one local per-thread.