Startup code, vector tables and default exception/interrupt handlers for various uCs
- STM32F10x
- STM32F2xx
- STM32F3xx
- STM32F4xx
- STM32F7xx
- STM32L0xx
- STM32L1xx
- STM32L4xx
- STM32F0xx
- STM32F0xx
- STM32H7xx
- LPC17xx
- LPC40xx
- EFM32G
- EFM32GG
- EFM32HG
- EFM32JG
- EFM32LG
- EFM32PG
- EFM32TG
- EFM32WG
- EFM32ZG
- EZR32LG
- EZR32WG
Library requires the following symbols to be defined in the user's linker script:
__top_of_stack : end of RAM area where stack located
__idata_start : start of initializers section
__data_start : start of initialized data section
__data_end : end of initialized data section
__bss_start : start of zero-filled data section
__bss_end : end of zero-filled data section
Startup code is unified and very simple. It consists of Reset_Handler
and two weak
initialization functions.
//------------------------------------------------------------------------------
void Reset_Handler()
{
if( __low_level_init() )
{
memcpy(__data_start, __idata_start, __data_end - __data_start); // copy initialized variables
memset(__bss_start, 0, __bss_end - __bss_start); // zero-fill uninitialized variables
__libc_init_array(); // low-level init & ctor loop
}
main();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
__attribute__ ((weak))
void _init()
{
}
//------------------------------------------------------------------------------
int __low_level_init()
{
return 1;
}
//------------------------------------------------------------------------------
At first, __low_level_init()
function called. The function by default returns 1
that means to perform initialization of RAM data objects. The user can override this function - in particular, to set up clock speed, check type of start ('cold', 'warm', etc.) and return 0
if required that causes skip of initialization.
The second initialization function _init()
called from __libc_init_array()
1 just before calling of global class-object constructors.
This flexible approach allows for the user to run custom initialization code to satisfy user's project demands.
Vector table uses weak handler stubs that can be overrided in the user's project code. There are two sets of the stubs - for debug and release configurations. Debug configuration includes sepatate 'empty' function for each handler:
__attribute__ ((noreturn))
static void default_handler() { for(;;) { } }
and special function for HardFault_Handler:
static void hf_handler()
{
volatile int i = 0; // debug variable: set non-zero value to
while(!i) { } // return from handler - this figures out
// an address where HW fault raised
}
Such code allows the user to change value of internal variable i
to cause return from the handler and, therefore, jump to program address where Hard Fault occured.
In release configuration all vector table addresses point to the same default handler. This reduces code memory consumption.
By default, debug configuration is turned on. The user can switch release configuration by definition of NDEBUG
macro with -D
command line option.
[1] Library function from GCC toolchain.