1# Copyright (c) 2017 Linaro Limited. 2# Copyright (c) 2023 Nordic Semiconductor ASA. 3# 4# SPDX-License-Identifier: Apache-2.0 5 6'''Runner for flashing with nrfjprog.''' 7 8import subprocess 9import sys 10 11from runners.nrf_common import ErrNotAvailableBecauseProtection, ErrVerify, NrfBinaryRunner 12 13# https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_nrf_cltools%2FUG%2Fcltools%2Fnrf_nrfjprogexe_return_codes.html&cp=9_1_3_1 14UnavailableOperationBecauseProtectionError = 16 15VerifyError = 55 16 17class NrfJprogBinaryRunner(NrfBinaryRunner): 18 '''Runner front-end for nrfjprog.''' 19 20 def __init__(self, cfg, family, softreset, dev_id, erase=False, 21 reset=True, tool_opt=None, force=False, recover=False, 22 qspi_ini=None): 23 24 super().__init__(cfg, family, softreset, dev_id, erase, reset, 25 tool_opt, force, recover) 26 27 self.qspi_ini = qspi_ini 28 29 @classmethod 30 def name(cls): 31 return 'nrfjprog' 32 33 @classmethod 34 def tool_opt_help(cls) -> str: 35 return 'Additional options for nrfjprog, e.g. "--clockspeed"' 36 37 @classmethod 38 def do_create(cls, cfg, args): 39 return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset, 40 args.dev_id, erase=args.erase, 41 reset=args.reset, 42 tool_opt=args.tool_opt, force=args.force, 43 recover=args.recover, qspi_ini=args.qspi_ini) 44 @classmethod 45 def do_add_parser(cls, parser): 46 super().do_add_parser(parser) 47 parser.add_argument('--qspiini', required=False, dest='qspi_ini', 48 help='path to an .ini file with qspi configuration') 49 50 def do_get_boards(self): 51 snrs = self.check_output(['nrfjprog', '--ids']) 52 return snrs.decode(sys.getdefaultencoding()).strip().splitlines() 53 54 def do_require(self): 55 self.require('nrfjprog') 56 57 def do_exec_op(self, op, force=False): 58 self.logger.debug(f'Executing op: {op}') 59 # Translate the op 60 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'} 66 67 core_opt = ['--coprocessor', cores[op['core']]] \ 68 if op.get('core') else [] 69 70 cmd = ['nrfjprog'] 71 _op = op['operation'] 72 op_type = _op['type'] 73 # options that are an empty dict must use "in" instead of get() 74 if op_type == 'pinreset-enable': 75 cmd.append('--pinresetenable') 76 elif op_type == 'program': 77 cmd.append('--program') 78 cmd.append(_op['firmware']['file']) 79 erase = _op['chip_erase_mode'] 80 if erase == 'ERASE_ALL': 81 cmd.append('--chiperase') 82 elif erase == 'ERASE_PAGES': 83 cmd.append('--sectorerase') 84 elif erase == 'ERASE_PAGES_INCLUDING_UICR': 85 cmd.append('--sectoranduicrerase') 86 elif erase == 'NO_ERASE': 87 pass 88 else: 89 raise RuntimeError(f'Invalid erase mode: {erase}') 90 91 if _op.get('qspi_erase_mode'): 92 # In the future there might be multiple QSPI erase modes 93 cmd.append('--qspisectorerase') 94 if _op.get('verify'): 95 # In the future there might be multiple verify modes 96 cmd.append('--verify') 97 if self.qspi_ini: 98 cmd.append('--qspiini') 99 cmd.append(self.qspi_ini) 100 elif op_type == 'recover': 101 cmd.append('--recover') 102 elif op_type == 'reset': 103 if _op['option'] == 'RESET_SYSTEM': 104 cmd.append('--reset') 105 if _op['option'] == 'RESET_PIN': 106 cmd.append('--pinreset') 107 elif op_type == 'erasepage': 108 cmd.append('--erasepage') 109 cmd.append(f"0x{_op['page']:08x}") 110 else: 111 raise RuntimeError(f'Invalid operation: {op_type}') 112 113 try: 114 self.check_call(cmd + ['-f', families[self.family]] + core_opt + 115 ['--snr', self.dev_id] + self.tool_opt) 116 except subprocess.CalledProcessError as cpe: 117 # Translate error codes 118 if cpe.returncode == UnavailableOperationBecauseProtectionError: 119 cpe.returncode = ErrNotAvailableBecauseProtection 120 elif cpe.returncode == VerifyError: 121 cpe.returncode = ErrVerify 122 raise cpe 123 return True 124