probe_rs/architecture/arm/component/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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
//! Types and functions for interacting with CoreSight Components
mod dwt;
mod itm;
mod scs;
mod swo;
mod tmc;
mod tpiu;
mod trace_funnel;
use crate::{
architecture::arm::{
core::armv6m::Demcr,
memory::romtable::{CoresightComponent, PeripheralType, RomTableError},
ArmError, ArmProbeInterface, DpAddress, SwoConfig, SwoMode,
},
Core, Error, MemoryInterface, MemoryMappedRegister,
};
pub use self::itm::Itm;
pub use dwt::Dwt;
pub use scs::Scs;
pub use swo::Swo;
pub use tmc::TraceMemoryController;
pub use tpiu::Tpiu;
pub use trace_funnel::TraceFunnel;
use super::memory::Component;
/// Specifies the data sink (destination) for trace data.
#[derive(Debug, Copy, Clone)]
pub enum TraceSink {
/// Trace data should be sent to the SWO peripheral.
///
/// # Note
/// On some architectures, there is no distinction between SWO and TPIU.
Swo(SwoConfig),
/// Trace data should be sent to the TPIU peripheral.
Tpiu(SwoConfig),
/// Trace data should be sent to the embedded trace buffer for software-based trace collection.
TraceMemory,
}
/// An error when operating a core ROM table component occurred.
#[derive(thiserror::Error, Debug)]
pub enum ComponentError {
/// Nordic chips do not support setting all TPIU clocks. Try choosing another clock speed.
#[error("Nordic does not support TPIU CLK value of {0}")]
NordicUnsupportedTPUICLKValue(u32),
}
/// A trait to be implemented on memory mapped register types for debug component interfaces.
pub trait DebugComponentInterface:
MemoryMappedRegister<u32> + Clone + From<u32> + Into<u32> + Sized + std::fmt::Debug
{
/// Loads the register value from the given debug component via the given core.
fn load(
component: &CoresightComponent,
interface: &mut dyn ArmProbeInterface,
) -> Result<Self, ArmError> {
Ok(Self::from(
component.read_reg(interface, Self::ADDRESS_OFFSET as u32)?,
))
}
/// Loads the register value from the given component in given unit via the given core.
fn load_unit(
component: &CoresightComponent,
interface: &mut dyn ArmProbeInterface,
unit: usize,
) -> Result<Self, ArmError> {
Ok(Self::from(component.read_reg(
interface,
Self::ADDRESS_OFFSET as u32 + 16 * unit as u32,
)?))
}
/// Stores the register value to the given debug component via the given core.
fn store(
&self,
component: &CoresightComponent,
interface: &mut dyn ArmProbeInterface,
) -> Result<(), ArmError> {
component.write_reg(interface, Self::ADDRESS_OFFSET as u32, self.clone().into())
}
/// Stores the register value to the given component in given unit via the given core.
fn store_unit(
&self,
component: &CoresightComponent,
interface: &mut dyn ArmProbeInterface,
unit: usize,
) -> Result<(), ArmError> {
component.write_reg(
interface,
Self::ADDRESS_OFFSET as u32 + 16 * unit as u32,
self.clone().into(),
)
}
}
/// Reads all the available ARM CoresightComponents of the currently attached target.
///
/// This will recursively parse the Romtable of the attached target
/// and create a list of all the contained components.
pub fn get_arm_components(
interface: &mut dyn ArmProbeInterface,
dp: DpAddress,
) -> Result<Vec<CoresightComponent>, ArmError> {
let mut components = Vec::new();
for ap_index in interface.access_ports(dp)? {
let component = if let Ok(mut memory) = interface.memory_interface(&ap_index) {
match memory.base_address()? {
0 => Err(Error::Other("AP has a base address of 0".to_string())),
debug_base_address => {
let component = Component::try_parse(&mut *memory, debug_base_address)?;
Ok(CoresightComponent::new(component, ap_index.clone()))
}
}
} else {
// Return an error, only possible to get Component from MemoryAP
Err(Error::Other(format!(
"AP {:#x?} is not a MemoryAP, unable to get ARM component.",
ap_index.clone()
)))
};
match component {
Ok(component) => {
components.push(component);
}
Err(e) => {
tracing::info!("Not counting AP {} because of: {}", ap_index.ap_v1()?, e);
}
}
}
Ok(components)
}
/// Goes through every component in the vector and tries to find the first component with the given type
pub fn find_component(
components: &[CoresightComponent],
peripheral_type: PeripheralType,
) -> Result<&CoresightComponent, ArmError> {
let component = components
.iter()
.find_map(|component| component.find_component(peripheral_type))
.ok_or_else(|| RomTableError::ComponentNotFound(peripheral_type))?;
Ok(component)
}
/// Configure the Trace Port Interface Unit
///
/// # Note
/// This configures the TPIU in serial wire mode.
///
/// # Args
/// * `interface` - The interface with the probe.
/// * `component` - The TPIU CoreSight component found.
/// * `config` - The SWO pin configuration to use.
fn configure_tpiu(
interface: &mut dyn ArmProbeInterface,
component: &CoresightComponent,
config: &SwoConfig,
) -> Result<(), Error> {
let mut tpiu = Tpiu::new(interface, component);
tpiu.set_port_size(1)?;
let prescaler = (config.tpiu_clk() / config.baud()) - 1;
tpiu.set_prescaler(prescaler)?;
match config.mode() {
SwoMode::Manchester => tpiu.set_pin_protocol(1)?,
SwoMode::Uart => tpiu.set_pin_protocol(2)?,
}
// Formatter: TrigIn enabled, bypass optional
if config.tpiu_continuous_formatting() {
// Set EnFCont for continuous formatting even over SWO.
tpiu.set_formatter(0x102)?;
} else {
// Clear EnFCont to only pass through raw ITM/DWT data.
tpiu.set_formatter(0x100)?;
}
Ok(())
}
/// Sets up all the SWV components.
///
/// Expects to be given a list of all ROM table `components` as the second argument.
pub(crate) fn setup_tracing(
interface: &mut dyn ArmProbeInterface,
components: &[CoresightComponent],
sink: &TraceSink,
) -> Result<(), Error> {
// Configure DWT
let mut dwt = Dwt::new(interface, find_component(components, PeripheralType::Dwt)?);
dwt.enable()?;
dwt.enable_exception_trace()?;
// Configure ITM
let mut itm = Itm::new(interface, find_component(components, PeripheralType::Itm)?);
itm.unlock()?;
itm.tx_enable()?;
// Configure the trace destination.
match sink {
TraceSink::Tpiu(config) => {
configure_tpiu(
interface,
find_component(components, PeripheralType::Tpiu)?,
config,
)?;
}
TraceSink::Swo(config) => {
if let Ok(peripheral) = find_component(components, PeripheralType::Swo) {
let mut swo = Swo::new(interface, peripheral);
swo.unlock()?;
let prescaler = (config.tpiu_clk() / config.baud()) - 1;
swo.set_prescaler(prescaler)?;
match config.mode() {
SwoMode::Manchester => swo.set_pin_protocol(1)?,
SwoMode::Uart => swo.set_pin_protocol(2)?,
}
} else {
// For Cortex-M4, the SWO and the TPIU are combined. If we don't find a SWO
// peripheral, use the TPIU instead.
configure_tpiu(
interface,
find_component(components, PeripheralType::Tpiu)?,
config,
)?;
}
}
TraceSink::TraceMemory => {
let mut tmc = TraceMemoryController::new(
interface,
find_component(components, PeripheralType::Tmc)?,
);
// Clear out the TMC FIFO before initiating the capture.
tmc.disable_capture()?;
while !tmc.ready()? {}
// Configure the TMC for software-polled mode, as we will read out data using the debug
// interface.
tmc.set_mode(tmc::Mode::Software)?;
tmc.enable_capture()?;
}
}
Ok(())
}
/// Read trace data from internal trace memory
///
/// # Args
/// * `interface` - The interface with the debug probe.
/// * `components` - The CoreSight debug components identified in the system.
///
/// # Note
/// This function will read any available trace data in trace memory without blocking. At most,
/// this function will read as much data as can fit in the FIFO - if the FIFO continues to be
/// filled while trace data is being extracted, this function can be called again to return that
/// data.
///
/// # Returns
/// All data stored in trace memory, with an upper bound at the size of internal trace memory.
pub(crate) fn read_trace_memory(
interface: &mut dyn ArmProbeInterface,
components: &[CoresightComponent],
) -> Result<Vec<u8>, ArmError> {
let mut tmc =
TraceMemoryController::new(interface, find_component(components, PeripheralType::Tmc)?);
let fifo_size = tmc.fifo_size()?;
// This sequence is taken from "CoreSight Trace memory Controller Technical Reference Manual"
// Section 2.2.2 "Software FIFO Mode". Without following this procedure, the trace data does
// not properly stop even after disabling capture.
// Read all of the data from the ETM into a vector for further processing.
let mut etf_trace: Vec<u8> = Vec::new();
loop {
match tmc.read()? {
Some(data) => etf_trace.extend_from_slice(&data.to_le_bytes()),
None => {
// If there's nothing available in the FIFO, we can only break out of reading if we
// have an integer number of formatted frames, which are 16 bytes each.
if (etf_trace.len() % 16) == 0 {
break;
}
}
}
// If the FIFO is being filled faster than we can read it, break out after reading a
// maximum number of frames.
let frame_boundary = (etf_trace.len() % 16) == 0;
if frame_boundary && etf_trace.len() >= fifo_size as usize {
break;
}
}
// The TMC formats data into frames, as it contains trace data from multiple data sources. We
// need to deserialize the frames and pull out only the data source of interest. For now, all
// we care about is the ITM data.
let mut id = 0.into();
let mut itm_trace = Vec::new();
// Process each formatted frame and extract the multiplexed trace data.
for frame_buffer in etf_trace.chunks_exact(16) {
let mut frame = tmc::Frame::new(frame_buffer, id);
for (id, data) in &mut frame {
match id.into() {
// ITM ATID, see Itm::tx_enable()
13 => itm_trace.push(data),
0 => (),
id => tracing::warn!("Unexpected trace source ATID {id}: {data}, ignoring"),
}
}
id = frame.id();
}
Ok(itm_trace)
}
/// Configures DWT trace unit `unit` to begin tracing `address`.
///
///
/// Expects to be given a list of all ROM table `components` as the second argument.
pub(crate) fn add_swv_data_trace(
interface: &mut dyn ArmProbeInterface,
components: &[CoresightComponent],
unit: usize,
address: u32,
) -> Result<(), ArmError> {
let mut dwt = Dwt::new(interface, find_component(components, PeripheralType::Dwt)?);
dwt.enable_data_trace(unit, address)
}
/// Configures DWT trace unit `unit` to stop tracing `address`.
///
///
/// Expects to be given a list of all ROM table `components` as the second argument.
pub fn remove_swv_data_trace(
interface: &mut dyn ArmProbeInterface,
components: &[CoresightComponent],
unit: usize,
) -> Result<(), ArmError> {
let mut dwt = Dwt::new(interface, find_component(components, PeripheralType::Dwt)?);
dwt.disable_data_trace(unit)
}
/// Sets TRCENA in DEMCR to begin trace generation.
pub fn enable_tracing(core: &mut Core) -> Result<(), Error> {
let mut demcr = Demcr(core.read_word_32(Demcr::get_mmio_address())?);
demcr.set_dwtena(true);
core.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
Ok(())
}
/// Disables TRCENA in DEMCR to disable trace generation.
pub fn disable_swv(core: &mut Core) -> Result<(), Error> {
let mut demcr = Demcr(core.read_word_32(Demcr::get_mmio_address())?);
demcr.set_dwtena(false);
core.write_word_32(Demcr::get_mmio_address(), demcr.into())?;
Ok(())
}