west: runners: Add support for a common --reset argument

Some of the runners in the tree have been adding their own,
class-specific versions of a switch to instruct the runner to reset or
not the device after flashing.

In order to better support multi-image builds that require more than one
flash operation, introduce a new --reset,--no-reset command-line
parameter that is part of the RunnerCaps so taht this functionality can
be accessed in a standardized manner.

Implementations for the new parameter are provided for the runner
classes that were already configurable in this regard.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
Carles Cufi 2023-09-26 13:09:06 +02:00 committed by Martí Bolívar
commit 2d38c095a6
8 changed files with 60 additions and 29 deletions

View file

@ -229,6 +229,9 @@ class RunnerCaps:
erased by the underlying tool before flashing; UICR on nRF SoCs erased by the underlying tool before flashing; UICR on nRF SoCs
is one example.) is one example.)
- reset: whether the runner supports a --reset option, which
resets the device after a flash operation is complete.
- tool_opt: whether the runner supports a --tool-opt (-O) option, which - tool_opt: whether the runner supports a --tool-opt (-O) option, which
can be given multiple times and is passed on to the underlying tool can be given multiple times and is passed on to the underlying tool
that the runner wraps. that the runner wraps.
@ -240,12 +243,14 @@ class RunnerCaps:
dev_id: bool = False, dev_id: bool = False,
flash_addr: bool = False, flash_addr: bool = False,
erase: bool = False, erase: bool = False,
reset: bool = False,
tool_opt: bool = False, tool_opt: bool = False,
file: bool = False): file: bool = False):
self.commands = commands self.commands = commands
self.dev_id = dev_id self.dev_id = dev_id
self.flash_addr = bool(flash_addr) self.flash_addr = bool(flash_addr)
self.erase = bool(erase) self.erase = bool(erase)
self.reset = bool(reset)
self.tool_opt = bool(tool_opt) self.tool_opt = bool(tool_opt)
self.file = bool(file) self.file = bool(file)
@ -254,6 +259,7 @@ class RunnerCaps:
f'dev_id={self.dev_id}, ' f'dev_id={self.dev_id}, '
f'flash_addr={self.flash_addr}, ' f'flash_addr={self.flash_addr}, '
f'erase={self.erase}, ' f'erase={self.erase}, '
f'reset={self.reset}, '
f'tool_opt={self.tool_opt}, ' f'tool_opt={self.tool_opt}, '
f'file={self.file}' f'file={self.file}'
')') ')')
@ -521,9 +527,16 @@ class ZephyrBinaryRunner(abc.ABC):
parser.add_argument('--erase', '--no-erase', nargs=0, parser.add_argument('--erase', '--no-erase', nargs=0,
action=_ToggleAction, action=_ToggleAction,
help=("mass erase flash before loading, or don't" help=("mass erase flash before loading, or don't. "
"Default action depends on each specific runner."
if caps.erase else argparse.SUPPRESS)) if caps.erase else argparse.SUPPRESS))
parser.add_argument('--reset', '--no-reset', nargs=0,
action=_ToggleAction,
help=("reset device after flashing, or don't. "
"Default action depends on each specific runner."
if caps.reset else argparse.SUPPRESS))
parser.add_argument('-O', '--tool-opt', dest='tool_opt', parser.add_argument('-O', '--tool-opt', dest='tool_opt',
default=[], action='append', default=[], action='append',
help=(cls.tool_opt_help() if caps.tool_opt help=(cls.tool_opt_help() if caps.tool_opt
@ -552,6 +565,8 @@ class ZephyrBinaryRunner(abc.ABC):
_missing_cap(cls, '--dt-flash') _missing_cap(cls, '--dt-flash')
if args.erase and not caps.erase: if args.erase and not caps.erase:
_missing_cap(cls, '--erase') _missing_cap(cls, '--erase')
if args.reset and not caps.reset:
_missing_cap(cls, '--reset')
if args.tool_opt and not caps.tool_opt: if args.tool_opt and not caps.tool_opt:
_missing_cap(cls, '--tool-opt') _missing_cap(cls, '--tool-opt')
if args.file and not caps.file: if args.file and not caps.file:
@ -564,6 +579,8 @@ class ZephyrBinaryRunner(abc.ABC):
ret = cls.do_create(cfg, args) ret = cls.do_create(cfg, args)
if args.erase: if args.erase:
ret.logger.info('mass erase requested') ret.logger.info('mass erase requested')
if args.reset:
ret.logger.info('reset after flashing requested')
return ret return ret
@classmethod @classmethod

View file

@ -16,13 +16,15 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for espidf.''' '''Runner front-end for espidf.'''
def __init__(self, cfg, device, boot_address, part_table_address, def __init__(self, cfg, device, boot_address, part_table_address,
app_address, erase=False, baud=921600, flash_size='detect', app_address, erase=False, reset=False, baud=921600,
flash_freq='40m', flash_mode='dio', espidf='espidf', flash_size='detect', flash_freq='40m', flash_mode='dio',
bootloader_bin=None, partition_table_bin=None, no_stub=False): espidf='espidf', bootloader_bin=None, partition_table_bin=None,
no_stub=False):
super().__init__(cfg) super().__init__(cfg)
self.elf = cfg.elf_file self.elf = cfg.elf_file
self.app_bin = cfg.bin_file self.app_bin = cfg.bin_file
self.erase = bool(erase) self.erase = bool(erase)
self.reset = bool(reset)
self.device = device self.device = device
self.boot_address = boot_address self.boot_address = boot_address
self.part_table_address = part_table_address self.part_table_address = part_table_address
@ -42,7 +44,7 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
@classmethod @classmethod
def capabilities(cls): def capabilities(cls):
return RunnerCaps(commands={'flash'}, erase=True) return RunnerCaps(commands={'flash'}, erase=True, reset=True)
@classmethod @classmethod
def do_add_parser(cls, parser): def do_add_parser(cls, parser):
@ -77,6 +79,8 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
parser.add_argument('--esp-no-stub', default=False, action='store_true', parser.add_argument('--esp-no-stub', default=False, action='store_true',
help='Disable launching the flasher stub, only talk to ROM bootloader') help='Disable launching the flasher stub, only talk to ROM bootloader')
parser.set_defaults(reset=True)
@classmethod @classmethod
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
if args.esp_tool: if args.esp_tool:
@ -88,7 +92,7 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
return Esp32BinaryRunner( return Esp32BinaryRunner(
cfg, args.esp_device, boot_address=args.esp_boot_address, cfg, args.esp_device, boot_address=args.esp_boot_address,
part_table_address=args.esp_partition_table_address, part_table_address=args.esp_partition_table_address,
app_address=args.esp_app_address, erase=args.erase, app_address=args.esp_app_address, erase=args.erase, reset=args.reset,
baud=args.esp_baud_rate, flash_size=args.esp_flash_size, baud=args.esp_baud_rate, flash_size=args.esp_flash_size,
flash_freq=args.esp_flash_freq, flash_mode=args.esp_flash_mode, flash_freq=args.esp_flash_freq, flash_mode=args.esp_flash_mode,
espidf=espidf, bootloader_bin=args.esp_flash_bootloader, espidf=espidf, bootloader_bin=args.esp_flash_bootloader,
@ -111,7 +115,8 @@ class Esp32BinaryRunner(ZephyrBinaryRunner):
cmd_flash.extend(['--port', self.device]) cmd_flash.extend(['--port', self.device])
cmd_flash.extend(['--baud', self.baud]) cmd_flash.extend(['--baud', self.baud])
cmd_flash.extend(['--before', 'default_reset']) cmd_flash.extend(['--before', 'default_reset'])
cmd_flash.extend(['--after', 'hard_reset', 'write_flash', '-u']) if self.reset:
cmd_flash.extend(['--after', 'hard_reset', 'write_flash', '-u'])
cmd_flash.extend(['--flash_mode', self.flash_mode]) cmd_flash.extend(['--flash_mode', self.flash_mode])
cmd_flash.extend(['--flash_freq', self.flash_freq]) cmd_flash.extend(['--flash_freq', self.flash_freq])
cmd_flash.extend(['--flash_size', self.flash_size]) cmd_flash.extend(['--flash_size', self.flash_size])

View file

@ -10,13 +10,14 @@ DEFAULT_EZFLASHCLI = "ezFlashCLI"
class EzFlashCliBinaryRunner(ZephyrBinaryRunner): class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for ezFlashCLI''' '''Runner front-end for ezFlashCLI'''
def __init__(self, cfg, tool, sn, erase=False): def __init__(self, cfg, tool, sn, erase=False, reset=True):
super().__init__(cfg) super().__init__(cfg)
self.bin_ = cfg.bin_file self.bin_ = cfg.bin_file
self.tool = tool self.tool = tool
self.sn_arg = ['-j', f'{sn}'] if sn is not None else [] self.sn_arg = ['-j', f'{sn}'] if sn is not None else []
self.erase = bool(erase) self.erase = bool(erase)
self.reset = bool(reset)
@classmethod @classmethod
def name(cls): def name(cls):
@ -24,7 +25,7 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
@classmethod @classmethod
def capabilities(cls): def capabilities(cls):
return RunnerCaps(commands={'flash'}, erase=True) return RunnerCaps(commands={'flash'}, erase=True, reset=True)
@classmethod @classmethod
def do_add_parser(cls, parser): def do_add_parser(cls, parser):
@ -34,6 +35,8 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
parser.add_argument('--sn', default=None, required=False, parser.add_argument('--sn', default=None, required=False,
help='J-Link probe serial number') help='J-Link probe serial number')
parser.set_defaults(reset=True)
@classmethod @classmethod
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
return EzFlashCliBinaryRunner(cfg, tool=args.tool, sn=args.sn, return EzFlashCliBinaryRunner(cfg, tool=args.tool, sn=args.sn,
@ -64,7 +67,7 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
load_offset = self.build_conf['CONFIG_FLASH_LOAD_OFFSET'] load_offset = self.build_conf['CONFIG_FLASH_LOAD_OFFSET']
self.check_call([self.tool] + self.sn_arg + ["write_flash", f'0x{load_offset:x}', self.bin_]) self.check_call([self.tool] + self.sn_arg + ["write_flash", f'0x{load_offset:x}', self.bin_])
def reset(self): def reset_device(self):
self.logger.info("Resetting...") self.logger.info("Resetting...")
self.check_call([self.tool] + self.sn_arg + ["go"]) self.check_call([self.tool] + self.sn_arg + ["go"])
@ -72,4 +75,5 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
self.require(self.tool) self.require(self.tool)
self.ensure_output('bin') self.ensure_output('bin')
self.program_bin() self.program_bin()
self.reset() if self.reset:
self.reset_device()

View file

@ -35,7 +35,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
def __init__(self, cfg, device, dev_id=None, def __init__(self, cfg, device, dev_id=None,
commander=DEFAULT_JLINK_EXE, commander=DEFAULT_JLINK_EXE,
dt_flash=True, erase=True, reset_after_load=False, dt_flash=True, erase=True, reset=False,
iface='swd', speed='auto', iface='swd', speed='auto',
loader=None, loader=None,
gdbserver='JLinkGDBServer', gdbserver='JLinkGDBServer',
@ -54,7 +54,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
self.commander = commander self.commander = commander
self.dt_flash = dt_flash self.dt_flash = dt_flash
self.erase = erase self.erase = erase
self.reset_after_load = reset_after_load self.reset = reset
self.gdbserver = gdbserver self.gdbserver = gdbserver
self.iface = iface self.iface = iface
self.speed = speed self.speed = speed
@ -74,7 +74,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
@classmethod @classmethod
def capabilities(cls): def capabilities(cls):
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'}, return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
dev_id=True, flash_addr=True, erase=True, dev_id=True, flash_addr=True, erase=True, reset=True,
tool_opt=True, file=True) tool_opt=True, file=True)
@classmethod @classmethod
@ -114,11 +114,11 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
help=f'''J-Link Commander, default is help=f'''J-Link Commander, default is
{DEFAULT_JLINK_EXE}''') {DEFAULT_JLINK_EXE}''')
parser.add_argument('--reset-after-load', '--no-reset-after-load', parser.add_argument('--reset-after-load', '--no-reset-after-load',
dest='reset_after_load', nargs=0, dest='reset', nargs=0,
action=ToggleAction, action=ToggleAction,
help='reset after loading? (default: no)') help='obsolete synonym for --reset/--no-reset')
parser.set_defaults(reset_after_load=False) parser.set_defaults(reset=False)
@classmethod @classmethod
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
@ -127,7 +127,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
commander=args.commander, commander=args.commander,
dt_flash=args.dt_flash, dt_flash=args.dt_flash,
erase=args.erase, erase=args.erase,
reset_after_load=args.reset_after_load, reset=args.reset,
iface=args.iface, speed=args.speed, iface=args.iface, speed=args.speed,
gdbserver=args.gdbserver, gdbserver=args.gdbserver,
loader=args.loader, loader=args.loader,
@ -266,7 +266,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
client_cmd += ['-ex', 'monitor halt', client_cmd += ['-ex', 'monitor halt',
'-ex', 'monitor reset', '-ex', 'monitor reset',
'-ex', 'load'] '-ex', 'load']
if self.reset_after_load: if self.reset:
client_cmd += ['-ex', 'monitor reset'] client_cmd += ['-ex', 'monitor reset']
if not self.gdb_host: if not self.gdb_host:
self.require(self.gdbserver) self.require(self.gdbserver)
@ -326,7 +326,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
# Flash the selected build artifact # Flash the selected build artifact
lines.append(flash_cmd) lines.append(flash_cmd)
if self.reset_after_load: if self.reset:
lines.append('r') # Reset and halt the target lines.append('r') # Reset and halt the target
lines.append('g') # Start the CPU lines.append('g') # Start the CPU

View file

@ -28,7 +28,7 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end base class for nrf tools.''' '''Runner front-end base class for nrf tools.'''
def __init__(self, cfg, family, softreset, dev_id, erase=False, def __init__(self, cfg, family, softreset, dev_id, erase=False,
tool_opt=[], force=False, recover=False): reset=True, tool_opt=[], force=False, recover=False):
super().__init__(cfg) super().__init__(cfg)
self.hex_ = cfg.hex_file self.hex_ = cfg.hex_file
if family and not family.endswith('_FAMILY'): if family and not family.endswith('_FAMILY'):
@ -37,6 +37,7 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
self.softreset = softreset self.softreset = softreset
self.dev_id = dev_id self.dev_id = dev_id
self.erase = bool(erase) self.erase = bool(erase)
self.reset = bool(reset)
self.force = force self.force = force
self.recover = bool(recover) self.recover = bool(recover)
@ -47,7 +48,7 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
@classmethod @classmethod
def capabilities(cls): def capabilities(cls):
return RunnerCaps(commands={'flash'}, dev_id=True, erase=True, return RunnerCaps(commands={'flash'}, dev_id=True, erase=True,
tool_opt=True) reset=True, tool_opt=True)
@classmethod @classmethod
def dev_id_help(cls) -> str: def dev_id_help(cls) -> str:
@ -75,6 +76,8 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
memory and disable read back protection before memory and disable read back protection before
flashing (erases flash for both cores on nRF53)''') flashing (erases flash for both cores on nRF53)''')
parser.set_defaults(reset=True)
def ensure_snr(self): def ensure_snr(self):
if not self.dev_id or "*" in self.dev_id: if not self.dev_id or "*" in self.dev_id:
self.dev_id = self.get_board_snr(self.dev_id or "*") self.dev_id = self.get_board_snr(self.dev_id or "*")
@ -398,7 +401,8 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
if self.recover: if self.recover:
self.recover_target() self.recover_target()
self.program_hex() self.program_hex()
self.reset_target() if self.reset:
self.reset_target()
# All done, now flush any outstanding ops # All done, now flush any outstanding ops
self.flush(force=True) self.flush(force=True)

View file

@ -30,6 +30,7 @@ class NrfJprogBinaryRunner(NrfBinaryRunner):
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset, return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
args.dev_id, erase=args.erase, args.dev_id, erase=args.erase,
reset=args.reset,
tool_opt=args.tool_opt, force=args.force, tool_opt=args.tool_opt, force=args.force,
recover=args.recover) recover=args.recover)

View file

@ -16,9 +16,9 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
'''Runner front-end for nrfutil.''' '''Runner front-end for nrfutil.'''
def __init__(self, cfg, family, softreset, dev_id, erase=False, def __init__(self, cfg, family, softreset, dev_id, erase=False,
tool_opt=[], force=False, recover=False): reset=True, tool_opt=[], force=False, recover=False):
super().__init__(cfg, family, softreset, dev_id, erase, super().__init__(cfg, family, softreset, dev_id, erase, reset,
tool_opt, force, recover) tool_opt, force, recover)
self._ops = [] self._ops = []
self._op_id = 1 self._op_id = 1
@ -35,6 +35,7 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
return NrfUtilBinaryRunner(cfg, args.nrf_family, args.softreset, return NrfUtilBinaryRunner(cfg, args.nrf_family, args.softreset,
args.dev_id, erase=args.erase, args.dev_id, erase=args.erase,
reset=args.reset,
tool_opt=args.tool_opt, force=args.force, tool_opt=args.tool_opt, force=args.force,
recover=args.recover) recover=args.recover)

View file

@ -37,7 +37,7 @@ class Stm32flashBinaryRunner(ZephyrBinaryRunner):
@classmethod @classmethod
def capabilities(cls): def capabilities(cls):
return RunnerCaps(commands={'flash'}) return RunnerCaps(commands={'flash'}, reset=True)
@classmethod @classmethod
def do_add_parser(cls, parser): def do_add_parser(cls, parser):
@ -72,12 +72,11 @@ class Stm32flashBinaryRunner(ZephyrBinaryRunner):
parser.add_argument('--serial-mode', default='8e1', required=False, parser.add_argument('--serial-mode', default='8e1', required=False,
help='serial port mode, default \'8e1\'') help='serial port mode, default \'8e1\'')
parser.add_argument('--reset', default=False, required=False, action='store_true',
help='reset device at exit, default False')
parser.add_argument('--verify', default=False, required=False, action='store_true', parser.add_argument('--verify', default=False, required=False, action='store_true',
help='verify writes, default False') help='verify writes, default False')
parser.set_defaults(reset=False)
@classmethod @classmethod
def do_create(cls, cfg, args): def do_create(cls, cfg, args):
return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action, return Stm32flashBinaryRunner(cfg, device=args.device, action=args.action,