Skip to content

Commit

Permalink
stm32/powerctrl: Write bootloader-state as 64-bit word to work on H7.
Browse files Browse the repository at this point in the history
H7 MCUs have ECC and writes do not go through to SRAM until 64-bits have
been written (on another location is written).  So use 64-bit writes for
the bootloader-state variable so it is committed before the system reset.

As part of this change, the lower byte of the bootloader address in
BL_STATE must now be the magic number 0x5a5 for the state to be valid
(previously this was 0x000 which is not as robust).

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Jan 31, 2022
1 parent 4a4f269 commit c99ed8d
Showing 1 changed file with 19 additions and 11 deletions.
30 changes: 19 additions & 11 deletions ports/stm32/powerctrl.c
Expand Up @@ -75,9 +75,19 @@
#endif

#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
// Location in RAM of bootloader state (just after the top of the stack)
extern uint32_t _estack[];
#define BL_STATE ((uint32_t *)&_estack)
// Location in RAM of bootloader state (just after the top of the stack).
// STM32H7 has ECC and writes to RAM must be 64-bit so they are fully committed
// to actual SRAM before a system reset occurs.
#define BL_STATE_PTR ((uint64_t *)&_estack)
#define BL_STATE_KEY (0x5a5)
#define BL_STATE_KEY_MASK (0xfff)
#define BL_STATE_KEY_SHIFT (32)
#define BL_STATE_INVALID (0)
#define BL_STATE_VALID(reg, addr) ((uint64_t)(reg) | ((uint64_t)((addr) | BL_STATE_KEY)) << BL_STATE_KEY_SHIFT)
#define BL_STATE_GET_REG(s) ((s) & 0xffffffff)
#define BL_STATE_GET_KEY(s) (((s) >> BL_STATE_KEY_SHIFT) & BL_STATE_KEY_MASK)
#define BL_STATE_GET_ADDR(s) (((s) >> BL_STATE_KEY_SHIFT) & ~BL_STATE_KEY_MASK)
extern uint64_t _estack[];
#endif

static inline void powerctrl_disable_hsi_if_unused(void) {
Expand All @@ -89,7 +99,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) {

NORETURN void powerctrl_mcu_reset(void) {
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
BL_STATE[1] = 1; // invalidate bootloader address
*BL_STATE_PTR = BL_STATE_INVALID;
#if __DCACHE_PRESENT == 1
SCB_CleanDCache();
#endif
Expand All @@ -112,8 +122,7 @@ NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {

// Enter the bootloader via a reset, so everything is reset (including WDT).
// Upon reset powerctrl_check_enter_bootloader() will jump to the bootloader.
BL_STATE[0] = r0;
BL_STATE[1] = bl_addr;
*BL_STATE_PTR = BL_STATE_VALID(r0, bl_addr);
#if __DCACHE_PRESENT == 1
SCB_CleanDCache();
#endif
Expand All @@ -129,16 +138,15 @@ NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) {

void powerctrl_check_enter_bootloader(void) {
#if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET
uint32_t bl_addr = BL_STATE[1];
BL_STATE[1] = 1; // invalidate bootloader address
if ((bl_addr & 0xfff) == 0 && (RCC->RCC_SR & RCC_SR_SFTRSTF)) {
uint64_t bl_state = *BL_STATE_PTR;
*BL_STATE_PTR = BL_STATE_INVALID;
if (BL_STATE_GET_KEY(bl_state) == BL_STATE_KEY && (RCC->RCC_SR & RCC_SR_SFTRSTF)) {
// Reset by NVIC_SystemReset with bootloader data set -> branch to bootloader
RCC->RCC_SR = RCC_SR_RMVF;
#if defined(STM32F0) || defined(STM32F4) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB)
__HAL_SYSCFG_REMAPMEMORY_SYSTEMFLASH();
#endif
uint32_t r0 = BL_STATE[0];
branch_to_bootloader(r0, bl_addr);
branch_to_bootloader(BL_STATE_GET_REG(bl_state), BL_STATE_GET_ADDR(bl_state));
}
#endif
}
Expand Down

0 comments on commit c99ed8d

Please sign in to comment.