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, pinreset, 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, pinreset, 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 capabilities(cls): 35 return NrfBinaryRunner._capabilities() 36 37 @classmethod 38 def dev_id_help(cls) -> str: 39 return NrfBinaryRunner._dev_id_help() 40 41 @classmethod 42 def tool_opt_help(cls) -> str: 43 return 'Additional options for nrfjprog, e.g. "--clockspeed"' 44 45 @classmethod 46 def do_create(cls, cfg, args): 47 return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset, 48 args.pinreset, args.dev_id, erase=args.erase, 49 reset=args.reset, 50 tool_opt=args.tool_opt, force=args.force, 51 recover=args.recover, qspi_ini=args.qspi_ini) 52 @classmethod 53 def do_add_parser(cls, parser): 54 super().do_add_parser(parser) 55 parser.add_argument('--qspiini', required=False, dest='qspi_ini', 56 help='path to an .ini file with qspi configuration') 57 58 def do_get_boards(self): 59 snrs = self.check_output(['nrfjprog', '--ids']) 60 return snrs.decode(sys.getdefaultencoding()).strip().splitlines() 61 62 def do_require(self): 63 self.require('nrfjprog') 64 65 def do_exec_op(self, op, force=False): 66 self.logger.debug(f'Executing op: {op}') 67 # Translate the op 68 69 families = {'nrf51': 'NRF51', 'nrf52': 'NRF52', 70 'nrf53': 'NRF53', 'nrf54l': 'NRF54L', 71 'nrf91': 'NRF91'} 72 cores = {'Application': 'CP_APPLICATION', 73 'Network': 'CP_NETWORK'} 74 75 core_opt = ['--coprocessor', cores[op['core']]] \ 76 if op.get('core') else [] 77 78 cmd = ['nrfjprog'] 79 _op = op['operation'] 80 op_type = _op['type'] 81 # options that are an empty dict must use "in" instead of get() 82 if op_type == 'pinreset-enable': 83 cmd.append('--pinresetenable') 84 elif op_type == 'program': 85 cmd.append('--program') 86 cmd.append(_op['firmware']['file']) 87 opts = _op['options'] 88 erase = opts['chip_erase_mode'] 89 if erase == 'ERASE_ALL': 90 cmd.append('--chiperase') 91 elif erase == 'ERASE_RANGES_TOUCHED_BY_FIRMWARE': 92 if self.family == 'nrf52': 93 cmd.append('--sectoranduicrerase') 94 else: 95 cmd.append('--sectorerase') 96 elif erase == 'NO_ERASE': 97 pass 98 else: 99 raise RuntimeError(f'Invalid erase mode: {erase}') 100 101 if opts.get('ext_mem_erase_mode'): 102 if opts['ext_mem_erase_mode'] == 'ERASE_RANGES_TOUCHED_BY_FIRMWARE': 103 cmd.append('--qspisectorerase') 104 elif opts['ext_mem_erase_mode'] == 'ERASE_ALL': 105 cmd.append('--qspichiperase') 106 107 if opts.get('verify'): 108 # In the future there might be multiple verify modes 109 cmd.append('--verify') 110 if self.qspi_ini: 111 cmd.append('--qspiini') 112 cmd.append(self.qspi_ini) 113 elif op_type == 'recover': 114 cmd.append('--recover') 115 elif op_type == 'reset': 116 if _op['kind'] == 'RESET_SYSTEM': 117 cmd.append('--reset') 118 if _op['kind'] == 'RESET_PIN': 119 cmd.append('--pinreset') 120 elif op_type == 'erase': 121 cmd.append(f'--erase{_op["kind"]}') 122 else: 123 raise RuntimeError(f'Invalid operation: {op_type}') 124 125 try: 126 self.check_call(cmd + ['-f', families[self.family]] + core_opt + 127 ['--snr', self.dev_id] + self.tool_opt) 128 except subprocess.CalledProcessError as cpe: 129 # Translate error codes 130 if cpe.returncode == UnavailableOperationBecauseProtectionError: 131 cpe.returncode = ErrNotAvailableBecauseProtection 132 elif cpe.returncode == VerifyError: 133 cpe.returncode = ErrVerify 134 raise cpe 135 return True 136