,

Understanding Thread Mode, Handler Mode, Privileged & Unprivileged Modes in ARM Cortex-M4 Processor

When working with ARM Cortex-M processors like STM32, understanding the different processor modes is crucial. These modes define how the processor executes code and manages system access. In this article, we’ll break down the four essential concepts:

  • 🧵 Thread Mode
  • 🔁 Handler Mode
  • 🔐 Privileged Mode
  • 🔒 Unprivileged Mode

Whether you’re building a bare-metal embedded system or working with an RTOS, this beginner-friendly guide will help you understand what these modes are and when they are used.

What is Thread Mode?

Thread Mode is the default mode in which your program starts executing. Think of it as the normal mode where your main() function and background tasks run.

🔹 Runs application-level code
🔹 Can be privileged or unprivileged
🔹 Ideal for user programs or system tasks

🧠 Example:

int main(void) {
    // This is Thread Mode
    while(1) {
        // Your application logic
    }
}

What is Handler Mode?

Handler Mode is entered automatically when an interrupt or exception occurs. The processor switches to this mode to execute the Interrupt Service Routine (ISR).

🔹 Used for handling exceptions or interrupts
🔹 Always runs in privileged mode
🔹 Cannot be unprivileged

🧠 Example:

void TIM2_IRQHandler(void) {
    // This code runs in Handler Mode
}

When does the processor switch to Handler Mode?

  • A hardware interrupt is triggered (e.g., timer, GPIO, UART)
  • A software-triggered exception like SVC or PendSV occurs
  • A fault happens (like HardFault or BusFault)

What is Privileged Mode?

Privileged Mode gives the program full access to system resources. It’s like being the system administrator.

🔓 Can:

  • Access all memory and registers
  • Enable or disable interrupts
  • Switch to unprivileged mode

Used by:

  • Startup code
  • Kernel or system-level services
  • ISRs (Interrupt Service Routines)

🧠 Example: Your code runs in privileged mode by default when the MCU powers up.

What is Unprivileged Mode?

Unprivileged Mode has limited access to the system. It’s like a restricted user account.

🔒 Cannot:

  • Access certain system registers
  • Change back to privileged mode directly
  • Modify critical configuration

Used by:

  • User applications
  • RTOS user threads
  • Tasks that should be isolated for security

How to switch to unprivileged mode?

You can write to the CONTROL register:

__asm volatile("MRS R0, CONTROL");  // Read control register
__asm volatile("ORR R0, R0, #1");   // Set bit 0 to enter unprivileged
__asm volatile("MSR CONTROL, R0");  // Write back

❗ Once you’re in unprivileged mode, you cannot switch back to privileged mode without triggering an exception, like SVC.

Summary Table

ModeTypeAccess LevelUse Case
Thread ModeNormalPriv or UnprivMain code, RTOS tasks
Handler ModeExceptionAlways PrivilegedISRs, Fault Handlers
PrivilegedAccess LevelFull system accessKernel, Drivers, ISRs
UnprivilegedAccess LevelRestricted accessUser apps, RTOS user threads

Why Are These Modes Important?

These modes are essential for:

  • Security: Prevent untrusted code from damaging the system
  • ⚙️ RTOS Support: Allow task isolation and privilege management
  • 🧪 Testing: Simulate different behavior based on access level
  • 🛡️ Fault Prevention: Avoid accidental writes to protected memory

Final Thoughts

Understanding Thread Mode, Handler Mode, Privileged and Unprivileged Modes is crucial for building reliable and secure embedded applications on ARM Cortex-M microcontrollers. These concepts help separate system-level code from user-level code and enable safer execution in real-time environments.

By leveraging these modes correctly, you can:

  • Improve your system’s security
  • Create better RTOS applications
  • Understand advanced embedded behavior like fault handling and privilege escalation

Full Code Example (STM32, CMSIS, No HAL)

  • The main() function starts in Thread Mode and Privileged Mode.
  • It configures the LED pin (e.g., PA5 on STM32F401/STM32F103).
  • It then switches to Unprivileged Mode using the CONTROL register.
  • It generates a software interrupt (EXTI3_IRQHandler).
  • The ISR runs in Handler Mode (and always Privileged).
  • Inside the ISR, we toggle the LED.
#include "stm32f4xx.h"
#include <stdint.h>

/* PA5 = On-board LED for STM32F401/STM32F103 */
#define LED_PIN 5

/* Function to initialize GPIOA pin 5 as output */
void GPIO_Init(void) {
    // Enable clock for GPIOA
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    // Set PA5 as General Purpose Output
    GPIOA->MODER &= ~(3U << (LED_PIN * 2));  // Clear mode bits
    GPIOA->MODER |=  (1U << (LED_PIN * 2));  // Set to Output

    // Optional: Set output type to push-pull
    GPIOA->OTYPER &= ~(1U << LED_PIN);

    // Optional: Set speed to high
    GPIOA->OSPEEDR |= (3U << (LED_PIN * 2));

    // Optional: No pull-up/pull-down
    GPIOA->PUPDR &= ~(3U << (LED_PIN * 2));
}

/* Toggle LED */
void toggle_led(void) {
    GPIOA->ODR ^= (1U << LED_PIN);
}

/* Function to trigger a software interrupt for EXTI3 */
void generate_interrupt(void) {
    // Enable IRQ3 (EXTI3_IRQn)
    NVIC->ISER[0] |= (1 << EXTI3_IRQn);

    // Use STIR to trigger EXTI3
    *((volatile uint32_t*)0xE000EF00) = (3 & 0x1FF); // IRQn = 3
}

/* Change to Unprivileged Thread Mode */
void switch_to_unprivileged(void) {
    __asm volatile("MRS R0, CONTROL");
    __asm volatile("ORR R0, R0, #1");    // Set bit 0 to switch to unprivileged
    __asm volatile("MSR CONTROL, R0");
    __asm volatile("ISB");               // Flush pipeline
}

int main(void) {
    GPIO_Init();
    
    // Ensure LED is initially OFF
    GPIOA->ODR &= ~(1U << LED_PIN);

    // Thread Mode + Privileged
    toggle_led();  // Blink once before switching

    // Switch to Unprivileged
    switch_to_unprivileged();

    // Now still in Thread Mode, but Unprivileged
    generate_interrupt(); // This will switch to Handler Mode

    // Back to Thread Mode (still Unprivileged)
    while (1);
}

/* Handler Mode: Always Privileged */
void EXTI3_IRQHandler(void) {
    toggle_led();  // ISR toggles LED
    // Clear pending bit (not needed for software-triggered)
}

/* Optional: Catch unexpected hard faults */
void HardFault_Handler(void) {
    while (1);
}

How to Run

  1. Board: STM32F4-based board (e.g., Nucleo-F401RE or STM32F103).
  2. Toolchain: STM32CubeIDE or bare-metal ARM GCC with linker script.
  3. Steps:
    • Copy code into main.c
    • Build and flash it to the board
    • Observe:
      • LED blinks once from main() (Thread + Privileged)
      • ISR toggles the LED again (Handler + Privileged)
    • You’ve just switched between modes and access levels!

What You Just Learned

StepModeAccess Level
Running main()ThreadPrivileged
Switched via CONTROL regThreadUnprivileged
Triggered interrupt (EXTI3)HandlerPrivileged
ISR toggled LEDHandlerPrivileged
Back to main()ThreadStill Unprivileged

Bonus Tip (RTOS Context)

In an RTOS, the kernel runs in Privileged mode, and tasks run in Unprivileged mode. This is how the system keeps tasks isolated and protected — just like you saw in this demo!

Leave a Reply

Your email address will not be published. Required fields are marked *