diff --git a/ports/stm32/mboot/fwupdate.py b/ports/stm32/mboot/fwupdate.py index d7c8f46db4..8a366dae29 100644 --- a/ports/stm32/mboot/fwupdate.py +++ b/ports/stm32/mboot/fwupdate.py @@ -1,5 +1,5 @@ # Update Mboot or MicroPython from a .dfu.gz file on the board's filesystem -# MIT license; Copyright (c) 2019-2020 Damien P. George +# MIT license; Copyright (c) 2019-2022 Damien P. George from micropython import const import struct, time @@ -16,9 +16,6 @@ _ELEM_TYPE_MOUNT = const(2) _ELEM_TYPE_FSLOAD = const(3) _ELEM_TYPE_STATUS = const(4) -FLASH_KEY1 = 0x45670123 -FLASH_KEY2 = 0xCDEF89AB - def check_mem_contains(addr, buf): mem8 = stm.mem8 @@ -29,15 +26,6 @@ def check_mem_contains(addr, buf): return True -def check_mem_erased(addr, size): - mem16 = stm.mem16 - r = range(0, size, 2) - for off in r: - if mem16[addr + off] != 0xFFFF: - return False - return True - - def dfu_read(filename): f = open(filename, "rb") @@ -85,39 +73,99 @@ def dfu_read(filename): return elems -def flash_wait_not_busy(): - while stm.mem32[stm.FLASH + stm.FLASH_SR] & 1 << 16: - machine.idle() +class Flash: + _FLASH_KEY1 = 0x45670123 + _FLASH_KEY2 = 0xCDEF89AB + def __init__(self): + import os, uctypes -def flash_unlock(): - stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY1 - stm.mem32[stm.FLASH + stm.FLASH_KEYR] = FLASH_KEY2 + self.addressof = uctypes.addressof + # Detect MCU. + machine = os.uname().machine + if "STM32F4" in machine or "STM32F7" in machine: + dev_id = stm.mem32[0xE004_2000] & 0xFFF + elif "STM32H7" in machine: + dev_id = stm.mem32[0x5C00_1000] & 0xFFF + else: + dev_id = 0 -def flash_lock(): - stm.mem32[stm.FLASH + stm.FLASH_CR] = 1 << 31 # LOCK + # Configure flash parameters based on MCU. + if dev_id in (0x413, 0x419, 0x431, 0x451, 0x452): + # 0x413: STM32F405/407, STM32F415/417 + # 0x419: STM32F42x/43x + # 0x431: STM32F411 + # 0x451: STM32F76x/77x + # 0x452: STM32F72x/73x + self._keyr = stm.FLASH + stm.FLASH_KEYR + self._sr = stm.FLASH + stm.FLASH_SR + self._sr_busy = 1 << 16 + self._cr = stm.FLASH + stm.FLASH_CR + self._cr_lock = 1 << 31 + self._cr_init_erase = lambda s: 2 << 8 | s << 3 | 1 << 1 # PSIZE=32-bits, SNB, SER + self._cr_start_erase = 1 << 16 # STRT + self._cr_init_write = 2 << 8 | 1 << 0 # PSIZE=32-bits, PG + self._cr_flush = None + self._write_multiple = 4 + if dev_id == 0x451 and stm.mem32[0x1FFF_0008] & 1 << 13: # check nDBANK + # STM32F76x/77x in single-bank mode + self.sector0_size = 32 * 1024 + else: + self.sector0_size = 16 * 1024 + elif dev_id == 0x450: + # 0x450: STM32H742, STM32H743/753, STM32H750 + self._keyr = stm.FLASH + stm.FLASH_KEYR1 + self._sr = stm.FLASH + stm.FLASH_SR1 + self._sr_busy = 1 << 2 # QW1 + self._cr = stm.FLASH + stm.FLASH_CR1 + self._cr_lock = 1 << 0 # LOCK1 + self._cr_init_erase = lambda s: s << 8 | 3 << 4 | 1 << 2 # SNB1, PSIZE1=64-bits, SER1 + self._cr_start_erase = 1 << 7 # START1 + self._cr_init_write = 3 << 4 | 1 << 1 # PSIZE1=64-bits, PG1=1 + self._cr_flush = 1 << 6 # FW1=1 + self._write_multiple = 16 + self.sector0_size = 128 * 1024 + else: + raise Exception(f"unknown MCU {machine} DEV_ID=0x{dev_id:x}") + def wait_not_busy(self): + while stm.mem32[self._sr] & self._sr_busy: + machine.idle() -def flash_erase_sector(sector): - assert 0 <= sector <= 7 # for F722 - flash_wait_not_busy() - cr = 2 << 8 | sector << 3 | 1 << 1 # PSIZE = 32 bits # SNB # SER - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr | 1 << 16 # STRT - flash_wait_not_busy() - stm.mem32[stm.FLASH + stm.FLASH_CR] = 0 + def unlock(self): + if stm.mem32[self._cr] & self._cr_lock: + stm.mem32[self._keyr] = self._FLASH_KEY1 + stm.mem32[self._keyr] = self._FLASH_KEY2 + def lock(self): + stm.mem32[self._cr] = self._cr_lock -def flash_write(addr, buf): - assert len(buf) % 4 == 0 - flash_wait_not_busy() - cr = 2 << 8 | 1 << 0 # PSIZE = 32 bits # PG - stm.mem32[stm.FLASH + stm.FLASH_CR] = cr - for off in range(0, len(buf), 4): - stm.mem32[addr + off] = struct.unpack_from("I", buf, off)[0] - flash_wait_not_busy() - stm.mem32[stm.FLASH + stm.FLASH_CR] = 0 + def erase_sector(self, sector): + assert 0 <= sector <= 7 + self.wait_not_busy() + stm.mem32[self._cr] = self._cr_init_erase(sector) + stm.mem32[self._cr] |= self._cr_start_erase + self.wait_not_busy() + stm.mem32[self._cr] = 0 + + # This method is optimised for speed, to reduce the time data is being written. + def write(self, addr, buf): + assert len(buf) % 4 == 0 + mem32 = stm.mem32 + buf_addr = self.addressof(buf) + r = range(0, len(buf), 4) + self.wait_not_busy() + mem32[self._cr] = self._cr_init_write + for off in r: + mem32[addr + off] = mem32[buf_addr + off] + if off % self._write_multiple == 0: + while mem32[self._sr] & self._sr_busy: + pass + if self._cr_flush is not None: + mem32[self._cr] |= self._cr_flush + self.wait_not_busy() + mem32[self._cr] = 0 def update_mboot(filename): @@ -144,13 +192,14 @@ def update_mboot(filename): print("Programming Mboot, do not turn off!") time.sleep_ms(50) + flash = Flash() irq = machine.disable_irq() - flash_unlock() - flash_erase_sector(0) - if len(mboot_fw) > 16 * 1024 and not check_mem_erased(mboot_addr + 16 * 1024, 16 * 1024): - flash_erase_sector(1) - flash_write(mboot_addr, mboot_fw) - flash_lock() + flash.unlock() + flash.erase_sector(0) + if len(mboot_fw) > flash.sector0_size: + flash.erase_sector(1) + flash.write(mboot_addr, mboot_fw) + flash.lock() machine.enable_irq(irq) print("New Mboot programmed.")