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 riscv32(extensions: &str) -> Self {
470 Self {
471 family: ArchFamily::RiscV,
472 triple: format!("riscv32{extensions}-unknown-none-elf"),
473 isa: IsaVariant::RiscV32 {
474 extensions: extensions.to_string(),
475 },
476 mem_protection: MemProtection::Pmp { entries: 16 },
477 fpu: None,
478 }
479 }
480
481 pub fn riscv32imac() -> Self {
482 Self::riscv32("imac")
483 }
484
485 pub fn cortex_m55() -> Self {
487 Self {
488 family: ArchFamily::ArmCortexM,
489 triple: "thumbv8.1m.main-none-eabi".to_string(),
490 isa: IsaVariant::Thumb2,
491 mem_protection: MemProtection::Mpu { regions: 16 },
492 fpu: Some(FPUPrecision::Single),
493 }
494 }
495
496 pub fn from_triple(triple: &str) -> std::result::Result<Self, String> {
498 match triple {
499 "thumbv7m-none-eabi" | "cortex-m3" => Ok(Self::cortex_m3()),
500 "thumbv7em-none-eabi" | "cortex-m4" => Ok(Self::cortex_m4()),
501 "thumbv7em-none-eabihf" | "cortex-m4f" => Ok(Self::cortex_m4f()),
502 "cortex-m7" => Ok(Self::cortex_m7()),
503 "cortex-m7dp" => Ok(Self::cortex_m7dp()),
504 "thumbv8.1m.main-none-eabi" | "cortex-m55" => Ok(Self::cortex_m55()),
505 "armv7r-none-eabihf" | "cortex-r5" => Ok(Self::cortex_r5()),
506 "aarch64-none-elf" | "cortex-a53" => Ok(Self::cortex_a53()),
507 "riscv32imac-unknown-none-elf" | "riscv32imac" | "rv32imac" => {
511 Ok(Self::riscv32("imac"))
512 }
513 "riscv32imc" | "rv32imc" | "esp32c3" => Ok(Self::riscv32("imc")),
514 "riscv32im" | "rv32im" => Ok(Self::riscv32("im")),
515 "riscv32i" | "rv32i" => Ok(Self::riscv32("i")),
516 "riscv32gc" | "rv32gc" => Ok(Self::riscv32("gc")),
517 "riscv32" | "rv32" => Ok(Self::riscv32("imac")),
519 _ => Err(format!(
520 "unknown target triple: {triple}. Supported: \
521 cortex-m3, cortex-m4, cortex-m4f, cortex-m7, cortex-m7dp; \
522 rv32imac, rv32imc, rv32im, rv32i, rv32gc, esp32c3"
523 )),
524 }
525 }
526
527 pub fn has_fpu(&self) -> bool {
529 self.fpu.is_some()
530 }
531
532 pub fn is_thumb2(&self) -> bool {
534 self.isa == IsaVariant::Thumb2
535 }
536}
537
538#[cfg(test)]
539mod tests {
540 use super::*;
541
542 #[test]
547 fn test_rv32_short_target_names_218() {
548 for (name, ext) in [
549 ("rv32imac", "imac"),
550 ("rv32imc", "imc"),
551 ("rv32im", "im"),
552 ("rv32i", "i"),
553 ("rv32gc", "gc"),
554 ("esp32c3", "imc"), ("riscv32", "imac"), ("riscv32imac", "imac"),
557 ] {
558 let spec = TargetSpec::from_triple(name)
559 .unwrap_or_else(|e| panic!("RV32 target '{name}' must resolve: {e}"));
560 assert_eq!(spec.family, ArchFamily::RiscV, "{name}");
561 assert_eq!(
562 spec.isa,
563 IsaVariant::RiscV32 {
564 extensions: ext.to_string()
565 },
566 "{name}"
567 );
568 }
569 let err = TargetSpec::from_triple("rv99zzz").unwrap_err();
571 assert!(
572 err.contains("rv32imac"),
573 "error should list RV32 targets: {err}"
574 );
575 }
576
577 #[test]
578 fn test_target_spec_from_triple() {
579 let spec = TargetSpec::from_triple("cortex-m4").unwrap();
580 assert_eq!(spec.family, ArchFamily::ArmCortexM);
581 assert!(spec.is_thumb2());
582 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 8 });
583 assert!(!spec.has_fpu());
584 }
585
586 #[test]
587 fn test_target_spec_unknown() {
588 assert!(TargetSpec::from_triple("mips-unknown-none").is_err());
589 }
590
591 #[test]
592 fn test_cortex_m3() {
593 let spec = TargetSpec::from_triple("cortex-m3").unwrap();
594 assert_eq!(spec.triple, "thumbv7m-none-eabi");
595 assert!(!spec.has_fpu());
596 }
597
598 #[test]
599 fn test_cortex_m4f() {
600 let spec = TargetSpec::from_triple("cortex-m4f").unwrap();
601 assert_eq!(spec.triple, "thumbv7em-none-eabihf");
602 assert!(spec.has_fpu());
603 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
604 }
605
606 #[test]
607 fn test_cortex_m7dp() {
608 let spec = TargetSpec::from_triple("cortex-m7dp").unwrap();
609 assert!(spec.has_fpu());
610 assert_eq!(spec.fpu, Some(FPUPrecision::Double));
611 assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 16 });
612 }
613
614 #[test]
615 fn test_cortex_m7_has_fpu() {
616 let spec = TargetSpec::cortex_m7();
617 assert!(spec.has_fpu());
618 assert_eq!(spec.fpu, Some(FPUPrecision::Single));
619 }
620
621 #[test]
626 fn test_cortex_m3_isa_features() {
627 let m3 = CortexMVariant::M3;
628 assert!(!m3.has_dsp(), "M3 should not have DSP");
629 assert!(!m3.has_single_precision_fpu(), "M3 should not have FPU");
630 assert!(
631 !m3.has_double_precision_fpu(),
632 "M3 should not have double FPU"
633 );
634 assert!(!m3.has_trustzone(), "M3 should not have TrustZone");
635 assert!(!m3.has_helium(), "M3 should not have Helium");
636 }
637
638 #[test]
639 fn test_cortex_m4_isa_features() {
640 let m4 = CortexMVariant::M4;
641 assert!(m4.has_dsp(), "M4 should have DSP");
642 assert!(
643 !m4.has_single_precision_fpu(),
644 "M4 (no F) should not have FPU"
645 );
646 assert!(
647 !m4.has_double_precision_fpu(),
648 "M4 should not have double FPU"
649 );
650 }
651
652 #[test]
653 fn test_cortex_m4f_isa_features() {
654 let m4f = CortexMVariant::M4F;
655 assert!(m4f.has_dsp(), "M4F should have DSP");
656 assert!(
657 m4f.has_single_precision_fpu(),
658 "M4F should have single-precision FPU"
659 );
660 assert!(
661 !m4f.has_double_precision_fpu(),
662 "M4F should not have double FPU"
663 );
664 }
665
666 #[test]
667 fn test_cortex_m7dp_isa_features() {
668 let m7dp = CortexMVariant::M7DP;
669 assert!(m7dp.has_dsp(), "M7DP should have DSP");
670 assert!(
671 m7dp.has_single_precision_fpu(),
672 "M7DP should have single-precision FPU"
673 );
674 assert!(
675 m7dp.has_double_precision_fpu(),
676 "M7DP should have double-precision FPU"
677 );
678 }
679
680 #[test]
681 fn test_cortex_m33_isa_features() {
682 let m33 = CortexMVariant::M33;
683 assert!(m33.has_dsp(), "M33 should have DSP");
684 assert!(m33.has_trustzone(), "M33 should have TrustZone");
685 assert!(!m33.has_helium(), "M33 should not have Helium");
686 }
687
688 #[test]
689 fn test_cortex_m55_isa_features() {
690 let m55 = CortexMVariant::M55;
691 assert!(m55.has_dsp(), "M55 should have DSP");
692 assert!(
693 m55.has_single_precision_fpu(),
694 "M55 should have single-precision FPU"
695 );
696 assert!(m55.has_trustzone(), "M55 should have TrustZone");
697 assert!(m55.has_helium(), "M55 should have Helium");
698 }
699
700 #[test]
701 fn test_target_spec_fpu_precision_queries() {
702 let m3 = TargetSpec::cortex_m3();
703 assert!(!m3.has_single_precision_fpu(), "M3 spec has no FPU");
704 assert!(!m3.has_double_precision_fpu(), "M3 spec has no double FPU");
705
706 let m4f = TargetSpec::cortex_m4f();
707 assert!(m4f.has_single_precision_fpu(), "M4F spec has single FPU");
708 assert!(
709 !m4f.has_double_precision_fpu(),
710 "M4F spec has no double FPU"
711 );
712
713 let m7dp = TargetSpec::cortex_m7dp();
714 assert!(m7dp.has_single_precision_fpu(), "M7DP spec has single FPU");
715 assert!(m7dp.has_double_precision_fpu(), "M7DP spec has double FPU");
716 }
717
718 #[test]
719 fn test_cortex_m55_target_spec() {
720 let m55 = TargetSpec::cortex_m55();
721 assert_eq!(m55.family, ArchFamily::ArmCortexM);
722 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
723 assert!(m55.is_thumb2());
724 assert!(m55.has_fpu());
725 assert!(m55.has_single_precision_fpu());
726 assert_eq!(m55.mem_protection, MemProtection::Mpu { regions: 16 });
727 }
728
729 #[test]
730 fn test_cortex_m55_from_triple() {
731 let m55 = TargetSpec::from_triple("cortex-m55").unwrap();
732 assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
733 assert!(m55.has_fpu());
734
735 let m55_triple = TargetSpec::from_triple("thumbv8.1m.main-none-eabi").unwrap();
736 assert_eq!(m55_triple.triple, "thumbv8.1m.main-none-eabi");
737 }
738
739 #[test]
744 fn test_imxrt1062_capabilities() {
745 let caps = HardwareCapabilities::imxrt1062();
746 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7));
747 assert!(caps.has_mpu, "i.MX RT1062 has an MPU");
748 assert_eq!(caps.mpu_regions, 16, "M7 parts have 16 MPU regions");
749 assert!(!caps.has_pmp, "ARM parts do not have PMP");
750 assert_eq!(caps.pmp_entries, 0);
751 assert!(caps.has_fpu, "i.MX RT1062 has FPU");
752 assert_eq!(
753 caps.fpu_precision,
754 Some(FPUPrecision::Single),
755 "i.MX RT1062 has single-precision FPU"
756 );
757 assert!(!caps.has_simd);
758 assert!(caps.simd_level.is_none());
759 assert!(caps.xip_capable);
760 assert_eq!(caps.flash_size, 8 * 1024 * 1024, "8MB QSPI typical");
761 assert_eq!(caps.ram_size, 1024 * 1024, "1MB OCRAM");
762 }
763
764 #[test]
765 fn test_stm32h743_capabilities() {
766 let caps = HardwareCapabilities::stm32h743();
767 assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7DP));
768 assert!(caps.has_mpu);
769 assert_eq!(caps.mpu_regions, 16, "STM32H743 has 16 MPU regions");
770 assert!(!caps.has_pmp);
771 assert!(caps.has_fpu);
772 assert_eq!(
773 caps.fpu_precision,
774 Some(FPUPrecision::Double),
775 "STM32H743 has double-precision FPU"
776 );
777 assert_eq!(caps.flash_size, 2 * 1024 * 1024, "2MB Flash");
778 assert_eq!(caps.ram_size, 1024 * 1024, "1MB RAM total");
779 assert!(caps.xip_capable);
780 }
781
782 #[test]
783 fn test_imxrt1062_arch_target_triple() {
784 let caps = HardwareCapabilities::imxrt1062();
786 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
787 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
788 assert!(caps.arch.has_hardware_fp());
789 }
790
791 #[test]
792 fn test_stm32h743_arch_target_triple() {
793 let caps = HardwareCapabilities::stm32h743();
795 assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
796 assert_eq!(caps.arch.cpu_name(), "cortex-m7");
797 assert!(caps.arch.has_hardware_fp());
798 }
799
800 #[test]
801 fn test_m7_hardware_capabilities_distinct_from_m4() {
802 let m4 = HardwareCapabilities::nrf52840();
806 let m7 = HardwareCapabilities::imxrt1062();
807 let m7dp = HardwareCapabilities::stm32h743();
808 assert_eq!(m4.mpu_regions, 8);
809 assert_eq!(m7.mpu_regions, 16);
810 assert_eq!(m7dp.mpu_regions, 16);
811 assert_eq!(m4.fpu_precision, Some(FPUPrecision::Single));
813 assert_eq!(m7.fpu_precision, Some(FPUPrecision::Single));
814 assert_eq!(m7dp.fpu_precision, Some(FPUPrecision::Double));
815 }
816}