Bootloader
Startup code, firmware update process.
Every embedded processor needs a mechanism to transfer control from hardware reset to the application firmware in a controlled and verifiable manner. A bootloader is a small, specialized piece of software that runs immediately after a processor is powered on or reset. It prepares the hardware environment, verifies the main application, and then transfers execution to it. Understanding the bootloader is essential for embedded systems engineers who work on firmware deployment, over-the-air updates, and secure boot implementations.
Core Concept: What a Bootloader Does
When a microcontroller is powered on or reset, the hardware automatically fetches an instruction from a fixed memory address known as the reset vector. This is typically the lowest address in flash memory (for example, address 0x08000000 on STM32 devices). The code at this location is the startup code or the bootloader, depending on how the flash is organized.
The bootloader has three primary responsibilities. First, it initializes the minimum hardware required to operate, such as configuring clocks, enabling RAM, and setting up the stack pointer. Second, it determines whether a firmware update is requested or necessary by checking a flag in non-volatile memory, inspecting a GPIO pin state, or receiving a command over a communication interface. Third, if no update is needed, it verifies the integrity of the application firmware and transfers execution to it by modifying the program counter.
Startup Code and Memory Initialization
Before the C application runs, a startup code (typically named crt0 or startup.s in assembly) must prepare the memory environment that C assumes is ready. This includes three critical operations: setting the stack pointer to the top of RAM, zeroing out the BSS segment (uninitialized global variables), and copying initial values for initialized global variables from flash to RAM.
The reason initialized variables need copying is that RAM loses its contents on power-off. Their initial values are stored in a special section of flash during programming. The startup code performs a memory copy from flash to RAM before calling main(). Without this step, global variables would have garbage values.
Flash Memory Layout
In a system with a custom bootloader, flash memory is typically divided into distinct regions. The bootloader occupies the lowest address range and is protected from accidental erasure. The application firmware is placed in a higher address region. A firmware descriptor or header at the beginning of the application region contains metadata such as version number, build timestamp, and a CRC or SHA hash of the firmware image. The bootloader reads this header to verify the application before jumping to it.
Firmware Update Process
A firmware update via bootloader follows a well-defined sequence. The host (a PC or a remote server) sends the new firmware image over a communication interface such as UART, USB, CAN, or Ethernet. The bootloader receives the image in chunks, writes each chunk to a designated area in flash (either a secondary slot or directly to the application area), verifies the complete image using a checksum, and then reboots the device to run the new firmware.
In over-the-air (OTA) update systems, a dual-bank flash layout is used. The device runs from Bank A while downloading the new firmware to Bank B. After successful verification, the bootloader swaps the banks on the next reset. This ensures the device always has a fallback if the new firmware is corrupt.
Jumping to Application
After verification, the bootloader transfers control to the application. This is not a simple function call. The bootloader must reinitialize the vector table base address to point to the application's vector table, disable interrupts to prevent stale interrupt handlers from firing, set the stack pointer to the application's stack top value (read from the first word of the application's vector table), and then perform an indirect branch to the application's reset handler address (the second word of the vector table).
Mathematical Expression
Firmware integrity is verified using a CRC (Cyclic Redundancy Check). A 32-bit CRC is computed over the entire firmware binary during compilation and stored in the firmware header. The bootloader recomputes the CRC over the received or stored firmware image and compares it with the stored value. A mismatch indicates corruption. CRC-32 has a Hamming distance sufficient to detect all single-bit errors and most burst errors up to 32 bits in length.
Given:
Firmware image size = 64 KB = 65536 bytes
Stored CRC-32 in header = 0xA3F2C910
Communication interface: UART at 115200 baud
Why this formula applies:
Time to transfer firmware via UART must fit within timeout window.
Baud rate determines bits per second.
Formula:
Transfer time = (Image size in bits) / Baud rate
Image size in bits = 65536 bytes x 10 bits/byte
(8 data bits + 1 start + 1 stop bit for UART framing)
Substitution:
Transfer time = (65536 x 10) / 115200
Calculation:
Transfer time = 655360 / 115200
Transfer time ≈ 5.69 seconds
Final Answer:
Firmware transfer time ≈ 5.69 seconds at 115200 baud.
Bootloader must keep UART receiver active for at least 6 seconds
before declaring a timeout error.Exam Tip: In bootloader-related questions, remember that the reset vector on ARM Cortex-M processors stores two values at the start of the vector table: word 0 is the initial stack pointer value, and word 1 is the reset handler address. The bootloader reads both before jumping to the application. This is different from older architectures where the reset vector is a single branch instruction.
Mechanism Explained
- The reset vector is the first address the CPU fetches from after power-on or reset. In ARM Cortex-M, the vector table at the base of flash holds the initial stack pointer and reset handler address.
- Startup code (crt0) zeros the BSS segment and copies initialized variable values from flash to RAM before main() is called.
- The bootloader checks an update flag or GPIO pin and either enters firmware receive mode or jumps to the verified application.
- CRC-32 verification of the firmware image ensures data integrity after transfer or storage. A mismatch means the image must be rejected and retransmitted.
- Dual-bank OTA update enables safe firmware upgrades by keeping the running firmware intact in Bank A while writing and verifying the new image in Bank B.
Quick Revision
- The bootloader runs immediately after reset, before the main application. It occupies the lowest flash address region.
- Startup code responsibilities: initialize stack pointer, zero BSS, copy .data section from flash to RAM, then call main().
- ARM Cortex-M vector table: Word 0 = initial SP value, Word 1 = reset handler address. Bootloader reads both when jumping to application.
- Firmware integrity: CRC-32 is computed over the image and verified before execution. Mismatch triggers error or retransmission.
- Dual-bank OTA: active firmware runs in Bank A, new firmware is written to Bank B, banks are swapped on verified reset.
- UART firmware transfer time = (image bytes x 10 bits) / baud rate. Plan timeout windows accordingly.
- Key trap: forgetting to relocate the vector table base address register (VTOR on Cortex-M) before jumping to application causes hard faults when interrupts fire.
Bootloader Quiz
Test your understanding of bootloader operation and firmware update processes.
Q1.On an ARM Cortex-M microcontroller, the very first two words in the flash vector table (at address 0x00000000) define what?
Related Articles
Embedded Systems Overview
Definition, constraints, design metrics.
9 min read
Bus Standards
PCI, USB, ISA historical view.
10 min read
Microcontroller Difference
Microprocessor vs Microcontroller.
12 min read
RISC vs CISC
Comparison of architectures, ARM vs x86 philosophy.
12 min read
USB Basics
Enumeration, endpoints, pipes.
12 min read