Skip to content

Commit b3b8360

Browse files
topisaninashif
authored andcommitted
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 <[email protected]>
1 parent 74bb5b3 commit b3b8360

File tree

5 files changed

+106
-6
lines changed

5 files changed

+106
-6
lines changed

scripts/west-commands.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ west-commands:
4646
- name: attach
4747
class: Attach
4848
help: interactively debug a board
49+
- name: rtt
50+
class: Rtt
51+
help: open an rtt shell
4952
- file: scripts/west_commands/export.py
5053
commands:
5154
- name: zephyr-export

scripts/west_commands/debug.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,21 @@ def do_add_parser(self, parser_adder):
7474

7575
def do_run(self, my_args, runner_args):
7676
do_run_common(self, my_args, runner_args)
77+
78+
79+
class Rtt(WestCommand):
80+
81+
def __init__(self):
82+
super(Rtt, self).__init__(
83+
'rtt',
84+
# Keep this in sync with the string in west-commands.yml.
85+
'open an rtt shell',
86+
"",
87+
accepts_unknown_args=True)
88+
self.runner_key = 'rtt-runner' # in runners.yaml
89+
90+
def do_add_parser(self, parser_adder):
91+
return add_parser_common(self, parser_adder)
92+
93+
def do_run(self, my_args, runner_args):
94+
do_run_common(self, my_args, runner_args)

scripts/west_commands/run_common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ def filetype(attr):
628628
filetype('file_type'),
629629
config('gdb'),
630630
config('openocd'),
631-
config('openocd_search', []))
631+
config('openocd_search', []),
632+
config('rtt_address'))
632633

633634
def dump_traceback():
634635
# Save the current exception to a file and return its path.

scripts/west_commands/runners/core.py

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
from typing import Dict, List, NamedTuple, NoReturn, Optional, Set, Type, \
3030
Union
3131

32+
try:
33+
from elftools.elf.elffile import ELFFile
34+
ELFTOOLS_MISSING = False
35+
except ImportError:
36+
ELFTOOLS_MISSING = True
37+
38+
3239
# Turn on to enable just logging the commands that would be run (at
3340
# info rather than debug level), without actually running them. This
3441
# can break runners that are expecting output or if one command
@@ -37,6 +44,27 @@
3744

3845
_logger = logging.getLogger('runners')
3946

47+
# FIXME: I assume this code belongs somewhere else, but i couldn't figure out
48+
# a good location for it, so i put it here for now
49+
# We could potentially search for RTT blocks in hex or bin files as well,
50+
# but since the magic string is "SEGGER RTT", i thought it might be better
51+
# to avoid, at the risk of false positives.
52+
def find_rtt_block(elf_file: str) -> Optional[int]:
53+
if ELFTOOLS_MISSING:
54+
raise RuntimeError('the Python dependency elftools was missing; '
55+
'see the getting started guide for details on '
56+
'how to fix')
57+
58+
with open(elf_file, 'rb') as f:
59+
elffile = ELFFile(f)
60+
for sect in elffile.iter_sections('SHT_SYMTAB'):
61+
symbols = sect.get_symbol_by_name('_SEGGER_RTT')
62+
if symbols is None:
63+
continue
64+
for s in symbols:
65+
return s.entry.get('st_value')
66+
return None
67+
4068

4169
class _DebugDummyPopen:
4270

@@ -219,7 +247,7 @@ def __init__(self, program):
219247
super().__init__(errno.ENOENT, os.strerror(errno.ENOENT), program)
220248

221249

222-
_RUNNERCAPS_COMMANDS = {'flash', 'debug', 'debugserver', 'attach', 'simulate', 'robot'}
250+
_RUNNERCAPS_COMMANDS = {'flash', 'debug', 'debugserver', 'attach', 'simulate', 'robot', 'rtt'}
223251

224252
@dataclass
225253
class RunnerCaps:
@@ -231,7 +259,7 @@ class RunnerCaps:
231259
Available capabilities:
232260
233261
- commands: set of supported commands; default is {'flash',
234-
'debug', 'debugserver', 'attach', 'simulate', 'robot'}.
262+
'debug', 'debugserver', 'attach', 'simulate', 'robot', 'rtt'}.
235263
236264
- dev_id: whether the runner supports device identifiers, in the form of an
237265
-i, --dev-id option. This is useful when the user has multiple debuggers
@@ -268,6 +296,9 @@ class RunnerCaps:
268296
discovered in the build directory.
269297
270298
- hide_load_files: whether the elf/hex/bin file arguments should be hidden.
299+
300+
- rtt: whether the runner supports SEGGER RTT. This adds a --rtt-address
301+
option.
271302
'''
272303

273304
commands: Set[str] = field(default_factory=lambda: set(_RUNNERCAPS_COMMANDS))
@@ -279,6 +310,8 @@ class RunnerCaps:
279310
tool_opt: bool = False
280311
file: bool = False
281312
hide_load_files: bool = False
313+
rtt: bool = False # This capability exists separately from the rtt command
314+
# to allow other commands to use the rtt address
282315

283316
def __post_init__(self):
284317
if not self.commands.issubset(_RUNNERCAPS_COMMANDS):
@@ -319,6 +352,7 @@ class RunnerConfig(NamedTuple):
319352
gdb: Optional[str] = None # path to a usable gdb
320353
openocd: Optional[str] = None # path to a usable openocd
321354
openocd_search: List[str] = [] # add these paths to the openocd search path
355+
rtt_address: Optional[int] = None # address of the rtt control block
322356

323357

324358
_YN_CHOICES = ['Y', 'y', 'N', 'n', 'yes', 'no', 'YES', 'NO']
@@ -572,6 +606,13 @@ def add_parser(cls, parser):
572606
help=(cls.tool_opt_help() if caps.tool_opt
573607
else argparse.SUPPRESS))
574608

609+
if caps.rtt:
610+
parser.add_argument('--rtt-address', dest='rtt_address',
611+
type=lambda x: int(x, 0),
612+
help="address of RTT control block. If not supplied, it will be autodetected if possible")
613+
else:
614+
parser.add_argument('--rtt-address', help=argparse.SUPPRESS)
615+
575616
# Runner-specific options.
576617
cls.do_add_parser(parser)
577618

@@ -607,6 +648,8 @@ def create(cls, cfg: RunnerConfig,
607648
raise ValueError("--file-type requires --file")
608649
if args.file_type and not caps.file:
609650
_missing_cap(cls, '--file-type')
651+
if args.rtt_address and not caps.rtt:
652+
_missing_cap(cls, '--rtt-address')
610653

611654
ret = cls.do_create(cfg, args)
612655
if args.erase:
@@ -731,6 +774,19 @@ def require(program: str, path: Optional[str] = None) -> str:
731774
raise MissingProgram(program)
732775
return ret
733776

777+
def get_rtt_address(self) -> int | None:
778+
'''Helper method for extracting a the RTT control block address.
779+
780+
If args.rtt_address was supplied, returns that.
781+
782+
Otherwise, attempt to locate an rtt block in the elf file.
783+
If this is not found, None is returned'''
784+
if self.cfg.rtt_address is not None:
785+
return self.cfg.rtt_address
786+
elif self.cfg.elf_file is not None:
787+
return find_rtt_block(self.cfg.elf_file)
788+
return None
789+
734790
def run_server_and_client(self, server, client, **kwargs):
735791
'''Run a server that ignores SIGINT, and a client that handles it.
736792

scripts/west_commands/runners/pyocd.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ def name(cls):
7676

7777
@classmethod
7878
def capabilities(cls):
79-
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
79+
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach', 'rtt'},
8080
dev_id=True, flash_addr=True, erase=True,
81-
tool_opt=True)
81+
tool_opt=True, rtt=True)
8282

8383
@classmethod
8484
def dev_id_help(cls) -> str:
@@ -142,7 +142,9 @@ def port_args(self):
142142

143143
def do_run(self, command, **kwargs):
144144
self.require(self.pyocd)
145-
if command == 'flash':
145+
if command == 'rtt':
146+
self.rtt(**kwargs)
147+
elif command == 'flash':
146148
self.flash(**kwargs)
147149
else:
148150
self.debug_debugserver(command, **kwargs)
@@ -214,3 +216,23 @@ def debug_debugserver(self, command, **kwargs):
214216
self.require(client_cmd[0])
215217
self.log_gdbserver_message()
216218
self.run_server_and_client(server_cmd, client_cmd)
219+
220+
221+
def rtt(self):
222+
rtt_addr = self.get_rtt_address()
223+
if rtt_addr is None:
224+
raise ValueError('RTT control block not found')
225+
226+
self.logger.debug(f'rtt address: 0x{rtt_addr:x}')
227+
228+
cmd = ([self.pyocd] +
229+
['rtt'] +
230+
self.pyocd_config_args +
231+
self.daparg_args +
232+
self.target_args +
233+
self.board_args +
234+
self.frequency_args +
235+
self.tool_opt_args +
236+
['-a', f'0x{rtt_addr:x}'])
237+
238+
self.check_call(cmd)

0 commit comments

Comments
 (0)