
In an embedded system, when a device powers on or resets, the journey from address 0x0 to the main() function typically involves several stages. If a bootloader is involved, it adds an extra step before the main application firmware begins executing.
Let’s break this down, step by step:
1. Power-On or Reset
When the microcontroller powers on or resets, the Program Counter (PC) is set to 0x0, where the vector table is stored. On ARM Cortex-M processors, the vector table starts with:
Address 0x0: The initial stack pointer value, which is loaded into the stack pointer register to manage the stack.
Address 0x4 (or nearby): The reset handler address, which is either the bootloader’s reset handler (if there is one) or the main application firmware’s reset handler.
2. Bootloader Execution (Optional)
If a bootloader is present, its reset handler is executed instead of the application’s reset handler. The bootloader serves as a small program with specific tasks like:
Checking for firmware updates: For example, via UART, USB, or other interfaces.
Loading new firmware: If an update is detected, the bootloader can overwrite the existing firmware with the new version.
Selecting which firmware to run: In some systems with multiple firmware versions, the bootloader chooses which one to execute.
System configuration: The bootloader may initialize basic hardware peripherals required to perform its functions.
Once the bootloader completes its tasks, it jumps to the application firmware by setting the PC to the reset handler address of the main application.
3. Application Firmware Reset Handler
When the bootloader finishes (or if no bootloader is used), control passes to the reset handler of the application firmware. The reset handler initializes the system before calling the main() function:
Memory Initialization:
Copying the initialized data section from Flash to RAM (for global/static variables with initial values).
Zeroing out the BSS section (for uninitialized global/static variables).
System Setup:
Initializing hardware peripherals (e.g., clocks, GPIOs, timers).
Configuring interrupts if necessary.
4. Jump to main()
Once the memory and system initialization is complete, the reset handler jumps to the main() function, and the core application begins running.
Example Flow:
Address 0x0: Initial stack pointer value.
Bootloader (if present):
Bootloader reset handler executes.
The bootloader checks for updates or performs system checks.
If no update is needed, it jumps to the application firmware's reset handler.
Application Reset Handler:
Initializes memory (data, BSS).
Configures system peripherals.
Jump to main(): The application starts executing.
Bootloader Example in Action
Here’s a simplified example of what happens in a system with a bootloader.
Bootloader Vector Table (Cortex-M):
__attribute__((section(".bootloader_vector")))
const uint32_t bootloader_vector_table[] =
{
(uint32_t)&_initial_stack_pointer, // Stack pointer at address 0x0
(uint32_t)&Bootloader_Reset_Handler // Bootloader reset handler at address 0x4
};
Bootloader Reset Handler:
void Bootloader_Reset_Handler(void)
{
// Perform bootloader tasks like:
// 1. Checking for firmware updates
// 2. Loading new firmware if available
// 3. Jumping to the application firmware's reset handler
Jump_To_Application();
}
void Jump_To_Application(void)
{
// Application's vector table typically starts at a fixed address in Flash
uint32_t app_reset_handler_address = *(uint32_t *)(APPLICATION_START_ADDRESS + 4);
// Set the stack pointer to the application's stack pointer value
__set_MSP(*(uint32_t *)APPLICATION_START_ADDRESS);
// Jump to the application's reset handler
((void (*)(void))app_reset_handler_address)();
}
Application Firmware Reset Handler:
void Reset_Handler(void)
{
// Initialize memory sections: copy data, zero BSS
extern uint32_t sdata, edata, ladata, sbss, ebss;
// Copy initialized data from Flash to RAM
uint32_t src = &_la_data;
uint32_t dst = &_sdata;
while (dst < &_edata) { dst++ = src++; }
// Zero out the BSS section
dst = &_sbss;
while (dst < &_ebss)
{
*dst++ = 0;
}
// Call system initialization (if any)
SystemInit();
// Finally, jump to main
main();
}
Summary of the Bootloader's Role:

The bootloader acts as a middle layer between power-on and the application firmware. It checks if a firmware update is necessary or if any pre-boot tasks need to be performed. Once the bootloader finishes, it hands over control to the main application firmware’s reset handler, which completes system initialization and jumps to the main() function.
In systems without a bootloader, the reset handler is executed directly after power-on. However, in systems with a bootloader, it allows features like firmware updates and alternate boot modes, providing flexibility and control before the main application starts.
It’s also important to note that the CPU reset address isn’t always zero. Many architectures, such as ARM (if I’m not mistaken), offer a configurable option for a high or low reset address. However, the general bootstrap mechanism remains largely the same as previously described.
Thanks NDOME and parlezvoustech
Commentaires