Skip to content

Commit 73c8235

Browse files
topisaninashif
authored andcommitted
west: runners: Implement the west rtt command for jlink
Moves the telnet client into runners/core.py as well, as this is now shared between openocd and jlink. Signed-off-by: Tobias Pisani <[email protected]>
1 parent 18f45b5 commit 73c8235

File tree

3 files changed

+70
-35
lines changed

3 files changed

+70
-35
lines changed

scripts/west_commands/runners/core.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,14 @@
1717
import logging
1818
import os
1919
import platform
20+
import re
21+
import selectors
2022
import shlex
2123
import shutil
2224
import signal
25+
import socket
2326
import subprocess
24-
import re
27+
import sys
2528
from dataclasses import dataclass, field
2629
from functools import partial
2730
from enum import Enum
@@ -902,3 +905,34 @@ def ensure_output(self, output_type: str) -> None:
902905

903906
# RuntimeError avoids a stack trace saved in run_common.
904907
raise RuntimeError(err)
908+
909+
def run_telnet_client(self, host: str, port: int) -> None:
910+
'''
911+
Run a telnet client for user interaction.
912+
'''
913+
# If a `nc` command is available, run it, as it will provide the best support for
914+
# CONFIG_SHELL_VT100_COMMANDS etc.
915+
if shutil.which('nc') is not None:
916+
client_cmd = ['nc', host, str(port)]
917+
self.run_client(client_cmd)
918+
return
919+
920+
# Otherwise, use a pure python implementation. This will work well for logging,
921+
# but input is line based only.
922+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
923+
sock.connect((host, port))
924+
sel = selectors.DefaultSelector()
925+
sel.register(sys.stdin, selectors.EVENT_READ)
926+
sel.register(sock, selectors.EVENT_READ)
927+
while True:
928+
events = sel.select()
929+
for key, _ in events:
930+
if key.fileobj == sys.stdin:
931+
text = sys.stdin.readline()
932+
if text:
933+
sock.send(text.encode())
934+
935+
elif key.fileobj == sock:
936+
resp = sock.recv(2048)
937+
if resp:
938+
print(resp.decode())

scripts/west_commands/runners/jlink.py

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import shlex
1313
import subprocess
1414
import sys
15+
import socket
16+
import time
1517
import tempfile
1618

1719
from runners.core import ZephyrBinaryRunner, RunnerCaps, FileType
@@ -25,6 +27,7 @@
2527

2628
DEFAULT_JLINK_EXE = 'JLink.exe' if sys.platform == 'win32' else 'JLinkExe'
2729
DEFAULT_JLINK_GDB_PORT = 2331
30+
DEFAULT_JLINK_RTT_PORT = 19021
2831

2932
def is_ip(ip):
3033
try:
@@ -49,6 +52,7 @@ def __init__(self, cfg, device, dev_id=None,
4952
gdbserver='JLinkGDBServer',
5053
gdb_host='',
5154
gdb_port=DEFAULT_JLINK_GDB_PORT,
55+
rtt_port=DEFAULT_JLINK_RTT_PORT,
5256
tui=False, tool_opt=[]):
5357
super().__init__(cfg)
5458
self.file = cfg.file
@@ -70,6 +74,7 @@ def __init__(self, cfg, device, dev_id=None,
7074
self.gdb_port = gdb_port
7175
self.tui_arg = ['-tui'] if tui else []
7276
self.loader = loader
77+
self.rtt_port = rtt_port
7378

7479
self.tool_opt = []
7580
for opts in [shlex.split(opt) for opt in tool_opt]:
@@ -81,9 +86,9 @@ def name(cls):
8186

8287
@classmethod
8388
def capabilities(cls):
84-
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach'},
89+
return RunnerCaps(commands={'flash', 'debug', 'debugserver', 'attach', 'rtt'},
8590
dev_id=True, flash_addr=True, erase=True, reset=True,
86-
tool_opt=True, file=True)
91+
tool_opt=True, file=True, rtt=True)
8792

8893
@classmethod
8994
def dev_id_help(cls) -> str:
@@ -126,6 +131,10 @@ def do_add_parser(cls, parser):
126131
dest='reset', nargs=0,
127132
action=ToggleAction,
128133
help='obsolete synonym for --reset/--no-reset')
134+
parser.add_argument('--rtt-client', default='JLinkRTTClient',
135+
help='RTT client, default is JLinkRTTClient')
136+
parser.add_argument('--rtt-port', default=DEFAULT_JLINK_RTT_PORT,
137+
help=f'jlink rtt port, defaults to {DEFAULT_JLINK_RTT_PORT}')
129138

130139
parser.set_defaults(reset=False)
131140

@@ -142,6 +151,7 @@ def do_create(cls, cfg, args):
142151
loader=args.loader,
143152
gdb_host=args.gdb_host,
144153
gdb_port=args.gdb_port,
154+
rtt_port=args.rtt_port,
145155
tui=args.tui, tool_opt=args.tool_opt)
146156

147157
def print_gdbserver_message(self):
@@ -248,6 +258,7 @@ def do_run(self, command, **kwargs):
248258
'-singlerun'] +
249259
(['-nogui'] if self.supports_nogui else []) +
250260
(['-rtos', plugin_dir] if rtos else []) +
261+
['-rtttelnetport', str(self.rtt_port)] +
251262
self.tool_opt)
252263

253264
if command == 'flash':
@@ -258,6 +269,27 @@ def do_run(self, command, **kwargs):
258269
self.require(self.gdbserver)
259270
self.print_gdbserver_message()
260271
self.check_call(server_cmd)
272+
elif command == 'rtt':
273+
self.print_gdbserver_message()
274+
server_cmd += ['-nohalt']
275+
server_proc = self.popen_ignore_int(server_cmd)
276+
try:
277+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
278+
# wait for the port to be open
279+
while server_proc.poll() is None:
280+
try:
281+
sock.connect(('localhost', self.rtt_port))
282+
break
283+
except ConnectionRefusedError:
284+
time.sleep(0.1)
285+
sock.shutdown(socket.SHUT_RDWR)
286+
time.sleep(0.1)
287+
self.run_telnet_client('localhost', self.rtt_port)
288+
except Exception as e:
289+
self.logger.error(e)
290+
finally:
291+
server_proc.terminate()
292+
server_proc.wait()
261293
else:
262294
if self.gdb_cmd is None:
263295
raise ValueError('Cannot debug; gdb is missing')

scripts/west_commands/runners/openocd.py

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@
77
'''Runner for openocd.'''
88

99
import re
10-
import selectors
11-
import shutil
1210
import socket
1311
import subprocess
14-
import sys
1512
import time
1613

1714
from os import path
@@ -454,37 +451,9 @@ def do_rtt(self, **kwargs):
454451
# the port is open now.
455452
self.logger.info("Opening RTT")
456453
time.sleep(0.1) # Give the server a moment to output log messages first
457-
self._run_rtt_client()
454+
self.run_telnet_client('localhost', self.rtt_port)
458455
except Exception as e:
459456
self.logger.error(e)
460457
finally:
461458
server_proc.terminate()
462459
server_proc.wait()
463-
464-
def _run_rtt_client(self):
465-
# If a `nc` command is available, run it, as it will provide the best support for
466-
# CONFIG_SHELL_VT100_COMMANDS etc.
467-
if shutil.which('nc') is not None:
468-
client_cmd = ['nc', 'localhost', str(self.rtt_port)]
469-
self.run_client(client_cmd)
470-
return
471-
472-
# Otherwise, use a pure python implementation. This will work well for logging,
473-
# but input is line based only.
474-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
475-
sock.connect(('localhost', self.rtt_port))
476-
sel = selectors.DefaultSelector()
477-
sel.register(sys.stdin, selectors.EVENT_READ)
478-
sel.register(sock, selectors.EVENT_READ)
479-
while True:
480-
events = sel.select()
481-
for key, _ in events:
482-
if key.fileobj == sys.stdin:
483-
text = sys.stdin.readline()
484-
if text:
485-
sock.send(text.encode())
486-
487-
elif key.fileobj == sock:
488-
resp = sock.recv(2048)
489-
if resp:
490-
print(resp.decode())

0 commit comments

Comments
 (0)