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 Armv7r,
40 Armv7m,
42 Armv7em,
44 Armv8a,
46 Armv8m,
48 Riscv,
50 Riscv64,
52 Xtensa,
54}
55
56impl CoreType {
57 pub fn is_cortex_m(&self) -> bool {
59 matches!(
60 self,
61 CoreType::Armv6m | CoreType::Armv7em | CoreType::Armv7m | CoreType::Armv8m
62 )
63 }
64
65 fn is_riscv(&self) -> bool {
66 matches!(self, CoreType::Riscv | CoreType::Riscv64)
67 }
68
69 fn is_xtensa(&self) -> bool {
70 matches!(self, CoreType::Xtensa)
71 }
72
73 fn is_arm(&self) -> bool {
74 matches!(
75 self,
76 CoreType::Armv6m
77 | CoreType::Armv7a
78 | CoreType::Armv7r
79 | CoreType::Armv7em
80 | CoreType::Armv7m
81 | CoreType::Armv8a
82 | CoreType::Armv8m
83 )
84 }
85
86 pub fn architecture(&self) -> Architecture {
88 match self {
89 CoreType::Riscv | CoreType::Riscv64 => Architecture::Riscv,
90 CoreType::Xtensa => Architecture::Xtensa,
91 _ => Architecture::Arm,
92 }
93 }
94}
95
96#[derive(Clone, Copy, Debug, PartialEq, Eq)]
98pub enum Architecture {
99 Arm,
101 Riscv,
103 Xtensa,
105}
106
107#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
109pub enum InstructionSet {
110 Thumb2,
112 A32,
114 A64,
116 RV32,
118 RV32C,
120 RV64,
122 RV64C,
124 Xtensa,
126}
127
128impl InstructionSet {
129 pub fn from_target_triple(triple: &str) -> Option<Self> {
131 match triple.split('-').next()? {
132 "thumbv6m" | "thumbv7em" | "thumbv7m" | "thumbv8m" => Some(InstructionSet::Thumb2),
133 "arm" => Some(InstructionSet::A32),
134 "aarch64" => Some(InstructionSet::A64),
135 "xtensa" => Some(InstructionSet::Xtensa),
136 other => {
137 if let Some(features) = other.strip_prefix("riscv32") {
138 if features.contains('c') {
139 Some(InstructionSet::RV32C)
140 } else {
141 Some(InstructionSet::RV32)
142 }
143 } else if let Some(features) = other.strip_prefix("riscv64") {
144 if features.contains('c') {
145 Some(InstructionSet::RV64C)
146 } else {
147 Some(InstructionSet::RV64)
148 }
149 } else {
150 None
151 }
152 }
153 }
154 }
155
156 pub fn get_minimum_instruction_size(&self) -> u8 {
158 match self {
159 InstructionSet::Thumb2 => {
160 2
162 }
163 InstructionSet::A32 => 4,
164 InstructionSet::A64 => 4,
165 InstructionSet::RV32 => 4,
166 InstructionSet::RV32C => 2,
167 InstructionSet::RV64 => 4,
169 InstructionSet::RV64C => 2,
170 InstructionSet::Xtensa => 2,
171 }
172 }
173 pub fn get_maximum_instruction_size(&self) -> u8 {
175 4
177 }
178
179 pub fn is_compatible(&self, instr_set: InstructionSet) -> bool {
181 if *self == instr_set {
182 return true;
183 }
184
185 matches!(
186 (self, instr_set),
187 (InstructionSet::RV32C, InstructionSet::RV32)
188 | (InstructionSet::RV64C, InstructionSet::RV64)
189 )
190 }
191}
192
193#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
195pub enum Endian {
196 Little,
198 Big,
200}
201
202#[derive(Debug, Clone, Serialize, Deserialize)]
207#[serde(deny_unknown_fields)]
208pub struct ChipFamily {
209 pub name: String,
212 #[serde(serialize_with = "hex_jep106_option")]
214 pub manufacturer: Option<JEP106Code>,
215 #[serde(default)]
217 pub chip_detection: Vec<ChipDetectionMethod>,
218 #[serde(default)]
221 pub generated_from_pack: bool,
222 #[serde(default)]
227 pub pack_file_release: Option<String>,
228 pub variants: Vec<Chip>,
230 #[serde(default)]
232 pub flash_algorithms: Vec<RawFlashAlgorithm>,
233 #[serde(skip, default = "default_source")]
234 pub source: TargetDescriptionSource,
236}
237
238fn default_source() -> TargetDescriptionSource {
239 TargetDescriptionSource::External
240}
241
242impl ChipFamily {
243 pub fn validate(&self) -> Result<(), String> {
247 self.reject_duplicate_target_names()?;
248 self.ensure_algorithms_exist()?;
249 self.ensure_at_least_one_core()?;
250 self.reject_incorrect_core_access_options()?;
251 self.validate_memory_regions()?;
252 self.validate_rtt_scan_regions()?;
253
254 Ok(())
255 }
256
257 fn reject_duplicate_target_names(&self) -> Result<(), String> {
260 use std::collections::HashSet;
261
262 let mut seen = HashSet::new();
263
264 for chip in &self.variants {
265 if !seen.insert(&chip.name) {
266 return Err(format!(
267 "Target {} appears multiple times in {}",
268 chip.name, self.name,
269 ));
270 }
271 }
272
273 Ok(())
274 }
275
276 fn ensure_algorithms_exist(&self) -> Result<(), String> {
278 for variant in &self.variants {
279 for algorithm_name in variant.flash_algorithms.iter() {
280 if !self
281 .flash_algorithms
282 .iter()
283 .any(|algorithm| &algorithm.name == algorithm_name)
284 {
285 return Err(format!(
286 "The chip variant {chip_name} refers to a flash algorithm {algorithm_name}, \
287 which is not defined in the {family_name} family.",
288 chip_name = variant.name,
289 family_name = self.name,
290 ));
291 }
292 }
293 }
294
295 Ok(())
296 }
297
298 fn ensure_at_least_one_core(&self) -> Result<(), String> {
300 for variant in &self.variants {
301 let Some(core) = variant.cores.first() else {
302 return Err(format!(
303 "variant `{}` does not contain any cores",
304 variant.name
305 ));
306 };
307
308 let architecture = core.core_type.architecture();
310 if variant
311 .cores
312 .iter()
313 .any(|core| core.core_type.architecture() != architecture)
314 {
315 return Err(format!(
316 "variant `{}` contains mixed core architectures",
317 variant.name
318 ));
319 }
320 }
321
322 Ok(())
323 }
324
325 fn reject_incorrect_core_access_options(&self) -> Result<(), String> {
326 for variant in &self.variants {
329 for core in variant.cores.iter() {
331 match &core.core_access_options {
333 CoreAccessOptions::Arm(_) if !core.core_type.is_arm() => {
334 return Err(format!(
335 "Arm options don't match core type {:?} on core {}",
336 core.core_type, core.name
337 ));
338 }
339 CoreAccessOptions::Riscv(_) if !core.core_type.is_riscv() => {
340 return Err(format!(
341 "Riscv options don't match core type {:?} on core {}",
342 core.core_type, core.name
343 ));
344 }
345 CoreAccessOptions::Xtensa(_) if !core.core_type.is_xtensa() => {
346 return Err(format!(
347 "Xtensa options don't match core type {:?} on core {}",
348 core.core_type, core.name
349 ));
350 }
351 CoreAccessOptions::Arm(options) => {
352 if matches!(
353 core.core_type,
354 CoreType::Armv7a | CoreType::Armv7r | CoreType::Armv8a
355 ) && options.debug_base.is_none()
356 {
357 return Err(format!("Core {} requires setting debug_base", core.name));
358 }
359
360 if core.core_type == CoreType::Armv8a && options.cti_base.is_none() {
361 return Err(format!("Core {} requires setting cti_base", core.name));
362 }
363 }
364 _ => {}
365 }
366 }
367 }
368
369 Ok(())
370 }
371
372 fn validate_memory_regions(&self) -> Result<(), String> {
374 for variant in &self.variants {
375 let core_names = variant
376 .cores
377 .iter()
378 .map(|core| &core.name)
379 .collect::<Vec<_>>();
380
381 if variant.memory_map.is_empty() && self.source != TargetDescriptionSource::Generic {
382 return Err(format!(
383 "Variant {} does not contain any memory regions",
384 variant.name
385 ));
386 }
387
388 for memory in &variant.memory_map {
389 for core in memory.cores() {
390 if !core_names.contains(&core) {
391 return Err(format!(
392 "Variant {}, memory region {:?} is assigned to a non-existent core {}",
393 variant.name, memory, core
394 ));
395 }
396 }
397
398 if memory.cores().is_empty() {
399 return Err(format!(
400 "Variant {}, memory region {:?} is not assigned to a core",
401 variant.name, memory
402 ));
403 }
404 }
405 }
406
407 Ok(())
408 }
409
410 fn validate_rtt_scan_regions(&self) -> Result<(), String> {
411 for variant in &self.variants {
412 let Some(rtt_scan_ranges) = &variant.rtt_scan_ranges else {
413 return Ok(());
414 };
415
416 let ram_regions = variant
417 .memory_map
418 .iter()
419 .filter_map(MemoryRegion::as_ram_region)
420 .merge_consecutive()
421 .collect::<Vec<_>>();
422
423 for scan_range in rtt_scan_ranges {
426 if ram_regions
427 .iter()
428 .any(|region| region.range.contains_range(scan_range))
429 {
430 continue;
431 }
432
433 return Err(format!(
434 "The RTT scan region ({:#010x?}) of {} is not enclosed by any single RAM region.",
435 scan_range, variant.name,
436 ));
437 }
438 }
439
440 Ok(())
441 }
442}
443
444impl ChipFamily {
445 pub fn variants(&self) -> &[Chip] {
448 &self.variants
449 }
450
451 pub fn algorithms(&self) -> &[RawFlashAlgorithm] {
453 &self.flash_algorithms
454 }
455
456 pub fn get_algorithm(&self, name: impl AsRef<str>) -> Option<&RawFlashAlgorithm> {
458 let name = name.as_ref();
459 self.flash_algorithms.iter().find(|elem| elem.name == name)
460 }
461
462 pub fn get_algorithm_for_chip(
465 &self,
466 name: impl AsRef<str>,
467 chip: &Chip,
468 ) -> Option<RawFlashAlgorithm> {
469 self.get_algorithm(name).map(|algo| {
470 let mut algo_cores = if algo.cores.is_empty() {
471 chip.cores.iter().map(|core| core.name.clone()).collect()
472 } else {
473 algo.cores.clone()
474 };
475
476 algo_cores.retain(|algo_core| {
478 chip.cores
479 .iter()
480 .any(|chip_core| &chip_core.name == algo_core)
481 });
482
483 RawFlashAlgorithm {
484 cores: algo_cores,
485 ..algo.clone()
486 }
487 })
488 }
489}