Preventing an interruption to interrupt handlers
Article ID: 103490253
Published date: 13 Feb 2018
Last updated: -
Applies to: Cortex-M3, Cortex-M4, Cortex-M7
Can I prevent my interrupt handlers from being interrupted?
Exceptions (which include interrupts and faults) will only pre-empt (interrupt) the current execution context if the new exception has a strictly higher priority than the current context. The prioritization scheme is an architected feature, therefore you should consult the Armv7-M Architecture Reference Manual for details. The current execution priority is the highest priority of all currently active exceptions, or if no exceptions are active, the base priority level.
Three exceptions have pre-defined (very high) priorities:
-3 : Reset
-2 : NMI
-1 : Hard Fault
All other exceptions, including regular interrupts, have programmable priorities, with a maximum priority level of 0 and higher numerical values giving lower priority levels.
The priority scheme considers each exception to have an 8-bit priority value. Usually, only the 3 or 4 highest order bits are implemented, with unimplemented lower order bits appearing as zeros. The base priority level can be considered to have a priority lower than the lowest programmable priority level. (Base priority level can be considered to be 256.)
The apparent 8-bit field is divided, at a position programmed in the PRIGROUP field of the Application Interrupt and Reset Control Register, into a higher order portion known as group priority (or pre-empting priority) and a lower order portion known as sub-priority.
Exceptions with configurable priority cannot interrupt each other if they have the same value in their group priority field. This becomes trivially true if the PRIGROUP is programmed to '111' to select zero bits of group priority and eight bits of sub-priority. More flexibility can be provided by allowing some pre-empting levels but programming specific exceptions to have the same group priority to cause them not to interrupt each other.
The technique of branching directly from the end of one Interrupt Service Routine (ISR) to the start of another ISR, without ever popping the interrupted context off the stack, is known as 'tail chaining'. Because pre-emption has a higher overhead than tail chaining, the scenario of having many interrupts with equal group priority is actually beneficial for overall system performance, because it wastes fewer cycles in the administrative procedure between execution of ISRs where multiple interrupts are pending (waiting to be serviced) or active at the same time.
The sub-priority field allows control over the decisions about which interrupt to service next, where there are several pending interrupts waiting to be serviced after the currently executing ISR. Where group priority and sub-priority are both equal, the interrupt number itself is used to determine the next interrupt to be handled.
The current execution priority can be artificially boosted by software using MSR or CPS instructions to control the PRIMASK, FAULTMASK or BASEPRI settings. Setting PRIMASK (for example, CPSID i) raises the current execution priority to 0, inhibiting any exceptions of programmable priority, other than those exceptions that escalate to Hard Fault. Setting FAULTMASK (for example, CPSID f) raises the current execution priority to -1, inhibiting all exceptions, including Faults, other than NMI and Reset. Programming BASEPRI can elevate the current execution priority to any intermediate level higher than the current level but lower than priority zero.
For any Fault with configurable priority, if the fault occurs but the handler is not enabled or the priority is too low, it will automatically escalate to a Hard Fault and call the Hard Fault handler at a priority of -1, unless the current execution priority is already -1 or higher.