Skip to content

Commit

Permalink
tools/bootloader: add force-erase option
Browse files Browse the repository at this point in the history
If the STM32H7 fails to program or erase a full chunk of 256 bytes, the
ECC check will trigger a busfault when trying to read from it.

To speed up erasing and optimize wear, we read before erasing to check
if it actually needs erasing. That's when a busfault happens and the
erase time outs.

The workaround is to add an option to do a full erase without check.

Credit goes to:
ArduPilot/ardupilot#22090

And the protocol option added to the bootloader is the same as for
ArduPilot, so compatible.

Signed-off-by: Julian Oes <[email protected]>
  • Loading branch information
julianoes committed Mar 4, 2024
1 parent 7c9ce8e commit 7b09244
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 18 deletions.
25 changes: 18 additions & 7 deletions Tools/px_uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,14 @@ class uploader(object):
GET_CHIP = b'\x2c' # rev5+ , get chip version
SET_BOOT_DELAY = b'\x2d' # rev5+ , set boot delay
GET_CHIP_DES = b'\x2e' # rev5+ , get chip description in ASCII
CHIP_FULL_ERASE = b'\x40' # full erase of flash, rev6+
MAX_DES_LENGTH = 20

REBOOT = b'\x30'

INFO_BL_REV = b'\x01' # bootloader protocol revision
BL_REV_MIN = 2 # minimum supported bootloader protocol
BL_REV_MAX = 5 # maximum supported bootloader protocol
BL_REV_MAX = 6 # maximum supported bootloader protocol
INFO_BOARD_ID = b'\x02' # board type
INFO_BOARD_REV = b'\x03' # board revision
INFO_FLASH_SIZE = b'\x04' # max firmware size in bytes
Expand Down Expand Up @@ -408,11 +409,20 @@ def __drawProgressBar(self, label, progress, maxVal):
sys.stdout.flush()

# send the CHIP_ERASE command and wait for the bootloader to become ready
def __erase(self, label):
def __erase(self, label, force_erase):
print("Windowed mode: %s" % self.ackWindowedMode)
print("\n", end='')
self.__send(uploader.CHIP_ERASE +
uploader.EOC)

if force_erase:
if self.bl_rev < 6:
raise RuntimeError("bootloader revision does not support force erase")

print("Force erasing full chip\n")
self.__send(uploader.CHIP_FULL_ERASE +
uploader.EOC)
else:
self.__send(uploader.CHIP_ERASE +
uploader.EOC)

# erase is very slow, give it 30s
deadline = time.time() + 30.0
Expand Down Expand Up @@ -570,7 +580,7 @@ def identify(self):
self.fw_maxsize = self.__getInfo(uploader.INFO_FLASH_SIZE)

# upload the firmware
def upload(self, fw, force=False, boot_delay=None):
def upload(self, fw, force=False, boot_delay=None, force_erase=False):
# Make sure we are doing the right thing
start = time.time()
if self.board_type != fw.property('board_id'):
Expand Down Expand Up @@ -654,7 +664,7 @@ def upload(self, fw, force=False, boot_delay=None):
"If you know you that the board does not have the silicon errata, use\n"
"this script with --force, or update the bootloader. If you are invoking\n"
"upload using make, you can use force-upload target to force the upload.\n")
self.__erase("Erase ")
self.__erase("Erase ", force_erase)
self.__program("Program", fw)

if self.bl_rev == 2:
Expand Down Expand Up @@ -750,6 +760,7 @@ def main():
parser.add_argument('--baud-bootloader', action="store", type=int, default=115200, help="Baud rate of the serial port (default is 115200) when communicating with bootloader, only required for true serial ports.")
parser.add_argument('--baud-flightstack', action="store", default="57600", help="Comma-separated list of baud rate of the serial port (default is 57600) when communicating with flight stack (Mavlink or NSH), only required for true serial ports.")
parser.add_argument('--force', action='store_true', default=False, help='Override board type check, or silicon errata checks and continue loading')
parser.add_argument('--force-erase', action="store_true", help="Do not perform the blank check, always erase every sector of the application space")
parser.add_argument('--boot-delay', type=int, default=None, help='minimum boot delay to store in flash')
parser.add_argument('--use-protocol-splitter-format', action='store_true', help='use protocol splitter format for reboot')
parser.add_argument('firmware', action="store", help="Firmware file to be uploaded")
Expand Down Expand Up @@ -877,7 +888,7 @@ def main():

try:
# ok, we have a bootloader, try flashing it
up.upload(fw, force=args.force, boot_delay=args.boot_delay)
up.upload(fw, force=args.force, boot_delay=args.boot_delay, force_erase=args.force_erase)

# if we made this far without raising exceptions, the upload was successful
successful = True
Expand Down
14 changes: 11 additions & 3 deletions platforms/nuttx/src/bootloader/common/bl.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
// RESET finalise flash programming, reset chip and starts application
//

#define BL_PROTOCOL_VERSION 5 // The revision of the bootloader protocol
#define BL_PROTOCOL_VERSION 6 // The revision of the bootloader protocol
//* Next revision needs to update

// protocol bytes
Expand Down Expand Up @@ -114,6 +114,7 @@
#define PROTO_RESERVED_0X37 0x37 // Reserved
#define PROTO_RESERVED_0X38 0x38 // Reserved
#define PROTO_RESERVED_0X39 0x39 // Reserved
#define PROTO_CHIP_FULL_ERASE 0x40 // Full erase, without any flash wear optimization

#define PROTO_PROG_MULTI_MAX 64 // maximum PROG_MULTI size
#define PROTO_READ_MULTI_MAX 255 // size of the size field
Expand Down Expand Up @@ -645,6 +646,8 @@ bootloader(unsigned timeout)

led_on(LED_ACTIVITY);

bool full_erase = false;

// handle the command byte
switch (c) {

Expand Down Expand Up @@ -724,6 +727,10 @@ bootloader(unsigned timeout)
// success reply: INSYNC/OK
// erase failure: INSYNC/FAILURE
//
case PROTO_CHIP_FULL_ERASE:
full_erase = true;

// Fallthrough
case PROTO_CHIP_ERASE:

/* expect EOC */
Expand Down Expand Up @@ -751,17 +758,18 @@ bootloader(unsigned timeout)
arch_flash_unlock();

for (int i = 0; flash_func_sector_size(i) != 0; i++) {
flash_func_erase_sector(i);
flash_func_erase_sector(i, full_erase);
}

// disable the LED while verifying the erase
led_set(LED_OFF);

// verify the erase
for (address = 0; address < board_info.fw_size; address += 4)
for (address = 0; address < board_info.fw_size; address += 4) {
if (flash_func_read_word(address) != 0xffffffff) {
goto cmd_fail;
}
}

address = 0;
SET_BL_STATE(STATE_PROTO_CHIP_ERASE);
Expand Down
4 changes: 3 additions & 1 deletion platforms/nuttx/src/bootloader/common/bl.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@

#pragma once

#include <stdbool.h>

/*****************************************************************************
* Generic bootloader functions.
*/
Expand Down Expand Up @@ -105,7 +107,7 @@ extern void board_deinit(void);
extern uint32_t board_get_devices(void);
extern void clock_deinit(void);
extern uint32_t flash_func_sector_size(unsigned sector);
extern void flash_func_erase_sector(unsigned sector);
extern void flash_func_erase_sector(unsigned sector, bool force);
extern void flash_func_write_word(uintptr_t address, uint32_t word);
extern uint32_t flash_func_read_word(uintptr_t address);
extern uint32_t flash_func_read_otp(uintptr_t address);
Expand Down
9 changes: 2 additions & 7 deletions platforms/nuttx/src/bootloader/stm/stm32_common/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,18 +458,13 @@ flash_func_sector_size(unsigned sector)
}

void
flash_func_erase_sector(unsigned sector)
flash_func_erase_sector(unsigned sector, bool force)
{
if (sector > BOARD_FLASH_SECTORS || (int)sector < BOARD_FIRST_FLASH_SECTOR_TO_ERASE) {
return;
}

/* blank-check the sector */

bool blank = up_progmem_ispageerased(sector) == 0;

/* erase the sector if it failed the blank check */
if (!blank) {
if (force || (up_progmem_ispageerased(sector) != 0)) {
up_progmem_eraseblock(sector);
}
}
Expand Down

0 comments on commit 7b09244

Please sign in to comment.