This command runs separately from a debug server, instead of attaching to a running server. This is both the easiest out of the box experience, and also should be possible to implement consistently for most runners. This commit includes an initial implementation for pyocd. Signed-off-by: Tobias Pisani <mail@topisani.dev>
239 lines
8.4 KiB
Python
239 lines
8.4 KiB
Python
# Copyright (c) 2017 Linaro Limited.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
'''Runner for pyOCD .'''
|
|
|
|
import os
|
|
from os import path
|
|
|
|
from runners.core import ZephyrBinaryRunner, RunnerCaps, BuildConfiguration
|
|
|
|
DEFAULT_PYOCD_GDB_PORT = 3333
|
|
DEFAULT_PYOCD_TELNET_PORT = 4444
|
|
|
|
|
|
class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
|
'''Runner front-end for pyOCD.'''
|
|
|
|
def __init__(self, cfg, target,
|
|
pyocd='pyocd',
|
|
dev_id=None, flash_addr=0x0, erase=False, flash_opts=None,
|
|
gdb_port=DEFAULT_PYOCD_GDB_PORT,
|
|
telnet_port=DEFAULT_PYOCD_TELNET_PORT, tui=False,
|
|
pyocd_config=None,
|
|
daparg=None, frequency=None, tool_opt=None):
|
|
super().__init__(cfg)
|
|
|
|
default = path.join(cfg.board_dir, 'support', 'pyocd.yaml')
|
|
if path.exists(default):
|
|
self.pyocd_config = default
|
|
else:
|
|
self.pyocd_config = None
|
|
|
|
|
|
self.target_args = ['-t', target]
|
|
self.pyocd = pyocd
|
|
self.flash_addr_args = ['-a', hex(flash_addr)] if flash_addr else []
|
|
self.erase = erase
|
|
self.gdb_cmd = [cfg.gdb] if cfg.gdb is not None else None
|
|
self.gdb_port = gdb_port
|
|
self.telnet_port = telnet_port
|
|
self.tui_args = ['-tui'] if tui else []
|
|
self.hex_name = cfg.hex_file
|
|
self.bin_name = cfg.bin_file
|
|
self.elf_name = cfg.elf_file
|
|
|
|
pyocd_config_args = []
|
|
|
|
if self.pyocd_config is not None:
|
|
pyocd_config_args = ['--config', self.pyocd_config]
|
|
|
|
self.pyocd_config_args = pyocd_config_args
|
|
|
|
board_args = []
|
|
if dev_id is not None:
|
|
board_args = ['-u', dev_id]
|
|
self.board_args = board_args
|
|
|
|
daparg_args = []
|
|
if daparg is not None:
|
|
daparg_args = ['-da', daparg]
|
|
self.daparg_args = daparg_args
|
|
|
|
frequency_args = []
|
|
if frequency is not None:
|
|
frequency_args = ['-f', frequency]
|
|
self.frequency_args = frequency_args
|
|
|
|
self.tool_opt_args = tool_opt or []
|
|
|
|
self.flash_extra = flash_opts if flash_opts else []
|
|
|
|
@classmethod
|
|
def name(cls):
|
|
return 'pyocd'
|
|
|
|
@classmethod
|
|
def capabilities(cls):
|
|
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach', 'rtt'},
|
|
dev_id=True, flash_addr=True, erase=True,
|
|
tool_opt=True, rtt=True)
|
|
|
|
@classmethod
|
|
def dev_id_help(cls) -> str:
|
|
return '''Device identifier. Use it to select the probe's unique ID
|
|
or substring thereof.'''
|
|
|
|
@classmethod
|
|
def do_add_parser(cls, parser):
|
|
parser.add_argument('--target', required=True,
|
|
help='target override')
|
|
|
|
parser.add_argument('--daparg',
|
|
help='Additional -da arguments to pyocd tool')
|
|
parser.add_argument('--pyocd', default='pyocd',
|
|
help='path to pyocd tool, default is pyocd')
|
|
parser.add_argument('--flash-opt', default=[], action='append',
|
|
help='''Additional options for pyocd flash,
|
|
e.g. --flash-opt="-e=chip" to chip erase''')
|
|
parser.add_argument('--frequency',
|
|
help='SWD clock frequency in Hz')
|
|
parser.add_argument('--gdb-port', default=DEFAULT_PYOCD_GDB_PORT,
|
|
help='pyocd gdb port, defaults to {}'.format(
|
|
DEFAULT_PYOCD_GDB_PORT))
|
|
parser.add_argument('--telnet-port', default=DEFAULT_PYOCD_TELNET_PORT,
|
|
help='pyocd telnet port, defaults to {}'.format(
|
|
DEFAULT_PYOCD_TELNET_PORT))
|
|
parser.add_argument('--tui', default=False, action='store_true',
|
|
help='if given, GDB uses -tui')
|
|
parser.add_argument('--board-id', dest='dev_id',
|
|
help='obsolete synonym for -i/--dev-id')
|
|
|
|
@classmethod
|
|
def tool_opt_help(cls) -> str:
|
|
return """Additional options for pyocd commander,
|
|
e.g. '--script=user.py'"""
|
|
|
|
@classmethod
|
|
def do_create(cls, cfg, args):
|
|
build_conf = BuildConfiguration(cfg.build_dir)
|
|
flash_addr = cls.get_flash_address(args, build_conf)
|
|
|
|
ret = PyOcdBinaryRunner(
|
|
cfg, args.target,
|
|
pyocd=args.pyocd,
|
|
flash_addr=flash_addr, erase=args.erase, flash_opts=args.flash_opt,
|
|
gdb_port=args.gdb_port, telnet_port=args.telnet_port, tui=args.tui,
|
|
dev_id=args.dev_id, daparg=args.daparg,
|
|
frequency=args.frequency,
|
|
tool_opt=args.tool_opt)
|
|
|
|
daparg = os.environ.get('PYOCD_DAPARG')
|
|
if not ret.daparg_args and daparg:
|
|
ret.logger.warning('PYOCD_DAPARG is deprecated; use --daparg')
|
|
ret.logger.debug('--daparg={} via PYOCD_DAPARG'.format(daparg))
|
|
ret.daparg_args = ['-da', daparg]
|
|
|
|
return ret
|
|
|
|
def port_args(self):
|
|
return ['-p', str(self.gdb_port), '-T', str(self.telnet_port)]
|
|
|
|
def do_run(self, command, **kwargs):
|
|
self.require(self.pyocd)
|
|
if command == 'rtt':
|
|
self.rtt(**kwargs)
|
|
elif command == 'flash':
|
|
self.flash(**kwargs)
|
|
else:
|
|
self.debug_debugserver(command, **kwargs)
|
|
|
|
def flash(self, **kwargs):
|
|
# Use hex, bin or elf file provided by the buildsystem.
|
|
# Preferring .hex over .bin and .elf
|
|
if self.hex_name is not None and os.path.isfile(self.hex_name):
|
|
fname = self.hex_name
|
|
# Preferring .bin over .elf
|
|
elif self.bin_name is not None and os.path.isfile(self.bin_name):
|
|
fname = self.bin_name
|
|
elif self.elf_name is not None and os.path.isfile(self.elf_name):
|
|
fname = self.elf_name
|
|
else:
|
|
raise ValueError(
|
|
'Cannot flash; no hex ({}), bin ({}) or elf ({}) files found. '.format(
|
|
self.hex_name, self.bin_name, self.elf_name))
|
|
|
|
erase_method = 'chip' if self.erase else 'sector'
|
|
|
|
cmd = ([self.pyocd] +
|
|
['flash'] +
|
|
self.pyocd_config_args +
|
|
['-e', erase_method] +
|
|
self.flash_addr_args +
|
|
self.daparg_args +
|
|
self.target_args +
|
|
self.board_args +
|
|
self.frequency_args +
|
|
self.tool_opt_args +
|
|
self.flash_extra +
|
|
[fname])
|
|
|
|
self.logger.info('Flashing file: {}'.format(fname))
|
|
self.check_call(cmd)
|
|
|
|
def log_gdbserver_message(self):
|
|
self.logger.info('pyOCD GDB server running on port {}'.
|
|
format(self.gdb_port))
|
|
|
|
def debug_debugserver(self, command, **kwargs):
|
|
server_cmd = ([self.pyocd] +
|
|
['gdbserver'] +
|
|
self.daparg_args +
|
|
self.port_args() +
|
|
self.target_args +
|
|
self.board_args +
|
|
self.frequency_args +
|
|
self.tool_opt_args)
|
|
|
|
if command == 'debugserver':
|
|
self.log_gdbserver_message()
|
|
self.check_call(server_cmd)
|
|
else:
|
|
if self.gdb_cmd is None:
|
|
raise ValueError('Cannot debug; gdb is missing')
|
|
if self.elf_name is None:
|
|
raise ValueError('Cannot debug; elf is missing')
|
|
client_cmd = (self.gdb_cmd +
|
|
self.tui_args +
|
|
[self.elf_name] +
|
|
['-ex', 'target remote :{}'.format(self.gdb_port)])
|
|
if command == 'debug':
|
|
client_cmd += ['-ex', 'monitor halt',
|
|
'-ex', 'monitor reset',
|
|
'-ex', 'load']
|
|
|
|
self.require(client_cmd[0])
|
|
self.log_gdbserver_message()
|
|
self.run_server_and_client(server_cmd, client_cmd)
|
|
|
|
|
|
def rtt(self):
|
|
rtt_addr = self.get_rtt_address()
|
|
if rtt_addr is None:
|
|
raise ValueError('RTT control block not found')
|
|
|
|
self.logger.debug(f'rtt address: 0x{rtt_addr:x}')
|
|
|
|
cmd = ([self.pyocd] +
|
|
['rtt'] +
|
|
self.pyocd_config_args +
|
|
self.daparg_args +
|
|
self.target_args +
|
|
self.board_args +
|
|
self.frequency_args +
|
|
self.tool_opt_args +
|
|
['-a', f'0x{rtt_addr:x}'])
|
|
|
|
self.check_call(cmd)
|