Interrupts are in-dispensable when writing any practical embedded firmware. With the CPU generally concentrating itself on a computationally intensive job, peripherals are used for IO intensive (reading from serial port) or memory intensive (DMA transfers) operations. Interrupts are the mechanism to gain attention of the CPU asynchronously either on completion of the operation or need for beginning of next cycle of operations. In any modern Operating System, timer interrupts are needed for scheduling and Software Interrupts (SWI) is the way to enter kernel mode when executing system calls.
In a series of blogs beginning with this, we will explore various Interrupt Architectures and Interrupt handling in embedded software across different CPU architectures. Some of the topics that we will cover includes
- Introduction to ARM Architecture – ISA, Registers and Exception Model
- ARM Interrupt architecture
- ARM Interrupt handling – Firmware perspective
- ARM Linux Interrupt handling
- ARM WinCE Interrupt handling
- Nested Vectored Interrupt Controller – NVIC
- 8051 Interrupt architecture and handling
- PIC Interrupt architecture and handling
- AVR Interrupt architecture and handling
- x86 Interrupt architecture and handling
As you can see, more emphasis is given to ARM architecture due to its huge popularity and deployment across embedded systems.
Interrupt Mechanism and ISR
As a quick introduction, we will see how the interrupt mechanism works and Interrupt handling done in any embedded software. Assume the CPU is performing some activity by running a sequence of instructions. Now when a peripheral controller wants to break the sequence, it generates the Interrupt signal, which is typically a single line of connection from the peripheral/interrupt controller.
The sequence operations related to interrupt handling is captured in the below diagram.
Up on reception of the same, the general sequence of handling is as follows
- Current instruction being executed is completed.
- CPU copies the internal register called PSW – Program Status Word (the register that has the flags like Carry, Zero etc) on to stack or an internal backup register
- Address of the next instruction to be executed is saved on the stack or another internal backup register so that it can be resumed after interrupt processing.
- The Program counter is set to the vector address specific to that interrupt.
A piece of code called the Interrupt Service Routine (ISR) is placed in the vector location of an interrupt, to handle it. Typical flow of operation includes
- If needed, store the previous PSW and return address in memory
- Store the context – any more internal registers, status registers etc that might be modified during the course of execution of this ISR
- Perform necessary operations to process that interrupt like copying data to/from the peripheral, prepare for next cycle etc.
- Up on completion, restore the context back to the original values from the stored values
- Restore the PSW and jump to the address of the instruction next to the interrupted one.
While the overall interrupt mechanism remains same, the exact handling, ways to return from interrupt etc are different for CPU architectures like 8051, ARM, AVR etc.
Interrupt Concepts and Terms
Some of the other concepts that we need to familiarize ourselves with are listed below.
In a typical SoC, there could be hundreds of peripherals – interrupt sources that may vie for processor attention. It is impossible to route all these signals to the CPU. So there is another special purpose peripheral called Interrupt Controller to which all the peripheral interrupts signals are connected. The controller is in turn connected to the CPU with one or few lines of signals, with which it can interrupt the CPU. Controller is provided with various registers to mask/unmask the interrupts, read pending/raw status, set priority etc. The controller monitors the signals from peripherals and if there are any active interrupts, based on the preconfigured priority decides the source to be processed first. Then it signals the CPU with interrupt. The CPU, in the ISR, can then read the pending interrupt register and process the same.
Interrupts could be from hardware, software or other sources. Hardware interrupts are the most discussed types originating from on-chip/on-board peripherals. Apart of them, most architectures provides instructions (such as SWI – Software Interrupt) that can be used to generate an interrupt – typically to enter kernel mode. Also exceptions like page fault, instruction fetch errors etc are handled as interrupts.
Interrupt Types and Levels
The interrupt signal could either level triggered or edge triggered. Level triggered interrupts remains active till the condition of interrupt remains, where as with edge triggered interrupts there is a change in signal level and then it again changes back to original state. The type of interrupt can be decided based on various conditions like transition in state (level triggered) or occurrence of events (edge) and is mostly specific to the hardware implementation.
Again the level triggered interrupt could be
- Low level triggered
- High level triggered
And the edge triggered interrupt could be
- Rising edge triggered
- Falling edge triggered
- Both edge triggered
While most of the interrupts could be enabled or disabled by the will of the user, in some implementations, there might be some interrupts that cannot be disabled or masked. These Non-Maskable Interrupts or NMI, as they are called, are used to inform CPU of critical conditions like power going down, occurrence of watchdog interrupt etc. Most likely, there is no need for special handling of the same in software perspective.
As we have seen the Interrupt controller can accept hundreds of interrupt sources, there is a need for a mechanism to determine the priority when two or more interrupts are active simultaneously. So the controller provides configurations to set the priority. Also the CPU itself may accept multiple lines of interrupts each with different priorities and handles them suitably.
Interrupt Nesting and Preemption
The concept of priority automatically leads us to the concept of nesting and preemption. It might be required to process a higher priority interrupt even when the CPU is servicing a lower priority interrupts. So we need to support nesting, whereby interrupts are enabled as soon as possible after entering the ISR and current priority level of interrupt processing is set. When a higher priority interrupt occurs, it enters the ISR of that interrupt, interrupts enabled soon and the cycle goes on.
There is always a delay from the time of assertion of interrupt and the action handling of that interrupt. This might be due to various reasons like the CPU executing a critical section with interrupts disabled, or executing a higher priority interrupt etc. Handling of this delay or latency determines the difference between a General Purpose (no guaranteed latency) and a Real Time Operating System (guaranteed latency).
With this quick introduction about Interrupt mechanism, concepts and associated terms, we will now explore various methods of interrupt handling in embedded systems starting with an introduction to the ARM architecture.