Skip to main content

probe_rs/config/
target.rs

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