The ipsr register is an important part of the ARM Cortex-M4 processor. It stands for Interrupt Program Status Register and contains information about the processor’s state during interrupt handling. Understanding how to properly utilize the ipsr register is key to developing effective interrupt service routines (ISRs) on Cortex-M4 based microcontrollers.
Overview of ipsr register
The 32-bit ipsr register contains a number of useful status flags and data:
- Bits 31-9 are reserved and read-as-zero
- Bits 8-0 contain the exception number of the current ISR. This allows identifying which interrupt or exception triggered the ISR.
During normal program operation, the ipsr contains 0x00000000. When an interrupt occurs, the processor automatically pushes the contents of the ipsr onto the stack along with other register values. It then vectors to the corresponding ISR based on the exception number and loads the ipsr with this number.
Inside the ISR, software can check the ipsr value to determine the source of the interrupt. This allows a single ISR handler to service multiple interrupt sources by checking the exception number in the ipsr.
Important Cortex-M4 exception numbers
Here are some key exception numbers that may appear in the Cortex-M4 ipsr during interrupt handling:
- 0 = Thread mode default handler
- 1 = Reset
- 2 = Non-maskable Interrupt (NMI)
- 3 = HardFault
- 4-10 = Reserved
- 11 = SVCall
- 12-13 = Reserved
- 14 = PendSV
- 15 = SysTick
- 16+ = Device specific peripheral interrupts
So for example, if the ipsr contains 0x00000015 during an ISR, you know the SysTick timer interrupt caused the call to the handler. If it contains 0x00000014, it was the PendSV interrupt.
Reading the ipsr in code
To read the current value of the ipsr in C code on a Cortex-M4 MCU, simply dereference the register address: uint32_t ipsr_val = IPSR;
The IPSR register is memory mapped to address 0xE000ED04 within the Cortex-M4 system memory space. So this dereference compiles to a single LDR instruction to load the value from that address.
Using ipsr in interrupt handling
Typical steps to utilize the ipsr in an ISR handler:
- Save interrupted context (registers, etc) onto stack
- Read IPSR to identify interrupt source
- Branch/jump to specific handler code based on IPSR value
- Execute handler code for that interrupt
- Clear pending interrupt
- Restore saved context from stack
- Return from ISR
For example: void ISR_Handler() { // Save register context // … // Identify interrupt uint32_t int_num = IPSR; // Handle specific IRQ if (int_num == 16) { // Handle IRQ16 } else if (int_num == 17) { // Handle IRQ17 } // … // Restore context // … // Return from ISR }
This allows one ISR handler to service multiple interrupt sources cleanly and efficiently.
Saving/restoring ipsr during context switch
The processor automatically preserves ipsr during interrupt handling. But during context switches of threads, the ipsr value must be manually saved and restored.
For pre-emptive RTOSes and multi-threading, add ipsr to the stack frame and save/restore it on context switch: // Save context push {r0-r12, lr, ipsr} // Restore context pop {r0-r12, lr, ipsr}
This ensures the new thread or task starts executing with the right ipsr state.
Using ipsr register in Cortex-M4 assembly
The ipsr can also be accessed directly in assembly language within an ISR: push {r0-r3, lr} ; Save regs mrs r0, ipsr ; Read IPSR cmp r0, #16 ; IRQ16? bne .chkIRQ17 ; No, check other IRQs ; Handle IRQ16 bl IRQ16_Handler b .irq_done .chkIRQ17: cmp r0, #17 ; IRQ17? bne .irq_done ; Handle IRQ17 bl IRQ17_Handler .irq_done: ; Restore saved regs pop {r0-r3, lr}
This can provide faster ISR response times than calling an IPSR read function.
Modifying ipsr register contents
The processor prevents direct modification of the ipsr register contents during exception/interrupt handling. This protects the integrity of the interrupt architecture.
However, when executing in Thread mode, privileged software can directly write to the ipsr to change it. This allows OS or debugger software to manually set the ipsr value if needed. // Set ipsr value to show handling IRQ16 mov ipsr, #16
Care must be taken when changing ipsr directly, as it can disrupt normal interrupt handling flow. Generally this is only useful for low-level debugging purposes.
Conclusion
The ipsr register is a key component of Cortex-M interrupt handling. Examining its value allows concise ISR code to handle multiple interrupt sources. Properly utilizing the Cortex-M4 ipsr register can streamline interrupt management routines and optimize response times to asynchronous events.