Skip to main content

synth_core/
target.rs

1//! Target architecture specifications
2
3use serde::{Deserialize, Serialize};
4
5/// Target architecture for synthesis
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub enum TargetArch {
8    /// ARM Cortex-M series
9    ARMCortexM(CortexMVariant),
10
11    /// RISC-V
12    RISCV(RISCVVariant),
13}
14
15/// ARM Cortex-M variants
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub enum CortexMVariant {
18    /// Cortex-M3 (ARMv7-M)
19    M3,
20
21    /// Cortex-M4 (ARMv7E-M)
22    M4,
23
24    /// Cortex-M4F (with FPU)
25    M4F,
26
27    /// Cortex-M7 (ARMv7E-M, high performance)
28    M7,
29
30    /// Cortex-M7 with double-precision FPU
31    M7DP,
32
33    /// Cortex-M33 (ARMv8-M Mainline with TrustZone)
34    M33,
35
36    /// Cortex-M55 (ARMv8.1-M with Helium MVE)
37    M55,
38}
39
40/// RISC-V variants
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub enum RISCVVariant {
43    /// RV32I (32-bit integer)
44    RV32I,
45
46    /// RV32IMAC (32-bit with multiply, atomic, compressed)
47    RV32IMAC,
48
49    /// RV32GC (32-bit general-purpose with compressed)
50    RV32GC,
51
52    /// RV64I (64-bit integer)
53    RV64I,
54
55    /// RV64IMAC (64-bit with multiply, atomic, compressed)
56    RV64IMAC,
57
58    /// RV64GC (64-bit general-purpose with compressed)
59    RV64GC,
60}
61
62/// Hardware capabilities
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct HardwareCapabilities {
65    /// Target architecture
66    pub arch: TargetArch,
67
68    /// Has Memory Protection Unit (MPU) for ARM
69    pub has_mpu: bool,
70
71    /// Number of MPU regions (typically 8 or 16)
72    pub mpu_regions: u8,
73
74    /// Has Physical Memory Protection (PMP) for RISC-V
75    pub has_pmp: bool,
76
77    /// Number of PMP entries (up to 16)
78    pub pmp_entries: u8,
79
80    /// Has Floating Point Unit
81    pub has_fpu: bool,
82
83    /// FPU precision
84    pub fpu_precision: Option<FPUPrecision>,
85
86    /// Has SIMD/vector extensions
87    pub has_simd: bool,
88
89    /// SIMD level
90    pub simd_level: Option<SIMDLevel>,
91
92    /// Can execute-in-place (XIP) from flash
93    pub xip_capable: bool,
94
95    /// Flash size in bytes
96    pub flash_size: u64,
97
98    /// RAM size in bytes
99    pub ram_size: u64,
100}
101
102/// FPU precision
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
104pub enum FPUPrecision {
105    /// Single precision (32-bit)
106    Single,
107
108    /// Double precision (64-bit)
109    Double,
110}
111
112/// SIMD level
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
114pub enum SIMDLevel {
115    /// ARM Helium (MVE - M-Profile Vector Extension)
116    Helium,
117
118    /// RISC-V Vector Extension
119    RISCVVector,
120
121    /// WebAssembly SIMD (128-bit)
122    WASMSIMD,
123}
124
125impl TargetArch {
126    /// Get the target triple string (for compilation)
127    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    /// Get CPU name for compiler flags
148    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    /// Check if target has FPU
167    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    /// Create capabilities for a typical Cortex-M4F
185    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, // 1MB
198            ram_size: 192 * 1024,    // 192KB
199        }
200    }
201
202    /// Create capabilities for Nordic nRF52840
203    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, // 1MB
216            ram_size: 256 * 1024,    // 256KB
217        }
218    }
219
220    /// Create capabilities for STM32F407
221    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, // 1MB
234            ram_size: 192 * 1024,    // 192KB (128KB + 64KB CCM)
235        }
236    }
237
238    /// Create capabilities for STM32H743 (Cortex-M7 with double-precision FPU)
239    ///
240    /// 16 MPU regions, 2MB Flash, 1MB RAM (DTCM + AXI SRAM + SRAM1-4).
241    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, // 2MB
254            ram_size: 1024 * 1024,       // 1MB total
255        }
256    }
257
258    /// Create capabilities for i.MX RT1062 (Cortex-M7 with single-precision FPU)
259    ///
260    /// Representative high-end M7 with 16 MPU regions, single-precision FPU,
261    /// large OCRAM, and external XIP-capable QuadSPI Flash. Matches the
262    /// configuration of safety-grade lockstepped M7 platforms used in
263    /// industrial and embedded automotive contexts.
264    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, // 8MB external QSPI flash (typical)
277            ram_size: 1024 * 1024,       // 1MB OCRAM (FlexRAM 512KB + OCRAM 512KB)
278        }
279    }
280}
281
282// ============================================================================
283// Multi-backend target specification
284// ============================================================================
285
286/// Target architecture family (broader than CortexMVariant — spans all ARM + RISC-V)
287#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
288pub enum ArchFamily {
289    ArmCortexM,
290    ArmCortexR,
291    ArmCortexA,
292    RiscV,
293}
294
295/// ISA variant details — determines instruction encoding
296#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
297pub enum IsaVariant {
298    /// Cortex-M0 (v6-M, Thumb only)
299    Thumb,
300    /// Cortex-M3/M4/M7 (v7-M, Thumb-2)
301    Thumb2,
302    /// Cortex-R (v7-R, full ARM + Thumb-2)
303    Arm32,
304    /// Cortex-A53+ (v8-A)
305    AArch64,
306    /// RISC-V 32-bit with extension string (e.g. "imac")
307    RiscV32 { extensions: String },
308    /// RISC-V 64-bit with extension string
309    RiscV64 { extensions: String },
310}
311
312/// Memory protection model
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314pub enum MemProtection {
315    /// ARM MPU (Cortex-M, Cortex-R)
316    Mpu { regions: u8 },
317    /// ARM MMU (Cortex-A)
318    Mmu,
319    /// RISC-V Physical Memory Protection
320    Pmp { entries: u8 },
321    /// No hardware protection
322    SoftwareOnly,
323}
324
325/// Full target specification for multi-backend compilation
326#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
327pub struct TargetSpec {
328    /// Architecture family
329    pub family: ArchFamily,
330    /// LLVM-style triple (e.g. "thumbv7em-none-eabihf")
331    pub triple: String,
332    /// ISA variant details
333    pub isa: IsaVariant,
334    /// Memory protection model
335    pub mem_protection: MemProtection,
336    /// Floating-point unit capability (None = soft-float only)
337    pub fpu: Option<FPUPrecision>,
338}
339
340// ============================================================================
341// ISA feature gating — determines which instructions each variant supports
342// ============================================================================
343
344impl CortexMVariant {
345    /// Returns whether this variant supports DSP instructions (SSAT, USAT, SMLAL, etc.)
346    /// Only ARMv7E-M (Cortex-M4+) and above have DSP extensions.
347    pub fn has_dsp(&self) -> bool {
348        !matches!(self, Self::M3)
349    }
350
351    /// Returns whether this variant has a single-precision FPU
352    pub fn has_single_precision_fpu(&self) -> bool {
353        matches!(self, Self::M4F | Self::M7 | Self::M7DP | Self::M55)
354    }
355
356    /// Returns whether this variant has a double-precision FPU
357    pub fn has_double_precision_fpu(&self) -> bool {
358        matches!(self, Self::M7DP)
359    }
360
361    /// Returns whether this variant supports TrustZone security extensions
362    pub fn has_trustzone(&self) -> bool {
363        matches!(self, Self::M33 | Self::M55)
364    }
365
366    /// Returns whether this variant supports Helium MVE (M-Profile Vector Extension)
367    pub fn has_helium(&self) -> bool {
368        matches!(self, Self::M55)
369    }
370}
371
372impl TargetSpec {
373    /// Returns whether this target has a single-precision FPU
374    pub fn has_single_precision_fpu(&self) -> bool {
375        matches!(
376            self.fpu,
377            Some(FPUPrecision::Single) | Some(FPUPrecision::Double)
378        )
379    }
380
381    /// Returns whether this target has a double-precision FPU
382    pub fn has_double_precision_fpu(&self) -> bool {
383        matches!(self.fpu, Some(FPUPrecision::Double))
384    }
385}
386
387impl TargetSpec {
388    /// Cortex-M3 (ARMv7-M, no FPU, 8 MPU regions)
389    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    /// Cortex-M4 with 8 MPU regions (no FPU — M4 without F suffix)
400    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    /// Cortex-M4F with single-precision FPU
411    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    /// Cortex-M7 with single-precision FPU and 16 MPU regions
422    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    /// Cortex-M7 with double-precision FPU and 16 MPU regions
433    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    /// Cortex-R5 with 12 MPU regions
444    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    /// Cortex-A53 with MMU
455    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    /// RISC-V RV32IMAC with 16 PMP entries
466    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    /// Cortex-M55 with Helium MVE, single-precision FPU, TrustZone
479    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    /// Parse from an LLVM triple or shorthand name
490    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    /// Whether this target has a floating-point unit
506    pub fn has_fpu(&self) -> bool {
507        self.fpu.is_some()
508    }
509
510    /// Whether this target uses Thumb-2 encoding (Cortex-M)
511    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    // ========================================================================
565    // ISA feature gating tests
566    // ========================================================================
567
568    #[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    // ========================================================================
683    // M7 hardware constructor tests (PR #86 patch coverage)
684    // ========================================================================
685
686    #[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        // imxrt1062 uses the M7 variant, which maps to thumbv7em-none-eabihf.
728        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        // stm32h743 uses M7DP — same triple as M7 but flagged as double-precision.
737        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        // Regression: M7 hardware must report 16 regions, M4 must report 8.
746        // Previously the CLI's --hardware dispatch silently fell through to
747        // a wrong default — this guards the constructor selection.
748        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        // M7DP must report Double, M7 must report Single, M4F must report Single.
755        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}