The short answer is yes, it is possible to place interrupt vectors in RAM instead of flash memory on Cortex M0 microcontrollers after the bootloader has run. However, there are some important considerations to keep in mind when doing this.
Overview of Cortex M0 Interrupt Handling
On Cortex M0 MCUs, the interrupt vector table is normally located at the beginning of flash memory, starting at address 0x00000000. This vector table contains the reset vector and up to 32 interrupt vector entries. Each entry consists of a 32-bit pointer to the associated interrupt handler function.
By default, the processor fetches these vector addresses from flash memory on startup and whenever an interrupt occurs. However, it is possible to relocate the vector table to RAM after bootup. This offers a few potential benefits:
- Faster interrupt handling by reducing flash access latency
- Ability to modify interrupt vectors at runtime
- Freeing up flash memory by moving vectors to RAM
There are two main steps involved in moving the vectors to RAM:
- Copying the vector table from flash to RAM on startup
- Updating the Vector Table Offset Register (VTOR) to point to the new RAM-based table
Let’s look at these steps in more detail.
Copying the Vector Table to RAM
The first step is to copy the vector table from its default flash location to a RAM location. This would typically be done in the startup code, after the MCU comes out of reset but before jumping to the main application.
A simple way to do this is:
- Allocate a 32-byte aligned byte array in RAM to store the new vector table
- Copy the .vectors section from flash to this RAM array using memcpy() or a manual loop
- Update each vector’s address in RAM to point to the associated handler function
For example:
#define VECTORS_SIZE 0x100
static uint32_t ram_vectors[VECTORS_SIZE/sizeof(uint32_t)];
void copy_vectors(void) {
// Copy vectors from flash to RAM
memcpy(ram_vectors, (uint32_t*)0x00000000, VECTORS_SIZE);
// Update vector addresses to point to handler functions
ram_vectors[Reset_Handler] = (uint32_t)&ResetHandler;
ram_vectors[NMI_Handler] = (uint32_t)&NMI_Handler;
...
}
This copies the first 256 bytes (0x100) from flash to the RAM array. It then updates each address to point to the correct interrupt handler function.
Updating the VTOR
After copying the vector table, the next step is to update the VTOR register to redirect the processor to the new RAM location on subsequent interrupts or reset.
The VTOR holds the start address of the vector table. This can be changed at runtime. To point to the new RAM-based vector table, write the start address of the ram_vectors array to VTOR:
// Set VTOR to new RAM location
SCB->VTOR = (uint32_t)ram_vectors;
Now when interrupts occur, the processor will fetch the vector addresses from RAM instead of flash.
Considerations When Relocating Vectors
There are a few important considerations when relocating the vector table to RAM:
- The new vector table must remain resident in RAM and not get overwritten.
- Interrupt latency may increase slightly due to added RAM access time.
- Make sure to update VTOR before enabling interrupts.
- May need to handle interrupts differently during initial copy stage.
- Relocation code must execute from flash, not RAM.
- RAM usage increases by at least 256 bytes (for vector table).
Overall, putting vectors in RAM can improve interrupt response times, and free up flash memory for other uses. But care must be taken to do this properly without disrupting normal interrupt handling.
Implementing Vector Relocation After Bootloader
To implement vector relocation after a bootloader, the basic process would be:
- Bootloader runs initial system setup, copies itself to RAM, then jumps to the application entry point.
- Application startup code runs, which includes:
- Copy vector table from flash to RAM
- Update VTOR to point to new RAM table
- Continue application initialization
- Application main loop runs, with interrupts now being handled via RAM-based vector table.
This allows the flexibility of having a bootloader initialize the system, while still allowing the application to customize the interrupt handling by moving vectors to RAM.
Some things to consider when implementing this:
- The bootloader must copy itself to RAM before jumping to the application. Otherwise, the relocation code would improperly execute from flash rather than RAM.
- The bootloader should not enable interrupts initially. The application should handle enabling interrupts after relocating the vectors.
- Pass necessary information from bootloader to application (RAM table location, etc).
- May need bootloader to handle any interrupts that occur in initial stages before relocation.
- Ensure proper transition between bootloader and application entry points.
With careful coding, vector relocation can be cleanly integrated after a Cortex M0 bootloader. This takes advantage of both the initialization abilities of the bootloader, and the flexibility of RAM-based vectors for the application.
Example Code
Here is some example code showing a basic vector relocation implementation after a bootloader:
/* Bootloader */
void bootloader_start() {
// Initial HW setup
// Copy bootloader code to RAM
// Jump to application entry point
void (*application_entry)(void) = APPLICATION_ADDRESS;
application_entry();
}
/* Application Code */
#define VECTORS_SIZE 0x100
static uint32_t ram_vectors[VECTORS_SIZE/sizeof(uint32_t)];
void application_entry() {
// Copy vectors to RAM
copy_vectors();
// Set VTOR
SCB->VTOR = (uint32_t)ram_vectors;
// Continue application init
init_application();
// Start main loop
while(1) {
// Main loop
}
}
void copy_vectors(void) {
// Copy flash vectors to ram_vectors
// Update vector addresses to handler functions
}
This demonstrates the basic flow of initializing the bootloader, transitioning to the application entry point, relocating the vectors, and then running the main application loop.
In conclusion, with careful programming it is certainly possible to relocate the interrupt vectors to RAM after bootup on a Cortex M0 microcontroller. This can provide performance and flexibility benefits for the application after initial bootloader initialization.
References
[1] Cortex-M0 Devices Generic User Guide (DUI0662A). ARM Ltd.
[2] AN258 Application Note. Moving Interrupt Vectors from Flash to RAM on ARM Cortex-M-Based MCUs. Silicon Labs.
[3] Application Note 192. Relocating the ARM Cortex M0 Vector Table. NXP.