west: runners: nrf: Generalize the erase command

Instead of providing an `--erase-pages` command, generalize it to a new
`--erase-mode` one that can be set to `none`, `ranges`, or `all`. This
gives the user full control over the erase mode that will be passed on
to nrfutil.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Carles Cufi 2025-04-11 19:32:20 +02:00 committed by Benjamin Cabé
commit 58e0e31c7e
4 changed files with 32 additions and 19 deletions

View file

@ -41,8 +41,8 @@ Boards
* All boards based on a Nordic IC of the nRF54L series now default to not * All boards based on a Nordic IC of the nRF54L series now default to not
erasing any part of the internal storage when flashing. If you'd like to erasing any part of the internal storage when flashing. If you'd like to
revert to the previous default of erasing the pages that will be written to by revert to the previous default of erasing the pages that will be written to by
the firmware to be flashed you can use the new ``--erase-pages`` command-line the firmware to be flashed you can set the new ``--erase-mode`` command-line
switch when invoking ``west flash``. switch when invoking ``west flash`` to ``ranges``.
Note that RRAM on nRF54L devices is not physically paged, and paging is Note that RRAM on nRF54L devices is not physically paged, and paging is
only artificially provided, with a page size of 4096 bytes, for an easier only artificially provided, with a page size of 4096 bytes, for an easier
transition of nRF52 software to nRF54L devices. transition of nRF52 software to nRF54L devices.

View file

@ -79,7 +79,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, pinreset, dev_id, erase=False, def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False,
erase_pages=False, reset=True, tool_opt=None, force=False, erase_mode=None, reset=True, tool_opt=None, force=False,
recover=False): recover=False):
super().__init__(cfg) super().__init__(cfg)
self.hex_ = cfg.hex_file self.hex_ = cfg.hex_file
@ -89,7 +89,7 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
self.pinreset = pinreset self.pinreset = pinreset
self.dev_id = dev_id self.dev_id = dev_id
self.erase = bool(erase) self.erase = bool(erase)
self.erase_pages = bool(erase_pages) self.erase_mode = erase_mode
self.reset = bool(reset) self.reset = bool(reset)
self.force = force self.force = force
self.recover = bool(recover) self.recover = bool(recover)
@ -139,9 +139,10 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
help='''erase all user available non-volatile help='''erase all user available non-volatile
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.add_argument('--erase-pages', required=False, parser.add_argument('--erase-mode', required=False,
action='store_true', dest='erase_pages', choices=['none', 'ranges', 'all'], dest='erase_mode',
help='erase pages to be used by the firmware') help='Select the type of erase operation for the '
'internal non-volatile memory')
parser.set_defaults(reset=True) parser.set_defaults(reset=True)
@ -341,6 +342,18 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
return None return None
def _get_erase_mode(self, mode):
if not mode:
return None
elif mode == "none":
return "ERASE_NONE"
elif mode == "ranges":
return "ERASE_RANGES_TOUCHED_BY_FIRMWARE"
elif mode == "all":
return "ERASE_ALL"
else:
raise RuntimeError(f"Invalid erase mode: {mode}")
def program_hex(self): def program_hex(self):
# Get the command use to actually program self.hex_. # Get the command use to actually program self.hex_.
self.logger.info(f'Flashing file: {self.hex_}') self.logger.info(f'Flashing file: {self.hex_}')
@ -421,8 +434,8 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
else: else:
if self.erase: if self.erase:
erase_arg = 'ERASE_ALL' erase_arg = 'ERASE_ALL'
elif self.family == 'nrf54l' and not self.erase_pages: elif self.family == 'nrf54l':
erase_arg = 'ERASE_NONE' erase_arg = self._get_erase_mode(self.erase_mode) or 'ERASE_NONE'
else: else:
erase_arg = 'ERASE_RANGES_TOUCHED_BY_FIRMWARE' erase_arg = 'ERASE_RANGES_TOUCHED_BY_FIRMWARE'
@ -531,14 +544,14 @@ class NrfBinaryRunner(ZephyrBinaryRunner):
raise RuntimeError('Options --softreset and --pinreset are mutually ' raise RuntimeError('Options --softreset and --pinreset are mutually '
'exclusive.') 'exclusive.')
if self.erase and self.erase_pages: if self.erase and self.erase_mode:
raise RuntimeError('Options --erase and --erase-pages are mutually ' raise RuntimeError('Options --erase and --erase-mode are mutually '
'exclusive.') 'exclusive.')
self.ensure_family() self.ensure_family()
if self.family != 'nrf54l' and self.erase_pages: if self.family != 'nrf54l' and self.erase_mode:
raise RuntimeError('Option --erase-pages can only be used with the ' raise RuntimeError('Option --erase-mode can only be used with the '
'nRF54L family.') 'nRF54L family.')
self.ensure_output('hex') self.ensure_output('hex')

View file

@ -18,11 +18,11 @@ class NrfJprogBinaryRunner(NrfBinaryRunner):
'''Runner front-end for nrfjprog.''' '''Runner front-end for nrfjprog.'''
def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False, def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False,
erase_pages=False, reset=True, tool_opt=None, force=False, erase_mode=None, reset=True, tool_opt=None, force=False,
recover=False, qspi_ini=None): recover=False, qspi_ini=None):
super().__init__(cfg, family, softreset, pinreset, dev_id, erase, super().__init__(cfg, family, softreset, pinreset, dev_id, erase,
erase_pages, reset, tool_opt, force, recover) erase_mode, reset, tool_opt, force, recover)
self.qspi_ini = qspi_ini self.qspi_ini = qspi_ini
@ -46,7 +46,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.pinreset, args.dev_id, erase=args.erase, args.pinreset, args.dev_id, erase=args.erase,
erase_pages=args.erase_pages, reset=args.reset, erase_mode=args.erase_mode, reset=args.reset,
tool_opt=args.tool_opt, force=args.force, tool_opt=args.tool_opt, force=args.force,
recover=args.recover, qspi_ini=args.qspi_ini) recover=args.recover, qspi_ini=args.qspi_ini)
@classmethod @classmethod

View file

@ -17,11 +17,11 @@ class NrfUtilBinaryRunner(NrfBinaryRunner):
'''Runner front-end for nrfutil.''' '''Runner front-end for nrfutil.'''
def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False, def __init__(self, cfg, family, softreset, pinreset, dev_id, erase=False,
erase_pages=False, reset=True, tool_opt=None, force=False, erase_mode=None, reset=True, tool_opt=None, force=False,
recover=False, suit_starter=False, ext_mem_config_file=None): recover=False, suit_starter=False, ext_mem_config_file=None):
super().__init__(cfg, family, softreset, pinreset, dev_id, erase, super().__init__(cfg, family, softreset, pinreset, dev_id, erase,
erase_pages, reset, tool_opt, force, recover) erase_mode, reset, tool_opt, force, recover)
self.suit_starter = suit_starter self.suit_starter = suit_starter
self.ext_mem_config_file = ext_mem_config_file self.ext_mem_config_file = ext_mem_config_file
@ -50,7 +50,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.pinreset, args.dev_id, erase=args.erase, args.pinreset, args.dev_id, erase=args.erase,
erase_pages=args.erase_pages, reset=args.reset, erase_mode=args.erase_mode, 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,
suit_starter=args.suit_manifest_starter, suit_starter=args.suit_manifest_starter,