pub(crate) mod common;
pub(crate) mod usb_util;
pub mod blackmagic;
pub mod ch347usbjtag;
pub mod cmsisdap;
pub mod espusbjtag;
pub mod fake_probe;
pub mod ftdi;
pub mod glasgow;
pub mod jlink;
pub mod list;
pub mod sifliuart;
pub mod stlink;
pub mod wlink;
use crate::architecture::arm::sequences::{ArmDebugSequence, DefaultArmSequence};
use crate::architecture::arm::{ArmDebugInterface, ArmError, DapError};
use crate::architecture::arm::{RegisterAddress, SwoAccess, communication_interface::DapProbe};
use crate::architecture::riscv::communication_interface::{RiscvError, RiscvInterfaceBuilder};
use crate::architecture::xtensa::communication_interface::{
XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
};
use crate::config::TargetSelector;
use crate::config::registry::Registry;
use crate::probe::common::JtagState;
use crate::{Error, Permissions, Session};
use bitvec::slice::BitSlice;
use bitvec::vec::BitVec;
use common::ScanChainError;
use nusb::DeviceInfo;
use probe_rs_target::ScanChainElement;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
const LOW_TARGET_VOLTAGE_WARNING_THRESHOLD: f32 = 1.4;
#[derive(Copy, Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)]
pub enum WireProtocol {
Swd,
Jtag,
}
impl fmt::Display for WireProtocol {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
WireProtocol::Swd => f.write_str("SWD"),
WireProtocol::Jtag => f.write_str("JTAG"),
}
}
}
impl std::str::FromStr for WireProtocol {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_ascii_lowercase()[..] {
"swd" => Ok(WireProtocol::Swd),
"jtag" => Ok(WireProtocol::Jtag),
_ => Err(format!(
"'{s}' is not a valid protocol. Choose either 'swd' or 'jtag'."
)),
}
}
}
#[derive(Clone, Debug)]
pub enum BatchCommand {
Read(RegisterAddress),
Write(RegisterAddress, u32),
}
impl fmt::Display for BatchCommand {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
BatchCommand::Read(port) => {
write!(f, "Read(port={port:?})")
}
BatchCommand::Write(port, data) => {
write!(f, "Write(port={port:?}, data={data:#010x})")
}
}
}
}
pub trait ProbeError: std::error::Error + Send + Sync + std::any::Any {}
impl std::error::Error for Box<dyn ProbeError> {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.as_ref().source()
}
}
#[derive(Debug, thiserror::Error)]
#[error(transparent)]
pub struct BoxedProbeError(#[from] Box<dyn ProbeError>);
impl BoxedProbeError {
fn as_any(&self) -> &dyn std::any::Any {
self.0.as_ref()
}
pub fn is<T: ProbeError>(&self) -> bool {
self.as_any().is::<T>()
}
pub fn downcast_ref<T: ProbeError>(&self) -> Option<&T> {
self.as_any().downcast_ref()
}
pub fn downcast_mut<T: ProbeError>(&mut self) -> Option<&mut T> {
let any: &mut dyn std::any::Any = self.0.as_mut();
any.downcast_mut()
}
}
impl<T> From<T> for BoxedProbeError
where
T: ProbeError,
{
fn from(e: T) -> Self {
BoxedProbeError(Box::new(e))
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum DebugProbeError {
Usb(#[source] std::io::Error),
ProbeSpecific(#[source] BoxedProbeError),
ProbeCouldNotBeCreated(#[from] ProbeCreationError),
UnsupportedProtocol(WireProtocol),
#[ignore_extra_doc_attributes]
InterfaceNotAvailable {
interface_name: &'static str,
},
UnsupportedSpeed(u32),
#[ignore_extra_doc_attributes]
NotAttached,
#[ignore_extra_doc_attributes]
Attached,
TargetNotFound,
BatchError(BatchCommand),
#[ignore_extra_doc_attributes]
NotImplemented {
function_name: &'static str,
},
CommandNotSupportedByProbe {
command_name: &'static str,
},
JtagScanChain(#[from] ScanChainError),
#[display("{0}")]
Other(String),
Timeout,
}
impl<T: ProbeError> From<T> for DebugProbeError {
fn from(e: T) -> Self {
Self::ProbeSpecific(BoxedProbeError::from(e))
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum ProbeCreationError {
NotFound,
CouldNotOpen,
#[cfg(feature = "cmsisdap_v1")]
HidApi(#[from] hidapi::HidError),
Usb(#[source] std::io::Error),
ProbeSpecific(#[source] BoxedProbeError),
#[display("{0}")]
Other(&'static str),
}
impl<T: ProbeError> From<T> for ProbeCreationError {
fn from(e: T) -> Self {
Self::ProbeSpecific(BoxedProbeError::from(e))
}
}
#[derive(Debug)]
pub struct Probe {
inner: Box<dyn DebugProbe>,
attached: bool,
}
impl Probe {
pub fn new(probe: impl DebugProbe + 'static) -> Self {
Self {
inner: Box::new(probe),
attached: false,
}
}
pub(crate) fn from_attached_probe(probe: Box<dyn DebugProbe>) -> Self {
Self {
inner: probe,
attached: true,
}
}
pub fn from_specific_probe(probe: Box<dyn DebugProbe>) -> Self {
Probe {
inner: probe,
attached: false,
}
}
pub fn get_name(&self) -> String {
self.inner.get_name().to_string()
}
pub fn attach(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
let registry = Registry::from_builtin_families();
self.attach_with_registry(target, permissions, ®istry)
}
pub fn attach_with_registry(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
registry: &Registry,
) -> Result<Session, Error> {
Session::new(
self,
target.into(),
AttachMethod::Normal,
permissions,
registry,
)
}
pub fn attach_to_unspecified(&mut self) -> Result<(), Error> {
self.inner.attach()?;
self.attached = true;
Ok(())
}
pub fn attach_to_unspecified_under_reset(&mut self) -> Result<(), Error> {
if let Some(dap_probe) = self.try_as_dap_probe() {
DefaultArmSequence(()).reset_hardware_assert(dap_probe)?;
} else {
tracing::info!(
"Custom reset sequences are not supported on {}.",
self.get_name()
);
tracing::info!("Falling back to standard probe reset.");
self.target_reset_assert()?;
}
self.attach_to_unspecified()?;
Ok(())
}
pub fn attach_under_reset(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
) -> Result<Session, Error> {
let registry = Registry::from_builtin_families();
self.attach_under_reset_with_registry(target, permissions, ®istry)
}
pub fn attach_under_reset_with_registry(
self,
target: impl Into<TargetSelector>,
permissions: Permissions,
registry: &Registry,
) -> Result<Session, Error> {
Session::new(
self,
target.into(),
AttachMethod::UnderReset,
permissions,
registry,
)
.map_err(|e| match e {
Error::Arm(ArmError::Timeout)
| Error::Riscv(RiscvError::Timeout)
| Error::Xtensa(XtensaError::Timeout) => Error::Other(
"Timeout while attaching to target under reset. \
This can happen if the target is not responding to the reset sequence. \
Ensure the chip's reset pin is connected, or try attaching without reset \
(`connectUnderReset = false` for DAP Clients, or remove `connect-under-reset` \
option from CLI options.)."
.to_string(),
),
e => e,
})
}
pub fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
if !self.attached {
self.inner.select_protocol(protocol)
} else {
Err(DebugProbeError::Attached)
}
}
pub fn protocol(&self) -> Option<WireProtocol> {
self.inner.active_protocol()
}
pub fn detach(&mut self) -> Result<(), crate::Error> {
self.attached = false;
self.inner.detach()?;
Ok(())
}
pub fn target_reset(&mut self) -> Result<(), DebugProbeError> {
self.inner.target_reset()
}
pub fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Asserting target reset");
self.inner.target_reset_assert()
}
pub fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Deasserting target reset");
self.inner.target_reset_deassert()
}
pub fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
if !self.attached {
self.inner.set_speed(speed_khz)
} else {
Err(DebugProbeError::Attached)
}
}
pub fn speed_khz(&self) -> u32 {
self.inner.speed_khz()
}
pub fn has_xtensa_interface(&self) -> bool {
self.inner.has_xtensa_interface()
}
pub fn try_get_xtensa_interface<'probe>(
&'probe mut self,
state: &'probe mut XtensaDebugInterfaceState,
) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
if !self.attached {
Err(DebugProbeError::NotAttached.into())
} else {
Ok(self.inner.try_get_xtensa_interface(state)?)
}
}
pub fn has_arm_debug_interface(&self) -> bool {
self.inner.has_arm_interface()
}
pub fn try_into_arm_debug_interface<'probe>(
self,
sequence: Arc<dyn ArmDebugSequence>,
) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Self, ArmError)> {
if !self.attached {
Err((self, DebugProbeError::NotAttached.into()))
} else {
self.inner
.try_get_arm_debug_interface(sequence)
.map_err(|(probe, err)| (Probe::from_attached_probe(probe), err))
}
}
pub fn has_riscv_interface(&self) -> bool {
self.inner.has_riscv_interface()
}
pub fn try_get_riscv_interface_builder<'probe>(
&'probe mut self,
) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
if !self.attached {
Err(DebugProbeError::NotAttached.into())
} else {
self.inner.try_get_riscv_interface_builder()
}
}
pub fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
self.inner.try_as_jtag_probe()
}
pub fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
self.inner.get_swo_interface()
}
pub fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
self.inner.get_swo_interface_mut()
}
pub fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
self.inner.try_as_dap_probe()
}
pub fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
self.inner.get_target_voltage()
}
pub fn try_into<P: DebugProbe>(&mut self) -> Option<&mut P> {
(self.inner.as_mut() as &mut dyn Any).downcast_mut::<P>()
}
}
pub trait ProbeFactory: std::any::Any + std::fmt::Display + std::fmt::Debug + Sync {
fn open(&self, selector: &DebugProbeSelector) -> Result<Box<dyn DebugProbe>, DebugProbeError>;
fn list_probes(&self) -> Vec<DebugProbeInfo>;
fn list_probes_filtered(&self, selector: Option<&DebugProbeSelector>) -> Vec<DebugProbeInfo> {
self.list_probes()
.into_iter()
.filter(|probe| selector.as_ref().is_none_or(|s| s.matches_probe(probe)))
.collect()
}
}
pub trait DebugProbe: Any + Send + fmt::Debug {
fn get_name(&self) -> &str;
fn speed_khz(&self) -> u32;
fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError>;
fn attach(&mut self) -> Result<(), DebugProbeError>;
fn detach(&mut self) -> Result<(), crate::Error>;
fn target_reset(&mut self) -> Result<(), DebugProbeError>;
fn target_reset_assert(&mut self) -> Result<(), DebugProbeError>;
fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError>;
fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError>;
fn active_protocol(&self) -> Option<WireProtocol>;
fn has_arm_interface(&self) -> bool {
false
}
fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
None
}
fn try_get_arm_debug_interface<'probe>(
self: Box<Self>,
_sequence: Arc<dyn ArmDebugSequence>,
) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
Err((
self.into_probe(),
DebugProbeError::InterfaceNotAvailable {
interface_name: "ARM",
}
.into(),
))
}
fn try_get_riscv_interface_builder<'probe>(
&'probe mut self,
) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
Err(DebugProbeError::InterfaceNotAvailable {
interface_name: "RISC-V",
}
.into())
}
fn has_riscv_interface(&self) -> bool {
false
}
fn try_get_xtensa_interface<'probe>(
&'probe mut self,
_state: &'probe mut XtensaDebugInterfaceState,
) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
Err(DebugProbeError::InterfaceNotAvailable {
interface_name: "Xtensa",
}
.into())
}
fn has_xtensa_interface(&self) -> bool {
false
}
fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
None
}
fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
None
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe>;
fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
None
}
fn get_target_voltage(&mut self) -> Result<Option<f32>, DebugProbeError> {
Ok(None)
}
}
impl PartialEq for dyn ProbeFactory {
fn eq(&self, other: &Self) -> bool {
self.type_id() == other.type_id()
&& std::ptr::eq(
self as *const _ as *const (),
other as *const _ as *const (),
)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DebugProbeInfo {
pub identifier: String,
pub vendor_id: u16,
pub product_id: u16,
pub serial_number: Option<String>,
pub interface: Option<u8>,
pub is_hid_interface: bool,
probe_factory: &'static dyn ProbeFactory,
}
impl std::fmt::Display for DebugProbeInfo {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{} -- {:04x}:{:04x}",
self.identifier, self.vendor_id, self.product_id,
)?;
if let Some(interface) = self.interface {
write!(f, "-{}", interface)?;
}
write!(
f,
":{} ({})",
self.serial_number.as_deref().unwrap_or(""),
self.probe_factory
)
}
}
impl DebugProbeInfo {
pub fn new<S: Into<String>>(
identifier: S,
vendor_id: u16,
product_id: u16,
serial_number: Option<String>,
probe_factory: &'static dyn ProbeFactory,
interface: Option<u8>,
is_hid_interface: bool,
) -> Self {
Self {
identifier: identifier.into(),
vendor_id,
product_id,
serial_number,
probe_factory,
interface,
is_hid_interface,
}
}
pub fn open(&self) -> Result<Probe, DebugProbeError> {
let selector = DebugProbeSelector::from(self);
self.probe_factory
.open(&selector)
.map(Probe::from_specific_probe)
}
pub fn is_probe_type<F: ProbeFactory>(&self) -> bool {
self.probe_factory.type_id() == std::any::TypeId::of::<F>()
}
pub fn probe_type(&self) -> String {
self.probe_factory.to_string()
}
}
#[derive(thiserror::Error, Debug, docsplay::Display)]
pub enum DebugProbeSelectorParseError {
ParseInt(#[from] std::num::ParseIntError),
Format,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DebugProbeSelector {
pub vendor_id: u16,
pub product_id: u16,
pub interface: Option<u8>,
pub serial_number: Option<String>,
}
impl DebugProbeSelector {
pub(crate) fn matches(&self, info: &DeviceInfo) -> bool {
if self.interface.is_some() {
info.interfaces().any(|iface| {
self.match_probe_selector(
info.vendor_id(),
info.product_id(),
Some(iface.interface_number()),
info.serial_number(),
)
})
} else {
self.match_probe_selector(
info.vendor_id(),
info.product_id(),
None,
info.serial_number(),
)
}
}
pub fn matches_probe(&self, info: &DebugProbeInfo) -> bool {
self.match_probe_selector(
info.vendor_id,
info.product_id,
info.interface,
info.serial_number.as_deref(),
)
}
fn match_probe_selector(
&self,
vendor_id: u16,
product_id: u16,
interface: Option<u8>,
serial_number: Option<&str>,
) -> bool {
tracing::trace!(
"Matching probe selector:\nVendor ID: {vendor_id:04x} == {:04x}\nProduct ID: {product_id:04x} = {:04x}\nInterface: {interface:?} == {:?}\nSerial Number: {serial_number:?} == {:?}",
self.vendor_id,
self.product_id,
self.interface,
self.serial_number
);
vendor_id == self.vendor_id
&& product_id == self.product_id
&& self
.interface
.map(|iface| interface == Some(iface))
.unwrap_or(true) && self
.serial_number
.as_ref()
.map(|s| {
if let Some(serial_number) = serial_number {
serial_number == s
} else {
s.is_empty()
}
})
.unwrap_or(true)
}
}
impl TryFrom<&str> for DebugProbeSelector {
type Error = DebugProbeSelectorParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let mut split = value.splitn(3, ':');
let vendor_id = split.next().unwrap(); let mut product_id = split.next().ok_or(DebugProbeSelectorParseError::Format)?;
let interface = if let Some((id, iface)) = product_id.split_once("-") {
product_id = id;
if iface.is_empty() {
Ok(None)
} else {
iface.parse::<u8>().map(Some)
}
} else {
Ok(None)
}?;
let serial_number = split.next().map(|s| s.to_string());
Ok(DebugProbeSelector {
vendor_id: u16::from_str_radix(vendor_id, 16)?,
product_id: u16::from_str_radix(product_id, 16)?,
serial_number,
interface,
})
}
}
impl TryFrom<String> for DebugProbeSelector {
type Error = DebugProbeSelectorParseError;
fn try_from(value: String) -> Result<Self, Self::Error> {
TryFrom::<&str>::try_from(&value)
}
}
impl std::str::FromStr for DebugProbeSelector {
type Err = DebugProbeSelectorParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl From<DebugProbeInfo> for DebugProbeSelector {
fn from(selector: DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number,
interface: selector.interface,
}
}
}
impl From<&DebugProbeInfo> for DebugProbeSelector {
fn from(selector: &DebugProbeInfo) -> Self {
DebugProbeSelector {
vendor_id: selector.vendor_id,
product_id: selector.product_id,
serial_number: selector.serial_number.clone(),
interface: selector.interface,
}
}
}
impl From<&DebugProbeSelector> for DebugProbeSelector {
fn from(selector: &DebugProbeSelector) -> Self {
selector.clone()
}
}
impl fmt::Display for DebugProbeSelector {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04x}:{:04x}", self.vendor_id, self.product_id)?;
if let Some(ref sn) = self.serial_number {
write!(f, ":{sn}")?;
}
Ok(())
}
}
impl From<DebugProbeSelector> for String {
fn from(value: DebugProbeSelector) -> String {
value.to_string()
}
}
impl Serialize for DebugProbeSelector {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'a> Deserialize<'a> for DebugProbeSelector {
fn deserialize<D>(deserializer: D) -> Result<DebugProbeSelector, D::Error>
where
D: Deserializer<'a>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
pub(crate) trait RawSwdIo: DebugProbe {
fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
where
S: IntoIterator<Item = IoSequenceItem>;
fn swj_pins(
&mut self,
pin_out: u32,
pin_select: u32,
pin_wait: u32,
) -> Result<u32, DebugProbeError>;
fn swd_settings(&self) -> &SwdSettings;
fn probe_statistics(&mut self) -> &mut ProbeStatistics;
}
pub(crate) trait RawJtagIo: DebugProbe {
fn state_mut(&mut self) -> &mut JtagDriverState;
fn state(&self) -> &JtagDriverState;
fn shift_bits(
&mut self,
tms: impl IntoIterator<Item = bool>,
tdi: impl IntoIterator<Item = bool>,
cap: impl IntoIterator<Item = bool>,
) -> Result<(), DebugProbeError> {
for ((tms, tdi), cap) in tms.into_iter().zip(tdi.into_iter()).zip(cap.into_iter()) {
self.shift_bit(tms, tdi, cap)?;
}
Ok(())
}
fn shift_bit(&mut self, tms: bool, tdi: bool, capture: bool) -> Result<(), DebugProbeError>;
fn read_captured_bits(&mut self) -> Result<BitVec, DebugProbeError>;
fn reset_jtag_state_machine(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Resetting JTAG chain by setting tms high for 5 bits");
let tms = [true, true, true, true, true, false];
let tdi = std::iter::repeat(true);
self.shift_bits(tms, tdi, std::iter::repeat(false))?;
let response = self.read_captured_bits()?;
tracing::debug!("Response to reset: {response}");
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum IoSequenceItem {
Output(bool),
Input,
}
#[derive(Debug)]
pub(crate) struct SwdSettings {
pub num_idle_cycles_between_writes: usize,
pub num_retries_after_wait: usize,
pub max_retry_idle_cycles_after_wait: usize,
pub idle_cycles_before_write_verify: usize,
pub idle_cycles_after_transfer: usize,
}
impl Default for SwdSettings {
fn default() -> Self {
Self {
num_idle_cycles_between_writes: 2,
num_retries_after_wait: 1000,
max_retry_idle_cycles_after_wait: 128,
idle_cycles_before_write_verify: 8,
idle_cycles_after_transfer: 8,
}
}
}
#[derive(Debug)]
pub(crate) struct JtagDriverState {
pub state: JtagState,
pub expected_scan_chain: Option<Vec<ScanChainElement>>,
pub scan_chain: Vec<ScanChainElement>,
pub chain_params: ChainParams,
pub jtag_idle_cycles: usize,
}
impl JtagDriverState {
fn max_ir_address(&self) -> u32 {
(1 << self.chain_params.irlen) - 1
}
}
impl Default for JtagDriverState {
fn default() -> Self {
Self {
state: JtagState::Reset,
expected_scan_chain: None,
scan_chain: Vec::new(),
chain_params: ChainParams::default(),
jtag_idle_cycles: 0,
}
}
}
#[derive(Default, Debug)]
pub(crate) struct ProbeStatistics {
num_transfers: usize,
num_extra_transfers: usize,
num_io_calls: usize,
num_wait_resp: usize,
num_faults: usize,
}
impl ProbeStatistics {
pub fn record_extra_transfer(&mut self) {
self.num_extra_transfers += 1;
}
pub fn record_transfers(&mut self, num_transfers: usize) {
self.num_transfers += num_transfers;
}
pub fn report_io(&mut self) {
self.num_io_calls += 1;
}
pub fn report_swd_response<T>(&mut self, response: &Result<T, DapError>) {
match response {
Err(DapError::FaultResponse) => self.num_faults += 1,
Err(DapError::WaitResponse) => self.num_wait_resp += 1,
_ => (),
}
}
}
pub(crate) trait AutoImplementJtagAccess: RawJtagIo + 'static {}
pub trait JtagAccess: DebugProbe {
fn set_scan_chain(&mut self, scan_chain: &[ScanChainElement]) -> Result<(), DebugProbeError>;
fn scan_chain(&mut self) -> Result<&[ScanChainElement], DebugProbeError>;
fn shift_raw_sequence(&mut self, sequence: JtagSequence) -> Result<BitVec, DebugProbeError>;
fn tap_reset(&mut self) -> Result<(), DebugProbeError>;
fn set_idle_cycles(&mut self, idle_cycles: u8) -> Result<(), DebugProbeError>;
fn idle_cycles(&self) -> u8;
fn select_target(&mut self, index: usize) -> Result<(), DebugProbeError> {
if index != 0 {
return Err(DebugProbeError::NotImplemented {
function_name: "select_jtag_tap",
});
}
Ok(())
}
fn read_register(&mut self, address: u32, len: u32) -> Result<BitVec, DebugProbeError> {
let data = vec![0u8; len.div_ceil(8) as usize];
self.write_register(address, &data, len)
}
fn write_register(
&mut self,
address: u32,
data: &[u8],
len: u32,
) -> Result<BitVec, DebugProbeError>;
fn write_dr(&mut self, data: &[u8], len: u32) -> Result<BitVec, DebugProbeError>;
fn write_register_batch(
&mut self,
writes: &CommandQueue<JtagCommand>,
) -> Result<DeferredResultSet<CommandResult>, BatchExecutionError> {
tracing::debug!(
"Using default `JtagAccess::write_register_batch` hurts performance. Please implement proper batching for this probe."
);
let mut results = DeferredResultSet::new();
for (idx, write) in writes.iter() {
match write {
JtagCommand::WriteRegister(write) => {
match self
.write_register(write.address, &write.data, write.len)
.map_err(crate::Error::Probe)
.and_then(|response| (write.transform)(write, &response))
{
Ok(res) => results.push(idx, res),
Err(e) => return Err(BatchExecutionError::new(e, results)),
}
}
JtagCommand::ShiftDr(write) => {
match self
.write_dr(&write.data, write.len)
.map_err(crate::Error::Probe)
.and_then(|response| (write.transform)(write, &response))
{
Ok(res) => results.push(idx, res),
Err(e) => return Err(BatchExecutionError::new(e, results)),
}
}
}
}
Ok(results)
}
}
pub struct JtagSequence {
pub tdo_capture: bool,
pub tms: bool,
pub data: BitVec,
}
#[derive(Debug, Clone)]
pub struct JtagWriteCommand {
pub address: u32,
pub data: Vec<u8>,
pub len: u32,
pub transform: fn(&JtagWriteCommand, &BitSlice) -> Result<CommandResult, crate::Error>,
}
#[derive(Debug, Clone)]
pub struct ShiftDrCommand {
pub data: Vec<u8>,
pub len: u32,
pub transform: fn(&ShiftDrCommand, &BitSlice) -> Result<CommandResult, crate::Error>,
}
#[derive(Debug, Clone)]
pub enum JtagCommand {
WriteRegister(JtagWriteCommand),
ShiftDr(ShiftDrCommand),
}
impl From<JtagWriteCommand> for JtagCommand {
fn from(cmd: JtagWriteCommand) -> Self {
JtagCommand::WriteRegister(cmd)
}
}
impl From<ShiftDrCommand> for JtagCommand {
fn from(cmd: ShiftDrCommand) -> Self {
JtagCommand::ShiftDr(cmd)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct ChainParams {
pub index: usize,
pub irpre: usize,
pub irpost: usize,
pub drpre: usize,
pub drpost: usize,
pub irlen: usize,
}
impl ChainParams {
fn from_jtag_chain(chain: &[ScanChainElement], selected: usize) -> Option<Self> {
let mut params = Self {
index: selected,
..Default::default()
};
let mut found = false;
for (index, tap) in chain.iter().enumerate() {
let ir_len = tap.ir_len() as usize;
if index == selected {
params.irlen = ir_len;
found = true;
} else if found {
params.irpost += ir_len;
params.drpost += 1;
} else {
params.irpre += ir_len;
params.drpre += 1;
}
}
found.then_some(params)
}
}
#[derive(thiserror::Error, Debug)]
pub struct BatchExecutionError {
#[source]
pub error: crate::Error,
pub results: DeferredResultSet<CommandResult>,
}
impl BatchExecutionError {
pub(crate) fn new(
error: crate::Error,
results: DeferredResultSet<CommandResult>,
) -> BatchExecutionError {
BatchExecutionError { error, results }
}
}
impl std::fmt::Display for BatchExecutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Error cause was {}. Successful command count {}",
self.error,
self.results.len()
)
}
}
#[derive(Debug, Clone)]
pub enum CommandResult {
None,
U8(u8),
U16(u16),
U32(u32),
VecU8(Vec<u8>),
}
impl CommandResult {
pub fn into_u32(self) -> u32 {
match self {
CommandResult::U32(val) => val,
_ => panic!("CommandResult is not a u32"),
}
}
pub fn into_u8(self) -> u8 {
match self {
CommandResult::U8(val) => val,
_ => panic!("CommandResult is not a u8"),
}
}
}
pub struct CommandQueue<T> {
commands: Vec<(DeferredResultIndex, T)>,
cursor: usize,
}
impl<T: std::fmt::Debug> std::fmt::Debug for CommandQueue<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommandQueue")
.field("commands", &self.len())
.finish()
}
}
impl<T> Default for CommandQueue<T> {
fn default() -> Self {
Self {
commands: Vec::new(),
cursor: 0,
}
}
}
impl<T> CommandQueue<T> {
pub fn new() -> Self {
Self::default()
}
pub fn schedule(&mut self, command: impl Into<T>) -> DeferredResultIndex {
let index = DeferredResultIndex::new();
self.commands.push((index.clone(), command.into()));
index
}
pub fn len(&self) -> usize {
self.commands[self.cursor..].len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &(DeferredResultIndex, T)> {
self.commands[self.cursor..].iter()
}
pub(crate) fn rewind(&mut self, by: usize) -> bool {
if self.cursor >= by {
self.cursor -= by;
true
} else {
false
}
}
pub(crate) fn consume(&mut self, len: usize) {
debug_assert!(self.len() >= len);
self.cursor += len;
}
}
pub struct DeferredResultSet<T>(HashMap<DeferredResultIndex, T>);
impl<T: std::fmt::Debug> std::fmt::Debug for DeferredResultSet<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("DeferredResultSet").field(&self.0).finish()
}
}
impl<T> Default for DeferredResultSet<T> {
fn default() -> Self {
Self(HashMap::default())
}
}
impl<T> DeferredResultSet<T> {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
pub(crate) fn push(&mut self, idx: &DeferredResultIndex, result: T) {
self.0.insert(idx.clone(), result);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn merge_from(&mut self, other: DeferredResultSet<T>) {
self.0.extend(other.0);
self.0.retain(|k, _| k.should_capture());
}
pub fn take(&mut self, index: DeferredResultIndex) -> Result<T, DeferredResultIndex> {
self.0.remove(&index).ok_or(index)
}
}
#[derive(Eq)]
pub struct DeferredResultIndex(Arc<()>);
impl PartialEq for DeferredResultIndex {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl fmt::Debug for DeferredResultIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DeferredResultIndex")
.field(&self.id())
.finish()
}
}
impl DeferredResultIndex {
fn new() -> Self {
Self(Arc::new(()))
}
fn id(&self) -> usize {
Arc::as_ptr(&self.0) as usize
}
pub(crate) fn should_capture(&self) -> bool {
Arc::strong_count(&self.0) > 1
}
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl std::hash::Hash for DeferredResultIndex {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
}
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default, Serialize, Deserialize)]
pub enum AttachMethod {
#[default]
Normal,
UnderReset,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_is_probe_factory() {
let probe_info = DebugProbeInfo::new(
"Mock probe",
0x12,
0x23,
Some("mock_serial".to_owned()),
&ftdi::FtdiProbeFactory,
None,
false,
);
assert!(probe_info.is_probe_type::<ftdi::FtdiProbeFactory>());
assert!(!probe_info.is_probe_type::<espusbjtag::EspUsbJtagFactory>());
}
#[test]
fn test_parsing_many_colons() {
let selector: DebugProbeSelector = "303a:1001:DC:DA:0C:D3:FE:D8".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(
selector.serial_number,
Some("DC:DA:0C:D3:FE:D8".to_string())
);
}
#[test]
fn missing_serial_is_none() {
let selector: DebugProbeSelector = "303a:1001".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.serial_number, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_serial =
selector.match_probe_selector(0x303a, 0x1001, None, Some("serial"));
assert!(matches);
assert!(matches_with_serial);
}
#[test]
fn empty_serial_is_some() {
let selector: DebugProbeSelector = "303a:1001:".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.serial_number, Some(String::new()));
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_serial =
selector.match_probe_selector(0x303a, 0x1001, None, Some("serial"));
assert!(matches);
assert!(!matches_with_serial);
}
#[test]
fn missing_interface_is_none() {
let selector: DebugProbeSelector = "303a:1001".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
assert!(matches);
assert!(matches_with_interface);
}
#[test]
fn empty_interface_is_none() {
let selector: DebugProbeSelector = "303a:1001-".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, None);
let matches = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
assert!(matches);
assert!(matches_with_interface);
}
#[test]
fn set_interface_matches() {
let selector: DebugProbeSelector = "303a:1001-0".try_into().unwrap();
assert_eq!(selector.vendor_id, 0x303a);
assert_eq!(selector.product_id, 0x1001);
assert_eq!(selector.interface, Some(0));
let no_match = selector.match_probe_selector(0x303a, 0x1001, None, None);
let matches_with_interface = selector.match_probe_selector(0x303a, 0x1001, Some(0), None);
let no_match_with_wrong_interface =
selector.match_probe_selector(0x303a, 0x1001, Some(1), None);
assert!(!no_match);
assert!(matches_with_interface);
assert!(!no_match_with_wrong_interface);
}
}