The Cortex-M0 is one of ARM’s smallest and simplest microcontroller cores, aimed at low-cost and low-power embedded applications. Like all Cortex-M cores, the M0 starts execution at the reset vector located at address 0x00000000. This reset vector points to the initial stack pointer value and the reset handler function.
The vector table on Cortex-M0 contains up to 16 exception vectors, including the initial stack pointer, reset, NMI, hard fault, and debug monitor. The remaining vectors can be used for system exceptions like SVC and PendSV, or external interrupts.
1. Setting up the Vector Table
When creating a Cortex-M0 application, the first step is to set up the vector table. This is typically done in Assembly code and defines the initial stack pointer, reset handler, and optional exception vectors.
For example: .section .vectors .word _estack /* stack pointer value */ .word Reset_Handler /* reset handler */ .word NMI_Handler .word HardFault_Handler …
The vector table must be aligned on a 512 byte boundary. This allows the processor to quickly load the vectors using a simple masking operation. The linker script should contain directives to place the vector table at the correct aligned address.
2. Implementing the Reset Handler
The reset handler function pointed to by the reset vector is the Cortex-M0 entry point after reset. It needs to perform basic initialization tasks like:
- Setup the stack pointer for each software stack (MSP, PSP)
- Copy initialized data from flash to RAM
- Zero out the .bss section in RAM
- Call SystemInit() to configure clocks/PLL
- Call __libc_init_array() to run C++ constructors
- Call main() or a startup function
A simple reset handler implementation in C: void Reset_Handler(void) { // Initialize memory SystemInit(); // Initialize .data and .bss sections __Init_Data(); // Run constructors __libc_init_array(); // Call main main(); }
The reset handler forms the foundation for application startup and needs to executed key initialization tasks before launching main().
3. Configuring a Bootloader
Many Cortex-M0 applications use a bootloader located at the start of flash memory. The bootloader handles activities like:
- Initial debugging capability
- Flash programming and verification
- Booting into different application images
- Software update capability over a communication interface
To support a bootloader, the Cortex-M0 memory map needs to be configured as: 0x00000000 +———+ | | | Bootldr | | | +———+ 0xXXXXXXXX +———+ | Vectors | +———+ 0xYYYYYYYY +———+ | Code | +———+ 0xZZZZZZZZ +———+ | Data | +———+
The bootloader code occupies the start of flash at address 0x00000000. The application vector table is located further up in flash memory, typically on a 512 byte boundary.
The bootloader has its own embedded vector table that catches resets and exceptions during bootloader execution. But it then needs to configure the Cortex-M0 to use the application vector table before booting the application.
4. Bootloader Vector Table Initialization
The bootloader needs to perform the following steps to initialize the Cortex-M0 vector table on boot:
- Program the VTOR register with the address of the application vector table.
- Set up the MSP stack pointer by reading the initial stack pointer value from the application vector table.
- Configure the application reset handler address into the AIRCR register for exception handling.
Here is sample code to perform these steps: // Get the address of application vector table uint32_t app_vector_table = 0xYYYYYYYY; // Set VTOR to application vector table address SCB->VTOR = app_vector_table; // Get MSP value from application vector table uint32_t msp_value = *(uint32_t*) app_vector_table; // Set MSP __set_MSP(msp_value); // Set application reset handler SCB->AIRCR = (0x5FA << SCB_AIRCR_VECTKEY_Pos) | (app_vector_table << SCB_AIRCR_VECTRESET_Pos);
This configures the processor to use the application vector table and stack on exception entry. The bootloader can now safely jump to the application reset handler to launch the application.
5. Transferring Control to the Application
After configuring the vector table, the final step for the bootloader is to jump to the application reset handler. This can be done in C code by calling the reset handler as a function: void __attribute__((naked)) boot_to_app() { // Load application reset handler void (*reset_handler)(void) = *(uint32_t*)(app_vector_table + 4); // Clear pending interrupts __disable_irq(); NVIC->ICPR[0] = NVIC->ISPR[0]; // Jump to application reset handler reset_handler(); }
Or in Assembly: boot_to_app: ldr r0, =0xYYYYYYYY // Address of app vector table ldr r1, [r0, #4] // Load app reset handler bx r1 // Branch to reset handler
Before jumping, pending interrupts should be cleared to avoid accidentally re-entering the bootloader on exceptions.
Executing this final step passes control from the bootloader to the application code. The application reset handler pointed to by the vector table will then execute and complete the startup process.
6. Special Case: Single Main Application
For simple applications without a bootloader, the vector table still needs to be initialized correctly. This requires the following steps:
- Configure VTOR to point to the vector table.
- Initialize MSP from vector table stack pointer value.
- Set reset handler address in AIRCR.
This is similar to the bootloader initialization, but without a separate bootloader section.
Here is sample code to perform single application vector table initialization: uint32_t* vector_table = (uint32_t*) 0x00000000; // Set VTOR to vector table base SCB->VTOR = (uint32_t)vector_table; // Get MSP value from vector table uint32_t msp_value = vector_table[0]; // Set MSP __set_MSP(msp_value); // Set reset handler SCB->AIRCR = (0x5FA << SCB_AIRCR_VECTKEY_Pos) | ((uint32_t)vector_table[1] << SCB_AIRCR_VECTRESET_Pos);
This simple initialization is sufficient for single image projects without a bootloader. The application can then call its reset handler function directly.
7. Cortex-M0 Hardware Initialization
In addition to vector table configuration, several other hardware initialization steps are required when starting up a Cortex-M0 application:
- Clocks: The SystemInit() function needs to configure the main system clocks like the PLL.
- Memory: Flash memory may require initialization and .data/.bss sections need to be set up.
- Peripherals: Specific peripherals like GPIO pins and communication interfaces need to be initialized before use.
- Interrupts: The NVIC needs interrupt vector and priority configuration.
Much of this is application specific and is typically handled by startup code and hardware abstraction layers. But an awareness of the required hardware initialization can help guide and customize the reset sequence.
8. Tips for Bootloader Design
When designing a Cortex-M0 bootloader, keep these tips in mind:
- Place the bootloader code first in memory starting at address 0x00000000.
- Link the bootloader vector table to match the bootloader memory location.
- Initialize the VTOR, MSP, and reset handler to use the application vector table.
- Make sure to clear pending interrupts before booting the application.
- Validate application images before booting and implement watchdog safety checks.
- Provide an easy way to enter the bootloader from the application code.
- Consider protecting the bootloader with read-out protection if available.
Following these best practices will result in a bootloader that safely and effectively manages the Cortex-M0 vector table.
9. Summary
In summary, the key steps for Cortex-M0 vector table management with a bootloader are:
- Configure distinct vector tables for the bootloader and application.
- Initialize VTOR, MSP, and reset handler from the bootloader when booting the application.
- Clear pending interrupts before transferring control to the application.
- Perform clock, memory, peripheral, interrupt, and other hardware initialization.
- Structure the bootloader to support flash programming, image validation, and robust exception handling.
Properly managing the vector table is critical for safe Cortex-M0 startup and switching between bootloader and application execution modes. Following the techniques described in this article will help create a bootloader that leverages the Cortex-M0 vector table capabilities for effective embedded systems development.