You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
5.2 KiB
138 lines
5.2 KiB
# 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, pinreset, dev_id, erase=False, |
|
erase_mode=None, ext_erase_mode=None, reset=True, tool_opt=None, |
|
force=False, recover=False, qspi_ini=None): |
|
|
|
super().__init__(cfg, family, softreset, pinreset, dev_id, erase, |
|
erase_mode, ext_erase_mode, reset, tool_opt, force, |
|
recover) |
|
|
|
self.qspi_ini = qspi_ini |
|
|
|
@classmethod |
|
def name(cls): |
|
return 'nrfjprog' |
|
|
|
@classmethod |
|
def capabilities(cls): |
|
return NrfBinaryRunner._capabilities() |
|
|
|
@classmethod |
|
def dev_id_help(cls) -> str: |
|
return NrfBinaryRunner._dev_id_help() |
|
|
|
@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.pinreset, args.dev_id, erase=args.erase, |
|
erase_mode=args.erase_mode, |
|
ext_erase_mode=args.ext_erase_mode, |
|
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': 'NRF51', 'nrf52': 'NRF52', |
|
'nrf53': 'NRF53', 'nrf54l': 'NRF54L', |
|
'nrf91': 'NRF91'} |
|
cores = {'Application': 'CP_APPLICATION', |
|
'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']) |
|
opts = _op['options'] |
|
erase = opts['chip_erase_mode'] |
|
if erase == 'ERASE_ALL': |
|
cmd.append('--chiperase') |
|
elif erase == 'ERASE_RANGES_TOUCHED_BY_FIRMWARE': |
|
if self.family == 'nrf52': |
|
cmd.append('--sectoranduicrerase') |
|
else: |
|
cmd.append('--sectorerase') |
|
elif erase == 'ERASE_NONE': |
|
pass |
|
else: |
|
raise RuntimeError(f'Invalid erase mode: {erase}') |
|
|
|
if opts.get('ext_mem_erase_mode'): |
|
if opts['ext_mem_erase_mode'] == 'ERASE_RANGES_TOUCHED_BY_FIRMWARE': |
|
cmd.append('--qspisectorerase') |
|
elif opts['ext_mem_erase_mode'] == 'ERASE_ALL': |
|
cmd.append('--qspichiperase') |
|
|
|
if opts.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['kind'] == 'RESET_SYSTEM': |
|
cmd.append('--reset') |
|
if _op['kind'] == 'RESET_PIN': |
|
cmd.append('--pinreset') |
|
elif op_type == 'erase': |
|
cmd.append(f'--erase{_op["kind"]}') |
|
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
|
|
|