use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TargetArch {
ARMCortexM(CortexMVariant),
RISCV(RISCVVariant),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum CortexMVariant {
M3,
M4,
M4F,
M7,
M7DP,
M33,
M55,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum RISCVVariant {
RV32I,
RV32IMAC,
RV32GC,
RV64I,
RV64IMAC,
RV64GC,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HardwareCapabilities {
pub arch: TargetArch,
pub has_mpu: bool,
pub mpu_regions: u8,
pub has_pmp: bool,
pub pmp_entries: u8,
pub has_fpu: bool,
pub fpu_precision: Option<FPUPrecision>,
pub has_simd: bool,
pub simd_level: Option<SIMDLevel>,
pub xip_capable: bool,
pub flash_size: u64,
pub ram_size: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FPUPrecision {
Single,
Double,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SIMDLevel {
Helium,
RISCVVector,
WASMSIMD,
}
impl TargetArch {
pub fn target_triple(&self) -> &str {
match self {
TargetArch::ARMCortexM(variant) => match variant {
CortexMVariant::M3 => "thumbv7m-none-eabi",
CortexMVariant::M4 | CortexMVariant::M4F => "thumbv7em-none-eabi",
CortexMVariant::M7 | CortexMVariant::M7DP => "thumbv7em-none-eabihf",
CortexMVariant::M33 => "thumbv8m.main-none-eabi",
CortexMVariant::M55 => "thumbv8.1m.main-none-eabi",
},
TargetArch::RISCV(variant) => match variant {
RISCVVariant::RV32I | RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => {
"riscv32imac-unknown-none-elf"
}
RISCVVariant::RV64I | RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => {
"riscv64gc-unknown-none-elf"
}
},
}
}
pub fn cpu_name(&self) -> &str {
match self {
TargetArch::ARMCortexM(variant) => match variant {
CortexMVariant::M3 => "cortex-m3",
CortexMVariant::M4 | CortexMVariant::M4F => "cortex-m4",
CortexMVariant::M7 | CortexMVariant::M7DP => "cortex-m7",
CortexMVariant::M33 => "cortex-m33",
CortexMVariant::M55 => "cortex-m55",
},
TargetArch::RISCV(variant) => match variant {
RISCVVariant::RV32I => "generic-rv32",
RISCVVariant::RV32IMAC | RISCVVariant::RV32GC => "generic-rv32",
RISCVVariant::RV64I => "generic-rv64",
RISCVVariant::RV64IMAC | RISCVVariant::RV64GC => "generic-rv64",
},
}
}
pub fn has_hardware_fp(&self) -> bool {
match self {
TargetArch::ARMCortexM(variant) => matches!(
variant,
CortexMVariant::M4F
| CortexMVariant::M7
| CortexMVariant::M7DP
| CortexMVariant::M55
),
TargetArch::RISCV(variant) => {
matches!(variant, RISCVVariant::RV32GC | RISCVVariant::RV64GC)
}
}
}
}
impl HardwareCapabilities {
pub fn cortex_m4f_typical() -> Self {
Self {
arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
has_mpu: true,
mpu_regions: 8,
has_pmp: false,
pmp_entries: 0,
has_fpu: true,
fpu_precision: Some(FPUPrecision::Single),
has_simd: false,
simd_level: None,
xip_capable: true,
flash_size: 1024 * 1024, ram_size: 192 * 1024, }
}
pub fn nrf52840() -> Self {
Self {
arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
has_mpu: true,
mpu_regions: 8,
has_pmp: false,
pmp_entries: 0,
has_fpu: true,
fpu_precision: Some(FPUPrecision::Single),
has_simd: false,
simd_level: None,
xip_capable: true,
flash_size: 1024 * 1024, ram_size: 256 * 1024, }
}
pub fn stm32f407() -> Self {
Self {
arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
has_mpu: true,
mpu_regions: 8,
has_pmp: false,
pmp_entries: 0,
has_fpu: true,
fpu_precision: Some(FPUPrecision::Single),
has_simd: false,
simd_level: None,
xip_capable: true,
flash_size: 1024 * 1024, ram_size: 192 * 1024, }
}
pub fn stm32h743() -> Self {
Self {
arch: TargetArch::ARMCortexM(CortexMVariant::M7DP),
has_mpu: true,
mpu_regions: 16,
has_pmp: false,
pmp_entries: 0,
has_fpu: true,
fpu_precision: Some(FPUPrecision::Double),
has_simd: false,
simd_level: None,
xip_capable: true,
flash_size: 2 * 1024 * 1024, ram_size: 1024 * 1024, }
}
pub fn imxrt1062() -> Self {
Self {
arch: TargetArch::ARMCortexM(CortexMVariant::M7),
has_mpu: true,
mpu_regions: 16,
has_pmp: false,
pmp_entries: 0,
has_fpu: true,
fpu_precision: Some(FPUPrecision::Single),
has_simd: false,
simd_level: None,
xip_capable: true,
flash_size: 8 * 1024 * 1024, ram_size: 1024 * 1024, }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ArchFamily {
ArmCortexM,
ArmCortexR,
ArmCortexA,
RiscV,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum IsaVariant {
Thumb,
Thumb2,
Arm32,
AArch64,
RiscV32 { extensions: String },
RiscV64 { extensions: String },
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MemProtection {
Mpu { regions: u8 },
Mmu,
Pmp { entries: u8 },
SoftwareOnly,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TargetSpec {
pub family: ArchFamily,
pub triple: String,
pub isa: IsaVariant,
pub mem_protection: MemProtection,
pub fpu: Option<FPUPrecision>,
}
impl CortexMVariant {
pub fn has_dsp(&self) -> bool {
!matches!(self, Self::M3)
}
pub fn has_single_precision_fpu(&self) -> bool {
matches!(self, Self::M4F | Self::M7 | Self::M7DP | Self::M55)
}
pub fn has_double_precision_fpu(&self) -> bool {
matches!(self, Self::M7DP)
}
pub fn has_trustzone(&self) -> bool {
matches!(self, Self::M33 | Self::M55)
}
pub fn has_helium(&self) -> bool {
matches!(self, Self::M55)
}
}
impl TargetSpec {
pub fn has_single_precision_fpu(&self) -> bool {
matches!(
self.fpu,
Some(FPUPrecision::Single) | Some(FPUPrecision::Double)
)
}
pub fn has_double_precision_fpu(&self) -> bool {
matches!(self.fpu, Some(FPUPrecision::Double))
}
}
impl TargetSpec {
pub fn cortex_m3() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv7m-none-eabi".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 8 },
fpu: None,
}
}
pub fn cortex_m4() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv7em-none-eabi".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 8 },
fpu: None,
}
}
pub fn cortex_m4f() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv7em-none-eabihf".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 8 },
fpu: Some(FPUPrecision::Single),
}
}
pub fn cortex_m7() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv7em-none-eabihf".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 16 },
fpu: Some(FPUPrecision::Single),
}
}
pub fn cortex_m7dp() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv7em-none-eabihf".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 16 },
fpu: Some(FPUPrecision::Double),
}
}
pub fn cortex_r5() -> Self {
Self {
family: ArchFamily::ArmCortexR,
triple: "armv7r-none-eabihf".to_string(),
isa: IsaVariant::Arm32,
mem_protection: MemProtection::Mpu { regions: 12 },
fpu: None,
}
}
pub fn cortex_a53() -> Self {
Self {
family: ArchFamily::ArmCortexA,
triple: "aarch64-none-elf".to_string(),
isa: IsaVariant::AArch64,
mem_protection: MemProtection::Mmu,
fpu: None,
}
}
pub fn riscv32imac() -> Self {
Self {
family: ArchFamily::RiscV,
triple: "riscv32imac-unknown-none-elf".to_string(),
isa: IsaVariant::RiscV32 {
extensions: "imac".to_string(),
},
mem_protection: MemProtection::Pmp { entries: 16 },
fpu: None,
}
}
pub fn cortex_m55() -> Self {
Self {
family: ArchFamily::ArmCortexM,
triple: "thumbv8.1m.main-none-eabi".to_string(),
isa: IsaVariant::Thumb2,
mem_protection: MemProtection::Mpu { regions: 16 },
fpu: Some(FPUPrecision::Single),
}
}
pub fn from_triple(triple: &str) -> std::result::Result<Self, String> {
match triple {
"thumbv7m-none-eabi" | "cortex-m3" => Ok(Self::cortex_m3()),
"thumbv7em-none-eabi" | "cortex-m4" => Ok(Self::cortex_m4()),
"thumbv7em-none-eabihf" | "cortex-m4f" => Ok(Self::cortex_m4f()),
"cortex-m7" => Ok(Self::cortex_m7()),
"cortex-m7dp" => Ok(Self::cortex_m7dp()),
"thumbv8.1m.main-none-eabi" | "cortex-m55" => Ok(Self::cortex_m55()),
"armv7r-none-eabihf" | "cortex-r5" => Ok(Self::cortex_r5()),
"aarch64-none-elf" | "cortex-a53" => Ok(Self::cortex_a53()),
"riscv32imac-unknown-none-elf" | "riscv32imac" => Ok(Self::riscv32imac()),
_ => Err(format!("unknown target triple: {}", triple)),
}
}
pub fn has_fpu(&self) -> bool {
self.fpu.is_some()
}
pub fn is_thumb2(&self) -> bool {
self.isa == IsaVariant::Thumb2
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_target_spec_from_triple() {
let spec = TargetSpec::from_triple("cortex-m4").unwrap();
assert_eq!(spec.family, ArchFamily::ArmCortexM);
assert!(spec.is_thumb2());
assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 8 });
assert!(!spec.has_fpu());
}
#[test]
fn test_target_spec_unknown() {
assert!(TargetSpec::from_triple("mips-unknown-none").is_err());
}
#[test]
fn test_cortex_m3() {
let spec = TargetSpec::from_triple("cortex-m3").unwrap();
assert_eq!(spec.triple, "thumbv7m-none-eabi");
assert!(!spec.has_fpu());
}
#[test]
fn test_cortex_m4f() {
let spec = TargetSpec::from_triple("cortex-m4f").unwrap();
assert_eq!(spec.triple, "thumbv7em-none-eabihf");
assert!(spec.has_fpu());
assert_eq!(spec.fpu, Some(FPUPrecision::Single));
}
#[test]
fn test_cortex_m7dp() {
let spec = TargetSpec::from_triple("cortex-m7dp").unwrap();
assert!(spec.has_fpu());
assert_eq!(spec.fpu, Some(FPUPrecision::Double));
assert_eq!(spec.mem_protection, MemProtection::Mpu { regions: 16 });
}
#[test]
fn test_cortex_m7_has_fpu() {
let spec = TargetSpec::cortex_m7();
assert!(spec.has_fpu());
assert_eq!(spec.fpu, Some(FPUPrecision::Single));
}
#[test]
fn test_cortex_m3_isa_features() {
let m3 = CortexMVariant::M3;
assert!(!m3.has_dsp(), "M3 should not have DSP");
assert!(!m3.has_single_precision_fpu(), "M3 should not have FPU");
assert!(
!m3.has_double_precision_fpu(),
"M3 should not have double FPU"
);
assert!(!m3.has_trustzone(), "M3 should not have TrustZone");
assert!(!m3.has_helium(), "M3 should not have Helium");
}
#[test]
fn test_cortex_m4_isa_features() {
let m4 = CortexMVariant::M4;
assert!(m4.has_dsp(), "M4 should have DSP");
assert!(
!m4.has_single_precision_fpu(),
"M4 (no F) should not have FPU"
);
assert!(
!m4.has_double_precision_fpu(),
"M4 should not have double FPU"
);
}
#[test]
fn test_cortex_m4f_isa_features() {
let m4f = CortexMVariant::M4F;
assert!(m4f.has_dsp(), "M4F should have DSP");
assert!(
m4f.has_single_precision_fpu(),
"M4F should have single-precision FPU"
);
assert!(
!m4f.has_double_precision_fpu(),
"M4F should not have double FPU"
);
}
#[test]
fn test_cortex_m7dp_isa_features() {
let m7dp = CortexMVariant::M7DP;
assert!(m7dp.has_dsp(), "M7DP should have DSP");
assert!(
m7dp.has_single_precision_fpu(),
"M7DP should have single-precision FPU"
);
assert!(
m7dp.has_double_precision_fpu(),
"M7DP should have double-precision FPU"
);
}
#[test]
fn test_cortex_m33_isa_features() {
let m33 = CortexMVariant::M33;
assert!(m33.has_dsp(), "M33 should have DSP");
assert!(m33.has_trustzone(), "M33 should have TrustZone");
assert!(!m33.has_helium(), "M33 should not have Helium");
}
#[test]
fn test_cortex_m55_isa_features() {
let m55 = CortexMVariant::M55;
assert!(m55.has_dsp(), "M55 should have DSP");
assert!(
m55.has_single_precision_fpu(),
"M55 should have single-precision FPU"
);
assert!(m55.has_trustzone(), "M55 should have TrustZone");
assert!(m55.has_helium(), "M55 should have Helium");
}
#[test]
fn test_target_spec_fpu_precision_queries() {
let m3 = TargetSpec::cortex_m3();
assert!(!m3.has_single_precision_fpu(), "M3 spec has no FPU");
assert!(!m3.has_double_precision_fpu(), "M3 spec has no double FPU");
let m4f = TargetSpec::cortex_m4f();
assert!(m4f.has_single_precision_fpu(), "M4F spec has single FPU");
assert!(
!m4f.has_double_precision_fpu(),
"M4F spec has no double FPU"
);
let m7dp = TargetSpec::cortex_m7dp();
assert!(m7dp.has_single_precision_fpu(), "M7DP spec has single FPU");
assert!(m7dp.has_double_precision_fpu(), "M7DP spec has double FPU");
}
#[test]
fn test_cortex_m55_target_spec() {
let m55 = TargetSpec::cortex_m55();
assert_eq!(m55.family, ArchFamily::ArmCortexM);
assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
assert!(m55.is_thumb2());
assert!(m55.has_fpu());
assert!(m55.has_single_precision_fpu());
assert_eq!(m55.mem_protection, MemProtection::Mpu { regions: 16 });
}
#[test]
fn test_cortex_m55_from_triple() {
let m55 = TargetSpec::from_triple("cortex-m55").unwrap();
assert_eq!(m55.triple, "thumbv8.1m.main-none-eabi");
assert!(m55.has_fpu());
let m55_triple = TargetSpec::from_triple("thumbv8.1m.main-none-eabi").unwrap();
assert_eq!(m55_triple.triple, "thumbv8.1m.main-none-eabi");
}
#[test]
fn test_imxrt1062_capabilities() {
let caps = HardwareCapabilities::imxrt1062();
assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7));
assert!(caps.has_mpu, "i.MX RT1062 has an MPU");
assert_eq!(caps.mpu_regions, 16, "M7 parts have 16 MPU regions");
assert!(!caps.has_pmp, "ARM parts do not have PMP");
assert_eq!(caps.pmp_entries, 0);
assert!(caps.has_fpu, "i.MX RT1062 has FPU");
assert_eq!(
caps.fpu_precision,
Some(FPUPrecision::Single),
"i.MX RT1062 has single-precision FPU"
);
assert!(!caps.has_simd);
assert!(caps.simd_level.is_none());
assert!(caps.xip_capable);
assert_eq!(caps.flash_size, 8 * 1024 * 1024, "8MB QSPI typical");
assert_eq!(caps.ram_size, 1024 * 1024, "1MB OCRAM");
}
#[test]
fn test_stm32h743_capabilities() {
let caps = HardwareCapabilities::stm32h743();
assert_eq!(caps.arch, TargetArch::ARMCortexM(CortexMVariant::M7DP));
assert!(caps.has_mpu);
assert_eq!(caps.mpu_regions, 16, "STM32H743 has 16 MPU regions");
assert!(!caps.has_pmp);
assert!(caps.has_fpu);
assert_eq!(
caps.fpu_precision,
Some(FPUPrecision::Double),
"STM32H743 has double-precision FPU"
);
assert_eq!(caps.flash_size, 2 * 1024 * 1024, "2MB Flash");
assert_eq!(caps.ram_size, 1024 * 1024, "1MB RAM total");
assert!(caps.xip_capable);
}
#[test]
fn test_imxrt1062_arch_target_triple() {
let caps = HardwareCapabilities::imxrt1062();
assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
assert_eq!(caps.arch.cpu_name(), "cortex-m7");
assert!(caps.arch.has_hardware_fp());
}
#[test]
fn test_stm32h743_arch_target_triple() {
let caps = HardwareCapabilities::stm32h743();
assert_eq!(caps.arch.target_triple(), "thumbv7em-none-eabihf");
assert_eq!(caps.arch.cpu_name(), "cortex-m7");
assert!(caps.arch.has_hardware_fp());
}
#[test]
fn test_m7_hardware_capabilities_distinct_from_m4() {
let m4 = HardwareCapabilities::nrf52840();
let m7 = HardwareCapabilities::imxrt1062();
let m7dp = HardwareCapabilities::stm32h743();
assert_eq!(m4.mpu_regions, 8);
assert_eq!(m7.mpu_regions, 16);
assert_eq!(m7dp.mpu_regions, 16);
assert_eq!(m4.fpu_precision, Some(FPUPrecision::Single));
assert_eq!(m7.fpu_precision, Some(FPUPrecision::Single));
assert_eq!(m7dp.fpu_precision, Some(FPUPrecision::Double));
}
}