Skip to main content

nucleus_trace/
source.rs

1//! Byte sources for the SWO trace stream, and OpenOCD setup over telnet.
2//!
3//! The decoder doesn't care where bytes come from. In practice they arrive from
4//! OpenOCD: it captures SWO from the ST-Link and, when configured with
5//! `tpiu config internal :<port> …`, streams the raw trace to a TCP socket. We
6//! connect to that socket. For development and tests there is also a file
7//! replay of a captured stream.
8//!
9//! Per the README, OpenOCD's SWO command sequence is version- and probe-
10//! dependent; [`openocd_enable`] sends a best-effort sequence and the exact
11//! commands are documented for the user to adapt.
12
13use std::path::PathBuf;
14
15use tokio::io::{AsyncRead, AsyncWriteExt};
16use tokio::net::TcpStream;
17
18/// Where to read the raw SWO byte stream from.
19#[derive(Debug, Clone)]
20pub enum Source {
21    /// A TCP socket OpenOCD streams trace data to (`tpiu config internal :PORT`).
22    Tcp(String),
23    /// A captured raw-SWO file (handy for replay and validation).
24    File(PathBuf),
25}
26
27impl Source {
28    /// Open the source as an async byte reader.
29    pub async fn open(&self) -> std::io::Result<Box<dyn AsyncRead + Unpin + Send>> {
30        match self {
31            Source::Tcp(addr) => Ok(Box::new(TcpStream::connect(addr).await?)),
32            Source::File(path) => Ok(Box::new(tokio::fs::File::open(path).await?)),
33        }
34    }
35}
36
37/// Send OpenOCD (telnet console, default port 4444) the command sequence that
38/// enables SWO/ITM capture and routes trace to an internal TCP port.
39///
40/// `cpu_hz` is the core clock and `swo_hz` the SWO clock from `[trace].swo_freq`.
41/// This is best-effort: OpenOCD may already be configured, or use a different
42/// command spelling across versions; failures here are non-fatal to tracing.
43pub async fn openocd_enable(
44    telnet_addr: &str,
45    trace_port: u16,
46    cpu_hz: u32,
47    swo_hz: u32,
48) -> std::io::Result<()> {
49    let mut conn = TcpStream::connect(telnet_addr).await?;
50    let commands = [
51        format!("tpiu config internal :{trace_port} uart off {cpu_hz} {swo_hz}"),
52        "itm ports on".to_string(),
53    ];
54    for cmd in commands {
55        conn.write_all(cmd.as_bytes()).await?;
56        conn.write_all(b"\r\n").await?;
57    }
58    conn.flush().await?;
59    Ok(())
60}