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
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
can be given multiple times and is passed on to the underlying tool
that the runner wraps.
@ -240,12 +243,14 @@ class RunnerCaps:
dev_id: bool = False,
flash_addr: bool = False,
erase: bool = False,
reset: bool = False,
tool_opt: bool = False,
file: bool = False):
self.commands = commands
self.dev_id = dev_id
self.flash_addr = bool(flash_addr)
self.erase = bool(erase)
self.reset = bool(reset)
self.tool_opt = bool(tool_opt)
self.file = bool(file)
@ -254,6 +259,7 @@ class RunnerCaps:
f'dev_id={self.dev_id}, '
f'flash_addr={self.flash_addr}, '
f'erase={self.erase}, '
f'reset={self.reset}, '
f'tool_opt={self.tool_opt}, '
f'file={self.file}'
')')
@ -521,9 +527,16 @@ class ZephyrBinaryRunner(abc.ABC):
parser.add_argument('--erase', '--no-erase', nargs=0,
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))
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',
default=[], action='append',
help=(cls.tool_opt_help() if caps.tool_opt
@ -552,6 +565,8 @@ class ZephyrBinaryRunner(abc.ABC):
_missing_cap(cls, '--dt-flash')
if args.erase and not caps.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:
_missing_cap(cls, '--tool-opt')
if args.file and not caps.file:
@ -564,6 +579,8 @@ class ZephyrBinaryRunner(abc.ABC):
ret = cls.do_create(cfg, args)
if args.erase:
ret.logger.info('mass erase requested')
if args.reset:
ret.logger.info('reset after flashing requested')
return ret
@classmethod

View file

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

View file

@ -10,13 +10,14 @@ DEFAULT_EZFLASHCLI = "ezFlashCLI"
class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
'''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)
self.bin_ = cfg.bin_file
self.tool = tool
self.sn_arg = ['-j', f'{sn}'] if sn is not None else []
self.erase = bool(erase)
self.reset = bool(reset)
@classmethod
def name(cls):
@ -24,7 +25,7 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash'}, erase=True)
return RunnerCaps(commands={'flash'}, erase=True, reset=True)
@classmethod
def do_add_parser(cls, parser):
@ -34,6 +35,8 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
parser.add_argument('--sn', default=None, required=False,
help='J-Link probe serial number')
parser.set_defaults(reset=True)
@classmethod
def do_create(cls, cfg, args):
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']
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.check_call([self.tool] + self.sn_arg + ["go"])
@ -72,4 +75,5 @@ class EzFlashCliBinaryRunner(ZephyrBinaryRunner):
self.require(self.tool)
self.ensure_output('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,
commander=DEFAULT_JLINK_EXE,
dt_flash=True, erase=True, reset_after_load=False,
dt_flash=True, erase=True, reset=False,
iface='swd', speed='auto',
loader=None,
gdbserver='JLinkGDBServer',
@ -54,7 +54,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
self.commander = commander
self.dt_flash = dt_flash
self.erase = erase
self.reset_after_load = reset_after_load
self.reset = reset
self.gdbserver = gdbserver
self.iface = iface
self.speed = speed
@ -74,7 +74,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
@classmethod
def capabilities(cls):
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)
@classmethod
@ -114,11 +114,11 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
help=f'''J-Link Commander, default is
{DEFAULT_JLINK_EXE}''')
parser.add_argument('--reset-after-load', '--no-reset-after-load',
dest='reset_after_load', nargs=0,
dest='reset', nargs=0,
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
def do_create(cls, cfg, args):
@ -127,7 +127,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
commander=args.commander,
dt_flash=args.dt_flash,
erase=args.erase,
reset_after_load=args.reset_after_load,
reset=args.reset,
iface=args.iface, speed=args.speed,
gdbserver=args.gdbserver,
loader=args.loader,
@ -266,7 +266,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
client_cmd += ['-ex', 'monitor halt',
'-ex', 'monitor reset',
'-ex', 'load']
if self.reset_after_load:
if self.reset:
client_cmd += ['-ex', 'monitor reset']
if not self.gdb_host:
self.require(self.gdbserver)
@ -326,7 +326,7 @@ class JLinkBinaryRunner(ZephyrBinaryRunner):
# Flash the selected build artifact
lines.append(flash_cmd)
if self.reset_after_load:
if self.reset:
lines.append('r') # Reset and halt the target
lines.append('g') # Start the CPU

View file

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

View file

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

View file

@ -16,9 +16,9 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
'''Runner front-end for nrfutil.'''
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)
self._ops = []
self._op_id = 1
@ -35,6 +35,7 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
def do_create(cls, cfg, args):
return NrfUtilBinaryRunner(cfg, args.nrf_family, args.softreset,
args.dev_id, erase=args.erase,
reset=args.reset,
tool_opt=args.tool_opt, force=args.force,
recover=args.recover)

View file

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