When a Cortex-M3 microcontroller first powers on or resets, it will begin executing boot code located in internal flash or external memory. This boot code is responsible for configuring the system and preparing it for application code execution. However, resets and exceptions may occur during the boot process which must be handled properly to ensure reliable system start up. This article provides guidance on handling resets and exceptions during Cortex-M3 boot code execution.
Cortex-M3 Reset Types
There are several types of resets that can occur on a Cortex-M3:
- Power-on reset – Occurs when power is first applied to the microcontroller
- External reset – Triggered by an external reset signal
- Software reset – Triggered by software writing to the AIRCR register
- Watchdog reset – Triggered by the watchdog timer expiring
Upon any reset event, the Cortex-M3 will start executing code from the reset vector located at address 0x00000000. This reset handler code should perform basic system initialization like copying data sections to RAM, before jumping to more comprehensive boot code.
Reset Handler Code
Here is example reset handler code in C: void Reset_Handler(void) { // Copy .data section from flash to RAM uint32_t size = (uint32_t)&_edata – (uint32_t)&_sdata; uint8_t *pDst = &_sdata; uint8_t *pSrc = &_etext; while(size–) { *pDst++ = *pSrc++; } // Clear BSS section __asm(” ldr r0, =_sbss\n” ” ldr r1, =_ebss\n” ” mov r2, #0\n” ” .thumb_func\n” “zero_loop:\n” ” cmp r0, r1\n” ” it lt\n” ” strlt r2, [r0], #4\n” ” blt zero_loop”); // Call system initialization SystemInit(); // Jump to main __asm(” ldr r0,=_start\n” ” bx r0″); }
This performs essential startup tasks like zeroing RAM, copying initialized data to RAM, and calling SystemInit() before jumping to the main application entry point _start().
Configurable Reset Behavior
The Cortex-M3 reset behavior is configurable via the AIRCR and DEMCR registers:
- AIRCR controls reset type and reset vector
- DEMCR controls debug event behavior
For example, to enable a core reset and use the default reset vector: // Enable core reset AIRCR = AIRCR_VECTKEY | AIRCR_SYSRESETREQ; // Use default reset vector AIRCR = AIRCR & ~AIRCR_VECTRESET;
Handling Exceptions During Boot
Exceptions like hard faults, bus faults, and usage faults may occur during the boot process before the Cortex-M3 fault handlers are configured. To handle these cleanly:
- Implement a default fault handler that at least blinks an error LED
- Configure SVC_Handler and PendSV_Handler to an infinite loop
- Configure configurable fault handlers to point to the default handler
Example default fault handler code: void Fault_Handler(void) { // Blink error LED while(1) { Error_LED_Toggle(); } }
And example handler configuration: SVC_Handler = Fault_Handler; PendSV_Handler = Fault_Handler; MemManage_Handler = Fault_Handler; BusFault_Handler = Fault_Handler; UsageFault_Handler = Fault_Handler;
This helps prevent the boot code from crashing catastrophically before the proper application fault handlers are configured.
Debugging Boot Code
Debugging boot code brings unique challenges since the Cortex-M3 debug mechanisms may not yet be enabled. Possible debugging techniques include:
- Step through boot code with a JTAG/SWD debugger
- Use an ITM SWO trace to observe boot execution
- Integrate a UART driver early and print debug messages
- Toggle GPIO pins at key points to observe with a logic analyzer
Printing debug messages via UART is often the simplest and most informative technique. For example: void boot_message(char *msg) { UART_Init(); // Initialize UART peripheral UART_WriteString(msg); } void Reset_Handler() { boot_message(“Reset occurred”); // Reset handling code boot_message(“Done reset handling”); }
Validating Boot Code
Thoroughly test and validate boot code by injecting faults and exceptions. For example:
- Trigger reset during different application states
- Corrupt stack pointer to trigger stack faults
- Enable MemManage, BusFault, and UsageFault
- Trigger a hard fault exception
- Force boot code execution from flash rather than RAM
Validate that in each case, the system reliably recovers and indicates the error. This build confidence in the boot code’s fault handling mechanisms.
Boot Optimization Techniques
Certain optimization techniques can help optimize boot time and performance:
- Place boot code in ITCM RAM for single cycle access
- Initialize hardware in a sequential order
- Configure PLLs/clocks to minimum settings first
- Only enable necessary peripherals/interrupts
- Use DMA if possible for memory transfers
- Keep boot code compact by calling initialized C functions
Faster boot times increase responsiveness and reduce power consumption during start up. Boot time can be measured with oscilloscope on GPIO pins toggling at entry/exit of boot.
Boot Security Considerations
Keep these security considerations in mind for Cortex-M3 boot code:
- Prevent access to boot code regions via MPU or memory protections
- Do not include any debug functionality or backdoors
- Clear registers and memory regions containing sensitive data
- Use checksums to validate boot code integrity
- Encrypt boot code region for anti-tampering
Securing the boot code protects the system during its most vulnerable phase and forms a root of trust for chain of trust security architectures.
Conclusion
Robustly handling resets and exceptions during Cortex-M3 boot code is critical for reliable system start up. Use fault handlers, debug methods, and testing techniques to build confidence in the boot code. Apply optimizations and security principles to further enhance the boot process. With careful design and implementation, Cortex-M3 boot code can provide a solid foundation for the application and overall system operation.