1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub enum TargetArch {
8 ARMCortexM(CortexMVariant),
10
11 RISCV(RISCVVariant),
13}
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub enum CortexMVariant {
18 M3,
20
21 M4,
23
24 M4F,
26
27 M7,
29
30 M7DP,
32
33 M33,
35
36 M55,
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub enum RISCVVariant {
43 RV32I,
45
46 RV32IMAC,
48
49 RV32GC,
51
52 RV64I,
54
55 RV64IMAC,
57
58 RV64GC,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct HardwareCapabilities {
65 pub arch: TargetArch,
67
68 pub has_mpu: bool,
70
71 pub mpu_regions: u8,
73
74 pub has_pmp: bool,
76
77 pub pmp_entries: u8,
79
80 pub has_fpu: bool,
82
83 pub fpu_precision: Option<FPUPrecision>,
85
86 pub has_simd: bool,
88
89 pub simd_level: Option<SIMDLevel>,
91
92 pub xip_capable: bool,
94
95 pub flash_size: u64,
97
98 pub ram_size: u64,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum FPUPrecision {
105 Single,
107
108 Double,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114pub enum SIMDLevel {
115 Helium,
117
118 RISCVVector,
120
121 WASMSIMD,
123}
124
125impl TargetArch {
126 pub fn target_triple(&self) -> &str {
128 match self {
129 TargetArch::ARMCortexM(variant) => match variant {
130 CortexMVariant::M3 => "thumbv7m-none-eabi",
131 CortexMVariant::M4 | CortexMVariant::M4F => "thumbv7em-none-eabi",
132 CortexMVariant::M7 | CortexMVariant::M7DP => "thumbv7em-none-eabihf",
133 CortexMVariant::M33 => "thumbv8m.main-none-eabi",
134 CortexMVariant::M55 => "thumbv8.1m.main-none-eabi",
135 },
136 TargetArch::RISCV(variant) => match variant {
137 RISCVVariant::RV32I | RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => {
138 "riscv32imac-unknown-none-elf"
139 }
140 RISCVVariant::RV64I | RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => {
141 "riscv64gc-unknown-none-elf"
142 }
143 },
144 }
145 }
146
147 pub fn cpu_name(&self) -> &str {
149 match self {
150 TargetArch::ARMCortexM(variant) => match variant {
151 CortexMVariant::M3 => "cortex-m3",
152 CortexMVariant::M4 | CortexMVariant::M4F => "cortex-m4",
153 CortexMVariant::M7 | CortexMVariant::M7DP => "cortex-m7",
154 CortexMVariant::M33 => "cortex-m33",
155 CortexMVariant::M55 => "cortex-m55",
156 },
157 TargetArch::RISCV(variant) => match variant {
158 RISCVVariant::RV32I => "generic-rv32",
159 RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => "generic-rv32",
160 RISCVVariant::RV64I => "generic-rv64",
161 RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => "generic-rv64",
162 },
163 }
164 }
165
166 pub fn has_hardware_fp(&self) -> bool {
168 match self {
169 TargetArch::ARMCortexM(variant) => matches!(
170 variant,
171 CortexMVariant::M4F
172 | CortexMVariant::M7
173 | CortexMVariant::M7DP
174 | CortexMVariant::M55
175 ),
176 TargetArch::RISCV(variant) => {
177 matches!(variant, RISCVVariant::RV32GC | RISCVVariant::RV64GC)
178 }
179 }
180 }
181}
182
183impl HardwareCapabilities {
184 pub fn cortex_m4f_typical() -> Self {
186 Self {
187 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
188 has_mpu: true,
189 mpu_regions: 8,
190 has_pmp: false,
191 pmp_entries: 0,
192 has_fpu: true,
193 fpu_precision: Some(FPUPrecision::Single),
194 has_simd: false,
195 simd_level: None,
196 xip_capable: true,
197 flash_size: 1024 * 1024, ram_size: 192 * 1024, }
200 }
201
202 pub fn nrf52840() -> Self {
204 Self {
205 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
206 has_mpu: true,
207 mpu_regions: 8,
208 has_pmp: false,
209 pmp_entries: 0,
210 has_fpu: true,
211 fpu_precision: Some(FPUPrecision::Single),
212 has_simd: false,
213 simd_level: None,
214 xip_capable: true,
215 flash_size: 1024 * 1024, ram_size: 256 * 1024, }
218 }
219
220 pub fn stm32f407() -> Self {
222 Self {
223 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
224 has_mpu: true,
225 mpu_regions: 8,
226 has_pmp: false,
227 pmp_entries: 0,
228 has_fpu: true,
229 fpu_precision: Some(FPUPrecision::Single),
230 has_simd: false,
231 simd_level: None,
232 xip_capable: true,
233 flash_size: 1024 * 1024, ram_size: 192 * 1024, }
236 }
237
238 pub fn stm32h743() -> Self {
242 Self {
243 arch: TargetArch::ARMCortexM(CortexMVariant::M7DP),
244 has_mpu: true,
245 mpu_regions: 16,
246 has_pmp: false,
247 pmp_entries: 0,
248 has_fpu: true,
249 fpu_precision: Some(FPUPrecision::Double),
250 has_simd: false,
251 simd_level: None,
252 xip_capable: true,
253 flash_size: 2 * 1024 * 1024, ram_size: 1024 * 1024, }
256 }
257
258 pub fn imxrt1062() -> Self {
265 Self {
266 arch: TargetArch::ARMCortexM(CortexMVariant::M7),
267 has_mpu: true,
268 mpu_regions: 16,
269 has_pmp: false,
270 pmp_entries: 0,
271 has_fpu: true,
272 fpu_precision: Some(FPUPrecision::Single),
273 has_simd: false,
274 simd_level: None,
275 xip_capable: true,
276 flash_size: 8 * 1024 * 1024, ram_size: 1024 * 1024, }
279 }
280}
281
282#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
288pub enum ArchFamily {
289 ArmCortexM,
290 ArmCortexR,
291 ArmCortexA,
292 RiscV,
293}
294
295#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297pub enum IsaVariant {
298 Thumb,
300 Thumb2,
302 Arm32,
304 AArch64,
306 RiscV32 { extensions: String },
308 RiscV64 { extensions: String },
310}
311
312#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub enum MemProtection {
315 Mpu { regions: u8 },
317 Mmu,
319 Pmp { entries: u8 },
321 SoftwareOnly,
323}
324
325#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
327pub struct TargetSpec {
328 pub family: ArchFamily,
330 pub triple: String,
332 pub isa: IsaVariant,
334 pub mem_protection: MemProtection,
336 pub fpu: Option<FPUPrecision>,
338}
339
340impl CortexMVariant {
345 pub fn has_dsp(&self) -> bool {
348 !matches!(self, Self::M3)
349 }
350
351 pub fn has_single_precision_fpu(&self) -> bool {
353 matches!(self, Self::M4F | Self::M7 | Self::M7DP | Self::M55)
354 }
355
356 pub fn has_double_precision_fpu(&self) -> bool {
358 matches!(self, Self::M7DP)
359 }
360
361 pub fn has_trustzone(&self) -> bool {
363 matches!(self, Self::M33 | Self::M55)
364 }
365
366 pub fn has_helium(&self) -> bool {
368 matches!(self, Self::M55)
369 }
370}
371
372impl TargetSpec {
373 pub fn has_single_precision_fpu(&self) -> bool {
375 matches!(
376 self.fpu,
377 Some(FPUPrecision::Single) | Some(FPUPrecision::Double)
378 )
379 }
380
381 pub fn has_double_precision_fpu(&self) -> bool {
383 matches!(self.fpu, Some(FPUPrecision::Double))
384 }
385}
386
387impl TargetSpec {
388 pub fn cortex_m3() -> Self {
390 Self {
391 family: ArchFamily::ArmCortexM,
392 triple: "thumbv7m-none-eabi".to_string(),
393 isa: IsaVariant::Thumb2,
394 mem_protection: MemProtection::Mpu { regions: 8 },
395 fpu: None,
396 }
397 }
398
399 pub fn cortex_m4() -> Self {
401 Self {
402 family: ArchFamily::ArmCortexM,
403 triple: "thumbv7em-none-eabi".to_string(),
404 isa: IsaVariant::Thumb2,
405 mem_protection: MemProtection::Mpu { regions: 8 },
406 fpu: None,
407 }
408 }
409
410 pub fn cortex_m4f() -> Self {
412 Self {
413 family: ArchFamily::ArmCortexM,
414 triple: "thumbv7em-none-eabihf".to_string(),
415 isa: IsaVariant::Thumb2,
416 mem_protection: MemProtection::Mpu { regions: 8 },
417 fpu: Some(FPUPrecision::Single),
418 }
419 }
420
421 pub fn cortex_m7() -> Self {
423 Self {
424 family: ArchFamily::ArmCortexM,
425 triple: "thumbv7em-none-eabihf".to_string(),
426 isa: IsaVariant::Thumb2,
427 mem_protection: MemProtection::Mpu { regions: 16 },
428 fpu: Some(FPUPrecision::Single),
429 }
430 }
431
432 pub fn cortex_m7dp() -> Self {
434 Self {
435 family: ArchFamily::ArmCortexM,
436 triple: "thumbv7em-none-eabihf".to_string(),
437 isa: IsaVariant::Thumb2,
438 mem_protection: MemProtection::Mpu { regions: 16 },
439 fpu: Some(FPUPrecision::Double),
440 }
441 }
442
443 pub fn cortex_r5() -> Self {
445 Self {
446 family: ArchFamily::ArmCortexR,
447 triple: "armv7r-none-eabihf".to_string(),
448 isa: IsaVariant::Arm32,
449 mem_protection: MemProtection::Mpu { regions: 12 },
450 fpu: None,
451 }
452 }
453
454 pub fn cortex_a53() -> Self {
456 Self {
457 family: ArchFamily::ArmCortexA,
458 triple: "aarch64-none-elf".to_string(),
459 isa: IsaVariant::AArch64,
460 mem_protection: MemProtection::Mmu,
461 fpu: None,
462 }
463 }
464
465 pub fn riscv32imac() -> Self {
467 Self {
468 family: ArchFamily::RiscV,
469 triple: "riscv32imac-unknown-none-elf".to_string(),
470 isa: IsaVariant::RiscV32 {
471 extensions: "imac".to_string(),
472 },
473 mem_protection: MemProtection::Pmp { entries: 16 },
474 fpu: None,
475 }
476 }
477
478 pub fn cortex_m55() -> Self {
480 Self {
481 family: ArchFamily::ArmCortexM,
482 triple: "thumbv8.1m.main-none-eabi".to_string(),
483 isa: IsaVariant::Thumb2,
484 mem_protection: MemProtection::Mpu { regions: 16 },
485 fpu: Some(FPUPrecision::Single),
486 }
487 }
488
489 pub fn from_triple(triple: &str) -> std::result::Result<Self, String> {
491 match triple {
492 "thumbv7m-none-eabi" | "cortex-m3" => Ok(Self::cortex_m3()),
493 "thumbv7em-none-eabi" | "cortex-m4" => Ok(Self::cortex_m4()),
494 "thumbv7em-none-eabihf" | "cortex-m4f" => Ok(Self::cortex_m4f()),
495 "cortex-m7" => Ok(Self::cortex_m7()),
496 "cortex-m7dp" => Ok(Self::cortex_m7dp()),
497 "thumbv8.1m.main-none-eabi" | "cortex-m55" => Ok(Self::cortex_m55()),
498 "armv7r-none-eabihf" | "cortex-r5" => Ok(Self::cortex_r5()),
499 "aarch64-none-elf" | "cortex-a53" => Ok(Self::cortex_a53()),
500 "riscv32imac-unknown-none-elf" | "riscv32imac" => Ok(Self::riscv32imac()),
501 _ => Err(format!("unknown target triple: {}", triple)),
502 }
503 }
504
505 pub fn has_fpu(&self) -> bool {
507 self.fpu.is_some()
508 }
509
510 pub fn is_thumb2(&self) -> bool {
512 self.isa == IsaVariant::Thumb2
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519
520 #[test]
521 fn test_target_spec_from_triple() {
522 let spec = TargetSpec::from_triple("cortex-m4").unwrap();
523 assert_eq!(spec.family, ArchFamily::ArmCortexM);
524 assert!(spec.is_thumb2());
525 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 8 });
526 assert!(!spec.has_fpu());
527 }
528
529 #[test]
530 fn test_target_spec_unknown() {
531 assert!(TargetSpec::from_triple("mips-unknown-none").is_err());
532 }
533
534 #[test]
535 fn test_cortex_m3() {
536 let spec = TargetSpec::from_triple("cortex-m3").unwrap();
537 assert_eq!(spec.triple, "thumbv7m-none-eabi");
538 assert!(!spec.has_fpu());
539 }
540
541 #[test]
542 fn test_cortex_m4f() {
543 let spec = TargetSpec::from_triple("cortex-m4f").unwrap();
544 assert_eq!(spec.triple, "thumbv7em-none-eabihf");
545 assert!(spec.has_fpu());
546 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
547 }
548
549 #[test]
550 fn test_cortex_m7dp() {
551 let spec = TargetSpec::from_triple("cortex-m7dp").unwrap();
552 assert!(spec.has_fpu());
553 assert_eq!(spec.fpu, Some(FPUPrecision::Double));
554 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 16 });
555 }
556
557 #[test]
558 fn test_cortex_m7_has_fpu() {
559 let spec = TargetSpec::cortex_m7();
560 assert!(spec.has_fpu());
561 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
562 }
563
564 #[test]
569 fn test_cortex_m3_isa_features() {
570 let m3 = CortexMVariant::M3;
571 assert!(!m3.has_dsp(), "M3 should not have DSP");
572 assert!(!m3.has_single_precision_fpu(), "M3 should not have FPU");
573 assert!(
574 !m3.has_double_precision_fpu(),
575 "M3 should not have double FPU"
576 );
577 assert!(!m3.has_trustzone(), "M3 should not have TrustZone");
578 assert!(!m3.has_helium(), "M3 should not have Helium");
579 }
580
581 #[test]
582 fn test_cortex_m4_isa_features() {
583 let m4 = CortexMVariant::M4;
584 assert!(m4.has_dsp(), "M4 should have DSP");
585 assert!(
586 !m4.has_single_precision_fpu(),
587 "M4 (no F) should not have FPU"
588 );
589 assert!(
590 !m4.has_double_precision_fpu(),
591 "M4 should not have double FPU"
592 );
593 }
594
595 #[test]
596 fn test_cortex_m4f_isa_features() {
597 let m4f = CortexMVariant::M4F;
598 assert!(m4f.has_dsp(), "M4F should have DSP");
599 assert!(
600 m4f.has_single_precision_fpu(),
601 "M4F should have single-precision FPU"
602 );
603 assert!(
604 !m4f.has_double_precision_fpu(),
605 "M4F should not have double FPU"
606 );
607 }
608
609 #[test]
610 fn test_cortex_m7dp_isa_features() {
611 let m7dp = CortexMVariant::M7DP;
612 assert!(m7dp.has_dsp(), "M7DP should have DSP");
613 assert!(
614 m7dp.has_single_precision_fpu(),
615 "M7DP should have single-precision FPU"
616 );
617 assert!(
618 m7dp.has_double_precision_fpu(),
619 "M7DP should have double-precision FPU"
620 );
621 }
622
623 #[test]
624 fn test_cortex_m33_isa_features() {
625 let m33 = CortexMVariant::M33;
626 assert!(m33.has_dsp(), "M33 should have DSP");
627 assert!(m33.has_trustzone(), "M33 should have TrustZone");
628 assert!(!m33.has_helium(), "M33 should not have Helium");
629 }
630
631 #[test]
632 fn test_cortex_m55_isa_features() {
633 let m55 = CortexMVariant::M55;
634 assert!(m55.has_dsp(), "M55 should have DSP");
635 assert!(
636 m55.has_single_precision_fpu(),
637 "M55 should have single-precision FPU"
638 );
639 assert!(m55.has_trustzone(), "M55 should have TrustZone");
640 assert!(m55.has_helium(), "M55 should have Helium");
641 }
642
643 #[test]
644 fn test_target_spec_fpu_precision_queries() {
645 let m3 = TargetSpec::cortex_m3();
646 assert!(!m3.has_single_precision_fpu(), "M3 spec has no FPU");
647 assert!(!m3.has_double_precision_fpu(), "M3 spec has no double FPU");
648
649 let m4f = TargetSpec::cortex_m4f();
650 assert!(m4f.has_single_precision_fpu(), "M4F spec has single FPU");
651 assert!(
652 !m4f.has_double_precision_fpu(),
653 "M4F spec has no double FPU"
654 );
655
656 let m7dp = TargetSpec::cortex_m7dp();
657 assert!(m7dp.has_single_precision_fpu(), "M7DP spec has single FPU");
658 assert!(m7dp.has_double_precision_fpu(), "M7DP spec has double FPU");
659 }
660
661 #[test]
662 fn test_cortex_m55_target_spec() {
663 let m55 = TargetSpec::cortex_m55();
664 assert_eq!(m55.family, ArchFamily::ArmCortexM);
665 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
666 assert!(m55.is_thumb2());
667 assert!(m55.has_fpu());
668 assert!(m55.has_single_precision_fpu());
669 assert_eq!(m55.mem_protection, MemProtection::Mpu { regions: 16 });
670 }
671
672 #[test]
673 fn test_cortex_m55_from_triple() {
674 let m55 = TargetSpec::from_triple("cortex-m55").unwrap();
675 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
676 assert!(m55.has_fpu());
677
678 let m55_triple = TargetSpec::from_triple("thumbv8.1m.main-none-eabi").unwrap();
679 assert_eq!(m55_triple.triple, "thumbv8.1m.main-none-eabi");
680 }
681
682 #[test]
687 fn test_imxrt1062_capabilities() {
688 let caps = HardwareCapabilities::imxrt1062();
689 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7));
690 assert!(caps.has_mpu, "i.MX RT1062 has an MPU");
691 assert_eq!(caps.mpu_regions, 16, "M7 parts have 16 MPU regions");
692 assert!(!caps.has_pmp, "ARM parts do not have PMP");
693 assert_eq!(caps.pmp_entries, 0);
694 assert!(caps.has_fpu, "i.MX RT1062 has FPU");
695 assert_eq!(
696 caps.fpu_precision,
697 Some(FPUPrecision::Single),
698 "i.MX RT1062 has single-precision FPU"
699 );
700 assert!(!caps.has_simd);
701 assert!(caps.simd_level.is_none());
702 assert!(caps.xip_capable);
703 assert_eq!(caps.flash_size, 8 * 1024 * 1024, "8MB QSPI typical");
704 assert_eq!(caps.ram_size, 1024 * 1024, "1MB OCRAM");
705 }
706
707 #[test]
708 fn test_stm32h743_capabilities() {
709 let caps = HardwareCapabilities::stm32h743();
710 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7DP));
711 assert!(caps.has_mpu);
712 assert_eq!(caps.mpu_regions, 16, "STM32H743 has 16 MPU regions");
713 assert!(!caps.has_pmp);
714 assert!(caps.has_fpu);
715 assert_eq!(
716 caps.fpu_precision,
717 Some(FPUPrecision::Double),
718 "STM32H743 has double-precision FPU"
719 );
720 assert_eq!(caps.flash_size, 2 * 1024 * 1024, "2MB Flash");
721 assert_eq!(caps.ram_size, 1024 * 1024, "1MB RAM total");
722 assert!(caps.xip_capable);
723 }
724
725 #[test]
726 fn test_imxrt1062_arch_target_triple() {
727 let caps = HardwareCapabilities::imxrt1062();
729 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
730 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
731 assert!(caps.arch.has_hardware_fp());
732 }
733
734 #[test]
735 fn test_stm32h743_arch_target_triple() {
736 let caps = HardwareCapabilities::stm32h743();
738 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
739 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
740 assert!(caps.arch.has_hardware_fp());
741 }
742
743 #[test]
744 fn test_m7_hardware_capabilities_distinct_from_m4() {
745 let m4 = HardwareCapabilities::nrf52840();
749 let m7 = HardwareCapabilities::imxrt1062();
750 let m7dp = HardwareCapabilities::stm32h743();
751 assert_eq!(m4.mpu_regions, 8);
752 assert_eq!(m7.mpu_regions, 16);
753 assert_eq!(m7dp.mpu_regions, 16);
754 assert_eq!(m4.fpu_precision, Some(FPUPrecision::Single));
756 assert_eq!(m7.fpu_precision, Some(FPUPrecision::Single));
757 assert_eq!(m7dp.fpu_precision, Some(FPUPrecision::Double));
758 }
759}