cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G473RBT6 MCU DualBank Mode Bank-2 Page Erase Silently Fails

G4_B2_Page_Erase
Associate

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:

  • There are 3 virtual sections: 24 KB OTA Bootloader starting from 0x0800 0000. 52 KB Storage space starting from 0x0800 6000 and 52 KB Application space starting from 0x0801 3000. In total 128 KB as the STM32G473RB MCU has.
  • The custom bootloader reads incoming data, erases storage space and stores this new data in storage space, check CRC32, etc. Then the bootloader erases previous application in application space, and copies storage space to application space and starts from new application address.

The problem is that the bootloader only succeeds if the following condition met (basically full flash erase):

  1. I have a fully erased chip and only bootloader loaded. If I want to load a simple LED blink application which FLASH.lb file configured as following:
    MEMORY
    {
    RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
    FLASH (rx) : ORIGIN = 0x8013000, LENGTH = 52K
    }
    with this chip (only bootloader exist, all other addresses are 0xFF, checked with STM32CubeProgrammer) the custom bootloader copies to application space and successfully start from that new address.

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;
}

 

0 REPLIES 0