blob: ce135222ad0b64fdefe6694ecc5ca273062a96b3 [file] [log] [blame]
# 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.'''
def __init__(self, cfg, family, softreset, dev_id, erase=False,
reset=True, tool_opt=[], force=False, recover=False,
qspi_ini=None):
super().__init__(cfg, family, softreset, dev_id, erase, reset,
tool_opt, force, recover)
self.qspi_ini = qspi_ini
@classmethod
def name(cls):
return 'nrfjprog'
@classmethod
def tool_opt_help(cls) -> str:
return 'Additional options for nrfjprog, e.g. "--clockspeed"'
@classmethod
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, qspi_ini=args.qspi_ini)
@classmethod
def do_add_parser(cls, parser):
super().do_add_parser(parser)
parser.add_argument('--qspiini', required=False, dest='qspi_ini',
help='path to an .ini file with qspi configuration')
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', 'NRF54L_FAMILY': 'NRF54L',
'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')
elif erase == 'NO_ERASE':
pass
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')
if self.qspi_ini:
cmd.append('--qspiini')
cmd.append(self.qspi_ini)
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')
elif op_type == 'erasepage':
cmd.append('--erasepage')
cmd.append(f"0x{_op['page']:08x}")
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