Skip to content

fix(flash): unlock flash unconditionally for clone STM32F103 chips#12

Open
shaiku wants to merge 1 commit into
TheKikGen:masterfrom
shaiku:fix/flash-unlock-clone-chips
Open

fix(flash): unlock flash unconditionally for clone STM32F103 chips#12
shaiku wants to merge 1 commit into
TheKikGen:masterfrom
shaiku:fix/flash-unlock-clone-chips

Conversation

@shaiku

@shaiku shaiku commented Mar 13, 2026

Copy link
Copy Markdown

On certain clone STM32F103 devices (chips that correctly report DBGMCU_IDCODE = 0x10006412 but have a non-standard flash controller), FLASH_CR.LOCK reads as 0 after reset even though the flash controller is still locked in hardware until the correct key sequence is written to FLASH_KEYR.

The previous conditional unlock:

if (READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {
    WRITE_REG(FLASH->KEYR, FLASH_KEY1);
    WRITE_REG(FLASH->KEYR, FLASH_KEY2);
}

silently skipped the unlock sequence on these devices, causing every subsequent erase and write to do nothing. The bootloader still reported success because the checksum is computed over the received USB data, not the actual flash contents.

Fix: write the unlock keys unconditionally, matching the behaviour of STM32CubeProgrammer's 0x412.stldr flash algorithm, which also writes the keys without checking LOCK first. Writing the correct keys when the flash is already unlocked is a documented safe no-op on genuine STM32F1 silicon (RM0008 rev 21, section 3.3.3).

Also clear FLASH_SR error flags (PGERR, WRPRTERR, EOP) before each page operation, again matching CubeProgrammer's Init() sequence, to ensure a clean controller state if a previous operation left an error flag set.

Tested on a clone device (DBGMCU_IDCODE 0x10006412, WRPR 0xFFFFFFFF, no read protection) where tkg-flash previously reported "1 sectors written / no checksum error" but left flash completely unprogrammed. After this fix the bootloader programs and jumps to user code correctly.

On certain clone STM32F103 devices (chips that correctly report
DBGMCU_IDCODE = 0x10006412 but have a non-standard flash controller),
FLASH_CR.LOCK reads as 0 after reset even though the flash controller
is still locked in hardware until the correct key sequence is written
to FLASH_KEYR.

The previous conditional unlock:

    if (READ_BIT(FLASH->CR, FLASH_CR_LOCK)) {
        WRITE_REG(FLASH->KEYR, FLASH_KEY1);
        WRITE_REG(FLASH->KEYR, FLASH_KEY2);
    }

silently skipped the unlock sequence on these devices, causing every
subsequent erase and write to do nothing.  The bootloader still
reported success because the checksum is computed over the received
USB data, not the actual flash contents.

Fix: write the unlock keys unconditionally, matching the behaviour of
STM32CubeProgrammer's 0x412.stldr flash algorithm, which also writes
the keys without checking LOCK first.  Writing the correct keys when
the flash is already unlocked is a documented safe no-op on genuine
STM32F1 silicon (RM0008 rev 21, section 3.3.3).

Also clear FLASH_SR error flags (PGERR, WRPRTERR, EOP) before each
page operation, again matching CubeProgrammer's Init() sequence, to
ensure a clean controller state if a previous operation left an error
flag set.

Tested on a clone device (DBGMCU_IDCODE 0x10006412, WRPR 0xFFFFFFFF,
no read protection) where tkg-flash previously reported "1 sectors
written / no checksum error" but left flash completely unprogrammed.
After this fix the bootloader programs and jumps to user code correctly.
@shaiku

shaiku commented Mar 13, 2026

Copy link
Copy Markdown
Author

The backstory on this is I bought a batch of STM32F103C6T6 fake blue pills and none of the USB bootloaders would work correctly for various reasons. Yours has the additional initialization code that my chips seem to require (their POR behavior is not accurate), and tkg-flash reported success, but the bootloader failed to actually write data to flash.

STM32CubeProgrammer works, however, so I disassembled 0x412.stldr and compared the flash unlock logic to yours. These are the only differences and these changes are what made your bootloader actually work on my silicon.

It should be perfectly acceptable since it is the exact same logic ST themselves use to program STM32F1 parts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant