blob: 3ae03901b33758cf5216881e27e540cc60062bbc [file] [log] [blame]
# Copyright (c) 2017 Linaro Limited.
# Copyright (c) 2019 Nordic Semiconductor ASA.
#
# SPDX-License-Identifier: Apache-2.0
'''Runner for flashing with nrfjprog.'''
import os
import shlex
import sys
from re import fullmatch, escape
from runners.core import ZephyrBinaryRunner, RunnerCaps
class NrfJprogBinaryRunner(ZephyrBinaryRunner):
'''Runner front-end for nrfjprog.'''
def __init__(self, cfg, family, softreset, snr, erase=False,
tool_opt=[]):
super().__init__(cfg)
self.hex_ = cfg.hex_file
self.family = family
self.softreset = softreset
self.snr = snr
self.erase = erase
self.tool_opt = []
for opts in [shlex.split(opt) for opt in tool_opt]:
self.tool_opt += opts
@classmethod
def name(cls):
return 'nrfjprog'
@classmethod
def capabilities(cls):
return RunnerCaps(commands={'flash'}, erase=True)
@classmethod
def do_add_parser(cls, parser):
parser.add_argument('--nrf-family', required=True,
choices=['NRF51', 'NRF52', 'NRF53', 'NRF91'],
help='family of nRF MCU')
parser.add_argument('--softreset', required=False,
action='store_true',
help='use reset instead of pinreset')
parser.add_argument('--snr', required=False,
help="""Serial number of board to use.
'*' matches one or more characters/digits.""")
parser.add_argument('--tool-opt', default=[], action='append',
help='''Additional options for nrfjprog,
e.g. "--recover"''')
@classmethod
def do_create(cls, cfg, args):
return NrfJprogBinaryRunner(cfg, args.nrf_family, args.softreset,
args.snr, erase=args.erase,
tool_opt=args.tool_opt)
def ensure_snr(self):
if not self.snr or "*" in self.snr:
self.snr = self.get_board_snr(self.snr or "*")
def get_boards(self):
snrs = self.check_output(['nrfjprog', '--ids'])
snrs = snrs.decode(sys.getdefaultencoding()).strip().splitlines()
if not snrs:
raise RuntimeError('"nrfjprog --ids" did not find a board; '
'is the board connected?')
return snrs
@staticmethod
def verify_snr(snr):
if snr == '0':
raise RuntimeError('"nrfjprog --ids" returned 0; '
'is a debugger already connected?')
def get_board_snr(self, glob):
# Use nrfjprog --ids to discover connected boards.
#
# If there's exactly one board connected, it's safe to assume
# the user wants that one. Otherwise, bail unless there are
# multiple boards and we are connected to a terminal, in which
# case use print() and input() to ask what the user wants.
re_glob = escape(glob).replace(r"\*", ".+")
snrs = [snr for snr in self.get_boards() if fullmatch(re_glob, snr)]
if len(snrs) == 0:
raise RuntimeError(
'There are no boards connected{}.'.format(
f" matching '{glob}'" if glob != "*" else ""))
elif len(snrs) == 1:
board_snr = snrs[0]
self.verify_snr(board_snr)
print("Using board {}".format(board_snr))
return board_snr
elif not sys.stdin.isatty():
raise RuntimeError(
f'refusing to guess which of {len(snrs)} '
'connected boards to use. (Interactive prompts '
'disabled since standard input is not a terminal.) '
'Please specify a serial number on the command line.')
snrs = sorted(snrs)
print('There are multiple boards connected{}.'.format(
f" matching '{glob}'" if glob != "*" else ""))
for i, snr in enumerate(snrs, 1):
print('{}. {}'.format(i, snr))
p = 'Please select one with desired serial number (1-{}): '.format(
len(snrs))
while True:
try:
value = input(p)
except EOFError:
sys.exit(0)
try:
value = int(value)
except ValueError:
continue
if 1 <= value <= len(snrs):
break
return snrs[value - 1]
def do_run(self, command, **kwargs):
self.require('nrfjprog')
self.ensure_snr()
commands = []
board_snr = self.snr.lstrip("0")
if not os.path.isfile(self.hex_):
raise ValueError('Cannot flash; hex file ({}) does not exist. '.
format(self.hex_) +
'Try enabling CONFIG_BUILD_OUTPUT_HEX.')
program_cmd = ['nrfjprog', '--program', self.hex_, '-f', self.family,
'--snr', board_snr] + self.tool_opt
self.logger.info('Flashing file: {}'.format(self.hex_))
if self.erase:
commands.extend([
['nrfjprog',
'--eraseall',
'-f', self.family,
'--snr', board_snr],
program_cmd
])
else:
if self.family == 'NRF52':
commands.append(program_cmd + ['--sectoranduicrerase'])
else:
commands.append(program_cmd + ['--sectorerase'])
if self.family == 'NRF52' and not self.softreset:
commands.extend([
# Enable pin reset
['nrfjprog', '--pinresetenable', '-f', self.family,
'--snr', board_snr],
])
if self.softreset:
commands.append(['nrfjprog', '--reset', '-f', self.family,
'--snr', board_snr])
else:
commands.append(['nrfjprog', '--pinreset', '-f', self.family,
'--snr', board_snr])
for cmd in commands:
self.check_call(cmd)
self.logger.info('Board with serial number {} flashed successfully.'.
format(board_snr))