HomeAbout UsContact Us

Boot Sequence and Reset Handling in Embedded Systems

By Jithin Tom
Published in Embedded Concepts
June 30, 2026
3 min read
Boot Sequence and Reset Handling in Embedded Systems

Table Of Contents

01
The Reset Event and Vector Table
02
Startup Code: From Reset to main()
03
Types of Reset and Their Implications
04
Linker Script: The Memory Map Contract
05
Practical Defensive Patterns
06
Summary
07
Related Reading
08
References
09
Frequently Asked Questions

When you flip the power switch on a microcontroller, a carefully choreographed sequence of hardware and firmware handshakes unfolds before the first line of your main() ever executes. Understanding the boot sequence and reset handling is essential for every embedded developer, because the decisions made in the first few milliseconds determine whether your system starts reliably or silently fails.

The Reset Event and Vector Table

Every reset begins with the processor looking up its vector table — an array of function pointers stored at a well-defined memory address (typically 0x00000000 on ARM Cortex-M devices). The first entry is the initial stack pointer value; the second is the reset handler. The hardware automatically loads the stack pointer, then begins executing the reset handler.

/* Simplified startup assembly for ARM Cortex-M */
.section .isr_vector
.word _estack /* Initial Stack Pointer */
.word Reset_Handler /* Reset vector */
.word NMI_Handler /* NMI */
.word HardFault_Handler/* Hard Fault */

The vector table is the firmware’s root of trust. If it is misaligned, points to the wrong flash bank, or gets corrupted by a faulty bootloader update, the chip will not boot.

Startup Code: From Reset to main()

The reset handler (often called Reset_Handler) performs the bridge between bare-metal hardware and the C runtime environment. Its responsibilities are non-negotiable:

  1. Copy .data from Flash to RAM — initialized global and static variables must live in RAM but their initial values reside in non-volatile storage.
  2. Zero-fill .bss — the C standard mandates that uninitialized global variables start as zero.
  3. Initialize the C library (optional) — constructors for C++ objects, heap setup, etc.
  4. Jump to main() — hand control to application code.
+--------------------------------------------------+
| POWER ON / RESET |
+------------------------+-------------------------+
|
v
+------------------------+-------------------------+
| Load SP & PC from Vector Table |
+------------------------+-------------------------+
|
v
+------------------------+-------------------------+
| Reset_Handler (startup.s) |
| - Set system clock (PLL, HSI/HSE) |
| - Copy .data: flash -> RAM |
| - Zero .bss: RAM |
| - Call SystemInit() |
+------------------------+-------------------------+
|
v
+------------------------+-------------------------+
| __libc_init_array (C++ constructors) |
+------------------------+-------------------------+
|
v
+------------------------+-------------------------+
| main() |
+------------------------+-------------------------+

A minimal Reset_Handler in assembly looks like this:

void Reset_Handler(void) {
/* Copy .data section from Flash to RAM */
uint32_t *src = &_sidata;
uint32_t *dst = &_sdata;
while (dst < &_edata)
*dst++ = *src++;
/* Zero-fill .bss section */
dst = &_sbss;
while (dst < &_ebss)
*dst++ = 0;
/* Call system clock configuration */
SystemInit();
/* Enter main */
main();
/* main() should never return */
while (1) { }
}

The symbols _sdata, _edata, _sidata, _sbss, and _ebss are defined by the linker script, establishing the exact memory boundaries the startup code operates on.

Types of Reset and Their Implications

Not all resets are equal. Understanding the reset source helps diagnose faults and design robust recovery.

Reset SourceTriggerRecovery Notes
Power-On Reset (POR)VDD rising edgeFull initialization required
External Reset (nRST pin)Pin asserted lowGPIO state may be保留
Watchdog ResetWDT timeoutIndicates software failure
Brown-Out Reset (BOR)VDD below thresholdVoltage transient occurred
Software Reset (SYSRESETREQ)NVIC application interruptGraceful shutdown possible

Reset Cause Detection

Most MCUs expose a Reset Status Register that firmware can read on boot to determine why the previous run ended. This is invaluable for field diagnostics:

void check_reset_cause(void) {
uint32_t reset_flags = RCC->CSR;
if (reset_flags & RCC_CSR_PORRSTF) {
/* Power-on: device was off */
}
if (reset_flags & RCC_CSR_IWDGRSTF) {
/* Independent watchdog timeout */
}
if (reset_flags & RCC_CSR_SFTRSTF) {
/* Software-initiated reset */
}
/* Clear flags for next boot */
RCC->CSR |= RCC_CSR_RMVF;
}

Linker Script: The Memory Map Contract

The linker script (.ld file) is where the physical memory layout of the target is described. It tells the linker where Flash and RAM begin, how large they are, and where each section goes.

/* STM32F407 memory layout */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* Define symbols used by startup code */
_sidemandedata = LOADADDR(.data);
_sdata = ADDR(.data);
_edata = _sdata + SIZEOF(.data);

Without a correct linker script, the startup code has no way to know where .data lives in flash versus RAM. Misconfigured memory sizes lead to section overlap, silent corruption, or linker errors.

Practical Defensive Patterns

Double-Buffered Reset Reason

When an application intentionally triggers a software reset, the cause lives only in RAM until the new boot runs. A robust pattern is to store the reason in a backup register or no-init RAM section that survives soft reset:

/* Place in a no-init section that startup does not zero */
__attribute__((section(".noinit"))) uint32_t reset_reason;
void request_system_reset(uint32_t reason) {
reset_reason = reason;
NVIC_SystemReset();
}
void main(void) {
if (reset_reason == REASON_FIRMWARE_UPDATE) {
/* Jump to bootloader instead of normal app */
enter_bootloader();
}
}

Boot-to-Bootloader Path

Many production systems need a way to enter the system bootloader after reset — to accept new firmware over UART, USB, or CAN. This is typically done by checking a GPIO pin or a flag in backup RAM before the application initializes peripherals.

void check_bootloader_entry(void) {
/* Backup register set by app before requesting reset */
if (RTC->BKP0R == BOOTLOADER_MAGIC) {
RTC->BKP0R = 0;
/* Jump to system bootloader at 0x1FFF0000 */
((void (*)(void))(*(__IO uint32_t *)0x1FFF0004))();
}
}

Summary

The boot sequence is far more than a curiosity of microcontroller internals — it is the foundation of system reliability. Key takeaways:

  • The vector table is the first thing the processor reads; its integrity is paramount.
  • Startup code bridges hardware to C by initializing .data, .bss, and the system clock.
  • Different reset sources demand different recovery strategies — always read the reset status register.
  • The linker script is the contract between memory geometry and firmware; misconfiguration causes silent failures.
  • No-init sections and backup registers enable state to survive soft resets, enabling bootloader entry and crash diagnostics.

Mastering these concepts ensures your firmware boots predictably, recovers gracefully from faults, and supports field update workflows.

References

  1. ARM. Cortex-M4 Devices Generic User Guide — Exception model and vector table. ARM DUI 0553A.
  2. STMicroelectronics. RM0090: STM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439 advanced ARM-based 32-bit MCUs — Section 8: Reset and clock control (RCC).
  3. Joseph Yiu. The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors. Newnes, 2013.
  4. FreeRTOS. RTOS scheduler startup and system initialization (Cortex-M FAQ). https://www.freertos.org/FAQHelp.html.
  5. Quantum Leaps. Application Note: QP and ARM Cortex-M — Startup code and kernel configuration. https://www.state-machine.com/doc/AN_QP_and_ARM-Cortex-M.pdf.
  6. IAR Systems. ILink Linker Configuration — Placing code and data in memory. https://docs.iar.com/ewarm/9.7x/en/iar-c-c---development/linking-using-ilink/placing-code-and-data-the-linker-configuration-file.html.

Frequently Asked Questions

What happens when an embedded MCU powers on?

The processor loads the initial stack pointer and reset vector from the vector table at address 0x00000000, then executes the startup code that initializes .data, .bss, and the C runtime before jumping to main().

What are the common reset sources in an embedded system?

Common sources include power-on reset (POR), external reset pin assertion, watchdog timer reset, brown-out reset (BOR), and software-initiated system reset. Each has distinct recovery implications.

Why does the boot sequence initialize .data and .bss sections?

Initialized global variables (.data) must be copied from flash to RAM, and uninitialized globals (.bss) must be zeroed. Without this, variables contain garbage values when main() begins executing.

What is the difference between cold boot and warm reset?

Cold boot follows a full power cycle and runs the complete initialization chain. Warm reset (software or watchdog) may skip some steps but must still ensure the system reaches a known safe state.

Tags

boot-sequencereset-handlingstartup-codeembedded-systemsmcu-init

Share


Previous Article
Understanding const and volatile Pointer Types in Embedded C
Jithin Tom

Jithin Tom

A Closer Look at C/C++, RTOS, and Embedded Systems

Related Posts

Clock Tree and PLL Configuration in Embedded Systems
Clock Tree and PLL Configuration in Embedded Systems
June 26, 2026
6 min
© 2026, All Rights Reserved.
Powered By Netlyft

Quick Links

Advertise with usAbout UsContact Us

Social Media