Processor Operating Modes: Thread, Handler
Understand Thread Mode vs Handler Mode on ARM Cortex-M: when the switch happens, stack pointer behavior, privilege rules, and how to detect mode in code.

Contents & prerequisites
Prerequisites
What Are Thread and Handler Modes?
Thread Mode and Handler Mode are the two execution contexts an ARM Cortex-M processor can be in at any instant, and the processor itself — not your code — decides which one is active. Thread Mode is where normal program flow lives: your main() loop, your RTOS tasks, your application logic. Handler Mode is where the processor goes the instant it takes an exception or interrupt, and it stays there for the duration of that exception's handler.
Definition and purpose of each mode
The distinction exists so the processor can separate "the program I was asked to run" from "the thing that just interrupted it." Thread Mode is the default state after reset and the state your application spends almost all its time in. Handler Mode exists exclusively to execute exception handlers — the NVIC (Nested Vectored Interrupt Controller) hardware forces entry into it, and an architecturally defined return sequence forces exit from it. You never choose to enter Handler Mode with an instruction; it happens as a side effect of an exception being taken.
When the processor switches between modes
The switch to Handler Mode happens automatically whenever an exception is accepted: a hardware interrupt, a fault (bus fault, usage fault, memory management fault), SVCall, PendSV, SysTick, or a reset. The switch back to Thread Mode happens when the exception handler executes a special return instruction — a branch to a reserved value called EXC_RETURN — which the processor recognizes as "unwind the exception, don't branch there."
Practical implications for firmware design
You cannot write code that explicitly says "run this in Handler Mode" the way you'd spawn a thread. Mode is a side effect of how your code is invoked — as an interrupt service routine (ISR) versus as a function call from main(). This matters because register access permissions, stack pointer selection, and re-entrancy rules all change depending on which mode is active, and a routine that behaves correctly in Thread Mode can misbehave or fault in Handler Mode if it assumes the wrong context.
Thread Mode: Normal Application Execution
Thread Mode is where your application runs by default, and it is the only mode that can be either privileged or unprivileged. After reset, the core starts in Thread Mode with full privilege, and it remains there until the first exception fires.
Characteristics and restrictions
In Thread Mode, code executes using the stack pointer selected by the CONTROL register — either the Main Stack Pointer (MSP) or, in many RTOS designs, the Process Stack Pointer (PSP). Thread Mode code can be preempted at any instruction boundary by any enabled interrupt of sufficient priority; it has no inherent protection beyond what the Memory Protection Unit (MPU), if present, is configured to enforce.
Privilege levels within Thread Mode
Thread Mode supports two privilege levels: privileged and unprivileged (user) execution. An RTOS typically runs its kernel and ISRs privileged, then drops application tasks to unprivileged Thread Mode via the CONTROL register's nPRIV bit, so a task cannot touch the System Control Block, modify the MPU, or reconfigure the NVIC directly. This is the mechanism that makes task isolation meaningful rather than cosmetic.
When your main application code runs
Everything in your main() loop, every RTOS task body, every polling loop — all of it executes in Thread Mode. If you've written peripheral drivers using register-level peripheral access via CMSIS, the setup code that configures clocks and GPIO before interrupts are enabled runs here too, almost always privileged.
Handler Mode: Interrupt and Exception Context
Handler Mode exists only while an exception handler is executing, and application code never runs there by deliberate design — only by virtue of being the body of an ISR. You cannot call an arbitrary function and ask it to "run in Handler Mode"; the mode is a property of how execution arrived, not of the code itself.
Automatic entry during exceptions and interrupts
The NVIC pushes the core into Handler Mode the cycle an exception is accepted, with no software involvement beyond having configured and enabled that interrupt. Priority, vector fetch, and context stacking all happen in hardware before your handler's first instruction executes.
Always runs with full privilege
Handler Mode is always privileged — there is no unprivileged Handler Mode. This is a deliberate architectural simplification: whatever triggered the exception, the handler servicing it gets full access to system registers, the NVIC, and the MPU configuration, because exception handling is inherently a system-level responsibility.
Stack pointer behavior in Handler Mode
Handler Mode always uses the Main Stack Pointer (MSP), regardless of which stack Thread Mode was using when the exception hit. This is a common source of confusion: if your RTOS runs tasks on PSP and the kernel on MSP, every ISR you write is implicitly operating on the MSP stack, which must be sized to absorb worst-case interrupt stacking plus nesting, not just kernel calls.
Nesting interrupts and re-entrancy
Handler Mode supports nesting — a higher-priority interrupt can preempt a lower-priority one already executing, with the core staying in Handler Mode throughout and simply stacking another frame. This means Handler Mode code must be written as re-entrant and must not assume it's the only handler using the MSP stack at that moment; stack depth under worst-case nesting is a real budget you need to calculate, not estimate.
Mode Switching Mechanism
Mode switching is entirely hardware-driven and follows a fixed stacking and unstacking protocol defined by the architecture, not by your compiler or RTOS.
How exceptions trigger the switch to Handler Mode
On exception entry, the processor automatically pushes eight registers (xPSR, PC, LR, R12, R3-R0) onto the active stack, switches to Handler Mode, switches the stack pointer to MSP if it wasn't already, and loads a special value into the link register.
Return from Handler Mode to Thread Mode
Return happens when the handler executes a branch to the special LR value loaded on entry, rather than a normal BX LR to a code address. The processor recognizes this value isn't a real address, triggers the unstacking sequence, restores the eight stacked registers, and resumes Thread Mode exactly where it left off.
Exception entry and exit stacking behavior
The automatic stacking is why a C compiler can write an ISR as a normal C function with no special prologue for register preservation — the hardware already saved the caller-saved set. You're still responsible for saving any registers your handler clobbers beyond what the architecture auto-stacks, which the compiler's calling convention handles for you if the handler is a plain C function.
Role of the exception return value (EXC_RETURN)
EXC_RETURN is the magic 32-bit value loaded into LR on exception entry, and its low bits encode which stack (MSP or PSP) was in use, whether the exception came from Thread or Handler Mode, and whether floating-point state was stacked. An RTOS context switcher inspects or constructs this value directly when implementing PendSV-based task switching — it's the mechanism, not an abstraction over it.
Privilege Levels and Access Control
Privilege and mode are two different axes, and conflating them is a common design error. Mode (Thread/Handler) determines why code is running; privilege (privileged/unprivileged) determines what it's allowed to touch.
Privileged vs unprivileged execution context
Privileged code can access every system register and every memory region the MPU permits; unprivileged code is blocked from system control registers and triggers a fault on most of them.
System register access restrictions
Attempting to write NVIC_ISER or CONTROL from unprivileged Thread Mode faults rather than silently failing — this is the enforcement boundary an RTOS relies on for task isolation.
Memory protection implications
The MPU can define different access permissions for privileged versus unprivileged execution over the same memory region, letting a kernel mark its own data region inaccessible to unprivileged tasks while remaining accessible to itself and to Handler Mode code.
Relationship between mode and privilege
| Context | Possible privilege | Stack pointer |
|---|---|---|
| Thread Mode | Privileged or unprivileged | MSP or PSP (CONTROL-selected) |
| Handler Mode | Always privileged | Always MSP |
Practical Firmware Implications
Understanding mode isn't academic — it directly determines what's safe to do inside an ISR and how much stack to budget.
Writing interrupt handlers safely in Handler Mode
Keep ISRs short, avoid blocking calls, and never assume the task that was interrupted is resumable mid-function without proper re-entrancy — any global state an ISR touches needs the same protection discipline you'd apply across threads, which is where static, inline, and extern linkage choices for shared variables start to matter.
Stack usage differences between modes
Because Handler Mode always uses MSP, deep ISR nesting combined with an undersized MSP stack is a classic cause of stack-overflow corruption that's very hard to trace back to its root cause — size MSP for worst-case nested interrupt depth, documented per your specific Cortex-M variant's exception model.
Debugging and inspecting the current mode
A debugger reading IPSR (Interrupt Program Status Register) shows the current exception number, where zero means Thread Mode and any nonzero value identifies which exception put you in Handler Mode — most JTAG/SWD debug probes expose this directly in a core-registers view.
Common mistakes and best practices
Assuming PSP is active inside an ISR, forgetting that unprivileged Thread Mode code can't touch the NVIC, and undersizing MSP are the three recurring failures; none of them show up in a datasheet, which assumes you already know the exception model.
Mode Detection and Control in Code
Mode is queryable from C using CMSIS intrinsics, which read the special registers without needing inline assembly in most cases.
How to detect which mode you're running in
uint32_t ipsr = __get_IPSR();
if (ipsr == 0) {
// Thread Mode
} else {
// Handler Mode, servicing exception number `ipsr`
}
Conditional logic based on execution context
Some driver code is written to be callable both from a task and from an ISR, and checks IPSR to decide whether to take a blocking path (Thread Mode only) or a non-blocking, deferred path (Handler Mode) — this pattern shows up often in RTOS-aware peripheral drivers built on register-level peripheral access via CMSIS.
Tools for inspecting mode state during development
Beyond __get_IPSR(), a debugger's core register panel typically shows IPSR, CONTROL, and both stack pointers simultaneously, which is the fastest way to confirm — while halted inside a suspicious ISR — whether you're really in Handler Mode and which stack is active; cross-reference against your core's pipeline behavior in ARM Cortex-M Pipeline Stages (M0 to M55) if you need to reason about exactly when stacking completes relative to instruction fetch.
Learning
Sign in to track your progress.
Evidence
Public projects engineers linked to Processor Operating Modes: Thread, Handler.
No engineer has linked a project to this topic yet. Built something that proves it? Add the project and tag it with embedded-firmware-processor-operating-modes-thread-handler — it then shows here and on your public profile.
