mod commands;
mod tools;
use crate::{
CoreStatus,
architecture::{
arm::{
ArmCommunicationInterface, ArmDebugInterface, ArmError, DapError, Pins, RawDapAccess,
RegisterAddress, SwoAccess, SwoConfig, SwoMode,
communication_interface::DapProbe,
dp::{Abort, Ctrl, DpRegister},
sequences::ArmDebugSequence,
swo::poll_interval_from_buf_size,
},
riscv::{
communication_interface::{RiscvError, RiscvInterfaceBuilder},
dtm::jtag_dtm::JtagDtmBuilder,
},
xtensa::communication_interface::{
XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
},
},
probe::{
AutoImplementJtagAccess, BatchCommand, DebugProbe, DebugProbeError, DebugProbeInfo,
DebugProbeSelector, JtagAccess, JtagDriverState, ProbeFactory, WireProtocol,
cmsisdap::commands::{
CmsisDapError, RequestError,
general::info::{CapabilitiesCommand, PacketCountCommand, SWOTraceBufferSizeCommand},
},
},
};
use commands::{
CmsisDapDevice, Status,
general::{
connect::{ConnectRequest, ConnectResponse},
disconnect::{DisconnectRequest, DisconnectResponse},
host_status::{HostStatusRequest, HostStatusResponse},
info::Capabilities,
reset::{ResetRequest, ResetResponse},
},
jtag::{
JtagBuffer,
configure::ConfigureRequest as JtagConfigureRequest,
sequence::{
Sequence as JtagSequence, SequenceRequest as JtagSequenceRequest,
SequenceResponse as JtagSequenceResponse,
},
},
swd,
swj::{
clock::SWJClockRequest,
pins::{SWJPinsRequest, SWJPinsRequestBuilder, SWJPinsResponse},
sequence::{SequenceRequest, SequenceResponse},
},
swo,
transfer::{
Ack, TransferBlockRequest, TransferBlockResponse, TransferRequest,
configure::ConfigureRequest,
},
};
use probe_rs_target::ScanChainElement;
use std::{fmt::Write, sync::Arc, time::Duration};
use bitvec::prelude::*;
use super::common::{ScanChainError, extract_idcodes, extract_ir_lengths};
#[derive(Debug)]
pub struct CmsisDapFactory;
impl std::fmt::Display for CmsisDapFactory {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("CMSIS-DAP")
}
}
impl ProbeFactory for CmsisDapFactory {
fn open(&self, selector: &DebugProbeSelector) -> Result<Box<dyn DebugProbe>, DebugProbeError> {
CmsisDap::new_from_device(tools::open_device_from_selector(selector)?)
.map(Box::new)
.map(DebugProbe::into_probe)
}
fn list_probes(&self) -> Vec<DebugProbeInfo> {
tools::list_cmsisdap_devices()
}
}
pub struct CmsisDap {
device: CmsisDapDevice,
_hw_version: u8,
_jtag_version: u8,
protocol: Option<WireProtocol>,
packet_size: u16,
packet_count: u8,
capabilities: Capabilities,
swo_buffer_size: Option<usize>,
swo_active: bool,
swo_streaming: bool,
connected: bool,
speed_khz: u32,
batch: Vec<BatchCommand>,
jtag_state: JtagDriverState,
jtag_buffer: JtagBuffer,
}
impl std::fmt::Debug for CmsisDap {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.debug_struct("CmsisDap")
.field("protocol", &self.protocol)
.field("packet_size", &self.packet_size)
.field("packet_count", &self.packet_count)
.field("capabilities", &self.capabilities)
.field("swo_buffer_size", &self.swo_buffer_size)
.field("swo_active", &self.swo_active)
.field("swo_streaming", &self.swo_streaming)
.field("speed_khz", &self.speed_khz)
.finish()
}
}
impl CmsisDap {
fn new_from_device(mut device: CmsisDapDevice) -> Result<Self, DebugProbeError> {
device.drain();
let packet_size = device.find_packet_size()? as u16;
let packet_count = commands::send_command(&mut device, &PacketCountCommand {})?;
let caps: Capabilities = commands::send_command(&mut device, &CapabilitiesCommand {})?;
tracing::debug!("Detected probe capabilities: {:?}", caps);
let mut swo_buffer_size = None;
if caps.swo_uart_implemented || caps.swo_manchester_implemented {
let swo_size = commands::send_command(&mut device, &SWOTraceBufferSizeCommand {})?;
swo_buffer_size = Some(swo_size as usize);
tracing::debug!("Probe SWO buffer size: {}", swo_size);
}
Ok(Self {
device,
_hw_version: 0,
_jtag_version: 0,
protocol: None,
packet_count,
packet_size,
capabilities: caps,
swo_buffer_size,
swo_active: false,
swo_streaming: false,
connected: false,
speed_khz: 1_000,
batch: Vec::new(),
jtag_state: JtagDriverState::default(),
jtag_buffer: JtagBuffer::new(packet_size - 1),
})
}
fn set_swj_clock(&mut self, clock_speed_hz: u32) -> Result<(), CmsisDapError> {
let request = SWJClockRequest { clock_speed_hz };
commands::send_command(&mut self.device, &request).and_then(|v| match v.status {
Status::DapOk => Ok(()),
Status::DapError => Err(CmsisDapError::ErrorResponse(RequestError::SWJClock {
request,
})),
})
}
fn transfer_configure(&mut self, request: ConfigureRequest) -> Result<(), CmsisDapError> {
commands::send_command(&mut self.device, &request).and_then(|v| match v.status {
Status::DapOk => Ok(()),
Status::DapError => Err(CmsisDapError::ErrorResponse(
RequestError::TransferConfigure { request },
)),
})
}
fn configure_swd(
&mut self,
request: swd::configure::ConfigureRequest,
) -> Result<(), CmsisDapError> {
commands::send_command(&mut self.device, &request).and_then(|v| match v.status {
Status::DapOk => Ok(()),
Status::DapError => Err(CmsisDapError::ErrorResponse(RequestError::SwdConfigure {
request,
})),
})
}
fn jtag_ensure_test_logic_reset(&mut self) -> Result<(), CmsisDapError> {
let sequence = JtagSequence::no_capture(true, bits![0; 6])?;
let sequences = vec![sequence];
self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?;
Ok(())
}
fn jtag_ensure_run_test_idle(&mut self) -> Result<(), CmsisDapError> {
self.jtag_ensure_test_logic_reset()?;
let sequence = JtagSequence::no_capture(false, bits![0; 1])?;
let sequences = vec![sequence];
self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?;
Ok(())
}
fn jtag_scan(
&mut self,
ir_lengths: Option<&[usize]>,
) -> Result<Vec<ScanChainElement>, CmsisDapError> {
let (ir, dr) = self.jtag_reset_scan()?;
let idcodes = extract_idcodes(&dr)?;
let ir_lens = extract_ir_lengths(&ir, idcodes.len(), ir_lengths)?;
Ok(idcodes
.into_iter()
.zip(ir_lens)
.map(|(idcode, irlen)| ScanChainElement {
ir_len: Some(irlen as u8),
name: idcode.map(|i| i.to_string()),
})
.collect())
}
fn jtag_reset_scan(&mut self) -> Result<(BitVec, BitVec), CmsisDapError> {
let dr = self.jtag_scan_dr()?;
let ir = self.jtag_scan_ir()?;
self.jtag_ensure_run_test_idle()?;
Ok((ir, dr))
}
fn jtag_scan_ir(&mut self) -> Result<BitVec, CmsisDapError> {
self.jtag_ensure_shift_ir()?;
let data = self.jtag_scan_inner("IR")?;
Ok(data)
}
fn jtag_scan_dr(&mut self) -> Result<BitVec, CmsisDapError> {
self.jtag_ensure_shift_dr()?;
let data = self.jtag_scan_inner("DR")?;
Ok(data)
}
fn jtag_scan_inner(&mut self, name: &'static str) -> Result<BitVec, CmsisDapError> {
const MAX_LENGTH: usize = 128;
const BYTES_PER_REQUEST: usize = 16;
const REQUESTS: usize = MAX_LENGTH.div_ceil(BYTES_PER_REQUEST * 8);
let mut tdo_bytes: BitVec = BitVec::with_capacity(REQUESTS * BYTES_PER_REQUEST * 8);
for _ in 0..REQUESTS {
let sequences = vec![
JtagSequence::capture(false, bits![0; 64])?,
JtagSequence::capture(false, bits![0; 64])?,
];
tdo_bytes.extend_from_bitslice(
&self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?,
);
}
let d0 = tdo_bytes;
let mut tdo_bytes: BitVec<u8> = BitVec::with_capacity(REQUESTS * BYTES_PER_REQUEST * 8);
for _ in 0..REQUESTS {
let sequences = vec![
JtagSequence::capture(false, bits![1; 64])?,
JtagSequence::capture(false, bits![1; 64])?,
];
tdo_bytes.extend_from_bitslice(
&self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?,
);
}
let d1 = tdo_bytes;
let n = match d1.first_one() {
Some(n) => {
tracing::info!("JTAG {name} scan chain detected as {n} bits long");
n
}
None => {
let expected_bit = 1;
tracing::error!(
"JTAG {name} scan chain either broken or too long: did not detect {expected_bit}"
);
return Err(CmsisDapError::ErrorResponse(
RequestError::BrokenScanChain { name, expected_bit },
));
}
};
if n == 0 {
tracing::error!("JTAG {name} scan chain is empty");
return Err(CmsisDapError::ErrorResponse(RequestError::EmptyScanChain {
name,
}));
}
if d0[n..].any() {
let expected_bit = 0;
tracing::error!(
"JTAG {name} scan chain either broken or too long: did not detect {expected_bit}"
);
return Err(CmsisDapError::ErrorResponse(
RequestError::BrokenScanChain { name, expected_bit },
));
}
let data = d0[..n].to_bitvec();
Ok(data)
}
fn jtag_ensure_shift_dr(&mut self) -> Result<(), CmsisDapError> {
self.jtag_ensure_test_logic_reset()?;
let sequences = vec![
JtagSequence::no_capture(false, bits![0; 1])?,
JtagSequence::no_capture(true, bits![0; 1])?,
JtagSequence::no_capture(false, bits![0; 2])?,
];
self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?;
Ok(())
}
fn jtag_ensure_shift_ir(&mut self) -> Result<(), CmsisDapError> {
self.jtag_ensure_test_logic_reset()?;
let sequences = vec![
JtagSequence::no_capture(false, bits![0; 1])?,
JtagSequence::no_capture(true, bits![0; 2])?,
JtagSequence::no_capture(false, bits![0; 2])?,
];
self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?;
Ok(())
}
fn send_jtag_configure(&mut self, request: JtagConfigureRequest) -> Result<(), CmsisDapError> {
commands::send_command(&mut self.device, &request).and_then(|v| match v.status {
Status::DapOk => Ok(()),
Status::DapError => Err(CmsisDapError::ErrorResponse(RequestError::JtagConfigure {
request,
})),
})
}
fn send_jtag_sequences(
&mut self,
request: JtagSequenceRequest,
) -> Result<BitVec, CmsisDapError> {
commands::send_command(&mut self.device, &request).and_then(|v| match v {
JtagSequenceResponse(Status::DapOk, tdo) => Ok(tdo),
JtagSequenceResponse(Status::DapError, _) => {
Err(CmsisDapError::ErrorResponse(RequestError::JtagSequence {
request,
}))
}
})
}
fn send_swj_sequences(&mut self, request: SequenceRequest) -> Result<(), CmsisDapError> {
commands::send_command(&mut self.device, &request).and_then(|v| match v {
SequenceResponse(Status::DapOk) => Ok(()),
SequenceResponse(Status::DapError) => {
Err(CmsisDapError::ErrorResponse(RequestError::SwjSequence {
request,
}))
}
})
}
fn read_ctrl_register(&mut self) -> Result<Ctrl, ArmError> {
let mut request = TransferRequest::read(Ctrl::ADDRESS);
request.dap_index = self.jtag_state.chain_params.index as u8;
let response =
commands::send_command(&mut self.device, &request).map_err(DebugProbeError::from)?;
if response.last_transfer_response.protocol_error {
Err(DapError::Protocol(
self.protocol
.expect("A wire protocol should have been selected by now"),
)
.into())
} else {
if response.last_transfer_response.ack != Ack::Ok {
tracing::debug!(
"Error reading debug port CTRL register: {:?}. This should never fail!",
response.last_transfer_response.ack
);
}
match response.last_transfer_response.ack {
Ack::Ok => {
Ok(Ctrl(response.transfers[0].data.expect(
"CMSIS-DAP probe should always return data for a read.",
)))
}
Ack::Wait => Err(DapError::WaitResponse.into()),
Ack::Fault => Err(DapError::FaultResponse.into()),
Ack::NoAck => Err(DapError::NoAcknowledge.into()),
}
}
}
fn write_abort(&mut self, abort: Abort) -> Result<(), ArmError> {
let mut request = TransferRequest::write(Abort::ADDRESS, abort.into());
request.dap_index = self.jtag_state.chain_params.index as u8;
let response =
commands::send_command(&mut self.device, &request).map_err(DebugProbeError::from)?;
if response.last_transfer_response.protocol_error {
Err(DapError::Protocol(
self.protocol
.expect("A wire protocol should have been selected by now"),
)
.into())
} else {
match response.last_transfer_response.ack {
Ack::Ok => Ok(()),
Ack::Wait => Err(DapError::WaitResponse.into()),
Ack::Fault => Err(DapError::FaultResponse.into()),
Ack::NoAck => Err(DapError::NoAcknowledge.into()),
}
}
}
#[tracing::instrument(skip(self))]
fn process_batch(&mut self) -> Result<Option<u32>, ArmError> {
let batch = std::mem::take(&mut self.batch);
if batch.is_empty() {
return Ok(None);
}
tracing::debug!("{} items in batch", batch.len());
let mut transfers = TransferRequest::empty();
transfers.dap_index = self.jtag_state.chain_params.index as u8;
for command in batch.iter().cloned() {
match command {
BatchCommand::Read(port) => {
transfers.add_read(port);
}
BatchCommand::Write(port, value) => {
transfers.add_write(port, value);
}
}
}
let response =
commands::send_command(&mut self.device, &transfers).map_err(DebugProbeError::from)?;
let count = response.transfers.len();
tracing::debug!("{} of batch of {} items executed", count, batch.len());
if response.last_transfer_response.protocol_error {
tracing::warn!(
"Protocol error in response to command {}",
batch[count.saturating_sub(1)]
);
return Err(DapError::Protocol(
self.protocol
.expect("A wire protocol should have been selected by now"),
)
.into());
}
match response.last_transfer_response.ack {
Ack::Ok => {
if count < batch.len() {
tracing::warn!(
"CMSIS_DAP: Only {}/{} transfers were executed, but no error was reported.",
count,
batch.len()
);
return Err(ArmError::Other(format!(
"Possible error in CMSIS-DAP probe: Only {}/{} transfers were executed, but no error was reported.",
count,
batch.len()
)));
}
tracing::trace!("Last transfer status: ACK");
Ok(response.transfers[count - 1].data)
}
Ack::NoAck => {
tracing::debug!(
"Transfer status for batch item {}/{}: NACK",
count,
batch.len()
);
Err(DapError::NoAcknowledge.into())
}
Ack::Fault => {
tracing::debug!(
"Transfer status for batch item {}/{}: FAULT",
count,
batch.len()
);
let ctrl = self.read_ctrl_register()?;
tracing::trace!("Ctrl/Stat register value is: {:?}", ctrl);
if ctrl.sticky_err() {
self.write_abort({
let mut abort = Abort(0);
abort.set_stkerrclr(ctrl.sticky_err());
abort
})?;
}
Err(DapError::FaultResponse.into())
}
Ack::Wait => {
tracing::debug!(
"Transfer status for batch item {}/{}: WAIT",
count,
batch.len()
);
self.write_abort({
let mut abort = Abort(0);
abort.set_dapabort(true);
abort
})?;
Err(DapError::WaitResponse.into())
}
}
}
fn batch_add(&mut self, command: BatchCommand) -> Result<Option<u32>, ArmError> {
tracing::debug!("Adding command to batch: {}", command);
let command_is_read = matches!(command, BatchCommand::Read(_));
self.batch.push(command);
let max_writes = (self.packet_size as usize - 3) / (1 + 4);
if command_is_read || self.batch.len() == max_writes {
self.process_batch()
} else {
Ok(None)
}
}
fn set_swo_transport(
&mut self,
transport: swo::TransportRequest,
) -> Result<(), DebugProbeError> {
let response = commands::send_command(&mut self.device, &transport)?;
match response.status {
Status::DapOk => Ok(()),
Status::DapError => {
Err(CmsisDapError::ErrorResponse(RequestError::SwoTransport { transport }).into())
}
}
}
fn set_swo_mode(&mut self, mode: swo::ModeRequest) -> Result<(), DebugProbeError> {
let response = commands::send_command(&mut self.device, &mode)?;
match response.status {
Status::DapOk => Ok(()),
Status::DapError => {
Err(CmsisDapError::ErrorResponse(RequestError::SwoMode { mode }).into())
}
}
}
fn set_swo_baudrate(&mut self, request: swo::BaudrateRequest) -> Result<u32, DebugProbeError> {
let response = commands::send_command(&mut self.device, &request)?;
tracing::debug!("Requested baud {}, got {}", request.baudrate, response);
if response == 0 {
Err(CmsisDapError::SwoBaudrateNotConfigured.into())
} else {
Ok(response)
}
}
fn start_swo_capture(&mut self) -> Result<(), DebugProbeError> {
let command = swo::ControlRequest::Start;
let response = commands::send_command(&mut self.device, &command)?;
match response.status {
Status::DapOk => Ok(()),
Status::DapError => {
Err(CmsisDapError::ErrorResponse(RequestError::SwoControl { command }).into())
}
}
}
fn stop_swo_capture(&mut self) -> Result<(), DebugProbeError> {
let command = swo::ControlRequest::Stop;
let response = commands::send_command(&mut self.device, &command)?;
match response.status {
Status::DapOk => Ok(()),
Status::DapError => {
Err(CmsisDapError::ErrorResponse(RequestError::SwoControl { command }).into())
}
}
}
#[expect(dead_code)]
fn get_swo_status(&mut self) -> Result<swo::StatusResponse, DebugProbeError> {
Ok(commands::send_command(
&mut self.device,
&swo::StatusRequest,
)?)
}
#[expect(dead_code)]
fn get_swo_extended_status(
&mut self,
request: swo::ExtendedStatusRequest,
) -> Result<swo::ExtendedStatusResponse, DebugProbeError> {
Ok(commands::send_command(&mut self.device, &request)?)
}
fn get_swo_data(&mut self) -> Result<Vec<u8>, DebugProbeError> {
match self.swo_buffer_size {
Some(swo_buffer_size) => {
let n = usize::min(swo_buffer_size, self.packet_size as usize) as u16;
let response: swo::DataResponse =
commands::send_command(&mut self.device, &swo::DataRequest { max_count: n })?;
if response.status.error {
Err(CmsisDapError::SwoTraceStreamError.into())
} else {
Ok(response.data)
}
}
None => Ok(Vec::new()),
}
}
fn connect_if_needed(&mut self) -> Result<(), DebugProbeError> {
if self.connected {
return Ok(());
}
let protocol: ConnectRequest = if let Some(protocol) = self.protocol {
match protocol {
WireProtocol::Swd => ConnectRequest::Swd,
WireProtocol::Jtag => ConnectRequest::Jtag,
}
} else {
ConnectRequest::DefaultPort
};
let used_protocol =
commands::send_command(&mut self.device, &protocol).and_then(|v| match v {
ConnectResponse::SuccessfulInitForSWD => Ok(WireProtocol::Swd),
ConnectResponse::SuccessfulInitForJTAG => Ok(WireProtocol::Jtag),
ConnectResponse::InitFailed => {
Err(CmsisDapError::ErrorResponse(RequestError::InitFailed {
protocol: self.protocol,
}))
}
})?;
tracing::info!("Using protocol {}", used_protocol);
self.protocol = Some(used_protocol);
if matches!(self.protocol, Some(WireProtocol::Jtag)) {
commands::send_command(
&mut self.device,
&SWJPinsRequestBuilder::new().ntrst(true).build(),
)
.ok();
}
self.connected = true;
Ok(())
}
}
impl DebugProbe for CmsisDap {
fn get_name(&self) -> &str {
match &self.device {
CmsisDapDevice::V2 { handle, .. } => format!(
"CMSIS-DAP V2 IF: {} DESC: {:?}",
handle.interface_number(),
handle.descriptor()
)
.leak(),
#[cfg(feature = "cmsisdap_v1")]
_ => "CMSIS-DAP V1",
}
}
fn speed_khz(&self) -> u32 {
self.speed_khz
}
fn set_speed(&mut self, speed_khz: u32) -> Result<u32, DebugProbeError> {
self.set_swj_clock(speed_khz * 1_000)?;
self.speed_khz = speed_khz;
Ok(speed_khz)
}
#[tracing::instrument(skip(self))]
fn attach(&mut self) -> Result<(), DebugProbeError> {
tracing::debug!("Attaching to target system (clock = {}kHz)", self.speed_khz);
self.connect_if_needed()?;
self.set_speed(self.speed_khz)?;
self.transfer_configure(ConfigureRequest {
idle_cycles: 0,
wait_retry: 0xffff,
match_retry: 0,
})?;
if self.active_protocol() == Some(WireProtocol::Jtag) {
} else {
self.configure_swd(swd::configure::ConfigureRequest {})?;
}
let _: Result<HostStatusResponse, _> =
commands::send_command(&mut self.device, &HostStatusRequest::connected(true));
Ok(())
}
fn detach(&mut self) -> Result<(), crate::Error> {
self.process_batch()?;
if self.swo_active {
self.disable_swo()?;
}
let response = commands::send_command(&mut self.device, &DisconnectRequest {})
.map_err(DebugProbeError::from)?;
let request = HostStatusRequest::connected(false);
let _: Result<HostStatusResponse, _> = commands::send_command(&mut self.device, &request);
self.connected = false;
match response {
DisconnectResponse(Status::DapOk) => Ok(()),
DisconnectResponse(Status::DapError) => Err(crate::Error::Probe(
CmsisDapError::ErrorResponse(RequestError::HostStatus { request }).into(),
)),
}
}
fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
match protocol {
WireProtocol::Jtag if self.capabilities.jtag_implemented => {
self.protocol = Some(WireProtocol::Jtag);
Ok(())
}
WireProtocol::Swd if self.capabilities.swd_implemented => {
self.protocol = Some(WireProtocol::Swd);
Ok(())
}
_ => Err(DebugProbeError::UnsupportedProtocol(protocol)),
}
}
fn active_protocol(&self) -> Option<WireProtocol> {
self.protocol
}
fn target_reset(&mut self) -> Result<(), DebugProbeError> {
commands::send_command(&mut self.device, &ResetRequest).map(|v: ResetResponse| {
tracing::info!("Target reset response: {:?}", v);
})?;
Ok(())
}
fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
let request = SWJPinsRequestBuilder::new().nreset(false).build();
commands::send_command(&mut self.device, &request).map(|v: SWJPinsResponse| {
tracing::info!("Pin response: {:?}", v);
})?;
Ok(())
}
fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
let request = SWJPinsRequestBuilder::new().nreset(true).build();
commands::send_command(&mut self.device, &request).map(|v: SWJPinsResponse| {
tracing::info!("Pin response: {:?}", v);
})?;
Ok(())
}
fn get_swo_interface(&self) -> Option<&dyn SwoAccess> {
Some(self as _)
}
fn get_swo_interface_mut(&mut self) -> Option<&mut dyn SwoAccess> {
Some(self as _)
}
fn try_get_arm_debug_interface<'probe>(
self: Box<Self>,
sequence: Arc<dyn ArmDebugSequence>,
) -> Result<Box<dyn ArmDebugInterface + 'probe>, (Box<dyn DebugProbe>, ArmError)> {
Ok(ArmCommunicationInterface::create(self, sequence, false))
}
fn has_arm_interface(&self) -> bool {
true
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
self
}
fn try_as_jtag_probe(&mut self) -> Option<&mut dyn JtagAccess> {
Some(self)
}
fn try_as_dap_probe(&mut self) -> Option<&mut dyn DapProbe> {
Some(self)
}
fn has_riscv_interface(&self) -> bool {
true
}
fn try_get_riscv_interface_builder<'probe>(
&'probe mut self,
) -> Result<Box<dyn RiscvInterfaceBuilder<'probe> + 'probe>, RiscvError> {
Ok(Box::new(JtagDtmBuilder::new(self)))
}
fn try_get_xtensa_interface<'probe>(
&'probe mut self,
state: &'probe mut XtensaDebugInterfaceState,
) -> Result<XtensaCommunicationInterface<'probe>, XtensaError> {
Ok(XtensaCommunicationInterface::new(self, state))
}
fn has_xtensa_interface(&self) -> bool {
true
}
}
impl AutoImplementJtagAccess for CmsisDap {}
impl RawDapAccess for CmsisDap {
fn core_status_notification(&mut self, status: CoreStatus) -> Result<(), DebugProbeError> {
let running = status.is_running();
commands::send_command(&mut self.device, &HostStatusRequest::running(running))?;
Ok(())
}
fn raw_read_register(&mut self, address: RegisterAddress) -> Result<u32, ArmError> {
let res = self.batch_add(BatchCommand::Read(address))?;
Ok(res.unwrap())
}
fn raw_write_register(&mut self, address: RegisterAddress, value: u32) -> Result<(), ArmError> {
self.batch_add(BatchCommand::Write(address, value))
.map(|_| ())
}
fn raw_write_block(
&mut self,
address: RegisterAddress,
values: &[u32],
) -> Result<(), ArmError> {
self.process_batch()?;
let max_packet_size_words = (self.packet_size - 6) / 4;
let data_chunk_len = max_packet_size_words as usize;
for (i, chunk) in values.chunks(data_chunk_len).enumerate() {
let mut request = TransferBlockRequest::write_request(address, Vec::from(chunk));
request.dap_index = self.jtag_state.chain_params.index as u8;
tracing::debug!("Transfer block: chunk={}, len={} bytes", i, chunk.len() * 4);
let resp: TransferBlockResponse = commands::send_command(&mut self.device, &request)
.map_err(DebugProbeError::from)?;
if resp.transfer_response != 1 {
return Err(DebugProbeError::from(CmsisDapError::ErrorResponse(
RequestError::BlockTransfer {
dap_index: request.dap_index,
transfer_count: request.transfer_count,
transfer_request: request.transfer_request,
},
))
.into());
}
}
Ok(())
}
fn raw_read_block(
&mut self,
address: RegisterAddress,
values: &mut [u32],
) -> Result<(), ArmError> {
self.process_batch()?;
let max_packet_size_words = (self.packet_size - 6) / 4;
let data_chunk_len = max_packet_size_words as usize;
for (i, chunk) in values.chunks_mut(data_chunk_len).enumerate() {
let mut request = TransferBlockRequest::read_request(address, chunk.len() as u16);
request.dap_index = self.jtag_state.chain_params.index as u8;
tracing::debug!("Transfer block: chunk={}, len={} bytes", i, chunk.len() * 4);
let resp: TransferBlockResponse = commands::send_command(&mut self.device, &request)
.map_err(DebugProbeError::from)?;
if resp.transfer_response != 1 {
return Err(DebugProbeError::from(CmsisDapError::ErrorResponse(
RequestError::BlockTransfer {
dap_index: request.dap_index,
transfer_count: request.transfer_count,
transfer_request: request.transfer_request,
},
))
.into());
}
chunk.clone_from_slice(&resp.transfer_data[..]);
}
Ok(())
}
fn raw_flush(&mut self) -> Result<(), ArmError> {
self.process_batch()?;
Ok(())
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
self
}
fn configure_jtag(&mut self, skip_scan: bool) -> Result<(), DebugProbeError> {
let ir_lengths = if skip_scan {
self.jtag_state
.expected_scan_chain
.as_ref()
.map(|chain| chain.iter().filter_map(|s| s.ir_len).collect::<Vec<u8>>())
.unwrap_or_default()
} else {
let chain = self.jtag_scan(
self.jtag_state
.expected_scan_chain
.as_ref()
.map(|chain| {
chain
.iter()
.filter_map(|s| s.ir_len)
.map(|s| s as usize)
.collect::<Vec<usize>>()
})
.as_deref(),
)?;
chain.iter().map(|item| item.ir_len()).collect()
};
tracing::info!("Configuring JTAG with ir lengths: {:?}", ir_lengths);
self.send_jtag_configure(JtagConfigureRequest::new(ir_lengths)?)?;
Ok(())
}
fn jtag_sequence(&mut self, cycles: u8, tms: bool, tdi: u64) -> Result<(), DebugProbeError> {
self.connect_if_needed()?;
let tdi_bytes = tdi.to_le_bytes();
let sequence = JtagSequence::new(cycles, false, tms, tdi_bytes)?;
let sequences = vec![sequence];
self.send_jtag_sequences(JtagSequenceRequest::new(sequences)?)?;
Ok(())
}
fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
self.connect_if_needed()?;
let data = bits.to_le_bytes();
if tracing::enabled!(tracing::Level::TRACE) {
let mut seq = String::new();
let _ = write!(&mut seq, "swj sequence:");
for i in 0..bit_len {
let bit = (bits >> i) & 1;
if bit == 1 {
let _ = write!(&mut seq, "1");
} else {
let _ = write!(&mut seq, "0");
}
}
tracing::trace!("{}", seq);
}
self.send_swj_sequences(SequenceRequest::new(&data, bit_len)?)?;
Ok(())
}
fn swj_pins(
&mut self,
pin_out: u32,
pin_select: u32,
pin_wait: u32,
) -> Result<u32, DebugProbeError> {
self.connect_if_needed()?;
let request = SWJPinsRequest::from_raw_values(pin_out as u8, pin_select as u8, pin_wait);
let Pins(response) = commands::send_command(&mut self.device, &request)?;
Ok(response as u32)
}
}
impl DapProbe for CmsisDap {}
impl SwoAccess for CmsisDap {
fn enable_swo(&mut self, config: &SwoConfig) -> Result<(), ArmError> {
let caps = self.capabilities;
match config.mode() {
SwoMode::Uart if !caps.swo_uart_implemented => {
return Err(ArmError::Probe(CmsisDapError::SwoModeNotAvailable.into()));
}
SwoMode::Manchester if !caps.swo_manchester_implemented => {
return Err(ArmError::Probe(CmsisDapError::SwoModeNotAvailable.into()));
}
_ => (),
}
self.stop_swo_capture()?;
if caps.swo_streaming_trace_implemented && self.device.swo_streaming_supported() {
tracing::debug!("Starting SWO capture with streaming transport");
self.set_swo_transport(swo::TransportRequest::WinUsbEndpoint)?;
self.swo_streaming = true;
} else {
tracing::debug!("Starting SWO capture with polled transport");
self.set_swo_transport(swo::TransportRequest::DataCommand)?;
self.swo_streaming = false;
}
match config.mode() {
SwoMode::Uart => self.set_swo_mode(swo::ModeRequest::Uart)?,
SwoMode::Manchester => self.set_swo_mode(swo::ModeRequest::Manchester)?,
}
let baud = self.set_swo_baudrate(swo::BaudrateRequest {
baudrate: config.baud(),
})?;
if baud != config.baud() {
tracing::warn!(
"Target SWO baud rate not met: requested {}, got {}",
config.baud(),
baud
);
}
self.start_swo_capture()?;
self.swo_active = true;
Ok(())
}
fn disable_swo(&mut self) -> Result<(), ArmError> {
tracing::debug!("Stopping SWO capture");
self.stop_swo_capture()?;
self.swo_active = false;
Ok(())
}
fn read_swo_timeout(&mut self, timeout: Duration) -> Result<Vec<u8>, ArmError> {
if self.swo_active {
if self.swo_streaming {
let buffer = self
.device
.read_swo_stream(timeout)
.map_err(DebugProbeError::from)?;
tracing::trace!("SWO streaming buffer: {:?}", buffer);
Ok(buffer)
} else {
let data = self.get_swo_data()?;
tracing::trace!("SWO polled data: {:?}", data);
Ok(data)
}
} else {
Ok(Vec::new())
}
}
fn swo_poll_interval_hint(&mut self, config: &SwoConfig) -> Option<Duration> {
let caps = self.capabilities;
if caps.swo_streaming_trace_implemented && self.device.swo_streaming_supported() {
Some(Duration::from_secs(0))
} else {
match self.swo_buffer_size {
Some(buf_size) => poll_interval_from_buf_size(config, buf_size),
None => None,
}
}
}
fn swo_buffer_size(&mut self) -> Option<usize> {
self.swo_buffer_size
}
}
impl Drop for CmsisDap {
fn drop(&mut self) {
tracing::debug!("Detaching from CMSIS-DAP probe");
let _ = self.process_batch();
if self.swo_active {
let _ = self.disable_swo();
}
let _ = self.detach();
}
}
impl From<ScanChainError> for CmsisDapError {
fn from(error: ScanChainError) -> Self {
match error {
ScanChainError::InvalidIdCode => CmsisDapError::InvalidIdCode,
ScanChainError::InvalidIR => CmsisDapError::InvalidIR,
}
}
}