Placing the interrupt vector table in RAM instead of flash memory can provide flexibility for applications using a custom or proprietary bootloader on Cortex-M0 microcontrollers. This allows the application interrupt handlers to be modified at runtime without needing to reprogram the flash memory. Some key considerations when implementing this approach include:
Benefits of placing vector table in RAM
Here are some of the main benefits of locating the interrupt vector table in RAM rather than flash memory:
- Allows dynamic interrupt handler assignment at runtime – handlers can be changed on the fly without reflashing
- Reduces flash wear by moving frequently updated data like interrupt vectors out of flash memory
- Startup code can be simplified by avoiding flash copy of vector table on boot
- Enables custom bootloaders to have full control over exception handlers
- Easier to implement runtime firmware updates since vector table can be easily redirected
How to locate vector table in RAM
Here is an overview of how to locate the interrupt vector table in RAM:
- Allocate memory for the vector table – usually an array of pointers to interrupt handler functions
- Populate vector table entries with handler addresses in code memory
- Define a vector table variable pointing to the vector table array
- Set the VTOR register to the address of this vector table variable
- Implement handler functions with desired interrupt processing logic
This vector table array becomes the active vector table referenced for exception processing. The VTOR register specifies the start address of the vector table, which the processor will use on exception entry.
Vector table design considerations
Some key points on vector table design:
- Vector table must be 32-bit aligned in RAM
- Reserved exception handlers can be defined as weak aliases
- Stack pointers can be initialized directly in vector table
- NMI, HardFault and other critical handlers should have default implementations
- Vector table entries must point to valid handler code addresses
- RAM allocation must account for entire table size (number of entries * 4 bytes per entry)
Modifying vector table at runtime
To modify the vector table during runtime operation:
- Interrupt should be disabled before modifying table to avoid conflicts
- Change the function address for the desired exception handler
- Clear any pending state for that exception before re-enabling interrupt
- Restore interrupts once vector table is updated
This allows handlers to be dynamically remapped to different functions as needed while the system is running.
Initializing the vector table
Typical steps for initialization include:
- Allocate vector table array in RAM .bss or .data section
- Optional: clear vector table memory
- Populate vector table with default handler addresses
- Define vector table variable and assign address of array
- Set VTOR register to address of vector table variable
- Enable interrupts once vector table is configured
This is often done early during application startup or boot sequence, before main(). The bootloader may also perform vector table initialization prior to launching the application.
Bootloader considerations
Some considerations when using a custom bootloader:
- Bootloader will initialize and populate initial vector table entries
- May communicate vector table location via defined protocol
- Application links to bootloader symbols for common handlers
- Ensures proper stacked-based operation during startup
- May requireCoordination with bootloader for versioning and firmware updates
Proper coordination between the bootloader and application is needed to manage the vector table location and transitions between them.
Example vector table implementation
Here is an example of defining a vector table array in RAM: // Vector table array __attribute__ ((section(“.vectors”))) void (*const vectors[48])(void) = { (void(*)()) &Image$$ARM_LIB_STACK$$ZI$$Limit, // initial stack pointer Reset_Handler, // reset handler NMI_Handler, // NMI handler HardFault_Handler, // hard fault handler // … other handlers }; // Vector table pointer __attribute__ ((section(“.vectors”))) void (*const g_pfnVectors[])(void) = { vectors }; // Set vector table location SCB->VTOR = (uint32_t) g_pfnVectors;
This demonstrates an aligned array containing handler pointers, a vector table variable pointing to the array, and setting the VTOR register to this symbol’s address.
Testing considerations
Some tips for testing vector table relocation:
- Trigger interrupts and verify expected handler is called
- Check handler symbols are matched between bootloader and application
- Debug handler flow starting from exception entries
- Validate no latency or timing impacts from RAM vector table
- Test cases for unaligned vector table, invalid handlers, etc.
- Minimize code differences between flash and RAM handlers
Thorough testing is important to ensure stability across all use cases when modifying the standard vector table implementation.
Summary
In summary, placing the Cortex-M0 interrupt vector table in RAM provides flexibility for bootloader-based systems to customize exception handling on the fly. Careful design is needed to properly initialize, populate, and maintain the vector table, synchronizing with bootloader operation. Rigorous testing will help ensure this approach is robust across runtime conditions and firmware updates.