scripts: runners: nrf: Add support for the new nRF Util tool
Add support for Nordic's new nRF Util tool, aka nrfutil. Via its "device" command, nrfutil now supports most of the functionality offered by nrfjprog. The tool itself can be found here: https://www.nordicsemi.com/Products/Development-tools/nrf-util Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
parent
480b6fb7c0
commit
b022835146
4
boards/common/nrfutil.board.cmake
Normal file
4
boards/common/nrfutil.board.cmake
Normal file
|
@ -0,0 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
board_set_flasher_ifnset(nrfutil)
|
||||
board_finalize_runner_args(nrfutil) # No default arguments to provide.
|
|
@ -41,6 +41,7 @@ _names = [
|
|||
'misc',
|
||||
'nios2',
|
||||
'nrfjprog',
|
||||
'nrfutil',
|
||||
'nsim',
|
||||
'openocd',
|
||||
'pyocd',
|
||||
|
|
118
scripts/west_commands/runners/nrfutil.py
Normal file
118
scripts/west_commands/runners/nrfutil.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
# Copyright (c) 2023 Nordic Semiconductor ASA.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''Runner for flashing with nrfutil.'''
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from runners.nrf_common import NrfBinaryRunner
|
||||
|
||||
class NrfUtilBinaryRunner(NrfBinaryRunner):
|
||||
'''Runner front-end for nrfutil.'''
|
||||
|
||||
def __init__(self, cfg, family, softreset, dev_id, erase=False,
|
||||
tool_opt=[], force=False, recover=False):
|
||||
|
||||
super().__init__(cfg, family, softreset, dev_id, erase,
|
||||
tool_opt, force, recover)
|
||||
self._ops = []
|
||||
self._op_id = 1
|
||||
|
||||
@classmethod
|
||||
def name(cls):
|
||||
return 'nrfutil'
|
||||
|
||||
@classmethod
|
||||
def tool_opt_help(cls) -> str:
|
||||
return 'Additional options for nrfutil, e.g. "--log-level"'
|
||||
|
||||
@classmethod
|
||||
def do_create(cls, cfg, args):
|
||||
return NrfUtilBinaryRunner(cfg, args.nrf_family, args.softreset,
|
||||
args.dev_id, erase=args.erase,
|
||||
tool_opt=args.tool_opt, force=args.force,
|
||||
recover=args.recover)
|
||||
|
||||
def _exec(self, args):
|
||||
try:
|
||||
out = self.check_output(['nrfutil', '--json', 'device'] + args)
|
||||
except subprocess.CalledProcessError as e:
|
||||
# https://docs.python.org/3/reference/compound_stmts.html#except-clause
|
||||
cpe = e
|
||||
out = cpe.stdout
|
||||
else:
|
||||
cpe = None
|
||||
finally:
|
||||
# https://github.com/ndjson/ndjson-spec
|
||||
out = [json.loads(s) for s in
|
||||
out.decode(sys.getdefaultencoding()).split('\n') if len(s)]
|
||||
self.logger.debug(f'output: {out}')
|
||||
if cpe:
|
||||
if 'execute-batch' in args:
|
||||
for o in out:
|
||||
if o['type'] == 'batch_end' and o['data']['error']:
|
||||
cpe.returncode = o['data']['error']['code']
|
||||
raise cpe
|
||||
|
||||
return out
|
||||
|
||||
def do_get_boards(self):
|
||||
out = self._exec(['list'])
|
||||
devs = []
|
||||
for o in out:
|
||||
if o['type'] == 'task_end':
|
||||
devs = o['data']['data']['devices']
|
||||
snrs = [dev['serialNumber'] for dev in devs]
|
||||
|
||||
self.logger.debug(f'Found boards: {snrs}')
|
||||
return snrs
|
||||
|
||||
def do_require(self):
|
||||
self.require('nrfutil')
|
||||
|
||||
def _insert_op(self, op):
|
||||
op['operationId'] = f'{self._op_id}'
|
||||
self._op_id += 1
|
||||
self._ops.append(op)
|
||||
|
||||
def _exec_batch(self):
|
||||
# prepare the dictionary and convert to JSON
|
||||
batch = json.dumps({'family': f'{self.family}',
|
||||
'operations': [op for op in self._ops]},
|
||||
indent=4) + '\n'
|
||||
|
||||
hex_dir = Path(self.hex_).parent
|
||||
json_file = os.fspath(hex_dir / f'generated_nrfutil_batch.json')
|
||||
|
||||
with open(json_file, "w") as f:
|
||||
f.write(batch)
|
||||
|
||||
# reset first in case an exception is thrown
|
||||
self._ops = []
|
||||
self._op_id = 1
|
||||
self.logger.debug(f'Executing batch in: {json_file}')
|
||||
self._exec(['execute-batch', '--batch-path', f'{json_file}',
|
||||
'--serial-number', f'{self.dev_id}'])
|
||||
|
||||
def do_exec_op(self, op, force=False):
|
||||
self.logger.debug(f'Executing op: {op}')
|
||||
if force:
|
||||
if len(self._ops) != 0:
|
||||
raise RuntimeError(f'Forced exec with {len(self._ops)} ops')
|
||||
self._insert_op(op)
|
||||
self._exec_batch()
|
||||
return True
|
||||
# Defer by default
|
||||
return False
|
||||
|
||||
def flush_ops(self, force=True):
|
||||
if not force:
|
||||
return
|
||||
while self.ops:
|
||||
self._insert_op(self.ops.popleft())
|
||||
self._exec_batch()
|
Loading…
Reference in a new issue