2025-09-26 1:54 PM - edited 2025-09-26 1:56 PM
I believe I am seeing the same issue raised here:
but there were no replies before that thread was locked.
I'm working on a firmware-update mechanism, testing on a Nucleo STM32H755ZI (rev U). When a new firmware is available, I erase the inactive flash bank and then write the new firmware into the freshly erased bank, ping-ponging between banks each time the firmware gets updated.
// Find which flash bank we are running in now
if (FLASH->OPTCR & FLASH_OPTCR_SWAP_BANK)
{
ActiveBank = BANK2;
}
else
{
ActiveBank = BANK1;
}
...
// Bank-erase the inactive flash, and prepare to program it
if (ActiveBank == BANK1)
{
// Only unlock if currently locked
if (FLASH->CR2 & FLASH_CR_LOCK)
{
FLASH->KEYR2 = 0x45670123;
FLASH->KEYR2 = 0xcdef89ab;
}
FLASH->CR2 |= (FLASH_CR_BER | FLASH_CR_START); // perform bank 2 erase
while (FLASH->SR2 & FLASH_SR_QW)
{ }
FLASH->CR2 |= FLASH_CR_PG; // enable programming on this bank
}
else
{
// Only unlock if currently locked
if (FLASH->CR1 & FLASH_CR_LOCK)
{
FLASH->KEYR1 = 0x45670123;
FLASH->KEYR1 = 0xcdef89ab;
}
FLASH->CR1 |= (FLASH_CR_BER | FLASH_CR_START); // perform bank 1 erase
while (FLASH->SR1 & FLASH_SR_QW)
{ }
FLASH->CR1 |= FLASH_CR_PG; // enable programming on this bank
}
// then we program flash words with the incoming data (not shown)
After flash programming, I compute a SHA hash of the newly written firmware and compare it to the expected. If that validation check passes, I perform an option-byte update to flip the SWAP_BANK_OPT bit, then reset to boot into the other bank:
// Only unlock if currently locked
if (FLASH->OPTCR & FLASH_OPTCR_OPTLOCK)
{
FLASH->OPTKEYR = 0x08192a3b;
FLASH->OPTKEYR = 0x4c5d6e7f;
}
FLASH->OPTSR_PRG ^= FLASH_OPTSR_SWAP_BANK_OPT; // toggle the SWAP_BANK_OPT bit
FLASH->OPTCR |= FLASH_OPTCR_OPTSTART; // start option-byte write
while (FLASH->OPTSR_CUR & FLASH_OPTSR_OPT_BUSY) // wait for it to complete
{ }
// generate a software reset of the CM7 core
SCB->AIRCR = (0x5fa << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk;
All of this works great when the original firmware (flashed with STM32Cube programmer to 0x08000000) is running in bank 1, and I send a new firmware image. The new image gets programmed, verified, and the system resets and is now running in Bank 2.
Unfortunately, if I send another image, which should cause a bank-erase of Bank 1, programming of Bank 1, and reset into Bank 1, the process fails at the bank erase: The write to FLASH_CR1 never returns. Furthermore, the board will not boot again until I re-flash the firmware, which suggests that the "Bank 1" erase is somehow erasing Bank 2, instead of or in addition to, Bank 1.
Any insight as to what may be going wrong here?
Solved! Go to Solution.
2025-09-26 2:05 PM - edited 2025-09-26 2:07 PM
SWAP_BANK_OPT says it swaps registers as well.
Sending an erase to FLASH->CR1 will erase the currently running code. If you're always erasing the bank that you aren't booting from, which it sounds like, try sending the erase to FLASH->CR2.
Should be straightforward to verify what's happening by connecting with STM32CubeProgrammer and examining the content of each bank after it "fails to return".
2025-09-26 2:05 PM - edited 2025-09-26 2:07 PM
SWAP_BANK_OPT says it swaps registers as well.
Sending an erase to FLASH->CR1 will erase the currently running code. If you're always erasing the bank that you aren't booting from, which it sounds like, try sending the erase to FLASH->CR2.
Should be straightforward to verify what's happening by connecting with STM32CubeProgrammer and examining the content of each bank after it "fails to return".
2025-09-26 2:35 PM
Aha... thanks @TDK , I somehow managed to read the entire FLASH chapter without catching that detail! In my defense, the nomenclature is pretty confusing. Namely, Bank 1 is always bank 1 and bank 2 is always bank 2, but if you want to erase/program bank 1, you don't necessarily want to use the *1 registers; you might need the *2 registers instead. Perhaps they could have used 1 and 2 to refer to the [invariant] physical banks, and then "low bank" or "high bank" to refer to the bank currently mapped at 0x0800... and 0x08100... respectively.