probe_rs/vendor/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//! Vendor support modules.

use std::{ops::Deref, sync::LazyLock};

use parking_lot::{RwLock, RwLockReadGuard};
use probe_rs_target::Chip;

use crate::{
    architecture::{
        arm::{sequences::DefaultArmSequence, ArmChipInfo, ArmProbeInterface, DpAddress},
        riscv::communication_interface::RiscvCommunicationInterface,
        xtensa::communication_interface::{
            XtensaCommunicationInterface, XtensaDebugInterfaceState,
        },
    },
    config::{registry, ChipInfo, DebugSequence},
    probe::{DebugProbeError, Probe},
    Error, Target,
};

pub mod espressif;
pub mod infineon;
pub mod microchip;
pub mod nordicsemi;
pub mod nxp;
pub mod silabs;
pub mod st;
pub mod ti;
pub mod vorago;

/// Vendor support trait.
pub trait Vendor: Send + Sync + std::fmt::Display {
    /// Tries to create a debug sequence for the given chip.
    fn try_create_debug_sequence(&self, chip: &Chip) -> Option<DebugSequence>;

    /// Tries to identify an ARM chip. Returns `Some(target name)` on success.
    fn try_detect_arm_chip(
        &self,
        _probe: &mut dyn ArmProbeInterface,
        _chip_info: ArmChipInfo,
    ) -> Result<Option<String>, Error> {
        Ok(None)
    }

    /// Tries to identify an RISC-V chip. Returns `Some(target name)` on success.
    fn try_detect_riscv_chip(
        &self,
        _probe: &mut RiscvCommunicationInterface,
        _idcode: u32,
    ) -> Result<Option<String>, Error> {
        Ok(None)
    }

    /// Tries to identify an Xtensa chip. Returns `Some(target name)` on success.
    fn try_detect_xtensa_chip(
        &self,
        _probe: &mut XtensaCommunicationInterface,
        _idcode: u32,
    ) -> Result<Option<String>, Error> {
        Ok(None)
    }
}

static VENDORS: LazyLock<RwLock<Vec<Box<dyn Vendor>>>> = LazyLock::new(|| {
    let vendors: Vec<Box<dyn Vendor>> = vec![
        Box::new(microchip::Microchip),
        Box::new(infineon::Infineon),
        Box::new(silabs::SiliconLabs),
        Box::new(ti::TexasInstruments),
        Box::new(espressif::Espressif),
        Box::new(nordicsemi::NordicSemi),
        Box::new(nxp::Nxp),
        Box::new(st::St),
        Box::new(vorago::Vorago),
    ];

    RwLock::new(vendors)
});

/// Registers a new vendor.
pub fn register_vendor(vendor: Box<dyn Vendor>) {
    // Order matters. Prepend to allow users to override the default vendors.
    VENDORS.write().insert(0, vendor);
}

/// Returns a readable view of all known vendors.
fn vendors<'a>() -> impl Deref<Target = [Box<dyn Vendor>]> + 'a {
    RwLockReadGuard::map(VENDORS.read_recursive(), |v| v.as_slice())
}

/// Tries to create a debug sequence for the given chip.
pub fn try_create_debug_sequence(chip: &Chip) -> Option<DebugSequence> {
    let vendors = vendors();
    for vendor in vendors.iter() {
        if let Some(sequence) = vendor.try_create_debug_sequence(chip) {
            return Some(sequence);
        }
    }

    None
}

fn try_detect_arm_chip(mut probe: Probe) -> Result<(Probe, Option<Target>), Error> {
    let mut found_target = None;

    if !probe.has_arm_interface() {
        // No ARM interface available.
        tracing::debug!("No ARM interface available, skipping detection.");
        return Ok((probe, None));
    }

    // We have no information about the target, so we must assume it's using the default DP.
    // We cannot automatically detect DPs if SWD multi-drop is used.
    // TODO: collect known DP addresses for known targets.
    let dp_addresses = [DpAddress::Default];

    for dp_address in dp_addresses {
        // TODO: do not consume probe
        match probe.try_into_arm_interface() {
            Ok(interface) => {
                let mut interface =
                    match interface.initialize(DefaultArmSequence::create(), dp_address) {
                        Ok(interface) => interface,
                        Err((interface, error)) => {
                            probe = interface.close();
                            tracing::debug!("Error during ARM chip detection: {error}");
                            // If we can't connect, assume this is not an ARM chip and not an error.
                            return Ok((probe, None));
                        }
                    };

                let found_arm_chip = interface
                    .read_chip_info_from_rom_table(dp_address)
                    .unwrap_or_else(|error| {
                        tracing::debug!("Error during ARM chip detection: {error}");
                        None
                    });

                if let Some(found_chip) = found_arm_chip {
                    let vendors = vendors();
                    for vendor in vendors.iter() {
                        // TODO: only consider families with matching JEP106.
                        if let Some(target_name) =
                            vendor.try_detect_arm_chip(interface.as_mut(), found_chip)?
                        {
                            found_target = Some(registry::get_target_by_name(&target_name)?);
                            break;
                        }
                    }

                    // No vendor-specific match, try to find a target by chip info.
                    if found_target.is_none() {
                        found_target = Some(crate::config::get_target_by_chip_info(
                            ChipInfo::from(found_chip),
                        )?);
                    }
                }

                probe = interface.close();
            }
            Err((returned_probe, error)) => {
                probe = returned_probe;
                tracing::debug!("Error using ARM interface: {error}");
            }
        }
    }

    Ok((probe, found_target))
}

fn try_detect_riscv_chip(probe: &mut Probe) -> Result<Option<Target>, Error> {
    let mut found_target = None;

    probe.select_jtag_tap(0)?;

    match probe.try_get_riscv_interface_builder() {
        Ok(factory) => {
            let mut state = factory.create_state();
            let mut interface = factory.attach(&mut state)?;

            if let Err(error) = interface.enter_debug_mode() {
                tracing::debug!("Failed to enter RISC-V debug mode: {error}");
                return Ok(None);
            }

            match interface.read_idcode() {
                Ok(Some(idcode)) => {
                    tracing::debug!("ID code read over JTAG: {idcode:#x}");
                    let vendors = vendors();
                    for vendor in vendors.iter() {
                        // TODO: only consider families with matching JEP106.
                        if let Some(target_name) =
                            vendor.try_detect_riscv_chip(&mut interface, idcode)?
                        {
                            found_target = Some(registry::get_target_by_name(target_name)?);
                            break;
                        }
                    }
                }
                Ok(_) => tracing::debug!("No RISC-V ID code returned."),
                Err(error) => tracing::debug!("Error during RISC-V chip detection: {error}"),
            }

            // TODO: disable debug module
        }

        Err(DebugProbeError::InterfaceNotAvailable { .. }) => {
            tracing::debug!("No RISC-V interface available, skipping detection.");
        }

        Err(error) => {
            tracing::debug!("Error during RISC-V chip detection: {error}");
        }
    }

    Ok(found_target)
}

fn try_detect_xtensa_chip(probe: &mut Probe) -> Result<Option<Target>, Error> {
    let mut found_target = None;

    probe.select_jtag_tap(0)?;

    let mut state = XtensaDebugInterfaceState::default();
    match probe.try_get_xtensa_interface(&mut state) {
        Ok(mut interface) => {
            if let Err(error) = interface.enter_debug_mode() {
                tracing::debug!("Failed to enter Xtensa debug mode: {error}");
                return Ok(None);
            }

            match interface.read_idcode() {
                Ok(idcode) => {
                    tracing::debug!("ID code read over JTAG: {idcode:#x}");
                    let vendors = vendors();
                    for vendor in vendors.iter() {
                        // TODO: only consider families with matching JEP106.
                        if let Some(target_name) =
                            vendor.try_detect_xtensa_chip(&mut interface, idcode)?
                        {
                            found_target = Some(registry::get_target_by_name(target_name)?);
                            break;
                        }
                    }
                }
                Err(error) => tracing::debug!("Error during Xtensa chip detection: {error}"),
            }

            interface.leave_debug_mode()?;
        }

        Err(DebugProbeError::InterfaceNotAvailable { .. }) => {
            tracing::debug!("No Xtensa interface available, skipping detection.");
        }

        Err(error) => {
            tracing::debug!("Error during autodetection of Xtensa chips: {error}");
        }
    }

    Ok(found_target)
}

/// Tries to identify the chip using the given probe.
pub(crate) fn auto_determine_target(mut probe: Probe) -> Result<(Probe, Option<Target>), Error> {
    tracing::info!("Auto-detecting target");
    let mut found_target = None;

    // Xtensa and RISC-V interfaces don't need moving the probe. For clarity, their
    // handlers work with the borrowed probe, and we use these wrappers to adapt to the
    // ARM way of moving in and out of the probe.
    fn try_detect_riscv_chip_wrapper(mut probe: Probe) -> Result<(Probe, Option<Target>), Error> {
        try_detect_riscv_chip(&mut probe).map(|found_target| (probe, found_target))
    }

    fn try_detect_xtensa_chip_wrapper(mut probe: Probe) -> Result<(Probe, Option<Target>), Error> {
        try_detect_xtensa_chip(&mut probe).map(|found_target| (probe, found_target))
    }

    type DetectFn = fn(Probe) -> Result<(Probe, Option<Target>), Error>;
    const ARCHITECTURES: &[DetectFn] = &[
        try_detect_arm_chip,
        try_detect_riscv_chip_wrapper,
        try_detect_xtensa_chip_wrapper,
    ];

    for architecture in ARCHITECTURES {
        let (returned_probe, target) = architecture(probe)?;

        probe = returned_probe;
        if let Some(target) = target {
            tracing::info!("Found target: {}", target.name);
            found_target = Some(target);
            break;
        }
    }

    probe.detach()?;

    Ok((probe, found_target))
}