1use crate::memory::RegionMergeIterator as _;
2use crate::serialize::hex_jep106_option;
3use crate::{CoreAccessOptions, chip_detection::ChipDetectionMethod};
4use crate::{MemoryRange, MemoryRegion};
5
6use super::chip::Chip;
7use super::flash_algorithm::RawFlashAlgorithm;
8use jep106::JEP106Code;
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Debug, PartialEq, Eq)]
17pub enum TargetDescriptionSource {
18 Generic,
22 BuiltIn,
25 External,
28}
29
30#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
32#[serde(rename_all = "snake_case")]
33pub enum CoreType {
34 Armv6m,
36 Armv7a,
38 Armv7m,
40 Armv7em,
42 Armv8a,
44 Armv8m,
46 Riscv,
48 Xtensa,
50}
51
52impl CoreType {
53 pub fn is_cortex_m(&self) -> bool {
55 matches!(
56 self,
57 CoreType::Armv6m | CoreType::Armv7em | CoreType::Armv7m | CoreType::Armv8m
58 )
59 }
60
61 fn is_riscv(&self) -> bool {
62 matches!(self, CoreType::Riscv)
63 }
64
65 fn is_xtensa(&self) -> bool {
66 matches!(self, CoreType::Xtensa)
67 }
68
69 fn is_arm(&self) -> bool {
70 matches!(
71 self,
72 CoreType::Armv6m
73 | CoreType::Armv7a
74 | CoreType::Armv7em
75 | CoreType::Armv7m
76 | CoreType::Armv8a
77 | CoreType::Armv8m
78 )
79 }
80
81 pub fn architecture(&self) -> Architecture {
83 match self {
84 CoreType::Riscv => Architecture::Riscv,
85 CoreType::Xtensa => Architecture::Xtensa,
86 _ => Architecture::Arm,
87 }
88 }
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, Eq)]
93pub enum Architecture {
94 Arm,
96 Riscv,
98 Xtensa,
100}
101
102#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub enum InstructionSet {
105 Thumb2,
107 A32,
109 A64,
111 RV32,
113 RV32C,
115 Xtensa,
117}
118
119impl InstructionSet {
120 pub fn from_target_triple(triple: &str) -> Option<Self> {
122 match triple.split('-').next()? {
123 "thumbv6m" | "thumbv7em" | "thumbv7m" | "thumbv8m" => Some(InstructionSet::Thumb2),
124 "arm" => Some(InstructionSet::A32),
125 "aarch64" => Some(InstructionSet::A64),
126 "xtensa" => Some(InstructionSet::Xtensa),
127 other => {
128 if let Some(features) = other.strip_prefix("riscv32") {
129 if features.contains('c') {
130 Some(InstructionSet::RV32C)
131 } else {
132 Some(InstructionSet::RV32)
133 }
134 } else {
135 None
136 }
137 }
138 }
139 }
140
141 pub fn get_minimum_instruction_size(&self) -> u8 {
143 match self {
144 InstructionSet::Thumb2 => {
145 2
147 }
148 InstructionSet::A32 => 4,
149 InstructionSet::A64 => 4,
150 InstructionSet::RV32 => 4,
151 InstructionSet::RV32C => 2,
152 InstructionSet::Xtensa => 2,
153 }
154 }
155 pub fn get_maximum_instruction_size(&self) -> u8 {
157 4
159 }
160
161 pub fn is_compatible(&self, instr_set: InstructionSet) -> bool {
163 if *self == instr_set {
164 return true;
165 }
166
167 matches!(
168 (self, instr_set),
169 (InstructionSet::RV32C, InstructionSet::RV32)
170 )
171 }
172}
173
174#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
176pub enum Endian {
177 Little,
179 Big,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
188#[serde(deny_unknown_fields)]
189pub struct ChipFamily {
190 pub name: String,
193 #[serde(serialize_with = "hex_jep106_option")]
195 pub manufacturer: Option<JEP106Code>,
196 #[serde(default)]
198 pub chip_detection: Vec<ChipDetectionMethod>,
199 #[serde(default)]
202 pub generated_from_pack: bool,
203 #[serde(default)]
208 pub pack_file_release: Option<String>,
209 pub variants: Vec<Chip>,
211 #[serde(default)]
213 pub flash_algorithms: Vec<RawFlashAlgorithm>,
214 #[serde(skip, default = "default_source")]
215 pub source: TargetDescriptionSource,
217}
218
219fn default_source() -> TargetDescriptionSource {
220 TargetDescriptionSource::External
221}
222
223impl ChipFamily {
224 pub fn validate(&self) -> Result<(), String> {
228 self.reject_duplicate_target_names()?;
229 self.ensure_algorithms_exist()?;
230 self.ensure_at_least_one_core()?;
231 self.reject_incorrect_core_access_options()?;
232 self.validate_memory_regions()?;
233 self.validate_rtt_scan_regions()?;
234
235 Ok(())
236 }
237
238 fn reject_duplicate_target_names(&self) -> Result<(), String> {
241 use std::collections::HashSet;
242
243 let mut seen = HashSet::new();
244
245 for chip in &self.variants {
246 if !seen.insert(&chip.name) {
247 return Err(format!(
248 "Target {} appears multiple times in {}",
249 chip.name, self.name,
250 ));
251 }
252 }
253
254 Ok(())
255 }
256
257 fn ensure_algorithms_exist(&self) -> Result<(), String> {
259 for variant in &self.variants {
260 for algorithm_name in variant.flash_algorithms.iter() {
261 if !self
262 .flash_algorithms
263 .iter()
264 .any(|algorithm| &algorithm.name == algorithm_name)
265 {
266 return Err(format!(
267 "The chip variant {chip_name} refers to a flash algorithm {algorithm_name}, \
268 which is not defined in the {family_name} family.",
269 chip_name = variant.name,
270 family_name = self.name,
271 ));
272 }
273 }
274 }
275
276 Ok(())
277 }
278
279 fn ensure_at_least_one_core(&self) -> Result<(), String> {
281 for variant in &self.variants {
282 let Some(core) = variant.cores.first() else {
283 return Err(format!(
284 "variant `{}` does not contain any cores",
285 variant.name
286 ));
287 };
288
289 let architecture = core.core_type.architecture();
291 if variant
292 .cores
293 .iter()
294 .any(|core| core.core_type.architecture() != architecture)
295 {
296 return Err(format!(
297 "variant `{}` contains mixed core architectures",
298 variant.name
299 ));
300 }
301 }
302
303 Ok(())
304 }
305
306 fn reject_incorrect_core_access_options(&self) -> Result<(), String> {
307 for variant in &self.variants {
310 for core in variant.cores.iter() {
312 match &core.core_access_options {
314 CoreAccessOptions::Arm(_) if !core.core_type.is_arm() => {
315 return Err(format!(
316 "Arm options don't match core type {:?} on core {}",
317 core.core_type, core.name
318 ));
319 }
320 CoreAccessOptions::Riscv(_) if !core.core_type.is_riscv() => {
321 return Err(format!(
322 "Riscv options don't match core type {:?} on core {}",
323 core.core_type, core.name
324 ));
325 }
326 CoreAccessOptions::Xtensa(_) if !core.core_type.is_xtensa() => {
327 return Err(format!(
328 "Xtensa options don't match core type {:?} on core {}",
329 core.core_type, core.name
330 ));
331 }
332 CoreAccessOptions::Arm(options) => {
333 if matches!(core.core_type, CoreType::Armv7a | CoreType::Armv8a)
334 && options.debug_base.is_none()
335 {
336 return Err(format!("Core {} requires setting debug_base", core.name));
337 }
338
339 if core.core_type == CoreType::Armv8a && options.cti_base.is_none() {
340 return Err(format!("Core {} requires setting cti_base", core.name));
341 }
342 }
343 _ => {}
344 }
345 }
346 }
347
348 Ok(())
349 }
350
351 fn validate_memory_regions(&self) -> Result<(), String> {
353 for variant in &self.variants {
354 let core_names = variant
355 .cores
356 .iter()
357 .map(|core| &core.name)
358 .collect::<Vec<_>>();
359
360 if variant.memory_map.is_empty() && self.source != TargetDescriptionSource::Generic {
361 return Err(format!(
362 "Variant {} does not contain any memory regions",
363 variant.name
364 ));
365 }
366
367 for memory in &variant.memory_map {
368 for core in memory.cores() {
369 if !core_names.contains(&core) {
370 return Err(format!(
371 "Variant {}, memory region {:?} is assigned to a non-existent core {}",
372 variant.name, memory, core
373 ));
374 }
375 }
376
377 if memory.cores().is_empty() {
378 return Err(format!(
379 "Variant {}, memory region {:?} is not assigned to a core",
380 variant.name, memory
381 ));
382 }
383 }
384 }
385
386 Ok(())
387 }
388
389 fn validate_rtt_scan_regions(&self) -> Result<(), String> {
390 for variant in &self.variants {
391 let Some(rtt_scan_ranges) = &variant.rtt_scan_ranges else {
392 return Ok(());
393 };
394
395 let ram_regions = variant
396 .memory_map
397 .iter()
398 .filter_map(MemoryRegion::as_ram_region)
399 .merge_consecutive()
400 .collect::<Vec<_>>();
401
402 for scan_range in rtt_scan_ranges {
405 if ram_regions
406 .iter()
407 .any(|region| region.range.contains_range(scan_range))
408 {
409 continue;
410 }
411
412 return Err(format!(
413 "The RTT scan region ({:#010x?}) of {} is not enclosed by any single RAM region.",
414 scan_range, variant.name,
415 ));
416 }
417 }
418
419 Ok(())
420 }
421}
422
423impl ChipFamily {
424 pub fn variants(&self) -> &[Chip] {
427 &self.variants
428 }
429
430 pub fn algorithms(&self) -> &[RawFlashAlgorithm] {
432 &self.flash_algorithms
433 }
434
435 pub fn get_algorithm(&self, name: impl AsRef<str>) -> Option<&RawFlashAlgorithm> {
437 let name = name.as_ref();
438 self.flash_algorithms.iter().find(|elem| elem.name == name)
439 }
440
441 pub fn get_algorithm_for_chip(
444 &self,
445 name: impl AsRef<str>,
446 chip: &Chip,
447 ) -> Option<RawFlashAlgorithm> {
448 self.get_algorithm(name).map(|algo| {
449 let mut algo_cores = if algo.cores.is_empty() {
450 chip.cores.iter().map(|core| core.name.clone()).collect()
451 } else {
452 algo.cores.clone()
453 };
454
455 algo_cores.retain(|algo_core| {
457 chip.cores
458 .iter()
459 .any(|chip_core| &chip_core.name == algo_core)
460 });
461
462 RawFlashAlgorithm {
463 cores: algo_cores,
464 ..algo.clone()
465 }
466 })
467 }
468}