mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-13 13:09:49 +04:00
Scripts: OB recovery (#2964)
* Scripts: OB recovery * Scripts: slightly different ob * Scripts: remove excessive return * Scripts: simplifying work with registers * Make PVS happy Co-authored-by: SG <who.just.the.doctor@gmail.com>
This commit is contained in:
@@ -108,23 +108,27 @@ class STM32WB55:
|
||||
15: None, # Core 2 Options
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, openocd: OpenOCD):
|
||||
self.openocd = openocd
|
||||
self.logger = logging.getLogger("STM32WB55")
|
||||
|
||||
self.FLASH_CR.set_openocd(self.openocd)
|
||||
self.FLASH_SR.set_openocd(self.openocd)
|
||||
|
||||
class RunMode(Enum):
|
||||
Init = "init"
|
||||
Run = "run"
|
||||
Halt = "halt"
|
||||
|
||||
def reset(self, oocd: OpenOCD, mode: RunMode):
|
||||
def reset(self, mode: RunMode):
|
||||
self.logger.debug("Resetting device")
|
||||
oocd.send_tcl(f"reset {mode.value}")
|
||||
self.openocd.send_tcl(f"reset {mode.value}")
|
||||
|
||||
def clear_flash_errors(self, oocd: OpenOCD):
|
||||
def clear_flash_errors(self):
|
||||
# Errata 2.2.9: Flash OPTVERR flag is always set after system reset
|
||||
# And also clear all other flash error flags
|
||||
self.logger.debug("Resetting flash errors")
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
self.FLASH_SR.OP_ERR = 1
|
||||
self.FLASH_SR.PROG_ERR = 1
|
||||
self.FLASH_SR.WRP_ERR = 1
|
||||
@@ -135,51 +139,51 @@ class STM32WB55:
|
||||
self.FLASH_SR.FAST_ERR = 1
|
||||
self.FLASH_SR.RD_ERR = 1
|
||||
self.FLASH_SR.OPTV_ERR = 1
|
||||
self.FLASH_SR.store(oocd)
|
||||
self.FLASH_SR.store()
|
||||
|
||||
def flash_unlock(self, oocd: OpenOCD):
|
||||
def flash_unlock(self):
|
||||
# Check if flash is already unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock flash
|
||||
self.logger.debug("Unlocking Flash")
|
||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
|
||||
oocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
||||
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY1)
|
||||
self.openocd.write_32(self.FLASH_KEYR, self.FLASH_UNLOCK_KEY2)
|
||||
|
||||
# Check if flash is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 0:
|
||||
self.logger.debug("Flash unlocked")
|
||||
else:
|
||||
self.logger.error("Flash unlock failed")
|
||||
raise Exception("Flash unlock failed")
|
||||
|
||||
def option_bytes_unlock(self, oocd: OpenOCD):
|
||||
def option_bytes_unlock(self):
|
||||
# Check if options is already unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options is already unlocked")
|
||||
return
|
||||
|
||||
# Unlock options
|
||||
self.logger.debug("Unlocking Options")
|
||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
|
||||
oocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
||||
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY1)
|
||||
self.openocd.write_32(self.FLASH_OPTKEYR, self.FLASH_UNLOCK_OPTKEY2)
|
||||
|
||||
# Check if options is unlocked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 0:
|
||||
self.logger.debug("Options unlocked")
|
||||
else:
|
||||
self.logger.error("Options unlock failed")
|
||||
raise Exception("Options unlock failed")
|
||||
|
||||
def option_bytes_lock(self, oocd: OpenOCD):
|
||||
def option_bytes_lock(self):
|
||||
# Check if options is already locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options is already locked")
|
||||
return
|
||||
@@ -187,19 +191,19 @@ class STM32WB55:
|
||||
# Lock options
|
||||
self.logger.debug("Locking Options")
|
||||
self.FLASH_CR.OPT_LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check if options is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.OPT_LOCK == 1:
|
||||
self.logger.debug("Options locked")
|
||||
else:
|
||||
self.logger.error("Options lock failed")
|
||||
raise Exception("Options lock failed")
|
||||
|
||||
def flash_lock(self, oocd: OpenOCD):
|
||||
def flash_lock(self):
|
||||
# Check if flash is already locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash is already locked")
|
||||
return
|
||||
@@ -207,31 +211,31 @@ class STM32WB55:
|
||||
# Lock flash
|
||||
self.logger.debug("Locking Flash")
|
||||
self.FLASH_CR.LOCK = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check if flash is locked
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
if self.FLASH_CR.LOCK == 1:
|
||||
self.logger.debug("Flash locked")
|
||||
else:
|
||||
self.logger.error("Flash lock failed")
|
||||
raise Exception("Flash lock failed")
|
||||
|
||||
def option_bytes_apply(self, oocd: OpenOCD):
|
||||
def option_bytes_apply(self):
|
||||
self.logger.debug("Applying Option Bytes")
|
||||
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.OPT_STRT = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Wait for Option Bytes to be applied
|
||||
self.flash_wait_for_operation(oocd)
|
||||
self.flash_wait_for_operation()
|
||||
|
||||
def option_bytes_load(self, oocd: OpenOCD):
|
||||
def option_bytes_load(self):
|
||||
self.logger.debug("Loading Option Bytes")
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.OBL_LAUNCH = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
def option_bytes_id_to_address(self, id: int) -> int:
|
||||
# Check if this option byte (dword) is mapped to a register
|
||||
@@ -241,16 +245,16 @@ class STM32WB55:
|
||||
|
||||
return device_reg_addr
|
||||
|
||||
def flash_wait_for_operation(self, oocd: OpenOCD):
|
||||
def flash_wait_for_operation(self):
|
||||
# Wait for flash operation to complete
|
||||
# TODO: timeout
|
||||
while True:
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
if self.FLASH_SR.BSY == 0:
|
||||
break
|
||||
|
||||
def flash_dump_status_register(self, oocd: OpenOCD):
|
||||
self.FLASH_SR.load(oocd)
|
||||
def flash_dump_status_register(self):
|
||||
self.FLASH_SR.load()
|
||||
self.logger.info(f"FLASH_SR: {self.FLASH_SR.get():08x}")
|
||||
if self.FLASH_SR.EOP:
|
||||
self.logger.info(" End of operation")
|
||||
@@ -283,70 +287,87 @@ class STM32WB55:
|
||||
if self.FLASH_SR.PESD:
|
||||
self.logger.info(" Programming / erase operation suspended.")
|
||||
|
||||
def write_flash_64(self, oocd: OpenOCD, address: int, word_1: int, word_2: int):
|
||||
def write_flash_64(self, address: int, word_1: int, word_2: int):
|
||||
self.logger.debug(f"Writing flash at address {address:08x}")
|
||||
|
||||
if address % 8 != 0:
|
||||
self.logger.error("Address must be aligned to 8 bytes")
|
||||
raise Exception("Address must be aligned to 8 bytes")
|
||||
|
||||
if word_1 == oocd.read_32(address) and word_2 == oocd.read_32(address + 4):
|
||||
if word_1 == self.openocd.read_32(address) and word_2 == self.openocd.read_32(
|
||||
address + 4
|
||||
):
|
||||
self.logger.debug("Data is already programmed")
|
||||
return
|
||||
|
||||
self.flash_unlock(oocd)
|
||||
self.flash_unlock()
|
||||
|
||||
# Check that no flash main memory operation is ongoing by checking the BSY bit
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
if self.FLASH_SR.BSY:
|
||||
self.logger.error("Flash is busy")
|
||||
self.flash_dump_status_register(oocd)
|
||||
self.flash_dump_status_register()
|
||||
raise Exception("Flash is busy")
|
||||
|
||||
# Enable end of operation interrupts and error interrupts
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.EOPIE = 1
|
||||
self.FLASH_CR.ERRIE = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Check that flash memory program and erase operations are allowed
|
||||
if self.FLASH_SR.PESD:
|
||||
self.logger.error("Flash operations are not allowed")
|
||||
self.flash_dump_status_register(oocd)
|
||||
self.flash_dump_status_register()
|
||||
raise Exception("Flash operations are not allowed")
|
||||
|
||||
# Check and clear all error programming flags due to a previous programming.
|
||||
self.clear_flash_errors(oocd)
|
||||
self.clear_flash_errors()
|
||||
|
||||
# Set the PG bit in the Flash memory control register (FLASH_CR)
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.PG = 1
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
# Perform the data write operation at the desired memory address, only double word (64 bits) can be programmed.
|
||||
# Write the first word
|
||||
oocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
|
||||
self.openocd.send_tcl(f"mww 0x{address:08x} 0x{word_1:08x}")
|
||||
# Write the second word
|
||||
oocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
|
||||
self.openocd.send_tcl(f"mww 0x{(address + 4):08x} 0x{word_2:08x}")
|
||||
|
||||
# Wait for the BSY bit to be cleared
|
||||
self.flash_wait_for_operation(oocd)
|
||||
self.flash_wait_for_operation()
|
||||
|
||||
# Check that EOP flag is set in the FLASH_SR register
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
if not self.FLASH_SR.EOP:
|
||||
self.logger.error("Flash operation failed")
|
||||
self.flash_dump_status_register(oocd)
|
||||
self.flash_dump_status_register()
|
||||
raise Exception("Flash operation failed")
|
||||
|
||||
# Clear the EOP flag
|
||||
self.FLASH_SR.load(oocd)
|
||||
self.FLASH_SR.load()
|
||||
self.FLASH_SR.EOP = 1
|
||||
self.FLASH_SR.store(oocd)
|
||||
self.FLASH_SR.store()
|
||||
|
||||
# Clear the PG bit in the FLASH_CR register
|
||||
self.FLASH_CR.load(oocd)
|
||||
self.FLASH_CR.load()
|
||||
self.FLASH_CR.PG = 0
|
||||
self.FLASH_CR.store(oocd)
|
||||
self.FLASH_CR.store()
|
||||
|
||||
self.flash_lock(oocd)
|
||||
self.flash_lock()
|
||||
|
||||
def option_bytes_recover(self):
|
||||
self.openocd.send_tcl("mww 0x58004010 0x8000") # set OPTVERR to reset
|
||||
# Replace flash_unlock and option_bytes_unlock with the following lines, if this does not work
|
||||
# self.openocd.send_tcl("mww 0x58004008 0x45670123") # unlock FLASH
|
||||
# self.openocd.send_tcl("mww 0x58004008 0xCDEF89AB")
|
||||
# self.openocd.send_tcl("mww 0x5800400c 0x08192A3B") # unlock OB
|
||||
# self.openocd.send_tcl("mww 0x5800400c 0x4C5D6E7F")
|
||||
self.flash_unlock()
|
||||
self.option_bytes_unlock()
|
||||
self.openocd.send_tcl("mmw 0x58004020 0x3ffff1aa 0xffffffff") # Reset OB
|
||||
self.openocd.send_tcl("mww 0x5800402c 0xff") # Reset WRP1AR
|
||||
self.openocd.send_tcl("mww 0x58004030 0xff") # Reset WRP1BR
|
||||
self.openocd.send_tcl("mmw 0x58004014 0x00020000 0") # OPTSTRT
|
||||
self.openocd.send_tcl("mmw 0x58004014 0x08000000 0") # OBL_LAUNCH
|
||||
|
||||
Reference in New Issue
Block a user