mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-07 22:35:21 +00:00
We should simply exit if the user hits control-D during the prompt. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
178 lines
6.2 KiB
Python
178 lines
6.2 KiB
Python
# 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))
|