Repository: probe-rs/probe-rs
Pull request #2620 (2024-07-02 20:28:51)

Clean up a hard-to-read match


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 430ffa84..934aa3c4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -77,6 +77,11 @@ aarch64-unknown-linux-musl = "buildjet-2vcpu-ubuntu-2204-arm"
 libudev-dev = '*'
 libudev1 = { stage = ["run"] }
 
+[workspace.lints.clippy]
+unnecessary_self_imports = "deny"
+enum_glob_use = "deny"
+macro_use_imports = "deny"
+
 # The profile that 'cargo dist' will build with
 [profile.dist]
 inherits = "release"
diff --git a/changelog/removed-scan-region-range.md b/changelog/removed-scan-region-range.md
new file mode 100644
index 00000000..92ef92fe
--- /dev/null
+++ b/changelog/removed-scan-region-range.md
@@ -0,0 +1,1 @@
+Removed `ScanRegion::Range` and added `ScanRegion::range()`
\ No newline at end of file
diff --git a/probe-rs-mi/Cargo.toml b/probe-rs-mi/Cargo.toml
index bb445c09..e7b164ad 100644
--- a/probe-rs-mi/Cargo.toml
+++ b/probe-rs-mi/Cargo.toml
@@ -13,3 +13,6 @@ description = "The probe-rs machine interface for computers interfacing probe-rs
 [dependencies]
 semver = { version = "1", features = ["serde"] }
 serde = { version = "1", features = ["derive"] }
+
+[lints]
+workspace = true
diff --git a/probe-rs-mi/src/lib.rs b/probe-rs-mi/src/lib.rs
index dfbf1404..0d5ede3b 100644
--- a/probe-rs-mi/src/lib.rs
+++ b/probe-rs-mi/src/lib.rs
@@ -1,5 +1,4 @@
-#![warn(missing_docs)]
-
 //! This crate acts as the specification and API for the machine interface (MI) for probe-rs.
+#![warn(missing_docs)]
 
 pub mod meta;
diff --git a/probe-rs-target/Cargo.toml b/probe-rs-target/Cargo.toml
index 60190bfe..9f1f6667 100644
--- a/probe-rs-target/Cargo.toml
+++ b/probe-rs-target/Cargo.toml
@@ -16,3 +16,6 @@ jep106 = "0.2.8"
 serde = { version = "1", features = ["derive"] }
 base64 = "0.22.1"
 url = { version = "2.5", features = ["serde"] }
+
+[lints]
+workspace = true
diff --git a/probe-rs-target/src/chip_family.rs b/probe-rs-target/src/chip_family.rs
index ef38237e..de653ae3 100644
--- a/probe-rs-target/src/chip_family.rs
+++ b/probe-rs-target/src/chip_family.rs
@@ -1,5 +1,6 @@
 use crate::serialize::hex_jep106_option;
 use crate::{chip_detection::ChipDetectionMethod, CoreAccessOptions};
+use crate::{MemoryRange, MemoryRegion};
 
 use super::chip::Chip;
 use super::flash_algorithm::RawFlashAlgorithm;
@@ -221,6 +222,7 @@ impl ChipFamily {
         self.ensure_at_least_one_core()?;
         self.reject_incorrect_core_access_options()?;
         self.validate_memory_regions()?;
+        self.validate_rtt_scan_regions()?;
 
         Ok(())
     }
@@ -366,6 +368,38 @@ impl ChipFamily {
 
         Ok(())
     }
+
+    fn validate_rtt_scan_regions(&self) -> Result<(), String> {
+        for variant in &self.variants {
+            let Some(ranges) = &variant.rtt_scan_ranges else {
+                return Ok(());
+            };
+
+            let ram_regions = variant
+                .memory_map
+                .iter()
+                .filter_map(MemoryRegion::as_ram_region);
+
+            // The custom ranges must all be enclosed by exactly one of
+            // the defined RAM regions.
+            // TODO: we should allow consecutive RAM regions
+            for rng in ranges {
+                if ram_regions
+                    .clone()
+                    .all(|region| region.range.contains_range(rng))
+                {
+                    continue;
+                }
+
+                return Err(format!(
+                    "The RTT scan region ({:#010x?}) of {} is not enclosed by any single RAM region.",
+                    rng, variant.name,
+                ));
+            }
+        }
+
+        Ok(())
+    }
 }
 
 impl ChipFamily {
diff --git a/probe-rs-target/src/lib.rs b/probe-rs-target/src/lib.rs
index 96e0a414..9d00d32c 100644
--- a/probe-rs-target/src/lib.rs
+++ b/probe-rs-target/src/lib.rs
@@ -1,5 +1,3 @@
-#![warn(missing_docs)]
-
 //! Target description schema
 //!
 //! For debugging and flashing different chips, called *target* in probe-rs, some
@@ -10,6 +8,7 @@
 //!
 //! This crate contains the schema structs for the YAML target description files.
 //!
+#![warn(missing_docs)]
 
 mod chip;
 pub mod chip_detection;
diff --git a/probe-rs-tools/Cargo.toml b/probe-rs-tools/Cargo.toml
index a04430e2..9f0f2da1 100644
--- a/probe-rs-tools/Cargo.toml
+++ b/probe-rs-tools/Cargo.toml
@@ -105,3 +105,6 @@ pretty_assertions = "1.4.0"
 # Set the proper name for the homebrew formula
 [package.metadata.dist]
 formula = "probe-rs"
+
+[lints]
+workspace = true
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/error.rs b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/error.rs
index 14f96ce3..73fb627f 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/error.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/cargo_embed/error.rs
@@ -11,24 +11,20 @@ pub enum DownloadError {
 
 impl Error for DownloadError {
     fn source(&self) -> Option<&(dyn Error + 'static)> {
-        use DownloadError::*;
-
         match self {
-            DebugProbe(ref e) => Some(e),
-            AccessPort(ref e) => Some(e),
-            StdIO(ref e) => Some(e),
+            Self::DebugProbe(ref e) => Some(e),
+            Self::AccessPort(ref e) => Some(e),
+            Self::StdIO(ref e) => Some(e),
         }
     }
 }
 
 impl fmt::Display for DownloadError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        use DownloadError::*;
-
         match self {
-            DebugProbe(ref e) => e.fmt(f),
-            AccessPort(ref e) => e.fmt(f),
-            StdIO(ref e) => e.fmt(f),
+            Self::DebugProbe(ref e) => e.fmt(f),
+            Self::AccessPort(ref e) => e.fmt(f),
+            Self::StdIO(ref e) => e.fmt(f),
         }
     }
 }
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 6fcf37a3..2acbcfd1 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
@@ -8,7 +8,7 @@ use colored::Colorize;
 use parking_lot::FairMutex;
 use probe_rs::gdb_server::GdbInstanceConfiguration;
 use probe_rs::probe::list::Lister;
-use probe_rs::rtt::ScanRegion;
+use probe_rs::rtt::{try_attach_to_rtt_shared, Error, ScanRegion};
 use probe_rs::{probe::DebugProbeSelector, Session};
 use std::ffi::OsString;
 use std::fs;
@@ -27,9 +27,7 @@ use crate::util::cargo::target_instruction_set;
 use crate::util::common_options::{BinaryDownloadOptions, OperationError, ProbeOptions};
 use crate::util::flash::{build_loader, run_flash_download};
 use crate::util::logging::setup_logging;
-use crate::util::rtt::{
-    self, try_attach_to_rtt_shared, DefmtState, RttActiveTarget, RttChannelConfig, RttConfig,
-};
+use crate::util::rtt::{self, DefmtState, RttActiveTarget, RttChannelConfig, RttConfig};
 use crate::util::{cargo::build_artifact, common_options::CargoOptions, logging, rtt::DataFormat};
 use crate::FormatOptions;
 
@@ -369,7 +367,6 @@ fn run_rttui_app(
         session,
         core_id,
         config.rtt.timeout,
-        &ScanRegion::Ram,
         elf_path,
         &rtt_config,
         timezone_offset,
@@ -440,12 +437,10 @@ fn run_rttui_app(
     Ok(())
 }
 
-#[allow(clippy::too_many_arguments)]
 fn attach_to_rtt_shared(
     session: &FairMutex<Session>,
     core_id: usize,
     timeout: Duration,
-    rtt_region: &ScanRegion,
     elf_file: &Path,
     rtt_config: &RttConfig,
     timestamp_offset: UtcOffset,
@@ -457,14 +452,15 @@ fn attach_to_rtt_shared(
     let scan_region = if let Some(address) = RttActiveTarget::get_rtt_symbol_from_bytes(&elf) {
         ScanRegion::Exact(address)
     } else {
-        rtt_region.clone()
+        ScanRegion::Ram
     };
 
-    let rtt = try_attach_to_rtt_shared(session, core_id, timeout, &scan_region)?;
-
-    let Some(rtt) = rtt else {
-        return Ok(None);
+    let rtt = match try_attach_to_rtt_shared(session, core_id, timeout, &scan_region) {
+        Ok(rtt) => rtt,
+        Err(Error::NoControlBlockLocation) => return Ok(None),
+        Err(err) => return Err(anyhow!("Error attempting to attach to RTT: {err}")),
     };
+
     let mut session_handle = session.lock();
     let mut core = session_handle.core(core_id)?;
 
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
index 6ccc85a5..b3b3da17 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/core_data.rs
@@ -179,7 +179,7 @@ impl<'p> CoreHandle<'p> {
     pub fn attach_to_rtt<P: ProtocolAdapter>(
         &mut self,
         debug_adapter: &mut DebugAdapter<P>,
-        program_binary: &std::path::Path,
+        program_binary: &Path,
         rtt_config: &rtt::RttConfig,
         timestamp_offset: UtcOffset,
     ) -> Result<()> {
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
index dee69971..f29051cf 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/dap_server/server/session_data.rs
@@ -248,22 +248,19 @@ impl SessionData {
                 } else if debug_adapter.configuration_is_done() {
                     // We have not yet reached the point in the target application where the RTT buffers are initialized,
                     // so, provided we have processed the MSDAP request for "configurationDone" , we should check again.
-                    {
-                        #[allow(clippy::unwrap_used)]
-                        match target_core.attach_to_rtt(
-                            debug_adapter,
-                            core_config.program_binary.as_ref().unwrap(),
-                            &core_config.rtt_config,
-                            timestamp_offset,
-                        ) {
-                            Ok(_) => {
-                                // Nothing else to do.
-                            }
-                            Err(error) => {
-                                debug_adapter
-                                    .show_error_message(&DebuggerError::Other(error))
-                                    .ok();
-                            }
+
+                    #[allow(clippy::unwrap_used)]
+                    match target_core.attach_to_rtt(
+                        debug_adapter,
+                        core_config.program_binary.as_ref().unwrap(),
+                        &core_config.rtt_config,
+                        timestamp_offset,
+                    ) {
+                        Ok(_) => {} // Nothing else to do.
+                        Err(error) => {
+                            debug_adapter
+                                .show_error_message(&DebuggerError::Other(error))
+                                .ok();
                         }
                     }
                 }
diff --git a/probe-rs-tools/src/bin/probe-rs/cmd/run/mod.rs b/probe-rs-tools/src/bin/probe-rs/cmd/run/mod.rs
index ef3c12f7..4599ab85 100644
--- a/probe-rs-tools/src/bin/probe-rs/cmd/run/mod.rs
+++ b/probe-rs-tools/src/bin/probe-rs/cmd/run/mod.rs
@@ -5,7 +5,6 @@ use test_run_mode::*;
 
 use std::fs::{self, File};
 use std::io::{Read, Write};
-use std::ops::Range;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::Arc;
@@ -14,10 +13,11 @@ use std::time::{Duration, Instant};
 use anyhow::{anyhow, Context, Result};
 use probe_rs::debug::{DebugInfo, DebugRegisters};
 use probe_rs::flashing::FileDownloadError;
-use probe_rs::rtt::ScanRegion;
 use probe_rs::{
-    exception_handler_for_core, probe::list::Lister, Core, CoreInterface, Error, HaltReason,
-    Session, VectorCatchCondition,
+    exception_handler_for_core,
+    probe::list::Lister,
+    rtt::{try_attach_to_rtt, Error as RttError, ScanRegion},
+    Core, CoreInterface, Error, HaltReason, Session, VectorCatchCondition,
 };
 use signal_hook::consts::signal;
 use time::UtcOffset;
@@ -25,7 +25,7 @@ use time::UtcOffset;
 use crate::util::common_options::{BinaryDownloadOptions, ProbeOptions};
 use crate::util::flash::{build_loader, run_flash_download};
 use crate::util::rtt::{
-    self, try_attach_to_rtt, ChannelDataCallbacks, DefmtState, RttActiveTarget, RttConfig,
+    self, ChannelDataCallbacks, DefmtState, RttActiveTarget, RttChannelConfig, RttConfig,
 };
 use crate::FormatOptions;
 
@@ -122,7 +122,7 @@ impl Cmd {
 
         let rtt_scan_regions = match self.shared_options.rtt_scan_memory {
             true => session.target().rtt_scan_regions.clone(),
-            false => Vec::new(),
+            false => ScanRegion::Ranges(vec![]),
         };
 
         run_mode.run(
@@ -189,7 +189,7 @@ fn detect_run_mode(cmd: &Cmd) -> Result<Box<dyn RunMode>, anyhow::Error> {
 
 struct RunLoop {
     core_id: usize,
-    rtt_scan_regions: Vec<Range<u64>>,
+    rtt_scan_regions: ScanRegion,
     path: PathBuf,
     timestamp_offset: UtcOffset,
     always_print_stacktrace: bool,
@@ -258,8 +258,8 @@ impl RunLoop {
         }
         let start = Instant::now();
 
-        let mut rtt_config = rtt::RttConfig::default();
-        rtt_config.channels.push(rtt::RttChannelConfig {
+        let mut rtt_config = RttConfig::default();
+        rtt_config.channels.push(RttChannelConfig {
             channel_number: Some(0),
             show_location: !self.no_location,
             log_format: self.log_format.clone(),
@@ -269,8 +269,8 @@ impl RunLoop {
         let mut rtta = attach_to_rtt(
             core,
             Duration::from_secs(1),
-            &ScanRegion::Ranges(self.rtt_scan_regions.clone()),
-            Path::new(&self.path),
+            &self.rtt_scan_regions,
+            &self.path,
             &rtt_config,
             self.timestamp_offset,
         )
@@ -303,7 +303,7 @@ impl RunLoop {
     fn do_run_until<F, R>(
         &self,
         core: &mut Core,
-        rtta: &mut Option<rtt::RttActiveTarget>,
+        rtta: &mut Option<RttActiveTarget>,
         output_stream: OutputStream,
         timeout: Option<Duration>,
         start: Instant,
@@ -466,7 +466,7 @@ fn print_stacktrace<S: Write + ?Sized>(
 
 /// Poll RTT and print the received buffer.
 fn poll_rtt<S: Write + ?Sized>(
-    rtta: &mut Option<rtt::RttActiveTarget>,
+    rtta: &mut Option<RttActiveTarget>,
     core: &mut Core<'_>,
     out_stream: &mut S,
 ) -> Result<bool, anyhow::Error> {
@@ -516,16 +516,18 @@ fn attach_to_rtt(
     // If we find it, we can use the exact address to attach to the RTT control block. Otherwise, we
     // fall back to the caller-provided scan regions.
     let elf = fs::read(elf_file)?;
+    let exact_region;
     let scan_region = if let Some(address) = RttActiveTarget::get_rtt_symbol_from_bytes(&elf) {
-        ScanRegion::Exact(address)
+        exact_region = ScanRegion::Exact(address);
+        &exact_region
     } else {
-        rtt_region.clone()
+        rtt_region
     };
 
-    let rtt = try_attach_to_rtt(core, timeout, &scan_region)?;
-
-    let Some(rtt) = rtt else {
-        return Ok(None);
+    let rtt = match try_attach_to_rtt(core, timeout, scan_region) {
+        Ok(rtt) => rtt,
+        Err(RttError::NoControlBlockLocation) => return Ok(None),
+        Err(err) => return Err(anyhow!("Error attempting to attach to RTT: {err}")),
     };
 
     let defmt_state = DefmtState::try_from_bytes(&elf)?;
diff --git a/probe-rs-tools/src/bin/probe-rs/util/rtt.rs b/probe-rs-tools/src/bin/probe-rs/util/rtt.rs
index 7b2cb926..55358cae 100644
--- a/probe-rs-tools/src/bin/probe-rs/util/rtt.rs
+++ b/probe-rs-tools/src/bin/probe-rs/util/rtt.rs
@@ -2,13 +2,13 @@ use anyhow::{anyhow, Result};
 use defmt_decoder::log::format::{Formatter, FormatterConfig, FormatterFormat};
 use defmt_decoder::DecodeError;
 pub use probe_rs::rtt::ChannelMode;
-use probe_rs::rtt::{DownChannel, Error, Rtt, ScanRegion, UpChannel};
+use probe_rs::rtt::{DownChannel, Error, UpChannel};
 use probe_rs::{Core, Session};
 use probe_rs_target::MemoryRegion;
 use serde::{Deserialize, Serialize};
 use std::fs::File;
 use std::path::PathBuf;
-use std::time::{Duration, Instant};
+use std::time::Duration;
 use std::{
     fmt,
     fmt::Write,
@@ -49,29 +49,6 @@ pub fn get_target_core_id(session: &mut Session, elf_file: impl AsRef<Path>) ->
     maybe_core_id().unwrap_or(0)
 }
 
-/// Try to find the RTT control block in the ELF file and attach to it.
-///
-/// This function can return `Ok(None)` to indicate that RTT is not available on the target.
-fn try_attach_to_rtt_once(core: &mut Core, rtt_region: &ScanRegion) -> Result<Option<Rtt>> {
-    tracing::debug!("Initializing RTT");
-
-    if let ScanRegion::Ranges(rngs) = &rtt_region {
-        if rngs.is_empty() {
-            // We have no regions to scan so we cannot initialize RTT.
-            tracing::debug!("ELF file has no RTT block symbol, and this target does not support automatic scanning");
-            return Ok(None);
-        }
-    }
-
-    match Rtt::attach_region(core, rtt_region) {
-        Ok(rtt) => {
-            tracing::info!("RTT initialized.");
-            Ok(Some(rtt))
-        }
-        Err(err) => Err(anyhow!("Error attempting to attach to RTT: {}", err)),
-    }
-}
-
 /// Used by serde to provide defaults for `RttChannelConfig::show_timestamps`
 fn default_show_timestamps() -> bool {
     true
@@ -651,53 +628,3 @@ impl fmt::Debug for RttBuffer {
         self.0.fmt(f)
     }
 }
-
-fn try_attach_to_rtt_inner(
-    mut try_attach_once: impl FnMut() -> Result<Option<Rtt>>,
-    timeout: Duration,
-) -> Result<Option<Rtt>> {
-    let t = Instant::now();
-    let mut attempt = 1;
-    loop {
-        tracing::debug!("Initializing RTT (attempt {attempt})...");
-
-        match try_attach_once() {
-            Err(_) if t.elapsed() < timeout => {
-                attempt += 1;
-                tracing::debug!("Failed to initialize RTT. Retrying until timeout.");
-                thread::sleep(Duration::from_millis(50));
-            }
-            result => return result,
-        }
-    }
-}
-
-// TODO: these should be part of the library, but that requires refactoring the errors to not use
-// anyhow. Also, we probably should only have one variant of this function and pass the closure
-// in as an argument.
-
-/// Try to attach to RTT, with the given timeout.
-pub fn try_attach_to_rtt(
-    core: &mut Core<'_>,
-    timeout: Duration,
-    rtt_region: &ScanRegion,
-) -> Result<Option<Rtt>> {
-    try_attach_to_rtt_inner(|| try_attach_to_rtt_once(core, rtt_region), timeout)
-}
-
-/// Try to attach to RTT, with the given timeout.
-pub fn try_attach_to_rtt_shared(
-    session: &parking_lot::FairMutex<Session>,
-    core_id: usize,
-    timeout: Duration,
-    rtt_region: &ScanRegion,
-) -> Result<Option<Rtt>> {
-    try_attach_to_rtt_inner(
-        || {
-            let mut session_handle = session.lock();
-            let mut core = session_handle.core(core_id)?;
-            try_attach_to_rtt_once(&mut core, rtt_region)
-        },
-        timeout,
-    )
-}
diff --git a/probe-rs/Cargo.toml b/probe-rs/Cargo.toml
index 5805921d..0ea44e4a 100644
--- a/probe-rs/Cargo.toml
+++ b/probe-rs/Cargo.toml
@@ -106,3 +106,6 @@ replace = "## [{{version}}]\n\nReleased {{date}}"
 file = "../CHANGELOG.md"
 search = "\\[unreleased\\]: https://github.com/probe-rs/probe-rs/compare/v([a-z0-9.-]+)\\.\\.\\.master"
 replace = "[unreleased]: https://github.com/probe-rs/probe-rs/compare/v{{version}}...master\n[{{version}}]: https://github.com/probe-rs/probe-rs/compare/v$1...v{{version}}"
+
+[lints]
+workspace = true
diff --git a/probe-rs/src/architecture/arm/core/exception_handling/armv6m.rs b/probe-rs/src/architecture/arm/core/exception_handling/armv6m.rs
index 2764915c..fd0114fb 100644
--- a/probe-rs/src/architecture/arm/core/exception_handling/armv6m.rs
+++ b/probe-rs/src/architecture/arm/core/exception_handling/armv6m.rs
@@ -4,7 +4,7 @@ use crate::{
     Error, MemoryInterface,
 };
 
-use super::armv6m_armv7m_shared::{self};
+use super::armv6m_armv7m_shared;
 
 /// Decode the exception number.
 #[derive(Debug, Copy, Clone, PartialEq)]
diff --git a/probe-rs/src/architecture/arm/core/exception_handling/armv7m.rs b/probe-rs/src/architecture/arm/core/exception_handling/armv7m.rs
index 9023ff35..dbbe45ed 100644
--- a/probe-rs/src/architecture/arm/core/exception_handling/armv7m.rs
+++ b/probe-rs/src/architecture/arm/core/exception_handling/armv7m.rs
@@ -1,4 +1,4 @@
-use super::armv6m_armv7m_shared::{self};
+use super::armv6m_armv7m_shared;
 use crate::{
     core::{ExceptionInfo, ExceptionInterface},
     debug::{DebugInfo, DebugRegisters},
diff --git a/probe-rs/src/architecture/arm/dp/mod.rs b/probe-rs/src/architecture/arm/dp/mod.rs
index c68ab28f..2565255b 100644
--- a/probe-rs/src/architecture/arm/dp/mod.rs
+++ b/probe-rs/src/architecture/arm/dp/mod.rs
@@ -743,14 +743,12 @@ pub enum DebugPortVersion {
 
 impl From<DebugPortVersion> for u8 {
     fn from(version: DebugPortVersion) -> Self {
-        use DebugPortVersion::*;
-
         match version {
-            DPv0 => 0,
-            DPv1 => 1,
-            DPv2 => 2,
-            DPv3 => 3,
-            Unsupported(val) => val,
+            DebugPortVersion::DPv0 => 0,
+            DebugPortVersion::DPv1 => 1,
+            DebugPortVersion::DPv2 => 2,
+            DebugPortVersion::DPv3 => 3,
+            DebugPortVersion::Unsupported(val) => val,
         }
     }
 }
@@ -766,14 +764,12 @@ impl PartialOrd for DebugPortVersion {
 
 impl Display for DebugPortVersion {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        use DebugPortVersion::*;
-
         match self {
-            DPv0 => write!(f, "DPv0"),
-            DPv1 => write!(f, "DPv1"),
-            DPv2 => write!(f, "DPv2"),
-            DPv3 => write!(f, "DPv3"),
-            Unsupported(version) => write!(f, "<unsupported Debugport Version {version}>"),
+            Self::DPv0 => write!(f, "DPv0"),
+            Self::DPv1 => write!(f, "DPv1"),
+            Self::DPv2 => write!(f, "DPv2"),
+            Self::DPv3 => write!(f, "DPv3"),
+            Self::Unsupported(version) => write!(f, "<unsupported Debugport Version {version}>"),
         }
     }
 }
@@ -781,11 +777,11 @@ impl Display for DebugPortVersion {
 impl From<u8> for DebugPortVersion {
     fn from(value: u8) -> Self {
         match value {
-            0 => DebugPortVersion::DPv0,
-            1 => DebugPortVersion::DPv1,
-            2 => DebugPortVersion::DPv2,
-            3 => DebugPortVersion::DPv3,
-            value => DebugPortVersion::Unsupported(value),
+            0 => Self::DPv0,
+            1 => Self::DPv1,
+            2 => Self::DPv2,
+            3 => Self::DPv3,
+            value => Self::Unsupported(value),
         }
     }
 }
diff --git a/probe-rs/src/architecture/riscv/communication_interface.rs b/probe-rs/src/architecture/riscv/communication_interface.rs
index 44c4c255..0c6afce4 100644
--- a/probe-rs/src/architecture/riscv/communication_interface.rs
+++ b/probe-rs/src/architecture/riscv/communication_interface.rs
@@ -110,17 +110,15 @@ pub enum AbstractCommandErrorKind {
 
 impl AbstractCommandErrorKind {
     fn parse(value: u8) -> Self {
-        use AbstractCommandErrorKind::*;
-
         match value {
-            0 => None,
-            1 => Busy,
-            2 => NotSupported,
-            3 => Exception,
-            4 => HaltResume,
-            5 => Bus,
-            6 => _Reserved,
-            7 => Other,
+            0 => Self::None,
+            1 => Self::Busy,
+            2 => Self::NotSupported,
+            3 => Self::Exception,
+            4 => Self::HaltResume,
+            5 => Self::Bus,
+            6 => Self::_Reserved,
+            7 => Self::Other,
             _ => unreachable!("cmderr is a 3 bit value, values higher than 7 should not occur."),
         }
     }
diff --git a/probe-rs/src/architecture/riscv/dtm/jtag_dtm.rs b/probe-rs/src/architecture/riscv/dtm/jtag_dtm.rs
index cd8e73b9..8329fa0e 100644
--- a/probe-rs/src/architecture/riscv/dtm/jtag_dtm.rs
+++ b/probe-rs/src/architecture/riscv/dtm/jtag_dtm.rs
@@ -2,13 +2,15 @@
 //!
 //! The DTM is responsible for access to the debug module.
 //! Currently, only JTAG is supported.
-use crate::architecture::riscv::dtm::dtm_access::DtmAccess;
+
 use bitfield::bitfield;
 use std::time::{Duration, Instant};
 
 use crate::architecture::riscv::communication_interface::{
     RiscvCommunicationInterface, RiscvDebugInterfaceState, RiscvError, RiscvInterfaceBuilder,
 };
+use crate::architecture::riscv::dtm::dtm_access::DtmAccess;
+use crate::error::Error;
 use crate::probe::DebugProbeError;
 use crate::probe::{
     CommandResult, DeferredResultIndex, DeferredResultSet, JTAGAccess, JtagCommandQueue,
@@ -114,7 +116,7 @@ impl<'probe> JtagDtm<'probe> {
             transform: |_, result| {
                 Self::transform_dmi_result(result)
                     .map(CommandResult::U32)
-                    .map_err(|e| crate::Error::Riscv(e.map_as_err().unwrap_err()))
+                    .map_err(|e| Error::Riscv(e.map_as_err().unwrap_err()))
             },
             len: bit_size,
         }))
@@ -229,21 +231,17 @@ impl DtmAccess for JtagDtm<'_> {
                     return Ok(());
                 }
                 Err(e) => match e.error {
-                    crate::Error::Riscv(ae) => {
-                        match ae {
-                            RiscvError::DtmOperationInProcess => {
-                                self.clear_error_state()?;
-
-                                // queue up the remaining commands when we retry
-                                cmds.consume(e.results.len());
-                                self.state.jtag_results.merge_from(e.results);
-
-                                self.probe.set_idle_cycles(self.probe.idle_cycles() + 1);
-                            }
-                            _ => return Err(ae),
-                        }
+                    Error::Riscv(RiscvError::DtmOperationInProcess) => {
+                        self.clear_error_state()?;
+
+                        // queue up the remaining commands when we retry
+                        cmds.consume(e.results.len());
+                        self.state.jtag_results.merge_from(e.results);
+
+                        self.probe.set_idle_cycles(self.probe.idle_cycles() + 1);
                     }
-                    crate::Error::Probe(err) => return Err(err.into()),
+                    Error::Riscv(error) => return Err(error),
+                    Error::Probe(error) => return Err(error.into()),
                     _other => unreachable!(),
                 },
             }
@@ -303,17 +301,17 @@ pub enum DmiOperation {
 impl DmiOperation {
     fn opcode(&self) -> u8 {
         match self {
-            DmiOperation::NoOp => 0,
-            DmiOperation::Read { .. } => 1,
-            DmiOperation::Write { .. } => 2,
+            Self::NoOp => 0,
+            Self::Read { .. } => 1,
+            Self::Write { .. } => 2,
         }
     }
 
-    pub fn to_byte_batch(self: DmiOperation) -> [u8; 16] {
+    pub fn to_byte_batch(self) -> [u8; 16] {
         let (opcode, address, value): (u128, u128, u128) = match self {
-            DmiOperation::NoOp => (self.opcode() as u128, 0, 0),
-            DmiOperation::Read { address } => (self.opcode() as u128, address as u128, 0),
-            DmiOperation::Write { address, value } => {
+            Self::NoOp => (self.opcode() as u128, 0, 0),
+            Self::Read { address } => (self.opcode() as u128, address as u128, 0),
+            Self::Write { address, value } => {
                 (self.opcode() as u128, address as u128, value as u128)
             }
         };
@@ -337,23 +335,21 @@ pub enum DmiOperationStatus {
 impl DmiOperationStatus {
     pub fn map_as_err(self) -> Result<(), RiscvError> {
         match self {
-            DmiOperationStatus::Ok => Ok(()),
-            DmiOperationStatus::Reserved => unimplemented!("Reserved."),
-            DmiOperationStatus::OperationFailed => Err(RiscvError::DtmOperationFailed),
-            DmiOperationStatus::RequestInProgress => Err(RiscvError::DtmOperationInProcess),
+            Self::Ok => Ok(()),
+            Self::Reserved => unimplemented!("Reserved."),
+            Self::OperationFailed => Err(RiscvError::DtmOperationFailed),
+            Self::RequestInProgress => Err(RiscvError::DtmOperationInProcess),
         }
     }
 }
 
 impl DmiOperationStatus {
     pub(crate) fn parse(value: u8) -> Option<Self> {
-        use DmiOperationStatus::*;
-
         let status = match value {
-            0 => Ok,
-            1 => Reserved,
-            2 => OperationFailed,
-            3 => RequestInProgress,
+            0 => Self::Ok,
+            1 => Self::Reserved,
+            2 => Self::OperationFailed,
+            3 => Self::RequestInProgress,
             _ => return None,
         };
 
diff --git a/probe-rs/src/config/registry.rs b/probe-rs/src/config/registry.rs
index 31a27655..395be56d 100644
--- a/probe-rs/src/config/registry.rs
+++ b/probe-rs/src/config/registry.rs
@@ -30,8 +30,6 @@ pub enum RegistryError {
     Yaml(#[from] serde_yaml::Error),
     /// Invalid chip family definition ({0.name}): {1}
     InvalidChipFamilyDefinition(Box<ChipFamily>, String),
-    /// Chip's RTT scan region {0:#010X?} is not enclosed by any single RAM region.
-    InvalidRttScanRange(std::ops::Range<u64>),
 }
 
 fn add_generic_targets(vec: &mut Vec<ChipFamily>) {
@@ -221,7 +219,7 @@ impl Registry {
             );
         }
 
-        let targ = self.get_target(family, chip)?;
+        let targ = self.get_target(family, chip);
         Ok((targ, family.clone()))
     }
 
@@ -302,10 +300,10 @@ impl Registry {
                 identified_chips[0]
             }
         };
-        self.get_target(family, chip)
+        Ok(self.get_target(family, chip))
     }
 
-    fn get_target(&self, family: &ChipFamily, chip: &Chip) -> Result<Target, RegistryError> {
+    fn get_target(&self, family: &ChipFamily, chip: &Chip) -> Target {
         // The validity of the given `ChipFamily` is checked in test time and in `add_target_from_yaml`.
         Target::new(family, chip)
     }
@@ -507,7 +505,7 @@ mod tests {
                 family
                     .variants()
                     .iter()
-                    .map(|chip| registry.get_target(family, chip).unwrap())
+                    .map(|chip| registry.get_target(family, chip))
             })
             .for_each(|target| {
                 // Walk through the flash algorithms and cores and try to create each one.
diff --git a/probe-rs/src/config/target.rs b/probe-rs/src/config/target.rs
index 4c01b9e7..d3a5811b 100644
--- a/probe-rs/src/config/target.rs
+++ b/probe-rs/src/config/target.rs
@@ -1,15 +1,18 @@
-use super::{Core, MemoryRegion, RawFlashAlgorithm, RegistryError, TargetDescriptionSource};
-use crate::architecture::{
-    arm::{
-        ap::MemoryAp,
-        sequences::{ArmDebugSequence, DefaultArmSequence},
-        ApAddress, DpAddress,
+use super::{Core, MemoryRegion, RawFlashAlgorithm, TargetDescriptionSource};
+use crate::flashing::FlashLoader;
+use crate::{
+    architecture::{
+        arm::{
+            ap::MemoryAp,
+            sequences::{ArmDebugSequence, DefaultArmSequence},
+            ApAddress, DpAddress,
+        },
+        riscv::sequences::{DefaultRiscvSequence, RiscvDebugSequence},
+        xtensa::sequences::{DefaultXtensaSequence, XtensaDebugSequence},
     },
-    riscv::sequences::{DefaultRiscvSequence, RiscvDebugSequence},
-    xtensa::sequences::{DefaultXtensaSequence, XtensaDebugSequence},
+    rtt::ScanRegion,
 };
-use crate::flashing::FlashLoader;
-use probe_rs_target::{Architecture, BinaryFormat, Chip, ChipFamily, Jtag, MemoryRange};
+use probe_rs_target::{Architecture, BinaryFormat, Chip, ChipFamily, Jtag};
 use std::sync::Arc;
 
 /// This describes a complete target with a fixed chip model and variant.
@@ -31,7 +34,7 @@ pub struct Target {
     ///
     /// Each region must be enclosed in exactly one RAM region from
     /// `memory_map`.
-    pub rtt_scan_regions: Vec<std::ops::Range<u64>>,
+    pub rtt_scan_regions: ScanRegion,
     /// The Description of the scan chain
     ///
     /// The scan chain can be parsed from the CMSIS-SDF file, or specified
@@ -60,7 +63,7 @@ impl Target {
     /// Create a new target for the given details.
     ///
     /// The given chip must be a member of the given family.
-    pub(super) fn new(family: &ChipFamily, chip: &Chip) -> Result<Target, RegistryError> {
+    pub(super) fn new(family: &ChipFamily, chip: &Chip) -> Target {
         let mut flash_algorithms = Vec::new();
         for algo_name in chip.flash_algorithms.iter() {
             let algo = family.get_algorithm(algo_name).expect(
@@ -82,31 +85,12 @@ impl Target {
 
         tracing::info!("Using sequence {:?}", debug_sequence);
 
-        let ram_regions = chip
-            .memory_map
-            .iter()
-            .filter_map(MemoryRegion::as_ram_region);
         let rtt_scan_regions = match &chip.rtt_scan_ranges {
-            Some(ranges) => {
-                // The custom ranges must all be enclosed by exactly one of
-                // the defined RAM regions.
-                for rng in ranges {
-                    if !ram_regions
-                        .clone()
-                        .any(|region| region.range.contains_range(rng))
-                    {
-                        return Err(RegistryError::InvalidRttScanRange(rng.clone()));
-                    }
-                }
-                ranges.clone()
-            }
-            None => {
-                // By default we use all of the RAM ranges from the memory map.
-                ram_regions.map(|region| region.range.clone()).collect()
-            }
+            Some(ranges) => ScanRegion::Ranges(ranges.clone()),
+            None => ScanRegion::Ram, // By default we use all of the RAM ranges from the memory map.
         };
 
-        Ok(Target {
+        Target {
             name: chip.name.clone(),
             cores: chip.cores.clone(),
             flash_algorithms,
@@ -116,7 +100,7 @@ impl Target {
             rtt_scan_regions,
             jtag: chip.jtag.clone(),
             default_format: chip.default_binary_format.clone().unwrap_or_default(),
-        })
+        }
     }
 
     /// Get the architecture of the target
diff --git a/probe-rs/src/debug/mod.rs b/probe-rs/src/debug/mod.rs
index 3210db38..de90c953 100644
--- a/probe-rs/src/debug/mod.rs
+++ b/probe-rs/src/debug/mod.rs
@@ -33,7 +33,9 @@ pub use self::{
 };
 use crate::{core::Core, MemoryInterface};
 
+use gimli::AttributeValue;
 use gimli::DebuggingInformationEntry;
+use gimli::EvaluationResult;
 use typed_path::TypedPathBuf;
 
 use std::{
@@ -251,26 +253,22 @@ pub(crate) fn _print_all_attributes(
         }
         print!("{}: ", attr.name());
 
-        use gimli::AttributeValue::*;
-
         match attr.value() {
-            Addr(a) => println!("{a:#010x}"),
-            DebugStrRef(_) => {
+            AttributeValue::Addr(a) => println!("{a:#010x}"),
+            AttributeValue::DebugStrRef(_) => {
                 let val = dwarf.attr_string(unit, attr.value()).unwrap();
                 println!("{}", std::str::from_utf8(&val).unwrap());
             }
-            Exprloc(e) => {
+            AttributeValue::Exprloc(e) => {
                 let mut evaluation = e.evaluation(unit.encoding());
 
                 // go for evaluation
                 let mut result = evaluation.evaluate().unwrap();
 
                 loop {
-                    use gimli::EvaluationResult::*;
-
                     result = match result {
-                        Complete => break,
-                        RequiresMemory { address, size, .. } => {
+                        EvaluationResult::Complete => break,
+                        EvaluationResult::RequiresMemory { address, size, .. } => {
                             let mut buff = vec![0u8; size as usize];
                             core.read(address, &mut buff)
                                 .expect("Failed to read memory");
@@ -302,10 +300,10 @@ pub(crate) fn _print_all_attributes(
                                 }
                             }
                         }
-                        RequiresFrameBase => evaluation
+                        EvaluationResult::RequiresFrameBase => evaluation
                             .resume_with_frame_base(stackframe_cfa.unwrap())
                             .unwrap(),
-                        RequiresRegister {
+                        EvaluationResult::RequiresRegister {
                             register,
                             base_type,
                         } => {
@@ -322,7 +320,7 @@ pub(crate) fn _print_all_attributes(
                                 .resume_with_register(gimli::Value::Generic(raw_value))
                                 .unwrap()
                         }
-                        RequiresRelocatedAddress(address_index) => {
+                        EvaluationResult::RequiresRelocatedAddress(address_index) => {
                             // Use the address_index as an offset from 0, so just pass it into the next step.
                             evaluation
                                 .resume_with_relocated_address(address_index)
@@ -340,13 +338,13 @@ pub(crate) fn _print_all_attributes(
 
                 println!("Expression: {:x?}", &result[0]);
             }
-            LocationListsRef(_) => {
+            AttributeValue::LocationListsRef(_) => {
                 println!("LocationList");
             }
-            DebugLocListsBase(_) => {
+            AttributeValue::DebugLocListsBase(_) => {
                 println!(" LocationList");
             }
-            DebugLocListsIndex(_) => {
+            AttributeValue::DebugLocListsIndex(_) => {
                 println!(" LocationList");
             }
             _ => {
diff --git a/probe-rs/src/flashing/flasher.rs b/probe-rs/src/flashing/flasher.rs
index 47c7f2ba..4dbd6b9d 100644
--- a/probe-rs/src/flashing/flasher.rs
+++ b/probe-rs/src/flashing/flasher.rs
@@ -6,6 +6,7 @@ use crate::config::NvmRegion;
 use crate::flashing::encoder::FlashEncoder;
 use crate::flashing::FlashLayout;
 use crate::memory::MemoryInterface;
+use crate::rtt;
 use crate::{core::CoreRegisters, session::Session, Core, InstructionSet};
 use std::{
     fmt::Debug,
@@ -516,7 +517,7 @@ fn into_reg(val: u64) -> Result<u32, FlashError> {
 
 pub(super) struct ActiveFlasher<'probe, O: Operation> {
     core: Core<'probe>,
-    rtt: Option<crate::rtt::Rtt>,
+    rtt: Option<rtt::Rtt>,
     progress: FlashProgress,
     flash_algorithm: FlashAlgorithm,
     _operation: core::marker::PhantomData<O>,
@@ -687,30 +688,17 @@ impl<'probe, O: Operation> ActiveFlasher<'probe, O> {
         self.core.run()?;
 
         if let Some(rtt_address) = self.flash_algorithm.rtt_control_block {
-            // FIXME: replace this with try_attach_to_rtt once it's been moved to the library
-            let now = Instant::now();
-            let mut last_error = None;
-            while self.rtt.is_none() {
-                std::thread::sleep(Duration::from_millis(1));
-                let rtt = match crate::rtt::Rtt::attach_region(
-                    &mut self.core,
-                    &crate::rtt::ScanRegion::Exact(rtt_address),
-                ) {
-                    Ok(rtt) => Some(rtt),
-                    Err(error) => {
-                        last_error = Some(error);
-                        None
-                    }
-                };
-                self.rtt = rtt;
-
-                if now.elapsed() > Duration::from_secs(1) {
-                    break;
+            match rtt::try_attach_to_rtt(
+                &mut self.core,
+                Duration::from_secs(1),
+                &rtt::ScanRegion::Exact(rtt_address),
+            ) {
+                Ok(rtt) => self.rtt = Some(rtt),
+                Err(rtt::Error::NoControlBlockLocation) => {}
+                Err(error) => {
+                    tracing::error!("RTT could not be initialized: {error}");
                 }
             }
-            if let Some(error) = last_error {
-                tracing::error!("RTT could not be initialized: {error}");
-            }
         }
 
         Ok(())
diff --git a/probe-rs/src/probe/stlink/constants.rs b/probe-rs/src/probe/stlink/constants.rs
index d228d0a0..c8d62745 100644
--- a/probe-rs/src/probe/stlink/constants.rs
+++ b/probe-rs/src/probe/stlink/constants.rs
@@ -103,38 +103,37 @@ pub enum Status {
 
 impl From<u8> for Status {
     fn from(value: u8) -> Status {
-        use Status::*;
         match value {
-            0x80 => JtagOk,
-            0x01 => JtagUnknownError,
-            0x02 => JtagSpiError,
-            0x03 => JtagDmaError,
-            0x04 => JtagUnknownJtagChain,
-            0x05 => JtagNoDeviceConnected,
-            0x06 => JtagInternalError,
-            0x07 => JtagCmdWait,
-            0x08 => JtagCmdError,
-            0x09 => JtagGetIdcodeError,
-            0x0A => JtagAlignmentError,
-            0x0B => JtagDbgPowerError,
-            0x0C => JtagWriteError,
-            0x0D => JtagWriteVerifError,
-            0x0E => JtagAlreadyOpenedInOtherMode,
-            0x10 => SwdApWait,
-            0x11 => SwdApFault,
-            0x12 => SwdApError,
-            0x13 => SwdApParityError,
-            0x14 => SwdDpWait,
-            0x15 => SwdDpFault,
-            0x16 => SwdDpError,
-            0x17 => SwdDpParityError,
-            0x18 => SwdApWdataError,
-            0x19 => SwdApStickyError,
-            0x1A => SwdApStickyorunError,
-            0x20 => SwoNotAvailable,
-            0x41 => JtagFreqNotSupported,
-            0x42 => JtagUnknownCmd,
-            v => Other(v),
+            0x80 => Self::JtagOk,
+            0x01 => Self::JtagUnknownError,
+            0x02 => Self::JtagSpiError,
+            0x03 => Self::JtagDmaError,
+            0x04 => Self::JtagUnknownJtagChain,
+            0x05 => Self::JtagNoDeviceConnected,
+            0x06 => Self::JtagInternalError,
+            0x07 => Self::JtagCmdWait,
+            0x08 => Self::JtagCmdError,
+            0x09 => Self::JtagGetIdcodeError,
+            0x0A => Self::JtagAlignmentError,
+            0x0B => Self::JtagDbgPowerError,
+            0x0C => Self::JtagWriteError,
+            0x0D => Self::JtagWriteVerifError,
+            0x0E => Self::JtagAlreadyOpenedInOtherMode,
+            0x10 => Self::SwdApWait,
+            0x11 => Self::SwdApFault,
+            0x12 => Self::SwdApError,
+            0x13 => Self::SwdApParityError,
+            0x14 => Self::SwdDpWait,
+            0x15 => Self::SwdDpFault,
+            0x16 => Self::SwdDpError,
+            0x17 => Self::SwdDpParityError,
+            0x18 => Self::SwdApWdataError,
+            0x19 => Self::SwdApStickyError,
+            0x1A => Self::SwdApStickyorunError,
+            0x20 => Self::SwoNotAvailable,
+            0x41 => Self::JtagFreqNotSupported,
+            0x42 => Self::JtagUnknownCmd,
+            v => Self::Other(v),
         }
     }
 }
@@ -162,21 +161,19 @@ impl SwdFrequencyToDelayCount {
     /// If a direct match is not found, return the setting for a lower frequency
     /// if possible. If this is not possible, returns `None`.
     pub(crate) fn find_setting(frequency: u32) -> Option<SwdFrequencyToDelayCount> {
-        use SwdFrequencyToDelayCount::*;
-
         Some(match frequency {
-            _ if frequency >= 4_600 => Hz4600000,
-            _ if frequency >= 1_800 => Hz1800000,
-            _ if frequency >= 1_200 => Hz1200000,
-            _ if frequency >= 950 => Hz950000,
-            _ if frequency >= 650 => Hz650000,
-            _ if frequency >= 480 => Hz480000,
-            _ if frequency >= 400 => Hz400000,
-            _ if frequency >= 360 => Hz360000,
-            _ if frequency >= 240 => Hz240000,
-            _ if frequency >= 150 => Hz150000,
-            _ if frequency >= 125 => Hz125000,
-            _ if frequency >= 100 => Hz100000,
+            _ if frequency >= 4_600 => Self::Hz4600000,
+            _ if frequency >= 1_800 => Self::Hz1800000,
+            _ if frequency >= 1_200 => Self::Hz1200000,
+            _ if frequency >= 950 => Self::Hz950000,
+            _ if frequency >= 650 => Self::Hz650000,
+            _ if frequency >= 480 => Self::Hz480000,
+            _ if frequency >= 400 => Self::Hz400000,
+            _ if frequency >= 360 => Self::Hz360000,
+            _ if frequency >= 240 => Self::Hz240000,
+            _ if frequency >= 150 => Self::Hz150000,
+            _ if frequency >= 125 => Self::Hz125000,
+            _ if frequency >= 100 => Self::Hz100000,
             _ => {
                 return None;
             }
@@ -185,21 +182,19 @@ impl SwdFrequencyToDelayCount {
 
     /// Get the SWD frequency in kHz
     pub(crate) fn to_khz(self) -> u32 {
-        use SwdFrequencyToDelayCount::*;
-
         match self {
-            Hz4600000 => 4600,
-            Hz1800000 => 1800,
-            Hz1200000 => 1200,
-            Hz950000 => 950,
-            Hz650000 => 650,
-            Hz480000 => 480,
-            Hz400000 => 400,
-            Hz360000 => 360,
-            Hz240000 => 240,
-            Hz150000 => 150,
-            Hz125000 => 125,
-            Hz100000 => 100,
+            Self::Hz4600000 => 4600,
+            Self::Hz1800000 => 1800,
+            Self::Hz1200000 => 1200,
+            Self::Hz950000 => 950,
+            Self::Hz650000 => 650,
+            Self::Hz480000 => 480,
+            Self::Hz400000 => 400,
+            Self::Hz360000 => 360,
+            Self::Hz240000 => 240,
+            Self::Hz150000 => 150,
+            Self::Hz125000 => 125,
+            Self::Hz100000 => 100,
         }
     }
 }
@@ -223,36 +218,30 @@ impl JTagFrequencyToDivider {
     /// If a direct match is not found, return the setting for a higher frequency
     /// if possible. If this is not possible, returns `None`.
     pub(crate) fn find_setting(frequency: u32) -> Option<Self> {
-        use JTagFrequencyToDivider::*;
-
         Some(match frequency {
-            _ if frequency >= 18_000 => Hz18000000,
-            _ if frequency >= 9_000 => Hz9000000,
-            _ if frequency >= 4_500 => Hz4500000,
-            _ if frequency >= 2_225 => Hz2250000,
-            _ if frequency >= 1_120 => Hz1120000,
-            _ if frequency >= 560 => Hz560000,
-            _ if frequency >= 280 => Hz280000,
-            _ if frequency >= 140 => Hz140000,
-            _ => {
-                return None;
-            }
+            _ if frequency >= 18_000 => Self::Hz18000000,
+            _ if frequency >= 9_000 => Self::Hz9000000,
+            _ if frequency >= 4_500 => Self::Hz4500000,
+            _ if frequency >= 2_225 => Self::Hz2250000,
+            _ if frequency >= 1_120 => Self::Hz1120000,
+            _ if frequency >= 560 => Self::Hz560000,
+            _ if frequency >= 280 => Self::Hz280000,
+            _ if frequency >= 140 => Self::Hz140000,
+            _ => return None,
         })
     }
 
     /// Return the frequency in kHz
     pub(crate) fn to_khz(self) -> u32 {
-        use JTagFrequencyToDivider::*;
-
         match self {
-            Hz18000000 => 18_000,
-            Hz9000000 => 9_000,
-            Hz4500000 => 4_500,
-            Hz2250000 => 2_250,
-            Hz1120000 => 1_120,
-            Hz560000 => 560,
-            Hz280000 => 280,
-            Hz140000 => 140,
+            Self::Hz18000000 => 18_000,
+            Self::Hz9000000 => 9_000,
+            Self::Hz4500000 => 4_500,
+            Self::Hz2250000 => 2_250,
+            Self::Hz1120000 => 1_120,
+            Self::Hz560000 => 560,
+            Self::Hz280000 => 280,
+            Self::Hz140000 => 140,
         }
     }
 }
diff --git a/probe-rs/src/probe/stlink/mod.rs b/probe-rs/src/probe/stlink/mod.rs
index d948e3ef..d0d14443 100644
--- a/probe-rs/src/probe/stlink/mod.rs
+++ b/probe-rs/src/probe/stlink/mod.rs
@@ -422,13 +422,11 @@ impl<D: StLinkUsb> StLink<D> {
         self.device
             .write(&[commands::GET_CURRENT_MODE], &[], &mut buf, TIMEOUT)?;
 
-        use Mode::*;
-
         let mode = match buf[0] {
-            0 => Dfu,
-            1 => MassStorage,
-            2 => Jtag,
-            3 => Swim,
+            0 => Mode::Dfu,
+            1 => Mode::MassStorage,
+            2 => Mode::Jtag,
+            3 => Mode::Swim,
             _ => return Err(StlinkError::UnknownMode),
         };
 
diff --git a/probe-rs/src/rtt.rs b/probe-rs/src/rtt.rs
index 10febb40..92e749ae 100644
--- a/probe-rs/src/rtt.rs
+++ b/probe-rs/src/rtt.rs
@@ -47,9 +47,12 @@
 mod channel;
 pub use channel::*;
 
+use crate::Session;
 use crate::{config::MemoryRegion, Core, MemoryInterface};
-use std::borrow::Cow;
 use std::ops::Range;
+use std::thread;
+use std::time::Instant;
+use std::{borrow::Cow, time::Duration};
 use zerocopy::{FromBytes, FromZeroes};
 
 /// The RTT interface.
@@ -320,7 +323,6 @@ impl Rtt {
     ///
     /// `core` can be e.g. an owned `Core` or a shared `Rc<Core>`.
     pub fn attach_region(core: &mut Core, region: &ScanRegion) -> Result<Rtt, Error> {
-        let is_64_bit = core.is_64_bit();
         let ranges = match region.clone() {
             ScanRegion::Exact(addr) => {
                 tracing::debug!("Scanning at exact address: {:#010x}", addr);
@@ -335,17 +337,18 @@ impl Rtt {
                     .map(|r| r.range.clone())
                     .collect()
             }
+            ScanRegion::Ranges(regions) if regions.is_empty() => {
+                // We have no regions to scan so we cannot initialize RTT.
+                tracing::debug!("ELF file has no RTT block symbol, and this target does not support automatic scanning");
+                return Err(Error::NoControlBlockLocation);
+            }
             ScanRegion::Ranges(regions) => {
                 tracing::debug!("Scanning regions: {:#010x?}", region);
                 regions
             }
-
-            ScanRegion::Range(region) => {
-                tracing::debug!("Scanning region: {:#010x?}", region);
-                vec![region]
-            }
         };
 
+        let is_64_bit = core.is_64_bit();
         let minimal_header_size = RttControlBlockHeader::minimal_header_size(is_64_bit) as u64;
         let mut instances = ranges
             .into_iter()
@@ -419,14 +422,6 @@ pub enum ScanRegion {
     #[default]
     Ram,
 
-    /// Limit scanning to these memory addresses in target memory. It is up to the user to ensure
-    /// that reading from this range will not read from undefined memory.
-    ///
-    /// This variant is equivalent to using [`Self::Ranges`] with a single range as long as the
-    /// memory region fits into a 32-bit address space. This variant is for backward compatibility
-    /// for code written before the addition of [`Self::Ranges`].
-    Range(Range<u64>),
-
     /// Limit scanning to the memory addresses covered by all of the given ranges. It is up to the
     /// user to ensure that reading from this range will not read from undefined memory.
     Ranges(Vec<Range<u64>>),
@@ -437,9 +432,22 @@ pub enum ScanRegion {
     Exact(u64),
 }
 
+impl ScanRegion {
+    /// Creates a new `ScanRegion` that scans the given memory range.
+    ///
+    /// The memory range should be in a single memory block of the target.
+    pub fn range(range: Range<u64>) -> Self {
+        Self::Ranges(vec![range])
+    }
+}
+
 /// Error type for RTT operations.
 #[derive(thiserror::Error, Debug, docsplay::Display)]
 pub enum Error {
+    /// There is no control block location given. This usually means RTT is not present in the
+    /// firmware.
+    NoControlBlockLocation,
+
     /// RTT control block not found in target memory.
     /// - Make sure RTT is initialized on the target, AND that there are NO target breakpoints before RTT initialization.
     /// - For VSCode and probe-rs-debugger users, using `halt_after_reset:true` in your `launch.json` file will prevent RTT
@@ -470,6 +478,53 @@ fn display_list(list: &[Rtt]) -> String {
         .join(", ")
 }
 
+fn try_attach_to_rtt_inner(
+    mut try_attach_once: impl FnMut() -> Result<Rtt, Error>,
+    timeout: Duration,
+) -> Result<Rtt, Error> {
+    let t = Instant::now();
+    let mut attempt = 1;
+    loop {
+        tracing::debug!("Initializing RTT (attempt {attempt})...");
+
+        match try_attach_once() {
+            err @ Err(Error::NoControlBlockLocation) => return err,
+            Err(_) if t.elapsed() < timeout => {
+                attempt += 1;
+                tracing::debug!("Failed to initialize RTT. Retrying until timeout.");
+                thread::sleep(Duration::from_millis(50));
+            }
+            other => return other,
+        }
+    }
+}
+
+/// Try to attach to RTT, with the given timeout.
+pub fn try_attach_to_rtt(
+    core: &mut Core<'_>,
+    timeout: Duration,
+    rtt_region: &ScanRegion,
+) -> Result<Rtt, Error> {
+    try_attach_to_rtt_inner(|| Rtt::attach_region(core, rtt_region), timeout)
+}
+
+/// Try to attach to RTT, with the given timeout.
+pub fn try_attach_to_rtt_shared(
+    session: &parking_lot::FairMutex<Session>,
+    core_id: usize,
+    timeout: Duration,
+    rtt_region: &ScanRegion,
+) -> Result<Rtt, Error> {
+    try_attach_to_rtt_inner(
+        || {
+            let mut session_handle = session.lock();
+            let mut core = session_handle.core(core_id)?;
+            Rtt::attach_region(&mut core, rtt_region)
+        },
+        timeout,
+    )
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
diff --git a/rtthost/Cargo.toml b/rtthost/Cargo.toml
index 68354aa3..1c65ecd5 100644
--- a/rtthost/Cargo.toml
+++ b/rtthost/Cargo.toml
@@ -12,3 +12,6 @@ pretty_env_logger = "0.5"
 probe-rs.workspace = true
 anyhow.workspace = true
 clap = { version = "4", features = ["cargo", "derive"] }
+
+[lints]
+workspace = true
diff --git a/rtthost/src/main.rs b/rtthost/src/main.rs
index ae496a53..5624614a 100644
--- a/rtthost/src/main.rs
+++ b/rtthost/src/main.rs
@@ -50,7 +50,7 @@ fn parse_scan_region(
 
     match *parts.as_slice() {
         [addr] => Ok(ScanRegion::Exact(addr)),
-        [start, end] => Ok(ScanRegion::Range(start..end)),
+        [start, end] => Ok(ScanRegion::range(start..end)),
         _ => Err("Invalid range: multiple '..'s".into()),
     }
 }
diff --git a/target-gen/Cargo.toml b/target-gen/Cargo.toml
index 9822df54..c2fec286 100644
--- a/target-gen/Cargo.toml
+++ b/target-gen/Cargo.toml
@@ -56,3 +56,6 @@ assert_cmd = "2.0.14"
 predicates = "3.1.0"
 tempfile = "3.0"
 insta = { version = "1.38", default-features = false, features = ["yaml"] }
+
+[lints]
+workspace = true
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 593e8414..7a05b002 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -11,3 +11,6 @@ clap = { version = "4", features = ["derive"] }
 anyhow.workspace = true
 serde_json = "1"
 serde = { version = "1", features = ["derive"] }
+
+[lints]
+workspace = true


Test patch:
diff --git a/smoke-tester/Cargo.toml b/smoke-tester/Cargo.toml
index 14a854c4..b2f0f0c2 100644
--- a/smoke-tester/Cargo.toml
+++ b/smoke-tester/Cargo.toml
@@ -21,3 +21,6 @@ colored = "2.1.0"
 linkme = "0.3.25"
 miette = { version = "7.2.0", features = ["fancy"] }
 thiserror.workspace = true
+
+[lints]
+workspace = true
diff --git a/target-gen/src/commands/test.rs b/target-gen/src/commands/test.rs
index f46d616e..7c272128 100644
--- a/target-gen/src/commands/test.rs
+++ b/target-gen/src/commands/test.rs
@@ -159,7 +159,7 @@ pub fn cmd_test(
     run_flash_erase(
         &mut session,
         progress.clone(),
-        EraseSectors(test_start_sector_index, 2),
+        EraseType::EraseSectors(test_start_sector_index, 2),
     )?;
 
     println!("{test}: Erase done");
@@ -189,7 +189,7 @@ pub fn cmd_test(
     assert_eq!(readback, data);
 
     println!("{test}: Erasing the entire chip and writing two pages ...");
-    run_flash_erase(&mut session, progress.clone(), EraseAll)?;
+    run_flash_erase(&mut session, progress.clone(), EraseType::EraseAll)?;
     println!("{test}: Erase done");
     let mut readback = vec![0; (sector_size * 2) as usize];
     session
@@ -218,7 +218,7 @@ pub fn cmd_test(
     run_flash_erase(
         &mut session,
         progress.clone(),
-        EraseSectors(test_start_sector_index, 2),
+        EraseType::EraseSectors(test_start_sector_index, 2),
     )?;
     println!("{test}: Erase done");
 
@@ -281,7 +281,6 @@ pub enum EraseType {
     EraseAll,
     EraseSectors(usize, usize),
 }
-use EraseType::*;
 
 /// Erases the entire flash or just the sectors specified.
 pub fn run_flash_erase(
@@ -289,7 +288,7 @@ pub fn run_flash_erase(
     progress: FlashProgress,
     erase_type: EraseType,
 ) -> Result<()> {
-    if let EraseSectors(start_sector, sectors) = erase_type {
+    if let EraseType::EraseSectors(start_sector, sectors) = erase_type {
         erase_sectors(session, progress, start_sector, sectors)?;
     } else {
         erase_all(session, progress)?;