Repository: probe-rs/probe-rs
Pull request #3261: SWD: Encompass direction and value of IO sequence into single type 
Author: login: Cooler1989
Created: 2025-04-26T14:33:05Z | Merged: 2025-04-28T07:18:35Z

The change introduces new type for representing data IO sequence values and replaces bool. Only data i.e. SWD-IO and JTAG TDI signals are replaced with new type.

Advantages:

- Binds both logical meanings i.e. Direction and Value into single type

- Encompasses the bus logic sequence of the direction and value into single enum.

- Allows to avoid passing both vectors and thus dismissing all the validation of the lengths before using the sequence

- encourages to produce valid input from the source by more bounds i.e. input does not have value

- more control over conversion: i.e. one may use .into()/.try_into() instead of ‘as’

Disadvantages:

- some extra conversion needed, on a plus side those are simple and should not introduce more performance overhead


Diff:
diff --git a/changelog/changed-swd-probe-interface-sequence-type.md b/changelog/changed-swd-probe-interface-sequence-type.md
new file mode 100644
index 0000000000..b22708e496
--- /dev/null
+++ b/changelog/changed-swd-probe-interface-sequence-type.md
@@ -0,0 +1 @@
+Changed SWD protocol probe interface with a type to allow passing single stream of enums instead of two streams of bits.
diff --git a/probe-rs/src/probe/arm_debug_interface.rs b/probe-rs/src/probe/arm_debug_interface.rs
index 3d1d79c504..d05385e208 100644
--- a/probe-rs/src/probe/arm_debug_interface.rs
+++ b/probe-rs/src/probe/arm_debug_interface.rs
@@ -365,7 +365,7 @@ fn perform_swd_transfers<P: RawProtocolIo>(
         io_sequence.extend(&transfer.io_sequence());
     }
 
-    let result = probe.swd_io(io_sequence.direction_bits(), io_sequence.io_bits())?;
+    let result = probe.swd_io(io_sequence.io_items())?;
 
     let mut result_bits = &result[..];
 
@@ -826,32 +826,47 @@ enum TransferStatus {
     Failed(DapError),
 }
 
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum IoSequenceItem {
+    Output(bool),
+    Input,
+}
+
+impl From<IoSequenceItem> for bool {
+    fn from(item: IoSequenceItem) -> Self {
+        match item {
+            IoSequenceItem::Output(b) => b,
+            IoSequenceItem::Input => panic!("Input type is not supposed to hold a value!"),
+        }
+    }
+}
+
+impl From<IoSequenceItem> for u8 {
+    fn from(value: IoSequenceItem) -> Self {
+        match value {
+            IoSequenceItem::Output(b) => b as u8,
+            IoSequenceItem::Input => panic!("Input type is not supposed to hold a value!"),
+        }
+    }
+}
+
 struct IoSequence {
-    io: Vec<bool>,
-    direction: Vec<bool>,
+    io: Vec<IoSequenceItem>,
 }
 
 impl IoSequence {
-    const INPUT: bool = false;
-    const OUTPUT: bool = true;
-
     fn new() -> Self {
-        IoSequence {
-            io: vec![],
-            direction: vec![],
-        }
+        IoSequence { io: 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 {
@@ -871,13 +886,11 @@ impl IoSequence {
     }
 
     fn add_output(&mut self, bit: bool) {
-        self.io.push(bit);
-        self.direction.push(Self::OUTPUT);
+        self.io.push(IoSequenceItem::Output(bit));
     }
 
     fn add_input(&mut self) {
-        self.io.push(false);
-        self.direction.push(Self::INPUT);
+        self.io.push(IoSequenceItem::Input);
     }
 
     fn add_input_sequence(&mut self, length: usize) {
@@ -886,17 +899,12 @@ impl IoSequence {
         }
     }
 
-    fn io_bits(&self) -> impl Iterator<Item = bool> + '_ {
+    fn io_items(&self) -> impl Iterator<Item = IoSequenceItem> + '_ {
         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);
     }
 }
 
@@ -1037,10 +1045,9 @@ pub trait RawProtocolIo {
     where
         I: IntoIterator<Item = bool>;
 
-    fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
+    fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>;
+        S: IntoIterator<Item = IoSequenceItem>;
 
     fn swj_pins(
         &mut self,
@@ -1278,10 +1285,10 @@ fn send_sequence<P: RawProtocolIo + JTAGAccess>(
         WireProtocol::Jtag => {
             // Swj sequences should be shifted out to tms, since that is the pin
             // shared between swd and jtag modes.
-            probe.jtag_shift_tms(sequence.io_bits(), false)?;
+            probe.jtag_shift_tms(sequence.io_items().map(|i| i.into()), false)?;
         }
         WireProtocol::Swd => {
-            probe.swd_io(sequence.direction_bits(), sequence.io_bits())?;
+            probe.swd_io(sequence.io_items())?;
         }
     }
 
@@ -1300,7 +1307,7 @@ mod test {
     };
 
     use super::{
-        JTAG_ABORT_IR_VALUE, JTAG_ACCESS_PORT_IR_VALUE, JTAG_DEBUG_PORT_IR_VALUE,
+        IoSequenceItem, JTAG_ABORT_IR_VALUE, JTAG_ACCESS_PORT_IR_VALUE, JTAG_DEBUG_PORT_IR_VALUE,
         JTAG_DR_BIT_LENGTH, JTAG_STATUS_OK, JTAG_STATUS_WAIT, ProbeStatistics, RawProtocolIo,
         SwdSettings,
     };
@@ -1326,8 +1333,7 @@ mod test {
 
     #[derive(Debug)]
     struct MockJaylink {
-        direction_input: Option<Vec<bool>>,
-        io_input: Option<Vec<bool>>,
+        io_input: Option<Vec<IoSequenceItem>>,
         transfer_responses: Vec<Vec<bool>>,
         jtag_transactions: Vec<ExpectedJtagTransaction>,
 
@@ -1345,7 +1351,6 @@ mod test {
     impl MockJaylink {
         fn new() -> Self {
             Self {
-                direction_input: None,
                 io_input: None,
                 transfer_responses: vec![vec![]],
                 jtag_transactions: vec![],
@@ -1584,19 +1589,12 @@ mod test {
             Ok(())
         }
 
-        fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
+        fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
         where
-            D: IntoIterator<Item = bool>,
-            S: IntoIterator<Item = bool>,
+            S: IntoIterator<Item = IoSequenceItem>,
         {
-            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();
diff --git a/probe-rs/src/probe/blackmagic/mod.rs b/probe-rs/src/probe/blackmagic/mod.rs
index 9511155c64..381b1f026c 100644
--- a/probe-rs/src/probe/blackmagic/mod.rs
+++ b/probe-rs/src/probe/blackmagic/mod.rs
@@ -25,7 +25,7 @@ use serialport::{SerialPortType, available_ports};
 
 use super::{
     DebugProbeError, ProbeCreationError, ProbeError, WireProtocol,
-    arm_debug_interface::{ProbeStatistics, RawProtocolIo, SwdSettings},
+    arm_debug_interface::{IoSequenceItem, ProbeStatistics, RawProtocolIo, SwdSettings},
     common::{JtagDriverState, RawJtagIo},
 };
 
@@ -599,6 +599,15 @@ impl From<bool> for SwdDirection {
     }
 }
 
+impl From<IoSequenceItem> for SwdDirection {
+    fn from(value: IoSequenceItem) -> Self {
+        match value {
+            IoSequenceItem::Input => SwdDirection::Input,
+            IoSequenceItem::Output(_) => SwdDirection::Output,
+        }
+    }
+}
+
 /// A Black Magic Probe.
 pub struct BlackMagicProbe {
     reader: BufReader<Box<dyn Read + Send>>,
@@ -908,24 +917,19 @@ impl BlackMagicProbe {
     ///
     /// The caller needs to ensure that the given iterators are not longer than the maximum transfer size
     /// allowed. It seems that the maximum transfer size is determined by [`self.max_mem_block_size`].
-    fn perform_swdio_transfer<D, S>(
-        &mut self,
-        dir: D,
-        swdio: S,
-    ) -> Result<Vec<bool>, DebugProbeError>
+    #[allow(clippy::unnecessary_fallible_conversions)] //  IoSequenceItem conversion may panic
+    fn perform_swdio_transfer<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>,
+        S: IntoIterator<Item = IoSequenceItem>,
     {
-        let dir = dir.into_iter();
-        let swdio = swdio.into_iter();
+        let swdio_sequence = swdio.into_iter();
         let mut output = vec![];
 
         let mut accumulator = 0u32;
         let mut accumulator_length = 0;
 
-        for (dir, swdio) in dir.zip(swdio) {
-            let dir = SwdDirection::from(dir);
+        for swdio in swdio_sequence {
+            let dir: SwdDirection = swdio.into();
             if dir != self.swd_direction
                 || accumulator_length >= core::mem::size_of_val(&accumulator) * 8
             {
@@ -950,7 +954,11 @@ impl BlackMagicProbe {
                 accumulator_length = 0;
             }
             self.swd_direction = dir;
-            accumulator |= if swdio { 1 << accumulator_length } else { 0 };
+            accumulator |= if swdio.try_into().unwrap_or(false) {
+                1 << accumulator_length
+            } else {
+                0
+            };
             accumulator_length += 1;
         }
 
@@ -1273,13 +1281,12 @@ impl RawProtocolIo for BlackMagicProbe {
         Ok(())
     }
 
-    fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
+    fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>,
+        S: IntoIterator<Item = IoSequenceItem>,
     {
         self.probe_statistics.report_io();
-        self.perform_swdio_transfer(dir, swdio)
+        self.perform_swdio_transfer(swdio)
     }
 
     fn swj_pins(
diff --git a/probe-rs/src/probe/ftdi/mod.rs b/probe-rs/src/probe/ftdi/mod.rs
index 9c86872e10..112dce57d4 100644
--- a/probe-rs/src/probe/ftdi/mod.rs
+++ b/probe-rs/src/probe/ftdi/mod.rs
@@ -13,7 +13,7 @@ use crate::{
     probe::{
         DebugProbe, DebugProbeError, DebugProbeInfo, DebugProbeSelector, JTAGAccess,
         ProbeCreationError, ProbeFactory, ScanChainElement, WireProtocol,
-        arm_debug_interface::{ProbeStatistics, RawProtocolIo, SwdSettings},
+        arm_debug_interface::{IoSequenceItem, ProbeStatistics, RawProtocolIo, SwdSettings},
         common::{JtagDriverState, RawJtagIo},
     },
 };
@@ -457,10 +457,9 @@ impl RawProtocolIo for FtdiProbe {
         Ok(())
     }
 
-    fn swd_io<D, S>(&mut self, _dir: D, _swdio: S) -> Result<Vec<bool>, DebugProbeError>
+    fn swd_io<S>(&mut self, _swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>,
+        S: IntoIterator<Item = IoSequenceItem>,
     {
         Err(DebugProbeError::NotImplemented {
             function_name: "swd_io",
diff --git a/probe-rs/src/probe/jlink/bits.rs b/probe-rs/src/probe/jlink/bits.rs
index a3f7a96a13..2754435efe 100644
--- a/probe-rs/src/probe/jlink/bits.rs
+++ b/probe-rs/src/probe/jlink/bits.rs
@@ -65,7 +65,11 @@ pub(crate) trait IteratorExt: Sized {
     fn collapse_bytes(self) -> ByteIter<Self>;
 }
 
-impl<I: Iterator<Item = bool>> IteratorExt for I {
+impl<I, T> IteratorExt for I
+where
+    I: Iterator<Item = T>,
+    T: Into<bool>,
+{
     fn collapse_bytes(self) -> ByteIter<Self> {
         ByteIter { inner: self }
     }
@@ -75,7 +79,11 @@ pub(crate) struct ByteIter<I> {
     inner: I,
 }
 
-impl<I: Iterator<Item = bool>> Iterator for ByteIter<I> {
+impl<I, T> Iterator for ByteIter<I>
+where
+    I: Iterator<Item = T>,
+    T: Into<bool>,
+{
     type Item = u8;
 
     fn next(&mut self) -> Option<u8> {
@@ -84,7 +92,7 @@ impl<I: Iterator<Item = bool>> Iterator for ByteIter<I> {
         let mut has_data = false;
         for (pos, bit) in self.inner.by_ref().take(8).enumerate() {
             has_data = true;
-            byte |= (bit as u8) << pos;
+            byte |= (bit.into() as u8) << pos;
         }
 
         has_data.then_some(byte)
diff --git a/probe-rs/src/probe/jlink/mod.rs b/probe-rs/src/probe/jlink/mod.rs
index e439a6a873..6a30d8481a 100644
--- a/probe-rs/src/probe/jlink/mod.rs
+++ b/probe-rs/src/probe/jlink/mod.rs
@@ -48,7 +48,7 @@ use crate::{
     probe::{
         DebugProbe, DebugProbeError, DebugProbeInfo, DebugProbeSelector, ProbeFactory,
         WireProtocol,
-        arm_debug_interface::{ProbeStatistics, RawProtocolIo, SwdSettings},
+        arm_debug_interface::{IoSequenceItem, ProbeStatistics, RawProtocolIo, SwdSettings},
     },
 };
 
@@ -791,10 +791,9 @@ impl JLink {
     ///
     /// The caller needs to ensure that the given iterators are not longer than the maximum transfer size
     /// allowed. It seems that the maximum transfer size is determined by [`JLink::max_mem_block_size`].
-    fn perform_swdio_transfer<D, S>(&self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
+    fn perform_swdio_transfer<S>(&self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>,
+        S: IntoIterator<Item = IoSequenceItem>,
     {
         self.require_interface_selected(Interface::Swd)?;
 
@@ -803,30 +802,42 @@ impl JLink {
         let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
         let max_bits = std::cmp::min(max_bits, 65535);
 
-        let dir_chunks = dir.into_iter().chunks(max_bits);
         let swdio_chunks = swdio.into_iter().chunks(max_bits);
 
-        #[allow(clippy::useless_conversion)]
-        let chunks = dir_chunks.into_iter().zip(swdio_chunks.into_iter());
-
         let mut output = Vec::new();
         let mut buf = Vec::with_capacity(self.max_mem_block_size);
         buf.resize(COMMAND_OVERHEAD, 0);
 
-        for (dir, swdio) in chunks {
+        for swdio in swdio_chunks.into_iter() {
             buf.truncate(COMMAND_OVERHEAD);
 
-            let mut dir_bit_count = 0;
-            buf.extend(dir.inspect(|_| dir_bit_count += 1).collapse_bytes());
-            let mut swdio_bit_count = 0;
-            buf.extend(swdio.inspect(|_| swdio_bit_count += 1).collapse_bytes());
+            const INPUT: bool = false;
+            const OUTPUT: bool = true;
+
+            let swdio: Vec<_> = swdio.collect();
 
-            assert_eq!(
-                dir_bit_count, swdio_bit_count,
-                "`dir` and `swdio` must have the same number of bits"
+            //  Extend using the direction bits:
+            buf.extend(
+                swdio
+                    .iter()
+                    .map(|item| match item {
+                        IoSequenceItem::Input => INPUT,
+                        IoSequenceItem::Output { .. } => OUTPUT,
+                    })
+                    .collapse_bytes(),
+            );
+            //  Extend using the value bits:
+            buf.extend(
+                swdio
+                    .iter()
+                    .map(|item| match item {
+                        IoSequenceItem::Input => false,
+                        IoSequenceItem::Output(x) => *x,
+                    })
+                    .collapse_bytes(),
             );
 
-            let num_bits = dir_bit_count as u16;
+            let num_bits = swdio.len() as u16;
 
             buf[0] = Command::HwJtag3 as u8;
             // buf[1] is dummy data for alignment
@@ -1206,13 +1217,12 @@ impl RawProtocolIo for JLink {
         Ok(())
     }
 
-    fn swd_io<D, S>(&mut self, dir: D, swdio: S) -> Result<Vec<bool>, DebugProbeError>
+    fn swd_io<S>(&mut self, swdio: S) -> Result<Vec<bool>, DebugProbeError>
     where
-        D: IntoIterator<Item = bool>,
-        S: IntoIterator<Item = bool>,
+        S: IntoIterator<Item = IoSequenceItem>,
     {
         self.probe_statistics.report_io();
-        self.perform_swdio_transfer(dir, swdio)
+        self.perform_swdio_transfer(swdio)
     }
 
     fn swj_pins(


Reviews:
user: login: bugadani
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T14:36:44Z
user: login: Cooler1989
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T14:46:22Z
user: login: bugadani
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T17:34:23Z
user: login: KamilDuljas
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-26T17:39:34Z
user: login: Cooler1989
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-26T21:20:51Z
user: login: Cooler1989
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-27T10:04:46Z
user: login: bugadani
body: Thanks
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T07:12:43Z

Review comments:
diff_hunk: @@ -383,7 +383,7 @@ pub struct JLink {
     speed_khz: u32,
 
     jtag_tms_bits: Vec<bool>,
-    jtag_tdi_bits: Vec<bool>,
+    jtag_tdi_bits: Vec<IoSequenceItem>,
path: probe-rs/src/probe/jlink/mod.rs
user: login: bugadani
body: What does an "Input" value mean for TDI, exactly?
created_at: 2025-04-26T14:36:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -383,7 +383,7 @@ pub struct JLink {
     speed_khz: u32,
 
     jtag_tms_bits: Vec<bool>,
-    jtag_tdi_bits: Vec<bool>,
+    jtag_tdi_bits: Vec<IoSequenceItem>,
path: probe-rs/src/probe/jlink/mod.rs
user: login: Cooler1989
body: That is a good question. Maybe this type is universally where it should not be used.
The change in general is to set up some ground for more work related to SWD protocol. I will try to improve it in regards to JTAG: possibly first by ignoring the in the first step the JTAG protocol.

created_at: 2025-04-26T14:46:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -803,30 +802,19 @@ impl JLink {
         let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
         let max_bits = std::cmp::min(max_bits, 65535);
 
-        let dir_chunks = dir.into_iter().chunks(max_bits);
         let swdio_chunks = swdio.into_iter().chunks(max_bits);
 
-        #[allow(clippy::useless_conversion)]
-        let chunks = dir_chunks.into_iter().zip(swdio_chunks.into_iter());
-
         let mut output = Vec::new();
         let mut buf = Vec::with_capacity(self.max_mem_block_size);
         buf.resize(COMMAND_OVERHEAD, 0);
 
-        for (dir, swdio) in chunks {
+        for swdio in swdio_chunks.into_iter() {
path: probe-rs/src/probe/jlink/mod.rs
user: login: bugadani
body: Just by reading the change, it's unclear to me what is happening. `swdio` is a chunk of `IoSequenceItem`s, but then it's treated as a slice of u8s? bools?. I think something around this may not happen exactly as expected.
created_at: 2025-04-26T17:34:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -826,32 +826,47 @@ enum TransferStatus {
     Failed(DapError),
 }
 
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum IoSequenceItem {
+    Output(bool),
+    Input,
+}
+
+impl From<IoSequenceItem> for bool {
+    fn from(item: IoSequenceItem) -> Self {
+        match item {
+            IoSequenceItem::Output(b) => b,
+            IoSequenceItem::Input => panic!("Input type is not suppose to hold a value!"),
path: probe-rs/src/probe/arm_debug_interface.rs
user: login: KamilDuljas
body: This sentence should be corrected because "be supposed to" expresses an expectation, and the correct form is "is supposed to", not "is suppose to."
created_at: 2025-04-26T17:39:26Z
author_association: NONE
diff_hunk: @@ -803,30 +802,19 @@ impl JLink {
         let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
         let max_bits = std::cmp::min(max_bits, 65535);
 
-        let dir_chunks = dir.into_iter().chunks(max_bits);
         let swdio_chunks = swdio.into_iter().chunks(max_bits);
 
-        #[allow(clippy::useless_conversion)]
-        let chunks = dir_chunks.into_iter().zip(swdio_chunks.into_iter());
-
         let mut output = Vec::new();
         let mut buf = Vec::with_capacity(self.max_mem_block_size);
         buf.resize(COMMAND_OVERHEAD, 0);
 
-        for (dir, swdio) in chunks {
+        for swdio in swdio_chunks.into_iter() {
path: probe-rs/src/probe/jlink/mod.rs
user: login: Cooler1989
body: Nothing has changed in this division comparing to master. Here, it is still just dividing data into chunks. Not type conversion happens here. 

The problem is few lines below where I forgot to adjust my solution and replace this line: 
`buf.extend(dir.inspect(|_| dir_bit_count += 1).collapse_bytes());`

This part of implementation is not written to express clearly the intention as one would whish for. I don't know the J-LInk details so I got lost easily. Maybe something to improve for the future. 

I will try to fix it tomorrow. Thanks @bugadani for the support. Good that tests caught that. I was able to reproduce the failure in my case by grabbing some j-link and trying to connect some STM board.

created_at: 2025-04-26T21:20:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -803,30 +802,19 @@ impl JLink {
         let max_bits = (self.max_mem_block_size - COMMAND_OVERHEAD) / 2 * 8;
         let max_bits = std::cmp::min(max_bits, 65535);
 
-        let dir_chunks = dir.into_iter().chunks(max_bits);
         let swdio_chunks = swdio.into_iter().chunks(max_bits);
 
-        #[allow(clippy::useless_conversion)]
-        let chunks = dir_chunks.into_iter().zip(swdio_chunks.into_iter());
-
         let mut output = Vec::new();
         let mut buf = Vec::with_capacity(self.max_mem_block_size);
         buf.resize(COMMAND_OVERHEAD, 0);
 
-        for (dir, swdio) in chunks {
+        for swdio in swdio_chunks.into_iter() {
path: probe-rs/src/probe/jlink/mod.rs
user: login: Cooler1989
body: I just recreated (dir,bit) iterators as it was used before my change.
created_at: 2025-04-27T10:04:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: Cooler1989
created_at: 2025-04-26T15:59:14Z
body: @bugadani, I simplified the change by limiting it to only SWD for now. Can you check it out now?
author_association: CONTRIBUTOR
user: login: bugadani
created_at: 2025-04-26T16:16:24Z
body: Better, thanks. The test failures look real, though.
author_association: CONTRIBUTOR
user: login: Cooler1989
created_at: 2025-04-26T16:40:18Z
body: > Better, thanks. The test failures look real, though.

Tbh, I am not familiar with the test failures cases, so it may take me a while. 
I saw something about the USB. I don't think I touched anything related to USB, but I will try to inspect. If you have some advice or go-to references I would appreciate the support for a newbie :) 
author_association: CONTRIBUTOR
user: login: bugadani
created_at: 2025-04-26T16:42:56Z
body: My guess is something like "the device did not get a complete command, so probe-rs times out waiting for a response that will never arrive".
author_association: CONTRIBUTOR
user: login: bugadani
created_at: 2025-04-26T16:55:51Z
body: Unfortunately, this issue is also sticky in the case of one of the STM32F051R8Tx devices. I'll restart it occasionally if it dies, but don't be surprised if you manage to figure out the other 2 devices but not that one.
author_association: CONTRIBUTOR
user: login: bugadani
created_at: 2025-04-27T10:23:39Z
body: Thanks! As this is technically a breaking change, please add a changelog entry to the changelog folder, then we can get this merged.
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-04-27T15:28:50Z
body: The following changelog fragments do not match the expected pattern:
 - changelog/change-swd-probe-interface-sequence-type.md

Files should start with one of the categories followed by a dash, and end with '.md'
For example: 'added-foo-bar.md'

Valid categories are:
 - added
 - changed
 - fixed
 - removed

author_association: CONTRIBUTOR
user: login: Cooler1989
created_at: 2025-04-28T06:17:53Z
body: > Thanks! As this is technically a breaking change, please add a changelog entry to the changelog folder, then we can get this merged.

Thanks @bugadani, 
I added the changelog part.
author_association: CONTRIBUTOR