Skip to content

Commit 7d36867

Browse files
authored
Merge pull request #4 from Grapple-Systems/add-swd-sequence
Add support for SWD sequence
2 parents 48286e4 + a958131 commit 7d36867

File tree

7 files changed

+336
-4
lines changed

7 files changed

+336
-4
lines changed

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "dap-rs"
3-
version = "0.1.0"
3+
version = "0.2.0"
44
edition = "2021"
55
authors = [
66
"Emil Fresk <[email protected]>",
@@ -20,6 +20,9 @@ embedded-hal = "1.0.0"
2020
replace_with = { version = "0.1.7", default-features = false, features = ["panic_abort"] }
2121
bitflags = "1.3.2"
2222

23+
[dev-dependencies]
24+
mockall = "0.13.0"
25+
2326
[dependencies.defmt]
2427
optional = true
2528
version = "0.3"

src/dap.rs

Lines changed: 241 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,8 +374,46 @@ where
374374
}
375375
}
376376

377-
fn process_swd_sequence(&self, _req: Request, _resp: &mut ResponseWriter) {
378-
// TODO: Needs implementing
377+
fn process_swd_sequence(&mut self, mut req: Request, resp: &mut ResponseWriter) {
378+
self.state.to_swd();
379+
let swd = match &mut self.state {
380+
State::Swd(swd) => swd,
381+
_ => {
382+
resp.write_err();
383+
return;
384+
}
385+
};
386+
387+
resp.write_ok(); // assume ok until we finish
388+
let sequence_count = req.next_u8();
389+
let success = (0..sequence_count).map(|_| {
390+
// parse the seqnence info
391+
let sequence_info = req.next_u8();
392+
let nbits: usize = match sequence_info & 0x3F {
393+
// CMSIS-DAP says 0 means 64 bits
394+
0 => 64,
395+
// Other integers are normal.
396+
n => n as usize,
397+
};
398+
let nbytes = (nbits + 7) / 8;
399+
let output = (sequence_info & 0x80) == 0;
400+
401+
if output {
402+
let output_data = req.data.get(..nbytes).ok_or(())?;
403+
swd.write_sequence(nbits, output_data).or(Err(()))?;
404+
req.consume(nbytes);
405+
Ok(0)
406+
} else {
407+
let input_data = resp.remaining().get_mut(..nbytes).ok_or(())?;
408+
swd.read_sequence(nbits, input_data).or(Err(()))?;
409+
resp.skip(nbytes);
410+
Ok(nbytes)
411+
}
412+
}).all(|r: Result<usize, ()>| r.is_ok());
413+
414+
if !success {
415+
resp.write_u8_at(0, 0xFF);
416+
}
379417
}
380418

381419
fn process_swo_transport(&mut self, mut req: Request, resp: &mut ResponseWriter) {
@@ -792,3 +830,204 @@ impl<T> CheckResult<T> for swd::Result<T> {
792830
}
793831
}
794832
}
833+
834+
#[cfg(test)]
835+
mod test {
836+
use super::*;
837+
use crate::mock_device::*;
838+
use mockall::predicate::*;
839+
840+
struct FakeLEDs {}
841+
impl DapLeds for FakeLEDs {
842+
fn react_to_host_status(&mut self, _host_status: HostStatus) {}
843+
}
844+
845+
struct StdDelayUs {}
846+
impl DelayNs for StdDelayUs {
847+
fn delay_ns(&mut self, ns: u32) {
848+
std::thread::sleep(std::time::Duration::from_nanos(ns as u64));
849+
}
850+
}
851+
852+
type TestDap<'a> = Dap<
853+
'a,
854+
MockSwdJtagDevice,
855+
FakeLEDs,
856+
StdDelayUs,
857+
MockSwdJtagDevice,
858+
MockSwdJtagDevice,
859+
swo::MockSwo,
860+
>;
861+
862+
#[test]
863+
fn test_swd_output_reset() {
864+
let mut dap = TestDap::new(
865+
MockSwdJtagDevice::new(),
866+
FakeLEDs {},
867+
StdDelayUs {},
868+
None,
869+
"test_dap",
870+
);
871+
872+
let report = [0x1Du8, 1, 52, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0];
873+
let mut rbuf = [0u8; 64];
874+
dap.state.to_swd();
875+
match &mut dap.state {
876+
State::Swd(swd) => {
877+
swd.expect_write_sequence()
878+
.once()
879+
.with(eq(52), eq([0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0]))
880+
.return_const(Ok(()));
881+
}
882+
_ => assert!(false, "can't switch to swd"),
883+
}
884+
let rsize = dap.process_command(&report, &mut rbuf, DapVersion::V2);
885+
assert_eq!(rsize, 2);
886+
assert_eq!(&rbuf[..2], &[0x1Du8, 0x00])
887+
}
888+
889+
#[test]
890+
fn test_swd_output_max_size() {
891+
let mut dap = TestDap::new(
892+
MockSwdJtagDevice::new(),
893+
FakeLEDs {},
894+
StdDelayUs {},
895+
None,
896+
"test_dap",
897+
);
898+
899+
let report = [0x1Du8, 1, 0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00];
900+
let mut rbuf = [0u8; 64];
901+
dap.state.to_swd();
902+
match &mut dap.state {
903+
State::Swd(swd) => {
904+
swd.expect_write_sequence()
905+
.once()
906+
.with(
907+
eq(64),
908+
eq([0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00]),
909+
)
910+
.return_const(Ok(()));
911+
}
912+
_ => assert!(false, "can't switch to swd"),
913+
}
914+
let rsize = dap.process_command(&report, &mut rbuf, DapVersion::V2);
915+
assert_eq!(rsize, 2);
916+
assert_eq!(&rbuf[..2], &[0x1Du8, 0x00])
917+
}
918+
919+
#[test]
920+
fn test_swd_input() {
921+
let mut dap = TestDap::new(
922+
MockSwdJtagDevice::new(),
923+
FakeLEDs {},
924+
StdDelayUs {},
925+
None,
926+
"test_dap",
927+
);
928+
929+
let report = [0x1Du8, 1, 0x80 | 52];
930+
let mut rbuf = [0u8; 64];
931+
dap.state.to_swd();
932+
match &mut dap.state {
933+
State::Swd(swd) => {
934+
swd.expect_read_sequence()
935+
.once()
936+
.withf(|nbits, buf| buf.len() >= 7 && *nbits == 52)
937+
.returning(|_, buf| {
938+
buf[..7].clone_from_slice(&[0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0]);
939+
Ok(())
940+
});
941+
}
942+
_ => assert!(false, "can't switch to swd"),
943+
}
944+
let rsize = dap.process_command(&report, &mut rbuf, DapVersion::V2);
945+
assert_eq!(rsize, 9);
946+
assert_eq!(
947+
&rbuf[..9],
948+
&[0x1Du8, 0x00, 0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0]
949+
)
950+
}
951+
952+
#[test]
953+
fn test_swd_input_max_size() {
954+
let mut dap = TestDap::new(
955+
MockSwdJtagDevice::new(),
956+
FakeLEDs {},
957+
StdDelayUs {},
958+
None,
959+
"test_dap",
960+
);
961+
962+
let report = [0x1Du8, 1, 0x80];
963+
let mut rbuf = [0u8; 64];
964+
dap.state.to_swd();
965+
match &mut dap.state {
966+
State::Swd(swd) => {
967+
swd.expect_read_sequence()
968+
.once()
969+
.withf(|nbits, buf| buf.len() >= 8 && *nbits == 64)
970+
.returning(|_, buf| {
971+
buf[..8]
972+
.clone_from_slice(&[0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00]);
973+
Ok(())
974+
});
975+
}
976+
_ => assert!(false, "can't switch to swd"),
977+
}
978+
let rsize = dap.process_command(&report, &mut rbuf, DapVersion::V2);
979+
assert_eq!(rsize, 10);
980+
assert_eq!(
981+
&rbuf[..10],
982+
&[0x1Du8, 0x00, 0xFFu8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00]
983+
)
984+
}
985+
986+
#[test]
987+
fn test_target_select() {
988+
let mut dap = TestDap::new(
989+
MockSwdJtagDevice::new(),
990+
FakeLEDs {},
991+
StdDelayUs {},
992+
None,
993+
"test_dap",
994+
);
995+
996+
// write 8 bits, read 5 bits, write 33 bits
997+
let report = [0x1Du8, 3, 8, 0b10111101, 0x80 | 5, 33, 0x56, 0x83, 0xAB, 0x32, 0x01];
998+
let mut rbuf = [0u8; 64];
999+
dap.state.to_swd();
1000+
match &mut dap.state {
1001+
State::Swd(swd) => {
1002+
swd.expect_write_sequence()
1003+
.once()
1004+
.with(
1005+
eq(8),
1006+
eq([0b10111101u8]),
1007+
)
1008+
.return_const(Ok(()));
1009+
swd.expect_read_sequence()
1010+
.once()
1011+
.withf(|nbits, buf| buf.len() >= 1 && *nbits == 5)
1012+
.returning(|_, buf| {
1013+
buf[0] = 0x1F;
1014+
Ok(())
1015+
});
1016+
swd.expect_write_sequence()
1017+
.once()
1018+
.with(
1019+
eq(33),
1020+
eq([0x56, 0x83, 0xAB, 0x32, 0x01]),
1021+
)
1022+
.return_const(Ok(()));
1023+
}
1024+
_ => assert!(false, "can't switch to swd"),
1025+
}
1026+
let rsize = dap.process_command(&report, &mut rbuf, DapVersion::V2);
1027+
assert_eq!(rsize, 3);
1028+
assert_eq!(
1029+
&rbuf[..3],
1030+
&[0x1Du8, 0x00, 0x1F]
1031+
)
1032+
}
1033+
}

src/dap/request.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ impl<'a> Request<'a> {
3333
value
3434
}
3535

36+
pub fn consume(&mut self, count: usize) {
37+
self.data = &self.data[count..];
38+
}
39+
3640
pub fn rest(self) -> &'a [u8] {
3741
&self.data
3842
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! TODO: Crate docs
22
3-
#![no_std]
3+
#![cfg_attr(not(test), no_std)]
44
#![warn(missing_docs)]
55

66
/// TODO: Dap docs
@@ -11,6 +11,9 @@ pub mod swj;
1111
pub mod swo;
1212
pub mod usb;
1313

14+
#[cfg(test)]
15+
mod mock_device;
16+
1417
// Re-export the usb-device crate, so that crates depending on us can use it without
1518
// having to track it as a separate dependency.
1619
pub use usb_device;

src/mock_device.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use crate::{jtag, swd, swj};
2+
3+
#[mockall::automock]
4+
pub trait SwdJtagDevice {
5+
// swj
6+
fn high_impedance_mode(&mut self);
7+
fn process_swj_clock(&mut self, max_frequency: u32) -> bool;
8+
fn process_swj_pins(&mut self, output: swj::Pins, mask: swj::Pins, wait_us: u32) -> swj::Pins;
9+
fn process_swj_sequence(&mut self, data: &[u8], nbits: usize);
10+
11+
// swd
12+
fn read_inner(&mut self, apndp: swd::APnDP, a: swd::DPRegister) -> swd::Result<u32>;
13+
fn write_inner(&mut self, apndp: swd::APnDP, a: swd::DPRegister, data: u32) -> swd::Result<()>;
14+
fn read_sequence(&mut self, num_bits: usize, data: &mut [u8]) -> swd::Result<()>;
15+
fn write_sequence(&mut self, num_bits: usize, data: &[u8]) -> swd::Result<()>;
16+
17+
// jtag
18+
fn sequences(&mut self, data: &[u8], rxbuf: &mut [u8]) -> u32;
19+
20+
// swd/jtag
21+
fn set_clock(&mut self, max_frequency: u32) -> bool;
22+
}
23+
24+
impl swj::Dependencies<Self, Self> for MockSwdJtagDevice {
25+
fn high_impedance_mode(&mut self) {
26+
SwdJtagDevice::high_impedance_mode(self)
27+
}
28+
29+
fn process_swj_clock(&mut self, max_frequency: u32) -> bool {
30+
SwdJtagDevice::process_swj_clock(self, max_frequency)
31+
}
32+
33+
fn process_swj_pins(&mut self, output: swj::Pins, mask: swj::Pins, wait_us: u32) -> swj::Pins {
34+
SwdJtagDevice::process_swj_pins(self, output, mask, wait_us)
35+
}
36+
37+
fn process_swj_sequence(&mut self, data: &[u8], nbits: usize) {
38+
SwdJtagDevice::process_swj_sequence(self, data, nbits)
39+
}
40+
}
41+
42+
impl swd::Swd<Self> for MockSwdJtagDevice {
43+
const AVAILABLE: bool = true;
44+
45+
fn set_clock(&mut self, max_frequency: u32) -> bool {
46+
SwdJtagDevice::set_clock(self, max_frequency)
47+
}
48+
49+
fn read_inner(&mut self, apndp: swd::APnDP, a: swd::DPRegister) -> swd::Result<u32> {
50+
SwdJtagDevice::read_inner(self, apndp, a)
51+
}
52+
53+
fn write_inner(&mut self, apndp: swd::APnDP, a: swd::DPRegister, data: u32) -> swd::Result<()> {
54+
SwdJtagDevice::write_inner(self, apndp, a, data)
55+
}
56+
57+
fn read_sequence(&mut self, num_bits: usize, data: &mut [u8]) -> swd::Result<()> {
58+
SwdJtagDevice::read_sequence(self, num_bits, data)
59+
}
60+
61+
fn write_sequence(&mut self, num_bits: usize, data: &[u8]) -> swd::Result<()> {
62+
SwdJtagDevice::write_sequence(self, num_bits, data)
63+
}
64+
}
65+
66+
impl jtag::Jtag<MockSwdJtagDevice> for MockSwdJtagDevice {
67+
const AVAILABLE: bool = true;
68+
69+
fn set_clock(&mut self, max_frequency: u32) -> bool {
70+
SwdJtagDevice::set_clock(self, max_frequency)
71+
}
72+
73+
fn sequences(&mut self, data: &[u8], rxbuf: &mut [u8]) -> u32 {
74+
SwdJtagDevice::sequences(self, data, rxbuf)
75+
}
76+
}

src/swd.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ pub trait Swd<DEPS>: From<DEPS> {
152152

153153
/// Set the maximum clock frequency, return `true` if it is valid.
154154
fn set_clock(&mut self, max_frequency: u32) -> bool;
155+
156+
/// Write a sequence of bits using SWDIO and the clock line running at the configured freq.
157+
fn write_sequence(&mut self, num_bits: usize, data: &[u8]) -> Result<()>;
158+
159+
/// Read a sequence of bits using SWDIO and the clock line running at the configured freq.
160+
fn read_sequence(&mut self, num_bits: usize, data: &mut [u8]) -> Result<()>;
155161
}
156162

157163
/// Helper used by `Swd::read_inner` and `Swd::write_inner` to make the request byte.

src/swo.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub struct SwoStatus {
4242
pub bytes_available: u32,
4343
}
4444

45+
#[cfg_attr(test, mockall::automock)]
4546
pub trait Swo {
4647
fn set_transport(&mut self, transport: SwoTransport);
4748
fn set_mode(&mut self, mode: SwoMode);

0 commit comments

Comments
 (0)