The interrupt vector table in ARM processors contains the addresses of interrupt service routines and exception handlers. It provides the processor with the entry points to respond quickly to events like interrupts, exceptions, and resets. The interrupt vector table is one of the fundamental elements that enables efficient interrupt handling in ARM cores.
Overview of Interrupts and Exceptions
An interrupt is a signal sent to the processor that causes it to halt execution of the current program and execute an interrupt service routine (ISR). Interrupts are typically generated by external devices that need attention from the processor. For example, a UART module may generate an interrupt when it has received a new byte of data.
An exception is an error or abnormal condition that occurs as a result of executing instructions. Examples include illegal opcode exceptions, misaligned memory access faults, and divide by zero errors. Exceptions require special handling by the processor to recover and continue correct program execution.
Without a proper way to handle interrupts and exceptions, the processor would not be able to respond to important events in real-time. The interrupt vector table provides an elegant and efficient way for ARM processors to handle asynchronous interrupts and synchronous exceptions.
Organization of the Interrupt Vector Table
The interrupt vector table contains the memory addresses of specific interrupt service routines and exception handlers. It is organized as an array of function pointers, with each entry corresponding to a particular interrupt or exception cause.
In ARMv7 architectures, the interrupt vector table contains up to 240 entries. The first 32 entries are reserved for exceptions. The remaining entries are available for interrupts from on-chip peripherals and external interrupt sources.
In Cortex-M processors, the interrupt vector table can contain up to 496 function pointers. This provides ample space for many interrupts and exceptions.
Each entry in the vector table is 4 bytes long, corresponding to a 32-bit memory address. The order of the entries in the vector table is fixed and predefined. Upon receiving an interrupt or exception, the processor simply indexes into the appropriate entry and jumps to the handler routine.
Key Exception Entries
The first several entries in the exception portion of the interrupt vector table are critical for startup and fault handling:
- Reset – This is the first entry, called on power-up or warm reset. It handles initializations for the processor and C runtime.
- NMI – Entry for non-maskable interrupt handler caused by critical events like memory faults.
- HardFault – Catch-all exception handler for faults without a dedicated handler, like bus faults.
- MemManage – Entry point for Memory Management Unit faults, used for MPU or MMU exceptions.
- BusFault – Handler for prefetch, memory access errors from the bus infrastructure.
- UsageFault – Catches faults related to instruction execution, like undefined instructions.
These key exception handlers allow the processor to cleanly respond to serious error conditions that would otherwise cause a crash. The end user defines the actual handling code for each exception.
Interrupt Entries
After the initial exception entries, the remainder of the interrupt vector table contains entries for interrupts from peripherals and external sources. Some examples include:
- SysTick – The system timer interrupt, generated when the SysTick timer reaches zero.
- PendSV – The pending service call exception, used to implement context switching.
- ADC – Analog-to-digital converter completion interrupt.
- UART – UART receive/transmit interrupts.
- GPIO – General purpose I/O pin interrupts.
- DMA – Direct memory access controller interrupts.
The specific interrupt sources present depend on the ARM microcontroller and its on-chip peripherals. External interrupt controller mappings also appear in this section of the vector table.
Placement of the Interrupt Vector Table
The processor fetches the address of the interrupt vector table from a dedicated register during startup. For example, in Cortex-M the Vector Table Offset Register (VTOR) points to the start of the table.
The table is normally located at the very start of code memory (address 0x0000_0000) so that the processor can access it immediately. The linker script ensures the compiled vector table gets placed at the beginning.
However, the vector table can be relocated in memory if desired. Updating the VTOR allows the processor to access the table at a different memory location.
Vector Table Relocation
Here are some reasons why you may want to relocate the interrupt vector table:
- To place the vector table in RAM rather than flash memory for easier dynamic modification.
- To have multiple vector tables for different operating modes.
- To avoid conflicts when booting code from an external memory device.
- To keep the vector table out of the way of other code/data areas.
Relocating the vector table requires recalculating the offset addresses in the table entries, or mapping the entries to a secondary lookup table. So it adds some complexity.
Interrupt Latency
One of the benefits of the interrupt vector table is low interrupt latency. Latency is the time delay from interrupt assertion to starting execution of the handler.
Because the processor simply indexes into the vector table, it can almost immediately start executing the first few instructions of the interrupt service routine. This minimizes the worst-case interrupt latency.
Typical ARM Cortex-M cores have impressively low interrupt latency cycles:
- Cortex-M0+ – 10 cycles
- Cortex-M3 – 12 cycles
- Cortex-M4 – 15 cycles
- Cortex-M7 – 12 cycles
The interrupt vector table is a key factor enabling such low latency. The processor avoids having to fetch instructions from slower memory areas.
Dynamic Interrupt Configuration
The interrupt vector table facilitates dynamic interrupt configuration at runtime. Since each entry is just a 32-bit address pointer, the entries can be modified on the fly.
For example, interrupt priorities can be changed by remapping entries to different handler functions. Interrupts can also be dynamically enabled and disabled by setting entries to NULL or an empty handler.
Some compilers also allow you to define code that executes when the processor first initializes the vector table. This code can customize and initialize the table entries.
Tradeoffs and Considerations
The interrupt vector table comes with the following tradeoffs:
- It consumes memory – typically at least 256 bytes for a minimal Cortex-M table.
- Table maintenance can be challenging when relocating or modifying entries.
- Not all entries may be utilized, leading to gaps and some waste.
- Does not scale as well for systems with hundreds of interrupt sources.
Interrupt controller peripherals that remap interrupts help mitigate some of these issues. They allow condensing the vector table while handling many sources.
Overall, the vector table approach works very well for most embedded applications. The benefits of quick interrupt response and easy configuration outweigh the downsides.
Summary
The interrupt vector table is fundamental to interrupt handling on ARM Cortex-M and Cortex-A processors. It provides low latency access to interrupt service routines. The vector table’s simplicity also allows for easy configuration and integration.
Understanding the organization, contents, and use of the interrupt vector table helps embedded developers take full advantage of ARM’s interrupt handling capabilities.