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.