probe_rs/architecture/arm/
communication_interface.rs

1use crate::{
2    CoreStatus,
3    architecture::arm::{
4        ApAddress, ArmError, DapAccess, FullyQualifiedApAddress, RawDapAccess, RegisterAddress,
5        SwoAccess, SwoConfig, ap,
6        dp::{
7            Ctrl, DPIDR, DebugPortId, DebugPortVersion, DpAccess, DpAddress, DpRegisterAddress,
8            Select1, SelectV1, SelectV3,
9        },
10        memory::{ADIMemoryInterface, ArmMemoryInterface, Component},
11        sequences::ArmDebugSequence,
12    },
13    probe::{DebugProbe, DebugProbeError, Probe, WireProtocol},
14};
15use jep106::JEP106Code;
16
17use std::{
18    collections::{BTreeSet, HashMap, hash_map},
19    fmt::Debug,
20    sync::Arc,
21    time::Duration,
22};
23
24/// An error in the communication with an access port or
25/// debug port.
26#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq, Copy)]
27pub enum DapError {
28    /// A protocol error occurred during communication.
29    #[error("A protocol error occurred in the {0} communication between probe and device.")]
30    Protocol(WireProtocol),
31    /// The target device did not respond to the request.
32    #[error("Target device did not respond to request.")]
33    NoAcknowledge,
34    /// The target device responded with a FAULT response to the request.
35    #[error("Target device responded with a FAULT response to the request.")]
36    FaultResponse,
37    /// Target device responded with a WAIT response to the request.
38    #[error("Target device responded with a WAIT response to the request.")]
39    WaitResponse,
40    /// The parity bit on the read request was incorrect.
41    #[error("Incorrect parity on READ request.")]
42    IncorrectParity,
43}
44
45/// To be implemented by debug probe drivers that support the ARM debug interface.
46pub trait ArmDebugInterface: DapAccess + SwdSequence + SwoAccess + Send {
47    /// Reinitialize the communication interface (in place).
48    ///
49    /// Some chip-specific reset sequences may disable the debug port. `reinitialize` allows
50    /// a debug sequence to re-initialize the debug port.
51    ///
52    /// If you're invoking this from a debug sequence, know that `reinitialize` will likely
53    /// call back onto you! Specifically, it will invoke some sequence of `debug_port_*`
54    /// sequences with varying internal state. If you're not prepared for this, you might recurse.
55    ///
56    /// `reinitialize` does handle `debug_core_start` to re-initialize any core's debugging.
57    /// If you're a chip-specific debug sequence, you're expected to handle this yourself.
58    fn reinitialize(&mut self) -> Result<(), ArmError>;
59
60    /// Returns a vector of all the access ports the current debug port has.
61    ///
62    /// If the target device has multiple debug ports, this will switch the active debug port
63    /// if necessary.
64    fn access_ports(
65        &mut self,
66        dp: DpAddress,
67    ) -> Result<BTreeSet<FullyQualifiedApAddress>, ArmError>;
68
69    /// Closes the interface and returns back the generic probe it consumed.
70    fn close(self: Box<Self>) -> Probe;
71
72    /// Explicitly select a debug port.
73    ///
74    /// It is required that implementations connect to a debug port automatically,
75    /// but this method can be used to select a DP manually, to have control
76    /// over the point in time where the DP is selected.
77    fn select_debug_port(&mut self, dp: DpAddress) -> Result<(), ArmError>;
78
79    /// Return the currently connected debug port.
80    ///
81    /// None if the interface is not connected to a DP.
82    fn current_debug_port(&self) -> Option<DpAddress>;
83
84    /// Returns a memory interface to access the target's memory.
85    fn memory_interface(
86        &mut self,
87        access_port: &FullyQualifiedApAddress,
88    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError>;
89}
90
91/// Read chip information from the ROM tables
92pub fn read_chip_info_from_rom_table(
93    probe: &mut dyn ArmDebugInterface,
94    dp: DpAddress,
95) -> Result<Option<ArmChipInfo>, ArmError> {
96    for ap in probe.access_ports(dp)? {
97        if let Ok(mut memory) = probe.memory_interface(&ap) {
98            let base_address = memory.base_address()?;
99            let component = Component::try_parse(&mut *memory, base_address)?;
100
101            if let Component::Class1RomTable(component_id, _) = component
102                && let Some(jep106) = component_id.peripheral_id().jep106()
103            {
104                return Ok(Some(ArmChipInfo {
105                    manufacturer: jep106,
106                    part: component_id.peripheral_id().part(),
107                }));
108            }
109        }
110    }
111
112    Ok(None)
113}
114
115// TODO: Rename trait!
116pub trait SwdSequence {
117    /// Corresponds to the DAP_SWJ_Sequence function from the ARM Debug sequences
118    fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError>;
119
120    /// Corresponds to the DAP_SWJ_Pins function from the ARM Debug sequences
121    fn swj_pins(
122        &mut self,
123        pin_out: u32,
124        pin_select: u32,
125        pin_wait: u32,
126    ) -> Result<u32, DebugProbeError>;
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub(crate) enum SelectCache {
131    DPv1(SelectV1),
132    DPv3(SelectV3, Select1),
133}
134impl SelectCache {
135    pub fn dp_bank_sel(&self) -> u8 {
136        match self {
137            SelectCache::DPv1(s) => s.dp_bank_sel(),
138            SelectCache::DPv3(s, _) => s.dp_bank_sel(),
139        }
140    }
141    pub fn set_dp_bank_sel(&mut self, bank: u8) {
142        match self {
143            SelectCache::DPv1(s) => s.set_dp_bank_sel(bank),
144            SelectCache::DPv3(s, _) => s.set_dp_bank_sel(bank),
145        }
146    }
147}
148#[derive(Debug)]
149pub(crate) struct DpState {
150    pub debug_port_version: DebugPortVersion,
151
152    pub(crate) current_select: SelectCache,
153}
154
155impl DpState {
156    pub fn new() -> Self {
157        Self {
158            debug_port_version: DebugPortVersion::Unsupported(0xFF),
159            current_select: SelectCache::DPv1(SelectV1(0)),
160        }
161    }
162}
163
164/// An implementation of the communication protocol between probe and target.
165/// Can be used to perform all sorts of generic debug access on ARM targets with probes that support low level access.
166/// (E.g. CMSIS-DAP and J-Link support this, ST-Link does not)
167#[derive(Debug)]
168pub struct ArmCommunicationInterface {
169    probe: Option<Box<dyn DapProbe>>,
170
171    /// Currently selected debug port. For targets without multidrop,
172    /// this will always be the single, default debug port in the system.
173    ///
174    /// If this is None, the interface is in an uninitialized state.
175    current_dp: Option<DpAddress>,
176    dps: HashMap<DpAddress, DpState>,
177    use_overrun_detect: bool,
178    sequence: Arc<dyn ArmDebugSequence>,
179}
180
181impl Drop for ArmCommunicationInterface {
182    fn drop(&mut self) {
183        if self.probe.is_some() {
184            self.disconnect();
185
186            // Ensure we don't disconnect twice
187            self.probe = None;
188        }
189    }
190}
191
192impl ArmCommunicationInterface {
193    pub(crate) fn probe_mut(&mut self) -> &mut dyn DapProbe {
194        // Unwrap: Probe is only taken when the struct is dropped
195        self.probe.as_deref_mut().expect("ArmCommunicationInterface is in an inconsistent state. This is a bug, please report it.")
196    }
197
198    fn close(mut self) -> Probe {
199        self.disconnect();
200
201        let probe = self.probe.take().unwrap();
202
203        Probe::from_attached_probe(RawDapAccess::into_probe(probe))
204    }
205
206    /// Disconnect from all debug ports, by calling `debug_port_stop` on all DPs which we
207    /// are connected to.
208    fn disconnect(&mut self) {
209        let probe = self.probe.as_deref_mut().unwrap();
210
211        if let Some(current_dp) = self.current_dp.take() {
212            let _stop_span = tracing::debug_span!("debug_port_stop").entered();
213
214            // Stop the current DP, which may not be one of the known ones (i.e. RP2040 rescue DP).
215            self.sequence.debug_port_stop(probe, current_dp).ok();
216
217            drop(_stop_span);
218
219            // Stop all intentionally-connected DPs.
220            for dp in self.dps.keys().filter(|dp| **dp != current_dp) {
221                // Try to select the debug port we want to shut down.
222                if self.sequence.debug_port_connect(probe, *dp).is_ok() {
223                    self.sequence.debug_port_stop(probe, *dp).ok();
224                } else {
225                    tracing::warn!("Failed to stop DP {:x?}", dp);
226                }
227            }
228        };
229
230        probe.raw_flush().ok();
231    }
232}
233
234/// Helper trait for probes which offer access to ARM DAP (Debug Access Port).
235///
236/// This is used to combine the traits, because it cannot be done in the ArmCommunicationInterface
237/// struct itself.
238pub trait DapProbe: RawDapAccess + DebugProbe {}
239
240impl ArmDebugInterface for ArmCommunicationInterface {
241    fn reinitialize(&mut self) -> Result<(), ArmError> {
242        let current_dp = self.current_dp;
243
244        // Simulate the drop / close of the initialized communication interface.
245        self.disconnect();
246
247        // This should be set to None by the disconnect call above.
248        assert!(self.current_dp.is_none());
249
250        // Reconnect to the DP again
251        if let Some(dp) = current_dp {
252            self.select_dp(dp)?;
253        }
254
255        Ok(())
256    }
257
258    fn memory_interface(
259        &mut self,
260        access_port_address: &FullyQualifiedApAddress,
261    ) -> Result<Box<dyn ArmMemoryInterface + '_>, ArmError> {
262        let memory_interface = match access_port_address.ap() {
263            ApAddress::V1(_) => Box::new(ADIMemoryInterface::new(self, access_port_address)?)
264                as Box<dyn ArmMemoryInterface + '_>,
265            ApAddress::V2(_) => ap::v2::new_memory_interface(self, access_port_address)?,
266        };
267        Ok(memory_interface)
268    }
269
270    fn current_debug_port(&self) -> Option<DpAddress> {
271        self.current_dp
272    }
273
274    fn close(self: Box<Self>) -> Probe {
275        ArmCommunicationInterface::close(*self)
276    }
277
278    fn access_ports(
279        &mut self,
280        dp: DpAddress,
281    ) -> Result<BTreeSet<FullyQualifiedApAddress>, ArmError> {
282        match self.select_dp(dp).map(|state| state.debug_port_version)? {
283            DebugPortVersion::DPv0 | DebugPortVersion::DPv1 | DebugPortVersion::DPv2 => {
284                Ok(ap::v1::valid_access_ports(self, dp).into_iter().collect())
285            }
286            DebugPortVersion::DPv3 => ap::v2::enumerate_access_ports(self, dp),
287            DebugPortVersion::Unsupported(_) => unreachable!(),
288        }
289    }
290
291    fn select_debug_port(&mut self, dp: DpAddress) -> Result<(), ArmError> {
292        let _ = self.select_dp(dp)?;
293        Ok(())
294    }
295}
296
297impl SwdSequence for ArmCommunicationInterface {
298    fn swj_sequence(&mut self, bit_len: u8, bits: u64) -> Result<(), DebugProbeError> {
299        self.probe_mut().swj_sequence(bit_len, bits)?;
300
301        Ok(())
302    }
303
304    fn swj_pins(
305        &mut self,
306        pin_out: u32,
307        pin_select: u32,
308        pin_wait: u32,
309    ) -> Result<u32, DebugProbeError> {
310        self.probe_mut().swj_pins(pin_out, pin_select, pin_wait)
311    }
312}
313
314impl ArmCommunicationInterface {
315    /// Create a new instance of the communication interface,
316    /// which is not yet connected to a debug port.
317    pub fn create(
318        probe: Box<dyn DapProbe>,
319        sequence: Arc<dyn ArmDebugSequence>,
320        use_overrun_detect: bool,
321    ) -> Box<dyn ArmDebugInterface> {
322        let interface = ArmCommunicationInterface {
323            probe: Some(probe),
324            current_dp: None,
325            dps: Default::default(),
326            use_overrun_detect,
327            sequence,
328        };
329
330        Box::new(interface)
331    }
332
333    /// Inform the probe of the [`CoreStatus`] of the chip attached to the probe.
334    pub fn core_status_notification(&mut self, state: CoreStatus) {
335        self.probe_mut().core_status_notification(state).ok();
336    }
337
338    fn select_dp(&mut self, dp: DpAddress) -> Result<&mut DpState, ArmError> {
339        let mut switched_dp = false;
340
341        let sequence = self.sequence.clone();
342
343        if self.current_dp != Some(dp) {
344            tracing::debug!("Selecting DP {:x?}", dp);
345
346            switched_dp = true;
347
348            self.probe_mut().raw_flush()?;
349
350            // We are not currently connected to any DP,
351            // so we need to run the debug_port_setup sequence.
352            if self.current_dp.is_none() {
353                sequence.debug_port_setup(&mut *self.probe_mut(), dp)?;
354            } else {
355                // Try to switch to the new DP.
356                if let Err(e) = sequence.debug_port_connect(&mut *self.probe_mut(), dp) {
357                    tracing::warn!("Failed to switch to DP {:x?}: {}", dp, e);
358
359                    // Try the more involved debug_port_setup sequence, which also handles dormant mode.
360                    sequence.debug_port_setup(&mut *self.probe_mut(), dp)?;
361                }
362            }
363
364            self.current_dp = Some(dp);
365        }
366
367        // If we don't have  a state for this DP, this means that we haven't run the necessary init sequence yet.
368        if let hash_map::Entry::Vacant(entry) = self.dps.entry(dp) {
369            let sequence = self.sequence.clone();
370
371            entry.insert(DpState::new());
372
373            let start_span = tracing::debug_span!("debug_port_start").entered();
374            sequence.debug_port_start(self, dp)?;
375            drop(start_span);
376
377            // Make sure we enable the overrun detect mode when requested.
378            // For "bit-banging" probes, such as JLink or FTDI, we rely on it for good, stable communication.
379            // This is required as the default sequence (and most special implementations) does not do this.
380            let mut ctrl_reg: Ctrl = self.read_dp_register(dp)?;
381            if ctrl_reg.orun_detect() != self.use_overrun_detect {
382                tracing::debug!("Setting orun_detect: {}", self.use_overrun_detect);
383                // only write if there’s a need for it.
384                ctrl_reg.set_orun_detect(self.use_overrun_detect);
385                self.write_dp_register(dp, ctrl_reg)?;
386            }
387
388            let idr: DebugPortId = self.read_dp_register::<DPIDR>(dp)?.into();
389            tracing::info!(
390                "Debug Port version: {} MinDP: {:?}",
391                idr.version,
392                idr.min_dp_support
393            );
394
395            let state = self
396                .dps
397                .get_mut(&dp)
398                .expect("This DP State was inserted earlier in this function");
399            state.debug_port_version = idr.version;
400            if idr.version == DebugPortVersion::DPv3 {
401                state.current_select = SelectCache::DPv3(SelectV3(0), Select1(0));
402            }
403        } else if switched_dp {
404            let sequence = self.sequence.clone();
405
406            let start_span = tracing::debug_span!("debug_port_start").entered();
407            sequence.debug_port_start(self, dp)?;
408            drop(start_span);
409        }
410
411        // note(unwrap): Entry gets inserted above
412        Ok(self.dps.get_mut(&dp).unwrap())
413    }
414
415    fn select_dp_and_dp_bank(
416        &mut self,
417        dp: DpAddress,
418        dp_register_address: &DpRegisterAddress,
419    ) -> Result<(), ArmError> {
420        let dp_state = self.select_dp(dp)?;
421
422        // DP register addresses are 4 bank bits, 4 address bits. Lowest 2 address bits are
423        // always 0, so this leaves only 4 possible addresses: 0x0, 0x4, 0x8, 0xC.
424        // On ADIv5, only address 0x4 is banked, the rest are don't care.
425        // On ADIv6, address 0x0 and 0x4 are banked, the rest are don't care.
426
427        let &DpRegisterAddress {
428            bank,
429            address: addr,
430        } = dp_register_address;
431
432        if addr != 0 && addr != 4 {
433            return Ok(());
434        }
435
436        let bank = bank.unwrap_or(0);
437
438        if bank != dp_state.current_select.dp_bank_sel() {
439            dp_state.current_select.set_dp_bank_sel(bank);
440
441            tracing::debug!("Changing DP_BANK_SEL to {:x?}", dp_state.current_select);
442
443            match dp_state.current_select {
444                SelectCache::DPv1(select) => self.write_dp_register(dp, select)?,
445                SelectCache::DPv3(select, _) => self.write_dp_register(dp, select)?,
446            }
447        }
448
449        Ok(())
450    }
451
452    fn select_ap_and_ap_bank(
453        &mut self,
454        ap: &FullyQualifiedApAddress,
455        ap_register_address: u64,
456    ) -> Result<(), ArmError> {
457        let dp_state = self.select_dp(ap.dp())?;
458
459        let previous_select = dp_state.current_select;
460        match (ap.ap(), &mut dp_state.current_select) {
461            (ApAddress::V1(port), SelectCache::DPv1(s)) => {
462                let ap_register_address = (ap_register_address & 0xFF) as u8;
463                let ap_bank = ap_register_address >> 4;
464                s.set_ap_sel(*port);
465                s.set_ap_bank_sel(ap_bank);
466            }
467            (ApAddress::V2(base), SelectCache::DPv3(s, s1)) => {
468                let address = base.0.unwrap_or(0) + ap_register_address;
469                s.set_addr(((address >> 4) & 0xFFFF_FFFF) as u32);
470                s1.set_addr((address >> 32) as u32);
471            }
472            _ => unreachable!(
473                "Did not expect to be called with {ap:x?}. This is a bug, please report it."
474            ),
475        }
476
477        if previous_select != dp_state.current_select {
478            tracing::debug!("Changing SELECT to {:x?}", dp_state.current_select);
479
480            match dp_state.current_select {
481                SelectCache::DPv1(select) => {
482                    self.write_dp_register(ap.dp(), select)?;
483                }
484                SelectCache::DPv3(select, select1) => {
485                    self.write_dp_register(ap.dp(), select)?;
486                    self.write_dp_register(ap.dp(), select1)?;
487                }
488            }
489        }
490
491        Ok(())
492    }
493}
494
495impl SwoAccess for ArmCommunicationInterface {
496    fn enable_swo(&mut self, config: &SwoConfig) -> Result<(), ArmError> {
497        match self.probe_mut().get_swo_interface_mut() {
498            Some(interface) => interface.enable_swo(config),
499            None => Err(ArmError::ArchitectureRequired(&["ARMv7", "ARMv8"])),
500        }
501    }
502
503    fn disable_swo(&mut self) -> Result<(), ArmError> {
504        match self.probe_mut().get_swo_interface_mut() {
505            Some(interface) => interface.disable_swo(),
506            None => Err(ArmError::ArchitectureRequired(&["ARMv7", "ARMv8"])),
507        }
508    }
509
510    fn read_swo_timeout(&mut self, timeout: Duration) -> Result<Vec<u8>, ArmError> {
511        match self.probe_mut().get_swo_interface_mut() {
512            Some(interface) => interface.read_swo_timeout(timeout),
513            None => Err(ArmError::ArchitectureRequired(&["ARMv7", "ARMv8"])),
514        }
515    }
516}
517
518impl DapAccess for ArmCommunicationInterface {
519    fn read_raw_dp_register(
520        &mut self,
521        dp: DpAddress,
522        address: DpRegisterAddress,
523    ) -> Result<u32, ArmError> {
524        self.select_dp_and_dp_bank(dp, &address)?;
525        let result = self.probe_mut().raw_read_register(address.into())?;
526        Ok(result)
527    }
528
529    fn write_raw_dp_register(
530        &mut self,
531        dp: DpAddress,
532        address: DpRegisterAddress,
533        value: u32,
534    ) -> Result<(), ArmError> {
535        self.select_dp_and_dp_bank(dp, &address)?;
536        self.probe_mut().raw_write_register(address.into(), value)?;
537        Ok(())
538    }
539
540    fn read_raw_ap_register(
541        &mut self,
542        ap: &FullyQualifiedApAddress,
543        address: u64,
544    ) -> Result<u32, ArmError> {
545        self.select_ap_and_ap_bank(ap, address)?;
546
547        let result = self
548            .probe_mut()
549            .raw_read_register(RegisterAddress::ApRegister((address & 0xFF) as u8))?;
550
551        Ok(result)
552    }
553
554    fn read_raw_ap_register_repeated(
555        &mut self,
556        ap: &FullyQualifiedApAddress,
557        address: u64,
558        values: &mut [u32],
559    ) -> Result<(), ArmError> {
560        self.select_ap_and_ap_bank(ap, address)?;
561
562        self.probe_mut()
563            .raw_read_block(RegisterAddress::ApRegister((address & 0xFF) as u8), values)?;
564        Ok(())
565    }
566
567    fn write_raw_ap_register(
568        &mut self,
569        ap: &FullyQualifiedApAddress,
570        address: u64,
571        value: u32,
572    ) -> Result<(), ArmError> {
573        self.select_ap_and_ap_bank(ap, address)?;
574
575        self.probe_mut()
576            .raw_write_register(RegisterAddress::ApRegister((address & 0xFF) as u8), value)?;
577
578        Ok(())
579    }
580
581    fn write_raw_ap_register_repeated(
582        &mut self,
583        ap: &FullyQualifiedApAddress,
584        address: u64,
585        values: &[u32],
586    ) -> Result<(), ArmError> {
587        self.select_ap_and_ap_bank(ap, address)?;
588
589        self.probe_mut()
590            .raw_write_block(RegisterAddress::ApRegister((address & 0xFF) as u8), values)?;
591        Ok(())
592    }
593
594    fn flush(&mut self) -> Result<(), ArmError> {
595        self.probe_mut().raw_flush()
596    }
597
598    fn try_dap_probe(&self) -> Option<&dyn DapProbe> {
599        self.probe.as_deref()
600    }
601
602    fn try_dap_probe_mut(&mut self) -> Option<&mut dyn DapProbe> {
603        self.probe
604            .as_deref_mut()
605            // Need to explicitly coerce lifetimes: https://github.com/rust-lang/rust/issues/108999
606            .map(|p: &mut (dyn DapProbe + 'static)| p as &mut (dyn DapProbe + '_))
607    }
608}
609
610/// Information about the chip target we are currently attached to.
611/// This can be used for discovery, tho, for now it does not work optimally,
612/// as some manufacturers (e.g. ST Microelectronics) violate the spec and thus need special discovery procedures.
613#[derive(Debug, Clone, Copy)]
614pub struct ArmChipInfo {
615    /// The JEP106 code of the manufacturer of this chip target.
616    pub manufacturer: JEP106Code,
617    /// The unique part number of the chip target. Unfortunately this only unique in the spec.
618    /// In practice some manufacturers violate the spec and assign a part number to an entire family.
619    ///
620    /// Consider this not unique when working with targets!
621    pub part: u16,
622}
623
624impl std::fmt::Display for ArmChipInfo {
625    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
626        let manu = match self.manufacturer.get() {
627            Some(name) => name.to_string(),
628            None => format!(
629                "<unknown manufacturer (cc={:2x}, id={:2x})>",
630                self.manufacturer.cc, self.manufacturer.id
631            ),
632        };
633        write!(f, "{} 0x{:04x}", manu, self.part)
634    }
635}