cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H573 in-application programming using the dual bank approach

saeedraf
Associate

Hi, I'm following the steps on the reference manual to perform in-application programming by first writing the new firmware to bank 2 and then swapping the banks after doing some checks.

Screenshot 2025-03-21 115828.png

 

After completing the steps and performing a software reset, the new firmware was updated. However, after closing the st-link debugger and reopening it, the debugging session immediately closes. I'm trying to figure out why. 


I have written the new firmware starting at address 0x0810 0000 according to the following:
Screenshot 2025-03-21 114025.png

 

I was wondering whether I need to offset by a couple sectors from the 0x810 0000 start address before flashing new firmware. Please let me know I'm mistaken, thanks.

8 REPLIES 8

Well if you have common code at 0x0800000 and 0x08100000 you can at least fork() between the two regions with the bank switch. Perhaps a couple of KB for a loader/recovery method.

You need to link the image for the address it's going to physically execute, not necessarily where you have it parked before it's swapped around.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
saeedraf
Associate

Thanks for your helpful response! And, I'm assuming that I have to update the vector table offset to point to the new firmware location at 0x0810000 (i.e., starting address of bank 2), right? Thanks again.

To execute at 0x08100000 the Vector Table would need to reflect that in it's content, or you can move a fixed-up copy in RAM and point SCB->VTOR at that. 512-byte aligned.

But if you use the SWAP_BANK method to map the active image at 0x08000000, the vectors and table would need to built for that address

 

ie you build/link the content for 0x08000000, your update/park the FLASH at 0x08100000, at next boot you determine the most up to date firmware, at map it via SWAP_BANK

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
saeedraf
Associate

I'm still having issues with this; there is a flaw in my implementation that I'm failing to identify. 

My current implementation is doing the following (in the order provided):

1. Write new firmware to the start address of Bank 2 (0x0810 0000). I'm reading back the contents of the memory locations that I wrote to and I have verified that they are correct. 

2. Then, I followed the instructions, provided on p265 of RM0481 Rev 2, to change the SWAP_BANK bit to apply a new firmware update. 

3. Finally, I update the vector table offset SCB->VTOR = 0x0810 0000 before locking option control registers access and flash control registers access. However, the program terminates when the SCB->VTOR = 0x0810 0000 line executes, and the lines responsible for locking the flash are not executed.

After the final step, the new firmware was successfully uploaded based on the print statements in my serial console. However, debugger mode terminates immediately. When I try to debug again, I get the following in the debug console and I can't program/debug the micro anymore:

Remote communication error. Target disconnected.: Arg list too long.
GDB session ended. exit-code: 0
GDB never responded to an interrupt request. Trying to end session anyways

Based on the debug console, there seems to be an issue with servicing a specific interrupt. I thought that updating the vector table offset would solve the issue, but it did not. Any further guidance would be appreciated. 

The Vector Table enumerates literally dozens of ABSOLUTE ADDRESSES of functions deeper into the image.

Changing SCB->VTOR will point at a new table, and ALL active interrupts will then pivot through addresses in the NEW table, whether you're ready to deal with them or not.

a) Look at what Interrupts you currently have active.

b) Dump the content of the Vector Table

SWAP_BANK allows you to play slight-of-hand, so you can have TWO images designed to run at 0x08000000 and chose which one is active.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
saeedraf
Associate

I see. I thought that changing the vector table offset via SCB->VTOR will take effect after a system reset, but that doesn't seem to be the case--updating SCB->VTOR restarted the micro instantly. I wonder whether SCB->VTOR was overwritten again by the SystemInit() function to 0x08000000. What do you think? I think I'm mixing up the order of instructions. Thanks again.

saeedraf
Associate
ERROR_CODE_LIST_t EraseFlash(uint32_t length) {
    
    // unlock flash for writing
    HAL_FLASH_Unlock();  

    FLASH_EraseInitTypeDef eraseInit;
    uint32_t sectorError = 0;

    // Calculate the number of sectors to erase based on length
    uint32_t numSectors = (length + SECTOR_SIZE - 1) / SECTOR_SIZE; // round up

    if (length == 0 || (numSectors > MAX_SECTORS)) {
        return ERROR_INVALID_PARAM;
    }

    // Configure erase parameters
    eraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;
    eraseInit.Banks = FLASH_BANK_2; // Erase from inactive bank
    eraseInit.Sector = 0;           // Start sector
    eraseInit.NbSectors = numSectors;

    // Perform erase
    if (HAL_FLASHEx_Erase(&eraseInit, &sectorError) != HAL_OK) {
        HAL_FLASH_Lock();
        return ERROR_REQUEST_FAILED;
    }

    if (sectorError != 0xFFFFFFFF) {    // this means all sectors were correctly erased
        HAL_FLASH_Lock();
        return ERROR_REQUEST_FAILED;        
    }

    HAL_FLASH_Lock(); // Lock flash after operation
    return ERROR_SUCCESS;
}

ERROR_CODE_LIST_t WriteDataToFlash(uint32_t address, const uint8_t *data, uint32_t length) {

    // Check if the address is within valid flash range
    if (address < FLASH_BASE_ADDR || address >= (FLASH_BASE_ADDR + FLASH_BANK_SIZE)) {
        return ERROR_INVALID_PARAM;
    }

    HAL_FLASH_Unlock();  // Unlock flash for writing

    // Write firmware data to Flash
    for (uint32_t i = 0; i < length; i += 16) {
        if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, address + i, (uint32_t)(data + i)) != HAL_OK) {
            HAL_FLASH_Lock();
            return ERROR_REQUEST_FAILED;
        }
    }

    HAL_FLASH_Lock(); // Lock flash after operation
    return ERROR_SUCCESS;
}


ERROR_CODE_LIST_t SwapFlashMemory() {
    // check to see if banks are swapped and update
    uint32_t swap_state = READ_BIT(FLASH->OPTCR, FLASH_OPTSR_SWAP_BANK);
    swap_state = swap_state ? 0 : FLASH_OPTSR_SWAP_BANK;

    // unlock flash control registers access
    if (HAL_FLASH_Unlock() != HAL_OK){
        return ERROR_REQUEST_FAILED; 
    }
    
    //  unlock option control registers access
    if (HAL_FLASH_OB_Unlock() != HAL_OK) {
        return ERROR_REQUEST_FAILED; 
    }

    // wait for previous operations to be completed
    if (FLASH_WaitForLastOperation(FLASH_TIMEOUT_VALUE) != HAL_OK) {
        return ERROR_REQUEST_FAILED; 
    }

    // clear all error flags
    WRITE_REG(FLASH->NSCCR, FLASH_FLAG_ALL_ERRORS);

    // write updated swap_bank state
    MODIFY_REG(FLASH->OPTSR_PRG, FLASH_OPTSR_SWAP_BANK, swap_state);

    // start the option byte change sequence
    SET_BIT(FLASH->OPTCR, FLASH_OPTCR_OPTSTART);

    // wait until swap_bank bit has been changed
    while (READ_BIT(FLASH->OPTSR_CUR, FLASH_OPTSR_SWAP_BANK) != swap_state) (void) 0;

    // update vector table offset to point to new firmware location
    SCB->VTOR = FLASH_BASE_ADDR;

    //  lock option control registers access
    if (HAL_FLASH_OB_Lock() != HAL_OK) {
        return ERROR_REQUEST_FAILED; 
    }
    
    // lock flash control registers access
    if (HAL_FLASH_Lock() != HAL_OK){
        return ERROR_REQUEST_FAILED; 
    }

    return ERROR_SUCCESS;
}

Hey Tesla DeLorean! After erasing the the appropriate sectors from bank 2 of the flash, I'm writing the new firmware starting from FLASH_BASE_ADDR = 0x08100000 (starting address of bank 2). Finally I'm swapping both banks of the flash, updating the vector table offset to 0x08100000, and doing a system reset (not shown in the functions above). I still get the error when trying to debug the micro:

Program stopped, probably due to a reset and/or halt issued by debugger
2
STM32 Successfully completed reset operation (System reset)
Trying to halt core...
Note: automatically using hardware breakpoints for read-only addresses.
warning: Exception condition detected on fd 664
Remote communication error. Target disconnected.: Arg list too long.
GDB session ended. exit-code: 0
GDB never responded to an interrupt request. Trying to end session anyways

I think I'm really close to a viable implementation, but I can't seem to identify the final issue. I would appreciate your help if possible. It seems that I'm missing something, but I can't figure out what. Thanks.

Unless you're running identical code in FLASH, you can't BANK_SWAP from code running in FLASH. Even calling a routine in RAM probably won't help as when it returns it's going to go to the same address in the other bank. If the code is different, expect it to crash.

I mention fork() as its the UNIX way of taking a common code image and starting a second copy of one's self.

As I originally suggested, consider a common / identical loader, perhaps 2 or 4KB, that lives in both banks, and decides which to map, and then branches in through the Reset Handler for the vector table parked at 0x08001000 (base + 4KB)

Yes, SCB->VTOR typically gets set up in SystemInit(), it could use an address from the linker for the vectors rather than the #define mess ST uses.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..