Repository: probe-rs/probe-rs
Pull request #2391 (2024-04-20 10:00:21)

Add second smoke tester and fix flash testing ESP devices


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index c7b74d0f..be382ad5 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1884,7 +1884,7 @@ dependencies = [
  "futures-util",
  "http 0.2.12",
  "hyper 0.14.28",
- "rustls 0.21.10",
+ "rustls 0.21.11",
  "tokio",
  "tokio-rustls 0.24.1",
 ]
@@ -3160,7 +3160,7 @@ dependencies = [
  "once_cell",
  "percent-encoding",
  "pin-project-lite",
- "rustls 0.21.10",
+ "rustls 0.21.11",
  "rustls-native-certs",
  "rustls-pemfile 1.0.4",
  "serde",
@@ -3350,9 +3350,9 @@ dependencies = [
 
 [[package]]
 name = "rustls"
-version = "0.21.10"
+version = "0.21.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
+checksum = "7fecbfb7b1444f477b345853b1fce097a2c6fb637b2bfb87e6bc5db0f043fae4"
 dependencies = [
  "log",
  "ring",
@@ -3812,6 +3812,7 @@ dependencies = [
  "miette",
  "pretty_env_logger",
  "probe-rs",
+ "probe-rs-target",
  "serde",
  "thiserror",
  "toml",
@@ -4240,7 +4241,7 @@ version = "0.24.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
 dependencies = [
- "rustls 0.21.10",
+ "rustls 0.21.11",
  "tokio",
 ]
 
diff --git a/probe-rs/src/config/sequences/atsam.rs b/probe-rs/src/config/sequences/atsam.rs
index 43f21186..120f5998 100644
--- a/probe-rs/src/config/sequences/atsam.rs
+++ b/probe-rs/src/config/sequences/atsam.rs
@@ -5,17 +5,23 @@ use crate::{
         self,
         arm::{
             ap::MemoryAp,
+            armv7m::Dhcsr,
+            communication_interface::{DapProbe, SwdSequence},
             memory::adi_v5_memory_interface::ArmProbe,
             sequences::{ArmDebugSequence, ArmDebugSequenceError, DebugEraseSequence},
-            ApAddress, ArmError, ArmProbeInterface, DpAddress,
+            ApAddress, ArmError, ArmProbeInterface, DpAddress, Pins,
         },
     },
     probe::DebugProbeError,
     session::MissingPermissions,
-    Permissions,
+    MemoryMappedRegister, Permissions,
 };
 use bitfield::bitfield;
-use std::sync::Arc;
+use probe_rs_target::CoreType;
+use std::{
+    sync::Arc,
+    time::{Duration, Instant},
+};
 
 use anyhow::Result;
 
@@ -187,17 +193,15 @@ impl DsuStatusB {
 }
 
 /// A wrapper for different types that can perform SWD Commands (SWJ_Pins SWJ_Sequence)
-struct SwdSequenceShim<'a>(&'a mut dyn architecture::arm::communication_interface::DapProbe);
+struct SwdSequenceShim<'a>(&'a mut dyn DapProbe);
 
-impl<'a> From<&'a mut dyn architecture::arm::communication_interface::DapProbe>
-    for SwdSequenceShim<'a>
-{
-    fn from(p: &'a mut dyn architecture::arm::communication_interface::DapProbe) -> Self {
+impl<'a> From<&'a mut dyn DapProbe> for SwdSequenceShim<'a> {
+    fn from(p: &'a mut dyn DapProbe) -> Self {
         Self(p)
     }
 }
 
-impl<'a> architecture::arm::communication_interface::SwdSequence for SwdSequenceShim<'a> {
+impl<'a> SwdSequence for SwdSequenceShim<'a> {
     fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
         self.0.swj_sequence(bit_len, bits)
     }
@@ -262,8 +266,8 @@ impl AtSAM {
         tracing::info!("Chip-Erase started...");
 
         // Wait for it to finish
-        let start = std::time::Instant::now();
-        while start.elapsed() < std::time::Duration::from_secs(8) {
+        let start = Instant::now();
+        while start.elapsed() < Duration::from_secs(8) {
             let current_dsu_statusa = DsuStatusA::from(memory.read_word_8(DsuStatusA::ADDRESS)?);
             if current_dsu_statusa.done() {
                 tracing::info!("Chip-Erase complete");
@@ -284,7 +288,7 @@ impl AtSAM {
             } else if current_dsu_statusa.fail() {
                 return Err(ArmError::ChipEraseFailed);
             }
-            std::thread::sleep(std::time::Duration::from_millis(250));
+            std::thread::sleep(Duration::from_millis(250));
         }
 
         tracing::error!("Chip-Erase failed to complete within 8 seconds");
@@ -302,24 +306,23 @@ impl AtSAM {
     /// Subject to probe communication errors
     pub fn reset_hardware_with_extension(
         &self,
-        interface: &mut dyn architecture::arm::communication_interface::SwdSequence,
+        interface: &mut dyn SwdSequence,
     ) -> Result<(), ArmError> {
-        let mut pins = architecture::arm::Pins(0);
+        let mut pins = Pins(0);
         pins.set_nreset(true);
         pins.set_swdio_tms(true);
         pins.set_swclk_tck(true);
 
-        let mut pin_values = architecture::arm::Pins(0);
-
         // First set nReset, SWDIO and SWCLK to low.
-        let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-        std::thread::sleep(std::time::Duration::from_millis(10));
+        let mut pin_values = Pins(0);
+        interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
+        std::thread::sleep(Duration::from_millis(10));
 
         // Next release nReset, but keep SWDIO and SWCLK low. This should put the device into
-        // reset extension ()
+        // reset extension.
         pin_values.set_nreset(true);
-        let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-        std::thread::sleep(std::time::Duration::from_millis(20));
+        interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
+        std::thread::sleep(Duration::from_millis(20));
 
         Ok(())
     }
@@ -331,13 +334,19 @@ impl AtSAM {
     /// # Errors
     /// Subject to probe communication errors
     pub fn release_reset_extension(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
+        // Enable debug mode if it is not already enabled
+        let mut dhcsr = Dhcsr(0);
+        dhcsr.enable_write();
+        dhcsr.set_c_debugen(true);
+        memory.write_word_32(Dhcsr::ADDRESS_OFFSET, dhcsr.0)?;
+
         // clear the reset extension bit
         let mut dsu_statusa = DsuStatusA(0);
         dsu_statusa.set_crstext(true);
         memory.write_8(DsuStatusA::ADDRESS, &[dsu_statusa.0])?;
 
-        let start = std::time::Instant::now();
-        while start.elapsed() < std::time::Duration::from_millis(100) {
+        let start = Instant::now();
+        while start.elapsed() < Duration::from_millis(100) {
             let current_dsu_statusa = DsuStatusA::from(memory.read_word_8(DsuStatusA::ADDRESS)?);
             if !current_dsu_statusa.crstext() {
                 return Ok(());
@@ -351,25 +360,21 @@ impl AtSAM {
     ///
     /// # Errors
     /// Subject to probe communication errors
-    pub fn reset_hardware(
-        &self,
-        interface: &mut dyn architecture::arm::communication_interface::SwdSequence,
-    ) -> Result<(), ArmError> {
-        let mut pins = architecture::arm::Pins(0);
+    pub fn reset_hardware(&self, interface: &mut dyn SwdSequence) -> Result<(), ArmError> {
+        let mut pins = Pins(0);
         pins.set_nreset(true);
         pins.set_swdio_tms(true);
         pins.set_swclk_tck(true);
 
         let mut pin_values = pins;
-        pin_values.set_nreset(false);
 
-        let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-        std::thread::sleep(std::time::Duration::from_millis(10));
+        pin_values.set_nreset(false);
+        interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
+        std::thread::sleep(Duration::from_millis(10));
 
         pin_values.set_nreset(true);
-
-        let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-        std::thread::sleep(std::time::Duration::from_millis(10));
+        interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
+        std::thread::sleep(Duration::from_millis(10));
 
         Ok(())
     }
@@ -379,7 +384,7 @@ impl AtSAM {
     fn ensure_target_reset(
         &self,
         result: Result<(), ArmError>,
-        interface: &mut dyn architecture::arm::communication_interface::SwdSequence,
+        interface: &mut dyn SwdSequence,
     ) -> Result<(), ArmError> {
         // Fall back if the probe's swj_pins does not support the full set of pins
         match result {
@@ -388,19 +393,14 @@ impl AtSAM {
             })) => {
                 tracing::warn!("Using fallback reset method");
 
-                let mut pins = architecture::arm::Pins(0);
+                let mut pins = Pins(0);
                 pins.set_nreset(true);
 
-                let mut pin_values = pins;
-                pin_values.set_nreset(false);
-
-                let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-                std::thread::sleep(std::time::Duration::from_millis(10));
+                interface.swj_pins(0, pins.0 as u32, 0)?;
+                std::thread::sleep(Duration::from_millis(10));
 
-                pin_values.set_nreset(true);
-
-                let _ = interface.swj_pins(pin_values.0 as u32, pins.0 as u32, 0)?;
-                std::thread::sleep(std::time::Duration::from_millis(10));
+                interface.swj_pins(pins.0 as u32, pins.0 as u32, 0)?;
+                std::thread::sleep(Duration::from_millis(10));
 
                 Ok(())
             }
@@ -410,14 +410,24 @@ impl AtSAM {
 }
 
 impl ArmDebugSequence for AtSAM {
+    fn debug_core_start(
+        &self,
+        interface: &mut dyn ArmProbeInterface,
+        core_ap: MemoryAp,
+        _core_type: CoreType,
+        _debug_base: Option<u64>,
+        _cti_base: Option<u64>,
+    ) -> Result<(), ArmError> {
+        let mut core = interface.memory_interface(core_ap)?;
+
+        self.release_reset_extension(&mut *core)
+    }
+
     /// `reset_hardware_assert` for ATSAM devices
     ///
     /// Instead of keeping `nReset` asserted, the device is instead put into CPU Reset Extension
     /// which will keep the CPU Core in reset until manually released by the debugger probe.
-    fn reset_hardware_assert(
-        &self,
-        interface: &mut dyn architecture::arm::communication_interface::DapProbe,
-    ) -> Result<(), ArmError> {
+    fn reset_hardware_assert(&self, interface: &mut dyn DapProbe) -> Result<(), ArmError> {
         let mut shim = SwdSequenceShim::from(interface);
         let result = self.reset_hardware_with_extension(&mut shim);
 
@@ -429,11 +439,10 @@ impl ArmDebugSequence for AtSAM {
     /// Instead of de-asserting `nReset` here (this was already done during the CPU Reset Extension process),
     /// the device is released from Reset Extension.
     fn reset_hardware_deassert(&self, memory: &mut dyn ArmProbe) -> Result<(), ArmError> {
-        let mut pins = architecture::arm::Pins(0);
+        let mut pins = Pins(0);
         pins.set_nreset(true);
 
-        let current_pins =
-            architecture::arm::Pins(memory.swj_pins(pins.0 as u32, pins.0 as u32, 0)? as u8);
+        let current_pins = Pins(memory.swj_pins(pins.0 as u32, pins.0 as u32, 0)? as u8);
         if !current_pins.nreset() {
             return Err(ArmDebugSequenceError::SequenceSpecific(
                 "Expected nReset to already be de-asserted".into(),


Test patch:
diff --git a/.github/workflows/smoketest.yml b/.github/workflows/smoketest.yml
index 6190acff..271ace3c 100644
--- a/.github/workflows/smoketest.yml
+++ b/.github/workflows/smoketest.yml
@@ -40,7 +40,10 @@ jobs:
           path: target/aarch64-unknown-linux-gnu/release/smoke_tester
 
   test:
-    runs-on: ["self-hosted", "linux", "ARM64", "smoke-test"]
+    runs-on: ["self-hosted", "linux", "ARM64", "${{ matrix.runner }}"]
+    strategy:
+      matrix:
+        runner: ["smoke-test", "smoke-tester-2"]
 
     needs: build
 
diff --git a/smoke-tester/Cargo.toml b/smoke-tester/Cargo.toml
index 25a81d35..14a854c4 100644
--- a/smoke-tester/Cargo.toml
+++ b/smoke-tester/Cargo.toml
@@ -12,6 +12,7 @@ publish = false
 
 serde = { version = "1", features = ["derive"] }
 probe-rs = { workspace = true }
+probe-rs-target = { workspace = true }
 toml = "0.8.12"
 pretty_env_logger = "0.5.0"
 log = "0.4.21"
diff --git a/smoke-tester/src/tests.rs b/smoke-tester/src/tests.rs
index 549a0abb..668d18f4 100644
--- a/smoke-tester/src/tests.rs
+++ b/smoke-tester/src/tests.rs
@@ -230,7 +230,11 @@ pub fn test_flashing(tracker: &TestTracker, session: &mut Session) -> Result<(),
 
     let start_time = Instant::now();
 
-    download_file_with_options(session, test_binary, Format::Elf, options)
+    let format = match session.target().default_format {
+        probe_rs_target::BinaryFormat::Idf => Format::Idf(Default::default()),
+        probe_rs_target::BinaryFormat::Raw => Default::default(),
+    };
+    download_file_with_options(session, test_binary, format, options)
         .map_err(|err| TestFailure::Error(Box::new(err)))?;
 
     println!();