Skip to content

Commit 0525711

Browse files
committed
rtt: enable read/write of rtt channel data from/to a file
1 parent 769141d commit 0525711

4 files changed

Lines changed: 74 additions & 41 deletions

File tree

docs/options.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,10 @@ rtt:
562562
# server - Exposes channel over a TCP server.
563563
# systemview - Saves channel data to *.SVDat file for SEGGER SystemView.
564564
# systemview-server - Streams live data to SEGGER SystemView.
565+
# file - Routes channel data to and from files.
565566
port: # [optional] TCP port number (required for 'server' and 'systemview-server').
567+
file-out: # [optional] Output file path (only for 'file' mode; default: <target>_core<X>_ch<Y>.out")
568+
file-in: # [optional] Input file path (only for 'file' mode; default: <target>_core<X>_ch<Y>.in")
566569
</pre>
567570
<p>Example:</p>
568571
<ul>

pyocd/utility/rtt_manager.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# See the License for the specific language governing permissions and
1515
# limitations under the License.
1616

17-
from typing import Optional, Tuple, List
17+
from typing import Optional, Tuple, List, Union
1818
import os
1919
import logging
2020
from pathlib import Path
@@ -24,7 +24,7 @@
2424
from ..core.session import Session
2525
from ..debug.elf.elf import ELFBinaryFile
2626
from .stdio import StdioHandler
27-
from .rtt_server import RTTServer, RTTChanStdioWorker, RTTChanTCPWorker, RTTChanSysViewFileWorker, RTTChanSysViewTCPWorker
27+
from .rtt_server import RTTServer, RTTChanStdioWorker, RTTChanTCPWorker, RTTChanSysViewFileWorker, RTTChanSysViewTCPWorker, RTTChanFileWorker
2828
from .systemview import SystemViewConfig
2929

3030
LOG = logging.getLogger(__name__)
@@ -34,8 +34,8 @@ class RTTConfig:
3434
"""@brief Data class representing RTT configuration for a specific core."""
3535

3636
# type aliases scoped to the class
37-
RTTControlBlock = Optional[Tuple[Optional[int], Optional[int], bool]] # (address, size, auto-detect)
38-
RTTChannel = Optional[Tuple[int, str, Optional[int]]] # (number, mode, port)
37+
RTTControlBlock = Optional[Tuple[Optional[int], Optional[int], bool]] # (address, size, auto-detect)
38+
RTTChannel = Optional[Tuple[int, str, Optional[Union[int, str]], Optional[str]]] # (number, mode, port/file-out, file--in)
3939
RTTChannelList = Optional[Tuple[RTTChannel, ...]]
4040

4141
_session: Session
@@ -50,8 +50,14 @@ def __post_init__(self):
5050
self._rtt_configuration()
5151

5252
# Helper methods
53-
def _add_channel(self, ch_list: List["RTTConfig.RTTChannel"], number: int, mode: str, port: Optional[int] = None) -> None:
54-
SUPPORTED_MODES = {'stdio', 'server', 'systemview', 'systemview-server'}
53+
def _add_channel(self, ch_list: List["RTTConfig.RTTChannel"],
54+
number: int,
55+
mode: str,
56+
port: Optional[int] = None,
57+
file_out: Optional[str] = None,
58+
file_in: Optional[str] = None) -> None:
59+
60+
SUPPORTED_MODES = {'stdio', 'server', 'systemview', 'systemview-server', 'file'}
5561

5662
if number is None:
5763
# Warn about missing channel number
@@ -85,8 +91,32 @@ def _add_channel(self, ch_list: List["RTTConfig.RTTChannel"], number: int, mode:
8591
# Count the number of SystemView channels
8692
self.num_systemview_channels += 1
8793

88-
port_str = f", port={port}" if port is not None else ""
89-
ch_list.append((number, mode, port))
94+
if mode == 'file':
95+
if file_out is None or file_in is None:
96+
# If file not provided, set default: <target>_core<X>_ch<Y>
97+
file_root = f"{self._session.board.target_type}"
98+
if len(self._session.board.target.cores) > 1:
99+
file_root += f"_core{self._core}"
100+
file_root += f"_ch{number}"
101+
file_out = file_out or f"{file_root}.out"
102+
file_in = file_in or f"{file_root}.in"
103+
# Check if the folder exists for output file
104+
105+
if file_out is not None:
106+
dir_out = os.path.dirname(file_out)
107+
if dir_out and not os.path.exists(dir_out):
108+
LOG.warning("RTT channel %d configuration for core %d: directory for file-out '%s' for %s mode does not exist; channel out disabled", number, self._core, dir_out, mode)
109+
file_out = None
110+
111+
if file_in and not os.path.exists(file_in):
112+
LOG.warning("RTT channel %d configuration for core %d: file-in '%s' for %s mode does not exist; channel in disabled", number, self._core, file_in, mode)
113+
file_in = None
114+
115+
else:
116+
file_out = None
117+
file_in = None
118+
119+
ch_list.append((number, mode, port or file_out or None, file_in))
90120

91121
def _rtt_configuration(self) -> None:
92122
rtt_config_list = self._session.options.get('rtt') or []
@@ -127,15 +157,15 @@ def _rtt_configuration(self) -> None:
127157
rtt_ch: List["RTTConfig.RTTChannel"] = []
128158
if ch_l is not None:
129159
for ch in ch_l:
130-
self._add_channel(rtt_ch, ch.get('number'), ch.get('mode'), ch.get('port'))
160+
self._add_channel(rtt_ch, ch.get('number'), ch.get('mode'), ch.get('port') or ch.get('file-out'), ch.get('file-in'))
131161
if ch_g is not None:
132162
for ch in ch_g:
133163
if ch_l is not None and any(ch.get('number') == local_ch.get('number') for local_ch in ch_l):
134164
# Skip global channel configuration if a local channel with the same number exists
135165
LOG.debug("RTT channel %d configuration for core %d: pname specific configuration used; skipping global channel configuration",
136166
ch.get('number'), self._core)
137167
else:
138-
self._add_channel(rtt_ch, ch.get('number'), ch.get('mode'), ch.get('port'))
168+
self._add_channel(rtt_ch, ch.get('number'), ch.get('mode'), ch.get('port') or ch.get('file-out'), ch.get('file-in'))
139169

140170
# Sort channels by channel number if any were added
141171
if rtt_ch:
@@ -270,7 +300,7 @@ def configure_channels(self, stdio_handler: Optional[StdioHandler] = None):
270300

271301
stdio_enabled = False
272302

273-
for number, mode, server_port in self._rtt_config.channels:
303+
for number, mode, port_or_file_out, file_in in self._rtt_config.channels:
274304
if self._rtt_server.is_channel_idx_valid(number) is False:
275305
LOG.warning("RTT for core %d: channel index %d is out of range; skipping configuration for channel %d", self._core, number, number)
276306
continue
@@ -294,25 +324,34 @@ def configure_channels(self, stdio_handler: Optional[StdioHandler] = None):
294324
# Server mode
295325
elif mode == 'server':
296326
try:
297-
self._rtt_server.add_channel_worker(number, lambda: RTTChanTCPWorker(server_port, listen=True))
298-
LOG.info("RTT channel %d configuration for core %d: mode=%s, port=%d", number, self._core, mode, server_port)
327+
self._rtt_server.add_channel_worker(number, lambda: RTTChanTCPWorker(port_or_file_out, listen=True))
328+
LOG.info("RTT channel %d configuration for core %d: mode=%s, port=%d", number, self._core, mode, port_or_file_out)
299329
except exceptions.RTTError as e:
300330
LOG.error("RTT for core %d: failed to enable server mode for RTT channel %d: %s", self._core, number, e)
301331
# SystemView mode
302332
elif mode == 'systemview':
303333
try:
304334
fname_root = self._systemview.file.rsplit('.', 1)[0]
305335
fname = f'{fname_root}.core{self._core}.ch{number}.bin'
306-
self._rtt_server.add_channel_worker(number, lambda: RTTChanSysViewFileWorker(rtt_server=self._rtt_server, rtt_channel=number, file_out=fname, auto_start=self._systemview.auto_start, auto_stop=self._systemview.auto_stop))
336+
self._rtt_server.add_channel_worker(number, lambda: RTTChanSysViewFileWorker(rtt_server=self._rtt_server, channel=number, file_out=fname, auto_start=self._systemview.auto_start, auto_stop=self._systemview.auto_stop))
307337
LOG.info("RTT channel %d configuration for core %d: mode=%s", number, self._core, mode)
308338
except (IOError, exceptions.RTTError) as e:
309339
LOG.error("RTT for core %d: failed to enable systemview mode for RTT channel %d: %s", self._core, number, e)
310340
# SystemView server mode
311341
elif mode == 'systemview-server':
312342
try:
313-
self._rtt_server.add_channel_worker(number, lambda: RTTChanSysViewTCPWorker(server_port, listen=True))
314-
LOG.info("RTT channel %d configuration for core %d: mode=%s, port=%d", number, self._core, mode, server_port)
343+
self._rtt_server.add_channel_worker(number, lambda: RTTChanSysViewTCPWorker(port_or_file_out, listen=True))
344+
LOG.info("RTT channel %d configuration for core %d: mode=%s, port=%d", number, self._core, mode, port_or_file_out)
315345
except exceptions.RTTError as e:
316346
LOG.error("RTT for core %d: failed to enable systemview server mode for RTT channel %d: %s", self._core, number, e)
347+
# File mode
348+
elif mode == 'file':
349+
try:
350+
self._rtt_server.add_channel_worker(number, lambda: RTTChanFileWorker(channel=number, file_out=port_or_file_out, file_in=file_in))
351+
file_out_str = f", file-out={port_or_file_out}" if port_or_file_out else ""
352+
file_in_str = f", file-in={file_in}" if file_in else ""
353+
LOG.info("RTT channel %d configuration for core %d: mode=%s%s%s", number, self._core, mode, file_out_str, file_in_str)
354+
except (IOError, exceptions.RTTError) as e:
355+
LOG.error("RTT for core %d: failed to enable file mode for RTT channel %d: %s", self._core, number, e)
317356
else:
318357
LOG.warning("RTT for core %d: unsupported channel mode '%s' for channel %d; skipping configuration for channel %d", self._core, mode, number, number)

pyocd/utility/rtt_server.py

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -143,38 +143,29 @@ class RTTChanFileWorker(RTTChanWorker):
143143
_f_out_path: Optional[str]
144144
_f_in_path: Optional[str]
145145

146-
def __init__(self, channel: int, file_out: str, file_in: Optional[str] = None):
146+
def __init__(self, channel: int, file_out: Optional[str] = None, file_in: Optional[str] = None):
147147
"""
148148
@param file_out The file to write RTT channel data to.
149149
@param file_in The file to read data from into the RTT channel. If None, no data will be read.
150150
"""
151-
self._f_out = None
152-
self._f_in = None
153-
self._f_out_path = None
154-
self._f_in_path = None
151+
self._f_out = file_out
152+
self._f_in = file_in
155153

156-
# Check if the folder exists for output file
157-
dir_out = os.path.dirname(file_out)
158-
if dir_out and not os.path.exists(dir_out):
159-
f_name_out = os.path.basename(file_out)
160-
raise FileNotFoundError(
161-
f"Output directory '{dir_out}' for RTT channel {channel} (file '{f_name_out}') does not exist."
162-
)
163-
try:
164-
self._f_out = open(file_out, 'wb')
165-
self._f_out_path = file_out
166-
except OSError as e:
167-
raise OSError(f"Failed to open RTT output file {file_out}: {e}")
154+
if file_out is not None:
155+
try:
156+
self._f_out = open(file_out, 'wb')
157+
except OSError as e:
158+
raise OSError(f"Failed to open RTT output file {file_out}: {e}")
168159

169160
if file_in is not None:
170-
if os.path.exists(file_in):
161+
try:
171162
self._f_in = open(file_in, 'rb')
172-
self._f_in_path = file_in
173-
else:
174-
LOG.debug("Input file '%s' for RTT channel %d does not exist", os.path.basename(file_in), channel)
163+
except OSError as e:
164+
raise OSError(f"Failed to open RTT input file {file_in}: {e}")
165+
175166

176167
def write_up_data(self, data: bytes):
177-
if self._f_out is None:
168+
if self._f_out is None or not data:
178169
return 0
179170
return self._f_out.write(data)
180171

@@ -196,9 +187,9 @@ class RTTChanSysViewFileWorker(RTTChanWorker):
196187
_STOP_CMD = b"\x02"
197188
_START_SEQ = b"\x00" * 10
198189

199-
def __init__(self, rtt_server: RTTServer, rtt_channel: int, file_out: str, auto_start: bool = True, auto_stop: bool = True):
190+
def __init__(self, rtt_server: RTTServer, channel: int, file_out: str, auto_start: bool = True, auto_stop: bool = True):
200191
self._rtt_server = rtt_server
201-
self._rtt_channel = rtt_channel
192+
self._rtt_channel = channel
202193
self._auto_start = auto_start
203194
self._auto_stop = auto_stop
204195

pyocd/utility/systemview.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def __init__(self, session: Session, rtt_configs: Dict[int, "RTTConfig"], system
8181

8282
file_list: List[str] = []
8383
for ch in rtt.channels:
84-
number, mode, _ = ch
84+
number, mode, _, _ = ch
8585
if mode == 'systemview':
8686
fname = f'{fname_root}.core{core_number}.ch{number}.bin'
8787
if os.path.exists(fname):

0 commit comments

Comments
 (0)