Repository: probe-rs/probe-rs
Pull request #3031 (2025-01-18 12:44:17)

chore: Fix features for insta, update cargo-dist to 0.28.0
- Update colored to v3
- Use correct features for insta
- Update cargo-dist to version 0.28.0

probe-rs-perfbot[bot]: # Benchmarks

https://perfbot.drop.huesser.dev/run/12844101486

No benchmarks were found. Check your CI workflows.
    

Patch:
diff --git a/.github/workflows/v-release.yml b/.github/workflows/v-release.yml
index 0f1d198a..92f41c47 100644
--- a/.github/workflows/v-release.yml
+++ b/.github/workflows/v-release.yml
@@ -65,7 +65,7 @@ jobs:
         # we specify bash to get pipefail; it guards against the `curl` command
         # failing. otherwise `sh` won't catch that `curl` returned non-0
         shell: bash
-        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.26.1/cargo-dist-installer.sh | sh"
+        run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.28.0/cargo-dist-installer.sh | sh"
       - name: Cache dist
         uses: actions/upload-artifact@v4
         with:
diff --git a/Cargo.lock b/Cargo.lock
index b79ebd6e..afbdbe90 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -547,7 +547,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
 dependencies = [
  "lazy_static",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "colored"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e"
+dependencies = [
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -810,7 +819,7 @@ dependencies = [
  "alterable_logger",
  "anyhow",
  "byteorder",
- "colored",
+ "colored 2.2.0",
  "defmt-json-schema",
  "defmt-parser",
  "dissimilar",
@@ -2553,7 +2562,7 @@ dependencies = [
  "cargo_metadata",
  "clap",
  "clap_complete",
- "colored",
+ "colored 3.0.0",
  "defmt-decoder",
  "directories",
  "docsplay",
@@ -3369,7 +3378,7 @@ name = "smoke_tester"
 version = "0.26.0"
 dependencies = [
  "clap",
- "colored",
+ "colored 3.0.0",
  "linkme",
  "log",
  "miette",
@@ -3553,7 +3562,7 @@ dependencies = [
  "anyhow",
  "clap",
  "cmsis-pack",
- "colored",
+ "colored 3.0.0",
  "futures",
  "goblin",
  "insta",
@@ -4320,7 +4329,7 @@ version = "0.1.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
diff --git a/changelog/changed-make-ArmMemoryInterface-more-generic.md b/changelog/changed-make-ArmMemoryInterface-more-generic.md
new file mode 100644
index 00000000..dfb7cbe8
--- /dev/null
+++ b/changelog/changed-make-ArmMemoryInterface-more-generic.md
@@ -0,0 +1,1 @@
+Changed all functions in the `ArmMemoryInterface` trait that return something involving the `ArmCommunicationInterface` type and replace them with calls that return generic traits. Adapted all functions that relied on the old calls to use the new calls instead.
diff --git a/changelog/fixed-black-magic-probe.md b/changelog/fixed-black-magic-probe.md
new file mode 100644
index 00000000..dd3a4f23
--- /dev/null
+++ b/changelog/fixed-black-magic-probe.md
@@ -0,0 +1,1 @@
+The Black Magic Probe stopped working when #2972 was merged, because it added a call to `try_as_parts()` that only worked for `ArmCommunicationInterface` probes and not for probes where the communications interface is handled by the probe itself. Replace this call with one to the new function `generic_status()`. This also fixes the issue reported in #2977.
diff --git a/dist-workspace.toml b/dist-workspace.toml
index e0af9226..cdff43c3 100644
--- a/dist-workspace.toml
+++ b/dist-workspace.toml
@@ -4,7 +4,7 @@ members = ["cargo:."]
 # Config for 'dist'
 [dist]
 # The preferred dist version to use in CI (Cargo.toml SemVer syntax)
-cargo-dist-version = "0.26.1"
+cargo-dist-version = "0.28.0"
 # CI backends to support
 ci = "github"
 # Target platforms to build apps for (Rust target-triple syntax)
diff --git a/probe-rs-debug/Cargo.toml b/probe-rs-debug/Cargo.toml
index e031d37d..1fc9a3b3 100644
--- a/probe-rs-debug/Cargo.toml
+++ b/probe-rs-debug/Cargo.toml
@@ -28,7 +28,7 @@ typed-path = "0.10.0"
 workspace = true
 
 [dev-dependencies]
-insta = "1.41.1"
+insta = { version = "1.41.1", features = ["yaml"] }
 pretty_assertions = "1.4.1"
 probe-rs = { workspace = true, features = ["test"] }
 test-case = "3.3.1"
diff --git a/probe-rs-tools/Cargo.toml b/probe-rs-tools/Cargo.toml
index fcda00b8..557596b0 100644
--- a/probe-rs-tools/Cargo.toml
+++ b/probe-rs-tools/Cargo.toml
@@ -35,7 +35,7 @@ bytesize = "1"
 capstone = "0.12"
 cargo_metadata = "0.19"
 clap = { version = "4", features = ["derive", "env"] }
-colored = "2"
+colored = "3"
 defmt-decoder = "0.4"
 directories = "5"
 dunce = "1"
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/info.rs b/probe-rs-tools/src/bin/probe-rs/cmd/info.rs
index 1f1000d8..cf35c5ba 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/info.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/info.rs
@@ -5,7 +5,7 @@ use jep106::JEP106Code;
 use probe_rs::{
     architecture::{
         arm::{
-            ap::{ApClass, MemoryApType},
+            ap::ApClass,
             armv6m::Demcr,
             component::Scs,
             dp::{Ctrl, DebugPortId, DebugPortVersion, MinDpSupport, DLPIDR, DPIDR, TARGETID},
@@ -351,8 +351,7 @@ fn handle_memory_ap(
         let mut memory = interface.memory_interface(access_port)?;
 
         // Check if the AP is accessible
-        let (interface, ap) = memory.try_as_parts()?;
-        let csw = ap.generic_status(interface)?;
+        let csw = memory.generic_status()?;
         if !csw.DeviceEn {
             return Ok(Tree::new(
                 "Memory AP is not accessible, DeviceEn bit not set".to_string(),
diff --git a/probe-rs/Cargo.toml b/probe-rs/Cargo.toml
index 76622d9d..04a81a91 100644
--- a/probe-rs/Cargo.toml
+++ b/probe-rs/Cargo.toml
@@ -93,7 +93,7 @@ pretty_assertions = "1"
 test-case = "3"
 test-log = { version = "0.2.16", features = ["trace"] }
 termtree = "0.5"
-insta = { version = "1.38", default-features = false, features = ["yaml"] }
+insta = { version = "1.38", default-features = false }
 
 [[package.metadata.release.pre-release-replacements]]
 file = "../CHANGELOG.md"
diff --git a/probe-rs/src/architecture/arm/communication_interface.rs b/probe-rs/src/architecture/arm/communication_interface.rs
index 0955b2a0..66c4fdea 100644
--- a/probe-rs/src/architecture/arm/communication_interface.rs
+++ b/probe-rs/src/architecture/arm/communication_interface.rs
@@ -69,6 +69,20 @@ impl RegisterParseError {
 
 /// To be implemented by debug probe drivers that support debugging ARM cores.
 pub trait ArmProbeInterface: DapAccess + SwdSequence + SwoAccess + Send {
+    /// Reinitialize the communication interface (in place).
+    ///
+    /// Some chip-specific reset sequences may disable the debug port. `reinitialize` allows
+    /// a debug sequence to re-initialize the debug port, staying true to the `Initialized`
+    /// type state.
+    ///
+    /// If you're invoking this from a debug sequence, know that `reinitialize` will likely
+    /// call back onto you! Specifically, it will invoke some sequence of `debug_port_*`
+    /// sequences with varying internal state. If you're not prepared for this, you might recurse.
+    ///
+    /// `reinitialize` does handle `debug_core_start` to re-initialize any core's debugging.
+    /// If you're a chip-specific debug sequence, you're expected to handle this yourself.
+    fn reinitialize(&mut self) -> Result<(), ArmError>;
+
     /// Returns a vector of all the access ports the current debug port has.
     ///
     /// If the target device has multiple debug ports, this will switch the active debug port
@@ -248,7 +262,7 @@ impl<S: ArmDebugState> Drop for ArmCommunicationInterface<S> {
 }
 
 impl<S: ArmDebugState> ArmCommunicationInterface<S> {
-    fn probe_mut(&mut self) -> &mut dyn DapProbe {
+    pub(crate) fn probe_mut(&mut self) -> &mut dyn DapProbe {
         // Unwrap: Probe is only taken when the struct is dropped
         self.probe.as_deref_mut().expect("ArmCommunicationInterface is in an inconsistent state. This is a bug, please report it.")
     }
@@ -269,6 +283,30 @@ impl<S: ArmDebugState> ArmCommunicationInterface<S> {
 pub trait DapProbe: RawDapAccess + DebugProbe {}
 
 impl ArmProbeInterface for ArmCommunicationInterface<Initialized> {
+    fn reinitialize(&mut self) -> Result<(), ArmError> {
+        // Simulate the drop / close of the initialized communication interface.
+        let mut probe = self.probe.take().expect("ArmCommunicationInterface is in an inconsistent state. This is a bug, please report it.");
+        self.state.disconnect(&mut *probe);
+
+        match Self::try_setup(
+            probe,
+            self.state.sequence.clone(),
+            self.current_debug_port(),
+            self.state.use_overrun_detect,
+        ) {
+            Ok(reinitialized) => {
+                let _ = std::mem::replace(self, reinitialized);
+                // Dropping the original self. Since we've taken the probe, we've ensured
+                // that the drop effects don't happen again.
+                Ok(())
+            }
+            Err((probe, err)) => {
+                self.probe.replace(probe);
+                Err(err)
+            }
+        }
+    }
+
     fn memory_interface(
         &mut self,
         access_port_address: &FullyQualifiedApAddress,
@@ -374,42 +412,6 @@ impl<'interface> ArmCommunicationInterface<Initialized> {
         Ok(initializing)
     }
 
-    /// Reinitialize the communication interface (in place).
-    ///
-    /// Some chip-specific reset sequences may disable the debug port. `reinitialize` allows
-    /// a debug sequence to re-initialize the debug port, staying true to the `Initialized`
-    /// type state.
-    ///
-    /// If you're invoking this from a debug sequence, know that `reinitialize` will call back
-    /// onto you! Specifically, it will invoke some sequence of `debug_port_*` sequences with
-    /// varying internal state. If you're not prepared for this, you might recurse.
-    ///
-    /// `reinitialize` does handle `debug_core_start` to re-initialize any core's debugging.
-    /// If you're a chip-specific debug sequence, you're expected to handle this yourself.
-    pub(crate) fn reinitialize(&mut self) -> Result<(), ArmError> {
-        // Simulate the drop / close of the initialized communication interface.
-        let mut probe = self.probe.take().expect("ArmCommunicationInterface is in an inconsistent state. This is a bug, please report it.");
-        self.state.disconnect(&mut *probe);
-
-        match Self::try_setup(
-            probe,
-            self.state.sequence.clone(),
-            self.current_debug_port(),
-            self.state.use_overrun_detect,
-        ) {
-            Ok(reinitialized) => {
-                let _ = std::mem::replace(self, reinitialized);
-                // Dropping the original self. Since we've taken the probe, we've ensured
-                // that the drop effects don't happen again.
-                Ok(())
-            }
-            Err((probe, err)) => {
-                self.probe.replace(probe);
-                Err(err)
-            }
-        }
-    }
-
     /// Inform the probe of the [`CoreStatus`] of the chip attached to the probe.
     pub fn core_status_notification(&mut self, state: CoreStatus) {
         self.probe_mut().core_status_notification(state).ok();
@@ -707,6 +709,10 @@ impl DapAccess for ArmCommunicationInterface<Initialized> {
             .raw_write_block(PortType::AccessPort, address, values)?;
         Ok(())
     }
+
+    fn flush(&mut self) -> Result<(), ArmError> {
+        self.probe_mut().raw_flush()
+    }
 }
 
 /// Information about the chip target we are currently attached to.
diff --git a/probe-rs/src/architecture/arm/core/armv7a.rs b/probe-rs/src/architecture/arm/core/armv7a.rs
index 8c7edd3e..77865a33 100644
--- a/probe-rs/src/architecture/arm/core/armv7a.rs
+++ b/probe-rs/src/architecture/arm/core/armv7a.rs
@@ -1155,33 +1155,34 @@ mod test {
     }
 
     impl ArmMemoryInterface for MockProbe {
-        fn update_core_status(&mut self, _: CoreStatus) {}
+        fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError> {
+            Err(DebugProbeError::NotImplemented {
+                function_name: "get_swd_sequence",
+            })
+        }
 
-        fn get_arm_communication_interface(
+        fn get_arm_probe_interface(
             &mut self,
-        ) -> Result<
-            &mut crate::architecture::arm::ArmCommunicationInterface<
-                crate::architecture::arm::communication_interface::Initialized,
-            >,
-            DebugProbeError,
-        > {
+        ) -> Result<&mut dyn crate::architecture::arm::ArmProbeInterface, DebugProbeError> {
             Err(DebugProbeError::NotImplemented {
-                function_name: "get_arm_communication_interface",
+                function_name: "get_arm_probe_interface",
             })
         }
 
-        fn try_as_parts(
+        fn get_dap_access(
             &mut self,
-        ) -> Result<
-            (
-                &mut crate::architecture::arm::ArmCommunicationInterface<
-                    crate::architecture::arm::communication_interface::Initialized,
-                >,
-                &mut MemoryAp,
-            ),
-            DebugProbeError,
-        > {
-            todo!()
+        ) -> Result<&mut dyn crate::architecture::arm::DapAccess, DebugProbeError> {
+            Err(DebugProbeError::NotImplemented {
+                function_name: "get_dap_access",
+            })
+        }
+
+        fn generic_status(
+            &mut self,
+        ) -> Result<crate::architecture::arm::ap::memory_ap::registers::CSW, ArmError> {
+            Err(ArmError::Probe(DebugProbeError::NotImplemented {
+                function_name: "generic_status",
+            }))
         }
 
         fn ap(&mut self) -> &mut MemoryAp {
diff --git a/probe-rs/src/architecture/arm/core/armv8a.rs b/probe-rs/src/architecture/arm/core/armv8a.rs
index a11adbe8..5a232a06 100644
--- a/probe-rs/src/architecture/arm/core/armv8a.rs
+++ b/probe-rs/src/architecture/arm/core/armv8a.rs
@@ -1833,37 +1833,38 @@ mod test {
     }
 
     impl ArmMemoryInterface for MockProbe {
-        fn update_core_status(&mut self, _: CoreStatus) {}
-
         fn ap(&mut self) -> &mut MemoryAp {
             todo!()
         }
 
-        fn get_arm_communication_interface(
+        fn get_arm_probe_interface(
             &mut self,
-        ) -> Result<
-            &mut crate::architecture::arm::ArmCommunicationInterface<
-                crate::architecture::arm::communication_interface::Initialized,
-            >,
-            DebugProbeError,
-        > {
+        ) -> Result<&mut dyn crate::architecture::arm::ArmProbeInterface, DebugProbeError> {
+            Err(DebugProbeError::NotImplemented {
+                function_name: "get_arm_probe_interface",
+            })
+        }
+
+        fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError> {
             Err(DebugProbeError::NotImplemented {
-                function_name: "get_arm_communication_interface",
+                function_name: "get_swd_sequence",
             })
         }
 
-        fn try_as_parts(
+        fn get_dap_access(
             &mut self,
-        ) -> Result<
-            (
-                &mut crate::architecture::arm::ArmCommunicationInterface<
-                    crate::architecture::arm::communication_interface::Initialized,
-                >,
-                &mut MemoryAp,
-            ),
-            DebugProbeError,
-        > {
-            todo!()
+        ) -> Result<&mut dyn crate::architecture::arm::DapAccess, DebugProbeError> {
+            Err(DebugProbeError::NotImplemented {
+                function_name: "get_dap_access",
+            })
+        }
+
+        fn generic_status(
+            &mut self,
+        ) -> Result<crate::architecture::arm::ap::memory_ap::registers::CSW, ArmError> {
+            Err(ArmError::Probe(DebugProbeError::NotImplemented {
+                function_name: "generic_status",
+            }))
         }
 
         fn base_address(&mut self) -> Result<u64, ArmError> {
diff --git a/probe-rs/src/architecture/arm/memory/adi_v5_memory_interface.rs b/probe-rs/src/architecture/arm/memory/adi_v5_memory_interface.rs
index 54bbdbd4..64e10208 100644
--- a/probe-rs/src/architecture/arm/memory/adi_v5_memory_interface.rs
+++ b/probe-rs/src/architecture/arm/memory/adi_v5_memory_interface.rs
@@ -3,16 +3,16 @@ use std::any::Any;
 use crate::{
     architecture::arm::{
         ap::{
-            memory_ap::{DataSize, MemoryAp, MemoryApType},
+            memory_ap::{registers, DataSize, MemoryAp, MemoryApType},
             ApAccess,
         },
         communication_interface::{FlushableArmAccess, Initialized},
         dp::DpAccess,
         memory::ArmMemoryInterface,
-        ArmCommunicationInterface, ArmError, DapAccess, FullyQualifiedApAddress,
+        ArmCommunicationInterface, ArmError, ArmProbeInterface, DapAccess, FullyQualifiedApAddress,
     },
     probe::DebugProbeError,
-    MemoryInterface,
+    CoreStatus, MemoryInterface,
 };
 
 /// Calculate the maximum number of bytes we can write starting at address
@@ -512,7 +512,7 @@ where
 
 impl<APA> ArmMemoryInterface for ADIMemoryInterface<'_, APA>
 where
-    APA: std::any::Any + FlushableArmAccess + ApAccess + DpAccess,
+    APA: std::any::Any + FlushableArmAccess + ApAccess + DpAccess + ArmProbeInterface,
 {
     fn base_address(&mut self) -> Result<u64, ArmError> {
         self.memory_ap.base_address(self.interface)
@@ -523,25 +523,48 @@ where
         &mut self.memory_ap
     }
 
-    fn get_arm_communication_interface(
+    fn get_swd_sequence(
         &mut self,
-    ) -> Result<&mut ArmCommunicationInterface<Initialized>, DebugProbeError> {
-        (self.interface as &mut dyn Any)
-            .downcast_mut::<ArmCommunicationInterface<Initialized>>()
-            .ok_or(DebugProbeError::Other(
-                "Not an ArmCommunicationInterface".to_string(),
-            ))
+    ) -> Result<
+        &mut dyn crate::architecture::arm::communication_interface::SwdSequence,
+        DebugProbeError,
+    > {
+        Ok(self.interface)
     }
 
-    fn try_as_parts(
+    fn get_arm_probe_interface(
         &mut self,
-    ) -> Result<(&mut ArmCommunicationInterface<Initialized>, &mut MemoryAp), DebugProbeError> {
-        (self.interface as &mut dyn Any)
+    ) -> Result<&mut dyn crate::architecture::arm::ArmProbeInterface, DebugProbeError> {
+        Ok(self.interface)
+    }
+
+    fn get_dap_access(&mut self) -> Result<&mut dyn DapAccess, DebugProbeError> {
+        Ok(self.interface)
+    }
+
+    fn generic_status(&mut self) -> Result<registers::CSW, ArmError> {
+        // TODO: This assumes that the base type is `ArmCommunicationInterface`,
+        // which will fail if something else implements `ADIMemoryInterface`.
+        let Some(iface) = (self.interface as &mut dyn Any)
             .downcast_mut::<ArmCommunicationInterface<Initialized>>()
-            .ok_or(DebugProbeError::Other(
+        else {
+            return Err(ArmError::Probe(DebugProbeError::Other(
                 "Not an ArmCommunicationInterface".to_string(),
-            ))
-            .map(|iface| (iface, &mut self.memory_ap))
+            )));
+        };
+        self.memory_ap.generic_status(iface)
+    }
+
+    fn update_core_status(&mut self, state: CoreStatus) {
+        // TODO: This assumes that the base type is `ArmCommunicationInterface`,
+        // which will fail if something else implements `ADIMemoryInterface`.
+        let Some(iface) = (self.interface as &mut dyn Any)
+            .downcast_mut::<ArmCommunicationInterface<Initialized>>()
+        else {
+            return;
+        };
+
+        iface.probe_mut().core_status_notification(state).ok();
     }
 }
 
diff --git a/probe-rs/src/architecture/arm/memory/mod.rs b/probe-rs/src/architecture/arm/memory/mod.rs
index 4e581a81..c58d55f2 100644
--- a/probe-rs/src/architecture/arm/memory/mod.rs
+++ b/probe-rs/src/architecture/arm/memory/mod.rs
@@ -6,8 +6,9 @@ pub mod romtable;
 use crate::{memory::MemoryInterface, probe::DebugProbeError, CoreStatus};
 
 use super::{
-    ap::memory_ap::MemoryAp, communication_interface::Initialized, ArmCommunicationInterface,
-    ArmError,
+    ap::memory_ap::{registers, MemoryAp},
+    communication_interface::SwdSequence,
+    ArmError, ArmProbeInterface, DapAccess,
 };
 pub use romtable::{Component, ComponentId, CoresightComponent, PeripheralType};
 
@@ -19,27 +20,24 @@ pub trait ArmMemoryInterface: ArmMemoryInterfaceShim {
     /// The underlying memory AP’s base address.
     fn base_address(&mut self) -> Result<u64, ArmError>;
 
-    /// The underlying `ArmCommunicationInterface` if this is an `ArmCommunicationInterface`.
-    fn get_arm_communication_interface(
-        &mut self,
-    ) -> Result<&mut ArmCommunicationInterface<Initialized>, DebugProbeError>;
+    /// Get this interface as a SwdSequence object.
+    fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError>;
 
-    /// The underlying `ArmCommunicationInterface` and memory AP if the probe interface is an
-    /// `ArmCommunicationInterface`.
-    fn try_as_parts(
-        &mut self,
-    ) -> Result<(&mut ArmCommunicationInterface<Initialized>, &mut MemoryAp), DebugProbeError>;
+    /// Get this interface as a [`ArmProbeInterface`] object.
+    fn get_arm_probe_interface(&mut self) -> Result<&mut dyn ArmProbeInterface, DebugProbeError>;
+
+    /// Get this interface as a [`DapAccess`] object.
+    fn get_dap_access(&mut self) -> Result<&mut dyn DapAccess, DebugProbeError>;
+
+    /// Get the current value of the CSW reflected in this probe.
+    fn generic_status(&mut self) -> Result<registers::CSW, ArmError>;
 
     /// Inform the probe of the [`CoreStatus`] of the chip/core attached to
     /// the probe.
     //
     // NOTE: this function should be infallible as it is usually only
     // a visual indication.
-    fn update_core_status(&mut self, state: CoreStatus) {
-        self.get_arm_communication_interface()
-            .map(|iface| iface.core_status_notification(state))
-            .ok();
-    }
+    fn update_core_status(&mut self, _state: CoreStatus) {}
 }
 
 /// Implementation detail to allow trait upcasting-like behaviour.
diff --git a/probe-rs/src/architecture/arm/traits.rs b/probe-rs/src/architecture/arm/traits.rs
index 41dc467f..993a2569 100644
--- a/probe-rs/src/architecture/arm/traits.rs
+++ b/probe-rs/src/architecture/arm/traits.rs
@@ -322,4 +322,14 @@ pub trait DapAccess {
         }
         Ok(())
     }
+
+    /// Flush any outstanding operations.
+    ///
+    /// For performance, debug probe implementations may choose to batch writes;
+    /// to assure that any such batched writes have in fact been issued, `flush`
+    /// can be called.  Takes no arguments, but may return failure if a batched
+    /// operation fails.
+    fn flush(&mut self) -> Result<(), ArmError> {
+        Ok(())
+    }
 }
diff --git a/probe-rs/src/probe/blackmagic/arm.rs b/probe-rs/src/probe/blackmagic/arm.rs
index 942a36d4..31a5a730 100644
--- a/probe-rs/src/probe/blackmagic/arm.rs
+++ b/probe-rs/src/probe/blackmagic/arm.rs
@@ -1,15 +1,14 @@
 use crate::architecture::arm::ap::memory_ap::registers::{AddressIncrement, CSW};
 use crate::architecture::arm::ap::memory_ap::{DataSize, MemoryAp, MemoryApType};
 use crate::architecture::arm::ap::valid_access_ports;
-use crate::architecture::arm::communication_interface::{Initialized, SwdSequence};
+use crate::architecture::arm::communication_interface::SwdSequence;
 use crate::architecture::arm::dp::{Abort, Ctrl, DebugPortError, DpAccess, Select};
 use crate::architecture::arm::memory::ArmMemoryInterface;
 use crate::architecture::arm::{
     communication_interface::UninitializedArmProbe, sequences::ArmDebugSequence, ArmProbeInterface,
 };
 use crate::architecture::arm::{
-    ArmCommunicationInterface, ArmError, DapAccess, DpAddress, FullyQualifiedApAddress,
-    RawDapAccess, SwoAccess,
+    ArmError, DapAccess, DpAddress, FullyQualifiedApAddress, RawDapAccess, SwoAccess,
 };
 use crate::probe::blackmagic::{Align, BlackMagicProbe, ProtocolVersion, RemoteCommand};
 use crate::probe::{DebugProbeError, Probe};
@@ -323,6 +322,10 @@ impl ArmProbeInterface for BlackMagicProbeArmDebug {
             csw: csw.into(),
         }) as _)
     }
+
+    fn reinitialize(&mut self) -> Result<(), ArmError> {
+        Ok(())
+    }
 }
 
 impl SwoAccess for BlackMagicProbeArmDebug {
@@ -540,20 +543,22 @@ impl ArmMemoryInterface for BlackMagicProbeMemoryInterface<'_> {
         self.current_ap.base_address(self.probe)
     }
 
-    fn get_arm_communication_interface(
-        &mut self,
-    ) -> Result<&mut ArmCommunicationInterface<Initialized>, DebugProbeError> {
-        Err(DebugProbeError::InterfaceNotAvailable {
-            interface_name: "ARM",
-        })
+    fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError> {
+        Ok(self.probe)
+    }
+
+    fn get_arm_probe_interface(&mut self) -> Result<&mut dyn ArmProbeInterface, DebugProbeError> {
+        Ok(self.probe)
+    }
+
+    fn get_dap_access(&mut self) -> Result<&mut dyn DapAccess, DebugProbeError> {
+        Ok(self.probe)
     }
 
-    fn try_as_parts(
+    fn generic_status(
         &mut self,
-    ) -> Result<(&mut ArmCommunicationInterface<Initialized>, &mut MemoryAp), DebugProbeError> {
-        Err(DebugProbeError::InterfaceNotAvailable {
-            interface_name: "ARM",
-        })
+    ) -> Result<crate::architecture::arm::ap::memory_ap::registers::CSW, ArmError> {
+        CSW::try_from(self.csw).map_err(|e| ArmError::DebugPort(DebugPortError::RegisterParse(e)))
     }
 }
 
diff --git a/probe-rs/src/probe/blackmagic/mod.rs b/probe-rs/src/probe/blackmagic/mod.rs
index 5967faab..fd93e7c4 100644
--- a/probe-rs/src/probe/blackmagic/mod.rs
+++ b/probe-rs/src/probe/blackmagic/mod.rs
@@ -1042,10 +1042,17 @@ impl DebugProbe for BlackMagicProbe {
     }
 
     fn scan_chain(&self) -> Result<&[ScanChainElement], DebugProbeError> {
-        if let Some(scan_chain) = &self.jtag_state.expected_scan_chain {
-            Ok(scan_chain)
-        } else {
-            Ok(&[])
+        match self.active_protocol() {
+            Some(WireProtocol::Jtag) => {
+                if let Some(ref chain) = self.jtag_state.expected_scan_chain {
+                    Ok(chain.as_slice())
+                } else {
+                    Ok(&[])
+                }
+            }
+            _ => Err(DebugProbeError::InterfaceNotAvailable {
+                interface_name: "JTAG",
+            }),
         }
     }
 
diff --git a/probe-rs/src/probe/fake_probe.rs b/probe-rs/src/probe/fake_probe.rs
index 983ef142..acf5fd46 100644
--- a/probe-rs/src/probe/fake_probe.rs
+++ b/probe-rs/src/probe/fake_probe.rs
@@ -274,24 +274,21 @@ impl ArmMemoryInterface for &mut MockCore {
         todo!()
     }
 
-    fn get_arm_communication_interface(
-        &mut self,
-    ) -> Result<
-        &mut crate::architecture::arm::ArmCommunicationInterface<Initialized>,
-        DebugProbeError,
-    > {
+    fn get_arm_probe_interface(&mut self) -> Result<&mut dyn ArmProbeInterface, DebugProbeError> {
+        todo!()
+    }
+
+    fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError> {
+        todo!()
+    }
+
+    fn get_dap_access(&mut self) -> Result<&mut dyn DapAccess, DebugProbeError> {
         todo!()
     }
 
-    fn try_as_parts(
+    fn generic_status(
         &mut self,
-    ) -> Result<
-        (
-            &mut crate::architecture::arm::ArmCommunicationInterface<Initialized>,
-            &mut MemoryAp,
-        ),
-        DebugProbeError,
-    > {
+    ) -> Result<crate::architecture::arm::ap::memory_ap::registers::CSW, ArmError> {
         todo!()
     }
 
@@ -697,6 +694,10 @@ impl ArmProbeInterface for FakeArmInterface<Initialized> {
     fn current_debug_port(&self) -> DpAddress {
         self.state.current_dp
     }
+
+    fn reinitialize(&mut self) -> Result<(), ArmError> {
+        Ok(())
+    }
 }
 
 impl SwoAccess for FakeArmInterface<Initialized> {
diff --git a/probe-rs/src/probe/stlink/mod.rs b/probe-rs/src/probe/stlink/mod.rs
index f6288ea5..8a39419a 100644
--- a/probe-rs/src/probe/stlink/mod.rs
+++ b/probe-rs/src/probe/stlink/mod.rs
@@ -10,9 +10,7 @@ use crate::{
             memory_ap::{MemoryAp, MemoryApType},
             valid_access_ports, AccessPortType,
         },
-        communication_interface::{
-            ArmProbeInterface, Initialized, SwdSequence, UninitializedArmProbe,
-        },
+        communication_interface::{ArmProbeInterface, SwdSequence, UninitializedArmProbe},
         memory::ArmMemoryInterface,
         sequences::ArmDebugSequence,
         valid_32bit_arm_address, ArmError, DapAccess, DpAddress, FullyQualifiedApAddress, Pins,
@@ -1454,6 +1452,10 @@ impl ArmProbeInterface for StlinkArmDebug {
         // SWD multidrop is not supported on ST-Link
         DpAddress::Default
     }
+
+    fn reinitialize(&mut self) -> Result<(), ArmError> {
+        Ok(())
+    }
 }
 
 impl SwdSequence for StlinkArmDebug {
@@ -1789,29 +1791,24 @@ impl ArmMemoryInterface for StLinkMemoryInterface<'_> {
         &mut self.current_ap
     }
 
-    fn get_arm_communication_interface(
-        &mut self,
-    ) -> Result<
-        &mut crate::architecture::arm::ArmCommunicationInterface<Initialized>,
-        DebugProbeError,
-    > {
-        Err(DebugProbeError::InterfaceNotAvailable {
-            interface_name: "ARM",
-        })
+    fn get_swd_sequence(&mut self) -> Result<&mut dyn SwdSequence, DebugProbeError> {
+        Ok(self)
+    }
+
+    fn get_arm_probe_interface(&mut self) -> Result<&mut dyn ArmProbeInterface, DebugProbeError> {
+        Ok(self.probe)
     }
 
-    fn try_as_parts(
+    fn get_dap_access(&mut self) -> Result<&mut dyn DapAccess, DebugProbeError> {
+        Ok(self.probe)
+    }
+
+    fn generic_status(
         &mut self,
-    ) -> Result<
-        (
-            &mut crate::architecture::arm::ArmCommunicationInterface<Initialized>,
-            &mut MemoryAp,
-        ),
-        DebugProbeError,
-    > {
-        Err(DebugProbeError::InterfaceNotAvailable {
+    ) -> Result<crate::architecture::arm::ap::memory_ap::registers::CSW, ArmError> {
+        Err(ArmError::Probe(DebugProbeError::InterfaceNotAvailable {
             interface_name: "ARM",
-        })
+        }))
     }
 }
 
diff --git a/probe-rs/src/vendor/microchip/sequences/atsam.rs b/probe-rs/src/vendor/microchip/sequences/atsam.rs
index 34d2f7c8..54dc067b 100644
--- a/probe-rs/src/vendor/microchip/sequences/atsam.rs
+++ b/probe-rs/src/vendor/microchip/sequences/atsam.rs
@@ -311,7 +311,7 @@ impl AtSAM {
             let current_dsu_statusa = DsuStatusA::from(memory.read_word_8(DsuStatusA::ADDRESS)?);
             if current_dsu_statusa.done() {
                 tracing::info!("Chip-Erase complete");
-                let interface = memory.get_arm_communication_interface()?;
+                let interface = memory.get_swd_sequence()?;
 
                 // If the device was in Reset Extension when we started put it back into Reset Extension
                 let result = if dsu_status_a.crstext() {
diff --git a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
index 1c5c8676..c4c8f152 100644
--- a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
+++ b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
@@ -11,7 +11,7 @@ use std::{
 
 use crate::{
     architecture::arm::{
-        ap::{memory_ap::MemoryApType, AccessPortError, AccessPortType},
+        ap::{AccessPortError, AccessPortType},
         armv7m::{FpCtrl, FpRev2CompX},
         core::{
             armv7m::{Aircr, Dhcsr},
@@ -242,9 +242,8 @@ impl MIMXRT117x {
         let mut errors = 0usize;
         let mut disables = 0usize;
 
-        let (interface, memory_ap) = probe.try_as_parts()?;
         loop {
-            match memory_ap.generic_status(interface) {
+            match probe.generic_status() {
                 Ok(csw) if csw.DeviceEn => {
                     tracing::debug!("Device enabled after {}ms with {errors} errors and {disables} invalid statuses", start.elapsed().as_millis());
                     return Ok(());
@@ -458,7 +457,7 @@ impl ArmDebugSequence for MIMXRT117x {
         // The ARM communication interface knows how to re-initialize the debug port.
         // Re-initializing the core(s) is on us.
         let ap = probe.ap().ap_address().clone();
-        let interface = probe.get_arm_communication_interface()?;
+        let interface = probe.get_arm_probe_interface()?;
         interface.reinitialize()?;
 
         assert!(debug_base.is_none());
diff --git a/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs b/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
index 51baf386..1e3d061f 100644
--- a/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
+++ b/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
@@ -9,8 +9,8 @@ use std::{
 
 use crate::{
     architecture::arm::{
-        ap::{memory_ap::MemoryApType, AccessPortError, AccessPortType, ApAccess, GenericAp, IDR},
-        communication_interface::{FlushableArmAccess, Initialized},
+        ap::{AccessPortError, AccessPortType, GenericAp, IDR},
+        communication_interface::{Initialized, Register},
         core::armv8m::{Aircr, Demcr, Dhcsr},
         dp::{Abort, Ctrl, DpAccess, Select, DPIDR},
         memory::ArmMemoryInterface,
@@ -264,10 +264,9 @@ fn wait_for_stop_after_reset(memory: &mut dyn ArmMemoryInterface) -> Result<(),
 
     thread::sleep(Duration::from_millis(10));
 
-    let (interface, memory_ap) = memory.try_as_parts()?;
-    if memory_ap.generic_status(interface)?.DeviceEn {
-        let dp = memory_ap.ap_address().dp();
-        enable_debug_mailbox(interface, dp)?;
+    if memory.generic_status()?.DeviceEn {
+        let dp = memory.ap().ap_address().dp();
+        enable_debug_mailbox(memory.get_dap_access()?, dp)?;
     }
 
     let start = Instant::now();
@@ -304,20 +303,22 @@ fn wait_for_stop_after_reset(memory: &mut dyn ArmMemoryInterface) -> Result<(),
     Ok(())
 }
 
-fn enable_debug_mailbox(
-    interface: &mut ArmCommunicationInterface<Initialized>,
-    dp: DpAddress,
-) -> Result<(), ArmError> {
+fn enable_debug_mailbox(interface: &mut dyn DapAccess, dp: DpAddress) -> Result<(), ArmError> {
     tracing::info!("LPC55xx connect script start");
 
     let ap = FullyQualifiedApAddress::v1_with_dp(dp, 2);
 
-    let status: IDR = interface.read_ap_register(&GenericAp::new(ap.clone()))?;
+    let status: IDR = interface
+        .read_raw_ap_register(GenericAp::new(ap.clone()).ap_address(), IDR::ADDRESS)?
+        .try_into()?;
 
     tracing::info!("APIDR: {:?}", status);
     tracing::info!("APIDR: 0x{:08X}", u32::from(status));
 
-    let status: u32 = interface.read_dp_register::<DPIDR>(dp)?.into();
+    // ADIv5 specification section B4.3.3: "Connection and line reset sequence" states that
+    // in the reset state, reading DPIDR takes the target out of the reset state. Perform
+    // such a read here in order to ensure the core is no longer in reset.
+    let status: u32 = interface.read_raw_dp_register(dp, DPIDR::ADDRESS)?;
 
     tracing::info!("DPIDR: 0x{:08X}", status);
 
@@ -435,13 +436,12 @@ impl MIMXRT5xxS {
         let ap = probe.ap().ap_address().clone();
         let dp = ap.dp();
         let start = Instant::now();
-        while !self.csw_debug_ready(probe.get_arm_communication_interface()?, &ap)?
+        while !self.csw_debug_ready(probe.get_dap_access()?, &ap)?
             && start.elapsed() < Duration::from_millis(300)
         {
             // Wait for either condition
         }
-        let enabled_mailbox =
-            self.enable_debug_mailbox(probe.get_arm_communication_interface()?, dp, &ap)?;
+        let enabled_mailbox = self.enable_debug_mailbox(probe.get_dap_access()?, dp, &ap)?;
 
         // Halt the core in case it didn't stop at a breakpiont.
         tracing::trace!("halting MIMXRT5xxS Cortex-M33 core");
@@ -454,7 +454,7 @@ impl MIMXRT5xxS {
 
         if enabled_mailbox {
             // We'll double-check now to make sure we're in a reasonable state.
-            if !self.csw_debug_ready(probe.get_arm_communication_interface()?, &ap)? {
+            if !self.csw_debug_ready(probe.get_dap_access()?, &ap)? {
                 tracing::warn!("MIMXRT5xxS is still not ready to debug, even after using DebugMailbox to activate session");
             }
         }
@@ -531,7 +531,7 @@ impl MIMXRT5xxS {
 
     fn csw_debug_ready(
         &self,
-        interface: &mut ArmCommunicationInterface<Initialized>,
+        interface: &mut dyn DapAccess,
         ap: &FullyQualifiedApAddress,
     ) -> Result<bool, ArmError> {
         let csw = interface.read_raw_ap_register(ap, 0x00)?;
@@ -547,7 +547,7 @@ impl MIMXRT5xxS {
     /// if it was necessary but unsuccessful.
     fn enable_debug_mailbox(
         &self,
-        interface: &mut ArmCommunicationInterface<Initialized>,
+        interface: &mut dyn DapAccess,
         dp: DpAddress,
         mem_ap: &FullyQualifiedApAddress,
     ) -> Result<bool, ArmError> {
diff --git a/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs b/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
index b5012843..364bff0b 100644
--- a/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
+++ b/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
@@ -363,7 +363,7 @@ impl ArmDebugSequence for CC13xxCC26xx {
 
         // Re-initializing the core(s) is on us.
         let ap = probe.ap().ap_address().clone();
-        let interface = probe.get_arm_communication_interface()?;
+        let interface = probe.get_arm_probe_interface()?;
         interface.reinitialize()?;
 
         assert!(debug_base.is_none());
diff --git a/probe-rs/src/vendor/vorago/sequences/va416xx.rs b/probe-rs/src/vendor/vorago/sequences/va416xx.rs
index 5233f694..9b7f20d9 100644
--- a/probe-rs/src/vendor/vorago/sequences/va416xx.rs
+++ b/probe-rs/src/vendor/vorago/sequences/va416xx.rs
@@ -81,7 +81,7 @@ impl ArmDebugSequence for Va416xx {
         // Re-initializing the core(s) is on us.
         let ap = interface.ap().ap_address().clone();
 
-        let arm_interface = interface.get_arm_communication_interface()?;
+        let arm_interface = interface.get_arm_probe_interface()?;
         const NUM_RETRIES: u32 = 10;
         for i in 0..NUM_RETRIES {
             match arm_interface.reinitialize() {
diff --git a/target-gen/Cargo.toml b/target-gen/Cargo.toml
index 5a97cf8a..debff88f 100644
--- a/target-gen/Cargo.toml
+++ b/target-gen/Cargo.toml
@@ -37,7 +37,7 @@ zip = { version = "2.0.0", default-features = false, features = [
     "zstd",
 ] }
 clap = { version = "4.5", features = ["derive"] }
-colored = "2"
+colored = "3"
 anyhow.workspace = true
 reqwest = { version = "0.12.4", features = [
     "json",


Test patch:
diff --git a/smoke-tester/Cargo.toml b/smoke-tester/Cargo.toml
index bf76f2ba..40bbcdd0 100644
--- a/smoke-tester/Cargo.toml
+++ b/smoke-tester/Cargo.toml
@@ -16,7 +16,7 @@ toml = "0.8.12"
 pretty_env_logger = "0.5.0"
 log = "0.4.21"
 clap = { version = "4.5", features = ["derive"] }
-colored = "2.1.0"
+colored = "3"
 linkme = "0.3.25"
 miette = { version = "7.2.0", features = ["fancy"] }
 thiserror.workspace = true