From caa1370d409b41f43d22d1243e2c6eb9fa9172b5 Mon Sep 17 00:00:00 2001 From: Laura Abbott Date: Wed, 15 Jan 2025 09:13:46 -0500 Subject: [PATCH] Remove hardware tracing support We never really got ITM/ETM working as well as we wanted and keeping this support is making it harder to update probe-rs. Just drop it for now and an eager person can bring it back later. --- Cargo.lock | 31 - Cargo.toml | 4 - cmd/etm/Cargo.toml | 16 - cmd/etm/src/lib.rs | 637 ------------------- cmd/hydrate/src/lib.rs | 2 - cmd/itm/Cargo.toml | 16 - cmd/itm/src/lib.rs | 359 ----------- cmd/probe/src/lib.rs | 28 - humility-arch-cortex/src/etm.rs | 806 ------------------------- humility-arch-cortex/src/itm.rs | 605 ------------------- humility-arch-cortex/src/lib.rs | 3 - humility-arch-cortex/src/tpiu.rs | 562 ----------------- humility-bin/Cargo.toml | 2 - humility-core/src/archive.rs | 8 - humility-core/src/core.rs | 2 - humility-core/src/dump.rs | 8 - humility-dump-agent/src/lib.rs | 8 - humility-net-core/src/lib.rs | 8 - humility-probes-core/src/gdb.rs | 8 - humility-probes-core/src/openocd.rs | 84 +-- humility-probes-core/src/probe_rs.rs | 19 - humility-probes-core/src/unattached.rs | 8 - 22 files changed, 1 insertion(+), 3223 deletions(-) delete mode 100644 cmd/etm/Cargo.toml delete mode 100644 cmd/etm/src/lib.rs delete mode 100644 cmd/itm/Cargo.toml delete mode 100644 cmd/itm/src/lib.rs delete mode 100644 humility-arch-cortex/src/etm.rs delete mode 100644 humility-arch-cortex/src/itm.rs delete mode 100644 humility-arch-cortex/src/tpiu.rs diff --git a/Cargo.lock b/Cargo.lock index a6f821bfe..c7949de03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1282,7 +1282,6 @@ dependencies = [ "humility-cmd-diagnose", "humility-cmd-doc", "humility-cmd-dump", - "humility-cmd-etm", "humility-cmd-exec", "humility-cmd-extract", "humility-cmd-flash", @@ -1295,7 +1294,6 @@ dependencies = [ "humility-cmd-hydrate", "humility-cmd-i2c", "humility-cmd-ibc", - "humility-cmd-itm", "humility-cmd-jefe", "humility-cmd-lpc55gpio", "humility-cmd-lsusb", @@ -1538,21 +1536,6 @@ dependencies = [ "zip", ] -[[package]] -name = "humility-cmd-etm" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 3.2.23", - "csv", - "humility-cli", - "humility-cmd", - "humility-core", - "humility-cortex", - "log", - "parse_int", -] - [[package]] name = "humility-cmd-exec" version = "0.1.0" @@ -1751,20 +1734,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "humility-cmd-itm" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 3.2.23", - "csv", - "humility-cli", - "humility-cmd", - "humility-core", - "humility-cortex", - "parse_int", -] - [[package]] name = "humility-cmd-jefe" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 99bf8c55c..7f458825e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ members = [ "cmd/doc", "cmd/dump", "cmd/tofino-eeprom", - "cmd/etm", "cmd/exec", "cmd/extract", "cmd/flash", @@ -45,7 +44,6 @@ members = [ "cmd/rpc", "cmd/i2c", "cmd/ibc", - "cmd/itm", "cmd/jefe", "cmd/lpc55gpio", "cmd/lsusb", @@ -139,7 +137,6 @@ cmd-debugmailbox = { path = "./cmd/debugmailbox", package = "humility-cmd-debugm cmd-doc = { path = "./cmd/doc", package = "humility-cmd-doc" } cmd-dump = { path = "./cmd/dump", package = "humility-cmd-dump" } cmd-tofino-eeprom = { path = "./cmd/tofino-eeprom", package = "humility-cmd-tofino-eeprom" } -cmd-etm = { path = "./cmd/etm", package = "humility-cmd-etm" } cmd-exec = { path = "./cmd/exec", package = "humility-cmd-exec" } cmd-extract = { path = "./cmd/extract", package = "humility-cmd-extract" } cmd-flash = { path = "./cmd/flash", package = "humility-cmd-flash" } @@ -152,7 +149,6 @@ cmd-host = { path = "./cmd/host", package = "humility-cmd-host" } cmd-hydrate = { path = "./cmd/hydrate", package = "humility-cmd-hydrate" } cmd-i2c = { path = "./cmd/i2c", package = "humility-cmd-i2c" } cmd-ibc = { path = "./cmd/ibc", package = "humility-cmd-ibc" } -cmd-itm = { path = "./cmd/itm", package = "humility-cmd-itm" } cmd-jefe = { path = "./cmd/jefe", package = "humility-cmd-jefe" } cmd-lpc55gpio = { path = "./cmd/lpc55gpio", package = "humility-cmd-lpc55gpio" } cmd-lsusb = { path = "./cmd/lsusb", package = "humility-cmd-lsusb" } diff --git a/cmd/etm/Cargo.toml b/cmd/etm/Cargo.toml deleted file mode 100644 index 767405d0b..000000000 --- a/cmd/etm/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "humility-cmd-etm" -version = "0.1.0" -edition = "2021" -description = "commands for ARM's Embedded Trace Macrocell (ETM)" - -[dependencies] -humility = { workspace = true } -humility-cortex = { workspace = true } -humility-cmd = { workspace = true } -humility-cli = { workspace = true } -clap = { workspace = true } -anyhow = { workspace = true } -csv = { workspace = true } -parse_int = { workspace = true } -log = { workspace = true } diff --git a/cmd/etm/src/lib.rs b/cmd/etm/src/lib.rs deleted file mode 100644 index 94ab3aa00..000000000 --- a/cmd/etm/src/lib.rs +++ /dev/null @@ -1,637 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! ## `humility etm` -//! -//! Enables and operates upon the Embedded Trace Macrocell (ETM) found in -//! some ARM Cortex-M parts. -//! - -use anyhow::{bail, Result}; -use clap::{CommandFactory, Parser}; -use humility::core::Core; -use humility::hubris::*; -use humility::warn; -use humility_cli::{ExecutionContext, Subcommand}; -use humility_cmd::{attach_live, CommandKind}; -use humility_cmd::{Archive, Command}; -use humility_cortex::debug::*; -use humility_cortex::etm::*; -use humility_cortex::scs::*; -use humility_cortex::tpiu::*; -use std::fs::File; -use std::time::Instant; - -#[derive(Parser, Debug)] -#[clap(name = "etm", about = env!("CARGO_PKG_DESCRIPTION"))] -struct EtmArgs { - /// probe for ETM capability on attached device - #[clap( - long, short, conflicts_with_all = &["enable", "disable", "ingest"] - )] - probe: bool, - /// enable ETM on attached device - #[clap(long, short, conflicts_with_all = &["disable", "ingest"])] - enable: bool, - /// disable ETM on attached device - #[clap(long, short)] - disable: bool, - /// sets ETM trace identifier - #[clap( - long, short, value_name = "identifier", - default_value_t = 0x54, parse(try_from_str = parse_int::parse), - )] - traceid: u8, - /// ingest ETM data as CSV - #[clap(long, short, value_name = "filename")] - ingest: Option, - /// flowindent ingested data - #[clap(long, short = 'F')] - flowindent: bool, - /// sets the value of SWOSCALER - #[clap( - long, short, value_name = "scaler", requires = "enable", - parse(try_from_str = parse_int::parse) - )] - clockscaler: Option, - /// output ETM data as CSV - #[clap(long, short, conflicts_with = "ingest")] - output: bool, -} - -struct TraceInstruction { - nsecs: u64, - addr: u32, - _len: u32, - target: Option, - skipped: bool, -} - -struct TraceException { - nsecs: u64, - exception: ETM3Exception, -} - -#[derive(Debug)] -struct TraceConfig<'a> { - hubris: &'a HubrisArchive, - flowindent: bool, - traceid: u8, -} - -#[derive(Debug, Default)] -struct TraceState { - indent: usize, - target: Option, - inlined: Vec, - stack: Vec<(usize, Vec, u32)>, -} - -const HUMILITY_ETM_SWOSCALER: u16 = 7; -const HUMILITY_ETM_TRACEID_MAX: u8 = 0x7f; -const HUMILITY_ETM_ALWAYSTRUE: u32 = 0b110_1111; - -fn etmcmd_probe(core: &mut dyn Core) -> Result<()> { - let coreinfo = CoreInfo::read(core)?; - - let etm = match coreinfo.address(CoreSightComponent::ETM) { - None => { - bail!("ETM is not available on this CPU"); - } - Some(etm) => etm, - }; - - if etm != ETMCR::ADDRESS { - bail!( - "ETM base address (0x{:x}) is not at expected location ({:x})", - etm, - ETMCR::ADDRESS - ); - } - - let etmccr = ETMCCR::read(core)?; - humility::msg!("{etmccr:#x?}"); - - if !etmccr.has_etmidr() { - warn!("ETMv1.3 and earlier not supported"); - return Ok(()); - } - - let etmidr = ETMIDR::read(core)?; - humility::msg!("{etmidr:#x?}"); - - let etmccer = ETMCCER::read(core)?; - humility::msg!("{etmccer:#x?}"); - - Ok(()) -} - -fn etmcmd_enable( - core: &mut dyn Core, - clockscaler: Option, - traceid: u8, -) -> Result<()> { - let etmccr = ETMCCR::read(core)?; - - if !etmccr.has_etmidr() { - warn!("ETMv1.3 and earlier not supported"); - return Ok(()); - } - - let etmidr = ETMIDR::read(core)?; - log::trace!("{:?}", etmidr); - - let major = etmidr.etm_major() + 1; - let minor = etmidr.etm_minor(); - - if (major, minor) != (3, 5) { - warn!("only ETMv3.5 supported"); - return Ok(()); - } - - if !etmidr.has_branch_encoding() { - warn!("only alternative branch encoding supported"); - return Ok(()); - } - - // - // First, enable TRCENA in the DEMCR. - // - let mut val = DEMCR::read(core)?; - val.set_trcena(true); - val.write(core)?; - - // - // Now unlock the ETM. - // - ETMLAR::unlock(core)?; - - // - // STM32F407-specific: enable TRACE_IOEN in the DBGMCU_CR, and set the - // trace mode to be asynchronous. - // - let mut val = STM32F4_DBGMCU_CR::read(core)?; - val.set_trace_ioen(true); - val.set_trace_mode(0); - val.write(core)?; - - // - // Now setup the TPIU. - // - let mut val = TPIU_SPPR::read(core)?; - val.set_txmode(TPIUMode::NRZ); - val.write(core)?; - - let mut val = TPIU_FFCR::read(core)?; - val.set_continuous_formatting(true); - val.write(core)?; - - let mut acpr = TPIU_ACPR::read(core)?; - acpr.set_swoscaler(clockscaler.unwrap_or(HUMILITY_ETM_SWOSCALER).into()); - acpr.write(core)?; - log::trace!("{:#x?}", TPIU_ACPR::read(core)?); - - // - // We are now ready to enable ETM. There are a bunch of steps involved - // in this, but we need to first write to the ETMCR to indicate that - // we are programming it. Once done writing to the ETM control - // registers, we need to write to ETMCR again to indicate that we are - // done programming it. - // - log::trace!("{:#x?}", ETMCR::read(core)?); - let mut etmcr = ETMCR::read(core)?; - etmcr.set_branch_output(true); - etmcr.set_stall_processor(true); - etmcr.set_port_size(1); - etmcr.set_port_select(true); - etmcr.set_programming(true); - etmcr.set_power_down(false); - log::trace!("will write {:#x?}", etmcr); - etmcr.write(core)?; - - // - // Set to the hard-wired always-true event - // - let mut teevr = ETMTEEVR::read(core)?; - teevr.set_resource_a(HUMILITY_ETM_ALWAYSTRUE); - teevr.write(core)?; - log::trace!("{:#x?}", ETMTEEVR::read(core)?); - - let mut tecr1 = ETMTECR1::read(core)?; - tecr1.set_map_decode_select(0); - tecr1.set_comparator_select(0); - tecr1.set_exclude(true); - tecr1.write(core)?; - - let mut ffrr = ETMFFRR::read(core)?; - ffrr.set_map_decode_select(0); - ffrr.set_comparator_select(0); - ffrr.set_exclude(true); - ffrr.write(core)?; - - let mut fflr = ETMFFLR::read(core)?; - fflr.set_fifo_full_level(24); - fflr.write(core)?; - - log::trace!("{:#x?}", ETMFFLR::read(core)?); - - log::trace!("{:#x?}", ETMTRACEIDR::read(core)?); - let mut val = ETMTRACEIDR::read(core)?; - val.set_traceid(traceid.into()); - val.write(core)?; - log::trace!("{:#x?}", ETMTRACEIDR::read(core)?); - - // - // Finally, indicate that we are done programming! - // - etmcr.set_programming(false); - etmcr.write(core)?; - - humility::msg!("ETM enabled"); - - Ok(()) -} - -fn etmcmd_disable(core: &mut dyn Core) -> Result<()> { - let mut etmcr = ETMCR::read(core)?; - - if etmcr.power_down() { - humility::msg!("ETM not enabled"); - return Ok(()); - } - - etmcr.set_programming(true); - etmcr.write(core)?; - - etmcr.set_power_down(true); - etmcr.write(core)?; - - etmcr.set_programming(false); - etmcr.write(core)?; - - humility::msg!("ETM disabled"); - - Ok(()) -} - -#[rustfmt::skip::macros(println)] - -fn etmcmd_trace( - config: &TraceConfig, - instr: &TraceInstruction, - state: &mut TraceState, -) -> Result<()> { - let hubris = config.hubris; - let addr = instr.addr; - let c = if !instr.skipped { 'E' } else { 'N' }; - let module = hubris.instr_mod(addr).unwrap_or(""); - let sym = hubris.instr_sym(addr).unwrap_or(("", addr)); - let sigil = 2; - - if !config.flowindent { - println!("{:-10} {:08x} {} {}:{}+{:x} {:x?}", - instr.nsecs, addr, c, module, sym.0, addr - sym.1, instr.target); - return Ok(()); - } - - let inlined = hubris.instr_inlined(addr, sym.1); - - match state.target { - Some(HubrisTarget::Call(_)) | Some(HubrisTarget::IndirectCall) => { - state.indent += 2; - println!("{:-10} {:width$}-> {}:{}", instr.nsecs, "", module, sym.0, - width = state.indent); - } - None => { - println!("{:-10} {:width$} ? {}:{}", instr.nsecs, "", module, sym.0, - width = state.indent); - } - _ => {} - } - - for (i, element) in inlined.iter().enumerate() { - if i < state.inlined.len() && element.id == state.inlined[i] { - continue; - } - - println!("{:-10} {:width$} | {}:{} {}", instr.nsecs, "", module, - element.name, element.id, - width = state.indent + (i * 2) + sigil); - } - - state.inlined.clear(); - - state.target = instr.target; - - match instr.target { - Some(HubrisTarget::Call(_)) | Some(HubrisTarget::IndirectCall) => { - let mut nindent = state.indent; - - if !inlined.is_empty() { - nindent += (inlined.len() * 2) + 1; - } - - state.stack.push(( - state.indent, - inlined.iter().map(|i| i.id).collect(), - instr.addr, - )); - - state.indent = nindent; - - return Ok(()); - } - - Some(HubrisTarget::Return) => { - println!("{:-10} {:width$}<- {}:{}", instr.nsecs, "", module, sym.0, - width = state.indent); - - if !state.stack.is_empty() { - let top = state.stack.pop().unwrap(); - - state.inlined = top.1; - state.indent = top.0; - } else { - state.indent = 0; - } - } - _ => { - state.inlined = inlined.iter().map(|i| i.id).collect(); - } - } - - Ok(()) -} - -fn etmcmd_trace_exception( - _config: &TraceConfig, - exception: &TraceException, - _state: &mut TraceState, -) -> Result<()> { - println!("{:-10} {:8} X {:?}", exception.nsecs, "-", exception.exception); - - Ok(()) -} - -fn etmcmd_ingest(config: &TraceConfig, filename: &str) -> Result<()> { - let file = File::open(filename)?; - let mut rdr = csv::Reader::from_reader(file); - let mut curaddr: Option = None; - let mut lastaddr: Option = None; - let hubris = config.hubris; - - let econfig = &ETM3Config { - alternative_encoding: true, - context_id: 0, - data_access: false, - traceid: config.traceid, - }; - - type SaleaeTraceRecord = (f64, u8, Option, Option); - - let mut iter = rdr.deserialize(); - let mut broken = false; - let mut target: (Option, Option) = (None, None); - - let mut state = TraceState::default(); - - etm_ingest( - econfig, - || { - if let Some(line) = iter.next() { - let record: SaleaeTraceRecord = line?; - Ok(Some((record.1, record.0))) - } else { - Ok(None) - } - }, - |packet| { - let nsecs = (packet.time * 1_000_000_000_f64) as u64; - - match (lastaddr, packet.header) { - (None, ETM3Header::ISync) | (Some(_), _) => {} - (None, _) => { - if broken { - return Ok(()); - } - - bail!("non-ISync packet at time {}", nsecs); - } - } - - let mut instr = |skipped| { - if broken { - return Ok(()); - } - - let addr = curaddr.unwrap(); - let mut l = 0; - - curaddr = match hubris.instr_len(addr) { - Some(len) => { - l = len; - Some(addr + len) - } - None => { - warn!("unknown instruction length at {:x}!", addr); - broken = true; - None - } - }; - - target = (Some(addr), hubris.instr_target(addr)); - etmcmd_trace( - config, - &TraceInstruction { - nsecs, - addr, - target: target.1, - _len: l, - skipped, - }, - &mut state, - ) - }; - - println!("{:#x?}", packet); - - match packet.header { - ETM3Header::PHeaderFormat1 { e, n } => { - for _i in 0..e { - instr(false)?; - } - - for _i in 0..n { - instr(true)?; - } - } - ETM3Header::PHeaderFormat2 { e0, e1 } => { - instr(e0)?; - instr(e1)?; - } - ETM3Header::ExceptionExit - | ETM3Header::ASync - | ETM3Header::ISync - | ETM3Header::BranchAddress { .. } => {} - _ => { - bail!("unhandled packet: {:#x?}", packet); - } - } - - match packet.payload { - ETM3Payload::ISync { address, .. } => { - if broken { - warn!("re-railing at offset {}", packet.offset); - broken = false; - target = (None, None); - } - - curaddr = Some(address); - lastaddr = curaddr; - } - ETM3Payload::BranchAddress { addr, mask, exception } => { - curaddr = Some((lastaddr.unwrap() & mask) | addr); - lastaddr = curaddr; - - match target { - ( - Some(origin), - Some(HubrisTarget::Direct(expected)), - ) - | (Some(origin), Some(HubrisTarget::Call(expected))) => { - if curaddr.unwrap() != expected { - warn!( - "detected bad branch: at 0x{:x} expected \ - branch to 0x{:x}, found 0x{:x}; packet: {:x?}", - origin, - expected, - curaddr.unwrap(), - packet - ); - } - } - - (Some(origin), None) => { - if exception.is_none() { - warn!( - "detected bad branch: did not expect any \ - branch from 0x{:x}, but control transferred \ - to 0x{:x}; packet: {:x?}", - origin, - curaddr.unwrap(), - packet - ); - } - } - - (_, _) => {} - } - - if let Some(exception) = exception { - etmcmd_trace_exception( - config, - &TraceException { nsecs, exception }, - &mut state, - )?; - } - } - ETM3Payload::None => {} - } - - Ok(()) - }, - )?; - - Ok(()) -} - -fn etmcmd_output(core: &mut dyn Core) -> Result<()> { - let start = Instant::now(); - - println!("Time [s],Value,Parity Error,Framing Error"); - - loop { - let bytes = core.read_swv().unwrap(); - - for b in bytes { - println!("{:.15},0x{:02X},,", start.elapsed().as_secs_f64(), b); - } - } -} - -fn etmcmd(context: &mut ExecutionContext) -> Result<()> { - let Subcommand::Other(subargs) = context.cli.cmd.as_ref().unwrap(); - let hubris = context.archive.as_ref().unwrap(); - - let subargs = &EtmArgs::try_parse_from(subargs)?; - let mut rval = Ok(()); - - let traceid = subargs.traceid; - - if traceid >= HUMILITY_ETM_TRACEID_MAX { - bail!("traceid has a maximum value of {:x}", HUMILITY_ETM_TRACEID_MAX); - } - - if let Some(ingest) = &subargs.ingest { - let config = TraceConfig { - hubris, - flowindent: subargs.flowindent, - traceid: subargs.traceid, - }; - - match etmcmd_ingest(&config, ingest) { - Err(e) => { - bail!("failed to ingest {}: {}", ingest, e); - } - _ => { - return Ok(()); - } - } - } - - // - // For all of the other commands, we need to actually attach to the chip. - // - let mut core = attach_live(&context.cli, hubris)?; - core.halt()?; - - humility::msg!("core halted"); - - if subargs.probe { - rval = etmcmd_probe(core.as_mut()); - } - - if subargs.enable { - rval = etmcmd_enable(core.as_mut(), subargs.clockscaler, traceid); - } - - if subargs.disable { - rval = etmcmd_disable(core.as_mut()); - } - - core.run()?; - humility::msg!("core resumed"); - - if subargs.output { - match etmcmd_output(core.as_mut()) { - Err(e) => { - bail!("failed to output from attached device: {}", e); - } - _ => { - return Ok(()); - } - } - } - - rval -} - -pub fn init() -> Command { - Command { - app: EtmArgs::command(), - name: "etm", - run: etmcmd, - kind: CommandKind::Unattached { archive: Archive::Required }, - } -} diff --git a/cmd/hydrate/src/lib.rs b/cmd/hydrate/src/lib.rs index c6bef4a9f..32457ca38 100644 --- a/cmd/hydrate/src/lib.rs +++ b/cmd/hydrate/src/lib.rs @@ -69,8 +69,6 @@ impl humility::core::Core for DryCore { unsupported!(op_done()); unsupported!(op_start()); unsupported!(read_reg(_reg: ARMRegister) -> Result); - unsupported!(init_swv()); - unsupported!(read_swv() -> Result>); unsupported!(write_reg(_reg: ARMRegister, _value: u32)); unsupported!(write_word_32(_addr: u32, _data: u32)); unsupported!(reset_and_halt(_dur: std::time::Duration)); diff --git a/cmd/itm/Cargo.toml b/cmd/itm/Cargo.toml deleted file mode 100644 index 65c93839f..000000000 --- a/cmd/itm/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "humility-cmd-itm" -version = "0.1.0" -edition = "2021" -description = "commands for ARM's Instrumentation Trace Macrocell (ITM)" - -[dependencies] -humility = { workspace = true } -humility-cortex = { workspace = true } -humility-cli = { workspace = true } -humility-cmd = { workspace = true } - -clap = { workspace = true } -anyhow = { workspace = true } -csv = { workspace = true } -parse_int = { workspace = true } diff --git a/cmd/itm/src/lib.rs b/cmd/itm/src/lib.rs deleted file mode 100644 index 03810ecc4..000000000 --- a/cmd/itm/src/lib.rs +++ /dev/null @@ -1,359 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! ## `humility itm` -//! -//! `humility itm` consumes data from the Instrumentation Trace Macrocell -//! (ITM) present in many ARM Cortex-M variants. ITM is problematic in many -//! dimensions: it is lossy; it requires knowledge of the target's clocking to -//! configure properly; it relies on functionality (SWO/SWV) that is often -//! buggy in chip debuggers; it isn't present everywhere (Cortex-M0+ in -//! particular doesn't have ITM). So in general, ITM isn't what Hubris -//! programmers should be looking for: those developing code and wishing to -//! see if and how that code is executed should prefer ring buffers to -//! ITM-based instrumentation. (See the documentation for `humility ringbuf` -//! for details.) -//! -//! That said, ITM remains the best way to get certain messages from the -//! Hubris kernel (e.g., boot and panic messages); use `humility itm -ea` to -//! enable ITM and attach to the connected device. For example, if running -//! with the `ping` task, one will see messages from `jefe` restarting it: -//! -//! ```console -//! $ humility -a /path/to/my/hubris-archive.zip itm -ea -//! humility: attached via ST-Link -//! humility: core halted -//! humility: core resumed -//! humility: ITM synchronization packet found at offset 6 -//! Task #7 Divide-by-zero -//! Task #7 Memory fault at address 0x0 -//! Task #7 Divide-by-zero -//! ``` -//! - -use anyhow::{bail, Context, Result}; -use clap::{CommandFactory, Parser}; -use humility::core::Core; -use humility::hubris::*; -use humility_cli::{ExecutionContext, Subcommand}; -use humility_cmd::{attach_live, CommandKind}; -use humility_cmd::{Archive, Command}; -use humility_cortex::debug::*; -use humility_cortex::dwt::*; -use humility_cortex::itm::*; -use humility_cortex::scs::*; -use humility_cortex::tpiu::*; -use std::fs::File; -use std::io::Read; -use std::time::Instant; - -const ITM_TRACEID_MAX: u8 = 0x7f; - -#[derive(Parser, Debug)] -#[clap(name = "itm", about = env!("CARGO_PKG_DESCRIPTION"))] -struct ItmArgs { - /// probe for ITM capability on attached device - #[clap( - long, short, conflicts_with_all = &["enable", "disable", "ingest"] - )] - probe: bool, - - /// enable ITM on attached device - #[clap(long, short, conflicts_with_all = &["disable", "ingest"])] - enable: bool, - - /// disable ITM on attached device - #[clap(long, short)] - disable: bool, - - /// sets ITM trace identifier - #[clap( - long, short, default_value_t = 0x3a, value_name = "identifier", - parse(try_from_str = parse_int::parse) - )] - traceid: u8, - - /// ingest ITM data as CSV - #[clap(long, short, value_name = "filename")] - ingest: Option, - - /// ingest directly from attached device - #[clap(long, short, conflicts_with_all = &["disable", "ingest"])] - attach: bool, - - /// assume bypassed TPIU in ingested file - #[clap(long, short, requires = "ingest")] - bypass: bool, - - /// sets the value of SWOSCALER - #[clap(long, short, value_name = "scaler", requires = "enable", - parse(try_from_str = parse_int::parse), - )] - clockscaler: Option, - - /// reset target - #[clap(long, short, requires = "attach")] - reset: bool, -} - -fn itmcmd_probe(core: &mut dyn Core, coreinfo: &CoreInfo) -> Result<()> { - humility::msg!("{:#x?}", TPIU_ACPR::read(core)?); - humility::msg!("{:#x?}", TPIU_SPPR::read(core)?); - humility::msg!("{:#x?}", TPIU_FFCR::read(core)?); - - humility::msg!("{:#x?}", ITM_LSR::read(core)?); - humility::msg!("{:#x?}", ITM_TCR::read(core)?); - humility::msg!("{:#x?}", ITM_TER::read(core)?); - humility::msg!("{:#x?}", ITM_TPR::read(core)?); - - humility::msg!("{:#x?}", DWT_CTRL::read(core)?); - - humility::msg!("{:#x?}", DEMCR::read(core)?); - - match (coreinfo.vendor, coreinfo.part) { - (Vendor::ST, ARMCore::CortexM4) => { - humility::msg!("{:#x?}", STM32F4_DBGMCU_CR::read(core)?); - } - (Vendor::ST, ARMCore::CortexM7) => { - humility::msg!("{:#x?}", STM32H7_DBGMCU_CR::read(core)?); - } - _ => {} - } - - Ok(()) -} - -fn itmcmd_disable(core: &mut dyn Core) -> Result<()> { - // - // Unlock the ITM. - // - ITM_LAR::unlock(core)?; - - // - // Disable the ITM. - // - let mut tcr = ITM_TCR::read(core)?; - tcr.set_itm_enable(false); - tcr.write(core)?; - - // - // Now disable TRCENA in the DEMCR. - // - let mut val = DEMCR::read(core)?; - val.set_trcena(false); - val.write(core)?; - - humility::msg!("ITM disabled"); - - Ok(()) -} - -fn itmcmd_ingest(subargs: &ItmArgs, filename: &str) -> Result<()> { - let file = File::open(filename)?; - let traceid = if subargs.bypass { None } else { Some(subargs.traceid) }; - - let process = |packet: &ITMPacket| -> Result<()> { - if let ITMPayload::Instrumentation { payload, .. } = &packet.payload { - for p in payload { - print!("{}", *p as char); - } - } - - Ok(()) - }; - - let mut rdr = csv::Reader::from_reader(file); - - match rdr.headers() { - Ok(_hdr) => { - type SaleaeTraceRecord = (f64, u8, Option, Option); - let mut iter = rdr.deserialize(); - - itm_ingest( - traceid, - || { - if let Some(line) = iter.next() { - let record: SaleaeTraceRecord = line?; - Ok(Some((record.1, record.0))) - } else { - Ok(None) - } - }, - process, - ) - } - Err(_) => { - humility::msg!("not a Saleae trace file; assuming raw input"); - - let mut file = File::open(filename)?; - let mut buffer = [0; 1]; - - itm_ingest( - traceid, - || { - let nbytes = file.read(&mut buffer)?; - - match nbytes { - 1 => Ok(Some((buffer[0], 0.0))), - 0 => Ok(None), - _ => { - panic!("illegal read"); - } - } - }, - process, - ) - } - } -} - -fn itmcmd_ingest_attached( - core: &mut dyn Core, - coreinfo: &CoreInfo, - subargs: &ItmArgs, -) -> Result<()> { - let mut bytes: Vec = vec![]; - let mut ndx = 0; - - let traceid = if coreinfo.address(CoreSightComponent::SWO).is_some() { - None - } else { - Some(subargs.traceid) - }; - - let start = Instant::now(); - - itm_ingest( - traceid, - || { - while ndx == bytes.len() { - bytes = core.read_swv()?; - ndx = 0; - } - ndx += 1; - Ok(Some((bytes[ndx - 1], start.elapsed().as_secs_f64()))) - }, - |packet| { - if let ITMPayload::Instrumentation { payload, port } = - &packet.payload - { - if *port > 1 { - println!("{:x?}", payload); - return Ok(()); - } - - for p in payload { - print!("{}", *p as char); - } - } - - Ok(()) - }, - ) -} - -fn itmcmd(context: &mut ExecutionContext) -> Result<()> { - let Subcommand::Other(subargs) = context.cli.cmd.as_ref().unwrap(); - let hubris = context.archive.as_ref().unwrap(); - - let subargs = &ItmArgs::try_parse_from(subargs)?; - let mut rval = Ok(()); - - let traceid = subargs.traceid; - - if traceid >= ITM_TRACEID_MAX { - bail!("traceid has a maximum value of {:x}", ITM_TRACEID_MAX); - } - - if let Some(ingest) = &subargs.ingest { - match itmcmd_ingest(subargs, ingest) { - Err(e) => { - bail!("failed to ingest {}: {}", ingest, e); - } - _ => { - return Ok(()); - } - } - } - - // - // For all of the other commands, we need to actually attach to the chip. - // - let mut c = attach_live(&context.cli, hubris)?; - let core = c.as_mut(); - hubris.validate(core, HubrisValidate::ArchiveMatch)?; - - let coreinfo = CoreInfo::read(core)?; - - let _info = core.halt(); - humility::msg!("core halted"); - - if subargs.probe { - rval = itmcmd_probe(core, &coreinfo); - } - - if subargs.disable { - rval = itmcmd_disable(core); - } - - if subargs.enable { - if subargs.attach { - core.init_swv()?; - } - - // - // By default, we enable all logging (ports 0-7). - // - let stim = 0x0000_000f; - let clockscaler = match subargs.clockscaler { - Some(value) => value, - None => { - if !hubris.loaded() { - core.run()?; - bail!("must provide an archive"); - } - - swoscaler(hubris, core).with_context(|| { - "CPU frequency cannot be determined; the clock scaler \ - must be set manually. To determine the clock scaler, \ - take the CPU frequency in megahertz divide by 2, and \ - subtract 1 (e.g., 400 MHz yields a clock scaler of 199), \ - and specify via \"-c\" (e.g. \"-c 199\")" - })? - } - }; - - rval = itm_enable_explicit(core, &coreinfo, clockscaler, traceid, stim); - } - - core.run()?; - humility::msg!("core resumed"); - - if subargs.reset { - core.reset()?; - humility::msg!("core reset"); - } - - if rval.is_ok() && subargs.attach { - match itmcmd_ingest_attached(core, &coreinfo, subargs) { - Err(e) => { - bail!("failed to ingest from attached device: {}", e); - } - _ => { - return Ok(()); - } - } - } - - rval -} - -pub fn init() -> Command { - Command { - app: ItmArgs::command(), - name: "itm", - run: itmcmd, - kind: CommandKind::Unattached { archive: Archive::Optional }, - } -} diff --git a/cmd/probe/src/lib.rs b/cmd/probe/src/lib.rs index cb93a2efc..243e0041e 100644 --- a/cmd/probe/src/lib.rs +++ b/cmd/probe/src/lib.rs @@ -94,7 +94,6 @@ use humility_cli::{ExecutionContext, Subcommand}; use humility_cmd::CommandKind; use humility_cmd::{Archive, Attach, Command, Validate}; use humility_cortex::debug::*; -use humility_cortex::itm::*; use humility_cortex::scs::*; #[derive(Parser, Debug)] @@ -318,33 +317,6 @@ fn probecmd(context: &mut ExecutionContext) -> Result<()> { humility::msg!("{:>12} => {}", component.0, addrs); } - print( - "ITM status", - match coreinfo.address(CoreSightComponent::ITM) { - None => "absent".to_string(), - Some(_) => { - let mut itm = vec![]; - - if DEMCR::read(core)?.trcena() { - itm.push("TRCENA enabled"); - } else { - itm.push("TRCENA disabled"); - } - - if ITM_TCR::read(core)?.itm_enable() { - itm.push("TCR enabled") - } else { - itm.push("TCR disabled") - } - - let s = format!("TER=0x{:x}", u32::from(ITM_TER::read(core)?)); - - itm.push(&s); - itm.join(", ") - } - }, - ); - if !dhcsr.halted() { core.halt()?; } diff --git a/humility-arch-cortex/src/etm.rs b/humility-arch-cortex/src/etm.rs deleted file mode 100644 index 53c836775..000000000 --- a/humility-arch-cortex/src/etm.rs +++ /dev/null @@ -1,806 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::debug::Register; -use crate::register; -use crate::tpiu::*; -use anyhow::Result; -use bitfield::bitfield; -use humility::core::Core; - -macro_rules! etm_register { - ($reg:ty, $offs:expr, $($arg:tt)*) => ( - register!($reg, 0xe004_1000 + ($offs * 4), $($arg)*); - ) -} - -// -// ETM Main Configuration Register -// -etm_register!(ETMCR, 0x000, - #[derive(Copy, Clone)] - pub struct ETMCR(u32); - impl Debug; - pub vmid_trace_enable, _: 30; - pub timestamp_enable, set_timestamp_enable: 28; - pub processor_select, set_processor_select: 27, 25; - pub instrumentation_access_control, _: 24; - pub disable_software_writes, _: 23; - pub disable_debugger_writes, _: 22; - pub port_size_hibit, _: 21; - pub data_only_mode, _: 20; - pub filter_cprt, set_filter_cprt: 19; - pub suppress_data, set_suppress_data: 18; - pub port_mode, set_port_mode: 17, 16; - pub context_id, set_context_id: 15, 14; - pub half_rate_clocking, set_half_rate_clocking: 13; - pub cycle_accurate_tracing, set_cycle_accurate_tracing: 12; - pub port_select, set_port_select: 11; - pub programming, set_programming: 10; - pub debug_request_control, _: 9; - pub branch_output, set_branch_output: 8; - pub stall_processor, set_stall_processor: 7; - pub port_size, set_port_size: 6, 4; - pub data_access, set_data_access: 3, 2; - pub monitor_cprt, set_monitor_cprt: 1; - pub power_down, set_power_down: 0; -); - -// -// ETM Configuration Code Register -// -etm_register!(ETMCCR, 0x001, - #[derive(Copy, Clone)] - pub struct ETMCCR(u32); - impl Debug; - pub has_etmidr, _: 31; - pub protocol, _: 30, 28; - pub can_memmap, _: 27; - pub has_startstop, _: 26; - pub num_context_comparators, _: 25, 24; - pub has_fifofull, _: 23; - pub num_external_outputs, _: 22, 20; - pub num_external_inputs, _: 19, 17; - pub has_sequencer, _: 16; - pub num_counters, _: 15, 13; - pub num_decoder_inputs, _: 12, 8; - pub num_data_comparators, _: 7, 4; - pub num_address_comparators, _: 3, 0; -); - -// -// ETM Status Register -// -etm_register!(ETMSR, 0x004, - #[derive(Copy, Clone)] - pub struct ETMSR(u32); - impl Debug; - pub trigger, _: 3; - pub trace_startstop_status, _: 2; - pub programming_status, _: 1; - pub untraced_overflow, _: 0; -); - -// -// ETM System Configuration Register -// -etm_register!(ETMSCR, 0x005, - #[derive(Copy, Clone)] - pub struct ETMSCR(u32); - impl Debug; - pub no_fetch_comparisons, _: 17; - pub num_supported_processors, _: 14, 12; - pub has_port_mode, _: 11; - pub has_port_size, _: 10; - pub max_port_size_highbit, _: 9; - pub has_fifofull, _: 8; - pub has_demux, _: 7; - pub has_mux, _: 6; - pub has_normal, _: 5; - pub has_fullrate_clocking, _: 4; - pub has_halfrate_clocking, _: 3; - pub max_port_size, _: 2, 0; -); - -// -// ETM TraceEnable Event Register -// -etm_register!(ETMTEEVR, 0x008, - #[derive(Copy, Clone)] - pub struct ETMTEEVR(u32); - impl Debug; - pub fcn, _: 16, 14; - pub resource_b, set_resource_b: 13, 7; - pub resource_a, set_resource_a: 6, 0; -); - -// -// ETM TraceEnable Control 1 Register -// -etm_register!(ETMTECR1, 0x009, - #[derive(Copy, Clone)] - pub struct ETMTECR1(u32); - impl Debug; - pub trace_control_enable, _: 25; - pub exclude, set_exclude: 24; - pub map_decode_select, set_map_decode_select: 23, 8; - pub comparator_select, set_comparator_select: 7, 0; -); - -// -// ETM FIFOFULL Region Register -// -etm_register!(ETMFFRR, 0x00a, - #[derive(Copy, Clone)] - pub struct ETMFFRR(u32); - impl Debug; - pub exclude, set_exclude: 24; - pub map_decode_select, set_map_decode_select: 23, 8; - pub comparator_select, set_comparator_select: 7, 0; -); - -// -// ETM FIFOFULL Level Register -// -etm_register!(ETMFFLR, 0x00b, - #[derive(Copy, Clone)] - pub struct ETMFFLR(u32); - impl Debug; - pub fifo_full_level, set_fifo_full_level: 7, 0; -); - -// -// ETM Identification Register -// -etm_register!(ETMIDR, 0x079, - #[derive(Copy, Clone)] - pub struct ETMIDR(u32); - impl Debug; - pub implementer, _: 31, 24; - pub has_branch_encoding, _: 20; - pub has_security_extensions, _: 19; - pub has_thumb32, _: 18; - pub load_pc_first, _: 16; - pub family, _: 15, 12; - pub etm_major, _: 11, 8; - pub etm_minor, _: 7, 4; - pub etm_revision, _: 3, 0; -); - -// -// ETM Configuration Code Extension Register -// -etm_register!(ETMCCER, 0x07a, - #[derive(Copy, Clone)] - pub struct ETMCCER(u32); - impl Debug; - pub has_64bit_timestamp, _: 29; - pub has_binary_timestamp, _: 28; - pub has_reduced_counter, _: 27; - pub has_virtual_extensions, _: 26; - pub has_timestamping, _: 22; - pub has_etmeibcr, _: 21; - pub can_use_ice, _: 20; - pub num_ice_inputs, _: 19, 16; - pub num_instrumentation_resources, _: 15, 13; - pub no_data_comparison, _: 12; - pub all_registers_readable, _: 11; - pub extended_input_bus_size, _: 10, 3; - pub num_extended_input_selectors, _: 2, 0; -); - -// -// ETM Trace ID Register -// -etm_register!(ETMTRACEIDR, 0x080, - #[derive(Copy, Clone)] - pub struct ETMTRACEIDR(u32); - impl Debug; - pub traceid, set_traceid: 6, 0; -); - -// -// ETM Identification Register 2 -// -etm_register!(ETMIDR2, 0x082, - #[derive(Copy, Clone)] - pub struct ETMIDR2(u32); - impl Debug; - pub swp_store_before_load, _: 1; - pub rfe_cpsr_before_pc, _: 0; -); - -// -// ETM Lock Access Register -// -etm_register!(ETMLAR, 0x3ec, - #[derive(Copy, Clone)] - pub struct ETMLAR(u32); - impl Debug; - pub key, _: 1; -); - -impl ETMLAR { - pub fn unlock(core: &mut dyn humility::core::Core) -> Result<()> { - // - // To unlock, we write "CoreSight Access" in l33t - // - let val: u32 = 0xc5ac_ce55; - core.write_word_32(ETMLAR::ADDRESS, val)?; - Ok(()) - } - - pub fn lock(core: &mut dyn humility::core::Core) -> Result<()> { - let val: u32 = 0x1de_c0de; - core.write_word_32(ETMLAR::ADDRESS, val)?; - Ok(()) - } -} - -// -// ETM Lock Status Register -// -etm_register!(ETMLSR, 0x3ed, - #[derive(Copy, Clone)] - pub struct ETMLSR(u32); - impl Debug; - pub locked, _: 1; - pub unlock_required, _: 0; -); - -#[derive(Copy, Clone, Debug)] -pub enum ETM3Header { - BranchAddress { addr: u8, c: bool }, - ASync, - CycleCount, - ISync, - Trigger, - OutOfOrder { tag: u8, size: u8 }, - StoreFailed, - ISyncCycleCount, - OutOfOrderPlaceholder { a: bool, tag: u8 }, - VMID, - NormalData { a: bool, size: u8 }, - Timestamp { r: bool }, - DataSuppressed, - Ignore, - ValueNotTraced { a: bool }, - ContextID, - ExceptionExit, - ExceptionEntry, - PHeaderFormat1 { e: u8, n: u8 }, - PHeaderFormat2 { e0: bool, e1: bool }, -} - -#[derive(Copy, Clone, Debug)] -enum ETM3PacketState { - AwaitingHeader, - AwaitingPayload, - Complete, -} - -#[derive(Copy, Clone, Debug)] -pub enum ETM3SyncReason { - Periodic, - TracingEnabled, - TracingRestarted, - DebugExit, -} - -#[derive(Copy, Clone, Debug)] -pub enum ETM3ProcessorState { - ARM, - Thumb, - ThumbEE, - Jazelle, -} - -#[derive(Copy, Clone, Debug)] -pub enum ETM3Exception { - HardFault, - IRQ { irq: u16 }, - UsageFault, - NMI, - SVC, - DebugMonitor, - MemManage, - PendSV, - SysTick, - ProcessorReset, - BusFault, - Reserved { exception: u16 }, -} - -#[derive(Copy, Clone, Debug)] -pub enum ETM3Payload { - None, - BranchAddress { - addr: u32, - mask: u32, - exception: Option, - }, - ISync { - context: Option, - reason: ETM3SyncReason, - address: u32, - processor_state: ETM3ProcessorState, - }, -} - -#[derive(Copy, Clone, Debug)] -pub struct ETM3Packet { - pub header: ETM3Header, - pub payload: ETM3Payload, - pub offset: usize, - pub time: f64, -} - -pub struct ETM3Config { - pub alternative_encoding: bool, - pub context_id: u8, - pub data_access: bool, - pub traceid: u8, -} - -fn encode(hdr: ETM3Header) -> u8 { - match hdr { - ETM3Header::BranchAddress { addr, c } => { - 0b0000_0001 | if c { 1 << 7 } else { 0 } | (addr & 0b011_1111) << 1 - } - ETM3Header::ASync => 0b0000_0000, - ETM3Header::CycleCount => 0b0000_0100, - ETM3Header::ISync => 0b0000_1000, - ETM3Header::Trigger => 0b0000_1100, - ETM3Header::OutOfOrder { tag, size } => { - (tag & 0b11) << 5 | (size & 0b11) << 2 - } - ETM3Header::StoreFailed => 0b0101_0000, - ETM3Header::ISyncCycleCount => 0b0111_0000, - ETM3Header::OutOfOrderPlaceholder { a, tag } => { - 0b0101_0000 | if a { 1 << 5 } else { 0 } | ((tag & 0b11) << 2) - } - ETM3Header::VMID => 0b0011_1100, - ETM3Header::NormalData { a, size } => { - 0b0000_0010 | if a { 1 << 5 } else { 0 } | ((size & 0b11) << 2) - } - ETM3Header::Timestamp { r } => 0b0100_0010 | if r { 1 << 2 } else { 0 }, - ETM3Header::DataSuppressed => 0b0110_0010, - ETM3Header::Ignore => 0b0110_0110, - ETM3Header::ValueNotTraced { a } => { - 0b0110_1010 | if a { 1 << 4 } else { 0 } - } - ETM3Header::ContextID => 0b0110_1110, - ETM3Header::ExceptionExit => 0b0111_0110, - ETM3Header::ExceptionEntry => 0b0111_1110, - ETM3Header::PHeaderFormat1 { n, e } => { - 0b1000_0000 | ((n & 0b1) << 6) | ((e & 0b1111) << 2) - } - ETM3Header::PHeaderFormat2 { e0, e1 } => { - 0b1000_0010 - | if e0 { 1 << 3 } else { 0 } - | if e1 { 1 << 2 } else { 0 } - } - } -} - -fn set(table: &mut [Option], hdr: ETM3Header) { - let val = encode(hdr) as usize; - - match table[val] { - None => { - table[val] = Some(hdr); - } - Some(h) => { - panic!( - "two values for 0x{:x} (0b{:b}): {:?} and {:?}", - val, val, h, hdr - ); - } - } -} - -fn etm_hdrs() -> Vec> { - let mut hdr: Vec> = vec![None; 256]; - - for i in 0..=0b11_1111 { - set(&mut hdr, ETM3Header::BranchAddress { addr: i, c: true }); - set(&mut hdr, ETM3Header::BranchAddress { addr: i, c: false }); - } - - set(&mut hdr, ETM3Header::ASync); - set(&mut hdr, ETM3Header::CycleCount); - set(&mut hdr, ETM3Header::ISync); - set(&mut hdr, ETM3Header::Trigger); - - for tag in 1..=0b11 { - for size in 0..=0b11 { - set(&mut hdr, ETM3Header::OutOfOrder { tag, size }); - } - } - - set(&mut hdr, ETM3Header::StoreFailed); - set(&mut hdr, ETM3Header::ISyncCycleCount); - - for tag in 1..=0b11 { - set(&mut hdr, ETM3Header::OutOfOrderPlaceholder { tag, a: true }); - set(&mut hdr, ETM3Header::OutOfOrderPlaceholder { tag, a: false }); - } - - set(&mut hdr, ETM3Header::VMID); - - for size in 0..=0b11 { - set(&mut hdr, ETM3Header::NormalData { a: true, size }); - set(&mut hdr, ETM3Header::NormalData { a: false, size }); - } - - set(&mut hdr, ETM3Header::Timestamp { r: true }); - set(&mut hdr, ETM3Header::Timestamp { r: false }); - - set(&mut hdr, ETM3Header::DataSuppressed); - set(&mut hdr, ETM3Header::Ignore); - - set(&mut hdr, ETM3Header::ValueNotTraced { a: true }); - set(&mut hdr, ETM3Header::ValueNotTraced { a: false }); - - set(&mut hdr, ETM3Header::ContextID); - set(&mut hdr, ETM3Header::ExceptionExit); - set(&mut hdr, ETM3Header::ExceptionEntry); - - for e in 0..=0b1111 { - for n in 0..=0b1 { - set(&mut hdr, ETM3Header::PHeaderFormat1 { e, n }); - } - } - - set(&mut hdr, ETM3Header::PHeaderFormat2 { e0: false, e1: false }); - set(&mut hdr, ETM3Header::PHeaderFormat2 { e0: false, e1: true }); - set(&mut hdr, ETM3Header::PHeaderFormat2 { e0: true, e1: false }); - set(&mut hdr, ETM3Header::PHeaderFormat2 { e0: true, e1: true }); - - hdr -} - -fn etm_packet_state( - hdr: ETM3Header, - payload: &[u8], - config: &ETM3Config, -) -> ETM3PacketState { - let expect = |size: u8| { - if payload.len() < size as usize { - ETM3PacketState::AwaitingPayload - } else { - ETM3PacketState::Complete - } - }; - - let compressed = |max: u8| { - let mut ndx: u8 = 0; - - while ndx < payload.len() as u8 { - if ndx == max - 1 || (payload[ndx as usize] & 0b1000_0000) == 0 { - break; - } - - ndx += 1; - } - - ndx + 1 - }; - - // - // This assumes the alternative encoding for branch packets. - // - assert!(config.alternative_encoding); - - match hdr { - ETM3Header::BranchAddress { c, .. } => { - if payload.is_empty() { - if c { - ETM3PacketState::AwaitingPayload - } else { - ETM3PacketState::Complete - } - } else { - let last = payload[payload.len() - 1]; - - // - // If the high order bit is set, we are always awaiting more - // payload -- regardless of whether that is in one of the - // address bytes (up to five) or one of the exception bytes - // (up to three). - // - // If bit 6 is set, we are awaiting an Exception Information - // Byte. - // - if (last & 0b1000_0000) != 0 || (last & 0b0100_0000) != 0 { - ETM3PacketState::AwaitingPayload - } else { - ETM3PacketState::Complete - } - } - } - - ETM3Header::CycleCount => expect(compressed(5)), - ETM3Header::ISync => expect(5 + config.context_id), - ETM3Header::OutOfOrder { size, .. } => expect(size), - - ETM3Header::ISyncCycleCount => { - expect(compressed(5) + config.context_id + 5) - } - - ETM3Header::OutOfOrderPlaceholder { .. } => expect(5), - ETM3Header::VMID => expect(1), - ETM3Header::NormalData { a, size } => { - let dsize = if a && config.data_access { compressed(5) } else { 0 }; - - expect(dsize + 1 + size) - } - - ETM3Header::Timestamp { .. } => expect(compressed(9)), - - ETM3Header::ValueNotTraced { a } => { - if a { - expect(compressed(5)) - } else { - ETM3PacketState::Complete - } - } - - ETM3Header::ContextID => expect(config.context_id), - _ => ETM3PacketState::Complete, - } -} - -fn etm_payload_decode( - hdr: ETM3Header, - payload: &[u8], - config: &ETM3Config, -) -> ETM3Payload { - let context = |o| match config.context_id { - 0 => None, - 1 => Some(payload[o] as u32), - 2 => Some(u16::from_le_bytes([payload[o], payload[o + 1]]) as u32), - 4 => Some(u32::from_le_bytes([ - payload[o], - payload[o + 1], - payload[o + 2], - payload[o + 3], - ])), - _ => { - panic!("illegal context size"); - } - }; - - let reason = |ibyte: u8| match (ibyte >> 5) & 0b11 { - 0b00 => ETM3SyncReason::Periodic, - 0b01 => ETM3SyncReason::TracingEnabled, - 0b10 => ETM3SyncReason::TracingRestarted, - 0b11 => ETM3SyncReason::DebugExit, - _ => panic!("illegal reason"), - }; - - let processor_state = |ibyte, addr| { - let j = (ibyte & 0b0001_0000) != 0; - let t = (addr & 0b0000_0001) != 0; - let altisa = (ibyte & 0b0000_0100) != 0; - - match (j, t, altisa) { - (false, false, false) => ETM3ProcessorState::ARM, - (false, true, false) => ETM3ProcessorState::Thumb, - (false, true, true) => ETM3ProcessorState::ThumbEE, - (true, _, false) => ETM3ProcessorState::Jazelle, - (_, _, _) => panic!("unknown processor state information"), - } - }; - - let exception = |o| { - let eib0 = payload[o]; - - let mut xcp: u16 = ((eib0 & 0b0001_1110) as u16) >> 1; - - if (eib0 & 0b1000_0000) != 0 { - let eib1 = payload[o + 1]; - xcp |= ((eib1 & 0b0001_1111) as u16) << 4; - } - - match xcp { - 0x01..=0x07 => ETM3Exception::IRQ { irq: xcp }, - 0x08 => ETM3Exception::IRQ { irq: 0 }, - 0x09 => ETM3Exception::UsageFault, - 0x0a => ETM3Exception::NMI, - 0x0b => ETM3Exception::SVC, - 0x0c => ETM3Exception::DebugMonitor, - 0x0d => ETM3Exception::MemManage, - 0x0e => ETM3Exception::PendSV, - 0x0f => ETM3Exception::SysTick, - 0x11 => ETM3Exception::ProcessorReset, - 0x13 => ETM3Exception::HardFault, - 0x15 => ETM3Exception::BusFault, - 0x18..=0x1ff => ETM3Exception::IRQ { irq: xcp - 0x10 }, - _ => ETM3Exception::Reserved { exception: xcp }, - } - }; - - match hdr { - ETM3Header::ISync => { - let o = config.context_id as usize; - - let ibyte = payload[o]; - let addr = &payload[o + 1..o + 5]; - let processor_state = processor_state(ibyte, addr[0]); - let a0 = match processor_state { - ETM3ProcessorState::Jazelle => addr[0], - _ => addr[0] & !0b0000_0001, - }; - - ETM3Payload::ISync { - context: context(0), - reason: reason(ibyte), - address: u32::from_le_bytes([a0, addr[1], addr[2], addr[3]]), - processor_state, - } - } - ETM3Header::BranchAddress { addr, .. } => { - let mut target: u32 = (addr as u32) << 1; - let mut nbits = 7; - let mut xcp = None; - - for (i, pld) in payload.iter().enumerate() { - // - // If our continue bit is set, we always have seven new bits - // of address; or it in, increase the number of bits and - // continue. - // - if (pld & 0b1000_0000) != 0 { - target |= ((pld & 0b0111_1111) as u32) << nbits; - nbits += 7; - continue; - } - - // - // The continue bit is not set, so this packet terminates - // our address. Bit 6 will indicate whether or not we have - // exception bytes; if we have exception bytes, we'll pull - // those now. - // - if (pld & 0b0100_0000) != 0 { - xcp = Some(exception(i + 1)); - } - - // - // For our final address packet, it is generally six bits -- - // unless we have a full address change, in which case only - // four bits remain. - // - if i < 4 { - target |= ((pld & 0b0011_1111) as u32) << nbits; - nbits += 6; - break; - } - - assert_eq!(nbits, 28); - target |= ((pld & 0b0000_1111) as u32) << nbits; - nbits += 4; - } - - ETM3Payload::BranchAddress { - addr: target, - mask: if nbits == 32 { 0 } else { !((1 << nbits) - 1) }, - exception: xcp, - } - } - - _ => { - if payload.is_empty() { - return ETM3Payload::None; - } - - panic!("unhandled packet {:#x?}!", hdr); - } - } -} - -pub fn etm_ingest( - config: &ETM3Config, - mut readnext: impl FnMut() -> Result>, - mut callback: impl FnMut(&ETM3Packet) -> Result<()>, -) -> Result<()> { - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - enum IngestState { - ASyncSearching, - ISyncSearching, - Ingesting, - } - - let mut state: IngestState = IngestState::ASyncSearching; - let mut pstate: ETM3PacketState = ETM3PacketState::AwaitingHeader; - let mut vec = Vec::with_capacity(16); - - let mut valid = vec![false; 256]; - valid[config.traceid as usize] = true; - - let hdrs = &etm_hdrs(); - let mut hdr = ETM3Header::ASync; - let mut runlen = 0; - - tpiu_ingest(&valid, &mut readnext, |packet| { - let payload = &mut vec; - - if state == IngestState::ASyncSearching { - match packet.datum { - 0 => runlen += 1, - 0x80 => { - if runlen >= 5 { - humility::msg!( - "A-sync alignment synchronization \ - packet found at offset {}", - packet.offset - ); - state = IngestState::ISyncSearching; - } - } - _ => { - runlen = 0; - } - } - - return Ok(()); - } - - match pstate { - ETM3PacketState::AwaitingHeader => { - hdr = match hdrs[packet.datum as usize] { - Some(hdr) => hdr, - None => { - panic!( - "unrecognized ETMv3 header 0x{:x} at line {}", - packet.datum, packet.offset - ); - } - }; - - payload.truncate(0); - } - - ETM3PacketState::AwaitingPayload => { - payload.push(packet.datum); - } - - ETM3PacketState::Complete => { - panic!("unexpected packet state"); - } - } - - pstate = etm_packet_state(hdr, payload, config); - - match pstate { - ETM3PacketState::AwaitingHeader - | ETM3PacketState::AwaitingPayload => { - return Ok(()); - } - ETM3PacketState::Complete => {} - } - - if let (IngestState::ISyncSearching, ETM3Header::ISync) = (state, hdr) { - // - // We have our ISync packet -- we can now ingest everything - // (starting with this packet). - // - state = IngestState::Ingesting; - } - - if state == IngestState::Ingesting { - callback(&ETM3Packet { - header: hdr, - payload: etm_payload_decode(hdr, payload, config), - offset: packet.offset, - time: packet.time, - })?; - } - - pstate = ETM3PacketState::AwaitingHeader; - - Ok(()) - }) -} diff --git a/humility-arch-cortex/src/itm.rs b/humility-arch-cortex/src/itm.rs deleted file mode 100644 index fe7f1175c..000000000 --- a/humility-arch-cortex/src/itm.rs +++ /dev/null @@ -1,605 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::debug::*; -use crate::dwt::*; -use crate::register; -use crate::scs::*; -use crate::swo::*; -use crate::tpiu::*; -use anyhow::Result; -use bitfield::bitfield; -use humility::core::Core; -use humility::hubris::HubrisArchive; - -// -// ITM Trace Enable Register -// -register!(ITM_TER, 0xe000_0e00, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct ITM_TER(u32); - impl Debug; - pub enabled, set_enabled: 31, 0; -); - -// -// ITM Trace Privilege Register -// -register!(ITM_TPR, 0xe000_0e40, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct ITM_TPR(u32); - impl Debug; - pub privmask, set_privmask: 31, 0; -); - -// -// ITM Trace Configuration Register -// -register!(ITM_TCR, 0xe000_0e80, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct ITM_TCR(u32); - impl Debug; - pub itm_busy, _: 23; - pub traceid, set_traceid: 22, 16; - pub timestamp_prescaler, _: 9, 8; - pub swo_enable, _: 4; - pub dwt_enable, set_dwt_enable: 3; - pub sync_enable, set_sync_enable: 2; - pub timestamp_enable, set_timestamp_enable: 1; - pub itm_enable, set_itm_enable: 0; -); - -// -// ITM Lock Access Register -// -register!(ITM_LAR, 0xe000_0fb0, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct ITM_LAR(u32); - impl Debug; - pub key, _: 1; -); - -// -// ITM Lock Status Register -// -register!(ITM_LSR, 0xe000_0fb4, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct ITM_LSR(u32); - impl Debug; - pub locked, _: 1; - pub unlock_required, _: 0; -); - -impl ITM_LAR { - pub fn unlock(core: &mut dyn humility::core::Core) -> Result<()> { - // - // To unlock, we write "CoreSight Access" in l33t - // - let val: u32 = 0xc5ac_ce55; - core.write_word_32(ITM_LAR::ADDRESS, val)?; - Ok(()) - } - - pub fn lock(core: &mut dyn humility::core::Core) -> Result<()> { - let val: u32 = 0x1de_c0de; - core.write_word_32(ITM_LAR::ADDRESS, val)?; - Ok(()) - } -} - -#[derive(Debug)] -pub enum ITMPayload { - None, - LocalTimestamp { - timedelta: u32, - delayed: bool, - early: bool, - }, - #[allow(dead_code)] - Extension { - payload: u32, - sh: bool, - }, - #[allow(dead_code)] - GlobalTimestamp { - timestamp: u64, - }, - Instrumentation { - port: u32, - payload: Vec, - }, - #[allow(dead_code)] - Hardware { - source: u32, - payload: [u8; 4], - len: usize, - }, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ITMHeader { - Sync, - Overflow, - LocalTimestamp1 { tc: u8 }, - LocalTimestamp2 { ts: u8 }, - GlobalTimestamp1, - GlobalTimestamp2, - Extension { c: bool, d: u8, s: bool }, - Instrumentation { a: u8, ss: u8 }, - Hardware { a: u8, ss: u8 }, - Malformed(u8), -} - -#[derive(Copy, Clone, Debug)] -enum ITMPacketState { - AwaitingHeader, - AwaitingPayload, - Complete, -} - -#[derive(Debug)] -pub struct ITMPacket { - pub header: ITMHeader, - pub payload: ITMPayload, - pub offset: usize, - pub time: f64, -} - -fn encode(hdr: ITMHeader) -> u8 { - match hdr { - ITMHeader::Sync => 0, - ITMHeader::Overflow => 0b0111_0000, - ITMHeader::LocalTimestamp1 { tc } => { - assert!(tc <= 0b11); - 0b1100_0000 | (tc << 4) - } - - ITMHeader::LocalTimestamp2 { ts } => { - assert!(ts != 0); - assert!(ts < 0b111); - ts << 4 - } - - ITMHeader::GlobalTimestamp1 => 0b1001_0100, - ITMHeader::GlobalTimestamp2 => 0b1011_0100, - ITMHeader::Extension { c, d, s } => { - let sh = if s { 1 << 2 } else { 0 }; - 0b0000_1000 | (d & 0b111) << 4 | if c { 1 << 7 } else { 0 } | sh - } - - ITMHeader::Instrumentation { a, ss } => { - assert!(ss != 0); - assert!((a >> 5) == 0); - (a << 3) | ss - } - - ITMHeader::Hardware { a, ss } => { - assert!(ss != 0); - assert!((a >> 5) == 0); - 0b0000_0100 | (a << 3) | ss - } - - ITMHeader::Malformed(_) => { - panic!("attempt to encode malformed header"); - } - } -} - -fn set(table: &mut [Option], hdr: ITMHeader) { - let val = encode(hdr) as usize; - - match table[val] { - None => { - table[val] = Some(hdr); - } - Some(h) => { - panic!( - "two values for ITM header 0x{:x} (0b{:b}): {:?} and {:?}", - val, val, h, hdr - ); - } - } -} - -fn itm_hdrs() -> Vec> { - let mut hdr: Vec> = vec![None; 256]; - let bools = [false, true]; - - set(&mut hdr, ITMHeader::Sync); - set(&mut hdr, ITMHeader::Overflow); - - for a in 0..32 { - for ss in 1..=0b11 { - set(&mut hdr, ITMHeader::Instrumentation { a, ss }); - set(&mut hdr, ITMHeader::Hardware { a, ss }); - } - } - - for i in 0..=0b11 { - set(&mut hdr, ITMHeader::LocalTimestamp1 { tc: i }); - } - - for i in 1..0b111 { - set(&mut hdr, ITMHeader::LocalTimestamp2 { ts: i }); - } - - for i in 0..=0b111 { - for s in bools.iter() { - for c in bools.iter() { - set(&mut hdr, ITMHeader::Extension { c: *c, d: i, s: *s }); - } - } - } - - set(&mut hdr, ITMHeader::GlobalTimestamp1); - set(&mut hdr, ITMHeader::GlobalTimestamp2); - - hdr -} - -fn itm_packet_state(hdr: ITMHeader, payload: &[u8]) -> ITMPacketState { - let expect = |size: u8| { - if payload.len() < size as usize { - ITMPacketState::AwaitingPayload - } else { - ITMPacketState::Complete - } - }; - - let compressed = |max: u8| { - let mut ndx: u8 = 0; - - while ndx < payload.len() as u8 { - if ndx == max - 1 || (payload[ndx as usize] & 0b1000_0000) == 0 { - break; - } - - ndx += 1; - } - - ndx + 1 - }; - - match hdr { - ITMHeader::Sync => expect(5), - ITMHeader::Overflow => ITMPacketState::Complete, - ITMHeader::LocalTimestamp1 { .. } => expect(compressed(4)), - ITMHeader::LocalTimestamp2 { .. } => ITMPacketState::Complete, - ITMHeader::GlobalTimestamp1 => expect(compressed(4)), - ITMHeader::GlobalTimestamp2 => expect(4), - ITMHeader::Extension { .. } => expect(compressed(4)), - ITMHeader::Instrumentation { ss, .. } - | ITMHeader::Hardware { ss, .. } => expect(match ss { - 0b01 => 1, - 0b10 => 2, - 0b11 => 4, - _ => panic!("invalid ss"), - }), - ITMHeader::Malformed(_) => { - panic!("cannot determine packet state on malformed header"); - } - } -} - -fn itm_payload_decode(hdr: ITMHeader, payload: &[u8]) -> ITMPayload { - match hdr { - ITMHeader::Instrumentation { a, .. } => ITMPayload::Instrumentation { - port: a as u32, - payload: payload.to_vec(), - }, - - ITMHeader::LocalTimestamp1 { tc } => { - let mut delta: u32 = 0; - - for (i, pld) in payload.iter().enumerate() { - delta |= ((*pld as u32) & 0b0111_1111) << (i * 7); - } - - ITMPayload::LocalTimestamp { - delayed: (tc & 0b01) != 0, - early: (tc & 0b10) != 0, - timedelta: delta, - } - } - - _ => ITMPayload::None, - } -} - -pub fn itm_ingest( - traceid: Option, - mut readnext: impl FnMut() -> Result>, - mut callback: impl FnMut(&ITMPacket) -> Result<()>, -) -> Result<()> { - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - enum IngestState { - SyncSearching, - Ingesting, - } - - let mut state: IngestState = IngestState::SyncSearching; - let mut pstate: ITMPacketState = ITMPacketState::AwaitingHeader; - let mut vec = Vec::with_capacity(16); - - let hdrs = &itm_hdrs(); - let mut hdr = ITMHeader::Sync; - let mut runlen = 0; - - let process = |packet: &TPIUPacket| -> Result<()> { - let payload = &mut vec; - - if state == IngestState::SyncSearching { - match packet.datum { - 0 => runlen += 1, - 0x80 => { - if runlen >= 5 { - humility::msg!( - "ITM synchronization packet found at offset {}", - packet.offset - ); - state = IngestState::Ingesting; - } - } - _ => { - runlen = 0; - } - } - - return Ok(()); - } - - match pstate { - ITMPacketState::AwaitingHeader => { - hdr = match hdrs[packet.datum as usize] { - Some(hdr) => hdr, - None => { - callback(&ITMPacket { - header: ITMHeader::Malformed(packet.datum), - payload: ITMPayload::None, - offset: packet.offset, - time: packet.time, - })?; - - humility::msg!( - "unrecognized ITM header 0x{:x} at offset {}", - packet.datum, - packet.offset - ); - - state = IngestState::SyncSearching; - return Ok(()); - } - }; - - payload.truncate(0); - } - - ITMPacketState::AwaitingPayload => { - payload.push(packet.datum); - } - - ITMPacketState::Complete => { - panic!("unexpected packet state"); - } - } - - pstate = itm_packet_state(hdr, payload); - - match pstate { - ITMPacketState::AwaitingHeader - | ITMPacketState::AwaitingPayload => { - return Ok(()); - } - ITMPacketState::Complete => {} - } - - if state == IngestState::Ingesting { - callback(&ITMPacket { - header: hdr, - payload: itm_payload_decode(hdr, payload), - offset: packet.offset, - time: packet.time, - })?; - } else { - unreachable!(); - } - - pstate = ITMPacketState::AwaitingHeader; - - Ok(()) - }; - - match traceid { - Some(traceid) => { - let mut valid = vec![false; 256]; - valid[traceid as usize] = true; - tpiu_ingest(&valid, &mut readnext, process) - } - None => tpiu_ingest_bypass(&mut readnext, process), - } -} - -/// -/// Enables ITM with an explict clockscaler and traceid. -pub fn itm_enable_explicit( - core: &mut dyn Core, - coreinfo: &CoreInfo, - clockscaler: u16, - traceid: u8, - stimuli: u32, -) -> Result<()> { - // - // First, enable TRCENA in the DEMCR. - // - let mut val = DEMCR::read(core)?; - val.set_trcena(true); - val.write(core)?; - - match (coreinfo.vendor, coreinfo.part) { - (Vendor::ST, ARMCore::CortexM4) => { - // - // STM32F4xx-specific: enable TRACE_IOEN in the DBGMCU_CR, and - // set the trace mode to be asynchronous. - // - let mut val = STM32F4_DBGMCU_CR::read(core)?; - val.set_trace_ioen(true); - val.set_trace_mode(0); - val.write(core)?; - } - - (Vendor::ST, ARMCore::CortexM7) => { - // - // STM32H7xx-specific: enable D3 and D1 clock domain + traceclk - // - let mut cr = STM32H7_DBGMCU_CR::read(core)?; - cr.set_srdbgcken(true); - cr.set_cddbgcken(true); - cr.set_traceclken(true); - cr.write(core)?; - - let components = &coreinfo.components; - - if let Some(cstf) = components.get_vec(&CoreSightComponent::CSTF) { - // - // If we have two funnels, the first is in D3 -- and it needs - // to be unlocked and enabled. - // - if cstf.len() > 1 { - log::trace!("SWTF found at {:x}", cstf[0]); - SWO_LAR::unlock(core, cstf[0])?; - let mut swtf = SWTF_CTRL::read(core, cstf[0])?; - swtf.register.set_es0(true); - swtf.write(core)?; - } - } - } - - _ => {} - } - - let swoscaler = clockscaler as u32; - - if let Some(swo) = coreinfo.address(CoreSightComponent::SWO) { - // - // If we have a SWO unit, configure it instead of the TPIU - // - log::trace!("SWO found at {:x}", swo); - - SWO_LAR::unlock(core, swo)?; - - let mut codr = SWO_CODR::read(core, swo)?; - codr.register.set_prescaler(swoscaler); - codr.write(core)?; - - let mut sppr = SWO_SPPR::read(core, swo)?; - sppr.register.set_pprot(SWOMode::NRZ.into()); - sppr.write(core)?; - } else { - // - // Otherwise setup the TPIU. - // - let mut val = TPIU_SPPR::read(core)?; - val.set_txmode(TPIUMode::NRZ); - val.write(core)?; - - let mut val = TPIU_FFCR::read(core)?; - val.set_continuous_formatting(true); - val.write(core)?; - - let mut acpr = TPIU_ACPR::read(core)?; - acpr.set_swoscaler(swoscaler); - acpr.write(core)?; - log::trace!("{:#x?}", TPIU_ACPR::read(core)?); - } - - // - // Unlock the ITM. - // - ITM_LAR::unlock(core)?; - - // - // Disable the ITM. - // - let mut tcr = ITM_TCR::read(core)?; - tcr.set_itm_enable(false); - tcr.write(core)?; - - // - // Spin until the ITM is not busy - // - while ITM_TCR::read(core)?.itm_busy() { - continue; - } - - // - // Enable the DWT to generate a synchronization packet every 8M cycles. - // - let mut dwt = DWT_CTRL::read(core)?; - dwt.set_synctap(DWTSyncTapFrequency::CycCnt8M); - dwt.set_cyccnt_enabled(true); - dwt.write(core)?; - - // - // Enable stimuli - // - let mut ter = ITM_TER::read(core)?; - ter.set_enabled(stimuli); - ter.write(core)?; - - // - // Allow unprivileged access to all stimulus ports - // - let mut tpr = ITM_TPR::read(core)?; - tpr.set_privmask(0); - tpr.write(core)?; - - // - // Set the trace ID - // - tcr = ITM_TCR::read(core)?; - tcr.set_traceid(traceid.into()); - tcr.set_timestamp_enable(stimuli & 0xffff_0000 != 0); - - tcr.set_sync_enable(true); - tcr.set_itm_enable(true); - tcr.write(core)?; - - Ok(()) -} - -/// -/// Enables ITM by pulling clock scaler values from the specified Hubris -/// archive. -pub fn itm_enable_ingest( - core: &mut dyn Core, - hubris: &HubrisArchive, - stim: u32, -) -> Result> { - let coreinfo = CoreInfo::read(core)?; - - let _info = core.halt(); - core.init_swv()?; - - // - // Pull our clock scaler from the Hubris archive -- and set our traceid - // to be a recognizable value. - // - let clockscaler = swoscaler(hubris, core)?; - let traceid = 0x3a; - - itm_enable_explicit(core, &coreinfo, clockscaler, traceid, stim)?; - - core.run()?; - - Ok(if coreinfo.address(CoreSightComponent::SWO).is_some() { - None - } else { - Some(traceid) - }) -} diff --git a/humility-arch-cortex/src/lib.rs b/humility-arch-cortex/src/lib.rs index 72fdc0f27..7b15497e7 100644 --- a/humility-arch-cortex/src/lib.rs +++ b/humility-arch-cortex/src/lib.rs @@ -4,11 +4,8 @@ pub mod debug; pub mod dwt; -pub mod etm; -pub mod itm; pub mod scs; pub mod swo; -pub mod tpiu; #[macro_use] extern crate num_derive; diff --git a/humility-arch-cortex/src/tpiu.rs b/humility-arch-cortex/src/tpiu.rs deleted file mode 100644 index 9b192e1b1..000000000 --- a/humility-arch-cortex/src/tpiu.rs +++ /dev/null @@ -1,562 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use crate::debug::Register; -use crate::register; -use anyhow::Result; -use bitfield::bitfield; -use humility::core::Core; - -register!(TPIU_SSPSR, 0xe004_0000, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_SSPSR(u32); - impl Debug; - swidth, _: 31, 0; -); - -// -// TPIU Asynchronous Clock Prescaler Register -// -register!(TPIU_ACPR, 0xe004_0010, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_ACPR(u32); - impl Debug; - pub swoscaler, set_swoscaler: 15, 0; -); - -// -// TPIU Selected Pin Protocol Register -// -register!(TPIU_SPPR, 0xe004_00f0, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_SPPR(u32); - impl Debug; - _txmode, _set_txmode: 1, 0; -); - -pub enum TPIUMode { - Parallel, - Manchester, - NRZ, -} - -impl TPIU_SPPR { - pub fn set_txmode(&mut self, mode: TPIUMode) { - let val = match mode { - TPIUMode::Parallel => 0, - TPIUMode::Manchester => 1, - TPIUMode::NRZ => 2, - }; - - self._set_txmode(val); - } -} - -// -// TPIU Supported Test Patterns/Modes Register -// -register!(TPIU_STMR, 0xe004_0200, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_STMR(u32); - impl Debug; - pub continuous_mode, _: 17; - pub timed_mode, _: 16; - pub ff00_pattern, _: 3; - pub aa55_pattern, _: 2; - pub walking_0s_pattern, _: 1; - pub walking_1s_pattern, _: 0; -); - -// -// TPIU Flush and Format Status Register -// -register!(TPIU_FFSR, 0xe004_0300, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_FFSR(u32); - impl Debug; - pub tracectl_present, _: 2; - pub formatter_stopped, _: 1; - pub flush_in_progress, _: 0; -); - -// -// TPIU Flush and Format Control Register -// -register!(TPIU_FFCR, 0xe004_0304, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_FFCR(u32); - impl Debug; - pub trigin, _: 8; - pub continuous_formatting, set_continuous_formatting: 1; -); - -// -// TPIU Formatter Synchronization Counter Register -// -register!(TPIU_FSCR, 0xe004_0308, - #[derive(Copy, Clone)] - #[allow(non_camel_case_types)] - pub struct TPIU_FSCR(u32); - impl Debug; - pub counter, set_counter: 11, 0; -); - -bitfield! { - #[derive(Copy, Clone)] - pub struct TPIUFrameHalfWord(u16); - impl Debug; - data_or_aux, _: 15, 8; - data_or_id, _: 7, 1; - f_control, _: 0; -} - -impl From for TPIUFrameHalfWord { - fn from(value: u16) -> Self { - Self(value) - } -} - -impl From<(u8, u8)> for TPIUFrameHalfWord { - fn from(value: (u8, u8)) -> Self { - Self((value.1 as u16) << 8 | (value.0 as u16)) - } -} - -#[derive(Copy, Clone, Debug)] -pub struct TPIUPacket { - pub id: Option, - pub datum: u8, - pub offset: usize, - pub time: f64, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -enum TPIUState { - Searching, - SearchingSyncing(usize), - Framing, - FramingSyncing(usize), -} - -const TPIU_FRAME_SYNC: [u8; 4] = [0xff, 0xff, 0xff, 0x7f]; -const TPIU_ID_NULL: u8 = 0; - -fn tpiu_next_state(state: TPIUState, byte: u8, offset: usize) -> TPIUState { - let sync = &TPIU_FRAME_SYNC; - - // - // Based on our current state and the byte, we're looking at, determine - // our next framing state. - // - let nstate = match state { - TPIUState::SearchingSyncing(next) if byte != sync[next] => { - TPIUState::Searching - } - - TPIUState::FramingSyncing(next) if byte != sync[next] => { - humility::msg!("TPIU framing derailed at offset {offset}"); - TPIUState::Searching - } - - TPIUState::SearchingSyncing(next) if next + 1 < sync.len() => { - TPIUState::SearchingSyncing(next + 1) - } - - TPIUState::FramingSyncing(next) if next + 1 < sync.len() => { - TPIUState::FramingSyncing(next + 1) - } - - TPIUState::SearchingSyncing(next) => { - humility::msg!( - "TPIU sync packet found at offset {}", - offset - next - ); - TPIUState::Framing - } - - TPIUState::FramingSyncing(_) => TPIUState::Framing, - - TPIUState::Searching if byte != sync[0] => TPIUState::Searching, - - TPIUState::Searching => TPIUState::SearchingSyncing(1), - - TPIUState::Framing if byte != sync[0] => TPIUState::Framing, - - TPIUState::Framing => TPIUState::FramingSyncing(1), - }; - - match (state, nstate) { - (TPIUState::Searching, TPIUState::Searching) - | (TPIUState::Searching, TPIUState::SearchingSyncing(_)) - | (TPIUState::SearchingSyncing(_), TPIUState::Searching) - | (TPIUState::SearchingSyncing(_), TPIUState::Framing) - | (TPIUState::SearchingSyncing(_), TPIUState::SearchingSyncing(_)) - | (TPIUState::Framing, TPIUState::Framing) - | (TPIUState::Framing, TPIUState::FramingSyncing(_)) - | (TPIUState::FramingSyncing(_), TPIUState::Framing) - | (TPIUState::FramingSyncing(_), TPIUState::Searching) - | (TPIUState::FramingSyncing(_), TPIUState::FramingSyncing(_)) => {} - _ => { - panic!( - "illegal state transition at offset {}: {:?} -> {:?}", - offset, state, nstate - ); - } - } - - nstate -} - -fn tpiu_check_frame( - frame: &[(u8, f64, usize)], - valid: &[bool], - intermixed: bool, -) -> bool { - // - // To check a frame, we go through its half words, checking them for - // inconsistency. The false positive rate will very much depend on how - // crowded the ID space is: the sparser the valid space, the less likely - // we are to accept a frame that is in fact invalid. - // - let max = frame.len() / 2; - - for i in 0..max { - let base = i * 2; - let half = TPIUFrameHalfWord::from((frame[base].0, frame[base + 1].0)); - - if half.f_control() { - // - // The NULL source identifier denotes data we need to explicitly - // chuck; check for it. - // - if half.data_or_id() == TPIU_ID_NULL as u16 { - continue; - } - - // - // The two conditions under which we can reject a frame: we - // either have an ID that isn't expected, or we are not expecting - // intermixed output and we have an ID on anything but the first - // half-word of the frame. - // - if !valid[half.data_or_id() as usize] || (i > 0 && !intermixed) { - return false; - } - } - } - - true -} - -fn tpiu_check_byte(byte: u8, valid: &[bool]) -> bool { - let check: TPIUFrameHalfWord = (byte as u16).into(); - - check.f_control() && valid[check.data_or_id() as usize] -} - -fn tpiu_process_frame( - frame: &[(u8, f64, usize)], - id: Option, - mut callback: impl FnMut(&TPIUPacket) -> Result<()>, -) -> Result { - let high = frame.len() - 1; - let aux = TPIUFrameHalfWord::from((frame[high - 1].0, frame[high].0)); - let max = frame.len() / 2; - let mut current = id; - - for i in 0..max { - let base = i * 2; - let half = TPIUFrameHalfWord::from((frame[base].0, frame[base + 1].0)); - let auxbit = ((aux.data_or_aux() & (1 << i)) >> i) as u8; - let last = i == max - 1; - - if half.f_control() { - // - // If our bit is set, the sense of the auxiliary bit tells us - // if the ID takes effect with this halfword (bit is clear), or - // with the next (bit is set). - // - let delay = auxbit != 0; - let mut packet = TPIUPacket { - id: Some(half.data_or_id() as u8), - datum: half.data_or_aux() as u8, - time: frame[base + 1].1, - offset: frame[base + 1].2, - }; - - if last { - // - // If this is the last half-word, the auxiliary bit "must be - // ignored" (Sec. D4.2 in the ARM CoreSight Architecture - // Specification), and applies to the subsequent record. So - // in this case, we just return the ID. - // - return Ok(packet.id.unwrap()); - } - - match (delay, current) { - (false, _) => { - callback(&packet)?; - } - (true, Some(current)) => { - let saved = packet.id; - packet.id = Some(current); - callback(&packet)?; - packet.id = saved; - } - (true, None) => { - // - // We have no old ID -- we are going to discard this - // byte, but also warn about it. - // - warn!("orphaned byte at offset {}", packet.offset); - } - } - - current = packet.id; - } else { - // - // If our bit is NOT set, the auxiliary bit is the actual bit - // of data. If our current is not set, then we are still - // searching for a first frame; we don't have an ID - // to associate with it, so we need to chuck the - // data. - // - let id = match current { - Some(id) => id, - None => { - assert!(!last); - continue; - } - }; - - callback(&TPIUPacket { - id: Some(id), - datum: (half.data_or_id() << 1) as u8 | auxbit, - time: frame[base].1, - offset: frame[base].2, - })?; - - if last { - return Ok(id); - } - - callback(&TPIUPacket { - id: Some(id), - datum: half.data_or_aux() as u8, - time: frame[base + 1].1, - offset: frame[base + 1].2, - })?; - } - } - - // - // We shouldn't be able to get here: the last half-word handling logic - // should assure that we return from within the loop. - // - unreachable!(); -} - -pub fn tpiu_ingest_bypass( - mut readnext: impl FnMut() -> Result>, - mut callback: impl FnMut(&TPIUPacket) -> Result<()>, -) -> Result<()> { - let mut datum: u8; - let mut offs = 0; - let mut time: f64; - - loop { - match readnext()? { - Some(result) => { - datum = result.0; - time = result.1; - } - None => { - break; - } - } - - offs += 1; - - callback(&TPIUPacket { id: None, datum, time, offset: offs })? - } - - Ok(()) -} - -pub fn tpiu_ingest( - valid: &[bool], - mut readnext: impl FnMut() -> Result>, - mut callback: impl FnMut(&TPIUPacket) -> Result<()>, -) -> Result<()> { - let mut state = TPIUState::Searching; - - let mut ndx = 0; - let mut frame: Vec<(u8, f64, usize)> = vec![(0u8, 0.0, 0); 16]; - let mut replay: Vec<(u8, f64, usize)> = vec![]; - - let mut nvalid = 0; - let mut id = None; - let mut offs = 0; - let mut datum: u8; - let mut time: f64; - - let mut filter = |packet: &TPIUPacket| { - if packet.id == Some(TPIU_ID_NULL) { - Ok(()) - } else { - callback(packet) - } - }; - - loop { - if !replay.is_empty() { - let popped = replay.pop().unwrap(); - - datum = popped.0; - time = popped.1; - offs = popped.2; - } else { - match readnext()? { - Some(result) => { - datum = result.0; - time = result.1; - } - None => { - break; - } - } - - offs += 1; - } - - match state { - TPIUState::SearchingSyncing(_) | TPIUState::FramingSyncing(_) => { - state = tpiu_next_state(state, datum, offs); - - if state == TPIUState::Searching { - // - // We just got kicked back into searching; we need to - // replay this datum to see if it starts a frame. - // - replay.push((datum, time, offs)); - continue; - } - } - - TPIUState::Searching => { - if ndx == 0 { - state = tpiu_next_state(state, datum, offs); - - match state { - TPIUState::SearchingSyncing(_) => { - continue; - } - TPIUState::Searching => { - if !tpiu_check_byte(datum, valid) { - continue; - } - } - _ => { - unreachable!(); - } - } - } - - frame[ndx] = (datum, time, offs); - ndx += 1; - - if ndx < frame.len() { - continue; - } - - // - // We have a complete frame. We need to now check the entire - // frame. - // - if tpiu_check_frame(&frame, valid, true) { - humility::msg!( - "valid TPIU frame starting at offset {}", - frame[0].2 - ); - id = Some(tpiu_process_frame(&frame, id, &mut filter)?); - state = TPIUState::Framing; - nvalid = 1; - ndx = 0; - continue; - } - - // - // That wasn't a valid frame; we need to replay. - // - while ndx > 1 { - replay.push(frame[ndx - 1]); - ndx -= 1; - } - - ndx = 0; - } - - TPIUState::Framing => { - if ndx == 0 { - state = tpiu_next_state(state, datum, offs); - - match state { - TPIUState::Framing => {} - TPIUState::FramingSyncing(_) => { - continue; - } - _ => { - unreachable!(); - } - } - } - - frame[ndx] = (datum, time, offs); - ndx += 1; - - if ndx < frame.len() { - continue; - } - - // - // We have a complete frame, but we more or less expect it to - // be correct. If this fails, we need to go back in time - // and resume our search for a frame. - // - if !tpiu_check_frame(&frame, valid, true) { - warn!( - "after {} frame{}, invalid frame at offset {}", - nvalid, - if nvalid == 1 { "" } else { "s" }, - frame[0].2 - ); - - while ndx > 1 { - replay.push(frame[ndx - 1]); - ndx -= 1; - } - - nvalid = 0; - state = TPIUState::Searching; - } else { - nvalid += 1; - id = Some(tpiu_process_frame(&frame, id, &mut filter)?); - } - - ndx = 0; - } - } - } - - humility::msg!("{nvalid} valid TPIU frames"); - - Ok(()) -} diff --git a/humility-bin/Cargo.toml b/humility-bin/Cargo.toml index c4a407b13..63783fab5 100644 --- a/humility-bin/Cargo.toml +++ b/humility-bin/Cargo.toml @@ -46,7 +46,6 @@ cmd-debugmailbox = { workspace = true, optional = true } cmd-doc = { workspace = true } cmd-dump = { workspace = true } cmd-tofino-eeprom = { workspace = true } -cmd-etm = { workspace = true } cmd-exec = { workspace = true } cmd-extract = { workspace = true } cmd-flash = { workspace = true, optional = true } @@ -59,7 +58,6 @@ cmd-hiffy = { workspace = true } cmd-hydrate = { workspace = true } cmd-i2c = { workspace = true } cmd-ibc = { workspace = true } -cmd-itm = { workspace = true } cmd-jefe = { workspace = true } cmd-lpc55gpio = { workspace = true } cmd-lsusb = { workspace = true, optional = true } diff --git a/humility-core/src/archive.rs b/humility-core/src/archive.rs index 57803253b..d9cd2237b 100644 --- a/humility-core/src/archive.rs +++ b/humility-core/src/archive.rs @@ -70,14 +70,6 @@ impl Core for ArchiveCore { bail!("can't step an archive"); } - fn init_swv(&mut self) -> Result<()> { - bail!("cannot enable SWV with an archive"); - } - - fn read_swv(&mut self) -> Result> { - bail!("cannot read SWV with an archive"); - } - fn load(&mut self, _path: &Path) -> Result<()> { bail!("cannot load flash to an archive"); } diff --git a/humility-core/src/core.rs b/humility-core/src/core.rs index 9f4478db0..7dab40344 100644 --- a/humility-core/src/core.rs +++ b/humility-core/src/core.rs @@ -23,8 +23,6 @@ pub trait Core { fn read_8(&mut self, addr: u32, data: &mut [u8]) -> Result<()>; fn read_reg(&mut self, reg: ARMRegister) -> Result; fn write_reg(&mut self, reg: ARMRegister, value: u32) -> Result<()>; - fn init_swv(&mut self) -> Result<()>; - fn read_swv(&mut self) -> Result>; fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()>; fn write_8(&mut self, addr: u32, data: &[u8]) -> Result<()>; diff --git a/humility-core/src/dump.rs b/humility-core/src/dump.rs index da2c80780..ecffb32e1 100644 --- a/humility-core/src/dump.rs +++ b/humility-core/src/dump.rs @@ -146,14 +146,6 @@ impl Core for DumpCore { bail!("can't step a dump"); } - fn init_swv(&mut self) -> Result<()> { - bail!("cannot enable SWV on a dump"); - } - - fn read_swv(&mut self) -> Result> { - bail!("cannot read SWV on a dump"); - } - fn is_dump(&self) -> bool { true } diff --git a/humility-dump-agent/src/lib.rs b/humility-dump-agent/src/lib.rs index d9e0fa4ca..b062f9c50 100644 --- a/humility-dump-agent/src/lib.rs +++ b/humility-dump-agent/src/lib.rs @@ -349,14 +349,6 @@ impl Core for DumpAgentCore { bail!("can't step over dump agent"); } - fn init_swv(&mut self) -> Result<()> { - bail!("cannot enable SWV over dump agent"); - } - - fn read_swv(&mut self) -> Result> { - bail!("cannot read SWV over dump agent"); - } - fn load(&mut self, _path: &Path) -> Result<()> { bail!("cannot load flash over dump agent"); } diff --git a/humility-net-core/src/lib.rs b/humility-net-core/src/lib.rs index 5e32dbaca..3337083c7 100644 --- a/humility-net-core/src/lib.rs +++ b/humility-net-core/src/lib.rs @@ -385,14 +385,6 @@ impl Core for NetCore { bail!("can't step over network"); } - fn init_swv(&mut self) -> Result<()> { - bail!("cannot enable SWV over network"); - } - - fn read_swv(&mut self) -> Result> { - bail!("cannot read SWV over network"); - } - fn load(&mut self, _path: &Path) -> Result<()> { bail!("cannot load flash over network"); } diff --git a/humility-probes-core/src/gdb.rs b/humility-probes-core/src/gdb.rs index 5da9800ee..0d56f8d72 100644 --- a/humility-probes-core/src/gdb.rs +++ b/humility-probes-core/src/gdb.rs @@ -328,14 +328,6 @@ impl Core for GDBCore { Ok(()) } - fn init_swv(&mut self) -> Result<()> { - Ok(()) - } - - fn read_swv(&mut self) -> Result> { - Err(anyhow!("GDB target does not support SWV")) - } - fn load(&mut self, _path: &Path) -> Result<()> { bail!("Flash loading is not supported with GDB"); } diff --git a/humility-probes-core/src/openocd.rs b/humility-probes-core/src/openocd.rs index 4ae2dc722..d541398ff 100644 --- a/humility-probes-core/src/openocd.rs +++ b/humility-probes-core/src/openocd.rs @@ -10,16 +10,11 @@ use std::io::{Read, Write}; use std::net::TcpStream; use std::path::Path; use std::time::Duration; -use std::time::Instant; const OPENOCD_COMMAND_DELIMITER: u8 = 0x1a; -const OPENOCD_TRACE_DATA_BEGIN: &str = "type target_trace data "; -const OPENOCD_TRACE_DATA_END: &str = "\r\n"; pub struct OpenOCDCore { stream: TcpStream, - swv: bool, - last_swv: Option, } #[rustfmt::skip::macros(anyhow, bail)] @@ -67,7 +62,7 @@ impl OpenOCDCore { anyhow!("can't connect to OpenOCD on port 6666; is it running?") })?; - Ok(Self { stream, swv: false, last_swv: None }) + Ok(Self { stream }) } } @@ -179,83 +174,6 @@ impl Core for OpenOCDCore { Err(anyhow!("\"{}\": malformed return value: {:?}", cmd, rval)) } - fn init_swv(&mut self) -> Result<()> { - self.swv = true; - self.sendcmd("tpiu config disable")?; - - // - // XXX: This assumes STM32F4's 16Mhz clock - // - self.sendcmd("tpiu config internal - uart on 16000000")?; - self.sendcmd("tcl_trace on")?; - - Ok(()) - } - - fn read_swv(&mut self) -> Result> { - if !self.swv { - self.init_swv()? - } - - let mut rbuf = vec![0; 8192]; - let mut swv: Vec = Vec::with_capacity(8192); - - if let Some(last_swv) = self.last_swv { - // - // When we read from SWV from OpenOCD, it will block until data - // becomes available. To better approximate the (non-blocking) - // behavior we see on a directly attached debugger, we return a - // zero byte read if it has been less than 100 ms since our last - // read -- relying on OpenOCD to buffer things a bit. - // - if last_swv.elapsed().as_secs_f64() < 0.1 { - return Ok(swv); - } - } - - let rval = self.stream.read(&mut rbuf)?; - self.last_swv = Some(Instant::now()); - - if rbuf[rval - 1] != OPENOCD_COMMAND_DELIMITER { - bail!("missing trace data delimiter: {:?}", rval); - } - - // - // OpenOCD can sometimes send multiple command delimters -- or - // none at all. - // - if rval == 1 { - return Ok(swv); - } - - let rstr = if rbuf[0] == OPENOCD_COMMAND_DELIMITER { - std::str::from_utf8(&rbuf[1..rval - 1])? - } else { - std::str::from_utf8(&rbuf[0..rval - 1])? - }; - - if !rstr.starts_with(OPENOCD_TRACE_DATA_BEGIN) { - bail!("bogus trace data (bad start): {:?}", rstr); - } - - if !rstr.ends_with(OPENOCD_TRACE_DATA_END) { - bail!("bogus trace data (bad end): {:?}", rstr); - } - - let begin = OPENOCD_TRACE_DATA_BEGIN.len(); - let end = rstr.len() - OPENOCD_TRACE_DATA_END.len(); - - for i in (begin..end).step_by(2) { - if i + 1 >= end { - bail!("short trace data: {:?}", rval); - } - - swv.push(u8::from_str_radix(&rstr[i..=i + 1], 16)?); - } - - Ok(swv) - } - fn write_word_32(&mut self, addr: u32, data: u32) -> Result<()> { self.sendcmd(&format!("mww 0x{:x} 0x{:x}", addr, data))?; Ok(()) diff --git a/humility-probes-core/src/probe_rs.rs b/humility-probes-core/src/probe_rs.rs index eaa7695ad..f2ecf8631 100644 --- a/humility-probes-core/src/probe_rs.rs +++ b/humility-probes-core/src/probe_rs.rs @@ -213,25 +213,6 @@ impl Core for ProbeCore { Ok(()) } - fn init_swv(&mut self) -> Result<()> { - use probe_rs::architecture::arm::swo::SwoConfig; - - let config = SwoConfig::new(0).set_baud(2_000_000); - self.session.setup_swv(0, &config)?; - - // - // Because the probe can have sticky errors, we perform one read - // (and discard the results) to assure that any further errors - // are legit. - // - let _discard = self.session.read_swo(); - Ok(()) - } - - fn read_swv(&mut self) -> Result> { - Ok(self.session.read_swo()?) - } - fn load(&mut self, path: &Path) -> Result<()> { #[derive(Debug, Default)] struct LoadProgress { diff --git a/humility-probes-core/src/unattached.rs b/humility-probes-core/src/unattached.rs index 95157555b..3a6a6a281 100644 --- a/humility-probes-core/src/unattached.rs +++ b/humility-probes-core/src/unattached.rs @@ -73,14 +73,6 @@ impl Core for UnattachedCore { bail!("Core::step unimplemented when unattached!"); } - fn init_swv(&mut self) -> Result<()> { - bail!("Core::init_swv unimplemented when unattached!"); - } - - fn read_swv(&mut self) -> Result> { - bail!("Core::read_swv unimplemented when unattached!"); - } - fn load(&mut self, _path: &Path) -> Result<()> { bail!("Core::load unimplemented when unattached!"); }