2025-10-27 2:05 AM
Hello! I am working on a custom OTA bootloader system on a STM32G473RBT6 MCU in a custom board I have been using maybe for 3 years with no problem. My board is connected to a Raspberry Pi over RS485 network. My idea is this:
The problem is that the bootloader only succeeds if the following condition met (basically full flash erase):
However, when I try to upload a new firmware over the existing one, the bootloader cannot erase the pages "starting from bank 2 (application space)" HAL_FLASHEx_Erase even returns HAL_OK but when I check the addresses they are not filled with 0xFF, it cannot even erase a single page. Here "starting from" situation is the actual weird part because as you can see, the storage area takes space from both bank-1 and bank-2. When new code arrives, the header contains the flash size required. I designed a 52k program and I can successfully erase and write 52K program to storage when erase area starts from bank 1 (checked with STM32CubeProgrammer all the 52KB filled with 0x55 as designed). You probably think MCU is in single bank mode, but it is not. My code checks if FLASH_OPTR_DBANK defined as well as FLASH->OPTR & FLASH_OPTR_DBANK. Using single-bank mode or leaving a dummy blank page from the end of bank one might be a solution but I wonder what is wrong here.
So, my questions are, how can I erase page(s) from bank-2 in STM32G473RB microcontroller? Is there anything I am doing wrong here? Thank you very much in advance!
SUMMARY:
In dual bank mode STM32G473RBT6 cannot erase pages (although HAL_FLASHEx_Erase returns HAL_OK) from bank-2 only. I can erase pages from bank-2 only if I start erasing from bank-1 and go to bank-2.
Below are the flash_erase, flash_write and flash_copy functions I use if it helps:
#define FLASH_PROGRAM_ALIGNMENT 8
HAL_StatusTypeDef flash_erase_region(uint32_t start_address, uint32_t length)
{
HAL_StatusTypeDef status = HAL_OK;
FLASH_EraseInitTypeDef EraseInit;
uint32_t PageError = 0;
uint32_t first_page, num_pages, bank;
uint32_t start = start_address;
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
#if defined(FLASH_OPTR_DBANK)
// Check if dual-bank mode is active
uint32_t optcr = FLASH->OPTR;
uint8_t dual_bank = (optcr & FLASH_OPTR_DBANK) ? 1 : 0;
#else
uint8_t dual_bank = 0;
#endif
if (dual_bank) {
// 64 KB per bank, 2 KB per page → 32 pages per bank
const uint32_t bank_size = 64 * 1024;
const uint32_t page_size = 2 * 1024;
uint32_t end = start_address + length;
while (start < end) {
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
bank = (start < (FLASH_BASE + bank_size)) ? FLASH_BANK_1 : FLASH_BANK_2;
first_page = (start - ((bank == FLASH_BANK_1) ? FLASH_BASE : (FLASH_BASE + bank_size))) / page_size;
// Compute how many pages remain in this bank before possibly crossing boundary
uint32_t bank_end = (bank == FLASH_BANK_1) ? (FLASH_BASE + bank_size) : (FLASH_BASE + 2 * bank_size);
uint32_t bytes_this_bank = (end < bank_end) ? (end - start) : (bank_end - start);
num_pages = (bytes_this_bank + page_size - 1) / page_size;
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInit.Banks = bank;
EraseInit.Page = first_page;
EraseInit.NbPages = num_pages;
status = HAL_FLASHEx_Erase(&EraseInit, &PageError);
if (status != HAL_OK) break;
start += bytes_this_bank;
}
} else {
// Single bank (default, 2KB pages)
const uint32_t page_size = 2 * 1024;
first_page = (start - FLASH_BASE) / page_size;
num_pages = (length + page_size - 1) / page_size;
EraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
EraseInit.Banks = FLASH_BANK_1;
EraseInit.Page = first_page;
EraseInit.NbPages = num_pages;
status = HAL_FLASHEx_Erase(&EraseInit, &PageError);
}
for(uint32_t addr = start_address; addr < start_address + length; addr += 4){
if(*((uint32_t *)addr) != 0xFFFFFFFF){
status = HAL_ERROR;
break;
}
}
HAL_FLASH_Lock();
__enable_irq();
return status;
}
static HAL_StatusTypeDef flash_write_buffer(uint32_t addr, const uint8_t *buf, uint32_t len)
{
HAL_StatusTypeDef status = HAL_OK;
uint32_t aligned_len = (len + (FLASH_PROGRAM_ALIGNMENT - 1)) & ~(FLASH_PROGRAM_ALIGNMENT - 1);
uint8_t tmp[FLASH_PROGRAM_ALIGNMENT];
__disable_irq();
HAL_FLASH_Unlock();
__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_SR_ERRORS | FLASH_FLAG_EOP);
for (uint32_t offset = 0; offset < aligned_len; offset += FLASH_PROGRAM_ALIGNMENT) {
/* prepare a 64-bit value to program */
memset(tmp, 0xFF, sizeof(tmp));
uint32_t copy = (offset + FLASH_PROGRAM_ALIGNMENT <= len) ? FLASH_PROGRAM_ALIGNMENT : (len - offset);
if (copy) memcpy(tmp, buf + offset, copy);
uint64_t data64;
memcpy(&data64, tmp, sizeof(data64));
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr + offset, data64);
if (status != HAL_OK){
//status = HAL_FLASH_GetError();
break;
}
}
HAL_FLASH_Lock();
__enable_irq();
return status;
}
static HAL_StatusTypeDef flash_copy_region(uint32_t dst, uint32_t src, uint32_t len)
{
/* dst must be erased before calling this function */
HAL_StatusTypeDef status = HAL_OK;
const uint32_t chunk = CHUNK_SIZE;
uint8_t buf[chunk];
uint32_t remaining = len;
uint32_t rptr = src;
uint32_t wptr = dst;
uint32_t check_erase = len;
while(check_erase--){
if(*((uint8_t *)wptr+check_erase)!=0xFF){
return HAL_BUSY;
}
}
while (remaining) {
uint32_t toread = (remaining > chunk) ? chunk : remaining;
memcpy(buf, (uint8_t *)rptr, toread);
status = flash_write_buffer(wptr, buf, toread);
if (status != HAL_OK) return status;
rptr += toread; wptr += toread; remaining -= toread;
}
return status;
}
Solved! Go to Solution.
2025-10-27 6:25 AM - edited 2025-10-27 6:27 AM
If HAL_FLASHEx_Erase returns HAL_OK, it succeeded. If the pages you expect to be erased are not, the parameters you passed are probably not as expected.
What parameters do you pass to flash_erase_region?
What parameters get passed to HAL_FLASHEx_Erase?
What addresses do you expect to be 0xFF that aren't?
Note that banks are not contiguous. Not sure if it's relevant here.
2025-10-27 6:25 AM - edited 2025-10-27 6:27 AM
If HAL_FLASHEx_Erase returns HAL_OK, it succeeded. If the pages you expect to be erased are not, the parameters you passed are probably not as expected.
What parameters do you pass to flash_erase_region?
What parameters get passed to HAL_FLASHEx_Erase?
What addresses do you expect to be 0xFF that aren't?
Note that banks are not contiguous. Not sure if it's relevant here.
2025-10-28 2:20 AM
Thank you for your interest, TDK. Regarding your questions I checked the RM0440 once more but this time it was hopefully Revision 9 from the ST's website.
In this document it is explicitly shown in the footnote of TABLE 7 that in dual bank mode pages of each bank starts from 0, the starting address of the bank 2, independent of the flash size, 0x08040000.
If one uses continuous address and incremental page number, somehow, MCU writes those addresses and successfully starts an application from that offset which is also only possible because flash initially fully erased hence those addresses are writable.
However, if anyone wants to erase and write a valid program, addresses and pages are given in RM0440 Table 7 (rev >= 9). Make sure to check the latest reference manual or datasheet from ST's website. Don't do what I do, do not check a RM from a different website after a quick google search that contains revision 5 without a footnote, the provided addresses are confusable to 512KB version.