The Cortex-M4 interrupt vector table defines the location of exception and interrupt handlers in the microcontroller’s memory map. It provides a structured way to handle asynchronous events like interrupts from peripherals, faults, and exceptions in the processor. Understanding the vector table is key to configuring interrupts and exceptions properly in Cortex-M4 based systems.
Overview of Cortex-M4 Interrupts and Exceptions
The Cortex-M4 processor supports both interrupts and exceptions. Interrupts are events generated by peripherals external to the processor like timers, GPIO pins, or communication interfaces. Exceptions are synchronous events generated within the processor itself in response to instruction execution, for example on illegal opcode fetch or stack overflow.
Both types of events cause the processor to stop regular program execution and branch to a dedicated handler function. This allows the event to be serviced promptly before returning to the main program flow. The vector table defines the memory locations of these handler functions.
Cortex-M4 Vector Table Organization
The Cortex-M4 vector table contains up to 240 4-byte entries covering the full range of interrupts, exceptions, and processor resets. Each entry corresponds to a specific event and holds the address of its handler function. The table starts at memory address 0x00000000 in the Cortex-M4 memory map.
The table is grouped into the following sections:
- Initial stack pointer value
- Reset handlers
- NMI exception
- HardFault and MemManage/BusFaults
- Usage faults
- Reserved entries
- External interrupts
- PendSV, SysTick, and Debug Monitor exceptions
Initial Stack Pointer
The first entry at offset 0x00 holds the initial value for the main stack pointer (MSP) on processor reset. This sets up the stack for the reset handler immediately.
Reset Handlers
The next set of entries starting at 0x04 are for the various processor reset events like power-on, external pin reset, or watchdog reset. Each handler points to code that initializes components and data required for normal firmware operation after reset.
Exceptions
Entries 0x0C to 0x2C are dedicated to exception events like memory faults, bus faults, usage faults, and the NMI interrupt. These allow handlers to respond to error conditions in the processor and memory system.
External Interrupts
A large continuous block from 0x2C to 0x108 is reserved for handling external interrupts. This area is for connecting handler functions to specific peripherals like GPIO, timers, or communication interfaces that generate interrupts.
Processor Exceptions
The final entries starting at 0x108 are for exceptions related to the processor core itself. This includes the PendSV and SysTick handlers commonly used for task scheduling in RTOS systems.
Connecting Interrupts to Handlers
To use a particular interrupt, its handler address must be configured in the corresponding vector table entry. There are a few ways to do this on Cortex-M4:
- Direct modification of the vector table entries
- NVIC registers in the CORTEX_M core
- Vendor HAL functions to set handlers
For external interrupts, the NVIC peripheral is most commonly used. It has a set of Interrupt Set Enable Registers (ISERn) to enable each interrupt, and Interrupt Priority Registers (IPRn) to assign priorities. Interrupt handlers are assigned by writing the function address to the Interrupt Vector Table Offset Register (VTOR).
So a typical interrupt setup on Cortex-M4 involves:
- Write handler function address to VTOR
- Enable interrupt in ISERn
- Set priority if needed in IPRn
- Clear pending status in ICPRn
This chains the interrupt input to the handler function through the vector table.
Vector Table Placement
The Cortex-M4 vector table must be located at address 0x00000000 in the memory map. However, most microcontroller flash memory is not mapped here. So there are two main options for placing the vector table:
- Flash memory – Use linker scripts to remap flash to address 0x0.
- SRAM – Copy vector table from flash to SRAM at startup.
Remapping flash means any code and constants in flash will change address too. This can complicate debugging. SRAM placement avoids this issue but requires code to perform the copy on startup before interrupts are enabled.
Vendors also provide pragmas in compiler toolchains to automatically place the vector table in SRAM. This just inserts the copy table code when building.
Typical Interrupt Handler Structure
Cortex-M4 interrupt handlers follow a regular structure and coding conventions. This helps keep the handler code organized and efficient:
- Save context by pushing used registers to stack
- Clear pending interrupt if needed in ICPRn
- Handle interrupt event code
- Restore saved registers and context
- Return using special EXC_RETURN value
Saving the registers preserves the state of the main program so execution can resume after the ISR. Some registers may need to be preserved across the handler code if they hold important values.
The handler clears any pending interrupt bits itself at the start. This prevents repeated re-entry if the source peripheral reasserts the interrupt signal.
The handler returns using the EXC_RETURN opcode rather than BX LR. This properly restores the stacked program counter and processor state values.
Reducing Interrupt Latency
Some key considerations for fast interrupt response on Cortex-M4:
- Short handlers – Keep ISR code short and fast.
- Low wait states – Use zero wait state memory if possible.
- Tail chaining – Directly branch to next handler if servicing a series.
- Split stack – Dedicated stack for interrupt handlers.
- Premption – Use Preemption Priority to avoid waiting in handlers.
Simple handlers with few instructions minimize context switching time. Low wait state memory ensures fast instruction fetches from the vector table.
For serial events like a ADC sequence, tail chaining avoids returning until the end. The split stack reserves stack space just for ISR usage avoiding collisions with main stack.
Priority settings like Preemption Priority help avoid situations where a higher priority interrupt cannot preempt a lower priority handler, blocking time-sensitive interrupts.
Cortex-M4 Vector Table Example
Here is an example Cortex-M4 vector table with some common handler definitions: /* Vector table in flash */ __attribute__((section(“.vectors”))) const struct InterruptVector { uint32_t *stack_pointer; void (*Reset_Handler)(); void (*NMI_Handler)(); void (*HardFault_Handler)(); void (*MemManage_Handler)(); void (*BusFault_Handler)(); void (*UsageFault_Handler)(); void (*SVC_Handler)(); void (*DebugMon_Handler)(); void (*PendSV_Handler)(); void (*SysTick_Handler)(); } VectorTable = { .stack_pointer = &stack_top, .Reset_Handler = Reset_Handler, .NMI_Handler = NMI_Handler, .HardFault_Handler = HardFault_Handler, .MemManage_Handler = MemManage_Handler, .BusFault_Handler = BusFault_Handler, .UsageFault_Handler = UsageFault_Handler, //External interrupts… };
This shows the vector table structure defined as an array of function pointers and stack pointer values. Reset, fault, and exception handlers are defined along with space for external interrupt handlers.
In summary, understanding the Cortex-M4 vector table organization is key to properly configuring interrupt handlers and exception responses in your firmware. Using the vector table structure, interrupt latency can be optimized to build responsive real-time embedded systems.