5. Reset Sequence
Reset Sequence
The reset sequence for an STM32 microcontroller, like many other ARM Cortex-M based MCUs, generally involves the following steps:
1. Power-On Reset
When the microcontroller is powered on, or a manual reset occurs, all internal registers and memory locations are set to their default values.
2. System Initialization
After the power-on reset, the MCU initializes its system configuration, sets up the clock, and performs other hardware-related initializations. This stage might also include setting up the interrupt vector table.
3. Jump to Boot ROM (Optional)
In some configurations, the STM32 may first jump to a built-in bootloader. Whether it does so depends on the state of specific pins and internal settings. If this option is not enabled, the MCU skips this step.
4. Vector Table Relocation
The MCU relocates the interrupt vector table if needed. By default, it resides at the start of the flash memory, but you can change its location.
5. Stack Pointer Initialization
The initial value of the stack pointer (SP) is loaded from the address 0x00000000
. This address usually contains a value that points to the top of the stack in SRAM.
6. Jump to Reset Handler
The program counter (PC) is set to the address located at 0x00000004
, which is the entry point of the Reset_Handler
function. This function is responsible for setting up the conditions necessary for the main()
function to execute, such as initializing static variables.
7. Program Execution
After the Reset Handler does its job, the program jumps to the main()
function, and the application code starts executing.
Throughout this process, the MCU might also execute other housekeeping tasks like enabling certain peripherals, depending on the specific requirements or settings in the system.
Startup Code
When creating a STM32 project, the IDE generates a startup code file that contains the reset handler and other functions that are executed before the main()
function. The startup code file is usually named startup_stm32xxxx.s
or startup_stm32xxxxxx.s
, where xxxx
or xxxxxx
is the specific STM32 model. Let's take a look at its contents:
Set the Initial Stack Pointer (SP)
ldr r0, =_estack
mov sp, r0 /* set stack pointer */
This is part of the Reset_Handler
function. It sets the initial value of the stack pointer. _estack
is a symbol defined in the linker script to indicate the top of the stack.
Call System Initialization Function
bl SystemInit
This calls a function SystemInit
, which usually sets up things like the clock and other system configurations.
Copy Data Segment Initializers
When your program starts, any global or static variables that are initialized to non-zero values need to be set up. These initial values are stored in Flash memory, but the variables themselves live in SRAM when your program is running. The purpose of this block of code is to copy these initial values from Flash (_sidata
) to their proper locations in SRAM (_sdata
to _edata
).
For example, if you have a global variable like int foo = 42;
, the value 42
is stored in Flash. This code moves 42
from Flash to the SRAM location where foo
resides.
ldr r0, =_sdata
ldr r1, =_edata
ldr r2, =_sidata
...
b LoopCopyDataInit
This part copies the initialized data from flash to SRAM. _sdata
and _edata
are symbols representing the start and end of the data segment in SRAM. _sidata
represents where the initial values are stored in Flash.
Zero Fill the BSS Segment
The .bss
segment holds variables that are initialized to zero or are uninitialized. The C standard specifies that these should start as zero. Instead of storing a bunch of zeros in the Flash memory, which would be a waste of space, the BSS section simply declares where these zero-initialized variables should be in SRAM (_sbss
to _ebss
). This block of code then goes through and sets all those memory locations to zero.
Both of these steps help set the stage for running your main program. They make sure that all global and static variables have the values you expect before main()
is called.
ldr r2, =_sbss
ldr r4, =_ebss
...
b LoopFillZerobss
This section sets the entire .bss
segment in SRAM to zero. _sbss
and _ebss
are symbols defined in the linker script representing the start and end of the .bss
segment.
Call Static Constructors
bl __libc_init_array
This line is responsible for calling the static constructors of your program. In C++, static constructors are used to initialize objects that have static storage duration. These are global objects, static members of classes, and objects declared with the static
keyword inside functions. Before you enter main()
, these constructors have to be run to initialize these objects properly.
The __libc_init_array
function is provided by the C runtime library, and it essentially loops through an array of function pointers, calling each one in turn. These function pointers are set by the linker to point to the static constructors in your program.
This step is vital for C++ programs, but for a C program, it usually doesn't do anything as C doesn't have the concept of constructors. Nonetheless, this function is often included in startup code to make the environment compatible with both C and C++ programs.
Call Main
bl main
This transfers control to the main()
function in your C/C++ program.
Infinite Loop
LoopForever:
b LoopForever
If for some reason your main()
function returns, this will catch it and loop indefinitely.