An interrupt is the automatic transfer of software execution in response to a hardware event that is asynchronous with the current software execution.
This hardware event is called a trigger.
The hardware event can either be a busy to ready transition in an external I/O device (like the UART input/output) or an internal event (like bus fault, memory fault, or a periodic timer).
When the hardware needs service, signified by a busy to ready state transition, it will request an interrupt by setting its trigger flag.
A thread is defined as the path of action of software as it executes.
The execution of the interrupt service routine is called a background thread.
This thread is created by the hardware interrupt request and is killed when the interrupt service routine returns from interrupt.
A new thread is created for each interrupts request.
It is important to consider each individual request as a separate thread because local variables and registers used in the interrupt service routine are unique and separate from one interrupt event to the next interrupt.
There are no standard definitions for the terms mask, enable, and arm in the professional Computer Engineering communities.
To arm, a device means to allow the hardware trigger to interrupt.
Conversely, to disarm a device means to shut off or disconnect the hardware trigger from the interrupts.
Each potential interrupting trigger has a separate arm bit, One arm a trigger if one is interested in interrupts from this source, Conversely, one disarms a trigger if one is not interested in interrupts from this source.
To enable means to allow interrupts at this time, Conversely, to disable means to postpone interrupts until a later time.
On the ARM Cortex-M processor, there is one interrupt enable bit for the entire interrupt system.
In particular, to disable interrupts we set the I bit in PRIMASK.
The software has dynamic control over some aspects of the interrupt request sequence.
First, each potential interrupt trigger has a separate arm bit that the software can activate or deactivate.
The software will set the arm bits for those devices from which it wishes to accept interrupts and will deactivate the arm bits within those devices from which interrupts are not to be allowed.
For most devices, there is a enable bit in the NVIC that must be set (periodic SysTick interrupts are an exception, having no NVIC enable).
The third aspect that the software controls is the interrupt enables bit. Specifically, bit 0 of the special register PRIMASK is the interrupt mask bit, I.
If this bit is 1 most interrupt and exceptions are not allowed, which we will define as disabled. If the bit is 0, then interrupts are allowed, which we will define as enabled.
The fourth aspect is the priority, The BASEPRI register prevents interrupts with lower priority interrupts, but allows higher priority interrupts.
For example, if the software sets the BASEPRI to 3, then requests with level 0, 1, and 2 can interrupt, while requests at levels 3 and higher will be postponed.
The software can also specify the priority level of each interrupt request.
If BASEPRI is zero, then the priority feature is disabled and all interrupts are allowed.
The fifth aspect is the external hardware trigger, One example of a hardware trigger is the Count flag in the NVIC_ST_CTRL_R register which is set periodically by SysTick.
Another example of hardware triggers are bits in the GPIO_PORTF_RIS_R register that are set on rising or falling edges of digital input pins.
Five conditions must be true for an interrupt to be generated, For an interrupt to occur, these five conditions must be simultaneously true but can occur in any order:
- Device arm
- NVIC (Nested Vector Interrupt Controller) enable
- Global enable
- Interrupt priority level must be higher than current level executing
- Hardware event trigger
An interrupt causes the following sequence of five events:-
- The current instruction is finished.
- The execution of the currently running program is suspended, pushing eight registers on the stack (R0, R1, R2, R3, R12, LR, PC, and PSR with the R0 on top) , If the floating point unit is active, an additional 18 words will be pushed on the stack representing the floating point state, making a total of 26 words
- The LR is set to a specific value signifying an interrupt service routine (ISR) is being run (bits [31:4] to 0xFFFFFFF, and bits [3:0] specify the type of interrupt return to perform). In our examples, we will see LR is set to 0xFFFFFFF9. If the floating point registers were pushed, the LR will be 0xFFFFFFE9.
- he IPSR (Interrupt Program Status Register) is set to the interrupt number being processed.
5. The PC is loaded with the address of the ISR (vector).
These five steps, called a context switch, occur automatically in hardware as the context is switched from a foreground thread to a background thread.
We can also have a context switch from a lower priority ISR to a higher priority ISR. Next, the software executes the ISR.
If a trigger flag is set, but the interrupts are disabled (I=1), the interrupt level is not high enough, or the flag is disarmed, the request is not dismissed.
Rather the request is held pending, postponed until a later time when the system deems it convenient to handle the requests.
In other words, once the trigger flag is set, under most cases it remains set (pending) until the software clears it.
The five necessary events (device arm, NVIC enable, global enable, level, and trigger) can occur in any order. For example, the software can set the I bit to prevent interrupts, run some code that needs to run to completion, and then clear the I bit. A trigger occurring while running with I=1 is postponed until the time the I bit is cleared again.
Clearing a trigger flag is called acknowledgement, which occurs only by specific software action.
Each trigger flag has a specific action software must perform to clear that flag.
The SysTick periodic interrupt will be the only example of an automatic acknowledgement.
For SysTick, the periodic timer requests an interrupt, but the trigger flag will be automatically cleared when the ISR runs.
For all the other trigger flags, the ISR must explicitly execute code that clears the flag.
The interrupt service routine (ISR) is the software module that is executed when the hardware requests an interrupt.
There may be one large ISR that handles all requests (polled interrupts), or many small ISRs specific for each potential source of interrupt (vectored interrupts).
The design of the interrupt service routine requires careful consideration of many factors.
Except for the SysTick interrupt, the ISR software must explicitly clear the trigger flag that caused the interrupt (acknowledge).
After the ISR provides the necessary service, it will execute BX LR. Because LR contains a special value (e.g., 0xFFFFFFF9), this instruction pops the 8 registers from the stack, which returns control to the main program.
If the LR is 0xFFFFFFE9, then 26 registers (R0-R3,R12,LR,PC,PSW, and 18 floating point registers) will be popped by BX LR.
There are two stack pointers:- PSP, MSP, The software in this class will exclusively use the MSP.
It is imperative that the ISR software balance the stack before exiting because the execution of the previous thread will then continue with the exact stack and register values that existed before the interrupt.
Although interrupt handlers can create and use local variables, parameter passing between threads must be implemented using shared global memory variables.
A private global variable can be used if an interrupt thread wishes to pass information to itself, Ex :- one interrupt instance to another.
The execution of the main program is called the foreground thread, and the executions of the various interrupt service routines are called background threads.
The ISR should execute as fast as possible, The interrupt should occur when it is time to perform a needed function, and the interrupt service routine should perform that function, and return right away.
Placing backward branches (busy-wait loops, iterations) in the interrupt software should be avoided if possible.
The percentage of time spent executing interrupt software should be small when compared to the time between interrupt triggers.
Performance measures: latency and bandwidth:-
For an input device, the interface latency is the time between when a new input is available, and the time when the software reads the input data.
We can also define device latency as the response time of the external I/O device.
For example, if we request that a certain sector be read from a disk, then the device latency is the time it takes to find the correct track and spin the disk (seek) so the proper sector is positioned under the read head.
For an output device, the interface latency is the time between when the output device is idle, and the time when the software writes new data.
A real-time system is one that can guarantee the worst case interface latency.
Bandwidth is defined as the amount of data/sec being processed.
Many factors should be considered when deciding the most appropriate mechanism to synchronize hardware and software.
One should not always use busy wait because one is too lazy to implement the complexities of interrupts, On the other hand, one should not always use interrupts because they are fun and exciting.
Busy-wait synchronization is appropriate when the I/O timing is predictable and when the I/O structure is simple and fixed.
Busy wait should be used for dedicated single thread systems where there is nothing else to do while the I/O is busy.
Interrupt synchronization is appropriate when the I/O timing is variable, and when the I/O structure is complex.
In particular, interrupts are efficient when there are I/O devices with different speeds.
Interrupts allow for quick response times to important events.
In particular, using interrupts is one mechanism to design real-time systems, where the interface latency must be short and bounded.
Bounded means it is always less than a specified value. Short means the specified value is acceptable to our consumers.
Interrupts can also be used for infrequent but critical events like power failure, memory faults, and machine errors.
Periodic interrupts will be useful for real-time clocks, data acquisition systems, and control systems.
For extremely high bandwidth and low latency interfaces, direct memory access (DMA) should be used.
An atomic operation is a sequence that once started will always finish, and cannot be interrupted.
All instructions on the ARM® Cortex™-M processor are atomic except store and load multiple, STM LDM PUSH POP.
If we wish to make a section of code atomic, we can run that code with I=1. In this way, interrupts will not be able to break apart the sequence. Again, requested interrupts that are triggered while I=1 is not dismissed, but simply postponed until I=0.
In particular, to implement an atomic operation we will:-
- Save the current value of the PRIMASK.
- Disable interrupts.
- Execute the operation that needs to run atomically.
- Restore the PRIMASK back to its previous value.