west: runners: Add `west rtt` command with pyocd implementation
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>
This commit is contained in:
parent
74bb5b334d
commit
b3b8360f39
|
@ -46,6 +46,9 @@ west-commands:
|
|||
- name: attach
|
||||
class: Attach
|
||||
help: interactively debug a board
|
||||
- name: rtt
|
||||
class: Rtt
|
||||
help: open an rtt shell
|
||||
- file: scripts/west_commands/export.py
|
||||
commands:
|
||||
- name: zephyr-export
|
||||
|
|
|
@ -74,3 +74,21 @@ class Attach(WestCommand):
|
|||
|
||||
def do_run(self, my_args, runner_args):
|
||||
do_run_common(self, my_args, runner_args)
|
||||
|
||||
|
||||
class Rtt(WestCommand):
|
||||
|
||||
def __init__(self):
|
||||
super(Rtt, self).__init__(
|
||||
'rtt',
|
||||
# Keep this in sync with the string in west-commands.yml.
|
||||
'open an rtt shell',
|
||||
"",
|
||||
accepts_unknown_args=True)
|
||||
self.runner_key = 'rtt-runner' # in runners.yaml
|
||||
|
||||
def do_add_parser(self, parser_adder):
|
||||
return add_parser_common(self, parser_adder)
|
||||
|
||||
def do_run(self, my_args, runner_args):
|
||||
do_run_common(self, my_args, runner_args)
|
||||
|
|
|
@ -628,7 +628,8 @@ def get_runner_config(build_dir, yaml_path, runners_yaml, args=None):
|
|||
filetype('file_type'),
|
||||
config('gdb'),
|
||||
config('openocd'),
|
||||
config('openocd_search', []))
|
||||
config('openocd_search', []),
|
||||
config('rtt_address'))
|
||||
|
||||
def dump_traceback():
|
||||
# Save the current exception to a file and return its path.
|
||||
|
|
|
@ -29,6 +29,13 @@ from inspect import isabstract
|
|||
from typing import Dict, List, NamedTuple, NoReturn, Optional, Set, Type, \
|
||||
Union
|
||||
|
||||
try:
|
||||
from elftools.elf.elffile import ELFFile
|
||||
ELFTOOLS_MISSING = False
|
||||
except ImportError:
|
||||
ELFTOOLS_MISSING = True
|
||||
|
||||
|
||||
# Turn on to enable just logging the commands that would be run (at
|
||||
# info rather than debug level), without actually running them. This
|
||||
# can break runners that are expecting output or if one command
|
||||
|
@ -37,6 +44,27 @@ _DRY_RUN = False
|
|||
|
||||
_logger = logging.getLogger('runners')
|
||||
|
||||
# FIXME: I assume this code belongs somewhere else, but i couldn't figure out
|
||||
# a good location for it, so i put it here for now
|
||||
# We could potentially search for RTT blocks in hex or bin files as well,
|
||||
# but since the magic string is "SEGGER RTT", i thought it might be better
|
||||
# to avoid, at the risk of false positives.
|
||||
def find_rtt_block(elf_file: str) -> Optional[int]:
|
||||
if ELFTOOLS_MISSING:
|
||||
raise RuntimeError('the Python dependency elftools was missing; '
|
||||
'see the getting started guide for details on '
|
||||
'how to fix')
|
||||
|
||||
with open(elf_file, 'rb') as f:
|
||||
elffile = ELFFile(f)
|
||||
for sect in elffile.iter_sections('SHT_SYMTAB'):
|
||||
symbols = sect.get_symbol_by_name('_SEGGER_RTT')
|
||||
if symbols is None:
|
||||
continue
|
||||
for s in symbols:
|
||||
return s.entry.get('st_value')
|
||||
return None
|
||||
|
||||
|
||||
class _DebugDummyPopen:
|
||||
|
||||
|
@ -219,7 +247,7 @@ class MissingProgram(FileNotFoundError):
|
|||
super().__init__(errno.ENOENT, os.strerror(errno.ENOENT), program)
|
||||
|
||||
|
||||
_RUNNERCAPS_COMMANDS = {'flash', 'debug', 'debugserver', 'attach', 'simulate', 'robot'}
|
||||
_RUNNERCAPS_COMMANDS = {'flash', 'debug', 'debugserver', 'attach', 'simulate', 'robot', 'rtt'}
|
||||
|
||||
@dataclass
|
||||
class RunnerCaps:
|
||||
|
@ -231,7 +259,7 @@ class RunnerCaps:
|
|||
Available capabilities:
|
||||
|
||||
- commands: set of supported commands; default is {'flash',
|
||||
'debug', 'debugserver', 'attach', 'simulate', 'robot'}.
|
||||
'debug', 'debugserver', 'attach', 'simulate', 'robot', 'rtt'}.
|
||||
|
||||
- dev_id: whether the runner supports device identifiers, in the form of an
|
||||
-i, --dev-id option. This is useful when the user has multiple debuggers
|
||||
|
@ -268,6 +296,9 @@ class RunnerCaps:
|
|||
discovered in the build directory.
|
||||
|
||||
- hide_load_files: whether the elf/hex/bin file arguments should be hidden.
|
||||
|
||||
- rtt: whether the runner supports SEGGER RTT. This adds a --rtt-address
|
||||
option.
|
||||
'''
|
||||
|
||||
commands: Set[str] = field(default_factory=lambda: set(_RUNNERCAPS_COMMANDS))
|
||||
|
@ -279,6 +310,8 @@ class RunnerCaps:
|
|||
tool_opt: bool = False
|
||||
file: bool = False
|
||||
hide_load_files: bool = False
|
||||
rtt: bool = False # This capability exists separately from the rtt command
|
||||
# to allow other commands to use the rtt address
|
||||
|
||||
def __post_init__(self):
|
||||
if not self.commands.issubset(_RUNNERCAPS_COMMANDS):
|
||||
|
@ -319,6 +352,7 @@ class RunnerConfig(NamedTuple):
|
|||
gdb: Optional[str] = None # path to a usable gdb
|
||||
openocd: Optional[str] = None # path to a usable openocd
|
||||
openocd_search: List[str] = [] # add these paths to the openocd search path
|
||||
rtt_address: Optional[int] = None # address of the rtt control block
|
||||
|
||||
|
||||
_YN_CHOICES = ['Y', 'y', 'N', 'n', 'yes', 'no', 'YES', 'NO']
|
||||
|
@ -572,6 +606,13 @@ class ZephyrBinaryRunner(abc.ABC):
|
|||
help=(cls.tool_opt_help() if caps.tool_opt
|
||||
else argparse.SUPPRESS))
|
||||
|
||||
if caps.rtt:
|
||||
parser.add_argument('--rtt-address', dest='rtt_address',
|
||||
type=lambda x: int(x, 0),
|
||||
help="address of RTT control block. If not supplied, it will be autodetected if possible")
|
||||
else:
|
||||
parser.add_argument('--rtt-address', help=argparse.SUPPRESS)
|
||||
|
||||
# Runner-specific options.
|
||||
cls.do_add_parser(parser)
|
||||
|
||||
|
@ -607,6 +648,8 @@ class ZephyrBinaryRunner(abc.ABC):
|
|||
raise ValueError("--file-type requires --file")
|
||||
if args.file_type and not caps.file:
|
||||
_missing_cap(cls, '--file-type')
|
||||
if args.rtt_address and not caps.rtt:
|
||||
_missing_cap(cls, '--rtt-address')
|
||||
|
||||
ret = cls.do_create(cfg, args)
|
||||
if args.erase:
|
||||
|
@ -731,6 +774,19 @@ class ZephyrBinaryRunner(abc.ABC):
|
|||
raise MissingProgram(program)
|
||||
return ret
|
||||
|
||||
def get_rtt_address(self) -> int | None:
|
||||
'''Helper method for extracting a the RTT control block address.
|
||||
|
||||
If args.rtt_address was supplied, returns that.
|
||||
|
||||
Otherwise, attempt to locate an rtt block in the elf file.
|
||||
If this is not found, None is returned'''
|
||||
if self.cfg.rtt_address is not None:
|
||||
return self.cfg.rtt_address
|
||||
elif self.cfg.elf_file is not None:
|
||||
return find_rtt_block(self.cfg.elf_file)
|
||||
return None
|
||||
|
||||
def run_server_and_client(self, server, client, **kwargs):
|
||||
'''Run a server that ignores SIGINT, and a client that handles it.
|
||||
|
||||
|
|
|
@ -76,9 +76,9 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
|||
|
||||
@classmethod
|
||||
def capabilities(cls):
|
||||
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
|
||||
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach', 'rtt'},
|
||||
dev_id=True, flash_addr=True, erase=True,
|
||||
tool_opt=True)
|
||||
tool_opt=True, rtt=True)
|
||||
|
||||
@classmethod
|
||||
def dev_id_help(cls) -> str:
|
||||
|
@ -142,7 +142,9 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
|||
|
||||
def do_run(self, command, **kwargs):
|
||||
self.require(self.pyocd)
|
||||
if command == 'flash':
|
||||
if command == 'rtt':
|
||||
self.rtt(**kwargs)
|
||||
elif command == 'flash':
|
||||
self.flash(**kwargs)
|
||||
else:
|
||||
self.debug_debugserver(command, **kwargs)
|
||||
|
@ -214,3 +216,23 @@ class PyOcdBinaryRunner(ZephyrBinaryRunner):
|
|||
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)
|
||||
|
|
Loading…
Reference in New Issue