Skip to content

Commit 16f9163

Browse files
committed
[nrf fromtree] scripts: runners: nrfutil: Switch to command-line args instead of JSON
Instead of pre-generating the JSON batch to then execute it, use command-line arguments and --x-append-batch to generate the JSON file tht will then be passed to x-execute-batch. Signed-off-by: Carles Cufi <[email protected]> (cherry picked from commit 6e9e839)
1 parent 6fa629b commit 16f9163

File tree

4 files changed

+151
-132
lines changed

4 files changed

+151
-132
lines changed

scripts/west_commands/runners/nrf_common.py

Lines changed: 58 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@
3232
ErrVerify = 25
3333

3434
UICR_RANGES = {
35-
'NRF53_FAMILY': {
36-
'NRFDL_DEVICE_CORE_APPLICATION': (0x00FF8000, 0x00FF8800),
37-
'NRFDL_DEVICE_CORE_NETWORK': (0x01FF8000, 0x01FF8800),
35+
'nrf53': {
36+
'Application': (0x00FF8000, 0x00FF8800),
37+
'Network': (0x01FF8000, 0x01FF8800),
3838
},
39-
'NRF54H_FAMILY': {
40-
'NRFDL_DEVICE_CORE_APPLICATION': (0x0FFF8000, 0x0FFF8800),
41-
'NRFDL_DEVICE_CORE_NETWORK': (0x0FFFA000, 0x0FFFA800),
39+
'nrf54h': {
40+
'Application': (0x0FFF8000, 0x0FFF8800),
41+
'Network': (0x0FFFA000, 0x0FFFA800),
4242
},
43-
'NRF91_FAMILY': {
44-
'NRFDL_DEVICE_CORE_APPLICATION': (0x00FF8000, 0x00FF8800),
43+
'nrf91': {
44+
'Application': (0x00FF8000, 0x00FF8800),
4545
},
46-
'NRF92_FAMILY': {
47-
'NRFDL_DEVICE_CORE_APPLICATION': (0x0FFF8000, 0x0FFF8800),
48-
'NRFDL_DEVICE_CORE_NETWORK': (0x0FFFA000, 0x0FFFA800),
46+
'nrf92': {
47+
'Application': (0x0FFF8000, 0x0FFF8800),
48+
'Network': (0x0FFFA000, 0x0FFFA800),
4949
},
5050
}
5151

@@ -79,9 +79,8 @@ def __init__(self, cfg, family, softreset, dev_id, erase=False,
7979
reset=True, tool_opt=None, force=False, recover=False):
8080
super().__init__(cfg)
8181
self.hex_ = cfg.hex_file
82-
if family and not family.endswith('_FAMILY'):
83-
family = f'{family}_FAMILY'
84-
self.family = family
82+
# The old --nrf-family options takes upper-case family names
83+
self.family = family.lower() if family else None
8584
self.softreset = softreset
8685
self.dev_id = dev_id
8786
self.erase = bool(erase)
@@ -214,19 +213,19 @@ def ensure_family(self):
214213
return
215214

216215
if self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF51X'):
217-
self.family = 'NRF51_FAMILY'
216+
self.family = 'nrf51'
218217
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF52X'):
219-
self.family = 'NRF52_FAMILY'
218+
self.family = 'nrf52'
220219
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF53X'):
221-
self.family = 'NRF53_FAMILY'
220+
self.family = 'nrf53'
222221
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF54LX'):
223-
self.family = 'NRF54L_FAMILY'
222+
self.family = 'nrf54l'
224223
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF54HX'):
225-
self.family = 'NRF54H_FAMILY'
224+
self.family = 'nrf54h'
226225
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF91X'):
227-
self.family = 'NRF91_FAMILY'
226+
self.family = 'nrf91'
228227
elif self.build_conf.getboolean('CONFIG_SOC_SERIES_NRF92X'):
229-
self.family = 'NRF92_FAMILY'
228+
self.family = 'nrf92'
230229
else:
231230
raise RuntimeError(f'unknown nRF; update {__file__}')
232231

@@ -251,7 +250,7 @@ def flush(self, force=False):
251250
self.flush_ops(force=force)
252251
except subprocess.CalledProcessError as cpe:
253252
if cpe.returncode == ErrNotAvailableBecauseProtection:
254-
if self.family == 'NRF53_FAMILY':
253+
if self.family == 'nrf53':
255254
family_help = (
256255
' Note: your target is an nRF53; all flash memory '
257256
'for both the network and application cores will be '
@@ -278,7 +277,7 @@ def flush(self, force=False):
278277

279278

280279
def recover_target(self):
281-
if self.family in ('NRF53_FAMILY', 'NRF54H_FAMILY', 'NRF92_FAMILY'):
280+
if self.family in ('nrf53', 'nrf54h', 'nrf92'):
282281
self.logger.info(
283282
'Recovering and erasing flash memory for both the network '
284283
'and application cores.')
@@ -291,8 +290,8 @@ def recover_target(self):
291290
# keeps the debug access port open, recovering the network core last
292291
# would result in that small image being deleted from the app core.
293292
# In the case of the 54H, the order is indifferent.
294-
if self.family in ('NRF53_FAMILY', 'NRF54H_FAMILY', 'NRF92_FAMILY'):
295-
self.exec_op('recover', core='NRFDL_DEVICE_CORE_NETWORK')
293+
if self.family in ('nrf53', 'nrf54h', 'nrf92'):
294+
self.exec_op('recover', core='Network')
296295

297296
self.exec_op('recover')
298297

@@ -303,7 +302,7 @@ def program_hex(self):
303302
# What type of erase/core arguments should we pass to the tool?
304303
core = None
305304

306-
if self.family in ('NRF54H_FAMILY', 'NRF92_FAMILY'):
305+
if self.family in ('nrf54h', 'nrf92'):
307306
erase_arg = 'ERASE_NONE'
308307

309308
cpuapp = (
@@ -317,9 +316,9 @@ def program_hex(self):
317316
generated_uicr = self.build_conf.getboolean('CONFIG_NRF_REGTOOL_GENERATE_UICR')
318317

319318
if cpuapp:
320-
core = 'NRFDL_DEVICE_CORE_APPLICATION'
319+
core = 'Application'
321320
elif cpurad:
322-
core = 'NRFDL_DEVICE_CORE_NETWORK'
321+
core = 'Network'
323322

324323
if generated_uicr and not self.hex_get_uicrs().get(core):
325324
raise RuntimeError(
@@ -329,8 +328,8 @@ def program_hex(self):
329328
)
330329

331330
if self.erase:
332-
self.exec_op('erase', core='NRFDL_DEVICE_CORE_APPLICATION')
333-
self.exec_op('erase', core='NRFDL_DEVICE_CORE_NETWORK')
331+
self.exec_op('erase', core='Application')
332+
self.exec_op('erase', core='Network')
334333

335334
# Manage SUIT artifacts.
336335
# This logic should be executed only once per build.
@@ -354,15 +353,15 @@ def program_hex(self):
354353
'ERASE_NONE',
355354
None,
356355
defer=True,
357-
core='NRFDL_DEVICE_CORE_APPLICATION',
356+
core='Application',
358357
)
359358
if os.path.exists(rad_mpi_hex_file):
360359
self.op_program(
361360
rad_mpi_hex_file,
362361
'ERASE_NONE',
363362
None,
364363
defer=True,
365-
core='NRFDL_DEVICE_CORE_NETWORK',
364+
core='Network',
366365
)
367366

368367
# Handle SUIT root manifest if application manifests are not used.
@@ -378,41 +377,38 @@ def program_hex(self):
378377
'ERASE_NONE',
379378
None,
380379
defer=True,
381-
core='NRFDL_DEVICE_CORE_APPLICATION',
380+
core='Application',
382381
)
383382

384383
if not self.erase and generated_uicr:
385384
self.exec_op('erase', core=core, option={'chip_erase_mode': 'ERASE_UICR',
386-
'qspi_erase_mode': 'ERASE_NONE'})
385+
'ext_mem_erase_mode': 'ERASE_NONE'})
387386
else:
388387
if self.erase:
389388
erase_arg = 'ERASE_ALL'
390389
else:
391-
if self.family == 'NRF52_FAMILY':
392-
erase_arg = 'ERASE_PAGES_INCLUDING_UICR'
393-
else:
394-
erase_arg = 'ERASE_PAGES'
390+
erase_arg = 'ERASE_RANGES_TOUCHED_BY_FIRMWARE'
395391

396392
xip_ranges = {
397-
'NRF52_FAMILY': (0x12000000, 0x19FFFFFF),
398-
'NRF53_FAMILY': (0x10000000, 0x1FFFFFFF),
393+
'nrf52': (0x12000000, 0x19FFFFFF),
394+
'nrf53': (0x10000000, 0x1FFFFFFF),
399395
}
400-
qspi_erase_opt = None
396+
ext_mem_erase_opt = None
401397
if self.family in xip_ranges:
402398
xip_start, xip_end = xip_ranges[self.family]
403399
if self.hex_refers_region(xip_start, xip_end):
404-
qspi_erase_opt = 'ERASE_ALL'
400+
ext_mem_erase_opt = 'ERASE_ALL'
405401

406402
# What tool commands do we need to flash this target?
407-
if self.family == 'NRF53_FAMILY':
403+
if self.family == 'nrf53':
408404
# nRF53 requires special treatment due to the extra coprocessor.
409-
self.program_hex_nrf53(erase_arg, qspi_erase_opt)
405+
self.program_hex_nrf53(erase_arg, ext_mem_erase_opt)
410406
else:
411-
self.op_program(self.hex_, erase_arg, qspi_erase_opt, defer=True, core=core)
407+
self.op_program(self.hex_, erase_arg, ext_mem_erase_opt, defer=True, core=core)
412408

413409
self.flush(force=False)
414410

415-
def program_hex_nrf53(self, erase_arg, qspi_erase_opt):
411+
def program_hex_nrf53(self, erase_arg, ext_mem_erase_opt):
416412
# program_hex() helper for nRF53.
417413

418414
# *********************** NOTE *******************************
@@ -442,8 +438,8 @@ def program_hex_nrf53(self, erase_arg, qspi_erase_opt):
442438
# If there is nothing in the hex file for the network core,
443439
# only the application core is programmed.
444440
if not self.hex_refers_region(net_flash_start, net_flash_end):
445-
self.op_program(self.hex_, erase_arg, qspi_erase_opt, defer=True,
446-
core='NRFDL_DEVICE_CORE_APPLICATION')
441+
self.op_program(self.hex_, erase_arg, ext_mem_erase_opt, defer=True,
442+
core='Application')
447443
# If there is some content that addresses a region beyond the network
448444
# core flash range, two hex files are generated and the two cores
449445
# are programmed one by one.
@@ -473,22 +469,22 @@ def program_hex_nrf53(self, erase_arg, qspi_erase_opt):
473469
app_hex.write_hex_file(app_hex_file)
474470

475471
self.op_program(net_hex_file, erase_arg, None, defer=True,
476-
core='NRFDL_DEVICE_CORE_NETWORK')
477-
self.op_program(app_hex_file, erase_arg, qspi_erase_opt, defer=True,
478-
core='NRFDL_DEVICE_CORE_APPLICATION')
472+
core='Network')
473+
self.op_program(app_hex_file, erase_arg, ext_mem_erase_opt, defer=True,
474+
core='Application')
479475
# Otherwise, only the network core is programmed.
480476
else:
481477
self.op_program(self.hex_, erase_arg, None, defer=True,
482-
core='NRFDL_DEVICE_CORE_NETWORK')
478+
core='Network')
483479

484480
def reset_target(self):
485-
if self.family == 'NRF52_FAMILY' and not self.softreset:
481+
if self.family == 'nrf52' and not self.softreset:
486482
self.exec_op('pinreset-enable')
487483

488484
if self.softreset:
489-
self.exec_op('reset', option="RESET_SYSTEM")
485+
self.exec_op('reset', kind="RESET_SYSTEM")
490486
else:
491-
self.exec_op('reset', option="RESET_PIN")
487+
self.exec_op('reset', kind="RESET_PIN")
492488

493489
@abc.abstractmethod
494490
def do_require(self):
@@ -506,15 +502,15 @@ def _check_suit_starter(self, op):
506502

507503
return file
508504

509-
def op_program(self, hex_file, erase, qspi_erase, defer=False, core=None):
510-
args = self._op_program(hex_file, erase, qspi_erase)
505+
def op_program(self, hex_file, erase, ext_mem_erase, defer=False, core=None):
506+
args = self._op_program(hex_file, erase, ext_mem_erase)
511507
self.exec_op('program', defer, core, **args)
512508

513-
def _op_program(self, hex_file, erase, qspi_erase):
509+
def _op_program(self, hex_file, erase, ext_mem_erase):
514510
args = {'firmware': {'file': hex_file},
515-
'chip_erase_mode': erase, 'verify': 'VERIFY_READ'}
516-
if qspi_erase:
517-
args['qspi_erase_mode'] = qspi_erase
511+
'options': {'chip_erase_mode': erase, 'verify': 'VERIFY_READ'}}
512+
if ext_mem_erase:
513+
args['options']['ext_mem_erase_mode'] = ext_mem_erase
518514

519515
return args
520516

@@ -533,7 +529,7 @@ def _exec_op(op, defer=False, core=None, **kwargs):
533529

534530
_op = _exec_op(op, defer, core, **kwargs)
535531
# Check if the suit manifest starter needs programming
536-
if self.suit_starter and self.family == 'NRF54H_FAMILY':
532+
if self.suit_starter and self.family == 'nrf54h':
537533
file = self._check_suit_starter(_op)
538534
if file:
539535
args = self._op_program(file, 'ERASE_NONE', None)

scripts/west_commands/runners/nrfjprog.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ def do_exec_op(self, op, force=False):
5858
self.logger.debug(f'Executing op: {op}')
5959
# Translate the op
6060

61-
families = {'NRF51_FAMILY': 'NRF51', 'NRF52_FAMILY': 'NRF52',
62-
'NRF53_FAMILY': 'NRF53', 'NRF54L_FAMILY': 'NRF54L',
63-
'NRF91_FAMILY': 'NRF91'}
64-
cores = {'NRFDL_DEVICE_CORE_APPLICATION': 'CP_APPLICATION',
65-
'NRFDL_DEVICE_CORE_NETWORK': 'CP_NETWORK'}
61+
families = {'nrf51': 'NRF51', 'nrf52': 'NRF52',
62+
'nrf53': 'NRF53', 'nrf54l': 'NRF54L',
63+
'nrf91': 'NRF91'}
64+
cores = {'Application': 'CP_APPLICATION',
65+
'Network': 'CP_NETWORK'}
6666

6767
core_opt = ['--coprocessor', cores[op['core']]] \
6868
if op.get('core') else []
@@ -76,22 +76,24 @@ def do_exec_op(self, op, force=False):
7676
elif op_type == 'program':
7777
cmd.append('--program')
7878
cmd.append(_op['firmware']['file'])
79-
erase = _op['chip_erase_mode']
79+
opts = _op['options']
80+
erase = opts['chip_erase_mode']
8081
if erase == 'ERASE_ALL':
8182
cmd.append('--chiperase')
82-
elif erase == 'ERASE_PAGES':
83-
cmd.append('--sectorerase')
84-
elif erase == 'ERASE_PAGES_INCLUDING_UICR':
85-
cmd.append('--sectoranduicrerase')
83+
elif erase == 'ERASE_RANGES_TOUCHED_BY_FIRMWARE':
84+
if self.family == 'nrf52':
85+
cmd.append('--sectoranduicrerase')
86+
else:
87+
cmd.append('--sectorerase')
8688
elif erase == 'NO_ERASE':
8789
pass
8890
else:
8991
raise RuntimeError(f'Invalid erase mode: {erase}')
9092

91-
if _op.get('qspi_erase_mode'):
93+
if opts.get('ext_mem_erase_mode'):
9294
# In the future there might be multiple QSPI erase modes
9395
cmd.append('--qspisectorerase')
94-
if _op.get('verify'):
96+
if opts.get('verify'):
9597
# In the future there might be multiple verify modes
9698
cmd.append('--verify')
9799
if self.qspi_ini:
@@ -100,9 +102,9 @@ def do_exec_op(self, op, force=False):
100102
elif op_type == 'recover':
101103
cmd.append('--recover')
102104
elif op_type == 'reset':
103-
if _op['option'] == 'RESET_SYSTEM':
105+
if _op['kind'] == 'RESET_SYSTEM':
104106
cmd.append('--reset')
105-
if _op['option'] == 'RESET_PIN':
107+
if _op['kind'] == 'RESET_PIN':
106108
cmd.append('--pinreset')
107109
elif op_type == 'erasepage':
108110
cmd.append('--erasepage')

scripts/west_commands/runners/nrfutil.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
'''Runner for flashing with nrfutil.'''
66

77
import json
8-
import os
98
import subprocess
109
import sys
1110
from pathlib import Path
@@ -102,17 +101,37 @@ def _insert_op(self, op):
102101
self._op_id += 1
103102
self._ops.append(op)
104103

105-
def _exec_batch(self):
106-
# prepare the dictionary and convert to JSON
107-
batch = json.dumps({'family': f'{self.family}',
108-
'operations': [op for op in self._ops]},
109-
indent=4) + '\n'
110-
111-
hex_dir = Path(self.hex_).parent
112-
json_file = os.fspath(hex_dir / 'generated_nrfutil_batch.json')
104+
def _append_batch(self, op, json_file):
105+
_op = op['operation']
106+
op_type = _op['type']
107+
108+
cmd = [f'{op_type}']
109+
110+
if op_type == 'program':
111+
cmd += ['--firmware', _op['firmware']['file']]
112+
opts = _op['options']
113+
# populate the options
114+
cmd.append('--options')
115+
cli_opts = f"chip_erase_mode={opts['chip_erase_mode']}"
116+
if opts.get('ext_mem_erase_mode'):
117+
cli_opts += f",ext_mem_erase_mode={opts['ext_mem_erase_mode']}"
118+
if opts.get('verify'):
119+
cli_opts += f",verify={opts['verify']}"
120+
cmd.append(cli_opts)
121+
elif op_type == 'reset':
122+
cmd += ['--reset-kind', _op['kind']]
123+
124+
cmd += ['--core', op['core']] if op.get('core') else []
125+
cmd += ['--x-family', f'{self.family}']
126+
cmd += ['--x-append-batch', f'{json_file}']
127+
self._exec(cmd)
113128

114-
with open(json_file, "w") as f:
115-
f.write(batch)
129+
def _exec_batch(self):
130+
# Use x-append-batch to get the JSON from nrfutil itself
131+
json_file = Path(self.hex_).parent / 'generated_nrfutil_batch.json'
132+
json_file.unlink(missing_ok=True)
133+
for op in self._ops:
134+
self._append_batch(op, json_file)
116135

117136
# reset first in case an exception is thrown
118137
self._ops = []

0 commit comments

Comments
 (0)