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