In the development of ARM Cortex-M series processors, the Program Status Register (PSR or xPSR) is key to understanding the program’s execution state and exception handling.This article will delve into the structure of the PSR in Cortex-M3/M4, including the roles of APSR, IPSR, and EPSR, as well as practical application scenarios, helping developers accurately grasp the control logic of program states.
Composition and Function of PSR
The PSR of Cortex-M3/M4 is composed of three sub-registers, which can be accessed individually or collectively through special instructions:
-
APSR (Application PSR): Application Status Register, stores status flags for arithmetic operation results (such as carry, overflow, etc.).
-
IPSR (Interrupt PSR): Interrupt Status Register, records the number of the current exception/interrupt.
-
EPSR (Execution PSR): Execution Status Register, indicates the execution state of the current instruction (such as Thumb mode).
Access Permissions
APSR, IPSR, and EPSR can be operated individually through MRS/MSR instructions, or accessed collectively through the PSR combined register. It is important to note that software cannot access EPSR individually through these two instructions, and IPSR is read-only, so the MSR instruction cannot be used to write values to IPSR.
APSR can be accessed in both privileged and unprivileged modes, but IPSR and EPSR can only be accessed in privileged mode.
Accessing using assembly instructions as follows:
; Combined access
mrs r0, psr ; Read the entire program status word register value into r0
msr psr, r0 ; Write the value of r0 into the entire program status word register
; Individual access
mrs r0, apsr ; Read the flag status value into r0
mrs r0, ipsr ; Read the exception/interrupt status into r0
msr apsr, r0 ; Write the value of r0 into apsr
Overview of PSR RegisterPSR is composed of APSR + IPSR + EPSR, described as shown in the figure below
APSR: The “Barometer” of Program Execution
APSR contains the following key flags, which directly affect program branching and logical judgments. For example, conditional jump instructions (such as BGT, BLE) depend on the N/Z/C/V flags of APSR, and in DSP algorithms, the Q flag is used to determine whether data exceeds the dynamic range.
| Flag | Name | Description |
|---|---|---|
| N |
Negative Flag |
Set to 1 when the highest bit of the result is 1 (for signed number negative value judgment) |
| Z |
Zero Flag |
Set to 1 when the operation result is 0 |
| C | Carry/Borrow Flag |
Set to 1 when addition produces a carry or subtraction produces a borrow |
| V | Overflow Flag |
Set to 1 when signed number operations overflow |
| Q | Saturation Flag |
Set to 1 when data saturates in DSP operations (only supported by Cortex-M4) |
| GE | Greater or Equal Flag |
Flag for comparison results in SIMD instructions (only supported by Cortex-M4) |
IPSR: The “Navigator” for Exception Handling
IPSR is located in bits 0-9 of the PSR, storing the exception/interrupt number field (Exception Number), indicating the type of exception/interrupt currently being processed.
|
Exception/Interrupt Number |
Description |
|---|---|
| 0-15 |
System exceptions such as reset, NMI, HardFault, etc. |
|
16-255 |
Programmable Interrupt (IRQ) numbers |
EPSR: The “State Lock” for Instruction Execution
EPSR contains two key fields:
-
T bit (Thumb State Flag): T=1 indicates that Thumb instructions are currently being executed, T=0 indicates ARM instructions (the Cortex-M series only supports the Thumb-2 instruction set)
-
ICI/IT field: Saves the state of interrupt continuation execution, used for exception nesting handling
Example: Reading the Exception Number from IPSR
The ARM CMSIS-Core provides a C language function to read IPSR. When an exception/interrupt occurs, this value can be read to determine which exception occurred, and in some software architectures, this value can be used for IRQ Dispatch.
// Assume the interrupt number for UART0 is 20
#define UART0_IRQ_NO 20
void IRQ_Handler(void) { // Extract the low 8 bits of the exception number uint32_t irq_num = __get_IPSR() & 0xFF;
if (irq_num == UART0_IRQ_NO) { // do_uart0_irq // ... }}