probe_rs/architecture/arm/swo/mod.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
//! SWO tracing related functions.
use std::time::Duration;
use crate::architecture::arm::communication_interface::ArmProbeInterface;
use super::ArmError;
/// The protocol the SWO pin should use for data transmission.
#[derive(Debug, Copy, Clone)]
pub enum SwoMode {
/// UART
Uart,
/// Manchester
Manchester,
}
/// The config for the SWO pin.
#[derive(Debug, Copy, Clone)]
pub struct SwoConfig {
/// SWO mode: either UART or Manchester.
mode: SwoMode,
/// Baud rate of SWO, in Hz.
///
/// This value is used to configure what baud rate the target
/// generates and to configure what baud rate the probe receives,
/// so must be a baud rate supported by both target and probe.
baud: u32,
/// Clock input to TPIU in Hz. This is often the system clock (HCLK/SYSCLK etc).
tpiu_clk: u32,
/// Whether to enable TPIU formatting.
/// This is required to use ETM over SWO, but otherwise
/// adds overhead if only DWT/ITM data is used.
tpiu_continuous_formatting: bool,
}
impl SwoConfig {
/// Create a new SwoConfig using the specified TPIU clock in Hz.
///
/// By default the UART mode is used at 1MBd and
/// TPIU continuous formatting is disabled (DWT/ITM only).
pub fn new(tpiu_clk: u32) -> Self {
SwoConfig {
mode: SwoMode::Uart,
baud: 1_000_000,
tpiu_clk,
tpiu_continuous_formatting: false,
}
}
/// Set the baud rate in Hz.
pub fn set_baud(mut self, baud: u32) -> Self {
self.baud = baud;
self
}
/// Set the mode in this SwoConfig.
pub fn set_mode(mut self, mode: SwoMode) -> Self {
self.mode = mode;
self
}
/// Set the mode to UART
pub fn set_mode_uart(mut self) -> Self {
self.mode = SwoMode::Uart;
self
}
/// Set the mode to Manchester
pub fn set_mode_manchester(mut self) -> Self {
self.mode = SwoMode::Manchester;
self
}
/// Set the TPIU continuous formatting setting.
pub fn set_continuous_formatting(mut self, enabled: bool) -> Self {
self.tpiu_continuous_formatting = enabled;
self
}
/// The SWO mode.
pub fn mode(&self) -> SwoMode {
self.mode
}
/// Baud rate of SWO, in Hz.
///
/// This value is used to configure what baud rate the target generates
/// and to configure what baud rate the probe receives,
/// so must be a baud rate supported by both target and probe.
pub fn baud(&self) -> u32 {
self.baud
}
/// Clock input to TPIU in Hz. This is often the system clock (HCLK/SYSCLK etc).
pub fn tpiu_clk(&self) -> u32 {
self.tpiu_clk
}
/// Whether to enable TPIU formatting. This is required to use ETM over
/// SWO, but otherwise adds overhead if only DWT/ITM data is used.
pub fn tpiu_continuous_formatting(&self) -> bool {
self.tpiu_continuous_formatting
}
}
/// An interface to operate SWO to be implemented on drivers that support SWO.
pub trait SwoAccess {
/// Configure a SwoAccess interface for reading SWO data.
fn enable_swo(&mut self, config: &SwoConfig) -> Result<(), ArmError>;
/// Disable SWO reading on this SwoAccess interface.
fn disable_swo(&mut self) -> Result<(), ArmError>;
/// Read any available SWO data without waiting.
///
/// Returns a `Vec<u8>` of received SWO bytes since the last `read_swo()` call.
/// If no data was available, returns an empty Vec.
fn read_swo(&mut self) -> Result<Vec<u8>, ArmError> {
self.read_swo_timeout(Duration::from_millis(10))
}
/// Read SWO data for up to `timeout` duration.
///
/// If no data is received before the timeout, returns an empty Vec.
/// May return earlier than `timeout` if the receive buffer fills up.
fn read_swo_timeout(&mut self, timeout: Duration) -> Result<Vec<u8>, ArmError>;
/// Request an estimated best time to wait between polls of `read_swo`.
///
/// A probe can implement this if it can work out a sensible time to
/// wait between polls, for example using the probe's internal buffer
/// size and SWO baud rate, or a 0s duration if reads can block for
/// new data.
///
/// The default implementation computes an estimated interval based on the buffer
/// size, mode, and baud rate.
fn swo_poll_interval_hint(&mut self, config: &SwoConfig) -> Option<Duration> {
match self.swo_buffer_size() {
Some(size) => poll_interval_from_buf_size(config, size),
None => None,
}
}
/// Request the probe SWO buffer size, if known.
fn swo_buffer_size(&mut self) -> Option<usize> {
None
}
}
/// Helper function to compute a poll interval from a SwoConfig and SWO buffer size.
pub(crate) fn poll_interval_from_buf_size(config: &SwoConfig, buf_size: usize) -> Option<Duration> {
let time_to_full_ms = match config.mode() {
// In UART, the output data is at the baud rate with 10 clocks per byte.
SwoMode::Uart => (1000 * buf_size as u32) / (config.baud() / 10),
// In Manchester, the output data is at half the baud rate with
// between 8.25 and 10 clocks per byte, so use a conservative 8 clocks/byte.
SwoMode::Manchester => (500 * buf_size as u32) / (config.baud() / 8),
};
// Poll frequently enough to catch the buffer at 1/4 full
Some(Duration::from_millis(time_to_full_ms as u64 / 4))
}
/// A reader interface to pull SWO data from the underlying driver.
pub struct SwoReader<'a> {
interface: &'a mut dyn ArmProbeInterface,
buf: Vec<u8>,
}
impl<'a> SwoReader<'a> {
pub(crate) fn new(interface: &'a mut dyn ArmProbeInterface) -> Self {
Self {
interface,
buf: Vec::new(),
}
}
}
impl std::io::Read for SwoReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
use std::{
cmp,
io::{Error, ErrorKind},
mem,
};
// Always buffer: this pulls data as quickly as possible from
// the target to clear it's embedded trace buffer, minimizing
// the chance of an overflow event during which packets are
// lost.
self.buf.append(
&mut self
.interface
.read_swo()
.map_err(|e| Error::new(ErrorKind::Other, e))?,
);
let swo = {
let next_buf = self.buf.split_off(cmp::min(self.buf.len(), buf.len()));
mem::replace(&mut self.buf, next_buf)
};
buf[..swo.len()].copy_from_slice(&swo);
Ok(swo.len())
}
}