probe_rs/config/
target.rs

1use super::{Core, MemoryRegion, RawFlashAlgorithm, TargetDescriptionSource};
2use crate::flashing::FlashLoader;
3use crate::{
4    architecture::{
5        arm::{
6            ApV2Address, FullyQualifiedApAddress,
7            dp::DpAddress,
8            sequences::{ArmDebugSequence, DefaultArmSequence},
9        },
10        riscv::sequences::{DefaultRiscvSequence, RiscvDebugSequence},
11        xtensa::sequences::{DefaultXtensaSequence, XtensaDebugSequence},
12    },
13    rtt::ScanRegion,
14};
15use probe_rs_target::{
16    Architecture, Chip, ChipFamily, Jtag, MemoryAccess, MemoryRange as _, NvmRegion,
17};
18use std::sync::Arc;
19
20/// This describes a complete target with a fixed chip model and variant.
21#[derive(Clone)]
22pub struct Target {
23    /// The name of the target.
24    pub name: String,
25    /// The cores of the target.
26    pub cores: Vec<Core>,
27    /// The name of the flash algorithm.
28    pub flash_algorithms: Vec<RawFlashAlgorithm>,
29    /// The memory map of the target.
30    pub memory_map: Vec<MemoryRegion>,
31    /// Source of the target description. Used for diagnostics.
32    pub(crate) source: TargetDescriptionSource,
33    /// Debug sequences for the given target.
34    pub debug_sequence: DebugSequence,
35    /// The regions of memory to scan to try to find an RTT header.
36    ///
37    /// Each region must be enclosed in exactly one RAM region from
38    /// `memory_map`.
39    pub rtt_scan_regions: ScanRegion,
40    /// The Description of the scan chain
41    ///
42    /// The scan chain can be parsed from the CMSIS-SDF file, or specified
43    /// manually in the target.yaml file. It is used by some probes to determine
44    /// the number devices in the scan chain and their ir lengths.
45    pub jtag: Option<Jtag>,
46    /// The default executable format for the target.
47    pub default_format: Option<String>,
48}
49
50impl std::fmt::Debug for Target {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        write!(
53            f,
54            "Target {{
55            identifier: {:?},
56            flash_algorithms: {:?},
57            memory_map: {:?},
58        }}",
59            self.name, self.flash_algorithms, self.memory_map
60        )
61    }
62}
63
64impl Target {
65    /// Create a new target for the given details.
66    ///
67    /// The given chip must be a member of the given family.
68    pub(super) fn new(family: &ChipFamily, chip: &Chip) -> Target {
69        let mut memory_map = chip.memory_map.clone();
70        let mut flash_algorithms = Vec::new();
71        for algo_name in chip.flash_algorithms.iter() {
72            let Some(algo) = family.get_algorithm_for_chip(algo_name, chip) else {
73                unreachable!(
74                    "The chip {chip_name} refers to a flash algorithm called {algo_name}, which is \
75                    not defined in the family {family_name}. The flash algorithms should have been \
76                    validated when the ChipFamily was loaded. This is a bug, please report it.",
77                    chip_name = chip.name,
78                    family_name = family.name,
79                );
80            };
81
82            // If the flash algorithm addresses a range that is not covered by any memory region,
83            // we add a new memory region for it. This is usually the case for option bytes and
84            // some OTP memory regions in certain CMSIS packs.
85            let algo_range = &algo.flash_properties.address_range;
86            if !memory_map
87                .iter()
88                .any(|region| region.address_range().intersects_range(algo_range))
89            {
90                // HACK (doubly, even):
91                // Conjure up a memory region for the flash algorithm. This is mostly used by
92                // EEPROM regions. We probably don't want to erase these regions, so we set
93                // `is_alias`, which then causes "erase all" to skip the region.
94                memory_map.push(MemoryRegion::Nvm(NvmRegion {
95                    name: Some(format!("synthesized for {algo_name}")),
96                    range: algo_range.clone(),
97                    cores: if algo.cores.is_empty() {
98                        chip.cores.iter().map(|core| core.name.clone()).collect()
99                    } else {
100                        algo.cores.clone()
101                    },
102                    is_alias: true,
103                    access: Some(MemoryAccess {
104                        read: false,
105                        write: false,
106                        execute: false,
107                        boot: false,
108                    }),
109                }));
110            }
111
112            flash_algorithms.push(algo);
113        }
114
115        let debug_sequence = crate::vendor::try_create_debug_sequence(chip).unwrap_or_else(|| {
116            // Default to the architecture of the first core, which is okay if
117            // there is no mixed architectures.
118            match chip.cores[0].core_type.architecture() {
119                Architecture::Arm => DebugSequence::Arm(DefaultArmSequence::create()),
120                Architecture::Riscv => DebugSequence::Riscv(DefaultRiscvSequence::create()),
121                Architecture::Xtensa => DebugSequence::Xtensa(DefaultXtensaSequence::create()),
122            }
123        });
124
125        tracing::info!("Using sequence {:?}", debug_sequence);
126
127        let rtt_scan_regions = match &chip.rtt_scan_ranges {
128            Some(ranges) => ScanRegion::Ranges(ranges.clone()),
129            None => ScanRegion::Ram, // By default we use all of the RAM ranges from the memory map.
130        };
131
132        Target {
133            name: chip.name.clone(),
134            cores: chip.cores.clone(),
135            flash_algorithms,
136            source: family.source.clone(),
137            memory_map,
138            debug_sequence,
139            rtt_scan_regions,
140            jtag: chip.jtag.clone(),
141            default_format: chip.default_binary_format.clone(),
142        }
143    }
144
145    /// Get the architecture of the target
146    pub fn architecture(&self) -> Architecture {
147        let target_arch = self.cores[0].core_type.architecture();
148
149        // This should be ensured when a `ChipFamily` is loaded.
150        assert!(
151            self.cores
152                .iter()
153                .map(|core| core.core_type.architecture())
154                .all(|core_arch| core_arch == target_arch),
155            "Not all cores of the target are of the same architecture. Probe-rs doesn't support this (yet). If you see this, it is a bug. Please file an issue."
156        );
157
158        target_arch
159    }
160
161    /// Return the default core of the target, usually the first core.
162    ///
163    /// This core should be used for operations such as debug_unlock,
164    /// when nothing else is specified.
165    pub fn default_core(&self) -> &Core {
166        // TODO: Check if this is specified in the target description.
167        &self.cores[0]
168    }
169
170    /// Source description of this target.
171    pub fn source(&self) -> &TargetDescriptionSource {
172        &self.source
173    }
174
175    /// Create a [FlashLoader] for this target, which can be used
176    /// to program its non-volatile memory.
177    pub fn flash_loader(&self) -> FlashLoader {
178        FlashLoader::new(self.memory_map.clone(), self.source.clone())
179    }
180
181    /// Returns a [RawFlashAlgorithm] by name.
182    pub(crate) fn flash_algorithm_by_name(&self, name: &str) -> Option<&RawFlashAlgorithm> {
183        self.flash_algorithms.iter().find(|a| a.name == name)
184    }
185
186    /// Returns the core index from the core name
187    pub fn core_index_by_name(&self, name: &str) -> Option<usize> {
188        self.cores.iter().position(|c| c.name == name)
189    }
190
191    /// Returns the core index from the core name
192    pub fn core_index_by_address(&self, address: u64) -> Option<usize> {
193        let target_memory = self.memory_region_by_address(address)?;
194        let core_name = target_memory.cores().first()?;
195        self.core_index_by_name(core_name)
196    }
197
198    /// Returns the first found [MemoryRegion] that contains the given address
199    pub fn memory_region_by_address(&self, address: u64) -> Option<&MemoryRegion> {
200        self.memory_map
201            .iter()
202            .find(|region| region.contains(address))
203    }
204}
205
206/// Selector for the debug target.
207#[derive(Debug, Clone)]
208pub enum TargetSelector {
209    /// Specify the name of a target, which will
210    /// be used to search the internal list of
211    /// targets.
212    Unspecified(String),
213    /// Directly specify a target.
214    Specified(Target),
215    /// Try to automatically identify the target,
216    /// by reading identifying information from
217    /// the probe and / or target.
218    Auto,
219}
220
221impl From<&str> for TargetSelector {
222    fn from(value: &str) -> Self {
223        TargetSelector::Unspecified(value.into())
224    }
225}
226
227impl From<&String> for TargetSelector {
228    fn from(value: &String) -> Self {
229        TargetSelector::Unspecified(value.into())
230    }
231}
232
233impl From<String> for TargetSelector {
234    fn from(value: String) -> Self {
235        TargetSelector::Unspecified(value)
236    }
237}
238
239impl From<Option<&str>> for TargetSelector {
240    fn from(value: Option<&str>) -> Self {
241        match value {
242            Some(identifier) => identifier.into(),
243            None => TargetSelector::Auto,
244        }
245    }
246}
247
248impl From<()> for TargetSelector {
249    fn from(_value: ()) -> Self {
250        TargetSelector::Auto
251    }
252}
253
254impl From<Target> for TargetSelector {
255    fn from(target: Target) -> Self {
256        TargetSelector::Specified(target)
257    }
258}
259
260/// This is the type to denote a general debug sequence.
261/// It can differentiate between ARM and RISC-V for now.
262#[derive(Clone, Debug)]
263pub enum DebugSequence {
264    /// An ARM debug sequence.
265    Arm(Arc<dyn ArmDebugSequence>),
266    /// A RISC-V debug sequence.
267    Riscv(Arc<dyn RiscvDebugSequence>),
268    /// An Xtensa debug sequence.
269    Xtensa(Arc<dyn XtensaDebugSequence>),
270}
271
272pub(crate) trait CoreExt {
273    // Retrieve the Coresight MemoryAP which should be used to
274    // access the core, if available.
275    fn memory_ap(&self) -> Option<FullyQualifiedApAddress>;
276}
277
278impl CoreExt for Core {
279    fn memory_ap(&self) -> Option<FullyQualifiedApAddress> {
280        match &self.core_access_options {
281            probe_rs_target::CoreAccessOptions::Arm(options) => {
282                let dp = match options.targetsel {
283                    None => DpAddress::Default,
284                    Some(x) => DpAddress::Multidrop(x),
285                };
286                Some(match &options.ap {
287                    probe_rs_target::ApAddress::V1(ap) => {
288                        FullyQualifiedApAddress::v1_with_dp(dp, *ap)
289                    }
290                    probe_rs_target::ApAddress::V2(ap) => {
291                        FullyQualifiedApAddress::v2_with_dp(dp, ApV2Address::new(*ap))
292                    }
293                })
294            }
295            probe_rs_target::CoreAccessOptions::Riscv(_) => None,
296            probe_rs_target::CoreAccessOptions::Xtensa(_) => None,
297        }
298    }
299}