Skip to content

Commit 70f2550

Browse files
danieldegrassecfriedt
authored andcommitted
scripts: add rtt helpers to dump RTT data
Add helpers to dump RTT data for SMC and BMC. These helpers will not reset the system, but simply dump state. They can be useful for debugging a hung SMC or BMC Signed-off-by: Daniel DeGrasse <[email protected]>
1 parent caad5f3 commit 70f2550

File tree

3 files changed

+328
-0
lines changed

3 files changed

+328
-0
lines changed

scripts/bmc_rtt.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2025 Tenstorrent AI ULC
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
"""
7+
Script to open RTT console to BMC
8+
"""
9+
10+
from rtt_helper import RTTHelper
11+
from pathlib import Path
12+
13+
DEFAULT_CFG = (
14+
Path(__file__).parents[1]
15+
/ "boards/tenstorrent/tt_blackhole/support/tt_blackhole_bmc.cfg"
16+
)
17+
DEFAULT_SEARCH_BASE = 0x20000000
18+
DEFAULT_SEARCH_RANGE = 0x80000
19+
20+
21+
def start_bmc_rtt():
22+
"""
23+
Main function to start RTT console
24+
"""
25+
rtt_helper = RTTHelper(
26+
default_cfg=DEFAULT_CFG,
27+
default_search_base=DEFAULT_SEARCH_BASE,
28+
default_search_range=DEFAULT_SEARCH_RANGE,
29+
)
30+
rtt_helper.parse_args()
31+
rtt_helper.run_rtt_server()
32+
33+
34+
if __name__ == "__main__":
35+
start_bmc_rtt()

scripts/rtt_helper.py

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2025 Tenstorrent AI ULC
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
"""
7+
Helper functions to start an openocd RTT server and connect to it as a client.
8+
"""
9+
10+
import subprocess
11+
from pathlib import Path
12+
import argparse
13+
import logging
14+
import shutil
15+
import socket
16+
import sys
17+
import selectors
18+
19+
logger = logging.getLogger(__name__)
20+
21+
22+
def get_sdk_sysroot():
23+
"""
24+
Gets the Zephyr SDK sysroot, which is used as a path base for
25+
openocd executable
26+
"""
27+
# Execute CMake to locate SDK sysroot
28+
proc = subprocess.run(
29+
["cmake", "-P", str(Path(__file__).parent / "find_zephyr_sdk.cmake")],
30+
capture_output=True,
31+
check=False,
32+
)
33+
if "SDK_INSTALL_DIR" in str(proc.stderr):
34+
return (
35+
Path(str(proc.stderr).split(":")[1][:-3])
36+
/ "sysroots"
37+
/ "x86_64-pokysdk-linux"
38+
)
39+
return None
40+
41+
42+
class OpenOCDServer:
43+
"""
44+
Server to run openocd with rtt port
45+
"""
46+
47+
def __init__(self, openocd_exec, search_dir):
48+
"""
49+
Sets up openocd class
50+
@param openocd_exec: Openocd executable to use
51+
@param search_dir: Search directory to use for openocd
52+
"""
53+
self._openocd = openocd_exec
54+
self._search_dir = search_dir
55+
self._proc = None
56+
57+
def launch_openocd_server(self, cfg, rtt_port, search_base, search_range):
58+
"""
59+
Launches an openocd server, using the RTT port provided
60+
@param cfg openocd configuration file to use
61+
@param rtt_port RTT port to use
62+
@param search_base: base address to search for RTT block
63+
@param search_range: range to search for RTT block
64+
"""
65+
openocd_cmd = [
66+
self._openocd,
67+
"-s",
68+
self._search_dir,
69+
"-s",
70+
str(Path(cfg).parent),
71+
"-f",
72+
cfg,
73+
"-c",
74+
"init",
75+
"-c",
76+
f'rtt setup {search_base} {search_range} "SEGGER RTT"',
77+
"-c",
78+
"rtt start",
79+
"-c",
80+
f"rtt server start {rtt_port} 0",
81+
]
82+
logger.debug("OpenOCD command: %s", openocd_cmd)
83+
try:
84+
self._proc = subprocess.Popen(
85+
openocd_cmd,
86+
stdout=subprocess.PIPE,
87+
stderr=subprocess.PIPE,
88+
)
89+
while self._proc.poll() is None:
90+
line = self._proc.stderr.readline().decode().strip()
91+
logger.debug("Openocd Output: %s", line)
92+
if f"Listening on port {rtt_port} for rtt connections" in line:
93+
break
94+
except subprocess.CalledProcessError as e:
95+
logger.error("Failed to start OpenOCD server: %s", e)
96+
raise e
97+
98+
def stop_openocd_server(self):
99+
"""
100+
Stops the openocd server
101+
"""
102+
logger.info("Stopping OpenOCD server")
103+
self._proc.terminate()
104+
self._proc.wait()
105+
106+
107+
# Defaults used by the RTT helper
108+
DEFAULT_RTT_PORT = 5555
109+
DEFAULT_SYSROOT = get_sdk_sysroot() or Path(
110+
"/opt/zephyr-sdk/sysroots/x86_64-pokysdk-linux"
111+
)
112+
113+
114+
class RTTHelper:
115+
"""
116+
Helper to start rtt. Takes arguments from user
117+
"""
118+
119+
def __init__(self, default_cfg, default_search_base, default_search_range):
120+
"""
121+
Inits the RTT helper with default values
122+
@param default_cfg: default config to use for openocd
123+
@param default_search_base: default search base for RTT
124+
@param default_search_range: default search range for RTT
125+
"""
126+
self._default_cfg = default_cfg
127+
self._default_search_base = default_search_base
128+
self._default_search_range = default_search_range
129+
self._openocd = None
130+
self._search_dir = None
131+
self._cfg = None
132+
self._rtt_port = None
133+
self._search_base = None
134+
self._search_range = None
135+
136+
def parse_args(self):
137+
"""
138+
Parse arguments from user, and store them as object properties
139+
"""
140+
parser = argparse.ArgumentParser(
141+
description="Helper to run RTT console", allow_abbrev=False
142+
)
143+
parser.add_argument(
144+
"-c",
145+
"--config",
146+
type=Path,
147+
default=self._default_cfg,
148+
help="OpenOCD config file to use",
149+
)
150+
parser.add_argument(
151+
"-a",
152+
"--search_base",
153+
type=int,
154+
default=self._default_search_base,
155+
help="Base address to search for RTT block",
156+
)
157+
parser.add_argument(
158+
"-r",
159+
"--search_range",
160+
type=int,
161+
default=self._default_search_range,
162+
help="Range to search for RTT block",
163+
)
164+
parser.add_argument(
165+
"-p",
166+
"--rtt_port",
167+
type=int,
168+
default=DEFAULT_RTT_PORT,
169+
help="Port to use for RTT server",
170+
)
171+
parser.add_argument(
172+
"-o",
173+
"--openocd",
174+
default=DEFAULT_SYSROOT / "usr" / "bin" / "openocd",
175+
help="Path to OpenOCD executable",
176+
metavar="FILE",
177+
type=Path,
178+
)
179+
parser.add_argument(
180+
"-s",
181+
"--search_dir",
182+
default=DEFAULT_SYSROOT / "usr" / "share" / "openocd" / "scripts",
183+
help="Path to OpenOCD search directory",
184+
)
185+
parser.add_argument(
186+
"-d",
187+
"--debug",
188+
action="count",
189+
default=0,
190+
help="Increase debugging verbosity (pass -dd for debug, -d for info)",
191+
)
192+
args = parser.parse_args()
193+
if args.debug == 2:
194+
logging.basicConfig(level=logging.DEBUG)
195+
elif args.debug == 1:
196+
logging.basicConfig(level=logging.INFO)
197+
else:
198+
logging.basicConfig(level=logging.WARNING)
199+
200+
self._openocd = str(args.openocd)
201+
self._search_dir = str(args.search_dir)
202+
self._cfg = str(args.config)
203+
self._rtt_port = args.rtt_port
204+
self._search_base = args.search_base
205+
self._search_range = args.search_range
206+
207+
def run_rtt_server(self):
208+
"""
209+
Run the RTT server with the parameters provided
210+
"""
211+
openocd = OpenOCDServer(self._openocd, self._search_dir)
212+
openocd.launch_openocd_server(
213+
self._cfg,
214+
self._rtt_port,
215+
self._search_base,
216+
self._search_range,
217+
)
218+
print(f"OpenOCD server started on port {self._rtt_port}")
219+
if shutil.which("nc") is not None:
220+
# If a `nc` command is available, run it, as it will provide the
221+
# best support for CONFIG_SHELL_VT100_COMMANDS etc.
222+
client_cmd = ["nc", "localhost", str(self._rtt_port)]
223+
try:
224+
subprocess.run(client_cmd, check=True)
225+
except KeyboardInterrupt:
226+
# If the user interrupts the command, we need to stop the openocd server
227+
print("Caught SIGINT, exiting...")
228+
openocd.stop_openocd_server()
229+
except subprocess.CalledProcessError as e:
230+
print(f"Error running nc command: {e}")
231+
openocd.stop_openocd_server()
232+
return
233+
234+
logger.warning("No nc command found, using pure python implementation")
235+
# Otherwise, use a pure python implementation. This will work well for logging,
236+
# but input is line based only.
237+
# Start a new socket connection
238+
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
239+
sock.connect(("localhost", self._rtt_port))
240+
241+
sel = selectors.DefaultSelector()
242+
sel.register(sys.stdin, selectors.EVENT_READ)
243+
sel.register(sock, selectors.EVENT_READ)
244+
while True:
245+
events = sel.select()
246+
for key, _ in events:
247+
if key.fileobj == sys.stdin:
248+
text = sys.stdin.readline()
249+
if text:
250+
sock.send(text.encode())
251+
252+
elif key.fileobj == sock:
253+
resp = sock.recv(2048)
254+
if resp:
255+
print(resp.decode())
256+
257+
# Finally, shutdown the RTT server
258+
openocd.stop_openocd_server()

scripts/smc_rtt.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright (c) 2025 Tenstorrent AI ULC
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
"""
7+
Script to open RTT console to SMC
8+
"""
9+
10+
from rtt_helper import RTTHelper
11+
from pathlib import Path
12+
13+
DEFAULT_CFG = (
14+
Path(__file__).parents[1]
15+
/ "boards/tenstorrent/tt_blackhole/support/tt_blackhole_smc.cfg"
16+
)
17+
DEFAULT_SEARCH_BASE = 0x10000000
18+
DEFAULT_SEARCH_RANGE = 0x80000
19+
20+
21+
def start_smc_rtt():
22+
"""
23+
Main function to start RTT console
24+
"""
25+
rtt_helper = RTTHelper(
26+
default_cfg=DEFAULT_CFG,
27+
default_search_base=DEFAULT_SEARCH_BASE,
28+
default_search_range=DEFAULT_SEARCH_RANGE,
29+
)
30+
rtt_helper.parse_args()
31+
rtt_helper.run_rtt_server()
32+
33+
34+
if __name__ == "__main__":
35+
start_smc_rtt()

0 commit comments

Comments
 (0)