Repository: probe-rs/probe-rs
Pull request #2707 (2024-07-25 20:21:49)

Cancel in-progress CI jobs


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0b874e7a..318fbfce 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,6 +9,13 @@ on:
   pull_request:
   merge_group:
 
+# Cancel any currently running workflows from the same PR, branch, or
+# tag when a new workflow is triggered.
+#
+# https://stackoverflow.com/a/66336834
+concurrency:
+  cancel-in-progress: true
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
 
 name: Run CI
 
diff --git a/changelog/changed-factorize-MemoryInterface-slight-variations.md b/changelog/changed-factorize-MemoryInterface-slight-variations.md
new file mode 100644
index 00000000..eef95c3b
--- /dev/null
+++ b/changelog/changed-factorize-MemoryInterface-slight-variations.md
@@ -0,0 +1,1 @@
+Factorize the variants of `MemoryInterface` where only the return type changes into `MemoryInterface` with a generic type defaulting to `probe_rs::Error`.
diff --git a/changelog/changed-rename-ArmProbe-to-ArmMemoryInterface.md b/changelog/changed-rename-ArmProbe-to-ArmMemoryInterface.md
new file mode 100644
index 00000000..e954e420
--- /dev/null
+++ b/changelog/changed-rename-ArmProbe-to-ArmMemoryInterface.md
@@ -0,0 +1,1 @@
+Rename ArmProbe to ArmMemoryInterface
diff --git a/changelog/changed-use-FullyQualifiedApAddress-instead-of-MemoryAp-and-GenericAp.md b/changelog/changed-use-FullyQualifiedApAddress-instead-of-MemoryAp-and-GenericAp.md
new file mode 100644
index 00000000..00540606
--- /dev/null
+++ b/changelog/changed-use-FullyQualifiedApAddress-instead-of-MemoryAp-and-GenericAp.md
@@ -0,0 +1,1 @@
+arm: Use FullyQualifiedApAddress instead of MemoryAp and GenericAp in method
diff --git a/changelog/fixed-ApType-names.md b/changelog/fixed-ApType-names.md
new file mode 100644
index 00000000..7112cb6d
--- /dev/null
+++ b/changelog/fixed-ApType-names.md
@@ -0,0 +1,1 @@
+Fixed ApType names & documentation
diff --git a/probe-rs-target/src/chip.rs b/probe-rs-target/src/chip.rs
index 12b54cf2..c0e9588a 100644
--- a/probe-rs-target/src/chip.rs
+++ b/probe-rs-target/src/chip.rs
@@ -16,17 +16,6 @@ pub struct ScanChainElement {
     pub ir_len: Option<u8>,
 }
 
-/// A finite list of all possible binary formats a target might support.
-#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
-#[serde(rename_all = "lowercase")]
-pub enum BinaryFormat {
-    /// Program sections are bit-for-bit copied to flash.
-    #[default]
-    Raw,
-    /// Program sections are copied to flash, with the relevant headers and metadata for the [ESP-IDF bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/app_image_format.html#app-image-structures).
-    Idf,
-}
-
 /// Configuration for JTAG probes.
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 pub struct Jtag {
@@ -89,7 +78,9 @@ pub struct Chip {
     #[serde(default)]
     pub jtag: Option<Jtag>,
     /// The default binary format for this chip
-    pub default_binary_format: Option<BinaryFormat>,
+    // TODO: rename to default_platform
+    #[serde(default)]
+    pub default_binary_format: Option<String>,
 }
 
 impl Chip {
@@ -111,7 +102,7 @@ impl Chip {
             flash_algorithms: vec![],
             rtt_scan_ranges: None,
             jtag: None,
-            default_binary_format: Some(BinaryFormat::Raw),
+            default_binary_format: None,
         }
     }
 }
diff --git a/probe-rs-target/src/lib.rs b/probe-rs-target/src/lib.rs
index cca40435..da84405f 100644
--- a/probe-rs-target/src/lib.rs
+++ b/probe-rs-target/src/lib.rs
@@ -19,8 +19,8 @@ mod memory;
 pub(crate) mod serialize;
 
 pub use chip::{
-    ArmCoreAccessOptions, BinaryFormat, Chip, Core, CoreAccessOptions, Jtag,
-    RiscvCoreAccessOptions, ScanChainElement, XtensaCoreAccessOptions,
+    ArmCoreAccessOptions, Chip, Core, CoreAccessOptions, Jtag, RiscvCoreAccessOptions,
+    ScanChainElement, XtensaCoreAccessOptions,
 };
 pub use chip_family::{
     Architecture, ChipFamily, CoreType, InstructionSet, TargetDescriptionSource,
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 70697c7a..1538f199 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,6 @@ use jep106::JEP106Code;
 use probe_rs::{
     architecture::{
         arm::{
-            ap::{GenericAp, MemoryAp},
             armv6m::Demcr,
             component::Scs,
             dp::{DebugPortId, DebugPortVersion, MinDpSupport, DLPIDR, DPIDR, TARGETID},
@@ -295,22 +294,20 @@ fn show_arm_info(interface: &mut dyn ArmProbeInterface, dp: DpAddress) -> Result
     let num_access_ports = interface.num_access_ports(dp)?;
 
     for ap_index in 0..num_access_ports {
-        let ap = FullyQualifiedApAddress::v1_with_dp(dp, ap_index as u8);
-        let access_port = GenericAp::new(ap);
+        let address = FullyQualifiedApAddress::v1_with_dp(dp, ap_index as u8);
 
-        let ap_information = interface.ap_information(&access_port)?;
+        let ap_information = interface.ap_information(&address)?;
 
         match ap_information {
             ApInformation::MemoryAp(MemoryApInformation {
                 debug_base_address,
-                address,
                 device_enabled,
                 ..
             }) => {
                 let mut ap_nodes = Tree::new(format!("{} MemoryAP", address.ap_v1()?));
 
                 if *device_enabled {
-                    match handle_memory_ap(&access_port.into(), *debug_base_address, interface) {
+                    match handle_memory_ap(&address, *debug_base_address, interface) {
                         Ok(component_tree) => ap_nodes.push(component_tree),
                         Err(e) => ap_nodes.push(format!("Error during access: {e}")),
                     };
@@ -355,7 +352,7 @@ fn show_arm_info(interface: &mut dyn ArmProbeInterface, dp: DpAddress) -> Result
 }
 
 fn handle_memory_ap(
-    access_port: &MemoryAp,
+    access_port: &FullyQualifiedApAddress,
     base_address: u64,
     interface: &mut dyn ArmProbeInterface,
 ) -> Result<Tree<String>, anyhow::Error> {
@@ -374,7 +371,7 @@ fn handle_memory_ap(
 fn coresight_component_tree(
     interface: &mut dyn ArmProbeInterface,
     component: Component,
-    access_port: &MemoryAp,
+    access_port: &FullyQualifiedApAddress,
 ) -> Result<Tree<String>> {
     let tree = match &component {
         Component::GenericVerificationComponent(_) => Tree::new("Generic".to_string()),
@@ -455,7 +452,7 @@ fn process_vendor_rom_tables(
     interface: &mut dyn ArmProbeInterface,
     id: &ComponentId,
     _table: &RomTable,
-    access_port: &MemoryAp,
+    access_port: &FullyQualifiedApAddress,
     tree: &mut Tree<String>,
 ) -> Result<()> {
     let peripheral_id = id.peripheral_id();
@@ -485,7 +482,7 @@ fn process_component_entry(
     interface: &mut dyn ArmProbeInterface,
     peripheral_id: &PeripheralID,
     component: &Component,
-    access_port: &MemoryAp,
+    access_port: &FullyQualifiedApAddress,
 ) -> Result<()> {
     let Some(part) = peripheral_id.determine_part() else {
         return Ok(());
diff --git a/probe-rs-tools/src/bin/probe-rs/main.rs b/probe-rs-tools/src/bin/probe-rs/main.rs
index 9a4d8d11..597dbebb 100644
--- a/probe-rs-tools/src/bin/probe-rs/main.rs
+++ b/probe-rs-tools/src/bin/probe-rs/main.rs
@@ -12,12 +12,11 @@ use anyhow::{Context, Result};
 use clap::Parser;
 use colored::Colorize;
 use itertools::Itertools;
-use probe_rs::flashing::{BinOptions, Format, IdfOptions};
+use probe_rs::flashing::{BinOptions, Format, FormatKind, IdfOptions};
 use probe_rs::{probe::list::Lister, Target};
 use report::Report;
+use serde::Deserialize;
 use serde::Serialize;
-use serde::{de::Error, Deserialize, Deserializer};
-use serde_json::Value;
 use time::{OffsetDateTime, UtcOffset};
 
 use crate::util::logging::setup_logging;
@@ -105,15 +104,26 @@ pub(crate) struct CoreOptions {
     core: usize,
 }
 
-/// A helper function to deserialize a default [`Format`] from a string.
-fn format_from_str<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<Format>, D::Error> {
-    match Value::deserialize(deserializer)? {
-        Value::String(s) => match Format::from_str(s.as_str()) {
-            Ok(format) => Ok(Some(format)),
-            Err(e) => Err(D::Error::custom(e)),
-        },
-        _ => Ok(None),
-    }
+#[derive(clap::Parser, Clone, Serialize, Deserialize, Debug, Default)]
+#[serde(default)]
+pub struct BinaryCliOptions {
+    /// The address in memory where the binary will be put at. This is only considered when `bin` is selected as the format.
+    #[clap(long, value_parser = parse_u64, help_heading = "DOWNLOAD CONFIGURATION")]
+    base_address: Option<u64>,
+    /// The number of bytes to skip at the start of the binary file. This is only considered when `bin` is selected as the format.
+    #[clap(long, value_parser = parse_u32, default_value = "0", help_heading = "DOWNLOAD CONFIGURATION")]
+    skip: u32,
+}
+
+#[derive(clap::Parser, Clone, Serialize, Deserialize, Debug, Default)]
+#[serde(default)]
+pub struct IdfCliOptions {
+    /// The idf bootloader path
+    #[clap(long, help_heading = "DOWNLOAD CONFIGURATION")]
+    idf_bootloader: Option<PathBuf>,
+    /// The idf partition table path
+    #[clap(long, help_heading = "DOWNLOAD CONFIGURATION")]
+    idf_partition_table: Option<PathBuf>,
 }
 
 #[derive(clap::Parser, Clone, Serialize, Deserialize, Debug, Default)]
@@ -128,21 +138,13 @@ pub struct FormatOptions {
         long,
         help_heading = "DOWNLOAD CONFIGURATION"
     )]
-    // TODO: remove this alias in the next release after 0.24 and release of https://github.com/probe-rs/vscode/pull/86
-    #[serde(deserialize_with = "format_from_str", alias = "format")]
-    binary_format: Option<Format>,
-    /// The address in memory where the binary will be put at. This is only considered when `bin` is selected as the format.
-    #[clap(long, value_parser = parse_u64, help_heading = "DOWNLOAD CONFIGURATION")]
-    pub base_address: Option<u64>,
-    /// The number of bytes to skip at the start of the binary file. This is only considered when `bin` is selected as the format.
-    #[clap(long, value_parser = parse_u32, default_value = "0", help_heading = "DOWNLOAD CONFIGURATION")]
-    pub skip: u32,
-    /// The idf bootloader path
-    #[clap(long, help_heading = "DOWNLOAD CONFIGURATION")]
-    pub idf_bootloader: Option<PathBuf>,
-    /// The idf partition table path
-    #[clap(long, help_heading = "DOWNLOAD CONFIGURATION")]
-    pub idf_partition_table: Option<PathBuf>,
+    binary_format: Option<FormatKind>,
+
+    #[clap(flatten)]
+    bin_options: BinaryCliOptions,
+
+    #[clap(flatten)]
+    idf_options: IdfCliOptions,
 }
 
 impl FormatOptions {
@@ -150,25 +152,23 @@ impl FormatOptions {
     /// If a target has a preferred format, we use that.
     /// Finally, if neither of the above cases are true, we default to [`Format::default()`].
     pub fn into_format(self, target: &Target) -> Format {
-        let format = self
-            .binary_format
-            .unwrap_or_else(|| match target.default_format {
-                probe_rs_target::BinaryFormat::Idf => Format::Idf(Default::default()),
-                probe_rs_target::BinaryFormat::Raw => Default::default(),
-            });
+        let format = self.binary_format.unwrap_or_else(|| {
+            FormatKind::from_optional(target.default_format.as_deref())
+                .expect("Failed to parse a default binary format. This shouldn't happen.")
+        });
 
         match format {
-            Format::Bin(_) => Format::Bin(BinOptions {
-                base_address: self.base_address,
-                skip: self.skip,
+            FormatKind::Bin => Format::Bin(BinOptions {
+                base_address: self.bin_options.base_address,
+                skip: self.bin_options.skip,
             }),
-            Format::Hex => Format::Hex,
-            Format::Elf => Format::Elf,
-            Format::Idf(_) => Format::Idf(IdfOptions {
-                bootloader: self.idf_bootloader,
-                partition_table: self.idf_partition_table,
+            FormatKind::Hex => Format::Hex,
+            FormatKind::Elf => Format::Elf,
+            FormatKind::Uf2 => Format::Uf2,
+            FormatKind::Idf => Format::Idf(IdfOptions {
+                bootloader: self.idf_options.idf_bootloader,
+                partition_table: self.idf_options.idf_partition_table,
             }),
-            Format::Uf2 => Format::Uf2,
         }
     }
 }
@@ -265,7 +265,7 @@ fn main() -> Result<()> {
     if let Some(format_arg_pos) = args.iter().position(|arg| arg == "--format") {
         if let Some(format_arg) = args.get(format_arg_pos + 1) {
             if let Some(format_arg) = format_arg.to_str() {
-                if Format::from_str(format_arg).is_ok() {
+                if FormatKind::from_str(format_arg).is_ok() {
                     anyhow::bail!("--format has been renamed to --binary-format. Please use --binary-format {0} instead of --format {0}", format_arg);
                 }
             }
diff --git a/probe-rs/src/architecture/arm/ap/generic_ap.rs b/probe-rs/src/architecture/arm/ap/generic_ap.rs
index 99a93630..de53cc48 100644
--- a/probe-rs/src/architecture/arm/ap/generic_ap.rs
+++ b/probe-rs/src/architecture/arm/ap/generic_ap.rs
@@ -30,6 +30,7 @@ impl ApClass {
 }
 
 /// The type of AP defined in the [`ARM Debug Interface v5.2`](https://developer.arm.com/documentation/ihi0031/f/?lang=en) specification.
+/// You can find the details in the table C1-2 on page C1-146.
 /// The different types correspond to the different access/memory buses of ARM cores.
 #[allow(non_camel_case_types)]
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
@@ -39,17 +40,17 @@ pub enum ApType {
     JtagComAp = 0x0,
     /// A AMBA based AHB3 AP (see E1.5).
     AmbaAhb3 = 0x1,
-    /// A AMBA based AHB2 and AHB3 AP (see E1.8).
-    AmbaAhb2Ahb3 = 0x2,
+    /// A AMBA based APB2 and APB3 AP (see E1.8).
+    AmbaApb2Apb3 = 0x2,
     /// A AMBA based AXI3 and AXI4 AP (see E1.2).
     AmbaAxi3Axi4 = 0x4,
     /// A AMBA based AHB5 AP (see E1.6).
     AmbaAhb5 = 0x5,
-    /// A AMBA based AHB4 AP (see E1.3).
-    AmbaAhb4 = 0x6,
+    /// A AMBA based APB4 and APB5 AP (see E1.9).
+    AmbaApb4Apb5 = 0x6,
     /// A AMBA based AXI5 AP (see E1.4).
     AmbaAxi5 = 0x7,
-    /// A AMBA based protected AHB5 AP (see E1.7).
+    /// A AMBA based AHB5 AP with enhanced HPROT (see E1.7).
     AmbaAhb5Hprot = 0x8,
 }
 
@@ -59,10 +60,10 @@ impl ApType {
         match value {
             0x0 => Some(ApType::JtagComAp),
             0x1 => Some(ApType::AmbaAhb3),
-            0x2 => Some(ApType::AmbaAhb2Ahb3),
+            0x2 => Some(ApType::AmbaApb2Apb3),
             0x4 => Some(ApType::AmbaAxi3Axi4),
             0x5 => Some(ApType::AmbaAhb5),
-            0x6 => Some(ApType::AmbaAhb4),
+            0x6 => Some(ApType::AmbaApb4Apb5),
             0x7 => Some(ApType::AmbaAxi5),
             0x8 => Some(ApType::AmbaAhb5Hprot),
             _ => None,
diff --git a/probe-rs/src/architecture/arm/communication_interface.rs b/probe-rs/src/architecture/arm/communication_interface.rs
index fe7abe61..c33b45df 100644
--- a/probe-rs/src/architecture/arm/communication_interface.rs
+++ b/probe-rs/src/architecture/arm/communication_interface.rs
@@ -7,10 +7,7 @@ use super::{
         Abort, Ctrl, DebugPortError, DebugPortId, DebugPortVersion, DpAccess, Select, BASEPTR0,
         BASEPTR1, DPIDR, DPIDR1,
     },
-    memory::{
-        adi_v5_memory_interface::{ADIMemoryInterface, ArmProbe},
-        Component,
-    },
+    memory::{adi_v5_memory_interface::ADIMemoryInterface, ArmMemoryInterface, Component},
     sequences::{ArmDebugSequence, DefaultArmSequence},
     ArmError, DapAccess, DpAddress, FullyQualifiedApAddress, PortType, RawDapAccess, SwoAccess,
     SwoConfig,
@@ -78,11 +75,14 @@ pub trait ArmProbeInterface: DapAccess + SwdSequence + SwoAccess + Send {
     /// Returns a memory interface to access the target's memory.
     fn memory_interface(
         &mut self,
-        access_port: &MemoryAp,
-    ) -> Result<Box<dyn ArmProbe + '_>, ArmError>;
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError>;
 
     /// Returns information about a specific access port.
-    fn ap_information(&mut self, access_port: &GenericAp) -> Result<&ApInformation, ArmError>;
+    fn ap_information(
+        &mut self,
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<&ApInformation, ArmError>;
 
     /// Return the currently connected debug port.
     fn current_debug_port(&self) -> DpAddress;
@@ -390,15 +390,18 @@ pub trait DapProbe: RawDapAccess + DebugProbe {}
 impl ArmProbeInterface for ArmCommunicationInterface<Initialized> {
     fn memory_interface(
         &mut self,
-        access_port: &MemoryAp,
-    ) -> Result<Box<dyn ArmProbe + '_>, ArmError> {
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError> {
         ArmCommunicationInterface::memory_interface(self, access_port)
     }
 
-    fn ap_information(&mut self, access_port: &GenericAp) -> Result<&ApInformation, ArmError> {
+    fn ap_information(
+        &mut self,
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<&ApInformation, ArmError> {
         let info = ArmCommunicationInterface::ap_information(self, access_port)?;
 
-        info.ok_or_else(|| ArmError::ApDoesNotExist(access_port.ap_address().clone()))
+        info.ok_or_else(|| ArmError::ApDoesNotExist(access_port.clone()))
     }
 
     fn read_chip_info_from_rom_table(
@@ -547,11 +550,11 @@ impl<'interface> ArmCommunicationInterface<Initialized> {
     /// Tries to obtain a memory interface which can be used to read memory from ARM targets.
     pub fn memory_interface(
         &'interface mut self,
-        access_port: &MemoryAp,
-    ) -> Result<Box<dyn ArmProbe + 'interface>, ArmError> {
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<Box<dyn ArmMemoryInterface + 'interface>, ArmError> {
         let info = self
             .ap_information(access_port)?
-            .ok_or_else(|| ArmError::ApDoesNotExist(access_port.ap_address().clone()))?;
+            .ok_or_else(|| ArmError::ApDoesNotExist(access_port.clone()))?;
 
         match info {
             ApInformation::MemoryAp(ap_information) => {
@@ -738,10 +741,8 @@ impl<'interface> ArmCommunicationInterface<Initialized> {
     /// If the AP doesn't exist, None is returned.
     pub(crate) fn ap_information(
         &mut self,
-        access_port: &impl AccessPort,
+        addr: &FullyQualifiedApAddress,
     ) -> Result<Option<&ApInformation>, ArmError> {
-        let addr = access_port.ap_address();
-
         let state = self.select_dp(addr.dp())?;
 
         Ok(state.ap_information.get(addr.ap_v1()? as usize))
@@ -906,7 +907,7 @@ impl ArmCommunicationInterface<Initialized> {
 
                 let baseaddr = access_port.base_address(self)?;
 
-                let mut memory = self.memory_interface(&access_port)?;
+                let mut memory = self.memory_interface(access_port.ap_address())?;
 
                 let component = Component::try_parse(&mut *memory, baseaddr)?;
 
diff --git a/probe-rs/src/architecture/arm/component/mod.rs b/probe-rs/src/architecture/arm/component/mod.rs
index 4927bb1c..31936294 100644
--- a/probe-rs/src/architecture/arm/component/mod.rs
+++ b/probe-rs/src/architecture/arm/component/mod.rs
@@ -8,7 +8,6 @@ mod tmc;
 mod tpiu;
 mod trace_funnel;
 
-use super::ap::{GenericAp, MemoryAp};
 use super::memory::romtable::{CoresightComponent, PeripheralType, RomTableError};
 use super::memory::Component;
 use super::ArmError;
@@ -111,9 +110,7 @@ pub fn get_arm_components(
 
     for ap_index in 0..(interface.num_access_ports(dp)? as u8) {
         let ap_information = interface
-            .ap_information(&GenericAp::new(FullyQualifiedApAddress::v1_with_dp(
-                dp, ap_index,
-            )))?
+            .ap_information(&FullyQualifiedApAddress::v1_with_dp(dp, ap_index))?
             .clone();
 
         let component = match ap_information {
@@ -126,10 +123,9 @@ pub fn get_arm_components(
                 debug_base_address,
                 ..
             }) => {
-                let ap = MemoryAp::new(address);
-                let mut memory = interface.memory_interface(&ap)?;
+                let mut memory = interface.memory_interface(&address)?;
                 let component = Component::try_parse(&mut *memory, debug_base_address)?;
-                Ok(CoresightComponent::new(component, ap))
+                Ok(CoresightComponent::new(component, address))
             }
             ApInformation::Other { address, .. } => {
                 // Return an error, only possible to get Component from MemoryAP
diff --git a/probe-rs/src/architecture/arm/core/armv6m.rs b/probe-rs/src/architecture/arm/core/armv6m.rs
index bd94ae74..9773e366 100644
--- a/probe-rs/src/architecture/arm/core/armv6m.rs
+++ b/probe-rs/src/architecture/arm/core/armv6m.rs
@@ -2,14 +2,12 @@
 
 use super::{registers::cortex_m::*, CortexMState, Dfsr};
 use crate::{
-    architecture::arm::{
-        memory::adi_v5_memory_interface::ArmProbe, sequences::ArmDebugSequence, ArmError,
-    },
+    architecture::arm::{memory::ArmMemoryInterface, sequences::ArmDebugSequence, ArmError},
     core::{CoreRegisters, RegisterId, RegisterValue, VectorCatchCondition},
     error::Error,
     memory::valid_32bit_address,
     Architecture, BreakpointCause, CoreInformation, CoreInterface, CoreRegister, CoreStatus,
-    CoreType, HaltReason, InstructionSet, MemoryInterface, MemoryMappedRegister,
+    CoreType, HaltReason, InstructionSet, MemoryMappedRegister,
 };
 use bitfield::bitfield;
 use std::{
@@ -406,7 +404,7 @@ impl MemoryMappedRegister<u32> for Demcr {
 
 /// The state of a core that can be used to persist core state across calls to multiple different cores.
 pub(crate) struct Armv6m<'probe> {
-    memory: Box<dyn ArmProbe + 'probe>,
+    memory: Box<dyn ArmMemoryInterface + 'probe>,
 
     state: &'probe mut CortexMState,
 
@@ -415,7 +413,7 @@ pub(crate) struct Armv6m<'probe> {
 
 impl<'probe> Armv6m<'probe> {
     pub(crate) fn new(
-        mut memory: Box<dyn ArmProbe + 'probe>,
+        mut memory: Box<dyn ArmMemoryInterface + 'probe>,
         state: &'probe mut CortexMState,
         sequence: Arc<dyn ArmDebugSequence>,
     ) -> Result<Self, ArmError> {
@@ -908,100 +906,11 @@ impl<'probe> CoreInterface for Armv6m<'probe> {
     }
 }
 
-impl<'probe> MemoryInterface for Armv6m<'probe> {
-    fn supports_native_64bit_access(&mut self) -> bool {
-        self.memory.supports_native_64bit_access()
+impl super::CoreMemoryInterface for Armv6m<'_> {
+    fn memory(&self) -> &dyn ArmMemoryInterface {
+        &*self.memory
     }
-    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
-        let value = self.memory.read_word_64(address)?;
-        Ok(value)
-    }
-    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
-        let value = self.memory.read_word_32(address)?;
-        Ok(value)
-    }
-    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
-        let value = self.memory.read_word_16(address)?;
-        Ok(value)
-    }
-    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
-        let value = self.memory.read_word_8(address)?;
-        Ok(value)
-    }
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
-        self.memory.read_64(address, data)?;
-        Ok(())
-    }
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
-        self.memory.read_32(address, data)?;
-        Ok(())
-    }
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
-        self.memory.read_16(address, data)?;
-        Ok(())
-    }
-
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
-        self.memory.read_8(address, data)?;
-        Ok(())
-    }
-
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
-        self.memory.write_word_64(address, data)?;
-        Ok(())
-    }
-
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
-        self.memory.write_word_32(address, data)?;
-        Ok(())
-    }
-
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
-        self.memory.write_word_16(address, data)?;
-        Ok(())
-    }
-
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
-        self.memory.write_word_8(address, data)?;
-        Ok(())
-    }
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
-        self.memory.write_64(address, data)?;
-        Ok(())
-    }
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
-        self.memory.write_32(address, data)?;
-        Ok(())
-    }
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
-        self.memory.write_16(address, data)?;
-        Ok(())
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write_8(address, data)?;
-        Ok(())
-    }
-
-    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write(address, data)?;
-        Ok(())
-    }
-
-    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
-        let value = self.memory.supports_8bit_transfers()?;
-        Ok(value)
-    }
-
-    fn flush(&mut self) -> Result<(), Error> {
-        self.memory.flush()?;
-
-        Ok(())
+    fn memory_mut(&mut self) -> &mut dyn ArmMemoryInterface {
+        &mut *self.memory
     }
 }
diff --git a/probe-rs/src/architecture/arm/core/armv7a.rs b/probe-rs/src/architecture/arm/core/armv7a.rs
index 75874d3c..28df15bc 100644
--- a/probe-rs/src/architecture/arm/core/armv7a.rs
+++ b/probe-rs/src/architecture/arm/core/armv7a.rs
@@ -16,8 +16,8 @@ use super::{
 };
 use crate::{
     architecture::arm::{
-        core::armv7a_debug_regs::*, memory::adi_v5_memory_interface::ArmProbe,
-        sequences::ArmDebugSequence, ArmError,
+        core::armv7a_debug_regs::*, memory::ArmMemoryInterface, sequences::ArmDebugSequence,
+        ArmError,
     },
     core::{CoreRegisters, MemoryMappedRegister, RegisterId, RegisterValue},
     error::Error,
@@ -49,7 +49,7 @@ pub enum Armv7aError {
 
 /// Interface for interacting with an ARMv7-A core
 pub struct Armv7a<'probe> {
-    memory: Box<dyn ArmProbe + 'probe>,
+    memory: Box<dyn ArmMemoryInterface + 'probe>,
 
     state: &'probe mut CortexAState,
 
@@ -64,7 +64,7 @@ pub struct Armv7a<'probe> {
 
 impl<'probe> Armv7a<'probe> {
     pub(crate) fn new(
-        mut memory: Box<dyn ArmProbe + 'probe>,
+        mut memory: Box<dyn ArmMemoryInterface + 'probe>,
         state: &'probe mut CortexAState,
         base_address: u64,
         sequence: Arc<dyn ArmDebugSequence>,
@@ -767,11 +767,11 @@ impl<'probe> CoreInterface for Armv7a<'probe> {
         }
     }
 
-    fn fpu_support(&mut self) -> Result<bool, crate::error::Error> {
+    fn fpu_support(&mut self) -> Result<bool, Error> {
         Ok(self.state.fp_reg_count != 0)
     }
 
-    fn floating_point_register_count(&mut self) -> Result<usize, crate::error::Error> {
+    fn floating_point_register_count(&mut self) -> Result<usize, Error> {
         Ok(self.state.fp_reg_count)
     }
 
@@ -818,7 +818,7 @@ impl<'probe> MemoryInterface for Armv7a<'probe> {
         false
     }
 
-    fn read_word_64(&mut self, address: u64) -> Result<u64, crate::error::Error> {
+    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
         let mut ret: u64 = self.read_word_32(address)? as u64;
         ret |= (self.read_word_32(address + 4)? as u64) << 32;
 
@@ -865,7 +865,7 @@ impl<'probe> MemoryInterface for Armv7a<'probe> {
         Ok(data.to_le_bytes()[byte_offset as usize])
     }
 
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), crate::error::Error> {
+    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
         for (i, word) in data.iter_mut().enumerate() {
             *word = self.read_word_64(address + ((i as u64) * 8))?;
         }
@@ -897,7 +897,7 @@ impl<'probe> MemoryInterface for Armv7a<'probe> {
         Ok(())
     }
 
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), crate::error::Error> {
+    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
         let data_low = data as u32;
         let data_high = (data >> 32) as u32;
 
@@ -949,7 +949,7 @@ impl<'probe> MemoryInterface for Armv7a<'probe> {
         self.write_word_32(word_start, word)
     }
 
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), crate::error::Error> {
+    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
         for (i, word) in data.iter().enumerate() {
             self.write_word_64(address + ((i as u64) * 8), *word)?;
         }
@@ -1042,9 +1042,7 @@ mod test {
         }
     }
 
-    impl ArmProbe for MockProbe {
-        fn update_core_status(&mut self, _: CoreStatus) {}
-
+    impl MemoryInterface<ArmError> for MockProbe {
         fn read_8(&mut self, _address: u64, _data: &mut [u8]) -> Result<(), ArmError> {
             todo!()
         }
@@ -1138,10 +1136,26 @@ mod test {
             todo!()
         }
 
+        fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
+            todo!()
+        }
+
+        fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
+            todo!()
+        }
+
         fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
             Ok(false)
         }
 
+        fn supports_native_64bit_access(&mut self) -> bool {
+            false
+        }
+    }
+
+    impl ArmMemoryInterface for MockProbe {
+        fn update_core_status(&mut self, _: CoreStatus) {}
+
         fn get_arm_communication_interface(
             &mut self,
         ) -> Result<
@@ -1155,21 +1169,9 @@ mod test {
             })
         }
 
-        fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
-            todo!()
-        }
-
-        fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
-            todo!()
-        }
-
         fn ap(&mut self) -> MemoryAp {
             todo!()
         }
-
-        fn supports_native_64bit_access(&mut self) -> bool {
-            false
-        }
     }
 
     impl SwdSequence for MockProbe {
diff --git a/probe-rs/src/architecture/arm/core/armv7m.rs b/probe-rs/src/architecture/arm/core/armv7m.rs
index 38b86176..5df050c1 100644
--- a/probe-rs/src/architecture/arm/core/armv7m.rs
+++ b/probe-rs/src/architecture/arm/core/armv7m.rs
@@ -9,8 +9,8 @@ use super::{
 };
 use crate::{
     architecture::arm::{
-        core::registers::cortex_m::XPSR, memory::adi_v5_memory_interface::ArmProbe,
-        sequences::ArmDebugSequence, ArmError,
+        core::registers::cortex_m::XPSR, memory::ArmMemoryInterface, sequences::ArmDebugSequence,
+        ArmError,
     },
     core::{
         Architecture, CoreInformation, CoreInterface, CoreRegisters, CoreStatus, HaltReason,
@@ -18,7 +18,7 @@ use crate::{
     },
     error::Error,
     memory::valid_32bit_address,
-    BreakpointCause, CoreRegister, CoreType, InstructionSet, MemoryInterface,
+    BreakpointCause, CoreRegister, CoreType, InstructionSet,
 };
 use bitfield::bitfield;
 use std::{
@@ -587,7 +587,7 @@ impl FpRev2CompX {
 
 /// The state of a core that can be used to persist core state across calls to multiple different cores.
 pub struct Armv7m<'probe> {
-    memory: Box<dyn ArmProbe + 'probe>,
+    memory: Box<dyn ArmMemoryInterface + 'probe>,
 
     state: &'probe mut CortexMState,
 
@@ -596,7 +596,7 @@ pub struct Armv7m<'probe> {
 
 impl<'probe> Armv7m<'probe> {
     pub(crate) fn new(
-        mut memory: Box<dyn ArmProbe + 'probe>,
+        mut memory: Box<dyn ArmMemoryInterface + 'probe>,
         state: &'probe mut CortexMState,
         sequence: Arc<dyn ArmDebugSequence>,
     ) -> Result<Self, Error> {
@@ -1134,91 +1134,12 @@ impl<'probe> CoreInterface for Armv7m<'probe> {
     }
 }
 
-impl<'probe> MemoryInterface for Armv7m<'probe> {
-    fn supports_native_64bit_access(&mut self) -> bool {
-        self.memory.supports_native_64bit_access()
+impl super::CoreMemoryInterface for Armv7m<'_> {
+    fn memory(&self) -> &dyn ArmMemoryInterface {
+        &*self.memory
     }
-
-    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
-        self.memory.read_word_64(address).map_err(Error::from)
-    }
-
-    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
-        self.memory.read_word_32(address).map_err(Error::from)
-    }
-
-    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
-        self.memory.read_word_16(address).map_err(Error::from)
-    }
-
-    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
-        self.memory.read_word_8(address).map_err(Error::from)
-    }
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
-        self.memory.read_64(address, data).map_err(Error::from)
-    }
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
-        self.memory.read_32(address, data).map_err(Error::from)
-    }
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
-        self.memory.read_16(address, data).map_err(Error::from)
-    }
-
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
-        self.memory.read_8(address, data).map_err(Error::from)
-    }
-
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
-        self.memory
-            .write_word_64(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
-        self.memory
-            .write_word_32(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
-        self.memory
-            .write_word_16(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
-        self.memory.write_word_8(address, data).map_err(Error::from)
-    }
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
-        self.memory.write_64(address, data).map_err(Error::from)
-    }
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
-        self.memory.write_32(address, data).map_err(Error::from)
-    }
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
-        self.memory.write_16(address, data).map_err(Error::from)
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write_8(address, data).map_err(Error::from)
-    }
-
-    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write(address, data).map_err(Error::from)
-    }
-
-    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
-        self.memory.supports_8bit_transfers().map_err(Error::from)
-    }
-
-    fn flush(&mut self) -> Result<(), Error> {
-        self.memory.flush().map_err(Error::from)
+    fn memory_mut(&mut self) -> &mut dyn ArmMemoryInterface {
+        &mut *self.memory
     }
 }
 
diff --git a/probe-rs/src/architecture/arm/core/armv8a.rs b/probe-rs/src/architecture/arm/core/armv8a.rs
index 403f935e..20aa53ee 100644
--- a/probe-rs/src/architecture/arm/core/armv8a.rs
+++ b/probe-rs/src/architecture/arm/core/armv8a.rs
@@ -10,14 +10,14 @@ use super::{
 };
 use crate::{
     architecture::arm::{
-        core::armv8a_debug_regs::*, memory::adi_v5_memory_interface::ArmProbe,
-        sequences::ArmDebugSequence, ArmError,
+        core::armv8a_debug_regs::*, memory::ArmMemoryInterface, sequences::ArmDebugSequence,
+        ArmError,
     },
     core::{
         memory_mapped_registers::MemoryMappedRegister, CoreRegisters, RegisterId, RegisterValue,
     },
     error::Error,
-    memory::valid_32bit_address,
+    memory::{valid_32bit_address, MemoryNotAlignedError},
     Architecture, CoreInformation, CoreInterface, CoreRegister, CoreStatus, CoreType,
     InstructionSet, MemoryInterface,
 };
@@ -49,7 +49,7 @@ fn prep_instr_for_itr_32(instruction: u32) -> u32 {
 
 /// Interface for interacting with an ARMv8-A core
 pub struct Armv8a<'probe> {
-    memory: Box<dyn ArmProbe + 'probe>,
+    memory: Box<dyn ArmMemoryInterface + 'probe>,
 
     state: &'probe mut CortexAState,
 
@@ -64,7 +64,7 @@ pub struct Armv8a<'probe> {
 
 impl<'probe> Armv8a<'probe> {
     pub(crate) fn new(
-        mut memory: Box<dyn ArmProbe + 'probe>,
+        mut memory: Box<dyn ArmMemoryInterface + 'probe>,
         state: &'probe mut CortexAState,
         base_address: u64,
         cti_address: u64,
@@ -849,10 +849,11 @@ impl<'probe> Armv8a<'probe> {
             return Ok(());
         }
         if data.len() % 4 != 0 || address % 4 != 0 {
-            return Err(Error::MemoryNotAligned {
+            return Err(MemoryNotAlignedError {
                 address,
                 alignment: 4,
-            });
+            }
+            .into());
         }
 
         // Save x0
@@ -961,10 +962,11 @@ impl<'probe> Armv8a<'probe> {
             return Ok(());
         }
         if data.len() % 4 != 0 || address % 4 != 0 {
-            return Err(Error::MemoryNotAligned {
+            return Err(MemoryNotAlignedError {
                 address,
                 alignment: 4,
-            });
+            }
+            .into());
         }
 
         // Save x0
@@ -1730,10 +1732,7 @@ mod test {
             });
         }
     }
-
-    impl ArmProbe for MockProbe {
-        fn update_core_status(&mut self, _: CoreStatus) {}
-
+    impl MemoryInterface<ArmError> for MockProbe {
         fn read_8(&mut self, _address: u64, _data: &mut [u8]) -> Result<(), ArmError> {
             todo!()
         }
@@ -1811,10 +1810,30 @@ mod test {
             Ok(())
         }
 
+        fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
+            todo!()
+        }
+
+        fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
+            todo!()
+        }
+
         fn flush(&mut self) -> Result<(), ArmError> {
             todo!()
         }
 
+        fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
+            Ok(false)
+        }
+
+        fn supports_native_64bit_access(&mut self) -> bool {
+            false
+        }
+    }
+
+    impl ArmMemoryInterface for MockProbe {
+        fn update_core_status(&mut self, _: CoreStatus) {}
+
         fn ap(&mut self) -> MemoryAp {
             todo!()
         }
@@ -1831,22 +1850,6 @@ mod test {
                 function_name: "get_arm_communication_interface",
             })
         }
-
-        fn read_64(&mut self, _address: u64, _data: &mut [u64]) -> Result<(), ArmError> {
-            todo!()
-        }
-
-        fn write_64(&mut self, _address: u64, _data: &[u64]) -> Result<(), ArmError> {
-            todo!()
-        }
-
-        fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
-            Ok(false)
-        }
-
-        fn supports_native_64bit_access(&mut self) -> bool {
-            false
-        }
     }
 
     impl SwdSequence for MockProbe {
diff --git a/probe-rs/src/architecture/arm/core/armv8m.rs b/probe-rs/src/architecture/arm/core/armv8m.rs
index 83c5d45c..6a016d74 100644
--- a/probe-rs/src/architecture/arm/core/armv8m.rs
+++ b/probe-rs/src/architecture/arm/core/armv8m.rs
@@ -9,14 +9,14 @@ use super::{
 };
 use crate::{
     architecture::arm::{
-        core::registers::cortex_m::XPSR, memory::adi_v5_memory_interface::ArmProbe,
-        sequences::ArmDebugSequence, ArmError,
+        core::registers::cortex_m::XPSR, memory::ArmMemoryInterface, sequences::ArmDebugSequence,
+        ArmError,
     },
     core::{CoreRegisters, RegisterId, RegisterValue, VectorCatchCondition},
     error::Error,
     memory::valid_32bit_address,
     Architecture, BreakpointCause, CoreInformation, CoreInterface, CoreRegister, CoreStatus,
-    CoreType, HaltReason, InstructionSet, MemoryInterface, MemoryMappedRegister,
+    CoreType, HaltReason, InstructionSet, MemoryMappedRegister,
 };
 use bitfield::bitfield;
 use std::{
@@ -27,7 +27,7 @@ use std::{
 
 /// The state of a core that can be used to persist core state across calls to multiple different cores.
 pub struct Armv8m<'probe> {
-    memory: Box<dyn ArmProbe + 'probe>,
+    memory: Box<dyn ArmMemoryInterface + 'probe>,
 
     state: &'probe mut CortexMState,
 
@@ -36,7 +36,7 @@ pub struct Armv8m<'probe> {
 
 impl<'probe> Armv8m<'probe> {
     pub(crate) fn new(
-        mut memory: Box<dyn ArmProbe + 'probe>,
+        mut memory: Box<dyn ArmMemoryInterface + 'probe>,
         state: &'probe mut CortexMState,
         sequence: Arc<dyn ArmDebugSequence>,
     ) -> Result<Self, Error> {
@@ -540,91 +540,12 @@ impl<'probe> CoreInterface for Armv8m<'probe> {
     }
 }
 
-impl<'probe> MemoryInterface for Armv8m<'probe> {
-    fn supports_native_64bit_access(&mut self) -> bool {
-        self.memory.supports_native_64bit_access()
+impl super::CoreMemoryInterface for Armv8m<'_> {
+    fn memory(&self) -> &dyn ArmMemoryInterface {
+        &*self.memory
     }
-
-    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
-        self.memory.read_word_64(address).map_err(Error::from)
-    }
-
-    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
-        self.memory.read_word_32(address).map_err(Error::from)
-    }
-
-    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
-        self.memory.read_word_16(address).map_err(Error::from)
-    }
-
-    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
-        self.memory.read_word_8(address).map_err(Error::from)
-    }
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
-        self.memory.read_64(address, data).map_err(Error::from)
-    }
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
-        self.memory.read_32(address, data).map_err(Error::from)
-    }
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
-        self.memory.read_16(address, data).map_err(Error::from)
-    }
-
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
-        self.memory.read_8(address, data).map_err(Error::from)
-    }
-
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
-        self.memory
-            .write_word_64(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
-        self.memory
-            .write_word_32(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
-        self.memory
-            .write_word_16(address, data)
-            .map_err(Error::from)
-    }
-
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
-        self.memory.write_word_8(address, data).map_err(Error::from)
-    }
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
-        self.memory.write_64(address, data).map_err(Error::from)
-    }
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
-        self.memory.write_32(address, data).map_err(Error::from)
-    }
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
-        self.memory.write_16(address, data).map_err(Error::from)
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write_8(address, data).map_err(Error::from)
-    }
-
-    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        self.memory.write(address, data).map_err(Error::from)
-    }
-
-    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
-        self.memory.supports_8bit_transfers().map_err(Error::from)
-    }
-
-    fn flush(&mut self) -> Result<(), Error> {
-        self.memory.flush().map_err(Error::from)
+    fn memory_mut(&mut self) -> &mut dyn ArmMemoryInterface {
+        &mut *self.memory
     }
 }
 
diff --git a/probe-rs/src/architecture/arm/core/cortex_m.rs b/probe-rs/src/architecture/arm/core/cortex_m.rs
index e946bffa..f81d73f3 100644
--- a/probe-rs/src/architecture/arm/core/cortex_m.rs
+++ b/probe-rs/src/architecture/arm/core/cortex_m.rs
@@ -1,7 +1,7 @@
 //! Common functions and data types for Cortex-M core variants
 
 use crate::{
-    architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError},
+    architecture::arm::{memory::ArmMemoryInterface, ArmError},
     core::RegisterId,
     memory_mapped_bitfield_register,
     semihosting::decode_semihosting_syscall,
@@ -151,7 +151,10 @@ impl IdPfr1 {
     }
 }
 
-pub(crate) fn read_core_reg(memory: &mut dyn ArmProbe, addr: RegisterId) -> Result<u32, ArmError> {
+pub(crate) fn read_core_reg(
+    memory: &mut dyn ArmMemoryInterface,
+    addr: RegisterId,
+) -> Result<u32, ArmError> {
     // Write the DCRSR value to select the register we want to read.
     let mut dcrsr_val = Dcrsr(0);
     dcrsr_val.set_regwnr(false); // Perform a read.
@@ -167,7 +170,7 @@ pub(crate) fn read_core_reg(memory: &mut dyn ArmProbe, addr: RegisterId) -> Resu
 }
 
 pub(crate) fn write_core_reg(
-    memory: &mut dyn ArmProbe,
+    memory: &mut dyn ArmMemoryInterface,
     addr: RegisterId,
     value: u32,
 ) -> Result<(), ArmError> {
@@ -231,7 +234,7 @@ pub(crate) fn check_for_semihosting(
 }
 
 fn wait_for_core_register_transfer(
-    memory: &mut dyn ArmProbe,
+    memory: &mut dyn ArmMemoryInterface,
     timeout: Duration,
 ) -> Result<(), ArmError> {
     // now we have to poll the dhcsr register, until the dhcsr.s_regrdy bit is set
diff --git a/probe-rs/src/architecture/arm/core/mod.rs b/probe-rs/src/architecture/arm/core/mod.rs
index b33735ea..0f727a26 100644
--- a/probe-rs/src/architecture/arm/core/mod.rs
+++ b/probe-rs/src/architecture/arm/core/mod.rs
@@ -4,9 +4,12 @@ use serde::{Deserialize, Serialize};
 
 use crate::{
     core::{BreakpointCause, RegisterValue},
-    memory_mapped_bitfield_register, CoreStatus, HaltReason, SemihostingCommand,
+    memory_mapped_bitfield_register, CoreStatus, Error, HaltReason, MemoryInterface,
+    SemihostingCommand,
 };
 
+use super::memory::ArmMemoryInterface;
+
 pub mod armv6m;
 pub mod armv7a;
 pub mod armv7m;
@@ -205,10 +208,7 @@ impl CortexAState {
 /// It will reflect the core status to the probe/memory interface if
 /// the status has changed, and will replace `current_status` with
 /// `new_status`.
-pub fn update_core_status<
-    P: super::memory::adi_v5_memory_interface::ArmProbe + ?Sized,
-    T: core::ops::DerefMut<Target = P>,
->(
+pub fn update_core_status<P: ArmMemoryInterface + ?Sized, T: core::ops::DerefMut<Target = P>>(
     probe: &mut T,
     current_status: &mut CoreStatus,
     new_status: CoreStatus,
@@ -218,3 +218,115 @@ pub fn update_core_status<
     }
     *current_status = new_status;
 }
+
+trait CoreMemoryInterface {
+    fn memory(&self) -> &dyn ArmMemoryInterface;
+    fn memory_mut(&mut self) -> &mut dyn ArmMemoryInterface;
+}
+
+impl<T> MemoryInterface<Error> for T
+where
+    T: CoreMemoryInterface,
+{
+    fn supports_native_64bit_access(&mut self) -> bool {
+        self.memory_mut().supports_native_64bit_access()
+    }
+
+    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
+        self.memory_mut().read_word_64(address).map_err(Error::from)
+    }
+
+    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
+        self.memory_mut().read_word_32(address).map_err(Error::from)
+    }
+
+    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
+        self.memory_mut().read_word_16(address).map_err(Error::from)
+    }
+
+    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
+        self.memory_mut().read_word_8(address).map_err(Error::from)
+    }
+
+    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
+        self.memory_mut()
+            .read_64(address, data)
+            .map_err(Error::from)
+    }
+
+    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
+        self.memory_mut()
+            .read_32(address, data)
+            .map_err(Error::from)
+    }
+
+    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
+        self.memory_mut()
+            .read_16(address, data)
+            .map_err(Error::from)
+    }
+
+    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
+        self.memory_mut().read_8(address, data).map_err(Error::from)
+    }
+
+    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
+        self.memory_mut()
+            .write_word_64(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
+        self.memory_mut()
+            .write_word_32(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
+        self.memory_mut()
+            .write_word_16(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
+        self.memory_mut()
+            .write_word_8(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
+        self.memory_mut()
+            .write_64(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
+        self.memory_mut()
+            .write_32(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
+        self.memory_mut()
+            .write_16(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
+        self.memory_mut()
+            .write_8(address, data)
+            .map_err(Error::from)
+    }
+
+    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
+        self.memory_mut().write(address, data).map_err(Error::from)
+    }
+
+    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
+        self.memory().supports_8bit_transfers().map_err(Error::from)
+    }
+
+    fn flush(&mut self) -> Result<(), Error> {
+        self.memory_mut().flush().map_err(Error::from)
+    }
+}
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 dec50d9a..14c43e43 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
@@ -2,187 +2,15 @@ use super::super::ap::{
     AccessPortError, AddressIncrement, ApAccess, ApRegister, DataSize, MemoryAp, CSW, DRW, TAR,
     TAR2,
 };
+use crate::architecture::arm::ap::AccessPort;
 use crate::architecture::arm::communication_interface::{FlushableArmAccess, SwdSequence};
 use crate::architecture::arm::{
-    communication_interface::Initialized, dp::DpAccess, MemoryApInformation,
+    communication_interface::Initialized, dp::DpAccess, memory::ArmMemoryInterface,
+    MemoryApInformation,
 };
 use crate::architecture::arm::{ArmCommunicationInterface, ArmError};
-use crate::{probe::DebugProbeError, CoreStatus};
-
-pub trait ArmProbe: SwdSequence {
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError>;
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), ArmError>;
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), ArmError>;
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), ArmError>;
-
-    /// Reads a 64 bit word from `address`.
-    fn read_word_64(&mut self, address: u64) -> Result<u64, ArmError> {
-        let mut buff = [0];
-        self.read_64(address, &mut buff)?;
-
-        Ok(buff[0])
-    }
-
-    /// Reads a 32 bit word from `address`.
-    fn read_word_32(&mut self, address: u64) -> Result<u32, ArmError> {
-        let mut buff = [0];
-        self.read_32(address, &mut buff)?;
-
-        Ok(buff[0])
-    }
-
-    /// Reads a 16 bit word from `address`.
-    fn read_word_16(&mut self, address: u64) -> Result<u16, ArmError> {
-        let mut buff = [0];
-        self.read_16(address, &mut buff)?;
-
-        Ok(buff[0])
-    }
-
-    /// Reads an 8 bit word from `address`.
-    fn read_word_8(&mut self, address: u64) -> Result<u8, ArmError> {
-        let mut buff = [0];
-        self.read_8(address, &mut buff)?;
-
-        Ok(buff[0])
-    }
-
-    /// Read a block of 8bit words at `address`. May use 32 bit memory access,
-    /// so should only be used if reading memory locations that don't have side
-    /// effects. Generally faster than
-    /// [`MemoryInterface::read_8()`][crate::MemoryInterface::read_8()].
-    fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError> {
-        let len = data.len();
-        if address % 4 == 0 && len % 4 == 0 {
-            let mut buffer = vec![0u32; len / 4];
-            self.read_32(address, &mut buffer)?;
-            for (bytes, value) in data.chunks_exact_mut(4).zip(buffer.iter()) {
-                bytes.copy_from_slice(&u32::to_le_bytes(*value));
-            }
-        } else {
-            let start_extra_count = (address % 4) as usize;
-            let mut buffer = vec![0u32; (start_extra_count + len + 3) / 4];
-            let read_address = address - start_extra_count as u64;
-            self.read_32(read_address, &mut buffer)?;
-            for (bytes, value) in data
-                .chunks_exact_mut(4)
-                .zip(buffer[start_extra_count..start_extra_count + len].iter())
-            {
-                bytes.copy_from_slice(&u32::to_le_bytes(*value));
-            }
-        }
-        Ok(())
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), ArmError>;
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), ArmError>;
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), ArmError>;
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), ArmError>;
-
-    /// Writes a 64 bit word to `address`.
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), ArmError> {
-        self.write_64(address, &[data])
-    }
-
-    /// Writes a 32 bit word to `address`.
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), ArmError> {
-        self.write_32(address, &[data])
-    }
-
-    /// Writes a 16 bit word to `address`.
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), ArmError> {
-        self.write_16(address, &[data])
-    }
-
-    /// Writes a 8 bit word to `address`.
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), ArmError> {
-        self.write_8(address, &[data])
-    }
-
-    /// Write a block of 8bit words to `address`. May use 32 bit memory access,
-    /// so it should only be used if writing memory locations that don't have side
-    /// effects. Generally faster than
-    /// [`MemoryInterface::write_8()`][crate::MemoryInterface::write_8()].
-    fn write(&mut self, mut address: u64, mut data: &[u8]) -> Result<(), ArmError> {
-        let len = data.len();
-        // Number of unaligned bytes at the start
-        let start_extra_count = ((4 - (address % 4) as usize) % 4).min(len);
-        // Extra bytes to be written at the end
-        let end_extra_count = (len - start_extra_count) % 4;
-        // Number of bytes between start and end (i.e. number of bytes transmitted as 32 bit words)
-        let inbetween_count = len - start_extra_count - end_extra_count;
-
-        assert!(start_extra_count < 4);
-        assert!(end_extra_count < 4);
-        assert!(inbetween_count % 4 == 0);
-
-        // If we do not have 32 bit aligned access we first check that we can do 8 bit aligned access on this platform.
-        // If we cannot we throw an error.
-        // If we can we write the first n < 4 bytes up until the word aligned address that comes next.
-        if address % 4 != 0 || len % 4 != 0 {
-            // If we do not support 8 bit transfers we have to bail because we can only do 32 bit word aligned transers.
-            if !self.supports_8bit_transfers()? {
-                return Err(ArmError::alignment_error(address, 4));
-            }
-
-            // We first do an 8 bit write of the first < 4 bytes up until the 4 byte aligned boundary.
-            self.write_8(address, &data[..start_extra_count])?;
-
-            address += start_extra_count as u64;
-            data = &data[start_extra_count..];
-        }
-
-        // Make sure we don't try to do an empty but potentially unaligned write
-        if inbetween_count > 0 {
-            // We do a 32 bit write of the remaining bytes that are 4 byte aligned.
-            let mut buffer = vec![0u32; inbetween_count / 4];
-            for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
-                *value = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
-            }
-            self.write_32(address, &buffer)?;
-
-            address += inbetween_count as u64;
-            data = &data[inbetween_count..];
-        }
-
-        // We write the remaining bytes that we did not write yet which is always n < 4.
-        if end_extra_count > 0 {
-            self.write_8(address, &data[..end_extra_count])?;
-        }
-
-        Ok(())
-    }
-
-    fn flush(&mut self) -> Result<(), ArmError>;
-
-    fn supports_native_64bit_access(&mut self) -> bool;
-
-    fn supports_8bit_transfers(&self) -> Result<bool, ArmError>;
-
-    /// Returns the underlying [`MemoryAp`].
-    fn ap(&mut self) -> MemoryAp;
-
-    fn get_arm_communication_interface(
-        &mut self,
-    ) -> Result<&mut ArmCommunicationInterface<Initialized>, DebugProbeError>;
-
-    /// 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();
-    }
-}
+use crate::probe::DebugProbeError;
+use crate::MemoryInterface;
 
 /// Calculate the maximum number of bytes we can write starting at address
 /// before we run into the 10-bit TAR autoincrement limit.
@@ -308,7 +136,7 @@ where
         self.interface
             .read_ap_register(&self.memory_ap)
             .map_err(AccessPortError::register_read_error::<R, _>)
-            .map_err(|error| ArmError::from_access_port(error, &self.memory_ap))
+            .map_err(|error| ArmError::from_access_port(error, self.memory_ap.ap_address()))
     }
 
     /// Read multiple 32 bit values from the DRW register on the given AP.
@@ -325,7 +153,7 @@ where
             self.interface
                 .read_ap_register_repeated(&self.memory_ap, DRW { data: 0 }, values)
                 .map_err(AccessPortError::register_read_error::<DRW, _>)
-                .map_err(|err| ArmError::from_access_port(err, &self.memory_ap))
+                .map_err(|err| ArmError::from_access_port(err, self.memory_ap.ap_address()))
         }
     }
 
@@ -338,7 +166,7 @@ where
         self.interface
             .write_ap_register(&self.memory_ap, register)
             .map_err(AccessPortError::register_write_error::<R, _>)
-            .map_err(|e| ArmError::from_access_port(e, &self.memory_ap))
+            .map_err(|e| ArmError::from_access_port(e, self.memory_ap.ap_address()))
     }
 
     /// Write multiple 32 bit values to the DRW register on the given AP.
@@ -353,16 +181,44 @@ where
             self.interface
                 .write_ap_register_repeated(&self.memory_ap, DRW { data: 0 }, values)
                 .map_err(AccessPortError::register_write_error::<DRW, _>)
-                .map_err(|e| ArmError::from_access_port(e, &self.memory_ap))
+                .map_err(|e| ArmError::from_access_port(e, self.memory_ap.ap_address()))
         }
     }
+}
+impl<AP> SwdSequence for ADIMemoryInterface<'_, AP>
+where
+    AP: FlushableArmAccess + ApAccess + DpAccess,
+{
+    fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
+        self.get_arm_communication_interface()?
+            .swj_sequence(bit_len, bits)
+    }
+
+    fn swj_pins(
+        &mut self,
+        pin_out: u32,
+        pin_select: u32,
+        pin_wait: u32,
+    ) -> Result<u32, DebugProbeError> {
+        self.get_arm_communication_interface()?
+            .swj_pins(pin_out, pin_select, pin_wait)
+    }
+}
+
+impl<AP> MemoryInterface<ArmError> for ADIMemoryInterface<'_, AP>
+where
+    AP: FlushableArmAccess + ApAccess + DpAccess,
+{
+    fn supports_native_64bit_access(&mut self) -> bool {
+        self.ap_information.has_large_data_extension
+    }
 
     /// Read a block of 64 bit words at `address`.
     ///
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 8.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn read_64(&mut self, mut address: u64, mut data: &mut [u64]) -> Result<(), ArmError> {
+    fn read_64(&mut self, mut address: u64, mut data: &mut [u64]) -> Result<(), ArmError> {
         if data.is_empty() {
             return Ok(());
         }
@@ -415,7 +271,7 @@ where
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 4.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn read_32(&mut self, mut address: u64, mut data: &mut [u32]) -> Result<(), ArmError> {
+    fn read_32(&mut self, mut address: u64, mut data: &mut [u32]) -> Result<(), ArmError> {
         if data.is_empty() {
             return Ok(());
         }
@@ -455,7 +311,7 @@ where
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 2.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn read_16(&mut self, mut address: u64, mut data: &mut [u16]) -> Result<(), ArmError> {
+    fn read_16(&mut self, mut address: u64, mut data: &mut [u16]) -> Result<(), ArmError> {
         if self.ap_information.supports_only_32bit_data_size {
             return Err(ArmError::UnsupportedTransferWidth(8));
         }
@@ -508,7 +364,7 @@ where
     /// Read a block of 8 bit words at `address`.
     ///
     /// The number of words read is `data.len()`.
-    pub fn read_8(&mut self, mut address: u64, mut data: &mut [u8]) -> Result<(), ArmError> {
+    fn read_8(&mut self, mut address: u64, mut data: &mut [u8]) -> Result<(), ArmError> {
         if self.ap_information.supports_only_32bit_data_size {
             return Err(ArmError::UnsupportedTransferWidth(8));
         }
@@ -554,12 +410,35 @@ where
         Ok(())
     }
 
+    fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError> {
+        let len = data.len();
+        if address % 4 == 0 && len % 4 == 0 {
+            let mut buffer = vec![0u32; len / 4];
+            self.read_32(address, &mut buffer)?;
+            for (bytes, value) in data.chunks_exact_mut(4).zip(buffer.iter()) {
+                bytes.copy_from_slice(&u32::to_le_bytes(*value));
+            }
+        } else {
+            let start_extra_count = (address % 4) as usize;
+            let mut buffer = vec![0u32; (start_extra_count + len + 3) / 4];
+            let read_address = address - start_extra_count as u64;
+            self.read_32(read_address, &mut buffer)?;
+            for (bytes, value) in data
+                .chunks_exact_mut(4)
+                .zip(buffer[start_extra_count..start_extra_count + len].iter())
+            {
+                bytes.copy_from_slice(&u32::to_le_bytes(*value));
+            }
+        }
+        Ok(())
+    }
+
     /// Write a block of 64 bit words at `address`.
     ///
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 8.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn write_64(&mut self, mut address: u64, mut data: &[u64]) -> Result<(), ArmError> {
+    fn write_64(&mut self, mut address: u64, mut data: &[u64]) -> Result<(), ArmError> {
         if (address % 8) != 0 {
             return Err(ArmError::alignment_error(address, 8));
         }
@@ -617,7 +496,7 @@ where
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 4.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn write_32(&mut self, mut address: u64, mut data: &[u32]) -> Result<(), ArmError> {
+    fn write_32(&mut self, mut address: u64, mut data: &[u32]) -> Result<(), ArmError> {
         if (address % 4) != 0 {
             return Err(ArmError::alignment_error(address, 4));
         }
@@ -663,7 +542,7 @@ where
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 2.
     /// Returns `ArmError::MemoryNotAligned` if this does not hold true.
-    pub fn write_16(&mut self, mut address: u64, mut data: &[u16]) -> Result<(), ArmError> {
+    fn write_16(&mut self, mut address: u64, mut data: &[u16]) -> Result<(), ArmError> {
         if self.ap_information.supports_only_32bit_data_size {
             return Err(ArmError::UnsupportedTransferWidth(8));
         }
@@ -718,7 +597,7 @@ where
     /// Write a block of 8 bit words at `address`.
     ///
     /// The number of words written is `data.len()`.
-    pub fn write_8(&mut self, mut address: u64, mut data: &[u8]) -> Result<(), ArmError> {
+    fn write_8(&mut self, mut address: u64, mut data: &[u8]) -> Result<(), ArmError> {
         if self.ap_information.supports_only_32bit_data_size {
             return Err(ArmError::UnsupportedTransferWidth(8));
         }
@@ -767,67 +646,6 @@ where
 
         Ok(())
     }
-}
-
-impl<AP> SwdSequence for ADIMemoryInterface<'_, AP>
-where
-    AP: FlushableArmAccess + ApAccess + DpAccess,
-{
-    fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
-        self.get_arm_communication_interface()?
-            .swj_sequence(bit_len, bits)
-    }
-
-    fn swj_pins(
-        &mut self,
-        pin_out: u32,
-        pin_select: u32,
-        pin_wait: u32,
-    ) -> Result<u32, DebugProbeError> {
-        self.get_arm_communication_interface()?
-            .swj_pins(pin_out, pin_select, pin_wait)
-    }
-}
-
-impl<AP> ArmProbe for ADIMemoryInterface<'_, AP>
-where
-    AP: FlushableArmAccess + ApAccess + DpAccess,
-{
-    fn supports_native_64bit_access(&mut self) -> bool {
-        self.ap_information.has_large_data_extension
-    }
-
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), ArmError> {
-        self.read_8(address, data)
-    }
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), ArmError> {
-        self.read_16(address, data)
-    }
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), ArmError> {
-        self.read_32(address, data)
-    }
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), ArmError> {
-        self.read_64(address, data)
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), ArmError> {
-        self.write_8(address, data)
-    }
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), ArmError> {
-        self.write_16(address, data)
-    }
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), ArmError> {
-        self.write_32(address, data)
-    }
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), ArmError> {
-        self.write_64(address, data)
-    }
 
     fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
         Ok(!self.ap_information.supports_only_32bit_data_size)
@@ -838,7 +656,12 @@ where
 
         Ok(())
     }
+}
 
+impl<AP> ArmMemoryInterface for ADIMemoryInterface<'_, AP>
+where
+    AP: FlushableArmAccess + ApAccess + DpAccess,
+{
     /// Returns the underlying [`MemoryAp`].
     fn ap(&mut self) -> MemoryAp {
         self.memory_ap.clone()
@@ -854,13 +677,13 @@ where
 #[cfg(test)]
 mod tests {
     use scroll::Pread;
+    use test_log::test;
 
+    use crate::architecture::arm::ap::memory_ap::{mock::MockMemoryAp, MemoryAp};
     use crate::architecture::arm::{ap::AccessPort, FullyQualifiedApAddress, MemoryApInformation};
+    use crate::memory::MemoryInterface;
 
-    use super::super::super::ap::memory_ap::mock::MockMemoryAp;
-    use super::super::super::ap::memory_ap::MemoryAp;
     use super::ADIMemoryInterface;
-    use super::ArmProbe;
 
     const DUMMY_AP: MemoryAp = MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(0));
 
@@ -1059,7 +882,7 @@ mod tests {
         }
     }
 
-    #[test_log::test]
+    #[test]
     fn read_8() {
         let mut mock = MockMemoryAp::with_pattern_and_size(256);
         mock.memory[..DATA8.len()].copy_from_slice(DATA8);
diff --git a/probe-rs/src/architecture/arm/memory/mod.rs b/probe-rs/src/architecture/arm/memory/mod.rs
index 82198708..fd155691 100644
--- a/probe-rs/src/architecture/arm/memory/mod.rs
+++ b/probe-rs/src/architecture/arm/memory/mod.rs
@@ -3,5 +3,33 @@
 pub(crate) mod adi_v5_memory_interface;
 pub mod romtable;
 
-use super::ap::AccessPortError;
+use crate::{memory::MemoryInterface, probe::DebugProbeError, CoreStatus};
+
+use super::{
+    ap::memory_ap::MemoryAp,
+    communication_interface::{Initialized, SwdSequence},
+    ArmCommunicationInterface, ArmError,
+};
 pub use romtable::{Component, ComponentId, CoresightComponent, PeripheralType};
+
+/// An ArmMemoryInterface (ArmProbeInterface + MemoryAp)
+pub trait ArmMemoryInterface: SwdSequence + MemoryInterface<ArmError> {
+    /// The underlying MemoryAp.
+    fn ap(&mut self) -> MemoryAp;
+
+    /// The underlying `ArmCommunicationInterface` if this is an `ArmCommunicationInterface`.
+    fn get_arm_communication_interface(
+        &mut self,
+    ) -> Result<&mut ArmCommunicationInterface<Initialized>, DebugProbeError>;
+
+    /// 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();
+    }
+}
diff --git a/probe-rs/src/architecture/arm/memory/romtable.rs b/probe-rs/src/architecture/arm/memory/romtable.rs
index 1d9fc39d..9f26b14e 100644
--- a/probe-rs/src/architecture/arm/memory/romtable.rs
+++ b/probe-rs/src/architecture/arm/memory/romtable.rs
@@ -1,9 +1,11 @@
 //! CoreSight ROM table parsing and handling.
 
-use super::adi_v5_memory_interface::ArmProbe;
-use super::AccessPortError;
-use crate::architecture::arm::ArmError;
-use crate::architecture::arm::{ap::MemoryAp, communication_interface::ArmProbeInterface};
+use crate::architecture::arm::{
+    ap::{AccessPort, AccessPortError},
+    communication_interface::ArmProbeInterface,
+    memory::ArmMemoryInterface,
+    ArmError, FullyQualifiedApAddress,
+};
 
 /// An error to report any errors that are romtable discovery specific.
 #[derive(thiserror::Error, Debug, docsplay::Display)]
@@ -36,12 +38,12 @@ impl RomTableError {
 /// A lazy romtable reader that is used to create an iterator over all romtable entries.
 struct RomTableReader<'probe: 'memory, 'memory> {
     base_address: u64,
-    memory: &'memory mut (dyn ArmProbe + 'probe),
+    memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
 }
 
 /// Iterates over a ROM table non recursively.
 impl<'probe: 'memory, 'memory> RomTableReader<'probe, 'memory> {
-    fn new(memory: &'memory mut (dyn ArmProbe + 'probe), base_address: u64) -> Self {
+    fn new(memory: &'memory mut (dyn ArmMemoryInterface + 'probe), base_address: u64) -> Self {
         RomTableReader {
             base_address,
             memory,
@@ -118,7 +120,10 @@ impl RomTable {
     ///
     /// This does not check whether the data actually signalizes
     /// to contain a ROM table but assumes this was checked beforehand.
-    fn try_parse(memory: &mut dyn ArmProbe, base_address: u64) -> Result<RomTable, RomTableError> {
+    fn try_parse(
+        memory: &mut dyn ArmMemoryInterface,
+        base_address: u64,
+    ) -> Result<RomTable, RomTableError> {
         // This is required for the collect down below.
         let mut entries = vec![];
 
@@ -146,7 +151,7 @@ impl RomTable {
                     format: raw_entry.format,
                     power_domain_id: raw_entry.power_domain_id,
                     power_domain_valid: raw_entry.power_domain_valid,
-                    component: CoresightComponent::new(component, memory.ap()),
+                    component: CoresightComponent::new(component, memory.ap().ap_address().clone()),
                 });
             }
         }
@@ -265,12 +270,12 @@ impl ComponentId {
 /// This reader is meant for internal use only.
 pub struct ComponentInformationReader<'probe: 'memory, 'memory> {
     base_address: u64,
-    memory: &'memory mut (dyn ArmProbe + 'probe),
+    memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
 }
 
 impl<'probe: 'memory, 'memory> ComponentInformationReader<'probe, 'memory> {
     /// Creates a new `ComponentInformationReader` which can be used to extract the data from a component information table in memory.
-    pub fn new(base_address: u64, memory: &'memory mut (dyn ArmProbe + 'probe)) -> Self {
+    pub fn new(base_address: u64, memory: &'memory mut (dyn ArmMemoryInterface + 'probe)) -> Self {
         ComponentInformationReader {
             base_address,
             memory,
@@ -434,7 +439,7 @@ pub enum Component {
 impl Component {
     /// Tries to parse a CoreSight component table.
     pub fn try_parse<'probe: 'memory, 'memory>(
-        memory: &'memory mut (dyn ArmProbe + 'probe),
+        memory: &'memory mut (dyn ArmMemoryInterface + 'probe),
         baseaddr: u64,
     ) -> Result<Component, RomTableError> {
         tracing::debug!("\tReading component data at: {:#010x}", baseaddr);
@@ -493,13 +498,16 @@ pub struct CoresightComponent {
     /// The component variant that is accessible.
     pub component: Component,
     /// The probe access point where the component can be accessed from
-    pub ap: MemoryAp,
+    pub ap_address: FullyQualifiedApAddress,
 }
 
 impl CoresightComponent {
     /// Construct a coresight component found on the provided access point.
-    pub fn new(component: Component, ap: MemoryAp) -> Self {
-        Self { component, ap }
+    pub fn new(component: Component, ap: FullyQualifiedApAddress) -> Self {
+        Self {
+            component,
+            ap_address: ap,
+        }
     }
 
     /// Reads a register of the component pointed to by this romtable entry.
@@ -508,7 +516,7 @@ impl CoresightComponent {
         interface: &mut dyn ArmProbeInterface,
         offset: u32,
     ) -> Result<u32, ArmError> {
-        let mut memory = interface.memory_interface(&self.ap)?;
+        let mut memory = interface.memory_interface(&self.ap_address)?;
         let value = memory.read_word_32(self.component.id().component_address + offset as u64)?;
         Ok(value)
     }
@@ -520,7 +528,7 @@ impl CoresightComponent {
         offset: u32,
         value: u32,
     ) -> Result<(), ArmError> {
-        let mut memory = interface.memory_interface(&self.ap)?;
+        let mut memory = interface.memory_interface(&self.ap_address)?;
         memory.write_word_32(self.component.id().component_address + offset as u64, value)?;
         Ok(())
     }
diff --git a/probe-rs/src/architecture/arm/mod.rs b/probe-rs/src/architecture/arm/mod.rs
index 44d511a7..61be51dd 100644
--- a/probe-rs/src/architecture/arm/mod.rs
+++ b/probe-rs/src/architecture/arm/mod.rs
@@ -12,14 +12,18 @@ mod traits;
 
 pub use self::core::{armv6m, armv7a, armv7m, armv8a, armv8m, Dump};
 use self::{
-    ap::{AccessPort, AccessPortError},
+    ap::AccessPortError,
     communication_interface::RegisterParseError,
     dp::DebugPortError,
     memory::romtable::RomTableError,
     sequences::ArmDebugSequenceError,
     {armv7a::Armv7aError, armv8a::Armv8aError},
 };
-use crate::{core::memory_mapped_registers::RegisterAddressOutOfBounds, probe::DebugProbeError};
+use crate::{
+    core::memory_mapped_registers::RegisterAddressOutOfBounds,
+    memory::{InvalidDataLengthError, MemoryNotAlignedError},
+    probe::DebugProbeError,
+};
 pub use communication_interface::{
     ApInformation, ArmChipInfo, ArmCommunicationInterface, ArmProbeInterface, DapError,
     MemoryApInformation, Register,
@@ -28,26 +32,21 @@ pub use swo::{SwoAccess, SwoConfig, SwoMode, SwoReader};
 pub use traits::*;
 
 /// ARM-specific errors
-#[derive(Debug, thiserror::Error)]
+#[derive(Debug, thiserror::Error, docsplay::Display)]
 pub enum ArmError {
-    /// The operation requires a specific architecture.
-    #[error("The operation requires one of the following architectures: {0:?}.")]
+    /// The operation requires one of the following architectures: {0:?}
     ArchitectureRequired(&'static [&'static str]),
 
-    /// A timeout occurred during an operation
-    #[error("Timeout occurred during operation.")]
+    /// A timeout occurred during an operation.
     Timeout,
 
     /// The address is too large for the 32 bit address space.
-    #[error("Address is not in 32 bit address space.")]
     AddressOutOf32BitAddressSpace,
 
     /// The current target device is not an ARM device.
-    #[error("Target device is not an ARM device.")]
     NoArmTarget,
 
-    /// Error using a specific AP.
-    #[error("Error using access port {address:?}.")]
+    /// Error using access port {address:?}.
     AccessPort {
         /// Address of the access port
         address: FullyQualifiedApAddress,
@@ -56,131 +55,101 @@ pub enum ArmError {
     },
 
     /// An error occurred while using a debug port.
-    #[error("Error using a debug port.")]
     DebugPort(#[from] DebugPortError),
 
     /// The core has to be halted for the operation, but was not.
-    #[error("The core needs to be halted for this operation but was not")]
     CoreNotHalted,
 
     /// Performing certain operations (e.g device unlock or Chip-Erase) can leave the device in a
     /// state that requires a probe re-attach to resolve.
-    #[error("Probe and device internal state mismatch. A probe re-attach is required.")]
     ReAttachRequired,
 
-    /// An operation was not performed because the required permissions were not given.
+    /// An operation could not be performed because it lacked the permission to do so: {0}
     ///
     /// This can for example happen when the core is locked and needs to be erased to be unlocked.
     /// Then the correct permission needs to be given to automatically unlock the core to prevent
     /// accidental erases.
-    #[error("An operation could not be performed because it lacked the permission to do so: {0}.")]
+    #[ignore_extra_doc_attributes]
     MissingPermissions(String),
 
     /// An error occurred in the communication with an access port or debug port.
-    #[error("An error occurred in the communication with an access port or debug port.")]
     Dap(#[from] DapError),
 
     /// The debug probe encountered an error.
-    #[error("The debug probe encountered an error.")]
     Probe(#[from] DebugProbeError),
 
-    /// The given register address to perform an access on was not memory aligned.
-    /// Make sure it is aligned to the size of the access (`address & access_size == 0`).
-    #[error(
-        "Failed to access address 0x{address:08x} as it is not aligned to \
-        the requirement of {alignment} bytes for this platform and API call."
-    )]
-    MemoryNotAligned {
-        /// The address of the register.
-        address: u64,
-        /// The required alignment in bytes (address increments).
-        alignment: usize,
-    },
+    /// Failed to access address 0x{0.address:08x} as it is not aligned to the requirement of
+    /// {0.alignment} bytes for this platform and API call.
+    MemoryNotAligned(#[from] MemoryNotAlignedError),
 
     /// A region outside of the AP address space was accessed.
-    #[error("Out of bounds access.")]
     OutOfBounds,
 
-    /// The requested memory transfer width is not supported on the current core.
-    #[error("{0} bit is not a supported memory transfer width on the current core.")]
+    /// {0} bit is not a supported memory transfer width on the current core.
     UnsupportedTransferWidth(usize),
 
-    /// The AP with the specified address does not exist.
-    #[error("The AP with address {0:?} does not exist.")]
+    /// The AP with address {0:?} does not exist.
     ApDoesNotExist(FullyQualifiedApAddress),
 
     /// The AP has the wrong version for the operation.
-    #[error("Wrong AP version.")]
     WrongApVersion,
 
     /// The AP has the wrong type for the operation.
-    #[error("Wrong AP type.")]
     WrongApType,
 
-    /// It is not possible to create a breakpoint a the given address.
-    #[error(
-        "Unable to create a breakpoint at address {0:#010X}. \
-        Hardware breakpoints are only supported at addresses < 0x2000_0000."
-    )]
+    /// Unable to create a breakpoint at address {0:#010X}. Hardware breakpoints are only supported
+    /// at addresses < 0x2000_0000.
     UnsupportedBreakpointAddress(u32),
 
-    #[error("Armv8a specific error.")]
     /// ARMv8a specific error occurred.
     Armv8a(#[from] Armv8aError),
 
     /// ARMv7a specific error occurred.
-    #[error("Armv7a specific error.")]
     Armv7a(#[from] Armv7aError),
 
     /// Error occurred in a debug sequence.
-    #[error("Error occurred in a debug sequence.")]
     DebugSequence(#[from] ArmDebugSequenceError),
 
     /// Tracing has not been configured.
-    #[error("Tracing has not been configured.")]
     TracingUnconfigured,
 
     /// Error parsing a register.
-    #[error("Error parsing a register.")]
     RegisterParse(#[from] RegisterParseError),
 
     /// Error reading ROM table.
-    #[error("Error reading ROM table.")]
     RomTable(#[source] RomTableError),
 
-    /// Failed to erase chip
-    #[error("Chip erase failed.")]
+    /// Failed to erase chip.
     ChipEraseFailed,
 
-    /// The operation requires a specific extension.
-    #[error("The operation requires the following extension(s): {0:?}.")]
+    /// The operation requires the following extension(s): {0:?}.
     ExtensionRequired(&'static [&'static str]),
 
     /// An error occurred while calculating the address of a register.
-    #[error("Error calculating register address.")]
     RegisterAddressOutOfBounds(#[from] RegisterAddressOutOfBounds),
 
-    /// Some required functionality is not implemented.
-    #[error("Not implemented: {0}")]
+    /// Some required functionality is not implemented: {0}
     NotImplemented(&'static str),
 
-    /// Another ARM error occurred
-    #[error("{0}")]
+    /// Invalid data length error: {0}
+    InvalidDataLength(#[from] InvalidDataLengthError),
+
+    /// Another ARM error occurred: {0}
     Other(String),
 }
 
 impl ArmError {
     /// Constructs [`ArmError::MemoryNotAligned`] from the address and the required alignment.
-    pub fn from_access_port(err: AccessPortError, ap: &impl AccessPort) -> Self {
+    pub fn from_access_port(err: AccessPortError, ap_address: &FullyQualifiedApAddress) -> Self {
         ArmError::AccessPort {
-            address: ap.ap_address().clone(),
+            address: ap_address.clone(),
             source: err,
         }
     }
 
     /// Constructs a [`ArmError::MemoryNotAligned`] from the address and the required alignment.
     pub fn alignment_error(address: u64, alignment: usize) -> Self {
-        ArmError::MemoryNotAligned { address, alignment }
+        ArmError::MemoryNotAligned(MemoryNotAlignedError { address, alignment })
     }
 }
 
diff --git a/probe-rs/src/architecture/arm/sequences.rs b/probe-rs/src/architecture/arm/sequences.rs
index 4aff7d5d..1f1d2925 100644
--- a/probe-rs/src/architecture/arm/sequences.rs
+++ b/probe-rs/src/architecture/arm/sequences.rs
@@ -20,17 +20,18 @@ use crate::{
 };
 
 use super::{
-    ap::{AccessPortError, MemoryAp},
+    ap::AccessPortError,
     armv6m::Demcr,
     communication_interface::{DapProbe, Initialized},
     component::{TraceFunnel, TraceSink},
     core::cortex_m::Dhcsr,
     dp::{Abort, Ctrl, DebugPortError, DpAccess, Select, DPIDR},
     memory::{
-        adi_v5_memory_interface::ArmProbe,
         romtable::{CoresightComponent, PeripheralType},
+        ArmMemoryInterface,
     },
-    ArmCommunicationInterface, ArmError, DpAddress, Pins, PortType, Register,
+    ArmCommunicationInterface, ArmError, DpAddress, FullyQualifiedApAddress, Pins, PortType,
+    Register,
 };
 
 /// An error occurred when executing an ARM debug sequence
@@ -70,7 +71,7 @@ impl ArmDebugSequence for DefaultArmSequence {}
 
 /// ResetCatchSet for Cortex-A devices
 fn armv7a_reset_catch_set(
-    core: &mut dyn ArmProbe,
+    core: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7a_debug_regs::Dbgprcr;
@@ -90,7 +91,7 @@ fn armv7a_reset_catch_set(
 
 /// ResetCatchClear for Cortex-A devices
 fn armv7a_reset_catch_clear(
-    core: &mut dyn ArmProbe,
+    core: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7a_debug_regs::Dbgprcr;
@@ -109,7 +110,7 @@ fn armv7a_reset_catch_clear(
 }
 
 fn armv7a_reset_system(
-    interface: &mut dyn ArmProbe,
+    interface: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7a_debug_regs::{Dbgprcr, Dbgprsr};
@@ -139,7 +140,10 @@ fn armv7a_reset_system(
 }
 
 /// DebugCoreStart for v7 Cortex-A devices
-fn armv7a_core_start(core: &mut dyn ArmProbe, debug_base: Option<u64>) -> Result<(), ArmError> {
+fn armv7a_core_start(
+    core: &mut dyn ArmMemoryInterface,
+    debug_base: Option<u64>,
+) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7a_debug_regs::{Dbgdsccr, Dbgdscr, Dbgdsmcr, Dbglar};
 
     let debug_base =
@@ -178,7 +182,7 @@ fn armv7a_core_start(core: &mut dyn ArmProbe, debug_base: Option<u64>) -> Result
 
 /// ResetCatchSet for ARMv8-A devices
 fn armv8a_reset_catch_set(
-    core: &mut dyn ArmProbe,
+    core: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv8a_debug_regs::Edecr;
@@ -198,7 +202,7 @@ fn armv8a_reset_catch_set(
 
 /// ResetCatchClear for ARMv8-a devices
 fn armv8a_reset_catch_clear(
-    core: &mut dyn ArmProbe,
+    core: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv8a_debug_regs::Edecr;
@@ -217,7 +221,7 @@ fn armv8a_reset_catch_clear(
 }
 
 fn armv8a_reset_system(
-    interface: &mut dyn ArmProbe,
+    interface: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
 ) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv8a_debug_regs::{Edprcr, Edprsr};
@@ -248,7 +252,7 @@ fn armv8a_reset_system(
 
 /// DebugCoreStart for v8 Cortex-A devices
 fn armv8a_core_start(
-    core: &mut dyn ArmProbe,
+    core: &mut dyn ArmMemoryInterface,
     debug_base: Option<u64>,
     cti_base: Option<u64>,
 ) -> Result<(), ArmError> {
@@ -316,7 +320,7 @@ fn armv8a_core_start(
 }
 
 /// DebugCoreStart for Cortex-M devices
-pub(crate) fn cortex_m_core_start(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
+pub(crate) fn cortex_m_core_start(core: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7m::Dhcsr;
 
     let current_dhcsr = Dhcsr(core.read_word_32(Dhcsr::get_mmio_address())?);
@@ -338,7 +342,7 @@ pub(crate) fn cortex_m_core_start(core: &mut dyn ArmProbe) -> Result<(), ArmErro
 }
 
 /// ResetCatchClear for Cortex-M devices
-fn cortex_m_reset_catch_clear(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn cortex_m_reset_catch_clear(core: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7m::Demcr;
 
     // Clear reset catch bit
@@ -350,7 +354,7 @@ fn cortex_m_reset_catch_clear(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
 }
 
 /// ResetCatchSet for Cortex-M devices
-fn cortex_m_reset_catch_set(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn cortex_m_reset_catch_set(core: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7m::{Demcr, Dhcsr};
 
     // Request halt after reset
@@ -366,7 +370,7 @@ fn cortex_m_reset_catch_set(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
 }
 
 /// ResetSystem for Cortex-M devices
-fn cortex_m_reset_system(interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn cortex_m_reset_system(interface: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     use crate::architecture::arm::core::armv7m::{Aircr, Dhcsr};
 
     let mut aircr = Aircr(0);
@@ -422,7 +426,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     ///
     /// [ARM SVD Debug Description]: https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/debug_description.html#resetHardwareDeassert
     #[doc(alias = "ResetHardwareDeassert")]
-    fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn reset_hardware_deassert(&self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         let mut n_reset = Pins(0);
         n_reset.set_nreset(true);
         let n_reset = n_reset.0 as u32;
@@ -640,12 +644,12 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     fn debug_core_start(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        core_ap: MemoryAp,
+        core_ap: &FullyQualifiedApAddress,
         core_type: CoreType,
         debug_base: Option<u64>,
         cti_base: Option<u64>,
     ) -> Result<(), ArmError> {
-        let mut core = interface.memory_interface(&core_ap)?;
+        let mut core = interface.memory_interface(core_ap)?;
 
         // Dispatch based on core type (Cortex-A vs M)
         match core_type {
@@ -666,7 +670,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     #[doc(alias = "ResetCatchSet")]
     fn reset_catch_set(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         core_type: CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -689,7 +693,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     #[doc(alias = "ResetCatchClear")]
     fn reset_catch_clear(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         core_type: CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -742,7 +746,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     #[doc(alias = "ResetSystem")]
     fn reset_system(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         core_type: CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -767,7 +771,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     fn debug_device_unlock(
         &self,
         _interface: &mut dyn ArmProbeInterface,
-        _default_ap: &MemoryAp,
+        _default_ap: &FullyQualifiedApAddress,
         _permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         tracing::debug!("debug_device_unlock - empty by default");
@@ -779,7 +783,10 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     ///
     /// [ARM SVD Debug Description]: https://open-cmsis-pack.github.io/Open-CMSIS-Pack-Spec/main/html/debug_description.htmll#recoverSupportStart
     #[doc(alias = "RecoverSupportStart")]
-    fn recover_support_start(&self, _interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn recover_support_start(
+        &self,
+        _interface: &mut dyn ArmMemoryInterface,
+    ) -> Result<(), ArmError> {
         // Empty by default
         Ok(())
     }
@@ -792,7 +799,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
     #[doc(alias = "DebugCoreStop")]
     fn debug_core_stop(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         core_type: CoreType,
     ) -> Result<(), ArmError> {
         if core_type.is_cortex_m() {
diff --git a/probe-rs/src/config/registry.rs b/probe-rs/src/config/registry.rs
index 9bc4dea4..5ce57498 100644
--- a/probe-rs/src/config/registry.rs
+++ b/probe-rs/src/config/registry.rs
@@ -4,7 +4,7 @@ use super::{Chip, ChipFamily, ChipInfo, Core, Target, TargetDescriptionSource};
 use crate::config::CoreType;
 use once_cell::sync::Lazy;
 use parking_lot::{RwLock, RwLockReadGuard};
-use probe_rs_target::{BinaryFormat, CoreAccessOptions, RiscvCoreAccessOptions};
+use probe_rs_target::{CoreAccessOptions, RiscvCoreAccessOptions};
 use std::collections::HashMap;
 use std::io::Read;
 use std::ops::Deref;
@@ -110,7 +110,7 @@ fn add_generic_targets(vec: &mut Vec<ChipFamily>) {
                 flash_algorithms: vec![],
                 rtt_scan_ranges: None,
                 jtag: None,
-                default_binary_format: Some(BinaryFormat::Raw),
+                default_binary_format: None,
             }],
             flash_algorithms: vec![],
             source: TargetDescriptionSource::Generic,
@@ -324,12 +324,9 @@ impl Registry {
     {
         let family: ChipFamily = serde_yaml::from_reader(yaml_reader)?;
 
-        if let Err(error) = family.validate() {
-            return Err(RegistryError::InvalidChipFamilyDefinition(
-                Box::new(family),
-                error,
-            ));
-        };
+        validate_family(&family).map_err(|error| {
+            RegistryError::InvalidChipFamilyDefinition(Box::new(family.clone()), error)
+        })?;
 
         let family_name = family.name.clone();
 
@@ -437,6 +434,18 @@ fn match_name_prefix(pattern: &str, name: &str) -> bool {
     true
 }
 
+fn validate_family(family: &ChipFamily) -> Result<(), String> {
+    family.validate()?;
+
+    // We can't have this in the `validate` method as we need information that is not available in
+    // probe-rs-target.
+    for target in family.variants() {
+        crate::flashing::FormatKind::from_optional(target.default_binary_format.as_deref())?;
+    }
+
+    Ok(())
+}
+
 #[cfg(test)]
 mod tests {
     use crate::flashing::FlashAlgorithm;
@@ -513,7 +522,7 @@ mod tests {
             .iter()
             .flat_map(|family| {
                 // Validate all chip descriptors.
-                family.validate().unwrap();
+                validate_family(family).unwrap();
 
                 // Make additional checks by creating a target for each chip.
                 family
diff --git a/probe-rs/src/config/target.rs b/probe-rs/src/config/target.rs
index fd282ddb..db49d673 100644
--- a/probe-rs/src/config/target.rs
+++ b/probe-rs/src/config/target.rs
@@ -3,7 +3,6 @@ use crate::flashing::FlashLoader;
 use crate::{
     architecture::{
         arm::{
-            ap::MemoryAp,
             sequences::{ArmDebugSequence, DefaultArmSequence},
             DpAddress, FullyQualifiedApAddress,
         },
@@ -12,7 +11,7 @@ use crate::{
     },
     rtt::ScanRegion,
 };
-use probe_rs_target::{Architecture, BinaryFormat, Chip, ChipFamily, Jtag};
+use probe_rs_target::{Architecture, Chip, ChipFamily, Jtag};
 use std::sync::Arc;
 
 /// This describes a complete target with a fixed chip model and variant.
@@ -42,7 +41,7 @@ pub struct Target {
     /// the number devices in the scan chain and their ir lengths.
     pub jtag: Option<Jtag>,
     /// The default executable format for the target.
-    pub default_format: BinaryFormat,
+    pub default_format: Option<String>,
 }
 
 impl std::fmt::Debug for Target {
@@ -109,7 +108,7 @@ impl Target {
             debug_sequence,
             rtt_scan_regions,
             jtag: chip.jtag.clone(),
-            default_format: chip.default_binary_format.clone().unwrap_or_default(),
+            default_format: chip.default_binary_format.clone(),
         }
     }
 
@@ -236,20 +235,20 @@ pub enum DebugSequence {
 pub(crate) trait CoreExt {
     // Retrieve the Coresight MemoryAP which should be used to
     // access the core, if available.
-    fn memory_ap(&self) -> Option<MemoryAp>;
+    fn memory_ap(&self) -> Option<FullyQualifiedApAddress>;
 }
 
 impl CoreExt for Core {
-    fn memory_ap(&self) -> Option<MemoryAp> {
+    fn memory_ap(&self) -> Option<FullyQualifiedApAddress> {
         match &self.core_access_options {
             probe_rs_target::CoreAccessOptions::Arm(options) => {
-                Some(MemoryAp::new(FullyQualifiedApAddress::v1_with_dp(
+                Some(FullyQualifiedApAddress::v1_with_dp(
                     match options.psel {
                         0 => DpAddress::Default,
                         x => DpAddress::Multidrop(x),
                     },
                     options.ap,
-                )))
+                ))
             }
             probe_rs_target::CoreAccessOptions::Riscv(_) => None,
             probe_rs_target::CoreAccessOptions::Xtensa(_) => None,
diff --git a/probe-rs/src/core/core_state.rs b/probe-rs/src/core/core_state.rs
index cc308b82..bf878c15 100644
--- a/probe-rs/src/core/core_state.rs
+++ b/probe-rs/src/core/core_state.rs
@@ -1,7 +1,6 @@
 use crate::{
     architecture::{
         arm::{
-            ap::MemoryAp,
             core::{CortexAState, CortexMState},
             ArmProbeInterface, DpAddress, FullyQualifiedApAddress,
         },
@@ -125,7 +124,7 @@ impl CombinedCoreState {
             // Enable debug mode
             sequence.debug_core_start(
                 interface,
-                self.arm_memory_ap(),
+                &self.arm_memory_ap(),
                 self.core_type(),
                 options.debug_base,
                 options.cti_base,
@@ -233,7 +232,7 @@ impl CombinedCoreState {
     /// ## Panic
     ///
     /// This function will panic if the core is not an ARM core and doesn't have a memory AP
-    pub(crate) fn arm_memory_ap(&self) -> MemoryAp {
+    pub(crate) fn arm_memory_ap(&self) -> FullyQualifiedApAddress {
         self.core_state.memory_ap()
     }
 }
@@ -253,7 +252,7 @@ impl CoreState {
         }
     }
 
-    pub(crate) fn memory_ap(&self) -> MemoryAp {
+    pub(crate) fn memory_ap(&self) -> FullyQualifiedApAddress {
         let ResolvedCoreOptions::Arm { options, .. } = &self.core_access_options else {
             unreachable!(
                 "The stored core state is not compatible with the ARM architecture. \
@@ -266,9 +265,7 @@ impl CoreState {
             x => DpAddress::Multidrop(x),
         };
 
-        let ap = FullyQualifiedApAddress::v1_with_dp(dp, options.ap);
-
-        MemoryAp::new(ap)
+        FullyQualifiedApAddress::v1_with_dp(dp, options.ap)
     }
 }
 
diff --git a/probe-rs/src/error.rs b/probe-rs/src/error.rs
index d8582e58..9177f888 100644
--- a/probe-rs/src/error.rs
+++ b/probe-rs/src/error.rs
@@ -3,6 +3,7 @@ use crate::architecture::riscv::communication_interface::RiscvError;
 use crate::architecture::xtensa::communication_interface::XtensaError;
 use crate::config::RegistryError;
 use crate::core::memory_mapped_registers::RegisterAddressOutOfBounds;
+use crate::memory::{InvalidDataLengthError, MemoryNotAlignedError};
 use crate::probe::DebugProbeError;
 
 /// The overarching error type which contains all possible errors as variants.
@@ -47,13 +48,12 @@ pub enum Error {
     /// A timeout occurred.
     // TODO: Errors below should be core specific
     Timeout,
-    /// Memory access to address {address:#X?} was not aligned to {alignment} bytes.
-    MemoryNotAligned {
-        /// The address of the register.
-        address: u64,
-        /// The required alignment in bytes (address increments).
-        alignment: usize,
-    },
+    /// Memory access to address {0.address:#X?} was not aligned to {0.alignment} bytes.
+    #[error(transparent)]
+    MemoryNotAligned(#[from] MemoryNotAlignedError),
+    /// The data buffer had an invalid length.
+    #[error(transparent)]
+    InvalidDataLength(#[from] InvalidDataLengthError),
     /// Failed to write CPU register {register}.
     WriteRegister {
         /// The name of the register that was tried to be written.
@@ -74,9 +74,8 @@ impl From<ArmError> for Error {
     fn from(value: ArmError) -> Self {
         match value {
             ArmError::Timeout => Error::Timeout,
-            ArmError::MemoryNotAligned { address, alignment } => {
-                Error::MemoryNotAligned { address, alignment }
-            }
+            ArmError::MemoryNotAligned(e) => Error::MemoryNotAligned(e),
+            ArmError::InvalidDataLength(e) => Error::InvalidDataLength(e),
             other => Error::Arm(other),
         }
     }
diff --git a/probe-rs/src/flashing/download.rs b/probe-rs/src/flashing/download.rs
index bdfbc10d..d7634cfa 100644
--- a/probe-rs/src/flashing/download.rs
+++ b/probe-rs/src/flashing/download.rs
@@ -15,7 +15,7 @@ use super::*;
 use crate::session::Session;
 
 /// Extended options for flashing a binary file.
-#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
+#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Default)]
 pub struct BinOptions {
     /// The address in memory where the binary will be put at.
     pub base_address: Option<u64>,
@@ -34,11 +34,11 @@ pub struct IdfOptions {
 
 /// A finite list of all the available binary formats probe-rs understands.
 #[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
-pub enum Format {
+pub enum FormatKind {
     /// Marks a file in binary format. This means that the file contains the contents of the flash 1:1.
     /// [BinOptions] can be used to define the location in flash where the file contents should be put at.
     /// Additionally using the same config struct, you can skip the first N bytes of the binary file to have them not put into the flash.
-    Bin(BinOptions),
+    Bin,
     /// Marks a file in [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format.
     Hex,
     /// Marks a file in the [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) format.
@@ -46,29 +46,69 @@ pub enum Format {
     Elf,
     /// Marks a file in the [ESP-IDF bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/app_image_format.html#app-image-structures) format.
     /// Use [IdfOptions] to configure flashing.
-    Idf(IdfOptions),
+    Idf,
     /// Marks a file in the [UF2](https://github.com/microsoft/uf2) format.
     Uf2,
 }
 
-impl FromStr for Format {
+impl FormatKind {
+    /// Creates a new Format from an optional string.
+    ///
+    /// If the string is `None`, the default format is returned.
+    pub fn from_optional(s: Option<&str>) -> Result<Self, String> {
+        match s {
+            Some(format) => Self::from_str(format),
+            None => Ok(Self::default()),
+        }
+    }
+}
+
+impl FromStr for FormatKind {
     type Err = String;
 
     fn from_str(s: &str) -> Result<Self, Self::Err> {
         match &s.to_lowercase()[..] {
-            "bin" | "binary" => Ok(Format::Bin(BinOptions {
-                base_address: None,
-                skip: 0,
-            })),
-            "idf" | "esp-idf" => Ok(Format::Idf(Default::default())),
-            "hex" | "ihex" | "intelhex" => Ok(Format::Hex),
-            "elf" => Ok(Format::Elf),
-            "uf2" => Ok(Format::Uf2),
+            "bin" | "binary" => Ok(Self::Bin),
+            "hex" | "ihex" | "intelhex" => Ok(Self::Hex),
+            "elf" => Ok(Self::Elf),
+            "uf2" => Ok(Self::Uf2),
+            "idf" | "esp-idf" | "espidf" => Ok(Self::Idf),
             _ => Err(format!("Format '{s}' is unknown.")),
         }
     }
 }
 
+/// A finite list of all the available binary formats probe-rs understands.
+#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
+pub enum Format {
+    /// Marks a file in binary format. This means that the file contains the contents of the flash 1:1.
+    /// [BinOptions] can be used to define the location in flash where the file contents should be put at.
+    /// Additionally using the same config struct, you can skip the first N bytes of the binary file to have them not put into the flash.
+    Bin(BinOptions),
+    /// Marks a file in [Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX) format.
+    Hex,
+    /// Marks a file in the [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) format.
+    #[default]
+    Elf,
+    /// Marks a file in the [ESP-IDF bootloader](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/app_image_format.html#app-image-structures) format.
+    /// Use [IdfOptions] to configure flashing.
+    Idf(IdfOptions),
+    /// Marks a file in the [UF2](https://github.com/microsoft/uf2) format.
+    Uf2,
+}
+
+impl From<FormatKind> for Format {
+    fn from(kind: FormatKind) -> Self {
+        match kind {
+            FormatKind::Bin => Format::Bin(BinOptions::default()),
+            FormatKind::Hex => Format::Hex,
+            FormatKind::Elf => Format::Elf,
+            FormatKind::Uf2 => Format::Uf2,
+            FormatKind::Idf => Format::Idf(IdfOptions::default()),
+        }
+    }
+}
+
 /// A finite list of all the errors that can occur when flashing a given file.
 ///
 /// This includes corrupt file issues,
@@ -196,10 +236,10 @@ pub fn build_loader(
 /// This will ensure that memory boundaries are honored and does unlocking, erasing and programming of the flash for you.
 ///
 /// If you are looking for more options, have a look at [download_file_with_options].
-pub fn download_file<P: AsRef<Path>>(
+pub fn download_file(
     session: &mut Session,
-    path: P,
-    format: Format,
+    path: impl AsRef<Path>,
+    format: impl Into<Format>,
 ) -> Result<(), FileDownloadError> {
     download_file_with_options(session, path, format, DownloadOptions::default())
 }
@@ -209,13 +249,13 @@ pub fn download_file<P: AsRef<Path>>(
 /// This will ensure that memory boundaries are honored and does unlocking, erasing and programming of the flash for you.
 ///
 /// If you are looking for a simple version without many options, have a look at [download_file].
-pub fn download_file_with_options<P: AsRef<Path>>(
+pub fn download_file_with_options(
     session: &mut Session,
-    path: P,
-    format: Format,
+    path: impl AsRef<Path>,
+    format: impl Into<Format>,
     options: DownloadOptions,
 ) -> Result<(), FileDownloadError> {
-    let loader = build_loader(session, path, format, None)?;
+    let loader = build_loader(session, path, format.into(), None)?;
 
     loader
         .commit(session, options)
@@ -349,59 +389,39 @@ pub(super) fn extract_from_elf(
 mod tests {
     use std::str::FromStr;
 
-    use super::{BinOptions, Format};
+    use super::FormatKind;
 
     #[test]
     fn parse_format() {
-        assert_eq!(Format::from_str("hex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("Hex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("Ihex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("IHex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("iHex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("IntelHex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("intelhex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("intelHex"), Ok(Format::Hex));
-        assert_eq!(Format::from_str("Intelhex"), Ok(Format::Hex));
-        assert_eq!(
-            Format::from_str("bin"),
-            Ok(Format::Bin(BinOptions {
-                base_address: None,
-                skip: 0
-            }))
-        );
-        assert_eq!(
-            Format::from_str("Bin"),
-            Ok(Format::Bin(BinOptions {
-                base_address: None,
-                skip: 0
-            }))
-        );
-        assert_eq!(
-            Format::from_str("binary"),
-            Ok(Format::Bin(BinOptions {
-                base_address: None,
-                skip: 0
-            }))
-        );
-        assert_eq!(
-            Format::from_str("Binary"),
-            Ok(Format::Bin(BinOptions {
-                base_address: None,
-                skip: 0
-            }))
-        );
-        assert_eq!(Format::from_str("Elf"), Ok(Format::Elf));
-        assert_eq!(Format::from_str("elf"), Ok(Format::Elf));
+        assert_eq!(FormatKind::from_str("hex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("Hex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("Ihex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("IHex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("iHex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("IntelHex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("intelhex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("intelHex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("Intelhex"), Ok(FormatKind::Hex));
+        assert_eq!(FormatKind::from_str("bin"), Ok(FormatKind::Bin));
+        assert_eq!(FormatKind::from_str("Bin"), Ok(FormatKind::Bin));
+        assert_eq!(FormatKind::from_str("binary"), Ok(FormatKind::Bin));
+        assert_eq!(FormatKind::from_str("Binary"), Ok(FormatKind::Bin));
+        assert_eq!(FormatKind::from_str("Elf"), Ok(FormatKind::Elf));
+        assert_eq!(FormatKind::from_str("elf"), Ok(FormatKind::Elf));
+        assert_eq!(FormatKind::from_str("idf"), Ok(FormatKind::Idf));
+        assert_eq!(FormatKind::from_str("espidf"), Ok(FormatKind::Idf));
+        assert_eq!(FormatKind::from_str("esp-idf"), Ok(FormatKind::Idf));
+        assert_eq!(FormatKind::from_str("ESP-IDF"), Ok(FormatKind::Idf));
         assert_eq!(
-            Format::from_str("elfbin"),
+            FormatKind::from_str("elfbin"),
             Err("Format 'elfbin' is unknown.".to_string())
         );
         assert_eq!(
-            Format::from_str(""),
+            FormatKind::from_str(""),
             Err("Format '' is unknown.".to_string())
         );
         assert_eq!(
-            Format::from_str("asdasdf"),
+            FormatKind::from_str("asdasdf"),
             Err("Format 'asdasdf' is unknown.".to_string())
         );
     }
diff --git a/probe-rs/src/memory/mod.rs b/probe-rs/src/memory/mod.rs
index c5551e34..7ef86e8d 100644
--- a/probe-rs/src/memory/mod.rs
+++ b/probe-rs/src/memory/mod.rs
@@ -2,8 +2,37 @@ use crate::error::Error;
 
 use scroll::Pread;
 
+/// {function_name} was called with data length that is not a multiple of {alignment}
+#[derive(Debug, thiserror::Error, docsplay::Display)]
+pub struct InvalidDataLengthError {
+    /// Name of the function that caused the error.
+    pub function_name: &'static str,
+    /// The alignment required on the data length.
+    pub alignment: usize,
+}
+impl InvalidDataLengthError {
+    pub fn new(function_name: &'static str, alignment: usize) -> Self {
+        Self {
+            function_name,
+            alignment,
+        }
+    }
+}
+
+/// Memory access to address {address:#X?} was not aligned to {alignment} bytes.
+#[derive(Debug, thiserror::Error, docsplay::Display)]
+pub struct MemoryNotAlignedError {
+    /// The address of the register.
+    pub address: u64,
+    /// The required alignment in bytes (address increments).
+    pub alignment: usize,
+}
+
 /// An interface to be implemented for drivers that allow target memory access.
-pub trait MemoryInterface {
+pub trait MemoryInterface<ERR = Error>
+where
+    ERR: std::error::Error + From<InvalidDataLengthError> + From<MemoryNotAlignedError>,
+{
     /// Does this interface support native 64-bit wide accesses
     ///
     /// If false all 64-bit operations may be split into 32 or 8 bit operations.
@@ -15,60 +44,74 @@ pub trait MemoryInterface {
     ///
     /// The address where the read should be performed at has to be a multiple of 8.
     /// Returns `AccessPortError::MemoryNotAligned` if this does not hold true.
-    fn read_word_64(&mut self, address: u64) -> Result<u64, Error>;
+    fn read_word_64(&mut self, address: u64) -> Result<u64, ERR> {
+        let mut word = 0;
+        self.read_64(address, std::slice::from_mut(&mut word))?;
+        Ok(word)
+    }
 
     /// Read a 32bit word of at `address`.
     ///
     /// The address where the read should be performed at has to be a multiple of 4.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_word_32(&mut self, address: u64) -> Result<u32, Error>;
+    fn read_word_32(&mut self, address: u64) -> Result<u32, ERR> {
+        let mut word = 0;
+        self.read_32(address, std::slice::from_mut(&mut word))?;
+        Ok(word)
+    }
 
     /// Read a 16bit word of at `address`.
     ///
     /// The address where the read should be performed at has to be a multiple of 2.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_word_16(&mut self, address: u64) -> Result<u16, Error>;
+    fn read_word_16(&mut self, address: u64) -> Result<u16, ERR> {
+        let mut word = 0;
+        self.read_16(address, std::slice::from_mut(&mut word))?;
+        Ok(word)
+    }
 
     /// Read an 8bit word of at `address`.
-    fn read_word_8(&mut self, address: u64) -> Result<u8, Error>;
+    fn read_word_8(&mut self, address: u64) -> Result<u8, ERR> {
+        let mut word = 0;
+        self.read_8(address, std::slice::from_mut(&mut word))?;
+        Ok(word)
+    }
 
     /// Read a block of 64bit words at `address`.
     ///
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 8.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error>;
+    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), ERR>;
 
     /// Read a block of 32bit words at `address`.
     ///
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 4.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error>;
+    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), ERR>;
 
     /// Read a block of 16bit words at `address`.
     ///
     /// The number of words read is `data.len()`.
     /// The address where the read should be performed at has to be a multiple of 2.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error>;
+    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), ERR>;
 
     /// Read a block of 8bit words at `address`.
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error>;
+    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), ERR>;
 
     /// Reads bytes using 64 bit memory access.
     ///
     /// The address where the read should be performed at has to be a multiple of 8.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_mem_64bit(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
+    fn read_mem_64bit(&mut self, address: u64, data: &mut [u8]) -> Result<(), ERR> {
         // Default implementation uses `read_64`, then converts u64 values back
         // to bytes. Assumes target is little endian. May be overridden to
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 8 != 0 {
-            return Err(Error::Other(
-                "Call to read_mem_64bit with data.len() not a multiple of 8".to_string(),
-            ));
+            return Err(InvalidDataLengthError::new("read_mem_64bit", 8).into());
         }
         let mut buffer = vec![0u64; data.len() / 8];
         self.read_64(address, &mut buffer)?;
@@ -82,15 +125,13 @@ pub trait MemoryInterface {
     ///
     /// The address where the read should be performed at has to be a multiple of 4.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn read_mem_32bit(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
+    fn read_mem_32bit(&mut self, address: u64, data: &mut [u8]) -> Result<(), ERR> {
         // Default implementation uses `read_32`, then converts u32 values back
         // to bytes. Assumes target is little endian. May be overridden to
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 4 != 0 {
-            return Err(Error::Other(
-                "Call to read_mem_32bit with data.len() not a multiple of 4".to_string(),
-            ));
+            return Err(InvalidDataLengthError::new("read_mem_32bit", 4).into());
         }
         let mut buffer = vec![0u32; data.len() / 4];
         self.read_32(address, &mut buffer)?;
@@ -110,7 +151,7 @@ pub trait MemoryInterface {
     /// used.
     ///
     ///  Generally faster than `read_8`.
-    fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
+    fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), ERR> {
         if self.supports_native_64bit_access() {
             // Avoid heap allocation and copy if we don't need it.
             self.read_8(address, data)?;
@@ -130,58 +171,64 @@ pub trait MemoryInterface {
     ///
     /// The address where the write should be performed at has to be a multiple of 8.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error>;
+    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), ERR> {
+        self.write_64(address, std::slice::from_ref(&data))
+    }
 
     /// Write a 32bit word at `address`.
     ///
     /// The address where the write should be performed at has to be a multiple of 4.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error>;
+    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), ERR> {
+        self.write_32(address, std::slice::from_ref(&data))
+    }
 
     /// Write a 16bit word at `address`.
     ///
     /// The address where the write should be performed at has to be a multiple of 2.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error>;
+    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), ERR> {
+        self.write_16(address, std::slice::from_ref(&data))
+    }
 
     /// Write an 8bit word at `address`.
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error>;
+    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), ERR> {
+        self.write_8(address, std::slice::from_ref(&data))
+    }
 
     /// Write a block of 64bit words at `address`.
     ///
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 8.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error>;
+    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), ERR>;
 
     /// Write a block of 32bit words at `address`.
     ///
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 4.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error>;
+    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), ERR>;
 
     /// Write a block of 16bit words at `address`.
     ///
     /// The number of words written is `data.len()`.
     /// The address where the write should be performed at has to be a multiple of 2.
     /// Returns [`Error::MemoryNotAligned`] if this does not hold true.
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error>;
+    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), ERR>;
 
     /// Write a block of 8bit words at `address`.
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error>;
+    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), ERR>;
 
     /// Writes bytes using 64 bit memory access. Address must be 64 bit aligned
     /// and data must be an exact multiple of 8.
-    fn write_mem_64bit(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
+    fn write_mem_64bit(&mut self, address: u64, data: &[u8]) -> Result<(), ERR> {
         // Default implementation uses `write_64`, then converts u64 values back
         // to bytes. Assumes target is little endian. May be overridden to
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 8 != 0 {
-            return Err(Error::Other(
-                "Call to read_mem_64bit with data.len() not a multiple of 8".to_string(),
-            ));
+            return Err(InvalidDataLengthError::new("write_mem_64bit", 8).into());
         }
         let mut buffer = vec![0u64; data.len() / 8];
         for (bytes, value) in data.chunks_exact(8).zip(buffer.iter_mut()) {
@@ -196,15 +243,13 @@ pub trait MemoryInterface {
 
     /// Writes bytes using 32 bit memory access. Address must be 32 bit aligned
     /// and data must be an exact multiple of 8.
-    fn write_mem_32bit(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
+    fn write_mem_32bit(&mut self, address: u64, data: &[u8]) -> Result<(), ERR> {
         // Default implementation uses `write_32`, then converts u32 values back
         // to bytes. Assumes target is little endian. May be overridden to
         // provide an implementation that avoids heap allocation and endian
         // conversions. Must be overridden for big endian targets.
         if data.len() % 4 != 0 {
-            return Err(Error::Other(
-                "Call to read_mem_32bit with data.len() not a multiple of 4".to_string(),
-            ));
+            return Err(InvalidDataLengthError::new("write_mem_32bit", 4).into());
         }
         let mut buffer = vec![0u32; data.len() / 4];
         for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
@@ -223,9 +268,9 @@ pub trait MemoryInterface {
     ///
     /// If the target does not support 8-bit aligned access, and `address` is not
     /// aligned on a 32-bit boundary, this function will return a [`Error::MemoryNotAligned`] error.
-    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
+    fn write(&mut self, mut address: u64, mut data: &[u8]) -> Result<(), ERR> {
         let len = data.len();
-        let start_extra_count = 4 - (address % 4) as usize;
+        let start_extra_count = ((4 - (address % 4) as usize) % 4).min(len);
         let end_extra_count = (len - start_extra_count) % 4;
         let inbetween_count = len - start_extra_count - end_extra_count;
         assert!(start_extra_count < 4);
@@ -238,32 +283,43 @@ pub trait MemoryInterface {
         if address % 4 != 0 || len % 4 != 0 {
             // If we do not support 8 bit transfers we have to bail because we can only do 32 bit word aligned transers.
             if !self.supports_8bit_transfers()? {
-                return Err(Error::MemoryNotAligned {
+                return Err(MemoryNotAlignedError {
                     address,
                     alignment: 4,
-                });
+                }
+                .into());
             }
 
             // We first do an 8 bit write of the first < 4 bytes up until the 4 byte aligned boundary.
             self.write_8(address, &data[..start_extra_count])?;
+
+            address += start_extra_count as u64;
+            data = &data[start_extra_count..];
         }
 
-        let mut buffer = vec![0u32; inbetween_count / 4];
-        for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
-            *value = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
+        // Make sure we don't try to do an empty but potentially unaligned write
+        if inbetween_count > 0 {
+            // We do a 32 bit write of the remaining bytes that are 4 byte aligned.
+            let mut buffer = vec![0u32; inbetween_count / 4];
+            for (bytes, value) in data.chunks_exact(4).zip(buffer.iter_mut()) {
+                *value = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
+            }
+            self.write_32(address, &buffer)?;
+
+            address += inbetween_count as u64;
+            data = &data[inbetween_count..];
         }
-        self.write_32(address, &buffer)?;
 
-        // We read the remaining bytes that we did not read yet which is always n < 4.
+        // We write the remaining bytes that we did not write yet which is always n < 4.
         if end_extra_count > 0 {
-            self.write_8(address, &data[..start_extra_count])?;
+            self.write_8(address, &data[..end_extra_count])?;
         }
 
         Ok(())
     }
 
     /// Returns whether the current platform supports native 8bit transfers.
-    fn supports_8bit_transfers(&self) -> Result<bool, Error>;
+    fn supports_8bit_transfers(&self) -> Result<bool, ERR>;
 
     /// Flush any outstanding operations.
     ///
@@ -271,96 +327,7 @@ pub trait MemoryInterface {
     /// 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<(), Error>;
-}
-
-impl<T> MemoryInterface for &mut T
-where
-    T: MemoryInterface,
-{
-    fn supports_native_64bit_access(&mut self) -> bool {
-        (*self).supports_native_64bit_access()
-    }
-
-    fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
-        (*self).read_word_64(address)
-    }
-
-    fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
-        (*self).read_word_32(address)
-    }
-
-    fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
-        (*self).read_word_16(address)
-    }
-
-    fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
-        (*self).read_word_8(address)
-    }
-
-    fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
-        (*self).read_64(address, data)
-    }
-
-    fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
-        (*self).read_32(address, data)
-    }
-
-    fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
-        (*self).read_16(address, data)
-    }
-
-    fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
-        (*self).read_8(address, data)
-    }
-
-    fn read(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
-        (*self).read(address, data)
-    }
-
-    fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
-        (*self).write_word_64(address, data)
-    }
-
-    fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
-        (*self).write_word_32(address, data)
-    }
-
-    fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
-        (*self).write_word_16(address, data)
-    }
-
-    fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
-        (*self).write_word_8(address, data)
-    }
-
-    fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
-        (*self).write_64(address, data)
-    }
-
-    fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
-        (*self).write_32(address, data)
-    }
-
-    fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
-        (*self).write_16(address, data)
-    }
-
-    fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        (*self).write_8(address, data)
-    }
-
-    fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
-        (*self).write(address, data)
-    }
-
-    fn supports_8bit_transfers(&self) -> Result<bool, Error> {
-        MemoryInterface::supports_8bit_transfers(*self)
-    }
-
-    fn flush(&mut self) -> Result<(), Error> {
-        (*self).flush()
-    }
+    fn flush(&mut self) -> Result<(), ERR>;
 }
 
 // Helper functions to validate address space constraints
diff --git a/probe-rs/src/probe/fake_probe.rs b/probe-rs/src/probe/fake_probe.rs
index e8d364f5..a6b906c9 100644
--- a/probe-rs/src/probe/fake_probe.rs
+++ b/probe-rs/src/probe/fake_probe.rs
@@ -5,18 +5,18 @@ use probe_rs_target::ScanChainElement;
 
 use crate::{
     architecture::arm::{
-        ap::{memory_ap::mock::MockMemoryAp, AccessPort, MemoryAp},
+        ap::{memory_ap::mock::MockMemoryAp, MemoryAp},
         armv8m::Dhcsr,
         communication_interface::{
             ArmDebugState, Initialized, SwdSequence, Uninitialized, UninitializedArmProbe,
         },
-        memory::adi_v5_memory_interface::{ADIMemoryInterface, ArmProbe},
+        memory::{adi_v5_memory_interface::ADIMemoryInterface, ArmMemoryInterface},
         sequences::ArmDebugSequence,
         ArmError, ArmProbeInterface, DapAccess, DpAddress, FullyQualifiedApAddress,
         MemoryApInformation, PortType, RawDapAccess, SwoAccess,
     },
     probe::{DebugProbe, DebugProbeError, Probe, WireProtocol},
-    Error, MemoryMappedRegister,
+    Error, MemoryInterface, MemoryMappedRegister,
 };
 
 /// This is a mock probe which can be used for mocking things in tests or for dry runs.
@@ -74,7 +74,7 @@ impl SwdSequence for &mut MockCore {
     }
 }
 
-impl ArmProbe for &mut MockCore {
+impl MemoryInterface<ArmError> for &mut MockCore {
     fn read_8(&mut self, _address: u64, _data: &mut [u8]) -> Result<(), ArmError> {
         todo!()
     }
@@ -172,7 +172,9 @@ impl ArmProbe for &mut MockCore {
     fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
         todo!()
     }
+}
 
+impl ArmMemoryInterface for &mut MockCore {
     fn ap(&mut self) -> MemoryAp {
         todo!()
     }
@@ -483,10 +485,10 @@ impl UninitializedArmProbe for FakeArmInterface<Uninitialized> {
 impl ArmProbeInterface for FakeArmInterface<Initialized> {
     fn memory_interface(
         &mut self,
-        access_port: &MemoryAp,
-    ) -> Result<Box<dyn ArmProbe + '_>, ArmError> {
+        access_port_address: &FullyQualifiedApAddress,
+    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError> {
         let ap_information = MemoryApInformation {
-            address: access_port.ap_address().clone(),
+            address: access_port_address.clone(),
             supports_only_32bit_data_size: false,
             debug_base_address: 0xf000_0000,
             supports_hnonsec: false,
@@ -498,7 +500,7 @@ impl ArmProbeInterface for FakeArmInterface<Initialized> {
         match self.probe.memory_ap {
             MockedAp::MemoryAp(ref mut memory_ap) => {
                 let memory = ADIMemoryInterface::new(memory_ap, ap_information)
-                    .map_err(|e| ArmError::from_access_port(e, access_port))?;
+                    .map_err(|e| ArmError::from_access_port(e, access_port_address))?;
 
                 Ok(Box::new(memory) as _)
             }
@@ -508,7 +510,7 @@ impl ArmProbeInterface for FakeArmInterface<Initialized> {
 
     fn ap_information(
         &mut self,
-        _access_port: &crate::architecture::arm::ap::GenericAp,
+        _access_port: &FullyQualifiedApAddress,
     ) -> Result<&crate::architecture::arm::ApInformation, ArmError> {
         todo!()
     }
diff --git a/probe-rs/src/probe/stlink/mod.rs b/probe-rs/src/probe/stlink/mod.rs
index 7e40a886..c5659dc5 100644
--- a/probe-rs/src/probe/stlink/mod.rs
+++ b/probe-rs/src/probe/stlink/mod.rs
@@ -6,9 +6,9 @@ mod usb_interface;
 
 use self::usb_interface::{StLinkUsb, StLinkUsbDevice};
 use super::{DebugProbe, DebugProbeError, ProbeCreationError, WireProtocol};
-use crate::architecture::arm::ap::GenericAp;
-use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
+use crate::architecture::arm::memory::ArmMemoryInterface;
 use crate::architecture::arm::{valid_32bit_arm_address, ArmError};
+use crate::MemoryInterface;
 use crate::{
     architecture::arm::{
         ap::{valid_access_ports, AccessPort, ApAccess, ApClass, MemoryAp, IDR},
@@ -1493,11 +1493,12 @@ impl DapAccess for StlinkArmDebug {
 impl ArmProbeInterface for StlinkArmDebug {
     fn memory_interface(
         &mut self,
-        access_port: &MemoryAp,
-    ) -> Result<Box<dyn ArmProbe + '_>, ArmError> {
+        access_port: &FullyQualifiedApAddress,
+    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError> {
+        let mem_ap = MemoryAp::new(access_port.clone());
         let interface = StLinkMemoryInterface {
             probe: self,
-            current_ap: access_port.clone(),
+            current_ap: mem_ap,
         };
 
         Ok(Box::new(interface) as _)
@@ -1506,9 +1507,9 @@ impl ArmProbeInterface for StlinkArmDebug {
     #[tracing::instrument(skip(self))]
     fn ap_information(
         &mut self,
-        access_port: &GenericAp,
+        access_port: &FullyQualifiedApAddress,
     ) -> Result<&crate::architecture::arm::communication_interface::ApInformation, ArmError> {
-        let addr = access_port.ap_address();
+        let addr = access_port;
         if addr.dp() != DpAddress::Default {
             return Err(DebugProbeError::from(StlinkError::MultidropNotSupported).into());
         }
@@ -1536,7 +1537,7 @@ impl ArmProbeInterface for StlinkArmDebug {
 
                 let baseaddr = access_port.base_address(self)?;
 
-                let mut memory = self.memory_interface(&access_port)?;
+                let mut memory = self.memory_interface(access_port.ap_address())?;
 
                 let component = Component::try_parse(&mut *memory, baseaddr)?;
 
@@ -1625,7 +1626,7 @@ impl SwdSequence for StLinkMemoryInterface<'_> {
     }
 }
 
-impl ArmProbe for StLinkMemoryInterface<'_> {
+impl MemoryInterface<ArmError> for StLinkMemoryInterface<'_> {
     fn supports_native_64bit_access(&mut self) -> bool {
         false
     }
@@ -1894,7 +1895,9 @@ impl ArmProbe for StLinkMemoryInterface<'_> {
     fn supports_8bit_transfers(&self) -> Result<bool, ArmError> {
         Ok(true)
     }
+}
 
+impl ArmMemoryInterface for StLinkMemoryInterface<'_> {
     fn get_arm_communication_interface(
         &mut self,
     ) -> Result<
diff --git a/probe-rs/src/session.rs b/probe-rs/src/session.rs
index 7e5dd491..2c7ad5fe 100644
--- a/probe-rs/src/session.rs
+++ b/probe-rs/src/session.rs
@@ -1,27 +1,25 @@
-use crate::architecture::arm::ap::AccessPort;
-use crate::architecture::arm::component::get_arm_components;
-use crate::architecture::arm::sequences::{ArmDebugSequence, DefaultArmSequence};
-use crate::architecture::arm::{ArmError, DpAddress};
-use crate::architecture::riscv::communication_interface::{RiscvDebugInterfaceState, RiscvError};
-use crate::architecture::xtensa::communication_interface::{
-    XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
-};
-use crate::config::{CoreExt, RegistryError, Target, TargetSelector};
-use crate::core::{Architecture, CombinedCoreState};
-use crate::probe::fake_probe::FakeProbe;
-use crate::probe::ProbeCreationError;
 use crate::{
     architecture::{
         arm::{
-            communication_interface::ArmProbeInterface, component::TraceSink,
-            memory::CoresightComponent, SwoReader,
+            communication_interface::ArmProbeInterface,
+            component::{get_arm_components, TraceSink},
+            memory::CoresightComponent,
+            sequences::{ArmDebugSequence, DefaultArmSequence},
+            ArmError, DpAddress, SwoReader,
+        },
+        riscv::communication_interface::{
+            RiscvCommunicationInterface, RiscvDebugInterfaceState, RiscvError,
+        },
+        xtensa::communication_interface::{
+            XtensaCommunicationInterface, XtensaDebugInterfaceState, XtensaError,
         },
-        riscv::communication_interface::RiscvCommunicationInterface,
     },
-    config::DebugSequence,
-};
-use crate::{
-    probe::{list::Lister, AttachMethod, DebugProbeError, Probe},
+    config::{CoreExt, DebugSequence, RegistryError, Target, TargetSelector},
+    core::{Architecture, CombinedCoreState},
+    probe::{
+        fake_probe::FakeProbe, list::Lister, AttachMethod, DebugProbeError, Probe,
+        ProbeCreationError,
+    },
     Core, CoreType, Error,
 };
 use std::ops::DerefMut;
@@ -181,7 +179,7 @@ impl Session {
             ))
         })?;
 
-        let default_dp = default_memory_ap.ap_address().dp();
+        let default_dp = default_memory_ap.dp();
 
         let sequence_handle = match &target.debug_sequence {
             DebugSequence::Arm(sequence) => sequence.clone(),
diff --git a/probe-rs/src/vendor/infineon/mod.rs b/probe-rs/src/vendor/infineon/mod.rs
index 25ee0cd0..5bfac2d3 100644
--- a/probe-rs/src/vendor/infineon/mod.rs
+++ b/probe-rs/src/vendor/infineon/mod.rs
@@ -5,8 +5,8 @@ use probe_rs_target::{chip_detection::ChipDetectionMethod, Chip};
 
 use crate::{
     architecture::arm::{
-        ap::MemoryAp, memory::adi_v5_memory_interface::ArmProbe, ArmChipInfo, ArmError,
-        ArmProbeInterface, FullyQualifiedApAddress,
+        memory::ArmMemoryInterface, ArmChipInfo, ArmError, ArmProbeInterface,
+        FullyQualifiedApAddress,
     },
     config::{registry, DebugSequence},
     error::Error,
@@ -59,7 +59,7 @@ fn try_detect_xmc4xxx(
     }
 
     // FIXME: This is a bit shaky but good enough for now.
-    let access_port = &MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(0));
+    let access_port = &FullyQualifiedApAddress::v1_with_default_dp(0);
     let mut memory_interface = interface.memory_interface(access_port)?;
 
     // First, read the SCU peripheral ID register to verify that this is an XMC4000.
@@ -98,7 +98,7 @@ fn try_detect_xmc4xxx(
     Ok(None)
 }
 
-fn read_xmc4xxx_scu_idchip(memory: &mut dyn ArmProbe) -> Result<Option<u32>, ArmError> {
+fn read_xmc4xxx_scu_idchip(memory: &mut dyn ArmMemoryInterface) -> Result<Option<u32>, ArmError> {
     // The SCU peripheral has a peripheral/module ID register:
     bitfield::bitfield! {
         /// SCU->ID register.
@@ -130,7 +130,7 @@ fn read_xmc4xxx_scu_idchip(memory: &mut dyn ArmProbe) -> Result<Option<u32>, Arm
     memory.read_word_32(ScuChipId::ADDRESS as u64).map(Some)
 }
 
-fn probe_xmc4xxx_flash_size(start_addr: u32, memory: &mut dyn ArmProbe) -> u32 {
+fn probe_xmc4xxx_flash_size(start_addr: u32, memory: &mut dyn ArmMemoryInterface) -> u32 {
     let mut last_successful_size = 0;
     // TODO: if we need to be more general, implement a binary search here.
     for size in [
diff --git a/probe-rs/src/vendor/infineon/sequences/xmc4000.rs b/probe-rs/src/vendor/infineon/sequences/xmc4000.rs
index 99a2426c..36ab694e 100644
--- a/probe-rs/src/vendor/infineon/sequences/xmc4000.rs
+++ b/probe-rs/src/vendor/infineon/sequences/xmc4000.rs
@@ -1,7 +1,7 @@
 //! Sequences for XMC4000
 
 use crate::architecture::arm::armv7m::{Aircr, Dhcsr, FpCtrl, FpRev1CompX, FpRev2CompX};
-use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
+use crate::architecture::arm::memory::ArmMemoryInterface;
 use crate::architecture::arm::sequences::{ArmDebugSequence, ArmDebugSequenceError};
 use crate::architecture::arm::ArmError;
 use std::sync::Arc;
@@ -99,7 +99,7 @@ impl ArmDebugSequence for XMC4000 {
 
     fn reset_catch_set(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         core_type: probe_rs_target::CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -238,7 +238,7 @@ impl ArmDebugSequence for XMC4000 {
 
     fn reset_catch_clear(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         _core_type: probe_rs_target::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -275,7 +275,7 @@ impl ArmDebugSequence for XMC4000 {
 
     fn reset_system(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         _core_type: probe_rs_target::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -414,7 +414,7 @@ impl ArmDebugSequence for XMC4000 {
         Ok(())
     }
 
-    fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn reset_hardware_deassert(&self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         tracing::trace!("performing XMC4000 ResetHardwareDeassert");
 
         // We already deasserted nRST in ResetHardwareAssert, because that's how Cold Reset Halts
@@ -427,7 +427,10 @@ impl ArmDebugSequence for XMC4000 {
     }
 }
 
-fn spin_until_core_is_halted(core: &mut dyn ArmProbe, timeout: Duration) -> Result<(), ArmError> {
+fn spin_until_core_is_halted(
+    core: &mut dyn ArmMemoryInterface,
+    timeout: Duration,
+) -> Result<(), ArmError> {
     let start = Instant::now();
     loop {
         let dhcsr = Dhcsr(core.read_word_32(Dhcsr::get_mmio_address())?);
@@ -441,7 +444,7 @@ fn spin_until_core_is_halted(core: &mut dyn ArmProbe, timeout: Duration) -> Resu
     }
 }
 
-fn spin_until_dapsa_is_clear(core: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn spin_until_dapsa_is_clear(core: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     let start = Instant::now();
     loop {
         // DAPSA isn't directly accessible because of course it isn't.
diff --git a/probe-rs/src/vendor/microchip/mod.rs b/probe-rs/src/vendor/microchip/mod.rs
index c4ef01ba..2e080fc7 100644
--- a/probe-rs/src/vendor/microchip/mod.rs
+++ b/probe-rs/src/vendor/microchip/mod.rs
@@ -3,7 +3,7 @@
 use probe_rs_target::{chip_detection::ChipDetectionMethod, Chip};
 
 use crate::{
-    architecture::arm::{ap::MemoryAp, ArmChipInfo, ArmProbeInterface, FullyQualifiedApAddress},
+    architecture::arm::{ArmChipInfo, ArmProbeInterface, FullyQualifiedApAddress},
     config::{registry, DebugSequence},
     vendor::{
         microchip::sequences::atsam::{AtSAM, DsuDid},
@@ -44,11 +44,11 @@ impl Vendor for Microchip {
         }
 
         // FIXME: This is a bit shaky but good enough for now.
-        let access_port = MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(0));
+        let access_port = &FullyQualifiedApAddress::v1_with_default_dp(0);
         // This device has an Atmel DSU - Read and parse the DSU DID register
         let did = DsuDid(
             interface
-                .memory_interface(&access_port)?
+                .memory_interface(access_port)?
                 .read_word_32(DsuDid::ADDRESS)?,
         );
 
diff --git a/probe-rs/src/vendor/microchip/sequences/atsam.rs b/probe-rs/src/vendor/microchip/sequences/atsam.rs
index caed9bd5..46075464 100644
--- a/probe-rs/src/vendor/microchip/sequences/atsam.rs
+++ b/probe-rs/src/vendor/microchip/sequences/atsam.rs
@@ -1,16 +1,12 @@
 //! Sequences for ATSAM D1x/D2x/DAx/D5x/E5x target families
 
 use crate::{
-    architecture::{
-        self,
-        arm::{
-            ap::MemoryAp,
-            armv7m::Dhcsr,
-            communication_interface::{DapProbe, SwdSequence},
-            memory::adi_v5_memory_interface::ArmProbe,
-            sequences::{ArmDebugSequence, ArmDebugSequenceError, DebugEraseSequence},
-            ArmError, ArmProbeInterface, FullyQualifiedApAddress, Pins,
-        },
+    architecture::arm::{
+        armv7m::Dhcsr,
+        communication_interface::{DapProbe, SwdSequence},
+        memory::ArmMemoryInterface,
+        sequences::{ArmDebugSequence, ArmDebugSequenceError, DebugEraseSequence},
+        ArmError, ArmProbeInterface, FullyQualifiedApAddress, Pins,
     },
     probe::DebugProbeError,
     session::MissingPermissions,
@@ -281,7 +277,7 @@ impl AtSAM {
     /// to signal that a re-connect is needed for the DSU to start operating in unlocked mode.
     pub fn erase_all(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         permissions: &Permissions,
     ) -> Result<(), ArmError> {
         let dsu_status_a = DsuStatusA::from(memory.read_word_8(DsuStatusA::ADDRESS)?);
@@ -380,7 +376,10 @@ impl AtSAM {
     ///
     /// # Errors
     /// Subject to probe communication errors
-    pub fn release_reset_extension(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    pub fn release_reset_extension(
+        &self,
+        memory: &mut dyn ArmMemoryInterface,
+    ) -> Result<(), ArmError> {
         // Enable debug mode if it is not already enabled
         let mut dhcsr = Dhcsr(0);
         dhcsr.enable_write();
@@ -462,12 +461,12 @@ impl ArmDebugSequence for AtSAM {
     fn debug_core_start(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        core_ap: MemoryAp,
+        core_ap: &FullyQualifiedApAddress,
         _core_type: CoreType,
         _debug_base: Option<u64>,
         _cti_base: Option<u64>,
     ) -> Result<(), ArmError> {
-        let mut core = interface.memory_interface(&core_ap)?;
+        let mut core = interface.memory_interface(core_ap)?;
 
         self.release_reset_extension(&mut *core)
     }
@@ -487,7 +486,7 @@ impl ArmDebugSequence for AtSAM {
     ///
     /// Instead of de-asserting `nReset` here (this was already done during the CPU Reset Extension process),
     /// the device is released from Reset Extension.
-    fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn reset_hardware_deassert(&self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         let mut pins = Pins(0);
         pins.set_nreset(true);
 
@@ -515,7 +514,7 @@ impl ArmDebugSequence for AtSAM {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        default_ap: &architecture::arm::ap::MemoryAp,
+        default_ap: &FullyQualifiedApAddress,
         permissions: &Permissions,
     ) -> Result<(), ArmError> {
         // First check if the device is locked
@@ -537,7 +536,7 @@ impl ArmDebugSequence for AtSAM {
 
 impl DebugEraseSequence for AtSAM {
     fn erase_all(&self, interface: &mut dyn ArmProbeInterface) -> Result<(), ArmError> {
-        let mem_ap = &MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(0));
+        let mem_ap = &FullyQualifiedApAddress::v1_with_default_dp(0);
 
         let mut memory = interface.memory_interface(mem_ap)?;
 
diff --git a/probe-rs/src/vendor/nordicsemi/mod.rs b/probe-rs/src/vendor/nordicsemi/mod.rs
index 375ce627..8a814134 100644
--- a/probe-rs/src/vendor/nordicsemi/mod.rs
+++ b/probe-rs/src/vendor/nordicsemi/mod.rs
@@ -9,8 +9,7 @@ use probe_rs_target::{
 
 use crate::{
     architecture::arm::{
-        ap::MemoryAp, memory::adi_v5_memory_interface::ArmProbe, ArmChipInfo, ArmProbeInterface,
-        FullyQualifiedApAddress,
+        memory::ArmMemoryInterface, ArmChipInfo, ArmProbeInterface, FullyQualifiedApAddress,
     },
     config::{registry, DebugSequence},
     vendor::{
@@ -51,8 +50,8 @@ impl Vendor for NordicSemi {
         }
 
         // FIXME: This is a bit shaky but good enough for now.
-        let access_port = MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(0));
-        let mut memory_interface = probe.memory_interface(&access_port)?;
+        let access_port = &FullyQualifiedApAddress::v1_with_default_dp(0);
+        let mut memory_interface = probe.memory_interface(access_port)?;
 
         // Cache to avoid reading the same register multiple times
         let mut register_values: HashMap<u32, u32> = HashMap::new();
@@ -82,7 +81,7 @@ impl Vendor for NordicSemi {
 
 fn ficr_info_detect(
     register_values: &mut HashMap<u32, u32>,
-    memory_interface: &mut dyn ArmProbe,
+    memory_interface: &mut dyn ArmMemoryInterface,
     spec: &NordicFicrDetection,
 ) -> Option<String> {
     // Read the PART register, if not already read
@@ -104,7 +103,7 @@ fn ficr_info_detect(
 
 fn configid_detect(
     register_values: &mut HashMap<u32, u32>,
-    memory_interface: &mut dyn ArmProbe,
+    memory_interface: &mut dyn ArmMemoryInterface,
     spec: &NordicConfigIdDetection,
 ) -> Option<String> {
     // Read the CONFIGID register, if not already read
@@ -122,7 +121,7 @@ fn configid_detect(
 
 fn read_register_cached(
     register_values: &mut HashMap<u32, u32>,
-    memory_interface: &mut dyn ArmProbe,
+    memory_interface: &mut dyn ArmMemoryInterface,
     address: u32,
 ) -> Option<u32> {
     match register_values.entry(address) {
diff --git a/probe-rs/src/vendor/nordicsemi/sequences/nrf.rs b/probe-rs/src/vendor/nordicsemi/sequences/nrf.rs
index 86e9b55c..a9bd5a93 100644
--- a/probe-rs/src/vendor/nordicsemi/sequences/nrf.rs
+++ b/probe-rs/src/vendor/nordicsemi/sequences/nrf.rs
@@ -2,9 +2,8 @@
 
 use crate::{
     architecture::arm::{
-        ap::MemoryAp,
         communication_interface::Initialized,
-        memory::adi_v5_memory_interface::ArmProbe,
+        memory::ArmMemoryInterface,
         sequences::{ArmDebugSequence, ArmDebugSequenceError},
         ArmCommunicationInterface, ArmError, ArmProbeInterface, DapAccess, FullyQualifiedApAddress,
     },
@@ -16,7 +15,7 @@ pub trait Nrf: Sync + Send + Debug {
     /// Returns the ahb_ap and ctrl_ap of every core
     fn core_aps(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
     ) -> Vec<(FullyQualifiedApAddress, FullyQualifiedApAddress)>;
 
     /// Returns true when the core is unlocked and false when it is locked.
@@ -60,7 +59,7 @@ fn unlock_core(
 }
 
 /// Sets the network core to active running.
-fn set_network_core_running(interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn set_network_core_running(interface: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     // Determine if the RESET peripheral is mapped to secure or non-secure address space.
     let periph_config_address = APPLICATION_SPU_PERIPH_PERM + 0x4 * APPLICATION_RESET_PERIPH_ID;
     let periph_config = interface.read_word_32(periph_config_address)?;
@@ -82,7 +81,7 @@ impl<T: Nrf> ArmDebugSequence for T {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        default_ap: &MemoryAp,
+        default_ap: &FullyQualifiedApAddress,
         permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         let mut interface = interface.memory_interface(default_ap)?;
diff --git a/probe-rs/src/vendor/nordicsemi/sequences/nrf52.rs b/probe-rs/src/vendor/nordicsemi/sequences/nrf52.rs
index 067deba3..303abf0f 100644
--- a/probe-rs/src/vendor/nordicsemi/sequences/nrf52.rs
+++ b/probe-rs/src/vendor/nordicsemi/sequences/nrf52.rs
@@ -3,7 +3,6 @@
 use std::sync::Arc;
 
 use crate::architecture::arm::{
-    ap::MemoryAp,
     component::TraceSink,
     memory::CoresightComponent,
     sequences::{ArmDebugSequence, ArmDebugSequenceError},
@@ -49,7 +48,7 @@ impl Nrf52 {
 }
 
 mod clock {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the DBGMCU component
@@ -71,13 +70,13 @@ mod clock {
         const ADDRESS: u64 = 0x55C;
 
         /// Read the control register from memory.
-        pub fn read(memory: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+        pub fn read(memory: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
             let contents = memory.read_word_32(CLOCK + Self::ADDRESS)?;
             Ok(Self(contents))
         }
 
         /// Write the control register to memory.
-        pub fn write(&mut self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+        pub fn write(&mut self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
             memory.write_word_32(CLOCK + Self::ADDRESS, self.0)
         }
     }
@@ -87,7 +86,7 @@ impl ArmDebugSequence for Nrf52 {
     fn debug_device_unlock(
         &self,
         iface: &mut dyn ArmProbeInterface,
-        _default_ap: &MemoryAp,
+        _default_ap: &FullyQualifiedApAddress,
         permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         let ctrl_ap = &FullyQualifiedApAddress::v1_with_default_dp(1);
@@ -152,7 +151,7 @@ impl ArmDebugSequence for Nrf52 {
             }
         };
 
-        let mut memory = interface.memory_interface(&components[0].ap)?;
+        let mut memory = interface.memory_interface(&components[0].ap_address)?;
         let mut config = clock::TraceConfig::read(&mut *memory)?;
         config.set_traceportspeed(portspeed);
         if matches!(sink, TraceSink::Tpiu(_)) {
diff --git a/probe-rs/src/vendor/nordicsemi/sequences/nrf53.rs b/probe-rs/src/vendor/nordicsemi/sequences/nrf53.rs
index 8068fbc3..24c07a2c 100644
--- a/probe-rs/src/vendor/nordicsemi/sequences/nrf53.rs
+++ b/probe-rs/src/vendor/nordicsemi/sequences/nrf53.rs
@@ -4,7 +4,7 @@ use std::sync::Arc;
 
 use super::nrf::Nrf;
 use crate::architecture::arm::ap::{AccessPort, CSW};
-use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
+use crate::architecture::arm::memory::ArmMemoryInterface;
 use crate::architecture::arm::sequences::ArmDebugSequence;
 use crate::architecture::arm::ArmError;
 use crate::architecture::arm::{
@@ -26,7 +26,7 @@ impl Nrf5340 {
 impl Nrf for Nrf5340 {
     fn core_aps(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
     ) -> Vec<(FullyQualifiedApAddress, FullyQualifiedApAddress)> {
         let memory_ap = memory.ap();
         let ap_address = memory_ap.ap_address();
diff --git a/probe-rs/src/vendor/nordicsemi/sequences/nrf91.rs b/probe-rs/src/vendor/nordicsemi/sequences/nrf91.rs
index bf8f6f0c..232c5ece 100644
--- a/probe-rs/src/vendor/nordicsemi/sequences/nrf91.rs
+++ b/probe-rs/src/vendor/nordicsemi/sequences/nrf91.rs
@@ -3,12 +3,9 @@
 use std::sync::Arc;
 
 use super::nrf::Nrf;
-use crate::architecture::arm::ap::AccessPort;
-use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
-use crate::architecture::arm::sequences::ArmDebugSequence;
-use crate::architecture::arm::ArmError;
 use crate::architecture::arm::{
-    communication_interface::Initialized, ArmCommunicationInterface, DapAccess,
+    ap::AccessPort, communication_interface::Initialized, memory::ArmMemoryInterface,
+    sequences::ArmDebugSequence, ArmCommunicationInterface, ArmError, DapAccess,
     FullyQualifiedApAddress,
 };
 
@@ -26,10 +23,9 @@ impl Nrf9160 {
 impl Nrf for Nrf9160 {
     fn core_aps(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
     ) -> Vec<(FullyQualifiedApAddress, FullyQualifiedApAddress)> {
-        let memory_ap = memory.ap();
-        let ap_address = memory_ap.ap_address();
+        let dp = memory.ap().ap_address().dp();
 
         let core_aps = [(0, 4)];
 
@@ -37,8 +33,8 @@ impl Nrf for Nrf9160 {
             .into_iter()
             .map(|(core_ahb_ap, core_ctrl_ap)| {
                 (
-                    FullyQualifiedApAddress::v1_with_dp(ap_address.dp(), core_ahb_ap),
-                    FullyQualifiedApAddress::v1_with_dp(ap_address.dp(), core_ctrl_ap),
+                    FullyQualifiedApAddress::v1_with_dp(dp, core_ahb_ap),
+                    FullyQualifiedApAddress::v1_with_dp(dp, core_ctrl_ap),
                 )
             })
             .collect()
diff --git a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
index 5280f038..fca7bff4 100644
--- a/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
+++ b/probe-rs/src/vendor/nxp/sequences/nxp_armv7m.rs
@@ -11,13 +11,13 @@ use std::{
 
 use crate::{
     architecture::arm::{
-        ap::{AccessPortError, ApAccess, CSW},
+        ap::{AccessPort, AccessPortError, ApAccess, CSW},
         armv7m::{FpCtrl, FpRev2CompX},
         core::{
             armv7m::{Aircr, Dhcsr},
             registers::cortex_m::PC,
         },
-        memory::adi_v5_memory_interface::ArmProbe,
+        memory::ArmMemoryInterface,
         sequences::{ArmDebugSequence, ArmDebugSequenceError},
         ArmError,
     },
@@ -64,7 +64,10 @@ impl MIMXRT10xx {
     ///
     /// If the user changed the FlexRAM configuration in software, this will undo
     /// that configuration, preferring the system's POR FlexRAM state.
-    fn use_boot_fuses_for_flexram(&self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn use_boot_fuses_for_flexram(
+        &self,
+        probe: &mut dyn ArmMemoryInterface,
+    ) -> Result<(), ArmError> {
         const IOMUXC_GPR_GPR16: u64 = 0x400A_C040;
         const FLEXRAM_BANK_CFG_SEL_MASK: u32 = 1 << 2;
         let mut gpr16 = probe.read_word_32(IOMUXC_GPR_GPR16)?;
@@ -78,7 +81,7 @@ impl MIMXRT10xx {
 impl ArmDebugSequence for MIMXRT10xx {
     fn reset_system(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         core_type: crate::CoreType,
         _: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -164,7 +167,7 @@ impl MIMXRT11xx {
 
     /// To ensure we affect a system reset, clear the mask that would prevent
     /// a response to the CM7's SYSRESETREQ.
-    fn clear_src_srmr_mask(&self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn clear_src_srmr_mask(&self, probe: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         let mut srmr = probe.read_word_32(Self::SRC_SRMR)?;
         tracing::debug!("SRC_SRMR: {srmr:#010X}. Clearing the M7REQ_RESET_MODE mask...");
         srmr &= !(0b11 << 12);
@@ -174,7 +177,7 @@ impl MIMXRT11xx {
     }
 
     /// Halt or unhalt the core.
-    fn halt(&self, probe: &mut dyn ArmProbe, halt: bool) -> Result<(), ArmError> {
+    fn halt(&self, probe: &mut dyn ArmMemoryInterface, halt: bool) -> Result<(), ArmError> {
         let mut dhcsr = Dhcsr(probe.read_word_32(Dhcsr::get_mmio_address())?);
         dhcsr.set_c_halt(halt);
         dhcsr.set_c_debugen(true);
@@ -198,7 +201,11 @@ impl MIMXRT11xx {
     }
 
     /// Poll the AP's status until it can accept transfers.
-    fn wait_for_enable(&self, probe: &mut dyn ArmProbe, timeout: Duration) -> Result<(), ArmError> {
+    fn wait_for_enable(
+        &self,
+        probe: &mut dyn ArmMemoryInterface,
+        timeout: Duration,
+    ) -> Result<(), ArmError> {
         let ap = probe.ap();
         let interface = probe.get_arm_communication_interface()?;
 
@@ -227,7 +234,7 @@ impl MIMXRT11xx {
     /// Assumes that the core is halted.
     fn read_core_reg(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         reg: crate::core::registers::CoreRegister,
     ) -> Result<u32, ArmError> {
         crate::architecture::arm::core::cortex_m::read_core_reg(probe, reg.into())
@@ -236,7 +243,7 @@ impl MIMXRT11xx {
     /// Assumes that the core is halted.
     fn write_core_reg(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         reg: crate::core::registers::CoreRegister,
         value: u32,
     ) -> Result<(), ArmError> {
@@ -250,7 +257,7 @@ impl MIMXRT11xx {
     /// Assumes that the core is halted.
     fn check_pc(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         expected: u32,
     ) -> Result<(), ArmDebugSequenceError> {
         let pc = self
@@ -274,7 +281,7 @@ impl MIMXRT11xx {
     /// We might not find that reset handler. In that case, return `None`.
     fn find_flexspi_image_reset_handler(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
     ) -> Result<Option<u32>, ArmError> {
         /// Assumed by today's in-tree target definition.
         const FLEXSPI1: u64 = 0x30000000;
@@ -336,7 +343,10 @@ impl MIMXRT11xx {
     }
 
     /// See documentation for [`MIMXRT10xx::use_boot_fuses_for_flexram`].
-    fn use_boot_fuses_for_flexram(&self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn use_boot_fuses_for_flexram(
+        &self,
+        probe: &mut dyn ArmMemoryInterface,
+    ) -> Result<(), ArmError> {
         const IOMUXC_GPR_GPR16: u64 = 0x400E_4040;
         const FLEXRAM_BANK_CFG_SEL_MASK: u32 = 1 << 2;
         let mut gpr16 = probe.read_word_32(IOMUXC_GPR_GPR16)?;
@@ -350,7 +360,7 @@ impl MIMXRT11xx {
 impl ArmDebugSequence for MIMXRT11xx {
     fn reset_catch_set(
         &self,
-        _: &mut dyn ArmProbe,
+        _: &mut dyn ArmMemoryInterface,
         _: probe_rs_target::CoreType,
         _: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -359,7 +369,7 @@ impl ArmDebugSequence for MIMXRT11xx {
     }
     fn reset_catch_clear(
         &self,
-        _: &mut dyn ArmProbe,
+        _: &mut dyn ArmMemoryInterface,
         _: probe_rs_target::CoreType,
         _: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -369,7 +379,7 @@ impl ArmDebugSequence for MIMXRT11xx {
 
     fn reset_system(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         core_type: probe_rs_target::CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -415,12 +425,12 @@ impl ArmDebugSequence for MIMXRT11xx {
         //
         // The ARM communication interface knows how to re-initialize the debug port.
         // Re-initializing the core(s) is on us.
-        let ap = probe.ap();
+        let ap = probe.ap().ap_address().clone();
         let interface = probe.get_arm_communication_interface()?;
         interface.reinitialize()?;
 
         assert!(debug_base.is_none());
-        self.debug_core_start(interface, ap, core_type, None, None)?;
+        self.debug_core_start(interface, &ap, core_type, None, None)?;
 
         // Are we back?
         self.wait_for_enable(probe, Duration::from_millis(300))?;
@@ -487,7 +497,7 @@ struct DebugCache {
 
 impl DebugCache {
     /// Produce a debug cache from the target.
-    fn from_target(probe: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+    fn from_target(probe: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
         let fp_ctrl = FpCtrl(probe.read_word_32(FpCtrl::get_mmio_address())?);
 
         Ok(Self {
@@ -503,7 +513,7 @@ impl DebugCache {
     }
 
     /// Put this cached debug state back into the target.
-    fn restore(mut self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn restore(mut self, probe: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         self.fp_ctrl.set_key(true);
         probe.write_word_32(FpCtrl::get_mmio_address(), self.fp_ctrl.into())?;
 
diff --git a/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs b/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
index 86fe46e8..9ebfbc20 100644
--- a/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
+++ b/probe-rs/src/vendor/nxp/sequences/nxp_armv8m.rs
@@ -8,11 +8,11 @@ use std::{
 
 use crate::{
     architecture::arm::{
-        ap::{AccessPort, ApAccess, GenericAp, MemoryAp, CSW, IDR},
+        ap::{AccessPort, ApAccess, GenericAp, CSW, IDR},
         communication_interface::{FlushableArmAccess, Initialized},
         core::armv8m::{Aircr, Demcr, Dhcsr},
         dp::{Abort, Ctrl, DpAccess, Select, DPIDR},
-        memory::adi_v5_memory_interface::ArmProbe,
+        memory::ArmMemoryInterface,
         sequences::ArmDebugSequence,
         ArmCommunicationInterface, ArmError, DapAccess, DpAddress, FullyQualifiedApAddress, Pins,
     },
@@ -115,7 +115,7 @@ impl ArmDebugSequence for LPC55Sxx {
 
     fn reset_catch_set(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         _core_type: crate::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -196,7 +196,7 @@ impl ArmDebugSequence for LPC55Sxx {
 
     fn reset_catch_clear(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         _core_type: crate::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -212,7 +212,7 @@ impl ArmDebugSequence for LPC55Sxx {
 
     fn reset_system(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         _core_type: crate::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -254,7 +254,7 @@ impl ArmDebugSequence for LPC55Sxx {
     }
 }
 
-fn wait_for_stop_after_reset(memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+fn wait_for_stop_after_reset(memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
     tracing::info!("Wait for stop after reset");
 
     thread::sleep(Duration::from_millis(10));
@@ -417,7 +417,10 @@ impl MIMXRT5xxS {
 
     /// A port of the "WaitForStopAfterReset" sequence from the CMSIS Pack for
     /// this chip.
-    fn wait_for_stop_after_reset(&self, probe: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn wait_for_stop_after_reset(
+        &self,
+        probe: &mut dyn ArmMemoryInterface,
+    ) -> Result<(), ArmError> {
         tracing::trace!("waiting for MIMXRT5xxS halt after reset");
 
         // Note: despite the name of this sequence in the CMSIS Pack, the
@@ -430,8 +433,8 @@ impl MIMXRT5xxS {
         // Give bootloader time to do what it needs to do
         thread::sleep(Duration::from_millis(100));
 
-        let ap: MemoryAp = probe.ap();
-        let dp = ap.ap_address().dp();
+        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)?
             && start.elapsed() < Duration::from_millis(300)
@@ -485,7 +488,7 @@ impl MIMXRT5xxS {
         Ok(())
     }
 
-    fn reset_flash(&self, interface: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn reset_flash(&self, interface: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         if self.family == MIMXRTFamily::MIMXRT5 {
             tracing::trace!("MIMXRT595S-EVK FlexSPI flash reset (pulse PIO4_5)");
 
@@ -530,9 +533,9 @@ impl MIMXRT5xxS {
     fn csw_debug_ready(
         &self,
         interface: &mut ArmCommunicationInterface<Initialized>,
-        ap: &MemoryAp,
+        ap: &FullyQualifiedApAddress,
     ) -> Result<bool, ArmError> {
-        let csw = interface.read_raw_ap_register(ap.ap_address(), 0x00)?;
+        let csw = interface.read_raw_ap_register(ap, 0x00)?;
 
         Ok(csw & 0x40 != 0)
     }
@@ -547,7 +550,7 @@ impl MIMXRT5xxS {
         &self,
         interface: &mut ArmCommunicationInterface<Initialized>,
         dp: DpAddress,
-        mem_ap: &MemoryAp,
+        mem_ap: &FullyQualifiedApAddress,
     ) -> Result<bool, ArmError> {
         // Check AHB-AP CSW DbgStatus to decide if need enable DebugMailbox
         if self.csw_debug_ready(interface, mem_ap)? {
@@ -628,8 +631,7 @@ impl ArmDebugSequence for MIMXRT5xxS {
             interface.write_raw_dp_register(dp, SW_DP_ABORT, 0x0000001E)?;
 
             let ap = FullyQualifiedApAddress::v1_with_dp(dp, 0);
-            let mem_ap = MemoryAp::new(ap);
-            self.enable_debug_mailbox(interface, dp, &mem_ap)?;
+            self.enable_debug_mailbox(interface, dp, &ap)?;
         }
 
         tracing::trace!("MIMXRT5xxS debug port start was successful");
@@ -639,7 +641,7 @@ impl ArmDebugSequence for MIMXRT5xxS {
 
     fn reset_system(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         core_type: probe_rs_target::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -687,7 +689,7 @@ impl ArmDebugSequence for MIMXRT5xxS {
         self.wait_for_stop_after_reset(probe)
     }
 
-    fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+    fn reset_hardware_deassert(&self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
         tracing::trace!("MIMXRT5xxS reset hardware deassert");
         let n_reset = Pins(0x80).0 as u32;
 
diff --git a/probe-rs/src/vendor/silabs/sequences/efm32xg2.rs b/probe-rs/src/vendor/silabs/sequences/efm32xg2.rs
index 59147733..d36e0442 100644
--- a/probe-rs/src/vendor/silabs/sequences/efm32xg2.rs
+++ b/probe-rs/src/vendor/silabs/sequences/efm32xg2.rs
@@ -5,7 +5,7 @@ use std::sync::Arc;
 use crate::{
     architecture::arm::{
         core::armv7m::{Demcr, Dhcsr},
-        memory::adi_v5_memory_interface::ArmProbe,
+        memory::ArmMemoryInterface,
         sequences::ArmDebugSequence,
         ArmError,
     },
@@ -28,7 +28,7 @@ impl EFM32xG2 {
 impl ArmDebugSequence for EFM32xG2 {
     fn reset_catch_set(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         _core_type: probe_rs_target::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -54,7 +54,7 @@ impl ArmDebugSequence for EFM32xG2 {
 
     fn reset_catch_clear(
         &self,
-        core: &mut dyn ArmProbe,
+        core: &mut dyn ArmMemoryInterface,
         _core_type: probe_rs_target::CoreType,
         _debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
diff --git a/probe-rs/src/vendor/st/sequences/stm32_armv6.rs b/probe-rs/src/vendor/st/sequences/stm32_armv6.rs
index 801a8da4..0575d517 100644
--- a/probe-rs/src/vendor/st/sequences/stm32_armv6.rs
+++ b/probe-rs/src/vendor/st/sequences/stm32_armv6.rs
@@ -7,8 +7,8 @@ use std::sync::Arc;
 use probe_rs_target::CoreType;
 
 use crate::architecture::arm::{
-    ap::MemoryAp, memory::adi_v5_memory_interface::ArmProbe, sequences::ArmDebugSequence, ArmError,
-    ArmProbeInterface,
+    memory::ArmMemoryInterface, sequences::ArmDebugSequence, ArmError, ArmProbeInterface,
+    FullyQualifiedApAddress,
 };
 
 /// Supported families for custom sequences on ARMv6 STM32 devices.
@@ -38,7 +38,7 @@ impl Stm32Armv6 {
 }
 
 mod rcc {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the RCC peripheral
@@ -54,13 +54,16 @@ mod rcc {
             impl $name {
                 const ADDRESS: u64 = $offset;
                 /// Read the enable register from memory.
-                pub fn read(memory: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+                pub fn read(memory: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
                     let contents = memory.read_word_32(RCC + Self::ADDRESS)?;
                     Ok(Self(contents))
                 }
 
                 /// Write the enable register to memory.
-                pub fn write(&mut self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+                pub fn write(
+                    &mut self,
+                    memory: &mut dyn ArmMemoryInterface,
+                ) -> Result<(), ArmError> {
                     memory.write_word_32(RCC + Self::ADDRESS, self.0)
                 }
             }
@@ -75,7 +78,7 @@ mod rcc {
 }
 
 mod dbgmcu {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the DBGMCU component
@@ -96,13 +99,13 @@ mod dbgmcu {
         const ADDRESS: u64 = 0x04;
 
         /// Read the control register from memory.
-        pub fn read(memory: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+        pub fn read(memory: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
             let contents = memory.read_word_32(DBGMCU + Self::ADDRESS)?;
             Ok(Self(contents))
         }
 
         /// Write the control register to memory.
-        pub fn write(&mut self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+        pub fn write(&mut self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
             memory.write_word_32(DBGMCU + Self::ADDRESS, self.0)
         }
     }
@@ -112,7 +115,7 @@ impl ArmDebugSequence for Stm32Armv6 {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        default_ap: &MemoryAp,
+        default_ap: &FullyQualifiedApAddress,
         _permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         let mut memory = interface.memory_interface(default_ap)?;
@@ -145,7 +148,7 @@ impl ArmDebugSequence for Stm32Armv6 {
 
     fn debug_core_stop(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         _core_type: CoreType,
     ) -> Result<(), ArmError> {
         match self.family {
diff --git a/probe-rs/src/vendor/st/sequences/stm32_armv7.rs b/probe-rs/src/vendor/st/sequences/stm32_armv7.rs
index c3462926..3b5b71d2 100644
--- a/probe-rs/src/vendor/st/sequences/stm32_armv7.rs
+++ b/probe-rs/src/vendor/st/sequences/stm32_armv7.rs
@@ -13,11 +13,10 @@ use std::sync::Arc;
 use probe_rs_target::CoreType;
 
 use crate::architecture::arm::{
-    ap::MemoryAp,
     component::TraceSink,
-    memory::{adi_v5_memory_interface::ArmProbe, CoresightComponent},
+    memory::{ArmMemoryInterface, CoresightComponent},
     sequences::ArmDebugSequence,
-    ArmError, ArmProbeInterface,
+    ArmError, ArmProbeInterface, FullyQualifiedApAddress,
 };
 
 /// Marker structure for most ARMv7 STM32 devices.
@@ -32,7 +31,7 @@ impl Stm32Armv7 {
 }
 
 mod dbgmcu {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the DBGMCU component
@@ -56,13 +55,13 @@ mod dbgmcu {
         const ADDRESS: u64 = 0x04;
 
         /// Read the control register from memory.
-        pub fn read(memory: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+        pub fn read(memory: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
             let contents = memory.read_word_32(DBGMCU + Self::ADDRESS)?;
             Ok(Self(contents))
         }
 
         /// Write the control register to memory.
-        pub fn write(&mut self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+        pub fn write(&mut self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
             memory.write_word_32(DBGMCU + Self::ADDRESS, self.0)
         }
     }
@@ -72,7 +71,7 @@ impl ArmDebugSequence for Stm32Armv7 {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        default_ap: &MemoryAp,
+        default_ap: &FullyQualifiedApAddress,
         _permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         let mut memory = interface.memory_interface(default_ap)?;
@@ -88,7 +87,7 @@ impl ArmDebugSequence for Stm32Armv7 {
 
     fn debug_core_stop(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         _core_type: CoreType,
     ) -> Result<(), ArmError> {
         let mut cr = dbgmcu::Control::read(&mut *memory)?;
@@ -106,7 +105,7 @@ impl ArmDebugSequence for Stm32Armv7 {
         components: &[CoresightComponent],
         sink: &TraceSink,
     ) -> Result<(), ArmError> {
-        let mut memory = interface.memory_interface(&components[0].ap)?;
+        let mut memory = interface.memory_interface(&components[0].ap_address)?;
         let mut cr = dbgmcu::Control::read(&mut *memory)?;
 
         if matches!(sink, TraceSink::Tpiu(_) | TraceSink::Swo(_)) {
diff --git a/probe-rs/src/vendor/st/sequences/stm32_armv8.rs b/probe-rs/src/vendor/st/sequences/stm32_armv8.rs
index e332a936..58b17edd 100644
--- a/probe-rs/src/vendor/st/sequences/stm32_armv8.rs
+++ b/probe-rs/src/vendor/st/sequences/stm32_armv8.rs
@@ -9,11 +9,10 @@ use std::sync::Arc;
 use probe_rs_target::CoreType;
 
 use crate::architecture::arm::{
-    ap::MemoryAp,
     component::TraceSink,
-    memory::{adi_v5_memory_interface::ArmProbe, CoresightComponent},
+    memory::{ArmMemoryInterface, CoresightComponent},
     sequences::ArmDebugSequence,
-    ArmError, ArmProbeInterface,
+    ArmError, ArmProbeInterface, FullyQualifiedApAddress,
 };
 
 /// Marker structure for ARMv8 STM32 devices.
@@ -28,7 +27,7 @@ impl Stm32Armv8 {
 }
 
 mod dbgmcu {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the DBGMCU component
@@ -52,13 +51,13 @@ mod dbgmcu {
         const ADDRESS: u64 = 0x04;
 
         /// Read the control register from memory.
-        pub fn read(memory: &mut dyn ArmProbe) -> Result<Self, ArmError> {
+        pub fn read(memory: &mut dyn ArmMemoryInterface) -> Result<Self, ArmError> {
             let contents = memory.read_word_32(DBGMCU + Self::ADDRESS)?;
             Ok(Self(contents))
         }
 
         /// Write the control register to memory.
-        pub fn write(&mut self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+        pub fn write(&mut self, memory: &mut dyn ArmMemoryInterface) -> Result<(), ArmError> {
             memory.write_word_32(DBGMCU + Self::ADDRESS, self.0)
         }
     }
@@ -68,7 +67,7 @@ impl ArmDebugSequence for Stm32Armv8 {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        default_ap: &MemoryAp,
+        default_ap: &FullyQualifiedApAddress,
         _permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         let mut memory = interface.memory_interface(default_ap)?;
@@ -82,7 +81,7 @@ impl ArmDebugSequence for Stm32Armv8 {
 
     fn debug_core_stop(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         _core_type: CoreType,
     ) -> Result<(), ArmError> {
         let mut cr = dbgmcu::Control::read(&mut *memory)?;
@@ -99,7 +98,7 @@ impl ArmDebugSequence for Stm32Armv8 {
         components: &[CoresightComponent],
         sink: &TraceSink,
     ) -> Result<(), ArmError> {
-        let mut memory = interface.memory_interface(&components[0].ap)?;
+        let mut memory = interface.memory_interface(&components[0].ap_address)?;
         let mut cr = dbgmcu::Control::read(&mut *memory)?;
 
         if matches!(sink, TraceSink::Tpiu(_) | TraceSink::Swo(_)) {
diff --git a/probe-rs/src/vendor/st/sequences/stm32h7.rs b/probe-rs/src/vendor/st/sequences/stm32h7.rs
index 4c939e7a..bc018652 100644
--- a/probe-rs/src/vendor/st/sequences/stm32h7.rs
+++ b/probe-rs/src/vendor/st/sequences/stm32h7.rs
@@ -5,12 +5,8 @@ use std::sync::Arc;
 use probe_rs_target::CoreType;
 
 use crate::architecture::arm::{
-    ap::MemoryAp,
     component::{TraceFunnel, TraceSink},
-    memory::{
-        adi_v5_memory_interface::ArmProbe, romtable::RomTableError, CoresightComponent,
-        PeripheralType,
-    },
+    memory::{romtable::RomTableError, ArmMemoryInterface, CoresightComponent, PeripheralType},
     sequences::ArmDebugSequence,
     ArmError, ArmProbeInterface, FullyQualifiedApAddress,
 };
@@ -48,7 +44,7 @@ impl Stm32h7 {
     /// Configure all debug components on the chip.
     pub fn enable_debug_components(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         enable: bool,
     ) -> Result<(), ArmError> {
         if enable {
@@ -79,7 +75,7 @@ impl Stm32h7 {
 }
 
 mod dbgmcu {
-    use crate::architecture::arm::{memory::adi_v5_memory_interface::ArmProbe, ArmError};
+    use crate::architecture::arm::{memory::ArmMemoryInterface, ArmError};
     use bitfield::bitfield;
 
     /// The base address of the DBGMCU component
@@ -105,13 +101,16 @@ mod dbgmcu {
         const ADDRESS: u64 = 0x04;
 
         /// Read the control register from memory.
-        pub fn read(memory: &mut (impl ArmProbe + ?Sized)) -> Result<Self, ArmError> {
+        pub fn read(memory: &mut (impl ArmMemoryInterface + ?Sized)) -> Result<Self, ArmError> {
             let contents = memory.read_word_32(DBGMCU + Self::ADDRESS)?;
             Ok(Self(contents))
         }
 
         /// Write the control register to memory.
-        pub fn write(&mut self, memory: &mut (impl ArmProbe + ?Sized)) -> Result<(), ArmError> {
+        pub fn write(
+            &mut self,
+            memory: &mut (impl ArmMemoryInterface + ?Sized),
+        ) -> Result<(), ArmError> {
             memory.write_word_32(DBGMCU + Self::ADDRESS, self.0)
         }
     }
@@ -149,13 +148,13 @@ impl ArmDebugSequence for Stm32h7 {
     fn debug_device_unlock(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        _default_ap: &MemoryAp,
+        _default_ap: &FullyQualifiedApAddress,
         _permissions: &crate::Permissions,
     ) -> Result<(), ArmError> {
         // Power up the debug components through AP2, which is the default AP debug port.
-        let ap = MemoryAp::new(FullyQualifiedApAddress::v1_with_default_dp(2));
+        let ap = &FullyQualifiedApAddress::v1_with_default_dp(2);
 
-        let mut memory = interface.memory_interface(&ap)?;
+        let mut memory = interface.memory_interface(ap)?;
         self.enable_debug_components(&mut *memory, true)?;
 
         Ok(())
@@ -163,7 +162,7 @@ impl ArmDebugSequence for Stm32h7 {
 
     fn debug_core_stop(
         &self,
-        memory: &mut dyn ArmProbe,
+        memory: &mut dyn ArmMemoryInterface,
         _core_type: CoreType,
     ) -> Result<(), ArmError> {
         // Power down the debug components through AP2, which is the default AP debug port.
diff --git a/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs b/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
index d430084b..a3275d3a 100644
--- a/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
+++ b/probe-rs/src/vendor/ti/sequences/cc13xx_cc26xx.rs
@@ -2,9 +2,10 @@
 use std::sync::Arc;
 use std::time::Duration;
 
+use crate::architecture::arm::ap::AccessPort;
 use crate::architecture::arm::armv7m::{Demcr, Dhcsr};
 use crate::architecture::arm::communication_interface::DapProbe;
-use crate::architecture::arm::memory::adi_v5_memory_interface::ArmProbe;
+use crate::architecture::arm::memory::ArmMemoryInterface;
 use crate::architecture::arm::sequences::{ArmDebugSequence, ArmDebugSequenceError};
 use crate::architecture::arm::{ArmError, DpAddress};
 use crate::probe::{DebugProbeError, WireProtocol};
@@ -316,7 +317,7 @@ impl CC13xxCC26xx {
 ///
 /// The below code writes to the following bit
 /// `AON_PMCTL.RESETCTL.SYSRESET=1`d or its equivalent based on family
-fn reset_chip(chip: &str, probe: &mut dyn ArmProbe) {
+fn reset_chip(chip: &str, probe: &mut dyn ArmMemoryInterface) {
     // The CC family of device have a pattern where the 6th character of the device name dictates the family
     // Use this to determine the correct address to write to
     match chip.chars().nth(5).unwrap() {
@@ -344,7 +345,7 @@ fn reset_chip(chip: &str, probe: &mut dyn ArmProbe) {
 impl ArmDebugSequence for CC13xxCC26xx {
     fn reset_system(
         &self,
-        probe: &mut dyn ArmProbe,
+        probe: &mut dyn ArmMemoryInterface,
         core_type: probe_rs_target::CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -361,12 +362,12 @@ impl ArmDebugSequence for CC13xxCC26xx {
         std::thread::sleep(Duration::from_millis(1));
 
         // Re-initializing the core(s) is on us.
-        let ap = probe.ap();
+        let ap = probe.ap().ap_address().clone();
         let interface = probe.get_arm_communication_interface()?;
         interface.reinitialize()?;
 
         assert!(debug_base.is_none());
-        self.debug_core_start(interface, ap, core_type, None, None)?;
+        self.debug_core_start(interface, &ap, core_type, None, None)?;
 
         if demcr.vc_corereset() {
             // TODO! Find a way to call the armv7m::halt function instead
diff --git a/probe-rs/src/vendor/vorago/sequences/va416xx.rs b/probe-rs/src/vendor/vorago/sequences/va416xx.rs
index 899e24df..06ec8fcd 100644
--- a/probe-rs/src/vendor/vorago/sequences/va416xx.rs
+++ b/probe-rs/src/vendor/vorago/sequences/va416xx.rs
@@ -5,11 +5,11 @@ use probe_rs_target::CoreType;
 
 use crate::{
     architecture::arm::{
-        ap::MemoryAp,
+        ap::AccessPort,
         armv7m::Demcr,
-        memory::adi_v5_memory_interface::ArmProbe,
+        memory::ArmMemoryInterface,
         sequences::{cortex_m_core_start, ArmDebugSequence},
-        ArmError, ArmProbeInterface,
+        ArmError, ArmProbeInterface, FullyQualifiedApAddress,
     },
     MemoryMappedRegister,
 };
@@ -33,12 +33,12 @@ impl ArmDebugSequence for Va416xx {
     fn debug_core_start(
         &self,
         interface: &mut dyn ArmProbeInterface,
-        core_ap: MemoryAp,
+        core_ap: &FullyQualifiedApAddress,
         _core_type: CoreType,
         _debug_base: Option<u64>,
         _cti_base: Option<u64>,
     ) -> Result<(), ArmError> {
-        let mut core = interface.memory_interface(&core_ap)?;
+        let mut core = interface.memory_interface(core_ap)?;
         cortex_m_core_start(&mut *core)?;
         // Disable ROM protection
         core.write_32(0x4001_0010, &[0x000_0001])?;
@@ -57,7 +57,7 @@ impl ArmDebugSequence for Va416xx {
     /// and re-initializes the debug connection after a reset.
     fn reset_system(
         &self,
-        interface: &mut dyn ArmProbe,
+        interface: &mut dyn ArmMemoryInterface,
         core_type: CoreType,
         debug_base: Option<u64>,
     ) -> Result<(), ArmError> {
@@ -96,7 +96,7 @@ impl ArmDebugSequence for Va416xx {
         }
 
         assert!(debug_base.is_none());
-        self.debug_core_start(arm_interface, ap, core_type, None, None)?;
+        self.debug_core_start(arm_interface, ap.ap_address(), core_type, None, None)?;
 
         if demcr.vc_corereset() {
             // TODO! Find a way to call the armv7m::halt function instead
diff --git a/probe-rs/targets/STM32WBA_Series.yaml b/probe-rs/targets/STM32WBA_Series.yaml
index c8dae74b..a63fd644 100644
--- a/probe-rs/targets/STM32WBA_Series.yaml
+++ b/probe-rs/targets/STM32WBA_Series.yaml
@@ -3,8 +3,64 @@ manufacturer:
   id: 0x20
   cc: 0x0
 generated_from_pack: true
-pack_file_release: 1.2.1
+pack_file_release: 1.3.0
 variants:
+- name: STM32WBA50KEUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA50KGUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8100000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20020000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
 - name: STM32WBA52CEUx
   cores:
   - name: main
@@ -14,16 +70,45 @@ variants:
       psel: 0x0
   memory_map:
   - !Nvm
-    name: Flash
+    name: BANK_1
     range:
       start: 0x8000000
       end: 0x8080000
     cores:
     - main
     access:
+      write: false
       boot: true
   - !Ram
-    name: SRAM1_2
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA52CGUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8100000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
     range:
       start: 0x20000000
       end: 0x20020000
@@ -32,7 +117,7 @@ variants:
   flash_algorithms:
   - stm32wbax_1m_0800
   - stm32wbax_1m_0c00
-- name: STM32WBA52CEUxT
+- name: STM32WBA52KEUx
   cores:
   - name: main
     type: armv8m
@@ -41,16 +126,45 @@ variants:
       psel: 0x0
   memory_map:
   - !Nvm
-    name: Flash
+    name: BANK_1
     range:
       start: 0x8000000
       end: 0x8080000
     cores:
     - main
     access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA52KGUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8100000
+    cores:
+    - main
+    access:
+      write: false
       boot: true
   - !Ram
-    name: SRAM1_2
+    name: SRAM
     range:
       start: 0x20000000
       end: 0x20020000
@@ -59,7 +173,63 @@ variants:
   flash_algorithms:
   - stm32wbax_1m_0800
   - stm32wbax_1m_0c00
-- name: STM32WBA52CGUx
+- name: STM32WBA54CEUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA54CGUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8100000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20020000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA54KEUx
   cores:
   - name: main
     type: armv8m
@@ -68,16 +238,45 @@ variants:
       psel: 0x0
   memory_map:
   - !Nvm
-    name: Flash
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA54KGUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
     range:
       start: 0x8000000
       end: 0x8100000
     cores:
     - main
     access:
+      write: false
       boot: true
   - !Ram
-    name: SRAM1_2
+    name: SRAM
     range:
       start: 0x20000000
       end: 0x20020000
@@ -86,7 +285,35 @@ variants:
   flash_algorithms:
   - stm32wbax_1m_0800
   - stm32wbax_1m_0c00
-- name: STM32WBA52CGUxT
+- name: STM32WBA55CEUx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA55CGUx
   cores:
   - name: main
     type: armv8m
@@ -95,16 +322,17 @@ variants:
       psel: 0x0
   memory_map:
   - !Nvm
-    name: Flash
+    name: BANK_1
     range:
       start: 0x8000000
       end: 0x8100000
     cores:
     - main
     access:
+      write: false
       boot: true
   - !Ram
-    name: SRAM1_2
+    name: SRAM
     range:
       start: 0x20000000
       end: 0x20020000
@@ -113,7 +341,91 @@ variants:
   flash_algorithms:
   - stm32wbax_1m_0800
   - stm32wbax_1m_0c00
-- name: STM32WBA52CGUxU
+- name: STM32WBA55HEFx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA55HGFx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8100000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20020000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA55UEIx
+  cores:
+  - name: main
+    type: armv8m
+    core_access_options: !Arm
+      ap: 1
+      psel: 0x0
+  memory_map:
+  - !Nvm
+    name: BANK_1
+    range:
+      start: 0x8000000
+      end: 0x8080000
+    cores:
+    - main
+    access:
+      write: false
+      boot: true
+  - !Ram
+    name: SRAM
+    range:
+      start: 0x20000000
+      end: 0x20018000
+    cores:
+    - main
+  flash_algorithms:
+  - stm32wbax_1m_0800
+  - stm32wbax_1m_0c00
+- name: STM32WBA55UGIx
   cores:
   - name: main
     type: armv8m
@@ -122,16 +434,17 @@ variants:
       psel: 0x0
   memory_map:
   - !Nvm
-    name: Flash
+    name: BANK_1
     range:
       start: 0x8000000
       end: 0x8100000
     cores:
     - main
     access:
+      write: false
       boot: true
   - !Ram
-    name: SRAM1_2
+    name: SRAM
     range:
       start: 0x20000000
       end: 0x20020000


Test patch:
diff --git a/.github/workflows/smoketest.yml b/.github/workflows/smoketest.yml
index e3802375..40b3ba29 100644
--- a/.github/workflows/smoketest.yml
+++ b/.github/workflows/smoketest.yml
@@ -11,6 +11,14 @@ name: Run smoke test
 env:
   CARGO_INCREMENTAL: 0
 
+# Cancel any currently running workflows from the same PR, branch, or
+# tag when a new workflow is triggered.
+#
+# https://stackoverflow.com/a/66336834
+concurrency:
+  cancel-in-progress: true
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
+
 jobs:
   build:
     runs-on: ubuntu-latest
diff --git a/smoke-tester/src/tests.rs b/smoke-tester/src/tests.rs
index 10c1cc06..9d1184ac 100644
--- a/smoke-tester/src/tests.rs
+++ b/smoke-tester/src/tests.rs
@@ -4,7 +4,7 @@ use colored::Colorize;
 use linkme::distributed_slice;
 use probe_rs::{
     config::MemoryRegion,
-    flashing::{download_file_with_options, DownloadOptions, FlashProgress, Format},
+    flashing::{download_file_with_options, DownloadOptions, FlashProgress, FormatKind},
     Architecture, Core, MemoryInterface, Session,
 };
 
@@ -224,10 +224,7 @@ pub fn test_flashing(tracker: &TestTracker, session: &mut Session) -> Result<(),
 
     let start_time = Instant::now();
 
-    let format = match session.target().default_format {
-        probe_rs_target::BinaryFormat::Idf => Format::Idf(Default::default()),
-        probe_rs_target::BinaryFormat::Raw => Default::default(),
-    };
+    let format = FormatKind::from_optional(session.target().default_format.as_deref()).unwrap();
 
     let result = download_file_with_options(session, test_binary, format, options);
 
diff --git a/target-gen/src/commands/snapshots/target_gen__commands__elf__test__serialization_cleanup.snap b/target-gen/src/commands/snapshots/target_gen__commands__elf__test__serialization_cleanup.snap
index 0cfe0734..7fe4b7a1 100644
--- a/target-gen/src/commands/snapshots/target_gen__commands__elf__test__serialization_cleanup.snap
+++ b/target-gen/src/commands/snapshots/target_gen__commands__elf__test__serialization_cleanup.snap
@@ -35,4 +35,3 @@ variants:
     - main
     access:
       execute: false
-  default_binary_format: raw