Hello Forum,
I have a question regarding OTA and toggling between flash banks on a STM32L496. I have read many forum posts on here relating to this and I have reached a point where I need assistance as debugging iteratively is taking a lot of time.
I am developing a firmware update process that has the update code as part of the application, not as part of the bootloader. Serial messages are received containing the binary data (derived from a *.bin image) and are written to the flash bank which is currently not in use.
The part has a flash memory layout as follows:
Flash Area | Memory start address | Size | Name |
BANK 1 | 0x0800 0000 | 2k | page 0 |
| 0x0800 0800 | 2k | page 1 |
| 0x0800 1000 | 2k | page 2 |
| ... | | |
| 0x0807 F800 | 2k | page 255 |
BANK 2 | 0x0808 0000 | 2k | page 256 |
| 0x0808 0800 | 2k | page 257 |
| 0x0808 1000 | 2k | page 258 |
| ... | | |
| 0x080F F800 | 2k | page 511 |
If I have freshly reprogrammed the part using the ST-Linkv2 programmer, the code checks which bank the image is running from as follows:
uint32_t fb_mode = LL_SYSCFG_GetFlashBankMode();
if (fb_mode == 0)
{
/* Running from BANK 1 */
}
else
{
/* Running from BANK 2 */
}
I then erase all 255 pages of the flash bank which I am not running in. Once this completes successfully, I write sequentially to the bank I want to run from after the OTA is completed. Without labouring the point, if I was running from Bank 1, then I would perform the following pseudo operations:
HAL_StatusTypeDef status = HAL_OK;
uint64_t *ptr = buffer; /* Contiguous array of 256 bytes of binary image. */
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08080000, *ptr++);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08080800, *ptr++);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08081000, *ptr++);
This is performed in a loop and the pointer is updated to match the wrte-size. The point I am trying to make is the addresses of the writes are dendent on which block the image is running from.
If I was running from flash bank 2, I would expect the following:
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08000000, *ptr++);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08000800, *ptr++);
status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, 0x08001000, *ptr++);
Here we are writing directly to Bank 1. After this is completed, I would update the flash Options Bytes as follows:
/* Get the current configuration */
HAL_FLASHEx_OBGetConfig(&obConfig);
uint32_t fb_mode = LL_SYSCFG_GetFlashBankMode();
if (fb_mode == 0) {
/* Explicitly boot from Bank 2 following reset
* as we are running from Bank 1.
*/
obConfig.USERConfig = OB_BFB2_ENABLE;
}
else {
/* Explicitly boot from Bank 1 following reset
* as we are running from Bank 2.
*/
obConfig.USERConfig = OB_BFB2_DISABLE;
}
/* Initiating the modifications */
result = HAL_FLASH_Unlock();
/* program if unlock is successful */
if (result == HAL_OK) {
result = HAL_FLASH_OB_Unlock();
}
/* Program if unlock is successful. */
if ((READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) == RESET)) {
result = HAL_FLASHEx_OBProgram(&obConfig);
}
/* No return as the last call triggers a reset. */
The problem I have is that this sequence works perfectly until the second OTA and at that point I cannot write to the Bank 1 first address. I have read on these forums that OTA updates should always be written to Bank 2 and the swapping is provided automatically by the OB_OFB2_ENABLE/DISABLE mechanism. I just need to confirm that this is the case or do I inspect the fb_mode value and write to the flash bank which I am not running from.
Thank you for any help that you can offer.