Avoid declaring initialized global/static/volatile variables in system_xxxx.c, SystemInit() SystemCoreClockUpdate(), SystemCoreClock issues

05 Apr 2015

The specific F411 issue:

F411 uses stlink MCO as clock input source (HSE with bypass) but if you want to use internal oscillator (HSI) and setup jumper accordingly, code compiled with online IDE does not start.

The quick fix and go happy:

http://developer.mbed.org/questions/4684/Clock-sources-and-PC14PC15/ in system_stm32f4xx.c change

__IO const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

to

const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};

This fix was silently applied here and there to some other platforms (still not to F411?!?!?!), but if you search in mbed-src for AHBPrescTable you can see no one have clear idea on how to declare it. So I took some time and cigarettes to investigate and find exactly what's the problem and where it is.

The root cause:

At least for online compiler with its optimization enabled, SystemInit is called before the c/c++ compiler specific libraries, which prepare RW and ZI area in ram, so if some initialized global/static/volatile variables have been used in SystemInit and in any function it calls, they are not been set with their initial value yet, they are in ram RW zone but contain garbage.

uint32_t SystemCoreClock = 16000000; // SystemCoreClock is somewhere in 0x20000xxx RW area, but contains garbage -> FAIL
SystemCoreClock = 16000000; // now ok

__IO const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
// volatile (read everytime, don't optimize, can be changed outside the code) or const (no one will change it)?
// anyway compiler considers it volatile and put in ram RW area, but values not yet initialized -> FAIL

const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; // ok, in flash and don't waste 16byte of ram

Looking around different platforms, i saw similar situations, variables declared with initial values then variable set again with the same value, i don't know if the authors were aware of this RW zone issue or if they re-set the variable because "who cares, now it works", i just like to give a root cause to weird problems and share something to read.

The digging around

Just because i like to know how things work, i disassembled a simple blinky program compiled with online compiler. Early at the reset handler /media/uploads/Geremia/reset.png SystemInit() of system_stm32f4xx.c overview. It calls HAL_Init() of stm32f4xx_hal.c which calls HAL_InitTick() of hal_tick.c which calls SystemCoreClockUpdate() of system_stm32f4xx.c /media/uploads/Geremia/systeminit.png HAL_InitTick() view. It calls SystemCoreClockUpdate() and here it comes the bug /media/uploads/Geremia/hal_inittick.png

Hope it helps