pub(crate) mod common;
pub mod usb_util;
pub mod blackmagic;
pub mod ch347usbjtag;
pub mod cmsisdap;
pub mod fake_probe;
pub mod ftdi;
pub mod glasgow;
pub mod jlink;
pub mod list;
pub(crate) mod queue;
mod selector;
pub mod sifliuart;
pub mod stlink;
pub mod wlink;
use crate::architecture::arm::sequences::{ArmDebugSequence, DefaultArmSequence};
use crate::architecture::arm::{ArmDebugInterface, ArmError};
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::probe::queue::{BatchExecutionError, DeferredResultSet, ErasedQueue};
use crate::{Error, Permissions, Session};
use bitvec::slice::BitSlice;
use bitvec::vec::BitVec;
use common::ScanChainError;
use parking_lot::RwLock;
use probe_rs_target::ScanChainElement;
use serde::{Deserialize, Serialize};
use std::any::Any;
use std::fmt;
use std::sync::{Arc, LazyLock};
pub use selector::DebugProbeSelector;
const LOW_TARGET_VOLTAGE_WARNING_THRESHOLD: f32 = 1.4;
static DRIVERS: LazyLock<RwLock<Vec<&'static dyn ProbeFactory>>> = LazyLock::new(|| {
let probes: Vec<&'static dyn ProbeFactory> = vec![
&blackmagic::BlackMagicProbeFactory,
&cmsisdap::CmsisDapFactory,
&ftdi::FtdiProbeFactory,
&stlink::StLinkFactory,
&jlink::JLinkFactory,
&wlink::WchLinkFactory,
&sifliuart::SifliUartFactory,
&glasgow::GlasgowFactory,
&ch347usbjtag::Ch347UsbJtagFactory,
];
RwLock::new(probes)
});
pub(crate) fn register_probe_factory(factory: &'static dyn ProbeFactory) {
DRIVERS.write().push(factory);
}
#[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<list::ProbeListItem>;
fn list_probes_filtered(
&self,
selector: Option<&DebugProbeSelector>,
) -> Vec<list::ProbeListItem> {
self.list_probes()
.into_iter()
.filter(|probe| {
selector
.as_ref()
.is_none_or(|s| s.matches_probe(&probe.info))
})
.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()
}
}
pub 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;
}
pub 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).zip(cap) {
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 enum IoSequenceItem {
Output(bool),
Input,
}
#[derive(Debug)]
pub 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 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,
}
}
}
pub trait AutoImplementJtagAccess: RawJtagIo + 'static {}
pub trait JtagAccess: DebugProbe {
fn set_expected_scan_chain(
&mut self,
scan_chain: &[ScanChainElement],
) -> Result<(), DebugProbeError>;
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: &ErasedQueue,
) -> 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) => {
let response = match self.write_register(
write.inner.address,
&write.inner.data,
write.inner.len,
) {
Ok(response) => response,
Err(e) => {
return Err(BatchExecutionError::new_from_debug_probe(e, results));
}
};
match (write.transform)(&write.inner, &response) {
Ok(res) => results.push(idx, res),
Err(e) => {
return Err(BatchExecutionError::new_specific(e, results));
}
}
}
JtagCommand::ShiftDr(write) => {
let response = match self.write_dr(&write.inner.data, write.inner.len) {
Ok(response) => response,
Err(e) => {
return Err(BatchExecutionError::new_from_debug_probe(e, results));
}
};
match (write.transform)(&write.inner, &response) {
Ok(res) => results.push(idx, res),
Err(e) => return Err(BatchExecutionError::new_specific(e, results)),
}
}
}
}
Ok(results)
}
}
pub struct JtagSequence {
pub tdo_capture: bool,
pub tms: bool,
pub data: BitVec,
}
#[derive(Debug, Clone)]
pub struct JtagWriteData {
pub address: u32,
pub data: Vec<u8>,
pub len: u32,
}
#[derive(Debug, Clone)]
pub struct ShiftDrData {
pub data: Vec<u8>,
pub len: u32,
}
#[derive(Debug, Clone)]
pub struct JtagWriteCommand<E> {
pub data: JtagWriteData,
pub transform: fn(&JtagWriteData, &BitSlice) -> Result<CommandResult, E>,
}
impl<E: std::error::Error + Send + Sync + 'static> From<JtagWriteCommand<E>> for JtagCommand {
fn from(value: JtagWriteCommand<E>) -> Self {
let typed_transform = value.transform;
let erased_command = ErasedCommand {
inner: value.data,
transform: Box::new(move |data, bits| {
(typed_transform)(data, bits)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}),
};
JtagCommand::WriteRegister(erased_command)
}
}
#[derive(Debug, Clone)]
pub struct ShiftDrCommand<E> {
pub inner: ShiftDrData,
pub transform: fn(&ShiftDrData, &BitSlice) -> Result<CommandResult, E>,
}
impl<E: std::error::Error + Send + Sync + 'static> From<ShiftDrCommand<E>> for JtagCommand {
fn from(value: ShiftDrCommand<E>) -> Self {
let typed_transform = value.transform;
let erased_command = ErasedCommand {
inner: value.inner,
transform: Box::new(move |data, bits| {
(typed_transform)(data, bits)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
}),
};
JtagCommand::ShiftDr(erased_command)
}
}
pub(crate) type ErasedTransformFn<T> = Box<
dyn Fn(&T, &BitSlice) -> Result<CommandResult, Box<dyn std::error::Error + Send + Sync>>
+ Send
+ Sync,
>;
pub(crate) struct ErasedCommand<T> {
pub inner: T,
pub transform: ErasedTransformFn<T>,
}
impl<T: std::fmt::Debug> std::fmt::Debug for ErasedCommand<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ErasedCommand")
.field("inner", &self.inner)
.field("transform", &"<fn>")
.finish()
}
}
#[derive(Debug)]
pub(crate) enum JtagCommand {
WriteRegister(ErasedCommand<JtagWriteData>),
ShiftDr(ErasedCommand<ShiftDrData>),
}
impl From<ErasedCommand<JtagWriteData>> for JtagCommand {
fn from(cmd: ErasedCommand<JtagWriteData>) -> Self {
JtagCommand::WriteRegister(cmd)
}
}
impl From<ErasedCommand<ShiftDrData>> for JtagCommand {
fn from(cmd: ErasedCommand<ShiftDrData>) -> Self {
JtagCommand::ShiftDr(cmd)
}
}
#[derive(Clone, Copy, Debug, Default)]
pub 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(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"),
}
}
}
#[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::<jlink::JLinkFactory>());
}
}