Microcontrollers & architecture 3 min read

Reset and boot sequence

When an Arm Cortex-M leaves reset, the hardware reads two words from the vector table: the initial stack pointer and the address of Reset_Handler. Startup code then prepares memory and clocks, and only then calls main().

Six steps from power-on to main(): the reset pin is released; the CPU reads the initial stack pointer at 0x0000_0000; it reads the Reset_Handler address at 0x0000_0004; Reset_Handler copies .data to SRAM and zeroes .bss; SystemInit() configures the clocks; main() runs. Beside the steps, the vector table lists the initial SP, Reset_Handler, NMI_Handler and HardFault_Handler at addresses 0x0 to 0xC.
The hardware does steps 1–3; your startup code does the rest.

What causes a reset

Power-on, a supply dip (brown-out), the reset pin, a watchdog timeout, or software calling NVIC_SystemReset(). STM32 parts record the cause in flags in RCC->CSR, which helps you track down an unexpected watchdog reset.

The first two words

No code runs before the CPU has a stack, so the stack pointer is the very first entry in the vector table, followed by the reset vector. In C it looks like this:

__attribute__((section(".isr_vector")))
const uint32_t vector_table[] = {
  (uint32_t)&_estack,           // 0x00: initial SP (top of SRAM)
  (uint32_t)Reset_Handler,      // 0x04: where execution starts
  (uint32_t)NMI_Handler,        // 0x08
  (uint32_t)HardFault_Handler,  // 0x0C
  /* ... one entry per exception and interrupt ... */
};

Startup code builds the C world

C promises that globals start with their initial values and that uninitialised ones start at zero. Before main() can run, Reset_Handler has to make that true:

void Reset_Handler(void) {
  uint32_t *src = &_sidata, *dst = &_sdata;
  while (dst < &_edata) *dst++ = *src++;      // copy .data from Flash
  for (dst = &_sbss; dst < &_ebss; ) *dst++ = 0;  // zero .bss
  SystemInit();                               // clocks, e.g. the PLL
  main();
  while (1) {}                                // main() should never return
}

The _sdata-style symbols come from the linker script. That copy loop is why int count = 5; already holds 5 when main() starts. Some toolchains call SystemInit() first; the result is the same.