zephyr/scripts/west_commands/runners/nrfjprog.py
Carles Cufi ff9c38316e scripts: runners: nrf: Use nrfutil error codes
Standardize to nrfutil error codes instead of nrfjprog ones. This is
because nrfutil has a wider range of functionality and will eventually
be the default runner for all nRF devices. It is also consistent with
the internal operation representation, which is now using the nrfutil
format already.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
2023-04-17 09:29:04 -07:00

100 lines
3.7 KiB
Python

# Copyright (c) 2017 Linaro Limited.
# Copyright (c) 2023 Nordic Semiconductor ASA.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing with nrfjprog.'''
import subprocess
import sys
from runners.nrf_common import ErrNotAvailableBecauseProtection, ErrVerify, \
NrfBinaryRunner
# https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrf_cltools%2FUG%2Fcltools%2Fnrf_nrfjprogexe_return_codes.html&cp=9_1_3_1
UnavailableOperationBecauseProtectionError = 16
VerifyError = 55
class NrfJprogBinaryRunner(NrfBinaryRunner):
'''Runner front-end for nrfjprog.'''
@classmethod
def name(cls):
return 'nrfjprog'
@classmethod
def tool_opt_help(cls) -> str:
return 'Additional options for nrfjprog, e.g. "--recover"'
@classmethod
def do_create(cls, cfg, args):
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
args.dev_id, erase=args.erase,
tool_opt=args.tool_opt, force=args.force,
recover=args.recover)
def do_get_boards(self):
snrs = self.check_output(['nrfjprog', '--ids'])
return snrs.decode(sys.getdefaultencoding()).strip().splitlines()
def do_require(self):
self.require('nrfjprog')
def do_exec_op(self, op, force=False):
self.logger.debug(f'Executing op: {op}')
# Translate the op
families = {'NRF51_FAMILY': 'NRF51', 'NRF52_FAMILY': 'NRF52',
'NRF53_FAMILY': 'NRF53', 'NRF91_FAMILY': 'NRF91'}
cores = {'NRFDL_DEVICE_CORE_APPLICATION': 'CP_APPLICATION',
'NRFDL_DEVICE_CORE_NETWORK': 'CP_NETWORK'}
core_opt = ['--coprocessor', cores[op['core']]] \
if op.get('core') else []
cmd = ['nrfjprog']
_op = op['operation']
op_type = _op['type']
# options that are an empty dict must use "in" instead of get()
if op_type == 'pinreset-enable':
cmd.append('--pinresetenable')
elif op_type == 'program':
cmd.append('--program')
cmd.append(_op['firmware']['file'])
erase = _op['chip_erase_mode']
if erase == 'ERASE_ALL':
cmd.append('--chiperase')
elif erase == 'ERASE_PAGES':
cmd.append('--sectorerase')
elif erase == 'ERASE_PAGES_INCLUDING_UICR':
cmd.append('--sectoranduicrerase')
else:
raise RuntimeError(f'Invalid erase mode: {erase}')
if _op.get('qspi_erase_mode'):
# In the future there might be multiple QSPI erase modes
cmd.append('--qspisectorerase')
if _op.get('verify'):
# In the future there might be multiple verify modes
cmd.append('--verify')
elif op_type == 'recover':
cmd.append('--recover')
elif op_type == 'reset':
if _op['option'] == 'RESET_SYSTEM':
cmd.append('--reset')
if _op['option'] == 'RESET_PIN':
cmd.append('--pinreset')
else:
raise RuntimeError(f'Invalid operation: {op_type}')
try:
self.check_call(cmd + ['-f', families[self.family]] + core_opt +
['--snr', self.dev_id] + self.tool_opt)
except subprocess.CalledProcessError as cpe:
# Translate error codes
if cpe.returncode == UnavailableOperationBecauseProtectionError:
cpe.returncode = ErrNotAvailableBecauseProtection
elif cpe.returncode == VerifyError:
cpe.returncode = ErrVerify
raise cpe
return True