Flash Loader resets State Register incorrectly.
- September 27, 2026
- 0 replies
- 3 views
PROBLEM:
When loading a program, with size filling an odd number of memory sectors, into flash memory the program only starts every other upload.
I have a custom board with an STM32L4KBU. I am connecting to the stm32 with the STLINK-V3SET, over SWO. I am developing with the VSCode STM32CubeIDE extension pack, on linux mint.
STM32CubeProgrammer version: 2.23.0
Boot0 pin is pulled to GND through 10k, default boot option bytes
When using the STM32_Programmer_CLI to upload code there is a bug where the FLASH_SR PEMPTY register is not cleared correctly. It appears that the flash loader resets the register by writing all 1s, but the PEMPTY bit is specifically write 1 to toggle. This actually seems to be an odd design choice as the rest of the bits are write 1 to clear, and the language in the RM0394 Reference Manual is quite inconsistent (section 3.7.5, bit 17). A correct reset is to set this register to itself.
The FLASH_SR register seems to get this toggling for every sector written into flash, meaning if there are an odd number of sectors in the program image the bit ends up toggled - if it was 0 it will be set to 1 and the stm32 will jump to bootloader code instead of running user flash. No software or hardware nrst reset will reset PEMPTY except for a power off/on reset, which makes it difficult to attach a debugger right after upload.
I can write an empty program with a large dummy array to see the behavior:
...
/* USER CODE BEGIN 0 */ const uint8_t dummy[2048] = {1}; // odd sector count, PEMPTY toggles every upload // const uint8_t dummy[4096] = {1}; // even sector count, PEMPTY keeps value from last upload /* USER CODE END 0 */
/** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ __asm volatile ("" :: "r" (dummy)); //access dummy so it gets linked in /* USER CODE END 1 */
...
Ill attach the whole main.c, and even and odd sized compiled .elf files
Upload the odd sector size image with:
STM32_Programmer_CLI -c port=SWD -w loaderBug.elf -v -rst
and check FLASH_SR with:
STM32_Programmer_CLI -c port=SWD -r32 0x40022010 1
Every other upload FLASH_SR contains 0x00020000, instead of 0 every time.
Also using the debugger in VSCode and pausing we can see the execution either is user code, or somewhere in bootloader (PC ~0x1fff32xx) every other launch.
Related Reading
This very old thread with I think the exact same problem. Though they only noticed the jump to bootloader, no toggling behavior:
The patch there seems to be for the CUBE IDE specifically, and it appears whatever patch that was has been reverted or missed in the CLI.
This seems to be a recurring issue across some STM32 chips, I think because the PEMPTY bit is so strange. This was another clue: http://efton.sk/STM32/gotcha/g44.html
Another thread about several other issues with the way the empty check is implemented:
Workarounds:
I am able to get the debugger working on every upload by adding a clear to my launch.json:
“preRunCommands": ["set *(int*)0x40022010 = *(int*)0x40022010"]
Or I can toggle the PEMPTY bit with:
STM32_Programmer_CLI -c port=SWD -w32 0x40022010 0x00020000 -nv
(This is a toggle only, so I also have to check the value first)
