use crate::{
architecture::arm::{
ap::AccessPortError,
dp::{Abort, Ctrl, DebugPortError, DpRegister, RdBuff, DPIDR},
ArmError, DapError, FullyQualifiedApAddress, PortType, RawDapAccess, Register,
},
probe::{
common::bits_to_byte, CommandResult, DebugProbe, DebugProbeError, JTAGAccess,
JtagCommandQueue, JtagWriteCommand, WireProtocol,
},
Error,
};
#[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(Default, Debug)]
pub 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,
_ => (),
}
}
}
const JTAG_ABORT_VALUE: u64 = 0x8;
const JTAG_ABORT_IR_VALUE: u32 = 0x8; const JTAG_DEBUG_PORT_IR_VALUE: u32 = 0xA;
const JTAG_ACCESS_PORT_IR_VALUE: u32 = 0xB;
const JTAG_STATUS_WAIT: u32 = 0x1;
const JTAG_STATUS_OK: u32 = 0x2;
const JTAG_DR_BIT_LENGTH: u32 = 35;
fn build_jtag_payload_and_address(transfer: &DapTransfer) -> (u64, u32) {
if transfer.is_abort() {
(JTAG_ABORT_VALUE, JTAG_ABORT_IR_VALUE)
} else {
let address = match transfer.port {
PortType::DebugPort => JTAG_DEBUG_PORT_IR_VALUE,
PortType::AccessPort => JTAG_ACCESS_PORT_IR_VALUE,
};
let mut payload = 0u64;
payload |= (transfer.value as u64) << 3;
payload |= (transfer.address as u64 & 0b1000) >> 1;
payload |= (transfer.address as u64 & 0b0100) >> 1;
payload |= u64::from(transfer.direction == TransferDirection::Read);
(payload, address)
}
}
fn parse_jtag_response(data: &[u8]) -> u64 {
let mut received = 0u64;
for v in data.iter() {
received >>= 8;
received |= (*v as u64) << 32;
}
received
}
fn perform_jtag_transfer<P: JTAGAccess + RawProtocolIo>(
probe: &mut P,
transfer: &DapTransfer,
) -> Result<(u32, TransferStatus), DebugProbeError> {
let (payload, address) = build_jtag_payload_and_address(transfer);
let data = payload.to_le_bytes();
let idle_cycles = probe.idle_cycles();
probe.set_idle_cycles(transfer.idle_cycles_after.min(255) as u8);
let result = probe.write_register(address, &data[..], JTAG_DR_BIT_LENGTH);
probe.set_idle_cycles(idle_cycles);
let result = result?;
let received = parse_jtag_response(&result);
if transfer.is_abort() {
return Ok((0, TransferStatus::Ok));
}
let received_value = (received >> 3) as u32;
let status = (received & 0b111) as u32;
let transfer_status = match status {
s if s == JTAG_STATUS_WAIT => TransferStatus::Failed(DapError::WaitResponse),
s if s == JTAG_STATUS_OK => TransferStatus::Ok,
_ => {
tracing::debug!("Unexpected DAP response: {}", status);
TransferStatus::Failed(DapError::NoAcknowledge)
}
};
Ok((received_value, transfer_status))
}
fn perform_jtag_transfers<P: JTAGAccess + RawProtocolIo>(
probe: &mut P,
transfers: &mut [DapTransfer],
) -> Result<(), DebugProbeError> {
let mut queue = JtagCommandQueue::new();
let mut results = vec![];
for transfer in transfers.iter() {
results.push(queue.schedule(transfer.jtag_write()));
}
let last_is_abort = transfers[transfers.len() - 1].is_abort();
let last_is_rdbuff = transfers[transfers.len() - 1].is_rdbuff();
if !last_is_abort && !last_is_rdbuff {
results.push(
queue.schedule(DapTransfer::read(PortType::DebugPort, RdBuff::ADDRESS).jtag_write()),
);
}
if !last_is_abort {
results.push(
queue.schedule(DapTransfer::read(PortType::DebugPort, Ctrl::ADDRESS).jtag_write()),
);
results.push(
queue.schedule(DapTransfer::read(PortType::DebugPort, RdBuff::ADDRESS).jtag_write()),
);
}
let mut status_responses = vec![TransferStatus::Pending; results.len()];
let max_idle_cycles = transfers
.iter()
.map(|t| t.idle_cycles_after)
.max()
.unwrap_or(0);
let idle_cycles = probe.idle_cycles();
probe.set_idle_cycles(max_idle_cycles.min(255) as u8);
let mut jtag_results;
match probe.write_register_batch(&queue) {
Ok(r) => {
status_responses.fill(TransferStatus::Ok);
jtag_results = r;
}
Err(e) => {
let current_idx = e.results.len();
status_responses[..current_idx].fill(TransferStatus::Ok);
jtag_results = e.results;
match e.error {
Error::Arm(ArmError::AccessPort {
address: _,
source: AccessPortError::DebugPort(DebugPortError::Dap(failure)),
}) => {
status_responses[current_idx..].fill(TransferStatus::Failed(failure));
jtag_results.push(&results[current_idx], CommandResult::None);
}
Error::Probe(error) => return Err(error),
_other => unreachable!(),
}
}
}
probe.set_idle_cycles(idle_cycles);
for (i, transfer) in transfers.iter_mut().enumerate() {
transfer.status = *status_responses.get(i + 1).unwrap_or(&TransferStatus::Ok);
}
let ctrl_value = if !last_is_abort {
_ = results
.pop()
.expect("Failed to pop value that was pushed here.");
let rdbuff_result = results
.pop()
.expect("Failed to pop value that was pushed here.");
Some(rdbuff_result)
} else {
None
};
for (i, result) in results.into_iter().skip(1).enumerate() {
let transfer = &mut transfers[i];
if transfer.is_abort() || transfer.is_rdbuff() {
transfer.status = TransferStatus::Ok;
continue;
}
if transfer.status == TransferStatus::Ok && transfer.direction == TransferDirection::Read {
let response = jtag_results.take(result).unwrap();
transfer.value = response.into_u32();
}
}
if let Some(ctrl_value) = ctrl_value {
if let Ok(CommandResult::U32(received_value)) = jtag_results.take(ctrl_value) {
if Ctrl(received_value).sticky_err() {
tracing::debug!("JTAG transaction set failed: {:#X?}", transfers);
let (_, _) = perform_jtag_transfer(
probe,
&DapTransfer::write(PortType::DebugPort, Ctrl::ADDRESS, received_value),
)?;
for transfer in transfers.iter_mut() {
if transfer.status == TransferStatus::Ok {
transfer.status = TransferStatus::Failed(DapError::FaultResponse);
}
}
}
}
}
Ok(())
}
fn perform_swd_transfers<P: RawProtocolIo>(
probe: &mut P,
transfers: &mut [DapTransfer],
) -> Result<(), DebugProbeError> {
let mut io_sequence = IoSequence::new();
for transfer in transfers.iter() {
io_sequence.extend(&transfer.io_sequence());
}
let result = probe.swd_io(io_sequence.direction_bits(), io_sequence.io_bits())?;
let mut result_bits = &result[..];
for (i, transfer) in transfers.iter_mut().enumerate() {
let response_offset = 8;
let response = parse_swd_response(&result_bits[response_offset..], transfer.direction);
probe.probe_statistics().report_swd_response(&response);
transfer.status = match response {
Ok(response) => {
transfer.value = response;
TransferStatus::Ok
}
Err(e) => TransferStatus::Failed(e),
};
tracing::trace!(
"Transfer result {}: {:?} {:x?}",
i,
transfer.status,
transfer.value
);
result_bits = &result_bits[transfer.swd_response_length()..];
}
Ok(())
}
fn perform_transfers<P: DebugProbe + RawProtocolIo + JTAGAccess>(
probe: &mut P,
transfers: &mut [DapTransfer],
) -> Result<(), ArmError> {
assert!(!transfers.is_empty());
let mut final_transfers: Vec<DapTransfer> = Vec::with_capacity(transfers.len());
struct OriginalTransfer {
index: usize,
response_in_next: bool,
}
let mut result_indices = Vec::with_capacity(transfers.len());
let wire_protocol = probe.active_protocol().unwrap();
for (i, transfer) in transfers.iter().enumerate() {
let need_ap_read = transfer.is_ap_read();
let buffered_write = transfer.is_ap_write();
let write_response_pending = transfer.is_write() && !transfer.is_abort();
result_indices.push(OriginalTransfer {
index: final_transfers.len(),
response_in_next: wire_protocol == WireProtocol::Swd
&& (need_ap_read || write_response_pending),
});
let transfer = if transfer.is_write() {
let mut transfer = transfer.clone();
transfer.idle_cycles_after = probe.swd_settings().num_idle_cycles_between_writes;
transfer
} else {
transfer.clone()
};
final_transfers.push(transfer);
if wire_protocol == WireProtocol::Jtag {
continue;
}
let mut extra_idle_cycles = probe.swd_settings().idle_cycles_before_write_verify;
let mut need_extra = false;
if let Some(next) = transfers.get(i + 1) {
if need_ap_read && !next.is_ap_read() {
need_extra = true;
extra_idle_cycles = 0;
} else if buffered_write && next.must_not_stall() {
need_extra = true;
} else {
}
} else {
if !write_response_pending {
extra_idle_cycles = 0;
}
if need_ap_read || write_response_pending {
need_extra = true;
}
};
if need_extra {
final_transfers.last_mut().unwrap().idle_cycles_after += extra_idle_cycles;
final_transfers.push(DapTransfer::read(PortType::DebugPort, RdBuff::ADDRESS));
probe.probe_statistics().record_extra_transfer();
}
}
final_transfers.last_mut().unwrap().idle_cycles_after +=
probe.swd_settings().idle_cycles_after_transfer;
let num_transfers = final_transfers.len();
tracing::debug!(
"Performing {} transfers ({} additional transfers)",
num_transfers,
num_transfers - transfers.len()
);
probe.probe_statistics().record_transfers(num_transfers);
perform_raw_transfers_retry(probe, &mut final_transfers)?;
for (transfer, orig) in transfers.iter_mut().zip(result_indices) {
transfer.status = final_transfers[orig.index].status;
let response_idx = orig.index + orig.response_in_next as usize;
if orig.response_in_next && transfer.status == TransferStatus::Ok {
transfer.status = final_transfers[response_idx].status;
}
if transfer.direction == TransferDirection::Read {
transfer.value = final_transfers[response_idx].value;
}
}
Ok(())
}
fn perform_raw_transfers_retry<P: DebugProbe + RawProtocolIo + JTAGAccess>(
probe: &mut P,
transfers: &mut [DapTransfer],
) -> Result<(), ArmError> {
let mut successful_transfers = 0;
let mut idle_cycles = std::cmp::max(1, probe.swd_settings().num_idle_cycles_between_writes);
'transfer: for _ in 0..probe.swd_settings().num_retries_after_wait {
let chunk = &mut transfers[successful_transfers..];
assert!(!chunk.is_empty());
perform_raw_transfers(probe, chunk)?;
for transfer in chunk.iter() {
match transfer.status {
TransferStatus::Ok => successful_transfers += 1,
TransferStatus::Failed(DapError::WaitResponse) => {
tracing::debug!("got WAIT on transfer {}, retrying...", successful_transfers);
clear_overrun(probe)?;
for transfer in &mut chunk[..] {
if transfer.is_write() {
transfer.idle_cycles_after += idle_cycles;
}
}
idle_cycles = std::cmp::min(
probe.swd_settings().max_retry_idle_cycles_after_wait,
2 * idle_cycles,
);
continue 'transfer;
}
_ => break 'transfer, }
}
if successful_transfers == transfers.len() {
return Ok(());
}
}
write_dp_register(probe, {
let mut abort = Abort(0);
abort.set_dapabort(true);
abort
})?;
Ok(())
}
fn clear_overrun<P: DebugProbe + RawProtocolIo + JTAGAccess>(
probe: &mut P,
) -> Result<(), ArmError> {
write_dp_register(probe, {
let mut abort = Abort(0);
abort.set_orunerrclr(true);
abort.set_stkerrclr(true);
abort
})
}
fn write_dp_register<P: DebugProbe + RawProtocolIo + JTAGAccess, R: DpRegister>(
probe: &mut P,
register: R,
) -> Result<(), ArmError> {
let mut transfer = DapTransfer::write(PortType::DebugPort, R::ADDRESS, register.into());
transfer.idle_cycles_after = probe.swd_settings().idle_cycles_before_write_verify
+ probe.swd_settings().num_idle_cycles_between_writes;
perform_raw_transfers(probe, std::slice::from_mut(&mut transfer))?;
if let TransferStatus::Failed(e) = transfer.status {
Err(e)?
}
Ok(())
}
fn perform_raw_transfers<P: DebugProbe + RawProtocolIo + JTAGAccess>(
probe: &mut P,
transfers: &mut [DapTransfer],
) -> Result<(), DebugProbeError> {
match probe.active_protocol().unwrap() {
WireProtocol::Swd => perform_swd_transfers(probe, transfers),
WireProtocol::Jtag => perform_jtag_transfers(probe, transfers),
}
}
#[derive(Debug, Clone)]
struct DapTransfer {
port: PortType,
direction: TransferDirection,
address: u8,
value: u32,
status: TransferStatus,
idle_cycles_after: usize,
}
impl DapTransfer {
fn read(port: PortType, address: u8) -> DapTransfer {
Self {
port,
address,
direction: TransferDirection::Read,
value: 0,
status: TransferStatus::Pending,
idle_cycles_after: 0,
}
}
fn write(port: PortType, address: u8, value: u32) -> DapTransfer {
Self {
port,
address,
value,
direction: TransferDirection::Write,
status: TransferStatus::Pending,
idle_cycles_after: 0,
}
}
fn transfer_type(&self) -> TransferType {
match self.direction {
TransferDirection::Read => TransferType::Read,
TransferDirection::Write => TransferType::Write(self.value),
}
}
fn io_sequence(&self) -> IoSequence {
let mut seq = build_swd_transfer(self.port, self.transfer_type(), self.address);
seq.reserve(self.idle_cycles_after);
for _ in 0..self.idle_cycles_after {
seq.add_output(false);
}
seq
}
fn jtag_write(&self) -> JtagWriteCommand {
let (payload, address) = if self.is_abort() {
(JTAG_ABORT_VALUE, JTAG_ABORT_IR_VALUE)
} else {
let address = match self.port {
PortType::DebugPort => JTAG_DEBUG_PORT_IR_VALUE,
PortType::AccessPort => JTAG_ACCESS_PORT_IR_VALUE,
};
let mut payload = 0u64;
payload |= (self.value as u64) << 3;
payload |= (self.address as u64 & 0b1000) >> 1;
payload |= (self.address as u64 & 0b0100) >> 1;
payload |= u64::from(self.direction == TransferDirection::Read);
(payload, address)
};
JtagWriteCommand {
address,
data: payload.to_le_bytes().to_vec(),
len: JTAG_DR_BIT_LENGTH,
transform: |command, response| {
if command.address == JTAG_ABORT_IR_VALUE {
return Ok(CommandResult::None);
}
let received = parse_jtag_response(&response);
let received_value = (received >> 3) as u32;
let status = (received & 0b111) as u32;
let error = match status {
s if s == JTAG_STATUS_OK => return Ok(CommandResult::U32(received_value)),
s if s == JTAG_STATUS_WAIT => DapError::WaitResponse,
_ => {
tracing::debug!("Unexpected DAP response: {}", status);
DapError::NoAcknowledge
}
};
Err(Error::Arm(ArmError::AccessPort {
address: FullyQualifiedApAddress::v1_with_default_dp(0), source: AccessPortError::DebugPort(DebugPortError::Dap(error)),
}))
},
}
}
fn is_ap_read(&self) -> bool {
self.port == PortType::AccessPort && self.direction == TransferDirection::Read
}
fn is_ap_write(&self) -> bool {
self.port == PortType::AccessPort && self.direction == TransferDirection::Write
}
fn is_write(&self) -> bool {
self.direction == TransferDirection::Write
}
fn is_abort(&self) -> bool {
self.port == PortType::DebugPort
&& self.address == Abort::ADDRESS
&& self.direction == TransferDirection::Write
}
fn is_rdbuff(&self) -> bool {
self.port == PortType::DebugPort
&& self.address == RdBuff::ADDRESS
&& self.direction == TransferDirection::Read
}
fn swd_response_length(&self) -> usize {
self.direction.swd_response_length() + self.idle_cycles_after
}
fn must_not_stall(&self) -> bool {
let abort_write = self.is_abort();
let dpidr_read = self.port == PortType::DebugPort
&& self.address == DPIDR::ADDRESS
&& self.direction == TransferDirection::Read;
let ctrl_stat_read = self.port == PortType::DebugPort
&& self.address == Ctrl::ADDRESS
&& self.direction == TransferDirection::Read;
abort_write || dpidr_read || ctrl_stat_read
}
}
#[derive(Debug, PartialEq, Copy, Clone)]
enum TransferDirection {
Read,
Write,
}
impl TransferDirection {
const fn swd_response_length(self) -> usize {
match self {
TransferDirection::Read => 8 + 3 + 32 + 1 + 2,
TransferDirection::Write => 8 + 3 + 2 + 32 + 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
enum TransferStatus {
Pending,
Ok,
Failed(DapError),
}
struct IoSequence {
io: Vec<bool>,
direction: Vec<bool>,
}
impl IoSequence {
const INPUT: bool = false;
const OUTPUT: bool = true;
fn new() -> Self {
IoSequence {
io: vec![],
direction: vec![],
}
}
fn with_capacity(capacity: usize) -> Self {
IoSequence {
io: Vec::with_capacity(capacity),
direction: Vec::with_capacity(capacity),
}
}
fn reserve(&mut self, idle_cycles_after: usize) {
self.io.reserve(idle_cycles_after);
self.direction.reserve(idle_cycles_after);
}
fn from_bytes(data: &[u8], mut bits: usize) -> Self {
let mut this = Self::new();
'outer: for byte in data {
for i in 0..8 {
this.add_output(byte & (1 << i) != 0);
bits -= 1;
if bits == 0 {
break 'outer;
}
}
}
this
}
fn add_output(&mut self, bit: bool) {
self.io.push(bit);
self.direction.push(Self::OUTPUT);
}
fn add_input(&mut self) {
self.io.push(false);
self.direction.push(Self::INPUT);
}
fn add_input_sequence(&mut self, length: usize) {
for _ in 0..length {
self.add_input();
}
}
fn io_bits(&self) -> impl Iterator<Item = bool> + '_ {
self.io.iter().copied()
}
fn direction_bits(&self) -> impl Iterator<Item = bool> + '_ {
self.direction.iter().copied()
}
fn extend(&mut self, other: &IoSequence) {
self.io.extend_from_slice(&other.io);
self.direction.extend_from_slice(&other.direction);
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
enum TransferType {
Read,
Write(u32),
}
fn build_swd_transfer(port: PortType, direction: TransferType, address: u8) -> IoSequence {
let port = match port {
PortType::DebugPort => false,
PortType::AccessPort => true,
};
let direction_bit = direction == TransferType::Read;
let a2 = (address >> 2) & 0x01 == 1;
let a3 = (address >> 3) & 0x01 == 1;
let mut sequence = IoSequence::with_capacity(46);
sequence.add_output(true);
sequence.add_output(port);
sequence.add_output(direction_bit);
sequence.add_output(a2);
sequence.add_output(a3);
sequence.add_output(port ^ direction_bit ^ a2 ^ a3);
sequence.add_output(false);
sequence.add_output(true);
sequence.add_input();
sequence.add_input_sequence(3);
if let TransferType::Write(value) = direction {
sequence.add_input();
for i in 0..32 {
sequence.add_output(value & (1 << i) != 0);
}
sequence.add_output(value.count_ones() % 2 == 1);
} else {
sequence.add_input_sequence(32);
sequence.add_input();
sequence.add_input();
}
sequence
}
fn parse_swd_response(resp: &[bool], direction: TransferDirection) -> Result<u32, DapError> {
let (ack, response) = resp.split_at(3);
match (ack[0], ack[1], ack[2]) {
(true, true, true) => Err(DapError::NoAcknowledge),
(false, true, false) => Err(DapError::WaitResponse),
(false, false, true) => Err(DapError::FaultResponse),
(true, false, false) if direction == TransferDirection::Read => {
let value = bits_to_byte(response.iter().copied());
if value.count_ones() % 2 == response[32] as u32 {
tracing::trace!("DAP read {}.", value);
Ok(value)
} else {
Err(DapError::IncorrectParity)
}
}
(true, false, false) => Ok(0), _ => {
tracing::debug!(
"Unexpected response from target, does not conform to SWD specfication (ack={:?})",
resp
);
Err(DapError::SwdProtocol)
}
}
}
pub trait RawProtocolIo {
fn jtag_shift_tms<M>(&mut self, tms: M, tdi: bool) -> Result<(), DebugProbeError>
where
M: IntoIterator<Item = bool>;
fn jtag_shift_tdi<I>(&mut self, tms: bool, tdi: I) -> Result<(), DebugProbeError>
where
I: IntoIterator<Item = bool>;
fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
where
D: IntoIterator<Item = bool>,
S: IntoIterator<Item = bool>;
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;
}
impl<Probe: DebugProbe + RawProtocolIo + JTAGAccess + 'static> RawDapAccess for Probe {
fn raw_read_register(&mut self, port: PortType, address: u8) -> Result<u32, ArmError> {
let mut transfer = DapTransfer::read(port, address);
perform_transfers(self, std::slice::from_mut(&mut transfer))?;
match transfer.status {
TransferStatus::Ok => Ok(transfer.value),
TransferStatus::Failed(DapError::FaultResponse) => {
tracing::debug!("DAP FAULT");
if address != Ctrl::ADDRESS {
let response =
RawDapAccess::raw_read_register(self, PortType::DebugPort, Ctrl::ADDRESS)?;
let ctrl = Ctrl::try_from(response)?;
tracing::warn!(
"Reading DAP register failed. Ctrl/Stat register value is: {:#?}",
ctrl
);
if ctrl.sticky_orun() || ctrl.sticky_err() {
clear_overrun(self)?;
}
} else {
tracing::warn!("Error reading CTRL/STAT register. This should not happen...");
}
Err(DapError::FaultResponse.into())
}
TransferStatus::Failed(e) => Err(e.into()),
other => panic!(
"Unexpected transfer state after reading register: {other:?}. This is a bug!"
),
}
}
fn raw_read_block(
&mut self,
port: PortType,
address: u8,
values: &mut [u32],
) -> Result<(), ArmError> {
let mut transfers = vec![DapTransfer::read(port, address); values.len()];
perform_transfers(self, &mut transfers)?;
for (i, result) in transfers.iter().enumerate() {
match result.status {
TransferStatus::Ok => values[i] = result.value,
TransferStatus::Failed(err) => {
tracing::warn!(
"Error in access {}/{} of block access: {:?}",
i + 1,
values.len(),
err
);
return Err(err.into());
}
other => panic!(
"Unexpected transfer state after reading registers: {other:?}. This is a bug!"
),
}
}
Ok(())
}
fn raw_write_register(
&mut self,
port: PortType,
address: u8,
value: u32,
) -> Result<(), ArmError> {
let mut transfer = DapTransfer::write(port, address, value);
perform_transfers(self, std::slice::from_mut(&mut transfer))?;
match transfer.status {
TransferStatus::Ok => Ok(()),
TransferStatus::Failed(DapError::FaultResponse) => {
tracing::warn!("DAP FAULT");
let response =
RawDapAccess::raw_read_register(self, PortType::DebugPort, Ctrl::ADDRESS)?;
let ctrl = Ctrl::try_from(response)?;
tracing::warn!(
"Writing DAP register failed. Ctrl/Stat register value is: {:#?}",
ctrl
);
if ctrl.sticky_orun() || ctrl.sticky_err() {
clear_overrun(self)?;
}
Err(DapError::FaultResponse.into())
}
TransferStatus::Failed(e) => Err(e.into()),
other => panic!(
"Unexpected transfer state after writing register: {other:?}. This is a bug!"
),
}
}
fn raw_write_block(
&mut self,
port: PortType,
address: u8,
values: &[u32],
) -> Result<(), ArmError> {
let mut transfers = values
.iter()
.map(|v| DapTransfer::write(port, address, *v))
.collect::<Vec<_>>();
perform_transfers(self, &mut transfers)?;
for (i, result) in transfers.iter().enumerate() {
match result.status {
TransferStatus::Ok => {}
TransferStatus::Failed(err) => {
tracing::debug!(
"Error in access {}/{} of block access: {}",
i + 1,
values.len(),
err
);
return Err(err.into());
}
other => panic!(
"Unexpected transfer state after writing registers: {other:?}. This is a bug!"
),
}
}
Ok(())
}
fn swj_pins(
&mut self,
pin_out: u32,
pin_select: u32,
pin_wait: u32,
) -> Result<u32, DebugProbeError> {
RawProtocolIo::swj_pins(self, pin_out, pin_select, pin_wait)
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
self
}
fn jtag_sequence(&mut self, bit_len: u8, tms: bool, bits: u64) -> Result<(), DebugProbeError> {
let bits = (0..bit_len).map(|i| (bits >> i) & 1 == 1);
self.jtag_shift_tdi(tms, bits)?;
Ok(())
}
fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
let protocol = self.active_protocol().unwrap();
let io_sequence = IoSequence::from_bytes(&bits.to_le_bytes(), bit_len as usize);
send_sequence(self, protocol, &io_sequence)
}
fn core_status_notification(&mut self, _: crate::CoreStatus) -> Result<(), DebugProbeError> {
Ok(())
}
}
fn send_sequence<P: RawProtocolIo + JTAGAccess>(
probe: &mut P,
protocol: WireProtocol,
sequence: &IoSequence,
) -> Result<(), DebugProbeError> {
match protocol {
WireProtocol::Jtag => {
probe.jtag_shift_tms(sequence.io_bits(), false)?;
}
WireProtocol::Swd => {
probe.swd_io(sequence.direction_bits(), sequence.io_bits())?;
}
}
Ok(())
}
#[cfg(test)]
mod test {
use std::iter;
use crate::{
architecture::arm::{PortType, RawDapAccess},
error::Error,
probe::{DebugProbe, DebugProbeError, JTAGAccess, ScanChainElement, WireProtocol},
};
use super::{
parse_jtag_response, ProbeStatistics, RawProtocolIo, SwdSettings, JTAG_ABORT_IR_VALUE,
JTAG_ACCESS_PORT_IR_VALUE, JTAG_DEBUG_PORT_IR_VALUE, JTAG_DR_BIT_LENGTH, JTAG_STATUS_OK,
JTAG_STATUS_WAIT,
};
use bitvec::prelude::*;
#[allow(dead_code)]
enum DapAcknowledge {
Ok,
Wait,
Fault,
NoAck,
}
#[derive(Debug)]
struct ExpectedJtagTransaction {
ir_address: u32,
address: u32,
value: u32,
read: bool,
result: u64,
}
#[derive(Debug)]
struct MockJaylink {
direction_input: Option<Vec<bool>>,
io_input: Option<Vec<bool>>,
transfer_responses: Vec<Vec<bool>>,
jtag_transactions: Vec<ExpectedJtagTransaction>,
expected_transfer_count: usize,
performed_transfer_count: usize,
swd_settings: SwdSettings,
probe_statistics: ProbeStatistics,
protocol: WireProtocol,
idle_cycles: u8,
}
impl MockJaylink {
fn new() -> Self {
Self {
direction_input: None,
io_input: None,
transfer_responses: vec![vec![]],
jtag_transactions: vec![],
expected_transfer_count: 1,
performed_transfer_count: 0,
swd_settings: SwdSettings::default(),
probe_statistics: ProbeStatistics::default(),
protocol: WireProtocol::Swd,
idle_cycles: 0,
}
}
fn add_write_response(&mut self, acknowledge: DapAcknowledge, idle_cycles: usize) {
let last_transfer = self.transfer_responses.last_mut().unwrap();
let write_length = 8 + 1 + 3 + 2 + 32 + idle_cycles;
let mut response = BitVec::<usize, Lsb0>::repeat(false, write_length);
match acknowledge {
DapAcknowledge::Ok => {
response.set(8, true);
}
DapAcknowledge::Wait => {
response.set(9, true);
}
DapAcknowledge::Fault => {
response.set(10, true);
}
DapAcknowledge::NoAck => {
}
}
last_transfer.extend(response);
}
fn add_jtag_abort(&mut self) {
let expected = ExpectedJtagTransaction {
ir_address: JTAG_ABORT_IR_VALUE,
address: 0,
value: 0,
read: false,
result: 0,
};
self.jtag_transactions.push(expected);
self.expected_transfer_count += 1;
}
fn add_jtag_response(
&mut self,
port: PortType,
address: u32,
read: bool,
acknowlege: DapAcknowledge,
output_value: u32,
input_value: u32,
) {
let mut response = (output_value as u64) << 3;
let status = match acknowlege {
DapAcknowledge::Ok => JTAG_STATUS_OK,
DapAcknowledge::Wait => JTAG_STATUS_WAIT,
_ => 0b111,
};
response |= status as u64;
let expected = ExpectedJtagTransaction {
ir_address: if port == PortType::DebugPort {
JTAG_DEBUG_PORT_IR_VALUE
} else {
JTAG_ACCESS_PORT_IR_VALUE
},
address,
value: input_value,
read,
result: response,
};
self.jtag_transactions.push(expected);
self.expected_transfer_count += 1;
}
fn add_read_response(&mut self, acknowledge: DapAcknowledge, value: u32) {
let last_transfer = self.transfer_responses.last_mut().unwrap();
let write_length = 8 + 1 + 3 + 32 + 2;
let mut response = BitVec::<usize, Lsb0>::repeat(false, write_length);
match acknowledge {
DapAcknowledge::Ok => {
response.set(8, true);
}
DapAcknowledge::Wait => {
response.set(9, true);
}
DapAcknowledge::Fault => {
response.set(10, true);
}
DapAcknowledge::NoAck => {
}
}
response.get_mut(11..11 + 32).unwrap().store_le(value);
let parity_bit = value.count_ones() % 2 == 1;
response.set(11 + 32, parity_bit);
last_transfer.extend(response);
}
fn add_idle_cycles(&mut self, len: usize) {
let last_transfer = self.transfer_responses.last_mut().unwrap();
last_transfer.extend(iter::repeat(false).take(len))
}
fn add_transfer(&mut self) {
self.transfer_responses.push(Vec::new());
self.expected_transfer_count += 1;
}
}
impl JTAGAccess for MockJaylink {
fn scan_chain(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn tap_reset(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn read_register(&mut self, _address: u32, _len: u32) -> Result<Vec<u8>, DebugProbeError> {
todo!()
}
fn set_idle_cycles(&mut self, idle_cycles: u8) {
self.idle_cycles = idle_cycles;
}
fn idle_cycles(&self) -> u8 {
self.idle_cycles
}
fn write_register(
&mut self,
address: u32,
data: &[u8],
len: u32,
) -> Result<Vec<u8>, DebugProbeError> {
let jtag_value = parse_jtag_response(&data[..5]);
assert_eq!(len, JTAG_DR_BIT_LENGTH);
let jtag_transaction = self.jtag_transactions.remove(0);
assert_eq!(
jtag_transaction.ir_address,
address,
"Address mismatch with {} remaining transactions",
self.jtag_transactions.len()
);
if jtag_transaction.ir_address != JTAG_ABORT_IR_VALUE {
let value = (jtag_value >> 3) as u32;
let rnw = jtag_value & 1 == 1;
let dap_address = ((jtag_value & 0x6) << 1) as u32;
assert_eq!(dap_address, jtag_transaction.address);
assert_eq!(rnw, jtag_transaction.read);
assert_eq!(value, jtag_transaction.value);
}
self.performed_transfer_count += 1;
let ret = jtag_transaction.result;
Ok(ret.to_le_bytes()[..5].to_vec())
}
fn write_dr(&mut self, _data: &[u8], _len: u32) -> Result<Vec<u8>, DebugProbeError> {
unimplemented!()
}
}
impl RawProtocolIo for MockJaylink {
fn jtag_shift_tms<M>(&mut self, _tms: M, _tdi: bool) -> Result<(), DebugProbeError>
where
M: IntoIterator<Item = bool>,
{
Ok(())
}
fn jtag_shift_tdi<I>(&mut self, _tms: bool, _tdi: I) -> Result<(), DebugProbeError>
where
I: IntoIterator<Item = bool>,
{
Ok(())
}
fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
where
D: IntoIterator<Item = bool>,
S: IntoIterator<Item = bool>,
{
self.direction_input = Some(dir.into_iter().collect());
self.io_input = Some(swdio.into_iter().collect());
assert_eq!(
self.direction_input.as_ref().unwrap().len(),
self.io_input.as_ref().unwrap().len()
);
let transfer_response = self.transfer_responses.remove(0);
let io_bits = self.io_input.as_ref().map(|v| v.len()).unwrap();
assert_eq!(
transfer_response.len(),
io_bits,
"Length mismatch for transfer {}/{}. Transferred {} bits, expected {}",
self.performed_transfer_count + 1,
self.expected_transfer_count,
io_bits,
transfer_response.len(),
);
self.performed_transfer_count += 1;
Ok(transfer_response)
}
fn swj_pins(
&mut self,
_pin_out: u32,
_pin_select: u32,
_pin_wait: u32,
) -> Result<u32, DebugProbeError> {
Err(DebugProbeError::CommandNotSupportedByProbe {
command_name: "swj_pins",
})
}
fn swd_settings(&self) -> &SwdSettings {
&self.swd_settings
}
fn probe_statistics(&mut self) -> &mut ProbeStatistics {
&mut self.probe_statistics
}
}
impl DebugProbe for MockJaylink {
fn get_name(&self) -> &str {
todo!()
}
fn speed_khz(&self) -> u32 {
todo!()
}
fn set_speed(&mut self, _speed_khz: u32) -> Result<u32, DebugProbeError> {
todo!()
}
fn set_scan_chain(
&mut self,
_scan_chain: Vec<ScanChainElement>,
) -> Result<(), DebugProbeError> {
todo!()
}
fn scan_chain(&self) -> Result<&[ScanChainElement], DebugProbeError> {
todo!()
}
fn attach(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn detach(&mut self) -> Result<(), Error> {
todo!()
}
fn target_reset(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn target_reset_assert(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn target_reset_deassert(&mut self) -> Result<(), DebugProbeError> {
todo!()
}
fn select_protocol(&mut self, protocol: WireProtocol) -> Result<(), DebugProbeError> {
self.protocol = protocol;
Ok(())
}
fn active_protocol(&self) -> Option<WireProtocol> {
Some(self.protocol)
}
fn into_probe(self: Box<Self>) -> Box<dyn DebugProbe> {
todo!()
}
}
#[test]
fn read_register() {
let read_value = 12;
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Ok, read_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
let result = mock.raw_read_register(PortType::AccessPort, 4).unwrap();
assert_eq!(result, read_value);
}
#[test]
fn read_register_jtag() {
let read_value = 12;
let mut mock = MockJaylink::new();
let result = mock.select_protocol(WireProtocol::Jtag);
assert!(result.is_ok());
mock.add_jtag_response(PortType::AccessPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(
PortType::DebugPort,
12,
true,
DapAcknowledge::Ok,
read_value,
0,
);
mock.add_jtag_response(PortType::DebugPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(PortType::DebugPort, 12, true, DapAcknowledge::Ok, 0, 0);
let result = mock.raw_read_register(PortType::AccessPort, 4).unwrap();
assert_eq!(result, read_value);
}
#[test]
fn read_register_with_wait_response() {
let read_value = 47;
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Wait, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.add_transfer();
mock.add_write_response(
DapAcknowledge::Ok,
mock.swd_settings.num_idle_cycles_between_writes,
);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.add_transfer();
mock.add_read_response(DapAcknowledge::Ok, read_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
let result = mock.raw_read_register(PortType::AccessPort, 4).unwrap();
assert_eq!(result, read_value);
}
#[test]
fn read_register_with_wait_response_jtag() {
let read_value = 47;
let mut mock = MockJaylink::new();
let result = mock.select_protocol(WireProtocol::Jtag);
assert!(result.is_ok());
mock.add_jtag_response(PortType::AccessPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(PortType::DebugPort, 12, true, DapAcknowledge::Wait, 0, 0);
mock.add_jtag_abort();
mock.add_jtag_response(PortType::AccessPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(
PortType::DebugPort,
12,
true,
DapAcknowledge::Ok,
read_value,
0,
);
mock.add_jtag_response(PortType::DebugPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(PortType::DebugPort, 12, true, DapAcknowledge::Ok, 0, 0);
let result = mock.raw_read_register(PortType::AccessPort, 4).unwrap();
assert_eq!(result, read_value);
}
#[test]
fn write_register() {
let mut mock = MockJaylink::new();
let idle_cycles = mock.swd_settings.num_idle_cycles_between_writes;
mock.add_write_response(DapAcknowledge::Ok, idle_cycles);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_before_write_verify);
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.raw_write_register(PortType::AccessPort, 4, 0x123)
.expect("Failed to write register");
}
#[test]
fn write_register_jtag() {
let mut mock = MockJaylink::new();
let result = mock.select_protocol(WireProtocol::Jtag);
assert!(result.is_ok());
mock.add_jtag_response(
PortType::AccessPort,
4,
false,
DapAcknowledge::Ok,
0x0,
0x123,
);
mock.add_jtag_response(
PortType::DebugPort,
12,
true,
DapAcknowledge::Ok,
0x123,
0x0,
);
mock.add_jtag_response(PortType::DebugPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(PortType::DebugPort, 12, true, DapAcknowledge::Ok, 0, 0);
mock.raw_write_register(PortType::AccessPort, 4, 0x123)
.expect("Failed to write register");
}
#[test]
fn write_register_with_wait_response() {
let mut mock = MockJaylink::new();
let idle_cycles = mock.swd_settings.num_idle_cycles_between_writes;
mock.add_write_response(DapAcknowledge::Ok, idle_cycles);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_before_write_verify);
mock.add_read_response(DapAcknowledge::Wait, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.add_transfer();
mock.add_write_response(DapAcknowledge::Ok, idle_cycles);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.add_transfer();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
mock.raw_write_register(PortType::AccessPort, 4, 0x123)
.expect("Failed to write register");
}
#[test]
fn write_register_with_wait_response_jtag() {
let mut mock = MockJaylink::new();
let result = mock.select_protocol(WireProtocol::Jtag);
assert!(result.is_ok());
mock.add_jtag_response(
PortType::AccessPort,
4,
false,
DapAcknowledge::Ok,
0x0,
0x123,
);
mock.add_jtag_response(
PortType::DebugPort,
12,
true,
DapAcknowledge::Wait,
0x0,
0x0,
);
mock.add_jtag_abort();
mock.add_jtag_response(
PortType::AccessPort,
4,
false,
DapAcknowledge::Ok,
0x0,
0x123,
);
mock.add_jtag_response(
PortType::DebugPort,
12,
true,
DapAcknowledge::Ok,
0x123,
0x0,
);
mock.add_jtag_response(PortType::DebugPort, 4, true, DapAcknowledge::Ok, 0, 0);
mock.add_jtag_response(PortType::DebugPort, 12, true, DapAcknowledge::Ok, 0, 0);
mock.raw_write_register(PortType::AccessPort, 4, 0x123)
.expect("Failed to write register");
}
mod transfer_handling {
use super::{
super::{perform_transfers, DapTransfer, TransferStatus},
DapAcknowledge, MockJaylink,
};
use crate::architecture::arm::PortType;
#[test]
fn single_dp_register_read() {
let register_value = 32354;
let mut transfers = vec![DapTransfer::read(PortType::DebugPort, 0)];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, register_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
let transfer_result = &transfers[0];
assert_eq!(transfer_result.status, TransferStatus::Ok);
assert_eq!(transfer_result.value, register_value);
}
#[test]
fn single_ap_register_read() {
let register_value = 0x11_22_33_44u32;
let mut transfers = vec![DapTransfer::read(PortType::AccessPort, 0)];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Ok, register_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
let transfer_result = &transfers[0];
assert_eq!(transfer_result.status, TransferStatus::Ok);
assert_eq!(transfer_result.value, register_value);
}
#[test]
fn ap_then_dp_register_read() {
let ap_read_value = 0x123223;
let dp_read_value = 0xFFAABB;
let mut transfers = vec![
DapTransfer::read(PortType::AccessPort, 4),
DapTransfer::read(PortType::DebugPort, 3),
];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Ok, ap_read_value);
mock.add_read_response(DapAcknowledge::Ok, dp_read_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
assert_eq!(transfers[0].status, TransferStatus::Ok);
assert_eq!(transfers[0].value, ap_read_value);
assert_eq!(transfers[1].status, TransferStatus::Ok);
assert_eq!(transfers[1].value, dp_read_value);
}
#[test]
fn dp_then_ap_register_read() {
let ap_read_value = 0x123223;
let dp_read_value = 0xFFAABB;
let mut transfers = vec![
DapTransfer::read(PortType::DebugPort, 3),
DapTransfer::read(PortType::AccessPort, 4),
];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, dp_read_value);
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Ok, ap_read_value);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
assert_eq!(transfers[0].status, TransferStatus::Ok);
assert_eq!(transfers[0].value, dp_read_value);
assert_eq!(transfers[1].status, TransferStatus::Ok);
assert_eq!(transfers[1].value, ap_read_value);
}
#[test]
fn multiple_ap_read() {
let ap_read_values = [1, 2];
let mut transfers = vec![
DapTransfer::read(PortType::AccessPort, 4),
DapTransfer::read(PortType::AccessPort, 4),
];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_read_response(DapAcknowledge::Ok, ap_read_values[0]);
mock.add_read_response(DapAcknowledge::Ok, ap_read_values[1]);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
assert_eq!(transfers[0].status, TransferStatus::Ok);
assert_eq!(transfers[0].value, ap_read_values[0]);
assert_eq!(transfers[1].status, TransferStatus::Ok);
assert_eq!(transfers[1].value, ap_read_values[1]);
}
#[test]
fn multiple_dp_read() {
let dp_read_values = [1, 2];
let mut transfers = vec![
DapTransfer::read(PortType::DebugPort, 4),
DapTransfer::read(PortType::DebugPort, 4),
];
let mut mock = MockJaylink::new();
mock.add_read_response(DapAcknowledge::Ok, dp_read_values[0]);
mock.add_read_response(DapAcknowledge::Ok, dp_read_values[1]);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
assert_eq!(transfers[0].status, TransferStatus::Ok);
assert_eq!(transfers[0].value, dp_read_values[0]);
assert_eq!(transfers[1].status, TransferStatus::Ok);
assert_eq!(transfers[1].value, dp_read_values[1]);
}
#[test]
fn single_dp_register_write() {
let mut transfers = vec![DapTransfer::write(PortType::DebugPort, 0, 0x1234_5678)];
let mut mock = MockJaylink::new();
mock.add_write_response(
DapAcknowledge::Ok,
mock.swd_settings.num_idle_cycles_between_writes,
);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
let transfer_result = &transfers[0];
assert_eq!(transfer_result.status, TransferStatus::Ok);
}
#[test]
fn single_ap_register_write() {
let mut transfers = vec![DapTransfer::write(PortType::AccessPort, 0, 0x1234_5678)];
let mut mock = MockJaylink::new();
mock.add_write_response(
DapAcknowledge::Ok,
mock.swd_settings.num_idle_cycles_between_writes,
);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_before_write_verify);
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
let transfer_result = &transfers[0];
assert_eq!(transfer_result.status, TransferStatus::Ok);
}
#[test]
fn multiple_ap_register_write() {
let mut transfers = vec![
DapTransfer::write(PortType::AccessPort, 0, 0x1234_5678),
DapTransfer::write(PortType::AccessPort, 0, 0xABABABAB),
];
let mut mock = MockJaylink::new();
mock.add_write_response(
DapAcknowledge::Ok,
mock.swd_settings.num_idle_cycles_between_writes,
);
mock.add_write_response(
DapAcknowledge::Ok,
mock.swd_settings.num_idle_cycles_between_writes,
);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_before_write_verify);
mock.add_read_response(DapAcknowledge::Ok, 0);
mock.add_idle_cycles(mock.swd_settings.idle_cycles_after_transfer);
perform_transfers(&mut mock, &mut transfers).expect("Failed to perform transfer");
assert_eq!(transfers[0].status, TransferStatus::Ok);
assert_eq!(transfers[1].status, TransferStatus::Ok);
}
}
}