In the field of embedded development, the Cortex-M3/M4 processors have become the preferred architecture in areas such as the Internet of Things and industrial control due to their high performance, low power consumption, and rich peripheral support. This article will delve into its core design mechanisms—the cooperative working mechanism of dual stack pointers (MSP/PSP) and privileged/unprivileged modes, revealing how these underlying designs ensure system security and real-time performance.
CONTROL Register
The CONTROL register is responsible for switching between stack pointers and privilege levels, with the specific definitions as follows:
-
nPRIV: The access level in thread mode, 0-privileged level (default), 1-unprivileged level; it is important to note that the CPU’s operating mode is always in privileged level.
-
SPSEL: The stack pointer selection in thread mode, 0-MSP (default), 1-PSP; it is important to note that the CPU’s operating mode always uses MSP.
-
FPCA: Indicates whether the current context has floating-point operations, 0-the hardware does not automatically push floating-point unit data onto the stack after an exception, 1-the hardware does not automatically push floating-point unit data onto the stack after an exception; this feature is only applicable to Cortex-M4F architecture with a floating-point unit (FPU), the FPCA bit will be automatically set after the CPU executes floating-point operation instructions, automatically cleared at the exception entry, and set to the value of the 4th bit of EXC_RETURN inverted upon exception return, where a 0 in the 4th bit indicates the use of a long stack frame (R0~R3, R12, LR, PC, xPSR, S0~S15, FPSCR), and a 1 indicates the use of a short stack frame (R0~R3, R12, LR, PC, xPSR).

Privilege Levels
After reset, the CONTROL register defaults to 0, which means the processor is currently in thread mode, has privileged access, and uses the main stack pointer. By writing to the CONTROL register, privileged thread mode programs can switch the stack pointer selection or enter unprivileged access level. However, once nPRIV (the 0th bit of CONTROL) is set, it switches to unprivileged level, and programs running in thread mode can no longer access the CONTROL register, as the CONTROL register can only be accessed at privileged level.
Programs running at unprivileged level cannot switch back to privileged access level, thus providing a basic security model. For example, embedded systems may have untrusted applications running at unprivileged access level, and their access needs to be restricted to prevent unreliable programs from causing the entire system to crash.
If it is necessary to switch the processor back to privileged access level in thread mode, the exception mechanism must be used. During exception handling, the handler can clear the nPRIV bit, and upon returning to thread mode, the processor will enter privileged access level, as shown in the following diagram of switching between privileged and unprivileged threads:

However, for general simple RTOS (such as the standard version of FreeRTOS), the default thread is generally at privileged level, and there is no need to switch the nPRIV of the CONTROL register, as shown in the following diagram:

Stack Pointer
For simple bare-metal programs, there is generally no switching of the thread stack pointer, and the program will continuously use MSP for stack operations. For simple RTOS, after reset, the stack pointer will default to using MSP to run the kernel program, and when switching to a specific thread, the stack pointer will use PSP (not all RTOS are like this, such as Xiaomi’s Vela), at which point the thread stack operations will be managed by PSP, and when an exception occurs, the hardware automatically switches to using MSP.

Privilege Level and Stack Pointer Combinations
nPRIV and SPSEL have 4 different combinations corresponding to 4 different application scenarios, of which the first three are the more common combinations:
|
nPRIV |
SPSEL |
Application Scenario |
|---|---|---|
| 0 | 0 |
Commonly found in simple bare-metal programs, the entire program runs at privileged access level, with the main program and interrupt handling always using MSP. |
| 0 | 1 |
Commonly found in simple RTOS (e.g., standard version of FreeRTOS), the currently executing task runs in privileged thread mode, and the current task chooses to use the process stack pointer (PSP), while MSP is used for the OS kernel and exception handling. |
| 1 | 1 |
Commonly found in simple RTOS (such as FreeRTOS-MPU), the currently executing task runs in unprivileged thread mode, and the current task chooses to use the process stack pointer (PSP), while the OS kernel and exception handling use MSP. |
| 1 | 0 |
The thread mode runs at unprivileged access level and uses MSP, which is visible in the processing mode, while user tasks generally cannot use it, as in most embedded OS, the stack used by application tasks and the stack used by the OS kernel and exception handling are independent of each other. |