Repository: probe-rs/probe-rs
Pull request #2418 (2024-05-02 15:41:28)

Pass Session to ESP flash size detection


Patch:
diff --git a/changelog/added-embed-config-opts.md b/changelog/added-embed-config-opts.md
new file mode 100644
index 00000000..7dd4c2c2
--- /dev/null
+++ b/changelog/added-embed-config-opts.md
@@ -0,0 +1,1 @@
+Added `verify` and `disable_double_buffering` options to Embed.toml
\ No newline at end of file
diff --git a/changelog/added-ti-cc13xx-cc26xx-support.md b/changelog/added-ti-cc13xx-cc26xx-support.md
new file mode 100644
index 00000000..84d11270
--- /dev/null
+++ b/changelog/added-ti-cc13xx-cc26xx-support.md
@@ -0,0 +1,3 @@
+Support for Texas Instruments cc13x2_cc26x2 device family (#1729)
+
+- Made JTAG scanning optional during configuration (#2215)
\ No newline at end of file
diff --git a/changelog/removed-halt_afterwards.md b/changelog/removed-halt_afterwards.md
new file mode 100644
index 00000000..84d83149
--- /dev/null
+++ b/changelog/removed-halt_afterwards.md
@@ -0,0 +1,1 @@
+Removed `halt_afterwards` from Embed.toml
\ No newline at end of file
diff --git a/probe-rs-tools/src/bin/cargo-embed.rs b/probe-rs-tools/src/bin/cargo-embed.rs
index b05a6ea9..8094dd0d 100644
--- a/probe-rs-tools/src/bin/cargo-embed.rs
+++ b/probe-rs-tools/src/bin/cargo-embed.rs
@@ -3,8 +3,13 @@ use std::os::unix::process::CommandExt;
 use std::process::{exit, Command};
 
 fn main() {
-    let mut args: Vec<_> = std::env::args_os().skip(1).collect();
-    args.insert(0, "cargo-embed".into());
+    let mut args: Vec<_> = std::env::args_os().collect();
+    // No matter what the binary is called, we need to pass `cargo-embed` to `probe-rs`.
+    args[0] = "cargo-embed".into();
+    // Also, skip the very first argument if it's `embed`, because it was inserted by cargo.
+    if args.get(1).filter(|s| *s == "embed").is_some() {
+        args.remove(1);
+    }
     let mut cmd = Command::new("probe-rs");
     cmd.args(&args);
 
diff --git a/probe-rs-tools/src/bin/cargo-flash.rs b/probe-rs-tools/src/bin/cargo-flash.rs
index 75fcb690..92f9165d 100644
--- a/probe-rs-tools/src/bin/cargo-flash.rs
+++ b/probe-rs-tools/src/bin/cargo-flash.rs
@@ -3,8 +3,13 @@ use std::os::unix::process::CommandExt;
 use std::process::{exit, Command};
 
 fn main() {
-    let mut args: Vec<_> = std::env::args_os().skip(1).collect();
-    args.insert(0, "cargo-flash".into());
+    let mut args: Vec<_> = std::env::args_os().collect();
+    // No matter what the binary is called, we need to pass `cargo-flash` to `probe-rs`.
+    args[0] = "cargo-flash".into();
+    // Also, skip the very first argument if it's `flash`, because it was inserted by cargo.
+    if args.get(1).filter(|s| *s == "flash").is_some() {
+        args.remove(1);
+    }
     let mut cmd = Command::new("probe-rs");
     cmd.args(&args);
 
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml
index adab2ab0..2928a93c 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/default.toml
@@ -13,9 +13,6 @@ protocol = "Swd"
 [default.flashing]
 # Whether or not the target should be flashed.
 enabled = true
-# Whether or not the target should be halted after reset.
-# DEPRECATED, moved to reset section
-halt_afterwards = false
 # Whether or not bytes erased but not rewritten with data from the ELF
 # should be restored with their contents before erasing.
 restore_unwritten_bytes = false
@@ -23,6 +20,10 @@ restore_unwritten_bytes = false
 # flash_layout_output_path = "out.svg"
 # Triggers a full chip erase instead of a page by page erase.
 do_chip_erase = false
+# Whether to disable double buffering
+disable_double_buffering = false
+# Whether to verify flash contents after downloading
+verify = false
 
 [default.reset]
 # Whether or not the target should be reset.
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/mod.rs b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/mod.rs
index f1d41bdb..8a3df752 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/mod.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/config/mod.rs
@@ -46,14 +46,11 @@ pub struct Probe {
 #[serde(deny_unknown_fields)]
 pub struct Flashing {
     pub enabled: bool,
-    #[deprecated(
-        since = "0.9.0",
-        note = "The 'halt_afterwards' key has moved to the 'reset' section"
-    )]
-    pub halt_afterwards: bool,
     pub restore_unwritten_bytes: bool,
     pub flash_layout_output_path: Option<String>,
     pub do_chip_erase: bool,
+    pub disable_double_buffering: bool,
+    pub verify: bool,
 }
 
 /// The reset config struct holding all the possible reset options.
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/mod.rs b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/mod.rs
index c6f2a16e..85cba272 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/mod.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/mod.rs
@@ -62,7 +62,7 @@ struct CliOptions {
     cargo_options: CargoOptions,
 }
 
-pub fn main(args: Vec<OsString>, offset: UtcOffset) {
+pub fn main(args: &[OsString], offset: UtcOffset) {
     match main_try(args, offset) {
         Ok(_) => (),
         Err(e) => {
@@ -98,15 +98,9 @@ pub fn main(args: Vec<OsString>, offset: UtcOffset) {
     }
 }
 
-fn main_try(mut args: Vec<OsString>, offset: UtcOffset) -> Result<()> {
-    // When called by Cargo, the first argument after the binary name will be `embed`.
-    // If that's the case, remove it.
-    if args.get(1).and_then(|t| t.to_str()) == Some("embed") {
-        args.remove(1);
-    }
-
+fn main_try(args: &[OsString], offset: UtcOffset) -> Result<()> {
     // Parse the commandline options.
-    let opt = CliOptions::parse_from(&args);
+    let opt = CliOptions::parse_from(args);
 
     // Change the work dir if the user asked to do so.
     if let Some(ref work_dir) = opt.work_dir {
@@ -133,14 +127,6 @@ fn main_try(mut args: Vec<OsString>, offset: UtcOffset) -> Result<()> {
             .with_context(|| format!("failed to load the chip description from {cdp}"))?;
     }
 
-    // Remove executable name from the arguments list.
-    args.remove(0);
-
-    if let Some(index) = args.iter().position(|x| x == config_name) {
-        // We remove the argument we found.
-        args.remove(index);
-    }
-
     let cargo_options = opt.cargo_options.to_cargo_options();
 
     let artifact = build_artifact(&work_dir, &cargo_options)?;
@@ -242,10 +228,10 @@ fn main_try(mut args: Vec<OsString>, offset: UtcOffset) -> Result<()> {
 
         let download_options = BinaryDownloadOptions {
             disable_progressbars: opt.disable_progressbars,
-            disable_double_buffering: false,
+            disable_double_buffering: config.flashing.disable_double_buffering,
             restore_unwritten: config.flashing.restore_unwritten_bytes,
             flash_layout_output_path: None,
-            verify: false,
+            verify: config.flashing.verify,
         };
         let format_options = FormatOptions::default();
         let loader = build_loader(&mut session, path, format_options, image_instr_set)?;
@@ -263,14 +249,7 @@ fn main_try(mut args: Vec<OsString>, offset: UtcOffset) -> Result<()> {
     if config.reset.enabled {
         let mut core = session.core(core_id)?;
         let halt_timeout = Duration::from_millis(500);
-        #[allow(deprecated)] // Remove in 0.10
-        if config.flashing.halt_afterwards {
-            logging::eprintln(format!(
-                "     {} The 'flashing.halt_afterwards' option in the config has moved to the 'reset' section",
-                "Warning".yellow().bold()
-            ));
-            core.reset_and_halt(halt_timeout)?;
-        } else if config.reset.halt_afterwards {
+        if config.reset.halt_afterwards {
             core.reset_and_halt(halt_timeout)?;
         } else {
             core.reset()?;
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_flash/mod.rs b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_flash/mod.rs
index 71a9334b..33fd6214 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_flash/mod.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_flash/mod.rs
@@ -55,10 +55,8 @@ struct CliOptions {
     pub format_options: crate::FormatOptions,
 }
 
-pub fn main(args: Vec<OsString>) {
-    let lister = Lister::new();
-
-    match main_try(args, &lister) {
+pub fn main(args: &[OsString]) {
+    match main_try(args) {
         Ok(_) => (),
         Err(e) => {
             // Ensure stderr is flushed before calling process::exit,
@@ -73,15 +71,9 @@ pub fn main(args: Vec<OsString>) {
     }
 }
 
-fn main_try(mut args: Vec<OsString>, lister: &Lister) -> Result<(), OperationError> {
-    // When called by Cargo, the first argument after the binary name will be `flash`. If that's the
-    // case, remove one argument (`Opt::from_iter` will remove the binary name by itself).
-    if args.get(1).and_then(|t| t.to_str()) == Some("flash") {
-        args.remove(1);
-    }
-
+fn main_try(args: &[OsString]) -> Result<(), OperationError> {
     // Parse the commandline options.
-    let opt = CliOptions::parse_from(&args);
+    let opt = CliOptions::parse_from(args);
 
     // Initialize the logger with the loglevel given on the commandline.
     let _log_guard = setup_logging(None, opt.log);
@@ -131,8 +123,10 @@ fn main_try(mut args: Vec<OsString>, lister: &Lister) -> Result<(), OperationErr
         path.display()
     ));
 
+    let lister = Lister::new();
+
     // Attach to specified probe
-    let (mut session, probe_options) = opt.probe_options.simple_attach(lister)?;
+    let (mut session, probe_options) = opt.probe_options.simple_attach(&lister)?;
 
     // Flash the binary
     let loader =
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/adapter.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/adapter.rs
index effbae46..4aee6302 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/adapter.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/adapter.rs
@@ -486,7 +486,7 @@ impl<P: ProtocolAdapter> DebugAdapter<P> {
                             response_body.memory_reference =
                                 Some(variable.memory_location.to_string());
                             response_body.named_variables = Some(named_child_variables_cnt);
-                            response_body.result = variable.get_value(variable_cache);
+                            response_body.result = variable.to_string(variable_cache);
                             response_body.type_ = Some(variable.type_name());
                             response_body.variables_reference = variables_reference.into();
                         } else {
@@ -1523,7 +1523,7 @@ impl<P: ProtocolAdapter> DebugAdapter<P> {
                         named_variables: Some(named_child_variables_cnt),
                         presentation_hint: None,
                         type_: Some(variable.type_name()),
-                        value: variable.get_value(variable_cache),
+                        value: variable.to_string(variable_cache),
                         variables_reference: variables_reference.into(),
                     }
                 })
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/repl_commands_helpers.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/repl_commands_helpers.rs
index 5ed0ad6c..202e8f3a 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/repl_commands_helpers.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/repl_commands_helpers.rs
@@ -88,7 +88,7 @@ pub(crate) fn get_local_variable(
             response_body.result = format!(
                 "{} : {} ",
                 variable.name,
-                variable.get_value(variable_cache)
+                variable.to_string(variable_cache)
             );
             response_body.type_ = Some(variable.type_name());
             response_body.variables_reference = variable.variable_key().into();
@@ -98,7 +98,7 @@ pub(crate) fn get_local_variable(
                 variable.name,
                 variable.type_name(),
                 variable.memory_location,
-                variable.get_value(variable_cache)
+                variable.to_string(variable_cache)
             ));
         }
     }
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/request_helpers.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/request_helpers.rs
index 5c2718ef..7e05e1b9 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/request_helpers.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/debug_adapter/dap/request_helpers.rs
@@ -368,7 +368,7 @@ pub(crate) fn get_variable_reference(
             indexed_child_variables_cnt,
         )
     } else if parent_variable.variable_node_type.is_deferred()
-        && parent_variable.get_value(cache) != "()"
+        && parent_variable.to_string(cache) != "()"
     {
         // We have not yet cached the children for this reference.
         // Provide DAP Client with a reference so that it will explicitly ask for children when the user expands it.
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/debug.rs b/probe-rs-tools/src/bin/probe-rs/cmd/debug.rs
index 69b93c19..f2f44f5e 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/debug.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/debug.rs
@@ -791,7 +791,7 @@ impl DebugCli {
                                 "{}: {} = {}",
                                 child.name,
                                 child.type_name(),
-                                child.get_value(local_variable_cache)
+                                child.to_string(local_variable_cache)
                             );
                         }
                     }
diff --git a/probe-rs-tools/src/bin/probe-rs/main.rs b/probe-rs-tools/src/bin/probe-rs/main.rs
index 6c5c092e..335b317e 100644
--- a/probe-rs-tools/src/bin/probe-rs/main.rs
+++ b/probe-rs-tools/src/bin/probe-rs/main.rs
@@ -214,18 +214,19 @@ fn prune_logs(directory: &Path) -> Result<(), anyhow::Error> {
     Ok(())
 }
 
-/// Returns the cleaned arguments for the handler of the respective end binary (cli, cargo-flash, cargo-embed, etc.).
-fn multicall_check(args: &[OsString], want: &str) -> Option<Vec<OsString>> {
+/// Returns the cleaned arguments for the handler of the respective end binary
+/// (cli, cargo-flash, cargo-embed, etc.)
+fn multicall_check<'list>(args: &'list [OsString], want: &str) -> Option<&'list [OsString]> {
     let argv0 = Path::new(&args[0]);
     if let Some(command) = argv0.file_stem().and_then(|f| f.to_str()) {
         if command == want {
-            return Some(args.to_vec());
+            return Some(args);
         }
     }
 
     if let Some(command) = args.get(1).and_then(|f| f.to_str()) {
         if command == want {
-            return Some(args[1..].to_vec());
+            return Some(&args[1..]);
         }
     }
 
@@ -309,3 +310,31 @@ fn main() -> Result<()> {
         Subcommand::Complete(cmd) => cmd.run(&lister),
     }
 }
+
+#[cfg(test)]
+mod test {
+    use crate::multicall_check;
+
+    #[test]
+    fn argument_preprocessing() {
+        fn os_strs(args: &[&str]) -> Vec<std::ffi::OsString> {
+            args.iter().map(|s| s.into()).collect()
+        }
+
+        // cargo embed -h
+        assert_eq!(
+            multicall_check(&os_strs(&["probe-rs", "cargo-embed", "-h"]), "cargo-embed").unwrap(),
+            os_strs(&["cargo-embed", "-h"])
+        );
+
+        // cargo flash --chip esp32c2
+        assert_eq!(
+            multicall_check(
+                &os_strs(&["probe-rs", "cargo-flash", "--chip", "esp32c2"]),
+                "cargo-flash"
+            )
+            .unwrap(),
+            os_strs(&["cargo-flash", "--chip", "esp32c2"])
+        );
+    }
+}
diff --git a/probe-rs/Cargo.toml b/probe-rs/Cargo.toml
index 939734ff..1d4bed10 100644
--- a/probe-rs/Cargo.toml
+++ b/probe-rs/Cargo.toml
@@ -21,7 +21,7 @@ exclude = ["tests/"]
 
 [features]
 default = ["builtin-targets"]
-gdb-server = ["dep:gdbstub", "dep:itertools", "dep:parking_lot"]
+gdb-server = ["dep:gdbstub", "dep:parking_lot"]
 
 
 # Enable all built in targets.
@@ -48,6 +48,7 @@ hidapi = { version = "2", default-features = false, features = [
     "linux-native",
 ] }
 ihex = "3.0"
+itertools = "0.12.1"
 jep106 = "0.2"
 once_cell = "1"
 miniz_oxide = "0.7"
@@ -84,7 +85,6 @@ probe-rs-target = { workspace = true }
 
 # gdb server
 gdbstub = { version = "0.7", optional = true }
-itertools = { version = "0.12", optional = true }
 parking_lot = { version = "0.12.2", optional = true }
 
 [build-dependencies]
diff --git a/probe-rs/src/architecture/arm/communication_interface.rs b/probe-rs/src/architecture/arm/communication_interface.rs
index f66dcea3..6909ee16 100644
--- a/probe-rs/src/architecture/arm/communication_interface.rs
+++ b/probe-rs/src/architecture/arm/communication_interface.rs
@@ -173,7 +173,20 @@ impl ArmDebugState for Uninitialized {}
 impl ArmDebugState for Initialized {
     fn disconnect(&mut self, probe: &mut dyn DapProbe) {
         let stop_span = tracing::debug_span!("debug_port_stop").entered();
-        self.sequence.debug_port_stop(probe).ok();
+
+        // Stop the current DP, which may not be one of the known ones (i.e. RP2040 rescue DP).
+        self.sequence.debug_port_stop(probe, self.current_dp).ok();
+
+        // Stop all intentionally-connected DPs.
+        for dp in self.dps.keys() {
+            // Try to select the debug port we want to shut down.
+            if self.sequence.debug_port_connect(probe, *dp).is_ok() {
+                self.sequence.debug_port_stop(probe, *dp).ok();
+            } else {
+                tracing::warn!("Failed to stop DP {:x?}", dp);
+            }
+        }
+        probe.raw_flush().ok();
         drop(stop_span);
     }
 }
@@ -563,10 +576,6 @@ impl<'interface> ArmCommunicationInterface<Initialized> {
 
             self.probe_mut().raw_flush()?;
 
-            let stop_span = tracing::debug_span!("debug_port_stop").entered();
-            sequence.debug_port_stop(&mut *self.probe_mut())?;
-            drop(stop_span);
-
             // Try to switch to the new DP.
             if let Err(e) = sequence.debug_port_connect(&mut *self.probe_mut(), dp) {
                 tracing::warn!("Failed to switch to DP {:x?}: {}", dp, e);
diff --git a/probe-rs/src/architecture/arm/sequences.rs b/probe-rs/src/architecture/arm/sequences.rs
index 448c9aee..fedeb2b9 100644
--- a/probe-rs/src/architecture/arm/sequences.rs
+++ b/probe-rs/src/architecture/arm/sequences.rs
@@ -518,7 +518,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
                     interface.jtag_sequence(1, false, 0x01)?;
 
                     // Configure JTAG IR lengths in probe
-                    interface.configure_jtag()?;
+                    interface.configure_jtag(false)?;
                 }
                 Some(WireProtocol::Swd) => {
                     if has_dormant {
@@ -589,7 +589,7 @@ pub trait ArmDebugSequence: Send + Sync + Debug {
         let powered_down = !(ctrl.csyspwrupack() && ctrl.cdbgpwrupack());
 
         if powered_down {
-            tracing::debug!("Debug port is powered down, powering up");
+            tracing::info!("Debug port {dp:x?} is powered down, powering up");
             let mut ctrl = Ctrl(0);
             ctrl.set_cdbgpwrupreq(true);
             ctrl.set_csyspwrupreq(true);
@@ -817,14 +817,25 @@ 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#debugPortStop
     #[doc(alias = "DebugPortStop")]
-    fn debug_port_stop(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
+    fn debug_port_stop(&self, interface: &mut dyn DapProbe, dp: DpAddress) -> Result<(), ArmError> {
+        tracing::info!("Powering down debug port {dp:x?}");
         // Select Bank 0
         interface.raw_write_register(PortType::DebugPort, Select::ADDRESS, 0)?;
 
         // De-assert debug power request
         interface.raw_write_register(PortType::DebugPort, Ctrl::ADDRESS, 0)?;
 
-        Ok(())
+        // Wait for the power domains to go away
+        let start = Instant::now();
+        while start.elapsed() < Duration::from_micros(1_000_000) {
+            let ctrl = interface.raw_read_register(PortType::DebugPort, Ctrl::ADDRESS)?;
+            let ctrl = Ctrl(ctrl);
+            if !(ctrl.csyspwrupack() || ctrl.cdbgpwrupack()) {
+                return Ok(());
+            }
+        }
+
+        Err(ArmError::Timeout)
     }
 
     /// Perform a SWD line reset or enter the JTAG Run-Test-Idle state, and then try to connect to a debug port.
diff --git a/probe-rs/src/architecture/arm/traits.rs b/probe-rs/src/architecture/arm/traits.rs
index 8dcef401..54b99622 100644
--- a/probe-rs/src/architecture/arm/traits.rs
+++ b/probe-rs/src/architecture/arm/traits.rs
@@ -129,7 +129,7 @@ pub trait RawDapAccess {
     }
 
     /// Configures the probe for JTAG use (specifying IR lengths of each DAP).
-    fn configure_jtag(&mut self) -> Result<(), DebugProbeError> {
+    fn configure_jtag(&mut self, _skip_scan: bool) -> Result<(), DebugProbeError> {
         Ok(())
     }
 
diff --git a/probe-rs/src/architecture/riscv/sequences.rs b/probe-rs/src/architecture/riscv/sequences.rs
index 1c8a3179..8b8788c1 100644
--- a/probe-rs/src/architecture/riscv/sequences.rs
+++ b/probe-rs/src/architecture/riscv/sequences.rs
@@ -2,6 +2,7 @@
 
 use crate::architecture::riscv::communication_interface::RiscvError;
 use crate::architecture::riscv::Dmcontrol;
+use crate::Session;
 
 use super::communication_interface::RiscvCommunicationInterface;
 use std::fmt::Debug;
@@ -18,10 +19,7 @@ pub trait RiscvDebugSequence: Send + Sync + Debug {
     }
 
     /// Detects the flash size of the target.
-    fn detect_flash_size(
-        &self,
-        _interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
+    fn detect_flash_size(&self, _session: &mut Session) -> Result<Option<usize>, crate::Error> {
         Ok(None)
     }
 
diff --git a/probe-rs/src/architecture/xtensa/sequences.rs b/probe-rs/src/architecture/xtensa/sequences.rs
index 9969f75e..e0771f6b 100644
--- a/probe-rs/src/architecture/xtensa/sequences.rs
+++ b/probe-rs/src/architecture/xtensa/sequences.rs
@@ -3,6 +3,7 @@ use std::{fmt::Debug, sync::Arc, time::Duration};
 use crate::architecture::xtensa::communication_interface::{
     ProgramStatus, XtensaCommunicationInterface, XtensaError,
 };
+use crate::Session;
 
 /// A interface to operate debug sequences for Xtensa targets.
 ///
@@ -17,10 +18,7 @@ pub trait XtensaDebugSequence: Send + Sync + Debug {
     }
 
     /// Detects the flash size of the target.
-    fn detect_flash_size(
-        &self,
-        _interface: &mut XtensaCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
+    fn detect_flash_size(&self, _session: &mut Session) -> Result<Option<usize>, crate::Error> {
         Ok(None)
     }
 
diff --git a/probe-rs/src/config/sequences/cc13xx_cc26xx.rs b/probe-rs/src/config/sequences/cc13xx_cc26xx.rs
new file mode 100644
index 00000000..c2046841
--- /dev/null
+++ b/probe-rs/src/config/sequences/cc13xx_cc26xx.rs
@@ -0,0 +1,405 @@
+//! Sequences for cc13xx_cc26xx devices
+use std::sync::Arc;
+use std::time::Duration;
+
+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::sequences::{ArmDebugSequence, ArmDebugSequenceError};
+use crate::architecture::arm::{ArmError, DpAddress};
+use crate::probe::{DebugProbeError, WireProtocol};
+use crate::MemoryMappedRegister;
+
+/// Marker struct indicating initialization sequencing for cc13xx_cc26xx family parts.
+#[derive(Debug)]
+pub struct CC13xxCC26xx {}
+
+// IR register values, see https://www.ti.com/lit/ug/swcu185f/swcu185f.pdf table 6-7
+const IR_ROUTER: u64 = 0x02;
+const IR_CONNECT: u64 = 0x07;
+const IR_BYPASS: u64 = 0x3F;
+const IR_LEN_IN_BITS: u8 = 6;
+
+#[derive(PartialEq)]
+enum JtagState {
+    RunTestIdle = 0x1,
+    SelectDRScan = 0x2,
+    ShiftDR = 0x03,
+    ShiftIR = 0x04,
+}
+
+// Set the bottom n bits of a u64 to 1
+// This is lifted directly from:
+// https://users.rust-lang.org/t/how-to-make-an-integer-with-n-bits-set-without-overflow/63078/6
+fn set_n_bits(x: u32) -> u64 {
+    u64::checked_shl(1, x).unwrap_or(0).wrapping_sub(1)
+}
+
+impl CC13xxCC26xx {
+    /// Create the sequencer for the cc13xx_cc26xx family of parts.
+    pub fn create() -> Arc<Self> {
+        Arc::new(Self {})
+    }
+
+    /// This function implements a Zero Bit Scan(ZBS)
+    ///
+    /// The ZBS defined in section 6.2.2.1 of this document:
+    /// https://www.ti.com/lit/ug/swcu185f/swcu185f.pdf
+    ///
+    /// This function assumes that the JTAG state machine is in the Run-Test/Idle state
+    ///
+    /// * `interface` - Reference to interface to interact with CmsisDap
+    fn zero_bit_scan(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
+        // Enter DRSELECT state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter DRCAPTURE state
+        interface.jtag_sequence(1, false, 0x01)?;
+        // Enter DREXIT1 state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter DRPAUSE state
+        interface.jtag_sequence(1, false, 0x01)?;
+        // Enter DREXIT2 state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter DRUPDATE state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter Run/Idle state
+        interface.jtag_sequence(1, false, 0x01)?;
+
+        Ok(())
+    }
+    /// Load a value into the IR register
+    ///
+    /// This function is a wrapper on `shift_reg` that loads a value into the IR register
+    ///
+    /// * `interface` - Reference to interface to interact with CmsisDap
+    /// * `cycles`    - Number of TCK cycles to shift in the data to IR
+    /// * `reg`       - The value to shift into either IR
+    /// * `state`     - The current state of the JTAG state machine. Note this will be updated by this function so that the
+    ///                state is correct after the function returns
+    /// * `end_state` - The state to end in, this can either be `JtagState::RunTestIdle` or `JtagState::SelectDRScan`
+    fn shift_ir(
+        &self,
+        interface: &mut dyn DapProbe,
+        ir: u64,
+        state: &mut JtagState,
+        end_state: JtagState,
+    ) -> Result<(), ArmError> {
+        // THis is a wrapper around shift_reg that loads the IR register
+        self.shift_reg(
+            interface,
+            IR_LEN_IN_BITS,
+            ir,
+            state,
+            JtagState::ShiftIR,
+            end_state,
+        )?;
+
+        Ok(())
+    }
+
+    /// Load a value into the DR register
+    ///
+    /// This function is a wrapper on `shift_reg` that loads a value into the DR register
+    ///
+    /// * `interface` - Reference to interface to interact with CmsisDap
+    /// * `cycles`    - Number of TCK cycles to shift in the data to DR
+    /// * `reg`       - The value to shift into either DR
+    /// * `state`     - The current state of the JTAG state machine. Note this will be updated by this function so that the
+    ///                state is correct after the function returns
+    /// * `end_state` - The state to end in, this can either be `JtagState::RunTestIdle` or `JtagState::SelectDRScan`
+    fn shift_dr(
+        &self,
+        interface: &mut dyn DapProbe,
+        cycles: u8,
+        reg: u64,
+        state: &mut JtagState,
+        end_state: JtagState,
+    ) -> Result<(), ArmError> {
+        self.shift_reg(interface, cycles, reg, state, JtagState::ShiftDR, end_state)?;
+        Ok(())
+    }
+    /// Load a value into the IR or DR register
+    ///
+    /// This function moves through the JTAG state machine to load a value into
+    /// the IR or DR register. The function assumes that the JTAG state machine is in
+    /// either the Run-Test/Idle or Select-DR-Scan state.
+    ///
+    /// * `interface` - Reference to interface to interact with CmsisDap
+    /// * `cycles`    - Number of TCK cycles to shift in the data to either IR or DR
+    /// * `reg`       - The value to shift into either IR or DR
+    /// * `state`     - The current state of the JTAG state machine. Note this will be updated by this function so that the
+    ///                 state is correct after the function returns
+    /// * `action`    - Whether to load the IR or DR register, if IR is wanted then `JtagState::ShiftIR` should be passed
+    ///                 otherwise the default is to load DR.
+    /// * `end_state` - The state to end in, this can either be `JtagState::RunTestIdle` or `JtagState::SelectDRScan`
+    fn shift_reg(
+        &self,
+        interface: &mut dyn DapProbe,
+        cycles: u8,
+        reg: u64,
+        state: &mut JtagState,
+        action: JtagState,
+        end_state: JtagState,
+    ) -> Result<(), ArmError> {
+        if *state == JtagState::RunTestIdle {
+            // Enter DR-Scan state
+            interface.jtag_sequence(1, true, 0x01)?;
+        }
+        if action == JtagState::ShiftIR {
+            // Enter IR-Scan state,
+            interface.jtag_sequence(1, true, 0x01)?;
+        }
+        // Enter DR or IR CAPTURE state
+        interface.jtag_sequence(1, false, 0x01)?;
+        // Enter DR or IR EXIT1 state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter DRor IR PAUSE state
+        interface.jtag_sequence(1, false, 0x01)?;
+        // Enter DR or IR EXIT2 state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter DR or IR SHIFT state
+        interface.jtag_sequence(1, false, 0x01)?;
+        for i in 0..cycles {
+            // On the last cycle we want to leave the shift state
+            let tms = i == cycles - 1;
+            // Mask the register value to get the bit we want to shift in
+            let reg_masked = (reg & (0x01 << u64::from(i))) != 0;
+            // Send to the probe
+            interface.jtag_sequence(1, tms, u64::from(reg_masked))?;
+        }
+        // Enter DR or IR UPDATE state
+        interface.jtag_sequence(1, true, 0x01)?;
+        // Enter either run-test-idle or select-dr-scan depending on the end state
+        interface.jtag_sequence(1, end_state == JtagState::SelectDRScan, 0x01)?;
+
+        // Update the state to the desired end state
+        *state = end_state;
+
+        Ok(())
+    }
+
+    /// Reads or writes a given register using the ICEPICK router
+    ///
+    /// This function is used to load the router register of the ICEPICK TAP
+    /// and connect a given data register to the TDO.
+    ///
+    /// This is a direct port from the openocd implementation:
+    /// https://github.com/openocd-org/openocd/blob/master/tcl/target/icepick.cfg#L56-L70
+    ///
+    /// * `interface` - Reference to interface to interact with CmsisDap
+    /// * `rw`        - 0 for read, 1 for write
+    /// * `block`     - The register block to access
+    /// * `register`  - The register to access
+    /// * `payload`   - The data to write to the register
+    /// * `state`     - The current state of the JTAG state machine. Note this will be updated by this function
+    ///                so that the state is correct after the function returns
+    fn icepick_router(
+        &self,
+        interface: &mut dyn DapProbe,
+        rw: u32,
+        block: u32,
+        register: u32,
+        payload: u32,
+        state: &mut JtagState,
+    ) -> Result<(), ArmError> {
+        // Build the DR value based on the requested operation. The DR value
+        // is based on the input arguments and contains several bitfields
+        let dr: u32 = ((rw & 0x1) << 31)
+            | ((block & 0x7) << 28)
+            | ((register & 0xF) << 24)
+            | (payload & 0xFFFFFF);
+
+        // Load IR with the router instruction, this allows us to write or read a data register
+        self.shift_ir(interface, IR_ROUTER, state, JtagState::SelectDRScan)?;
+        // Load the data register with the register block, address, and data
+        // The address and value to be written is encoded in the DR value
+        self.shift_dr(interface, 32, dr as u64, state, JtagState::SelectDRScan)?;
+        Ok(())
+    }
+
+    /// Does setup of the ICEPICK
+    ///
+    /// This will setup the ICEPICK to have the CPU/DAP on the scan chain and
+    /// also power and enable the debug interface for use with probe-rs
+    ///
+    /// This is a direct port of the openocd implementation:
+    /// https://github.com/openocd-org/openocd/blob/master/tcl/target/icepick.cfg#L81-L124
+    /// A few things were removed to fit the cc13xx_cc26xx family.
+    fn enable_icepick(
+        &self,
+        interface: &mut dyn DapProbe,
+        state: &mut JtagState,
+    ) -> Result<(), ArmError> {
+        let port = 0;
+        let block = 0x02;
+
+        // Select the Connect register
+        self.shift_ir(interface, IR_CONNECT, state, JtagState::SelectDRScan)?;
+        // Enable write, set the `ConnectKey` to 0b1001 (0x9) as per TRM section 6.3.3
+        self.shift_dr(interface, 8, 0x89, state, JtagState::SelectDRScan)?;
+        // Write to register 1 in the ICEPICK control block - keep JTAG powered in test logic reset
+        self.icepick_router(interface, 1, 0, 1, 0x000080, state)?;
+        // Write to register 0 in the Debug TAP linking block (Section 6.3.4.3)
+        // Namely:
+        // * [20]   : `InhibitSleep`
+        // * [16:14]: `ResetControl == Wait In Reset`
+        // * [8]    : `SelectTAP == 1`
+        // * [3]    : `ForceActive == Enable clocks`
+        self.icepick_router(interface, 1, block, port, 0x110108, state)?;
+
+        // Enter the bypass state
+        self.shift_ir(interface, IR_BYPASS, state, JtagState::RunTestIdle)?;
+
+        // Remain in run-test idle for 10 cycles
+        interface.jtag_sequence(10, false, set_n_bits(10))?;
+
+        Ok(())
+    }
+    fn ctag_to_jtag(
+        &self,
+        interface: &mut dyn DapProbe,
+        jtag_state: &mut JtagState,
+    ) -> Result<(), ArmError> {
+        // Load IR with BYPASS
+        self.shift_ir(interface, IR_BYPASS, jtag_state, JtagState::RunTestIdle)?;
+
+        // cJTAG: Open Command Window
+        // This is described in section 6.2.2.1 of this document:
+        // https://www.ti.com/lit/ug/swcu185f/swcu185f.pdf
+        // Also refer to the openocd implementation:
+        // https://github.com/openocd-org/openocd/blob/master/tcl/target/ti-cjtag.cfg#L6-L35
+        self.zero_bit_scan(interface)?;
+        self.zero_bit_scan(interface)?;
+        self.shift_dr(interface, 1, 0x01, jtag_state, JtagState::RunTestIdle)?;
+
+        // cJTAG: Switch to 4 pin
+        // This is described in section 6.2.2.2 of this document:
+        // https://www.ti.com/lit/ug/swcu185f/swcu185f.pdf
+        // Also refer to the openocd implementation:
+        // https://github.com/openocd-org/openocd/blob/master/tcl/target/ti-cjtag.cfg#L6-L35
+        self.shift_dr(
+            interface,
+            2,
+            set_n_bits(2),
+            jtag_state,
+            JtagState::RunTestIdle,
+        )?;
+        self.shift_dr(
+            interface,
+            9,
+            set_n_bits(9),
+            jtag_state,
+            JtagState::RunTestIdle,
+        )?;
+
+        // Load IR with BYPASS so that future state transitions don't affect IR
+        self.shift_ir(interface, IR_BYPASS, jtag_state, JtagState::RunTestIdle)?;
+
+        // Connect CPU DAP to top level TAP
+        // This is done by interacting with the top level TAP which is called ICEPICK
+        // Some resouces on the ICEPICK, note that the cc13xx_cc26xx family implements ICEPICK-C
+        // https://www.ti.com/lit/ug/swcu185f/swcu185f.pdf, Section 6.3
+        // https://software-dl.ti.com/ccs/esd/documents/xdsdebugprobes/emu_icepick.html
+        self.enable_icepick(interface, jtag_state)?;
+
+        Ok(())
+    }
+}
+
+impl ArmDebugSequence for CC13xxCC26xx {
+    fn reset_system(
+        &self,
+        probe: &mut dyn ArmProbe,
+        core_type: probe_rs_target::CoreType,
+        debug_base: Option<u64>,
+    ) -> Result<(), ArmError> {
+        // Check if the previous code requested a halt before reset
+        let demcr = Demcr(probe.read_word_32(Demcr::get_mmio_address())?);
+
+        // Do a full system reset (emulated PIN reset)
+        // CPU reset alone is not possible since AIRCR.SYSRESETREQ will be
+        // converted to system reset on these devices.
+        //
+        // The below code writes to the following bit
+        // AON_PMCTL.RESETCTL.SYSRESET=1
+        probe.write_word_32(0x4009_0028, 0x8000_0000).ok();
+
+        // Since the system went down, including the debug, we should flush any pending operations
+        probe.flush().ok();
+
+        // Wait for the system to reset
+        std::thread::sleep(Duration::from_millis(1));
+
+        // Re-initializing the core(s) is on us.
+        let ap = probe.ap();
+        let interface = probe.get_arm_communication_interface()?;
+        interface.reinitialize()?;
+
+        assert!(debug_base.is_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
+            let mut value = Dhcsr(0);
+            value.set_c_halt(true);
+            value.set_c_debugen(true);
+            value.enable_write();
+
+            probe.write_word_32(Dhcsr::get_mmio_address(), value.into())?;
+        }
+
+        Ok(())
+    }
+
+    fn debug_port_setup(
+        &self,
+        interface: &mut dyn DapProbe,
+        _dp: DpAddress,
+    ) -> Result<(), ArmError> {
+        // Ensure current debug interface is in reset state.
+        interface.swj_sequence(51, 0x0007_FFFF_FFFF_FFFF)?;
+
+        match interface.active_protocol() {
+            Some(WireProtocol::Jtag) => {
+                let mut jtag_state: JtagState = JtagState::RunTestIdle;
+
+                // Enter Run-Test-Idle state, quit early if jtag_sequence is not supported
+                if let Err(DebugProbeError::CommandNotSupportedByProbe {
+                    command_name: "jtag_sequence",
+                }) = interface.jtag_sequence(1, false, 0x00)
+                {
+                    tracing::error!(
+                        "TI devices require a probe that supports the jtag_sequence command"
+                    );
+                    return Err(ArmError::Probe(
+                        DebugProbeError::CommandNotSupportedByProbe {
+                            command_name: "jtag_sequence",
+                        },
+                    ));
+                }
+                self.ctag_to_jtag(interface, &mut jtag_state)?;
+
+                // Call the configure JTAG function. We don't derive the scan chain at runtime
+                // for these devices, but regardless the scan chain must be told to the debug probe
+                // We avoid the live scan for the following reasons:
+                // 1. Only the ICEPICK is connected at boot so we need to manually the CPU to the scan chain
+                // 2. Entering test logic reset disconects the CPU again
+                interface.configure_jtag(true)?;
+            }
+            Some(WireProtocol::Swd) => {
+                return Err(ArmDebugSequenceError::SequenceSpecific(
+                    "The cc13xx_cc26xx family doesn't support SWD".into(),
+                )
+                .into());
+            }
+            _ => {
+                return Err(ArmDebugSequenceError::SequenceSpecific(
+                    "Cannot detect current protocol".into(),
+                )
+                .into());
+            }
+        }
+
+        Ok(())
+    }
+}
diff --git a/probe-rs/src/config/sequences/esp.rs b/probe-rs/src/config/sequences/esp.rs
index 4eb74550..a5066187 100644
--- a/probe-rs/src/config/sequences/esp.rs
+++ b/probe-rs/src/config/sequences/esp.rs
@@ -1,20 +1,14 @@
 use std::time::Duration;
 
-use probe_rs_target::{Chip, MemoryRegion};
+use probe_rs_target::{Architecture, Chip, MemoryRegion};
 
 use crate::{
-    architecture::{
-        riscv::communication_interface::RiscvCommunicationInterface,
-        xtensa::{
-            arch::{
-                instruction::{into_binary, Instruction},
-                CpuRegister, Register,
-            },
-            communication_interface::XtensaCommunicationInterface,
-            sequences::XtensaDebugSequence,
-        },
+    architecture::xtensa::arch::{
+        instruction::{into_binary, Instruction},
+        CpuRegister, Register,
     },
-    MemoryInterface,
+    config::DebugSequence,
+    MemoryInterface, Session,
 };
 
 #[derive(Debug)]
@@ -43,51 +37,40 @@ impl EspFlashSizeDetector {
 
     pub fn detect_flash_size_esp32(
         &self,
-        interface: &mut XtensaCommunicationInterface,
-        sequence: &impl XtensaDebugSequence,
+        session: &mut Session,
     ) -> Result<Option<usize>, crate::Error> {
         tracing::info!("Detecting flash size");
         attach_flash_xtensa(
-            interface,
+            session,
             self.stack_pointer,
             self.load_address,
             self.attach_fn,
-            sequence,
         )?;
-        detect_flash_size_esp32(interface, self.spiflash_peripheral)
-    }
 
-    pub fn detect_flash_size_xtensa(
-        &self,
-        interface: &mut XtensaCommunicationInterface,
-        sequence: &impl XtensaDebugSequence,
-    ) -> Result<Option<usize>, crate::Error> {
-        tracing::info!("Detecting flash size");
-        attach_flash_xtensa(
-            interface,
-            self.stack_pointer,
-            self.load_address,
-            self.attach_fn,
-            sequence,
-        )?;
         tracing::info!("Flash attached");
-        detect_flash_size(interface, self.spiflash_peripheral)
+        detect_flash_size_esp32(session, self.spiflash_peripheral)
     }
 
-    pub fn detect_flash_size_riscv(
-        &self,
-        interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        interface.halt(Duration::from_millis(100))?;
-
+    pub fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
         tracing::info!("Detecting flash size");
-        attach_flash_riscv(interface, self.stack_pointer, self.attach_fn)?;
-        detect_flash_size(interface, self.spiflash_peripheral)
+        if session.target().architecture() == Architecture::Xtensa {
+            attach_flash_xtensa(
+                session,
+                self.stack_pointer,
+                self.load_address,
+                self.attach_fn,
+            )?;
+        } else {
+            attach_flash_riscv(session, self.stack_pointer, self.attach_fn)?;
+        }
+
+        tracing::info!("Flash attached");
+        detect_flash_size(session, self.spiflash_peripheral)
     }
 }
 
 fn attach_flash_riscv(
-    interface: &mut RiscvCommunicationInterface,
+    session: &mut Session,
     stack_pointer: u32,
     attach_fn: u32,
 ) -> Result<(), crate::Error> {
@@ -97,6 +80,9 @@ fn attach_flash_riscv(
         registers::SP,
     };
 
+    let interface = &mut session.get_riscv_interface(0)?;
+    interface.halt(Duration::from_millis(100))?;
+
     // Set a valid-ish stack pointer
     interface.abstract_cmd_register_write(SP, stack_pointer)?;
 
@@ -124,12 +110,17 @@ fn attach_flash_riscv(
 }
 
 fn attach_flash_xtensa(
-    interface: &mut XtensaCommunicationInterface,
+    session: &mut Session,
     stack_pointer: u32,
     load_addr: u32,
     attach_fn: u32,
-    sequence: &impl XtensaDebugSequence,
 ) -> Result<(), crate::Error> {
+    // TODO: we shouldn't need to touch sequences here.
+    let DebugSequence::Xtensa(sequence) = session.target().debug_sequence.clone() else {
+        unreachable!()
+    };
+    let interface = &mut session.get_xtensa_interface(0)?;
+
     // We're very intrusive here but the flashing process should reset the MCU again anyway
     sequence.reset_system_and_halt(interface, Duration::from_millis(500))?;
 
@@ -257,13 +248,13 @@ fn execute_flash_command_generic(
 }
 
 fn detect_flash_size(
-    interface: &mut impl MemoryInterface,
+    session: &mut Session,
     spiflash_addr: u32,
 ) -> Result<Option<usize>, crate::Error> {
     const RDID: u8 = 0x9F;
 
     let value = execute_flash_command_generic(
-        interface,
+        &mut session.core(0)?,
         &SpiRegisters {
             base: spiflash_addr,
             cmd: 0x00,
@@ -283,13 +274,13 @@ fn detect_flash_size(
 }
 
 fn detect_flash_size_esp32(
-    interface: &mut impl MemoryInterface,
+    session: &mut Session,
     spiflash_addr: u32,
 ) -> Result<Option<usize>, crate::Error> {
     const RDID: u8 = 0x9F;
 
     let value = execute_flash_command_generic(
-        interface,
+        &mut session.core(0)?,
         &SpiRegisters {
             base: spiflash_addr,
             cmd: 0x00,
diff --git a/probe-rs/src/config/sequences/esp32.rs b/probe-rs/src/config/sequences/esp32.rs
index 1d785b54..fba148e3 100644
--- a/probe-rs/src/config/sequences/esp32.rs
+++ b/probe-rs/src/config/sequences/esp32.rs
@@ -9,7 +9,7 @@ use crate::{
         communication_interface::XtensaCommunicationInterface, sequences::XtensaDebugSequence,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32.
@@ -65,10 +65,7 @@ impl XtensaDebugSequence for ESP32 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut XtensaCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_esp32(interface, self)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size_esp32(session)
     }
 }
diff --git a/probe-rs/src/config/sequences/esp32c2.rs b/probe-rs/src/config/sequences/esp32c2.rs
index ce026dc1..c7d71d3f 100644
--- a/probe-rs/src/config/sequences/esp32c2.rs
+++ b/probe-rs/src/config/sequences/esp32c2.rs
@@ -10,7 +10,7 @@ use crate::{
         Dmcontrol,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32C2.
@@ -55,11 +55,8 @@ impl RiscvDebugSequence for ESP32C2 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_riscv(interface)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 
     fn reset_system_and_halt(
diff --git a/probe-rs/src/config/sequences/esp32c3.rs b/probe-rs/src/config/sequences/esp32c3.rs
index 7b7e7b67..e406d46d 100644
--- a/probe-rs/src/config/sequences/esp32c3.rs
+++ b/probe-rs/src/config/sequences/esp32c3.rs
@@ -10,7 +10,7 @@ use crate::{
         Dmcontrol,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32C3.
@@ -60,11 +60,8 @@ impl RiscvDebugSequence for ESP32C3 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_riscv(interface)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 
     fn reset_system_and_halt(
diff --git a/probe-rs/src/config/sequences/esp32c6.rs b/probe-rs/src/config/sequences/esp32c6.rs
index f9e50950..67f8a39e 100644
--- a/probe-rs/src/config/sequences/esp32c6.rs
+++ b/probe-rs/src/config/sequences/esp32c6.rs
@@ -11,7 +11,7 @@ use crate::{
         Dmcontrol,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32C6.
@@ -61,11 +61,8 @@ impl RiscvDebugSequence for ESP32C6 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_riscv(interface)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 
     fn reset_system_and_halt(
diff --git a/probe-rs/src/config/sequences/esp32h2.rs b/probe-rs/src/config/sequences/esp32h2.rs
index 66c3fcf5..09c94153 100644
--- a/probe-rs/src/config/sequences/esp32h2.rs
+++ b/probe-rs/src/config/sequences/esp32h2.rs
@@ -11,7 +11,7 @@ use crate::{
         Dmcontrol,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32H2.
@@ -61,11 +61,8 @@ impl RiscvDebugSequence for ESP32H2 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut RiscvCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_riscv(interface)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 
     fn reset_system_and_halt(
diff --git a/probe-rs/src/config/sequences/esp32s2.rs b/probe-rs/src/config/sequences/esp32s2.rs
index a8e6b40c..3b5e6317 100644
--- a/probe-rs/src/config/sequences/esp32s2.rs
+++ b/probe-rs/src/config/sequences/esp32s2.rs
@@ -9,7 +9,7 @@ use crate::{
         communication_interface::XtensaCommunicationInterface, sequences::XtensaDebugSequence,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32-S2.
@@ -63,10 +63,7 @@ impl XtensaDebugSequence for ESP32S2 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut XtensaCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_xtensa(interface, self)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 }
diff --git a/probe-rs/src/config/sequences/esp32s3.rs b/probe-rs/src/config/sequences/esp32s3.rs
index 61b01ed0..9d3aa131 100644
--- a/probe-rs/src/config/sequences/esp32s3.rs
+++ b/probe-rs/src/config/sequences/esp32s3.rs
@@ -9,7 +9,7 @@ use crate::{
         communication_interface::XtensaCommunicationInterface, sequences::XtensaDebugSequence,
     },
     config::sequences::esp::EspFlashSizeDetector,
-    MemoryInterface,
+    MemoryInterface, Session,
 };
 
 /// The debug sequence implementation for the ESP32-S3.
@@ -63,10 +63,7 @@ impl XtensaDebugSequence for ESP32S3 {
         Ok(())
     }
 
-    fn detect_flash_size(
-        &self,
-        interface: &mut XtensaCommunicationInterface,
-    ) -> Result<Option<usize>, crate::Error> {
-        self.inner.detect_flash_size_xtensa(interface, self)
+    fn detect_flash_size(&self, session: &mut Session) -> Result<Option<usize>, crate::Error> {
+        self.inner.detect_flash_size(session)
     }
 }
diff --git a/probe-rs/src/config/sequences/mod.rs b/probe-rs/src/config/sequences/mod.rs
index 7b13223d..f34d93ff 100644
--- a/probe-rs/src/config/sequences/mod.rs
+++ b/probe-rs/src/config/sequences/mod.rs
@@ -17,6 +17,7 @@ pub mod esp32s3;
 
 // ARM devices
 pub mod atsam;
+pub mod cc13xx_cc26xx;
 pub mod efm32xg2;
 pub mod infineon;
 pub mod nrf52;
diff --git a/probe-rs/src/config/target.rs b/probe-rs/src/config/target.rs
index c1a6aa0f..4d1c6299 100644
--- a/probe-rs/src/config/target.rs
+++ b/probe-rs/src/config/target.rs
@@ -1,6 +1,7 @@
 use super::{
     sequences::{
         atsam::AtSAM,
+        cc13xx_cc26xx::CC13xxCC26xx,
         efm32xg2::EFM32xG2,
         esp32::ESP32,
         esp32c2::ESP32C2,
@@ -186,6 +187,8 @@ impl Target {
             DebugSequence::Arm(AtSAM::create())
         } else if chip.name.starts_with("XMC4") {
             DebugSequence::Arm(XMC4000::create())
+        } else if chip.name.starts_with("CC13") || chip.name.starts_with("CC26") {
+            DebugSequence::Arm(CC13xxCC26xx::create())
         } else {
             // Default to the architecture of the first core, which is okay if
             // there is no mixed architectures.
diff --git a/probe-rs/src/debug/language.rs b/probe-rs/src/debug/language.rs
index e46b4afc..229b0351 100644
--- a/probe-rs/src/debug/language.rs
+++ b/probe-rs/src/debug/language.rs
@@ -16,8 +16,8 @@ pub mod rust;
 mod parsing;
 mod value;
 
-pub fn from_dwarf(dwarf_language: DwLang) -> Box<dyn ProgrammingLanguage> {
-    match dwarf_language {
+pub fn from_dwarf(language: DwLang) -> Box<dyn ProgrammingLanguage> {
+    match language {
         // Handle all C-like languages the same now.
         // We may have to split it later if this is not good enough.
         gimli::DW_LANG_C
diff --git a/probe-rs/src/debug/unit_info.rs b/probe-rs/src/debug/unit_info.rs
index 6aa92ef8..c9c8dd1a 100644
--- a/probe-rs/src/debug/unit_info.rs
+++ b/probe-rs/src/debug/unit_info.rs
@@ -313,7 +313,7 @@ impl UnitInfo {
 
                             let variant_part = if discriminant_variable.is_valid() {
                                 discriminant_variable
-                                    .get_value(cache)
+                                    .to_string(cache)
                                     .parse()
                                     .unwrap_or(u64::MAX)
                             } else {
@@ -1018,9 +1018,9 @@ impl UnitInfo {
                 }
             }
             gimli::DW_TAG_structure_type => {
-                child_variable.type_name = VariableType::Struct(
-                    type_name.unwrap_or_else(|| "<unnamed struct>".to_string()),
-                );
+                let type_name = type_name.unwrap_or_else(|| "<unnamed struct>".to_string());
+                child_variable.type_name = VariableType::Struct(type_name.clone());
+
                 self.process_memory_location(
                     debug_info,
                     node,
@@ -1031,29 +1031,28 @@ impl UnitInfo {
                 )?;
 
                 if child_variable.memory_location != VariableLocation::Unavailable {
-                    if let VariableType::Struct(name) = &child_variable.type_name {
-                        // The default behaviour is to defer the processing of child types.
-                        child_variable.variable_node_type =
-                            VariableNodeType::TypeOffset(self.debug_info_offset()?, node.offset());
-                        // In some cases, it really simplifies the UX if we can auto resolve the children and derive a value that is visible at first glance to the user.
-                        if self.language.auto_resolve_children(name) {
-                            let temp_node_type = std::mem::replace(
-                                &mut child_variable.variable_node_type,
-                                VariableNodeType::RecurseToBaseType,
-                            );
-
-                            let mut tree = self.unit.entries_tree(Some(node.offset()))?;
-
-                            self.process_tree(
-                                debug_info,
-                                tree.root()?,
-                                child_variable,
-                                memory,
-                                cache,
-                                frame_info,
-                            )?;
-                            child_variable.variable_node_type = temp_node_type;
-                        }
+                    // The default behaviour is to defer the processing of child types.
+                    child_variable.variable_node_type =
+                        VariableNodeType::TypeOffset(self.debug_info_offset()?, node.offset());
+                    // In some cases, it really simplifies the UX if we can auto resolve the
+                    // children and derive a value that is visible at first glance to the user.
+                    if self.language.auto_resolve_children(&type_name) {
+                        let temp_node_type = std::mem::replace(
+                            &mut child_variable.variable_node_type,
+                            VariableNodeType::RecurseToBaseType,
+                        );
+
+                        let mut tree = self.unit.entries_tree(Some(node.offset()))?;
+
+                        self.process_tree(
+                            debug_info,
+                            tree.root()?,
+                            child_variable,
+                            memory,
+                            cache,
+                            frame_info,
+                        )?;
+                        child_variable.variable_node_type = temp_node_type;
                     }
                 } else {
                     // If something is already broken, then do nothing ...
@@ -1095,7 +1094,7 @@ impl UnitInfo {
                         let mut enumerator_values = cache.get_children(child_variable.variable_key);
 
                         let is_this_value = |enumerator_variable: &&Variable| {
-                            enumerator_variable.get_value(cache) == this_enum_const_value
+                            enumerator_variable.to_string(cache) == this_enum_const_value
                         };
 
                         let enumumerator_value = match enumerator_values.find(is_this_value) {
diff --git a/probe-rs/src/debug/variable.rs b/probe-rs/src/debug/variable.rs
index a5242ec4..7ac560d8 100644
--- a/probe-rs/src/debug/variable.rs
+++ b/probe-rs/src/debug/variable.rs
@@ -3,15 +3,18 @@ use crate::debug::{language::ProgrammingLanguage, unit_info::UnitInfo};
 use super::*;
 use anyhow::anyhow;
 use gimli::{DebugInfoOffset, DwLang, UnitOffset};
+use itertools::Itertools;
 use num_traits::Zero;
 use std::ops::Range;
 
-/// Define the role that a variable plays in a Variant relationship. See section '5.7.10 Variant Entries' of the DWARF 5 specification
+/// Define the role that a variable plays in a Variant relationship. See section '5.7.10 Variant
+/// Entries' of the DWARF 5 specification
 #[derive(Debug, Clone, Eq, PartialEq, Default)]
 pub enum VariantRole {
     /// A (parent) Variable that can have any number of Variant's as its value
     VariantPart(u64),
-    /// A (child) Variable that defines one of many possible types to hold the current value of a VariantPart.
+    /// A (child) Variable that defines one of many possible types to hold the current value of a
+    /// VariantPart.
     Variant(u64),
     /// This variable doesn't play a role in a Variant relationship
     #[default]
@@ -20,7 +23,8 @@ pub enum VariantRole {
 
 /// A [Variable] will have either a valid value, or some reason why a value could not be constructed.
 /// - If we encounter expected errors, they will be displayed to the user as defined below.
-/// - If we encounter unexpected errors, they will be treated as proper errors and will propagated to the calling process as an `Err()`
+/// - If we encounter unexpected errors, they will be treated as proper errors and will propagated
+///   to the calling process as an `Err()`
 #[derive(Clone, Debug, PartialEq, Eq, Default)]
 pub enum VariableValue {
     /// A valid value of this variable
@@ -32,7 +36,8 @@ pub enum VariableValue {
     Error(String),
     /// The value has not been set. This could be because ...
     /// - It is too early in the process to have discovered its value, or ...
-    /// - The variable cannot have a stored value, e.g. a `struct`. In this case, please use `Variable::get_value` to infer a human readable value from the value of the struct's fields.
+    /// - The variable cannot have a stored value, e.g. a `struct`. In this case, please use
+    ///   `Variable::get_value` to infer a human readable value from the value of the struct's fields.
     #[default]
     Empty,
 }
@@ -101,36 +106,58 @@ impl std::fmt::Display for VariableName {
     }
 }
 
-/// Encode the nature of the Debug Information Entry in a way that we can resolve child nodes of a [Variable]
-/// The rules for 'lazy loading'/deferred recursion of [Variable] children are described under each of the enum values.
+/// Encode the nature of the Debug Information Entry in a way that we can resolve child nodes of a
+/// [Variable].
+///
+/// The rules for 'lazy loading'/deferred recursion of [Variable] children are described under each
+/// of the enum values.
 #[derive(Debug, PartialEq, Eq, Clone, Default)]
 pub enum VariableNodeType {
-    /// Use the `header_offset` and `type_offset` as direct references for recursing the variable children. With the current implementation, the `type_offset` will point to a DIE with a tag of `DW_TAG_structure_type`.
-    /// - Rule: For structured variables, we WILL NOT automatically expand their children, but we have enough information to expand it on demand. Except if they fall into one of the special cases handled by [VariableNodeType::RecurseToBaseType]
+    /// Use the `header_offset` and `type_offset` as direct references for recursing the variable
+    /// children. With the current implementation, the `type_offset` will point to a DIE with a tag
+    /// of `DW_TAG_structure_type`.
+    /// - Rule: For structured variables, we WILL NOT automatically expand their children, but we
+    ///         have enough information to expand it on demand. Except if they fall into one of the
+    ///         special cases handled by [VariableNodeType::RecurseToBaseType]
     TypeOffset(DebugInfoOffset, UnitOffset),
-    /// Use the `header_offset` and `entries_offset` as direct references for recursing the variable children.
-    /// - Rule: All top level variables in a [StackFrame] are automatically deferred, i.e [VariableName::LocalScopeRoot], [VariableName::RegistersRoot].
+    /// Use the `header_offset` and `entries_offset` as direct references for recursing the variable
+    /// children.
+    /// - Rule: All top level variables in a [StackFrame] are automatically deferred, i.e
+    /// [VariableName::LocalScopeRoot], [VariableName::RegistersRoot].
     DirectLookup(DebugInfoOffset, UnitOffset),
-    /// Look up information from all compilation units. This is used to resolve static variables, so when [`VariableName::StaticScopeRoot`] is used.
+    /// Look up information from all compilation units. This is used to resolve static variables, so
+    /// when [`VariableName::StaticScopeRoot`] is used.
     UnitsLookup,
     /// Sometimes it doesn't make sense to recurse the children of a specific node type
     /// - Rule: Pointers to `unit` datatypes WILL NOT BE resolved, because it doesn't make sense.
-    /// - Rule: Once we determine that a variable can not be recursed further, we update the variable_node_type to indicate that no further recursion is possible/required. This can be because the variable is a 'base' data type, or because there was some kind of error in processing the current node, so we don't want to incur cascading errors.
+    /// - Rule: Once we determine that a variable can not be recursed further, we update the
+    ///         variable_node_type to indicate that no further recursion is possible/required. This
+    ///         can be because the variable is a 'base' data type, or because there was some kind of
+    ///         error in processing the current node, so we don't want to incur cascading errors.
     /// TODO: Find code instances where we use magic values (e.g. u32::MAX) and replace with DoNotRecurse logic if appropriate.
     DoNotRecurse,
-    /// Unless otherwise specified, always recurse the children of every node until we get to the base data type.
-    /// - Rule: (Default) Unless it is prevented by any of the other rules, we always recurse the children of these variables.
-    /// - Rule: Certain structured variables (e.g. `&str`, `Some`, `Ok`, `Err`, etc.) are set to [VariableNodeType::RecurseToBaseType] to improve the debugger UX.
-    /// - Rule: Pointers to `const` variables WILL ALWAYS BE recursed, because they provide essential information, for example about the length of strings, or the size of arrays.
-    /// - Rule: Enumerated types WILL ALWAYS BE recursed, because we only ever want to see the 'active' child as the value.
-    /// - Rule: For now, Array types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to defer these.
-    /// - Rule: For now, Union types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to defer these.
+    /// Unless otherwise specified, always recurse the children of every node until we get to the
+    /// base data type.
+    /// - Rule: (Default) Unless it is prevented by any of the other rules, we always recurse the
+    ///         children of these variables.
+    /// - Rule: Certain structured variables (e.g. `&str`, `Some`, `Ok`, `Err`, etc.) are set to
+    ///         [VariableNodeType::RecurseToBaseType] to improve the debugger UX.
+    /// - Rule: Pointers to `const` variables WILL ALWAYS BE recursed, because they provide
+    ///         essential information, for example about the length of strings, or the size of
+    ///         arrays.
+    /// - Rule: Enumerated types WILL ALWAYS BE recursed, because we only ever want to see the
+    ///         'active' child as the value.
+    /// - Rule: For now, Array types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to
+    ///         defer these.
+    /// - Rule: For now, Union types WILL ALWAYS BE recursed. TODO: Evaluate if it is beneficial to
+    ///         defer these.
     #[default]
     RecurseToBaseType,
 }
 
 impl VariableNodeType {
-    /// Will return true if any of the `variable_node_type` value implies that the variable will be 'lazy' resolved.
+    /// Will return `true` if the `variable_node_type` value implies that the variable will be
+    /// 'lazy' resolved.
     pub fn is_deferred(&self) -> bool {
         match self {
             VariableNodeType::TypeOffset(_, _)
@@ -146,7 +173,6 @@ impl VariableNodeType {
 pub enum BitOffset {
     /// The bit offset is from the least significant bit.
     FromLsb(u64),
-
     /// The bit offset is from the most significant bit.
     FromMsb(u64),
 }
@@ -237,7 +263,8 @@ pub enum Modifier {
     Typedef(String),
 }
 
-/// The variants of VariableType allows us to streamline the conditional logic that requires specific handling depending on the nature of the variable.
+/// The variants of VariableType allows us to streamline the conditional logic that requires
+/// specific handling depending on the nature of the variable.
 #[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
 pub enum VariableType {
     /// A variable with a Rust base datatype.
@@ -248,9 +275,11 @@ pub enum VariableType {
     Struct(String),
     /// A Rust enum.
     Enum(String),
-    /// Namespace refers to the path that qualifies a variable. e.g. "std::string" is the namespace for the struct "String"
+    /// Namespace refers to the path that qualifies a variable. e.g. "std::string" is the namespace
+    /// for the struct "String"
     Namespace,
-    /// A Pointer is a variable that contains a reference to another variable, and the type of the referenced variable may not be known until the reference has been resolved.
+    /// A Pointer is a variable that contains a reference to another variable, and the type of the
+    /// referenced variable may not be known until the reference has been resolved.
     Pointer(Option<String>),
     /// A Rust array.
     Array {
@@ -264,7 +293,8 @@ pub enum VariableType {
     /// When we are unable to determine the name of a variable.
     #[default]
     Unknown,
-    /// For infrequently used categories of variables that does not fall into any of the other `VariableType` variants.
+    /// For infrequently used categories of variables that does not fall into any of the other
+    /// `VariableType` variants.
     Other(String),
 }
 
@@ -430,7 +460,9 @@ impl std::fmt::Display for VariableLocation {
 /// unless it is updated through one of the available methods on `VariableCache`.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Variable {
-    /// Every variable must have a unique key value assigned to it. The value will be zero until it is stored in VariableCache, at which time its value will be set to the same as the VariableCache::variable_cache_key
+    /// Every variable must have a unique key value assigned to it.
+    /// The value will be zero until it is stored in VariableCache, at which time its value will be
+    /// set to the same as the VariableCache::variable_cache_key
     pub(super) variable_key: ObjectRef,
     /// Every variable must have a unique parent assigned to it when stored in the VariableCache.
     pub parent_key: ObjectRef,
@@ -445,27 +477,30 @@ pub struct Variable {
 
     /// The name of the type of this variable.
     pub type_name: VariableType,
-    /// For 'lazy loading' of certain variable types we have to determine if the variable recursion should be deferred, and if so, how to resolve it when the request for further recursion happens.
+    /// For 'lazy loading' of certain variable types we have to determine if the variable recursion
+    /// should be deferred, and if so, how to resolve it when the request for further recursion
+    /// happens.
     /// See [VariableNodeType] for more information.
     pub variable_node_type: VariableNodeType,
     /// The starting location/address in memory where this Variable's value is stored.
     pub memory_location: VariableLocation,
     /// The size of this variable in bytes.
     pub byte_size: Option<u64>,
-    /// If this is a subrange (array, vector, etc.), is the ordinal position of this variable in that range
+    /// If this is a subrange (array, vector, etc.), is the ordinal position of this variable in
+    /// that range
     pub member_index: Option<i64>,
     /// The role of this variable.
     pub role: VariantRole,
 }
 
 impl Variable {
-    /// In most cases, Variables will be initialized with their ELF references so that we resolve their data types and values on demand.
+    /// In most cases, Variables will be initialized with their ELF references so that we resolve
+    /// their data types and values on demand.
     pub fn new(unit_info: Option<&UnitInfo>) -> Variable {
         Variable {
             language: unit_info
                 .map(|info| info.get_language())
                 .unwrap_or(gimli::DW_LANG_Rust),
-
             variable_key: Default::default(),
             parent_key: Default::default(),
             name: Default::default(),
@@ -491,46 +526,43 @@ impl Variable {
         self.variable_key
     }
 
-    /// Implementing set_value(), because the library passes errors into the value of the variable.
-    /// This ensures debug front ends can see the errors, but doesn't fail because of a single variable not being able to decode correctly.
+    /// This ensures debug frontends can see the errors, but doesn't fail because of a single
+    /// variable not being able to decode correctly.
     pub fn set_value(&mut self, new_value: VariableValue) {
         // Allow some block when logic requires it.
-        #[allow(clippy::if_same_then_else)]
-        if new_value.is_valid() {
+        if new_value.is_valid() || self.value.is_valid() {
             // Simply overwrite existing value with a new valid one.
             self.value = new_value;
-        } else if self.value.is_valid() {
-            // Overwrite a valid value with an error.
-            self.value = new_value;
         } else {
             // Concatenate the error messages ...
             self.value = VariableValue::Error(format!("{} : {}", self.value, new_value));
-        }
-        if !self.value.is_valid() {
-            // If the value is invalid, then make sure we don't propogate invalid memory location values.
+
+            // If the value is invalid, then make sure we don't propagate invalid memory location
+            // values.
             self.memory_location = VariableLocation::Unavailable;
         }
     }
 
-    /// Convert the [String] value into the appropriate memory format and update the target memory with the new value.
-    /// Currently this only works for base data types. There is no provision in the MS DAP API to catch
-    /// this client side, so we can only respond with a 'gentle' error message if the user attempts unsupported data types.
+    /// Convert the [String] value into the appropriate memory format and update the target memory
+    /// with the new value.
+    /// Currently this only works for base data types. There is no provision in the MS DAP API to
+    /// catch this client side, so we can only respond with a 'gentle' error message if the user
+    /// attempts unsupported data types.
     pub fn update_value(
         &self,
         memory: &mut impl MemoryInterface,
-        variable_cache: &mut variable_cache::VariableCache,
+        variable_cache: &mut VariableCache,
         new_value: String,
     ) -> Result<(), DebugError> {
-        if !self.is_valid()
-                // Need a valid type
-                || self.type_name == VariableType::Unknown
-                // Need a valid memory location
-                || !self.memory_location.valid()
-        {
+        let valid_value = self.is_valid();
+        let valid_type = self.type_name != VariableType::Unknown;
+        let valid_memory = self.memory_location.valid();
+        if !valid_value || !valid_type || !valid_memory {
             // Insufficient data available.
             Err(anyhow!(
                 "Cannot update variable: {:?}, with supplied information (value={:?}, type={:?}, memory location={:#010x?}).",
-                self.name, self.value, self.type_name, self.memory_location).into())
+                self.name, self.value, self.type_name, self.memory_location
+            ).into())
         } else {
             // We have everything we need to update the variable value.
             language::from_dwarf(self.language)
@@ -548,12 +580,13 @@ impl Variable {
         }
     }
 
-    /// Implementing get_value(), because Variable.value has to be private (a requirement of updating the value without overriding earlier values ... see set_value()).
-    pub fn get_value(&self, variable_cache: &variable_cache::VariableCache) -> String {
+    /// Implementing get_value(), because Variable.value has to be private (a requirement of
+    /// updating the value without overriding earlier values ... see set_value()).
+    pub fn to_string(&self, variable_cache: &VariableCache) -> String {
         // Allow for chained `if let` without complaining
         if !self.value.is_empty() {
-            // The `value` for this `Variable` is non empty because ...
-            // - It is base data type for which a value was determined based on the core runtime, or ...
+            // The `value` for this `Variable` is non empty because either
+            // - It is base data type for which a value was determined based on the core runtime
             // - We encountered an error somewhere, so report it to the user
             format!("{}", self.value)
         } else if matches!(
@@ -563,23 +596,29 @@ impl Variable {
             // Namespaces do not have values
             String::new()
         } else {
-            // We need to construct a 'human readable' value using `fmt::Display` to represent the values of complex types and pointers.
+            // We need to construct a 'human readable' value using `fmt::Display` to represent the
+            // values of complex types and pointers.
             if variable_cache.has_children(self) {
                 self.formatted_variable_value(variable_cache, 0, false)
+                    .unwrap_or_default()
             } else if self.type_name == VariableType::Unknown || !self.memory_location.valid() {
                 if self.variable_node_type.is_deferred() {
-                    // When we will do a lazy-load of variable children, and they have not yet been requested by the user, just display the type_name as the value
+                    // When we will do a lazy-load of variable children, and they have not yet been
+                    // requested by the user, just display the type_name as the value
                     self.type_name
                         .display_name(language::from_dwarf(self.language).as_ref())
                 } else {
-                    // This condition should only be true for intermediate nodes from DWARF. These should not show up in the final `VariableCache`
-                    // If a user sees this error, then there is a logic problem in the stack unwind
+                    // This condition should only be true for intermediate nodes
+                    // from DWARF. These should not show up in the final
+                    // `VariableCache`. If a user sees this error, then there is
+                    // a logic problem in the stack unwind
                     "Error: This is a bug! Attempted to evaluate a Variable with no type or no memory location".to_string()
                 }
             } else if matches!(self.type_name, VariableType::Struct(ref name) if name == "None") {
                 "None".to_string()
             } else if matches!(self.type_name, VariableType::Array { count: 0, .. }) {
                 self.formatted_variable_value(variable_cache, 0, false)
+                    .unwrap_or_default()
             } else {
                 format!(
                     "Unimplemented: Get value of type {:?} of ({:?} bytes) at location {}",
@@ -593,28 +632,29 @@ impl Variable {
     pub fn extract_value(
         &mut self,
         memory: &mut dyn MemoryInterface,
-        variable_cache: &variable_cache::VariableCache,
+        variable_cache: &VariableCache,
     ) {
         if let VariableValue::Error(_) = self.value {
             // Nothing more to do ...
             return;
         }
 
-        if !self.value.is_empty()
-        // The value was set explicitly, so just leave it as is, or it was an error, so don't attempt anything else
-        || !self.memory_location.valid()
-        // This may just be that we are early on in the process of `Variable` evaluation
-        || matches!(self.type_name.inner(), VariableType::Unknown)
+        let empty = self.value.is_empty();
+        // The value was set explicitly, so just leave it as is, or it was an error, so don't attempt
+        // anything else
+        let valid = self.memory_location.valid();
         // This may just be that we are early on in the process of `Variable` evaluation
-        {
-            // Quick exit if we don't really need to do much more.
+        let unknown = self.type_name.inner() == &VariableType::Unknown;
+
+        if !empty || !valid || unknown {
             return;
         }
 
         if self.variable_node_type.is_deferred()
             || matches!(self.type_name, VariableType::Pointer(_))
         {
-            // And we have not previously assigned the value, then assign the type and address as the value
+            // And we have not previously assigned the value, then assign the type and address as
+            // the value.
             self.value = VariableValue::Valid(format!(
                 "{} @ {}",
                 self.type_name
@@ -634,8 +674,9 @@ impl Variable {
             language::from_dwarf(self.language).read_variable_value(self, memory, variable_cache);
     }
 
-    /// The variable is considered to be an 'indexed' variable if the name starts with two underscores followed by a number. e.g. "__1".
-    /// TODO: Consider replacing this logic with `std::str::pattern::Pattern` when that API stabilizes
+    /// The variable is considered to be an 'indexed' variable if the name starts with two
+    /// underscores followed by a number. e.g. "__1".
+    // TODO: Consider replacing this logic with `std::str::pattern::Pattern` when that API stabilizes
     pub fn is_indexed(&self) -> bool {
         match &self.name {
             VariableName::Named(name) => {
@@ -649,220 +690,261 @@ impl Variable {
         }
     }
 
+    /// Returns `true` if the variable has a name, `false` otherwise.
+    pub fn is_named(&self) -> bool {
+        matches!(&self.name, VariableName::Named(_))
+    }
+
     /// `true` if the Variable has a valid value, or an empty value.
     /// `false` if the Variable has a VariableValue::Error(_) value
     pub fn is_valid(&self) -> bool {
         self.value.is_valid()
     }
 
+    /// Format the variable.
     fn formatted_variable_value(
         &self,
-        variable_cache: &variable_cache::VariableCache,
+        variable_cache: &VariableCache,
         indentation: usize,
         show_name: bool,
-    ) -> String {
-        let line_feed = if indentation == 0 { "" } else { "\n" };
-        let line_start = format!("{}{:\t<indentation$}", line_feed, "");
+    ) -> Option<String> {
         let type_name = self.type_name();
 
         if !self.value.is_empty() {
-            // Use the supplied value or error message.
-            if show_name {
+            // This is the end of the recursion where we already have a scalar
+            // value for a variable and we can just move it up.
+            let line_start = line_indent_string(indentation);
+            return Some(if show_name {
                 format!("{line_start}{}: {} = {}", self.name, type_name, self.value)
             } else {
                 format!("{line_start}{}", self.value)
-            }
+            });
         } else if matches!(
             self.name,
             VariableName::AnonymousNamespace | VariableName::Namespace(_)
         ) {
-            // Namespaces do not have values
-            String::new()
-        } else {
-            // Infer a human readable value using the available children of this variable.
-            let mut compound_value = String::new();
-            let children = variable_cache.get_children(self.variable_key);
-            let first_child = children.clone().next();
-
-            // Make sure we can safely unwrap() children.
-            match self.type_name.inner() {
-                VariableType::Pointer(_) => {
-                    // Pointers
-                    format!(
-                        "{line_start}{}",
-                        if let Some(first_child) = first_child {
-                            first_child.formatted_variable_value(
-                                variable_cache,
-                                indentation + 1,
-                                true,
-                            )
-                        } else {
-                            "Unable to resolve referenced variable value".to_string()
-                        }
-                    )
-                }
-                VariableType::Array { .. } => {
-                    // Limit arrays to 10(+1) elements
-                    const ARRAY_MAX_LENGTH: usize = 10;
-
-                    let mut comma = "";
-                    let mut printed_count = 0;
-                    let mut children = children.clone();
-
-                    compound_value = format!("{line_start}{type_name} = [");
-                    loop {
-                        if printed_count >= ARRAY_MAX_LENGTH {
-                            // Be a bit lenient with the limit, avoid showing "1 more" for a single child.
-                            let remaining = children.clone().count();
-                            if remaining > 1 {
-                                break;
-                            }
-                        }
-                        let Some(child) = children.next() else {
-                            break;
-                        };
-
-                        compound_value = format!(
-                            "{compound_value}{comma}{}",
-                            child.formatted_variable_value(variable_cache, indentation + 1, false),
-                        );
-                        printed_count += 1;
-                        comma = ",";
-                    }
-
-                    let remaining = children.count();
-                    if remaining > 0 {
-                        compound_value = format!(
-                            "{compound_value},\n{line_start}\t... and {} more",
-                            remaining
-                        );
-                    }
-
-                    format!("{compound_value}{line_start}]")
-                }
-
-                VariableType::Struct(name) if name == "Some" || name == "Ok" || name == "Err" => {
-                    // FIXME: this is not hit by any of the unwind tests, which is weird because
-                    // some of them contain `Some` structs.
-                    // Handle special structure types like the variant values of `Option<>` and `Result<>`
-                    compound_value = format!("{line_start}{} = (", type_name);
-                    for child in children {
-                        compound_value = format!(
-                            "{compound_value}{}",
-                            child.formatted_variable_value(variable_cache, indentation + 1, false)
-                        );
-                    }
-                    format!("{compound_value}{line_start})")
-                }
+            // Namespaces do not have values, so we report no value up.
+            // This will alow us to filter it out when we concatenate children.
+            return None;
+        }
 
-                _ if first_child.is_none() => {
-                    // Struct with no children -> just print type name
-                    // This is for example the None value of an Option.
-                    format!("{compound_value}{type_name}")
-                }
+        // Infer a human readable value using the available children of this variable.
+        let children = &mut variable_cache.get_children(self.variable_key);
+        let first_child = children.clone().next();
 
-                _ if matches!(
-                    self.name,
-                    VariableName::StaticScopeRoot
-                        | VariableName::LocalScopeRoot
-                        | VariableName::RegistersRoot
-                ) =>
-                {
-                    compound_value = format!("{compound_value}{line_start}{type_name} {{");
-
-                    let mut comma = "";
-                    for child in children {
-                        let formatted =
-                            child.formatted_variable_value(variable_cache, indentation + 1, true);
-                        if formatted.is_empty() {
-                            // Avoid printing empty commas
-                            continue;
-                        }
-                        compound_value = format!("{compound_value}{comma}{formatted}");
-                        comma = ",";
-                    }
-                    format!("{compound_value}{line_start}}}")
-                }
-
-                _ => {
-                    // Generic handling of other structured types.
-                    // The pre- and post- fix is determined by the type of children.
-                    // compound_value = format!("{} {}", compound_value, self.type_name);
-                    let (mut pre_fix, mut post_fix) = (None, None);
-
-                    let mut is_tuple = false;
-
-                    if let Some((child, child_name)) = children.clone().find_map(|c| {
-                        if let VariableName::Named(child_name) = &c.name {
-                            Some((c, child_name))
-                        } else {
-                            None
-                        }
-                    }) {
-                        if child_name.starts_with("__0") {
-                            is_tuple = true;
-                            // Treat this structure as a tuple
-                            pre_fix = Some(format!(
-                                "{}: {}({}) = {}(",
-                                self.name,
-                                type_name,
-                                child.type_name(),
-                                type_name,
-                            ));
-                            post_fix = Some(')');
-                        } else {
-                            // Treat this structure as a `struct`
-                            pre_fix = if show_name {
-                                Some(format!("{}: {} = {} {{", self.name, type_name, type_name))
-                            } else {
-                                Some(format!("{} {{", type_name))
-                            };
-                            post_fix = Some('}');
-                        }
-                    }
-                    if let Some(pre_fix) = &pre_fix {
-                        compound_value = format!("{compound_value}{line_start}{pre_fix}");
-                    }
-
-                    let print_name = !is_tuple;
-
-                    let mut comma = "";
-                    for child in children {
-                        compound_value = format!(
-                            "{compound_value}{comma}{}",
-                            child.formatted_variable_value(
-                                variable_cache,
-                                indentation + 1,
-                                print_name
-                            ),
-                        );
-                        comma = ",";
-                    }
-                    if let Some(post_fix) = &post_fix {
-                        compound_value = format!("{compound_value}{line_start}{post_fix}");
-                    }
-                    compound_value
-                }
+        // Make sure we can safely unwrap() children.
+        Some(match self.type_name.inner() {
+            VariableType::Pointer(_) => {
+                format_pointer_value(variable_cache, indentation, first_child)
             }
-        }
+            VariableType::Array { .. } => {
+                format_array_value(variable_cache, indentation, children, &type_name)
+            }
+            VariableType::Struct(name) if name == "Some" || name == "Ok" || name == "Err" => {
+                format_struct_value(variable_cache, indentation, children, &type_name)
+            }
+            _ if first_child.is_none() => {
+                // This is a struct with no children, so just print the type name.
+                // This is for example the None value of an Option or the empty type ().
+                type_name
+            }
+            _ if matches!(
+                self.name,
+                VariableName::StaticScopeRoot
+                    | VariableName::LocalScopeRoot
+                    | VariableName::RegistersRoot
+            ) =>
+            {
+                format_root_value(variable_cache, indentation, children, &type_name)
+            }
+            _ => format_default_value(
+                variable_cache,
+                indentation,
+                &self.name,
+                children,
+                &type_name,
+                show_name,
+            ),
+        })
     }
 
     /// Calculate the memory range that contains the value of this variable.
+    ///
     /// If the location and/or byte size is not known, then return None.
-    /// Note: We don't do any validation of the memory range here, and leave it up to the caller to
-    /// validate the memory ranges before attempting to read them.
+    /// Note: We don't do any validation of the memory range here and leave it
+    /// up to the caller to validate the memory ranges before attempting to read
+    /// them.
     pub fn memory_range(&self) -> Option<Range<u64>> {
-        if let VariableLocation::Address(address) = self.memory_location {
-            if self.byte_size.is_some_and(|byte_size| byte_size.is_zero()) {
-                // This can happen for instance with empty arrays, and even though we don't need to read the
-                // array data, we need to read the array structure type.
-                Some(address..address + 4)
+        let VariableLocation::Address(address) = self.memory_location else {
+            return None;
+        };
+
+        self.byte_size.map(|byte_size| {
+            if byte_size.is_zero() {
+                address..address + 4
             } else {
-                self.byte_size
-                    .map(|byte_size| address..(address + byte_size))
+                address..(address + byte_size)
             }
+        })
+    }
+}
+
+/// Format a pointer value
+///
+/// Formats the pointed to value and potential subsequent children as well.
+fn format_pointer_value(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    first_child: Option<&Variable>,
+) -> String {
+    let line_start = line_indent_string(indentation);
+
+    let value = if let Some(first_child) = first_child {
+        first_child
+            .formatted_variable_value(variable_cache, indentation + 1, true)
+            .expect("a child. This is a bug. Please report it.")
+    } else {
+        "Unable to resolve referenced variable value".to_string()
+    };
+
+    format!("{line_start}{value}")
+}
+
+/// Format any array like value.
+///
+/// Recursively formats all child values.
+fn format_array_value<'a>(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    children: &mut (impl Iterator<Item = &'a Variable> + Clone),
+    type_name: &str,
+) -> String {
+    let line_start = line_indent_string(indentation);
+
+    // Limit arrays to 10 elements
+    const ARRAY_MAX_LENGTH: usize = 10;
+
+    // If we at least ARRAY_MAX_LENGTH + 2 items in the iterator, cap at ARRAY_MAX_LENGTH.
+    // If we have less, cap at the actual number of items.
+    // This helps us to never write "and 1 more" with the reasoning that the space used for this
+    // text, can be used for printing that one item.
+    let count = children.clone().count();
+    let take = if count > ARRAY_MAX_LENGTH + 1 {
+        ARRAY_MAX_LENGTH
+    } else {
+        count
+    };
+
+    let children_values = children
+        .by_ref()
+        .take(take)
+        .filter_map(|child| child.formatted_variable_value(variable_cache, indentation + 1, false))
+        .join(",");
+
+    let remainder = if count > ARRAY_MAX_LENGTH + 1 {
+        format!(",\n{line_start}\t... and {} more", count - take)
+    } else {
+        String::new()
+    };
+
+    format!("{line_start}{type_name} = [{children_values}{remainder}{line_start}]")
+}
+
+/// Format any struct like value .
+///
+/// Recursively formats all child values.
+fn format_struct_value<'a>(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    children: &mut (impl Iterator<Item = &'a Variable> + Clone),
+    type_name: &str,
+) -> String {
+    let line_start = line_indent_string(indentation);
+
+    // FIXME: this is not hit by any of the unwind tests, which is weird because
+    // some of them contain `Some` structs.
+    // Handle special structure types like the variant values of `Option<>` and `Result<>`
+    let children_values = format_children_values(variable_cache, indentation, children, false);
+
+    format!("{line_start}{type_name} = ({children_values})")
+}
+
+/// Format any root value.
+///
+/// Recursively formats all child values.
+fn format_root_value<'a>(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    children: &mut (impl Iterator<Item = &'a Variable> + Clone),
+    type_name: &str,
+) -> String {
+    let line_start = line_indent_string(indentation);
+
+    let children_values = format_children_values(variable_cache, indentation, children, true);
+    format!("{line_start}{type_name} {{{children_values}{line_start}}}")
+}
+
+/// Format any value that has no type that requires special handling.
+///
+/// Recursively formats all child values.
+fn format_default_value<'a>(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    name: &VariableName,
+    children: &mut (impl Iterator<Item = &'a Variable> + Clone),
+    type_name: &String,
+    show_name: bool,
+) -> String {
+    let line_start = line_indent_string(indentation);
+
+    // Find the first child of the structure if it exists.
+    let child = children.clone().find(|v| v.is_named());
+
+    // If we do not have children, exit early because we cannot print more specifics (children)
+    // of this variable type. We instead print the empty type symbol.
+    let Some(child) = child else {
+        return "()".to_string();
+    };
+
+    let child_type_name = child.type_name();
+    if child.is_indexed() {
+        // Treat this structure as a tuple
+        let children_values = format_children_values(variable_cache, indentation, children, false);
+        let name = if show_name {
+            format!("{name}: {type_name}({child_type_name}) = ")
         } else {
-            None
-        }
+            String::new()
+        };
+        format!("{line_start}{name}{type_name}({children_values}{line_start})")
+    } else {
+        // Treat this structure as a `struct`
+        let children_values = format_children_values(variable_cache, indentation, children, true);
+        let name = if show_name {
+            format!("{name}: {type_name} = ")
+        } else {
+            String::new()
+        };
+        format!("{line_start}{name}{type_name} {{{children_values}{line_start}}}")
     }
 }
+
+/// Concatenate all children values with a comma.
+fn format_children_values<'a>(
+    variable_cache: &VariableCache,
+    indentation: usize,
+    children: &mut (impl Iterator<Item = &'a Variable> + Clone),
+    show_name: bool,
+) -> String {
+    children
+        .filter_map(|child| {
+            child.formatted_variable_value(variable_cache, indentation + 1, show_name)
+        })
+        .join(",")
+}
+
+/// Genarate a string that indents the line exactly the right amount.
+/// Includes a newline at the start if the indentation is bigger than 0.
+fn line_indent_string(indentation: usize) -> String {
+    let line_feed = if indentation == 0 { "" } else { "\n" };
+    format!("{line_feed}{:\t<indentation$}", "")
+}
diff --git a/probe-rs/src/debug/variable_cache.rs b/probe-rs/src/debug/variable_cache.rs
index 3677d403..c38b225a 100644
--- a/probe-rs/src/debug/variable_cache.rs
+++ b/probe-rs/src/debug/variable_cache.rs
@@ -45,7 +45,7 @@ impl Serialize for VariableCache {
             VariableTreeNode {
                 name: &root_node.name,
                 type_name: &root_node.type_name,
-                value: root_node.get_value(variable_cache),
+                value: root_node.to_string(variable_cache),
                 children: recurse_variables(variable_cache, root_node.variable_key, None),
             }
         }
@@ -77,7 +77,7 @@ impl Serialize for VariableCache {
                 out.push(VariableTreeNode {
                     name: &child_variable.name,
                     type_name: &child_variable.type_name,
-                    value: child_variable.get_value(variable_cache),
+                    value: child_variable.to_string(variable_cache),
                     children: recurse_variables(
                         variable_cache,
                         child_variable.variable_key,
@@ -474,7 +474,7 @@ impl VariableCache {
                     }) {
                         Some(string_length) => {
                             if string_length.is_valid() {
-                                string_length.get_value(self).parse().unwrap_or(0_usize)
+                                string_length.to_string(self).parse().unwrap_or(0_usize)
                             } else {
                                 0_usize
                             }
@@ -543,7 +543,7 @@ mod test {
             variable.variable_key,
             variable.name,
             variable.type_name,
-            variable.get_value(cache)
+            variable.to_string(cache)
         ));
 
         let children = cache.get_children(variable.variable_key);
@@ -571,7 +571,7 @@ mod test {
             VariableNodeType::UnitsLookup
         );
 
-        assert_eq!(cache_variable.get_value(&c), "<unknown>");
+        assert_eq!(cache_variable.to_string(&c), "<unknown>");
 
         assert_eq!(cache_variable.source_location, Default::default());
         assert_eq!(cache_variable.memory_location, VariableLocation::Unknown);
diff --git a/probe-rs/src/domain.rs b/probe-rs/src/domain.rs
new file mode 100644
index 00000000..d2f45af3
--- /dev/null
+++ b/probe-rs/src/domain.rs
@@ -0,0 +1,20 @@
+pub struct CountingIter<'a, I>(I, &'a mut usize);
+
+impl<I: Iterator> Iterator for CountingIter<'_, I> {
+    type Item = I::Item;
+    fn next(&mut self) -> Option<Self::Item> {
+        let item = self.0.next()?;
+        *self.1 += 1;
+        Some(item)
+    }
+}
+
+trait IterExt: Sized {
+    fn counting(self, counter: &mut usize) -> CountingIter<Self>;
+}
+
+impl<I: Iterator> IterExt for I {
+    fn counting(self, counter: &mut usize) -> CountingIter<Self> {
+        CountingIter(self, counter)
+    }
+}
diff --git a/probe-rs/src/flashing/loader.rs b/probe-rs/src/flashing/loader.rs
index 86aa9206..1b3fc2de 100644
--- a/probe-rs/src/flashing/loader.rs
+++ b/probe-rs/src/flashing/loader.rs
@@ -181,18 +181,14 @@ impl FlashLoader {
             XtalFrequency::_40Mhz
         };
 
-        let flash_size_result = session.halted_access(|sess| {
+        let flash_size_result = session.halted_access(|session| {
             // Figure out flash size from the memory map. We need a different bootloader for each size.
-            Ok(match sess.target().debug_sequence.clone() {
-                DebugSequence::Riscv(sequence) => {
-                    sequence.detect_flash_size(&mut sess.get_riscv_interface(0)?)
-                }
-                DebugSequence::Xtensa(sequence) => {
-                    sequence.detect_flash_size(&mut sess.get_xtensa_interface(0)?)
-                }
+            match session.target().debug_sequence.clone() {
+                DebugSequence::Riscv(sequence) => sequence.detect_flash_size(session),
+                DebugSequence::Xtensa(sequence) => sequence.detect_flash_size(session),
                 DebugSequence::Arm(_) => panic!("There are no ARM ESP targets."),
-            })
-        })?;
+            }
+        });
 
         let flash_size = match flash_size_result.map_err(FileDownloadError::FlashSizeDetection)? {
             Some(0x40000) => Some(espflash::flasher::FlashSize::_256Kb),
diff --git a/probe-rs/src/probe/cmsisdap/mod.rs b/probe-rs/src/probe/cmsisdap/mod.rs
index feeb5350..363470ab 100644
--- a/probe-rs/src/probe/cmsisdap/mod.rs
+++ b/probe-rs/src/probe/cmsisdap/mod.rs
@@ -1013,21 +1013,27 @@ impl RawDapAccess for CmsisDap {
         self
     }
 
-    fn configure_jtag(&mut self) -> Result<(), DebugProbeError> {
-        let chain = self.jtag_scan(
+    fn configure_jtag(&mut self, skip_scan: bool) -> Result<(), DebugProbeError> {
+        let ir_lengths = if skip_scan {
             self.scan_chain
                 .as_ref()
-                .map(|chain| {
-                    chain
-                        .iter()
-                        .filter_map(|s| s.ir_len)
-                        .map(|s| s as usize)
-                        .collect::<Vec<usize>>()
-                })
-                .as_deref(),
-        )?;
-        let ir_lengths = chain.iter().map(|item| item.irlen as u8).collect();
-
+                .map(|chain| chain.iter().filter_map(|s| s.ir_len).collect::<Vec<u8>>())
+                .unwrap_or_default()
+        } else {
+            let chain = self.jtag_scan(
+                self.scan_chain
+                    .as_ref()
+                    .map(|chain| {
+                        chain
+                            .iter()
+                            .filter_map(|s| s.ir_len)
+                            .map(|s| s as usize)
+                            .collect::<Vec<usize>>()
+                    })
+                    .as_deref(),
+            )?;
+            chain.iter().map(|item| item.irlen as u8).collect()
+        };
         tracing::info!("Configuring JTAG with ir lengths: {:?}", ir_lengths);
         self.send_jtag_configure(JtagConfigureRequest::new(ir_lengths)?)?;
 
diff --git a/probe-rs/targets/CC13XX_CC26XX_Series.yaml b/probe-rs/targets/CC13XX_CC26XX_Series.yaml
new file mode 100644
index 00000000..34d3f03f
--- /dev/null
+++ b/probe-rs/targets/CC13XX_CC26XX_Series.yaml
@@ -0,0 +1,284 @@
+---
+name: CC13xx CC26xx Series
+manufacturer: ~
+variants:
+##################### cc13x2_cc26x2 ############################################
+- name: CC2642R
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC2652R
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC2652RB
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC2652P
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC1312R
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC1352R
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+- name: CC1352P
+  part: ~
+  cores:
+  - name: main
+    type: armv7em
+    core_access_options:
+      !Arm
+        ap: 0
+        psel: 0
+        debug_base: ~
+        cti_base: ~
+  jtag:
+    scan_chain:
+      - name: core0
+        ir_len: 4
+      - name: ICEPICK
+        ir_len: 6
+  memory_map:
+  - !Ram
+      name: ~
+      range:
+        start: 0x20000000
+        end: 0x20014000
+      is_boot_memory: false
+      cores:
+      - main
+  - !Nvm
+      name: ~
+      range:
+        start: 0
+        end: 0x58000
+      is_boot_memory: true
+      cores:
+      - main
+  flash_algorithms:
+  - cc13x2-cc26x2-flash
+flash_algorithms:
+- name: cc13x2-cc26x2-flash
+  description: Flash algorithm for the cc13x2-cc26x2 family of devices
+  default: true
+  # Generated from https://github.com/seanmlyons22/cc13x2-cc26x2-flash/commit/f793a5a1c6642e84d47c63d1f000ebe91c247480
+  instructions: 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cmZsb3fU1NQUGAAgYAAAAOcAAAANAAAAFBgAIGAAAADoAAAADQAAABQYACBgAAAA+gAAAA0AAAAUGAAgYAAAAPwAAAANAAAAJwUAIGEJACAAAAAAAQAAAGMJACAwMDAxMDIwMzA0MDUwNjA3MDgwOTEwMTExMjEzMTQxNTE2MTcxODE5MjAyMTIyMjMyNDI1MjYyNzI4MjkzMDMxMzIzMzM0MzUzNjM3MzgzOTQwNDE0MjQzNDQ0NTQ2NDc0ODQ5NTA1MTUyNTM1NDU1NTY1NzU4NTk2MDYxNjI2MzY0NjU2NjY3Njg2OTcwNzE3MjczNzQ3NTc2Nzc3ODc5ODA4MTgyODM4NDg1ODY4Nzg4ODk5MDkxOTI5Mzk0OTU5Njk3OTg5OQDU1NQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANTU1A==
+  load_address: 0x20000020
+  pc_init: 0x1
+  pc_uninit: 0x125
+  pc_program_page: 0x1bb
+  pc_erase_sector: 0x163
+  pc_erase_all: 0x229
+  data_section_offset: 0x20001e24
+  rtt_location: 0x200019e4
+  flash_properties:
+    address_range:
+      start: 0x0
+      end: 0x58000
+    page_size: 0x2000
+    erased_byte_value: 0xff
+    program_page_timeout: 1000
+    erase_sector_timeout: 2000
+    sectors:
+    - size: 0x2000
+      address: 0x0
+  cores:
+  - main


Test patch:
diff --git a/target-gen/src/commands/test.rs b/target-gen/src/commands/test.rs
index 8991e919..9aa681c9 100644
--- a/target-gen/src/commands/test.rs
+++ b/target-gen/src/commands/test.rs
@@ -12,7 +12,7 @@ use probe_rs::{
     flashing::{erase_sectors, DownloadOptions, FlashLoader, FlashProgress},
     Permissions, Session,
 };
-use probe_rs_target::RawFlashAlgorithm;
+use probe_rs_target::{ChipFamily, RawFlashAlgorithm};
 use xshell::{cmd, Shell};
 
 use crate::commands::elf::cmd_elf;
@@ -57,153 +57,166 @@ pub fn cmd_test(
         println!("{error}");
     }
 
+    // Add the target to the registry from the generated YAML file
     let file = File::open(Path::new(definition_export_path))?;
     probe_rs::config::add_target_from_yaml(file)?;
-    let mut session =
-        probe_rs::Session::auto_attach(ALGORITHM_NAME, Permissions::new().allow_erase_all())?;
 
-    // Register callback to update the progress.
-    let t = Rc::new(RefCell::new(Instant::now()));
-    let progress = FlashProgress::new(move |event| {
-        use probe_rs::flashing::ProgressEvent;
-        match event {
-            ProgressEvent::StartedProgramming { .. } => {
-                let mut t = t.borrow_mut();
-                *t = Instant::now();
-            }
-            ProgressEvent::StartedErasing => {
-                let mut t = t.borrow_mut();
-                *t = Instant::now();
-            }
-            ProgressEvent::FailedErasing => {
-                println!("Failed erasing in {:?}", t.borrow().elapsed());
-            }
-            ProgressEvent::FinishedErasing => {
-                println!("Finished erasing in {:?}", t.borrow().elapsed());
-            }
-            ProgressEvent::FailedProgramming => {
-                println!("Failed programming in {:?}", t.borrow().elapsed());
-            }
-            ProgressEvent::FinishedProgramming => {
-                println!("Finished programming in {:?}", t.borrow().elapsed());
-            }
-            ProgressEvent::DiagnosticMessage { message } => {
-                let prefix = "Message".yellow();
-                if message.ends_with('\n') {
-                    print!("{prefix}: {message}");
-                } else {
-                    println!("{prefix}: {message}");
+    // Likely there is only one chip family in the YAML file,
+    // but still iterate over all families and then all varaiants within the family
+    let families: Vec<ChipFamily> = probe_rs::config::families();
+    for family in families.iter() {
+        for chip in family.variants.iter() {
+            let target = chip.name.clone();
+            // We need to get the chip name so that special startup procedure can be used. (matched on name)
+            let mut session =
+                probe_rs::Session::auto_attach(target, Permissions::new().allow_erase_all())?;
+
+            // Register callback to update the progress.
+            let t = Rc::new(RefCell::new(Instant::now()));
+            let progress = FlashProgress::new(move |event| {
+                use probe_rs::flashing::ProgressEvent;
+                match event {
+                    ProgressEvent::StartedProgramming { .. } => {
+                        let mut t = t.borrow_mut();
+                        *t = Instant::now();
+                    }
+                    ProgressEvent::StartedErasing => {
+                        let mut t = t.borrow_mut();
+                        *t = Instant::now();
+                    }
+                    ProgressEvent::FailedErasing => {
+                        println!("Failed erasing in {:?}", t.borrow().elapsed());
+                    }
+                    ProgressEvent::FinishedErasing => {
+                        println!("Finished erasing in {:?}", t.borrow().elapsed());
+                    }
+                    ProgressEvent::FailedProgramming => {
+                        println!("Failed programming in {:?}", t.borrow().elapsed());
+                    }
+                    ProgressEvent::FinishedProgramming => {
+                        println!("Finished programming in {:?}", t.borrow().elapsed());
+                    }
+                    ProgressEvent::DiagnosticMessage { message } => {
+                        let prefix = "Message".yellow();
+                        if message.ends_with('\n') {
+                            print!("{prefix}: {message}");
+                        } else {
+                            println!("{prefix}: {message}");
+                        }
+                    }
+                    _ => (),
                 }
-            }
-            _ => (),
-        }
-    });
+            });
 
-    let flash_algorithm = if let Some(test_start_sector_address) = test_start_sector_address {
-        let predicate = |x: &&RawFlashAlgorithm| {
-            x.flash_properties.address_range.start <= test_start_sector_address
-                && test_start_sector_address < x.flash_properties.address_range.end
-        };
-        let error_message = anyhow!("No flash algorithm matching specified address can be found");
-        session
-            .target()
-            .flash_algorithms
-            .iter()
-            .find(predicate)
-            .ok_or(error_message)?
-    } else {
-        &session.target().flash_algorithms[0]
-    };
-    let flash_properties = flash_algorithm.flash_properties.clone();
-    let start_address = flash_properties.address_range.start;
-    let end_address = flash_properties.address_range.end;
-    let data_size = flash_properties.page_size;
-    let erased_state = flash_properties.erased_byte_value;
-    let sector_size = flash_properties.sectors[0].size;
+            let flash_algorithm = if let Some(test_start_sector_address) = test_start_sector_address
+            {
+                let predicate = |x: &&RawFlashAlgorithm| {
+                    x.flash_properties.address_range.start <= test_start_sector_address
+                        && test_start_sector_address < x.flash_properties.address_range.end
+                };
+                let error_message =
+                    anyhow!("No flash algorithm matching specified address can be found");
+                session
+                    .target()
+                    .flash_algorithms
+                    .iter()
+                    .find(predicate)
+                    .ok_or(error_message)?
+            } else {
+                &session.target().flash_algorithms[0]
+            };
+            let flash_properties = flash_algorithm.flash_properties.clone();
+            let start_address = flash_properties.address_range.start;
+            let end_address = flash_properties.address_range.end;
+            let data_size = flash_properties.page_size;
+            let erased_state = flash_properties.erased_byte_value;
+            let sector_size = flash_properties.sectors[0].size;
 
-    let test_start_sector_address = test_start_sector_address.unwrap_or(start_address);
-    if test_start_sector_address < start_address
-        || test_start_sector_address > start_address + end_address - sector_size * 2
-        || test_start_sector_address % sector_size != 0
-    {
-        return Err(anyhow!(
-            "test_start_sector_address must be sector aligned address pointing flash range"
-        ));
-    }
-    let test_start_sector_index =
-        ((test_start_sector_address - start_address) / sector_size) as usize;
+            let test_start_sector_address = test_start_sector_address.unwrap_or(start_address);
+            if test_start_sector_address < start_address
+                || test_start_sector_address > start_address + end_address - sector_size * 2
+                || test_start_sector_address % sector_size != 0
+            {
+                return Err(anyhow!(
+                    "test_start_sector_address must be sector aligned address pointing flash range"
+                ));
+            }
+            let test_start_sector_index =
+                ((test_start_sector_address - start_address) / sector_size) as usize;
 
-    let test = "Test".green();
-    println!("{test}: Erasing sectorwise and writing two pages ...");
-    run_flash_erase(
-        &mut session,
-        progress.clone(),
-        EraseSectors(test_start_sector_index, 2),
-    )?;
-    let mut readback = vec![0; (sector_size * 2) as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address, &mut readback)?;
-    assert!(
-        !readback.iter().any(|v| *v != erased_state),
-        "Not all sectors were erased"
-    );
+            let test = "Test".green();
+            println!("{test}: Erasing sectorwise and writing two pages ...");
+            run_flash_erase(
+                &mut session,
+                progress.clone(),
+                EraseSectors(test_start_sector_index, 2),
+            )?;
+            let mut readback = vec![0; (sector_size * 2) as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address, &mut readback)?;
+            assert!(
+                !readback.iter().any(|v| *v != erased_state),
+                "Not all sectors were erased"
+            );
 
-    let mut loader = session.target().flash_loader();
-    let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
-    loader.add_data(test_start_sector_address + 1, &data)?;
-    run_flash_download(&mut session, loader, progress.clone(), true)?;
-    let mut readback = vec![0; data_size as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address + 1, &mut readback)?;
-    assert_eq!(readback, data);
+            let mut loader = session.target().flash_loader();
+            let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
+            loader.add_data(test_start_sector_address + 1, &data)?;
+            run_flash_download(&mut session, loader, progress.clone(), true)?;
+            let mut readback = vec![0; data_size as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address + 1, &mut readback)?;
+            assert_eq!(readback, data);
 
-    println!("{test}: Erasing the entire chip and writing two pages ...");
-    run_flash_erase(&mut session, progress.clone(), EraseAll)?;
-    let mut readback = vec![0; (sector_size * 2) as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address, &mut readback)?;
-    assert!(
-        !readback.iter().any(|v| *v != erased_state),
-        "Not all sectors were erased"
-    );
+            println!("{test}: Erasing the entire chip and writing two pages ...");
+            run_flash_erase(&mut session, progress.clone(), EraseAll)?;
+            let mut readback = vec![0; (sector_size * 2) as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address, &mut readback)?;
+            assert!(
+                !readback.iter().any(|v| *v != erased_state),
+                "Not all sectors were erased"
+            );
 
-    let mut loader = session.target().flash_loader();
-    let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
-    loader.add_data(test_start_sector_address + 1, &data)?;
-    run_flash_download(&mut session, loader, progress.clone(), true)?;
-    let mut readback = vec![0; data_size as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address + 1, &mut readback)?;
-    assert_eq!(readback, data);
+            let mut loader = session.target().flash_loader();
+            let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
+            loader.add_data(test_start_sector_address + 1, &data)?;
+            run_flash_download(&mut session, loader, progress.clone(), true)?;
+            let mut readback = vec![0; data_size as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address + 1, &mut readback)?;
+            assert_eq!(readback, data);
 
-    println!("{test}: Erasing sectorwise and writing two pages double buffered ...");
-    run_flash_erase(
-        &mut session,
-        progress.clone(),
-        EraseSectors(test_start_sector_index, 2),
-    )?;
-    let mut readback = vec![0; (sector_size * 2) as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address, &mut readback)?;
-    assert!(
-        !readback.iter().any(|v| *v != erased_state),
-        "Not all sectors were erased"
-    );
+            println!("{test}: Erasing sectorwise and writing two pages double buffered ...");
+            run_flash_erase(
+                &mut session,
+                progress.clone(),
+                EraseSectors(test_start_sector_index, 2),
+            )?;
+            let mut readback = vec![0; (sector_size * 2) as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address, &mut readback)?;
+            assert!(
+                !readback.iter().any(|v| *v != erased_state),
+                "Not all sectors were erased"
+            );
 
-    let mut loader = session.target().flash_loader();
-    let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
-    loader.add_data(test_start_sector_address + 1, &data)?;
-    run_flash_download(&mut session, loader, progress, false)?;
-    let mut readback = vec![0; data_size as usize];
-    session
-        .core(0)?
-        .read_8(test_start_sector_address + 1, &mut readback)?;
-    assert_eq!(readback, data);
+            let mut loader = session.target().flash_loader();
+            let data = (0..data_size).map(|n| (n % 256) as u8).collect::<Vec<_>>();
+            loader.add_data(test_start_sector_address + 1, &data)?;
+            run_flash_download(&mut session, loader, progress, false)?;
+            let mut readback = vec![0; data_size as usize];
+            session
+                .core(0)?
+                .read_8(test_start_sector_address + 1, &mut readback)?;
+            assert_eq!(readback, data);
+        }
+    }
 
     Ok(())
 }