use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CortexR {
R4,
R5,
R7,
R8,
R52,
R52Plus,
R82,
}
impl CortexR {
pub fn target_triple(&self) -> &'static str {
match self {
Self::R4 | Self::R5 => "armv7r-none-eabi",
Self::R7 => "armv7r-none-eabihf",
Self::R8 => "armv7r-none-eabi",
Self::R52 | Self::R52Plus => "armv8r-none-eabi",
Self::R82 => "armv8r-none-eabi",
}
}
pub fn cpu_flag(&self) -> &'static str {
match self {
Self::R4 => "cortex-r4", Self::R5 => "cortex-r5",
Self::R7 => "cortex-r7", Self::R8 => "cortex-r8",
Self::R52 => "cortex-r52", Self::R52Plus => "cortex-r52+",
Self::R82 => "cortex-r82",
}
}
pub fn has_fpu(&self) -> bool {
matches!(self, Self::R5 | Self::R7 | Self::R52 | Self::R52Plus | Self::R82)
}
pub fn has_tcm(&self) -> bool {
true
}
pub fn mpu_regions(&self) -> usize {
match self {
Self::R4 | Self::R5 => 12,
Self::R7 | Self::R8 => 16,
Self::R52 | Self::R52Plus | Self::R82 => 24,
}
}
}
#[derive(Debug, Clone)]
pub struct TcmConfig {
pub itcm_base: u64,
pub itcm_size: u64,
pub dtcm_base: u64,
pub dtcm_size: u64,
pub enable_ecc: bool,
}
impl TcmConfig {
pub fn new() -> Self {
Self { itcm_base: 0x00000000, itcm_size: 0x10000,
dtcm_base: 0x20000000, dtcm_size: 0x10000, enable_ecc: true }
}
pub fn generate_linker_script_section(&self) -> String {
let mut script = String::new();
script.push_str(" .itcm : {\n");
script.push_str(&format!(" . = ALIGN(4);\n"));
script.push_str(&format!(" *(.itcm .itcm.*)\n"));
script.push_str(" } > ITCM\n\n");
script.push_str(" .dtcm : {\n");
script.push_str(" *(.dtcm .dtcm.*)\n");
script.push_str(" *(.bss .bss.*)\n");
script.push_str(" } > DTCM\n");
script
}
}
#[derive(Debug, Clone)]
pub struct CortexRMpuRegion {
pub base_address: u64,
pub size: u64,
pub access_permissions: CortexRAccessPermission,
pub attributes: CortexRMemAttributes,
pub enabled: bool,
pub sub_region_disable: u8,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CortexRAccessPermission {
NoAccess,
PrivilegedReadWrite,
PrivilegedReadWriteUserReadOnly,
FullAccess,
PrivilegedReadOnly,
ReadOnly,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CortexRMemAttributes {
StronglyOrdered,
Device,
Normal { cacheable: bool, bufferable: bool, shared: bool },
NormalNonCacheable,
WriteThrough,
WriteBack,
}
impl CortexRMpuRegion {
pub fn encode_region(&self, region_num: u8) -> u32 {
let mut reg = 0u32;
reg |= (region_num as u32) & 0xF; if self.enabled { reg |= 1 << 4; } let size_bits = (self.size.trailing_zeros().saturating_sub(5)) & 0x1F;
reg |= (size_bits & 0x1F) << 5;
let ap = match self.access_permissions {
CortexRAccessPermission::NoAccess => 0b000,
CortexRAccessPermission::PrivilegedReadWrite => 0b001,
CortexRAccessPermission::PrivilegedReadWriteUserReadOnly => 0b010,
CortexRAccessPermission::FullAccess => 0b011,
CortexRAccessPermission::PrivilegedReadOnly => 0b101,
CortexRAccessPermission::ReadOnly => 0b110,
};
reg |= (ap & 0x7) << 13;
reg |= (self.sub_region_disable as u32) << 8;
reg
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RiscVPmpEntry {
pub address: u64,
pub addressing_mode: PmpAddressingMode,
pub permissions: PmpPermission,
pub locked: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PmpAddressingMode {
Off,
Tor,
Na4,
Napot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PmpPermission {
None,
Read,
Write,
ReadWrite,
Execute,
ReadExecute,
WriteExecute,
ReadWriteExecute,
}
impl PmpPermission {
pub fn encode(&self) -> u8 {
match self {
Self::None => 0b000,
Self::Read => 0b001,
Self::Write => 0b010,
Self::ReadWrite => 0b011,
Self::Execute => 0b100,
Self::ReadExecute => 0b101,
Self::WriteExecute => 0b110,
Self::ReadWriteExecute => 0b111,
}
}
}
impl RiscVPmpEntry {
pub fn encode_cfg(&self) -> u8 {
let mut cfg = self.permissions.encode();
match self.addressing_mode {
PmpAddressingMode::Off => {},
PmpAddressingMode::Tor => { cfg |= 0x08; }
PmpAddressingMode::Na4 => { cfg |= 0x10; }
PmpAddressingMode::Napot => { cfg |= 0x18; }
}
if self.locked { cfg |= 0x80; }
cfg
}
pub fn encode_addr(&self) -> u64 {
match self.addressing_mode {
PmpAddressingMode::Napot => {
self.address >> 2
}
PmpAddressingMode::Na4 => self.address >> 2,
PmpAddressingMode::Tor => self.address >> 2,
PmpAddressingMode::Off => 0,
}
}
}
#[derive(Debug, Clone)]
pub struct RiscVPmpConfig {
pub entries: Vec<RiscVPmpEntry>,
pub num_regions: usize,
pub grain_size: u8,
}
impl RiscVPmpConfig {
pub fn new(num_regions: usize) -> Self {
Self { entries: Vec::with_capacity(num_regions), num_regions, grain_size: 4 }
}
pub fn add_region(&mut self, entry: RiscVPmpEntry) {
if self.entries.len() < self.num_regions {
self.entries.push(entry);
}
}
pub fn generate_init_code(&self) -> String {
let mut asm = String::new();
asm.push_str("# RISC-V PMP initialization\n");
for (i, entry) in self.entries.iter().enumerate() {
if i % 4 == 0 {
let pmpcfg_idx = i / 4;
asm.push_str(&format!("# Configure PMP entries {}-{}\n", i, i + 4));
let mut cfg_byte = 0u32;
for j in 0..4 {
let idx = i + j;
if idx < self.entries.len() {
cfg_byte |= (self.entries[idx].encode_cfg() as u32) << (j * 8);
}
}
asm.push_str(&format!("li t0, 0x{:08x}\n", cfg_byte));
asm.push_str(&format!("csrw pmpcfg{}, t0\n", pmpcfg_idx));
}
asm.push_str(&format!("li t0, 0x{:016x}\n", entry.encode_addr()));
asm.push_str(&format!("csrw pmpaddr{}, t0\n", i));
}
asm
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrustZoneWorld {
Secure,
NonSecure,
}
#[derive(Debug, Clone)]
pub struct TrustZoneState {
pub current_world: TrustZoneWorld,
pub secure_monitor_call_enabled: bool,
pub ns_access_to_scb: bool,
pub idau_regions: Vec<IdauRegion>,
}
#[derive(Debug, Clone)]
pub struct IdauRegion {
pub base_address: u64,
pub size: u64,
pub security: TrustZoneWorld,
pub attribute: SauRegionAttribute,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SauRegionAttribute {
Secure,
NonSecureCallable,
NonSecure,
}
impl TrustZoneState {
pub fn new() -> Self {
Self { current_world: TrustZoneWorld::Secure,
secure_monitor_call_enabled: true,
ns_access_to_scb: false, idau_regions: Vec::new() }
}
pub fn smc_call_template(&self, function_id: u32) -> String {
format!(
" // SMC call — transition to {}\n mov r0, #{}\n smc #0\n bx lr\n",
match self.current_world {
TrustZoneWorld::Secure => "Secure world",
TrustZoneWorld::NonSecure => "Non-secure world",
},
function_id
)
}
pub fn enter_non_secure(&mut self) {
self.current_world = TrustZoneWorld::NonSecure;
}
pub fn return_to_secure(&mut self) {
self.current_world = TrustZoneWorld::Secure;
}
}
#[derive(Debug, Clone)]
pub struct RiscvTeeConfig {
pub enclave_memory_base: u64,
pub enclave_memory_size: u64,
pub security_monitor_base: u64,
pub pmp_config: RiscVPmpConfig,
pub attestation_enabled: bool,
pub sealed_storage_enabled: bool,
}
impl RiscvTeeConfig {
pub fn new(enclave_base: u64, enclave_size: u64) -> Self {
let mut pmp = RiscVPmpConfig::new(16);
pmp.add_region(RiscVPmpEntry {
address: 0x80000000, addressing_mode: PmpAddressingMode::Napot,
permissions: PmpPermission::ReadExecute, locked: true,
});
pmp.add_region(RiscVPmpEntry {
address: enclave_base, addressing_mode: PmpAddressingMode::Napot,
permissions: PmpPermission::ReadWriteExecute, locked: true,
});
Self { enclave_memory_base: enclave_base, enclave_memory_size: enclave_size,
security_monitor_base: 0x80000000, pmp_config: pmp,
attestation_enabled: true, sealed_storage_enabled: true }
}
pub fn tee_init_code(&self) -> String {
let mut code = String::new();
code.push_str("# RISC-V TEE Initialization\n");
code.push_str("# 1. Configure PMP for security monitor and enclave\n");
code.push_str(&self.pmp_config.generate_init_code());
code.push_str("# 2. Set up security monitor entry point\n");
code.push_str(&format!("la t0, 0x{:016x}\n", self.security_monitor_base));
code.push_str("csrw mtvec, t0\n");
code.push_str("# 3. Enable machine-mode interrupts\n");
code.push_str("li t0, 0x880\n");
code.push_str("csrs mstatus, t0\n");
code
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Msp430Variant {
Msp430G2553,
Msp430F5529,
Msp430FR5969,
Msp430FR6989,
Msp430FR2355,
Msp430FR2476,
Msp430FR4133,
Msp430i2041,
Msp430F1611,
Msp430F5438,
Msp430F6659,
}
impl Msp430Variant {
pub fn target_triple(&self) -> &'static str { "msp430-none-elf" }
pub fn cpu_flag(&self) -> &'static str {
match self {
Self::Msp430G2553 => "msp430g2553",
Self::Msp430F5529 => "msp430f5529",
Self::Msp430FR5969 | Self::Msp430FR6989 => "msp430fr5969",
_ => "msp430",
}
}
pub fn ram_bytes(&self) -> u32 {
match self {
Self::Msp430G2553 => 512,
Self::Msp430F5529 => 8192,
Self::Msp430FR5969 => 2048,
Self::Msp430FR6989 => 2048,
Self::Msp430F5438 => 16384,
Self::Msp430F6659 => 16384,
_ => 4096,
}
}
pub fn flash_bytes(&self) -> u32 {
match self {
Self::Msp430G2553 => 16384,
Self::Msp430F5529 => 131072,
Self::Msp430FR5969 => 65536,
Self::Msp430F5438 => 262144,
_ => 65536,
}
}
pub fn is_fram(&self) -> bool {
matches!(self, Self::Msp430FR5969 | Self::Msp430FR6989 | Self::Msp430FR2355
| Self::Msp430FR2476 | Self::Msp430FR4133)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PicArch {
Pic16,
Pic18,
Pic24,
DsPic30,
DsPic33,
Pic32Mx,
Pic32Mz,
Pic32Mm,
Pic32Mk,
}
impl PicArch {
pub fn bit_width(&self) -> u32 {
match self {
Self::Pic16 | Self::Pic18 => 8,
Self::Pic24 | Self::DsPic30 | Self::DsPic33 => 16,
Self::Pic32Mx | Self::Pic32Mz | Self::Pic32Mm | Self::Pic32Mk => 32,
}
}
pub fn is_harvard(&self) -> bool {
!matches!(self, Self::Pic32Mx | Self::Pic32Mz | Self::Pic32Mm | Self::Pic32Mk)
}
pub fn has_dsp(&self) -> bool {
matches!(self, Self::DsPic30 | Self::DsPic33)
}
pub fn memory_regions(&self) -> Vec<PicMemoryRegion> {
match self {
Self::Pic16 => vec![
PicMemoryRegion { name: "Program".into(), base: 0x0000, size: 0x2000, kind: PicMemoryKind::Flash },
PicMemoryRegion { name: "RAM".into(), base: 0x0020, size: 0x0100, kind: PicMemoryKind::Ram },
PicMemoryRegion { name: "EEPROM".into(), base: 0x2100, size: 0x0100, kind: PicMemoryKind::Eeprom },
],
Self::Pic32Mx => vec![
PicMemoryRegion { name: "Program Flash".into(), base: 0x1D000000, size: 0x80000, kind: PicMemoryKind::Flash },
PicMemoryRegion { name: "Boot Flash".into(), base: 0x1FC00000, size: 0x3000, kind: PicMemoryKind::Flash },
PicMemoryRegion { name: "Data RAM".into(), base: 0x00000000, size: 0x8000, kind: PicMemoryKind::Ram },
],
_ => vec![],
}
}
}
#[derive(Debug, Clone)]
pub struct PicMemoryRegion {
pub name: String,
pub base: u64,
pub size: u64,
pub kind: PicMemoryKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PicMemoryKind { Flash, Ram, Eeprom, ConfigWords, Peripheral }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenesasRxVariant {
Rx111, Rx113, Rx130, Rx13T,
Rx231, Rx23T, Rx23W,
Rx64M, Rx65N, Rx66T, Rx671,
Rx72M, Rx72N, Rx72T,
}
impl RenesasRxVariant {
pub fn cpu_type(&self) -> &str {
match self {
Self::Rx111 | Self::Rx113 => "RXv1",
Self::Rx130 | Self::Rx13T | Self::Rx231 | Self::Rx23T | Self::Rx23W => "RXv2",
Self::Rx64M | Self::Rx65N | Self::Rx66T | Self::Rx671 => "RXv2",
Self::Rx72M | Self::Rx72N | Self::Rx72T => "RXv3",
}
}
pub fn rom_bytes(&self) -> u32 {
match self {
Self::Rx111 => 16 * 1024,
Self::Rx64M => 256 * 1024,
Self::Rx65N => 512 * 1024,
Self::Rx72M => 1024 * 1024,
_ => 128 * 1024,
}
}
pub fn ram_bytes(&self) -> u32 {
match self { Self::Rx111 => 8 * 1024, Self::Rx65N => 128 * 1024, _ => 64 * 1024 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TriCoreVariant {
Tc2xx,
Tc3xx,
Tc4xx,
AurixTc233Lp,
AurixTc234Lp,
AurixTc275Tp,
AurixTc277Tp,
AurixTc297Tp,
AurixTc387Qp,
AurixTc389Qp,
AurixTc397Xp,
AurixTc399Xp,
}
impl TriCoreVariant {
pub fn core_count(&self) -> u32 {
match self {
Self::Tc2xx | Self::AurixTc233Lp => 1,
Self::AurixTc275Tp | Self::AurixTc277Tp => 3,
Self::AurixTc297Tp => 3,
Self::Tc3xx | Self::AurixTc387Qp => 3,
Self::AurixTc389Qp => 4,
Self::AurixTc397Xp => 6,
Self::AurixTc399Xp => 6,
Self::Tc4xx => 6,
}
}
pub fn has_lockstep_core(&self) -> bool { self.core_count() >= 3 }
pub fn has_gtm(&self) -> bool { true } pub fn has_hsm(&self) -> bool { matches!(self, Self::Tc3xx | Self::Tc4xx | Self::AurixTc387Qp | Self::AurixTc389Qp
| Self::AurixTc397Xp | Self::AurixTc399Xp)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Stm8Variant {
Stm8S003F3, Stm8S103F3, Stm8S105K4, Stm8S207R8, Stm8S208R8,
Stm8L051F3, Stm8L101F3, Stm8L152C6, Stm8L162R8,
Stm8Af5288, Stm8Af6223,
}
impl Stm8Variant {
pub fn flash_bytes(&self) -> u32 {
match self {
Self::Stm8S003F3 => 8192, Self::Stm8S103F3 => 8192,
Self::Stm8S105K4 => 16384, Self::Stm8S207R8 => 65536,
Self::Stm8L051F3 => 8192, Self::Stm8L152C6 => 32768,
_ => 16384,
}
}
pub fn ram_bytes(&self) -> u32 {
match self {
Self::Stm8S003F3 => 1024, Self::Stm8S103F3 => 1024,
Self::Stm8S207R8 => 6144, Self::Stm8L152C6 => 2048,
_ => 2048,
}
}
pub fn core_type(&self) -> &str {
match self { Self::Stm8S003F3 | Self::Stm8S103F3 | Self::Stm8S105K4 | Self::Stm8S207R8 | Self::Stm8S208R8 => "STM8S",
Self::Stm8L051F3 | Self::Stm8L101F3 | Self::Stm8L152C6 | Self::Stm8L162R8 => "STM8L",
_ => "STM8A" }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NxpS32Variant {
S32K1xx,
S32K3xx,
S32G2xx,
S32G3xx,
S32R294,
S32R45,
S32V234,
S32Z2xx,
S32E2xx,
}
impl NxpS32Variant {
pub fn cpu_cores(&self) -> &str {
match self {
Self::S32K1xx => "Cortex-M4F",
Self::S32K3xx => "Cortex-M7",
Self::S32G2xx | Self::S32G3xx => "Cortex-M7 + Cortex-A53",
Self::S32R294 | Self::S32R45 => "Cortex-R52",
Self::S32V234 => "Cortex-A53",
Self::S32Z2xx | Self::S32E2xx => "Cortex-R52",
}
}
pub fn target_category(&self) -> &str {
match self {
Self::S32K1xx | Self::S32K3xx => "Body/General Purpose",
Self::S32G2xx | Self::S32G3xx => "Gateway",
Self::S32R45 => "Radar",
Self::S32V234 => "Vision",
Self::S32Z2xx | Self::S32E2xx => "Zonal Controller",
_ => "Automotive",
}
}
pub fn has_hse(&self) -> bool { matches!(self, Self::S32K3xx | Self::S32G2xx | Self::S32G3xx | Self::S32R45 | Self::S32Z2xx | Self::S32E2xx)
}
}
#[derive(Debug, Clone)]
pub struct UartConfig {
pub baud_rate: u32,
pub data_bits: u8,
pub stop_bits: UartStopBits,
pub parity: UartParity,
pub flow_control: bool,
pub base_address: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UartStopBits { One, OnePointFive, Two }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UartParity { None, Even, Odd, Mark, Space }
impl UartConfig {
pub fn default_115200(base: u64) -> Self {
Self { baud_rate: 115200, data_bits: 8, stop_bits: UartStopBits::One,
parity: UartParity::None, flow_control: false, base_address: base }
}
pub fn uart_init_code(&self) -> String {
let mut code = String::new();
code.push_str("# UART Initialization\n");
code.push_str(&format!("# Base: 0x{:08X}, Baud: {}\n", self.base_address, self.baud_rate));
code.push_str(" // Configure baud rate\n");
code.push_str(" // Enable transmitter and receiver\n");
code.push_str(" // Set data bits, stop bits, parity\n");
code
}
pub fn uart_send_byte(&self, byte: u8) -> String {
format!(" // UART TX: 0x{:02X}\n strb r0, [uart_tx_reg]\n", byte)
}
pub fn uart_recv_byte(&self) -> String {
" // UART RX\n ldrb r0, [uart_rx_reg]\n".to_string()
}
}
#[derive(Debug, Clone)]
pub struct SpiConfig {
pub mode: SpiMode,
pub clock_hz: u32,
pub bit_order: SpiBitOrder,
pub data_width: u8,
pub base_address: u64,
pub cs_active_low: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpiMode { Mode0, Mode1, Mode2, Mode3 }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpiBitOrder { MsbFirst, LsbFirst }
impl SpiConfig {
pub fn default_mode0(base: u64) -> Self {
Self { mode: SpiMode::Mode0, clock_hz: 1_000_000, bit_order: SpiBitOrder::MsbFirst,
data_width: 8, base_address: base, cs_active_low: true }
}
pub fn spi_transfer(&self, tx_data: &[u8], rx_buf: &mut [u8]) -> String {
format!(" // SPI transfer: {} bytes TX, {} bytes RX\n", tx_data.len(), rx_buf.len())
}
}
#[derive(Debug, Clone)]
pub struct I2cConfig {
pub clock_hz: u32,
pub own_address: u8,
pub addressing_mode: I2cAddressingMode,
pub base_address: u64,
pub enable_stretching: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum I2cAddressingMode { SevenBit, TenBit }
impl I2cConfig {
pub fn standard_mode(base: u64) -> Self {
Self { clock_hz: 100_000, own_address: 0x50, addressing_mode: I2cAddressingMode::SevenBit,
base_address: base, enable_stretching: true }
}
pub fn i2c_write(&self, dev_addr: u8, data: &[u8]) -> String {
format!(" // I2C write to 0x{:02X}: {} bytes\n", dev_addr, data.len())
}
pub fn i2c_read(&self, dev_addr: u8, length: usize) -> String {
format!(" // I2C read from 0x{:02X}: {} bytes\n", dev_addr, length)
}
}
#[derive(Debug, Clone)]
pub struct GpioConfig {
pub port: char,
pub pin: u8,
pub mode: GpioMode,
pub pull: GpioPull,
pub speed: GpioSpeed,
pub alternate_function: Option<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioMode { Input, Output, AlternateFunction, Analog }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioPull { None, Up, Down }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpioSpeed { Low, Medium, High, VeryHigh }
impl GpioConfig {
pub fn output(port: char, pin: u8) -> Self {
Self { port, pin, mode: GpioMode::Output, pull: GpioPull::None,
speed: GpioSpeed::High, alternate_function: None }
}
pub fn input(port: char, pin: u8, pull: GpioPull) -> Self {
Self { port, pin, mode: GpioMode::Input, pull, speed: GpioSpeed::Low, alternate_function: None }
}
pub fn gpio_init_code(&self) -> String {
format!(" // GPIO {}{} init: {:?}\n", self.port, self.pin, self.mode)
}
pub fn gpio_set(&self, value: bool) -> String {
let val = if value { "1" } else { "0" };
format!(" // GPIO {}{} = {}\n", self.port, self.pin, val)
}
}
#[derive(Debug, Clone)]
pub struct AdcConfig {
pub resolution_bits: u8,
pub channels: Vec<u8>,
pub sample_rate_hz: u32,
pub reference_voltage_mv: u32,
pub continuous_conversion: bool,
pub dma_enabled: bool,
}
impl AdcConfig {
pub fn single_ended(resolution: u8) -> Self {
Self { resolution_bits: resolution, channels: vec![0], sample_rate_hz: 1000,
reference_voltage_mv: 3300, continuous_conversion: false, dma_enabled: false }
}
pub fn adc_read_channel(&self, channel: u8) -> String {
format!(" // ADC read channel {}\n", channel)
}
}
#[derive(Debug, Clone)]
pub struct PwmConfig {
pub frequency_hz: u32,
pub duty_cycle_percent: f64,
pub resolution_bits: u8,
pub channels: Vec<u8>,
pub complementary_output: bool,
pub dead_time_ns: Option<u32>,
}
impl PwmConfig {
pub fn basic(freq_hz: u32, duty: f64) -> Self {
Self { frequency_hz: freq_hz, duty_cycle_percent: duty, resolution_bits: 8,
channels: vec![1], complementary_output: false, dead_time_ns: None }
}
pub fn pwm_init_code(&self) -> String {
format!(" // PWM init: {} Hz, {}% duty\n", self.frequency_hz, self.duty_cycle_percent)
}
}
#[derive(Debug, Clone)]
pub struct TimerConfig {
pub prescaler: u32,
pub period: u32,
pub mode: TimerMode,
pub interrupt_enabled: bool,
pub auto_reload: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerMode { OneShot, Periodic, InputCapture, OutputCompare, Pwm }
impl TimerConfig {
pub fn periodic_tick(prescaler: u32, period: u32) -> Self {
Self { prescaler, period, mode: TimerMode::Periodic, interrupt_enabled: true, auto_reload: true }
}
pub fn timer_init_code(&self) -> String {
format!(" // Timer init: prescaler={}, period={}, mode={:?}\n",
self.prescaler, self.period, self.mode)
}
}
#[derive(Debug, Clone)]
pub struct Embedded2Registry {
pub cortex_r: Option<CortexR>,
pub tcm: Option<TcmConfig>,
pub trustzone: Option<TrustZoneState>,
pub riscv_tee: Option<RiscvTeeConfig>,
pub msp430_variant: Option<Msp430Variant>,
pub pic_arch: Option<PicArch>,
pub renesas_rx: Option<RenesasRxVariant>,
pub tricore: Option<TriCoreVariant>,
pub stm8: Option<Stm8Variant>,
pub nxp_s32: Option<NxpS32Variant>,
pub uart: Option<UartConfig>,
pub spi: Option<SpiConfig>,
pub i2c: Option<I2cConfig>,
pub gpios: Vec<GpioConfig>,
pub adc: Option<AdcConfig>,
pub pwm: Option<PwmConfig>,
pub timer: Option<TimerConfig>,
}
impl Embedded2Registry {
pub fn new() -> Self {
Self {
cortex_r: Some(CortexR::R5),
tcm: Some(TcmConfig::new()),
trustzone: Some(TrustZoneState::new()),
riscv_tee: None,
msp430_variant: Some(Msp430Variant::Msp430FR5969),
pic_arch: Some(PicArch::Pic32Mx),
renesas_rx: Some(RenesasRxVariant::Rx65N),
tricore: Some(TriCoreVariant::AurixTc387Qp),
stm8: Some(Stm8Variant::Stm8S207R8),
nxp_s32: Some(NxpS32Variant::S32K3xx),
uart: Some(UartConfig::default_115200(0x40000000)),
spi: Some(SpiConfig::default_mode0(0x40001000)),
i2c: Some(I2cConfig::standard_mode(0x40002000)),
gpios: vec![GpioConfig::output('A', 0), GpioConfig::input('A', 1, GpioPull::Up)],
adc: Some(AdcConfig::single_ended(12)),
pwm: Some(PwmConfig::basic(50000, 50.0)),
timer: Some(TimerConfig::periodic_tick(80, 1000)),
}
}
pub fn device_count(&self) -> usize {
let mut c = 0;
if self.cortex_r.is_some() { c += 1; }
if self.msp430_variant.is_some() { c += 1; }
if self.pic_arch.is_some() { c += 1; }
if self.renesas_rx.is_some() { c += 1; }
if self.tricore.is_some() { c += 1; }
if self.stm8.is_some() { c += 1; }
if self.nxp_s32.is_some() { c += 1; }
c
}
pub fn peripheral_count(&self) -> usize {
let mut c = 0;
if self.uart.is_some() { c += 1; }
if self.spi.is_some() { c += 1; }
if self.i2c.is_some() { c += 1; }
if !self.gpios.is_empty() { c += 1; }
if self.adc.is_some() { c += 1; }
if self.pwm.is_some() { c += 1; }
if self.timer.is_some() { c += 1; }
c
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cortex_r_variants() {
assert!(CortexR::R5.has_fpu());
assert!(!CortexR::R4.has_fpu());
assert_eq!(CortexR::R7.mpu_regions(), 16);
assert_eq!(CortexR::R52.mpu_regions(), 24);
}
#[test]
fn test_cortex_r_tcm() {
let tcm = TcmConfig::new();
assert!(tcm.enable_ecc);
let script = tcm.generate_linker_script_section();
assert!(script.contains("ITCM"));
assert!(script.contains("DTCM"));
}
#[test]
fn test_cortex_r_mpu_encode() {
let region = CortexRMpuRegion {
base_address: 0x08000000, size: 0x100000,
access_permissions: CortexRAccessPermission::FullAccess,
attributes: CortexRMemAttributes::Normal { cacheable: true, bufferable: true, shared: false },
enabled: true, sub_region_disable: 0,
};
let encoded = region.encode_region(1);
assert!(encoded != 0);
assert!(encoded & (1 << 4) != 0); }
#[test]
fn test_pmp_entry_encode_cfg() {
let entry = RiscVPmpEntry {
address: 0x80000000, addressing_mode: PmpAddressingMode::Napot,
permissions: PmpPermission::ReadWriteExecute, locked: true,
};
let cfg = entry.encode_cfg();
assert_eq!(cfg & 0x07, 0x07); assert_eq!(cfg & 0x18, 0x18); assert_eq!(cfg & 0x80, 0x80); }
#[test]
fn test_pmp_config_init_code() {
let mut pmp = RiscVPmpConfig::new(8);
pmp.add_region(RiscVPmpEntry {
address: 0x80000000, addressing_mode: PmpAddressingMode::Tor,
permissions: PmpPermission::ReadExecute, locked: true,
});
let code = pmp.generate_init_code();
assert!(code.contains("pmpcfg0"));
assert!(code.contains("pmpaddr0"));
}
#[test]
fn test_trustzone_smc_template() {
let tz = TrustZoneState::new();
let template = tz.smc_call_template(0x84000000);
assert!(template.contains("smc #0"));
}
#[test]
fn test_trustzone_world_switch() {
let mut tz = TrustZoneState::new();
assert_eq!(tz.current_world, TrustZoneWorld::Secure);
tz.enter_non_secure();
assert_eq!(tz.current_world, TrustZoneWorld::NonSecure);
tz.return_to_secure();
assert_eq!(tz.current_world, TrustZoneWorld::Secure);
}
#[test]
fn test_riscv_tee_new() {
let tee = RiscvTeeConfig::new(0x90000000, 0x10000000);
assert!(tee.attestation_enabled);
assert!(tee.sealed_storage_enabled);
let code = tee.tee_init_code();
assert!(code.contains("mtvec"));
assert!(code.contains("pmpcfg"));
}
#[test]
fn test_msp430_variant_specs() {
let m = Msp430Variant::Msp430FR5969;
assert!(m.is_fram());
assert_eq!(m.ram_bytes(), 2048);
let m2 = Msp430Variant::Msp430G2553;
assert!(!m2.is_fram());
}
#[test]
fn test_pic_arch_bit_width() {
assert_eq!(PicArch::Pic16.bit_width(), 8);
assert_eq!(PicArch::Pic32Mx.bit_width(), 32);
assert!(PicArch::Pic16.is_harvard());
}
#[test]
fn test_pic_memory_regions() {
let regions = PicArch::Pic16.memory_regions();
assert_eq!(regions.len(), 3);
assert_eq!(regions[0].kind, PicMemoryKind::Flash);
}
#[test]
fn test_renesas_rx_cpu_type() {
assert_eq!(RenesasRxVariant::Rx64M.cpu_type(), "RXv2");
assert_eq!(RenesasRxVariant::Rx72M.cpu_type(), "RXv3");
}
#[test]
fn test_tricore_variant() {
let tc = TriCoreVariant::AurixTc399Xp;
assert_eq!(tc.core_count(), 6);
assert!(tc.has_hsm());
assert!(tc.has_lockstep_core());
}
#[test]
fn test_stm8_variant() {
let stm = Stm8Variant::Stm8S207R8;
assert_eq!(stm.flash_bytes(), 65536);
assert_eq!(stm.ram_bytes(), 6144);
}
#[test]
fn test_nxp_s32_variant() {
let s32 = NxpS32Variant::S32K3xx;
assert_eq!(s32.cpu_cores(), "Cortex-M7");
assert!(s32.has_hse());
}
#[test]
fn test_uart_config_default() {
let uart = UartConfig::default_115200(0x40010000);
assert_eq!(uart.baud_rate, 115200);
assert_eq!(uart.data_bits, 8);
assert!(!uart.flow_control);
}
#[test]
fn test_uart_init_code() {
let uart = UartConfig::default_115200(0x40010000);
let code = uart.uart_init_code();
assert!(code.contains("115200"));
}
#[test]
fn test_spi_mode() {
let spi = SpiConfig::default_mode0(0x40020000);
assert_eq!(spi.mode, SpiMode::Mode0);
assert_eq!(spi.bit_order, SpiBitOrder::MsbFirst);
}
#[test]
fn test_i2c_config() {
let i2c = I2cConfig::standard_mode(0x40030000);
assert_eq!(i2c.clock_hz, 100_000);
assert_eq!(i2c.own_address, 0x50);
}
#[test]
fn test_gpio_output() {
let gpio = GpioConfig::output('B', 5);
assert_eq!(gpio.port, 'B');
assert_eq!(gpio.pin, 5);
assert_eq!(gpio.mode, GpioMode::Output);
}
#[test]
fn test_adc_config() {
let adc = AdcConfig::single_ended(12);
assert_eq!(adc.resolution_bits, 12);
}
#[test]
fn test_pwm_config() {
let pwm = PwmConfig::basic(50000, 25.0);
assert!((pwm.duty_cycle_percent - 25.0).abs() < 0.01);
}
#[test]
fn test_timer_config() {
let timer = TimerConfig::periodic_tick(80, 1000);
assert!(timer.auto_reload);
assert!(timer.interrupt_enabled);
}
#[test]
fn test_embedded2_registry_device_count() {
let reg = Embedded2Registry::new();
assert_eq!(reg.device_count(), 7);
}
#[test]
fn test_embedded2_registry_peripheral_count() {
let reg = Embedded2Registry::new();
assert_eq!(reg.peripheral_count(), 7);
}
}
#[derive(Debug, Clone)]
pub struct WatchdogConfig {
pub timeout_ms: u32,
pub window_mode: bool,
pub window_start_ms: Option<u32>,
pub enable_interrupt: bool,
pub enable_reset: bool,
pub clock_source: WatchdogClockSource,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WatchdogClockSource { InternalRc, ExternalOscillator, SystemClock }
impl WatchdogConfig {
pub fn standard(timeout_ms: u32) -> Self {
Self { timeout_ms, window_mode: false, window_start_ms: None,
enable_interrupt: true, enable_reset: true, clock_source: WatchdogClockSource::InternalRc }
}
pub fn kick_code(&self) -> String {
" // Watchdog kick sequence\n str r0, [wdog_kick_reg]\n".to_string()
}
}
#[derive(Debug, Clone)]
pub struct DmaConfig {
pub channels: Vec<DmaChannel>,
pub base_address: u64,
pub enable_error_interrupt: bool,
}
#[derive(Debug, Clone)]
pub struct DmaChannel {
pub channel_id: u8,
pub source_address: u64,
pub destination_address: u64,
pub transfer_size: u32,
pub direction: DmaDirection,
pub burst_size: DmaBurstSize,
pub circular_mode: bool,
pub trigger_source: Option<u8>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DmaDirection { MemoryToMemory, MemoryToPeripheral, PeripheralToMemory }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DmaBurstSize { Single, Burst4, Burst8, Burst16 }
impl DmaConfig {
pub fn new(base: u64) -> Self { Self { channels: Vec::new(), base_address: base, enable_error_interrupt: true } }
pub fn add_channel(&mut self, ch: DmaChannel) { self.channels.push(ch); }
pub fn dma_init_code(&self) -> String {
let mut code = String::new();
code.push_str("# DMA Initialization\n");
for ch in &self.channels {
code.push_str(&format!(" // Channel {}: 0x{:08X} -> 0x{:08X}, {} bytes\n",
ch.channel_id, ch.source_address, ch.destination_address, ch.transfer_size));
}
code
}
}
#[derive(Debug, Clone)]
pub struct CanConfig {
pub bit_rate: u32,
pub sample_point_percent: f64,
pub sjw: u8,
pub mode: CanMode,
pub filters: Vec<CanFilter>,
pub base_address: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanMode { Normal, ListenOnly, Loopback, SilentLoopback }
#[derive(Debug, Clone)]
pub struct CanFilter {
pub id: u32,
pub mask: u32,
pub format: CanIdFormat,
pub filter_type: CanFilterType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanIdFormat { Standard11Bit, Extended29Bit }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CanFilterType { Range, Dual, Classic, Mask }
impl CanConfig {
pub fn can_500k(base: u64) -> Self {
Self { bit_rate: 500_000, sample_point_percent: 75.0, sjw: 3,
mode: CanMode::Normal, filters: Vec::new(), base_address: base }
}
pub fn can_send_message(&self, id: u32, data: &[u8]) -> String {
format!(" // CAN TX: ID=0x{:X}, DLC={}\n", id, data.len())
}
pub fn can_init_code(&self) -> String {
format!(" // CAN init: {} bps, sample point={:.1}%\n",
self.bit_rate, self.sample_point_percent)
}
}
#[derive(Debug, Clone)]
pub struct LinConfig {
pub bit_rate: u32,
pub mode: LinMode,
pub base_address: u64,
pub auto_baud: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinMode { Master, Slave }
impl LinConfig {
pub fn master_19200(base: u64) -> Self {
Self { bit_rate: 19200, mode: LinMode::Master, base_address: base, auto_baud: false }
}
pub fn lin_send_frame(&self, pid: u8, data: &[u8]) -> String {
format!(" // LIN TX: PID=0x{:02X}, {} bytes\n", pid, data.len())
}
}
#[derive(Debug, Clone)]
pub struct ExtiConfig {
pub lines: Vec<ExtiLine>,
pub base_address: u64,
}
#[derive(Debug, Clone)]
pub struct ExtiLine {
pub line_number: u8,
pub port: char,
pub pin: u8,
pub trigger: ExtiTrigger,
pub enable: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExtiTrigger { RisingEdge, FallingEdge, BothEdges }
impl ExtiConfig {
pub fn new(base: u64) -> Self { Self { lines: Vec::new(), base_address: base } }
pub fn add_line(&mut self, line: ExtiLine) { self.lines.push(line); }
pub fn exti_init_code(&self) -> String {
let mut code = String::new();
code.push_str("# EXTI Configuration\n");
for line in &self.lines {
code.push_str(&format!(" // Line {}: {}{} -> {:?}\n",
line.line_number, line.port, line.pin, line.trigger));
}
code
}
}
#[derive(Debug, Clone)]
pub struct EmbeddedStackProtection {
pub stack_top: u64,
pub stack_size: u64,
pub guard_page_size: u64,
pub use_mpu_guard: bool,
pub use_canary: bool,
pub canary_value: u64,
}
impl EmbeddedStackProtection {
pub fn new(stack_top: u64, stack_size: u64) -> Self {
Self { stack_top, stack_size, guard_page_size: 256,
use_mpu_guard: true, use_canary: true, canary_value: 0xDEADBEEFCAFEBABE }
}
pub fn generate_guard_init(&self) -> String {
format!(" // Stack guard: top=0x{:08X}, size={}, canary=0x{:016X}\n",
self.stack_top, self.stack_size, self.canary_value)
}
pub fn stack_canary_check_code(&self) -> String {
" // Check stack canary\n ldr r0, [stack_canary_addr]\n cmp r0, stack_canary_value\n bne stack_overflow_handler\n".to_string()
}
}
#[derive(Debug, Clone)]
pub struct E2EProtection {
pub profile: E2EProfile,
pub data_id: u16,
pub counter_offset: usize,
pub crc_offset: usize,
pub data_length: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum E2EProfile { Profile1, Profile2, Profile4, Profile5, Profile6, Profile7, Profile11, Profile22 }
impl E2EProtection {
pub fn new(profile: E2EProfile, data_id: u16, data_length: usize) -> Self {
Self { profile, data_id, counter_offset: 0, crc_offset: data_length, data_length }
}
pub fn compute_crc(&self, data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFFFFFF;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
if crc & 1 != 0 { crc = (crc >> 1) ^ 0xEDB88320; }
else { crc >>= 1; }
}
}
!crc
}
pub fn protect_frame(&self, data: &[u8]) -> Vec<u8> {
let mut frame = Vec::with_capacity(self.crc_offset + 4);
frame.extend_from_slice(&data[..self.crc_offset]);
let counter_bytes = 0u32.to_le_bytes(); frame.extend_from_slice(&counter_bytes);
let crc = self.compute_crc(&frame[..self.crc_offset + 4]);
frame.extend_from_slice(&crc.to_le_bytes());
frame
}
}
#[derive(Debug, Clone)]
pub struct AutosarTask {
pub name: String,
pub priority: u32,
pub stack_size: u32,
pub activation: u32,
pub autostart: bool,
pub schedule: TaskScheduleType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskScheduleType { FullPreemptive, NonPreemptive, MixedPreemptive }
impl AutosarTask {
pub fn new(name: &str, priority: u32) -> Self {
Self { name: name.to_string(), priority, stack_size: 4096,
activation: 1, autostart: true, schedule: TaskScheduleType::FullPreemptive }
}
}
#[cfg(test)]
mod extended_tests {
use super::*;
#[test]
fn test_watchdog_standard() {
let wdg = WatchdogConfig::standard(1000);
assert_eq!(wdg.timeout_ms, 1000);
assert!(wdg.enable_reset);
assert!(!wdg.window_mode);
}
#[test]
fn test_watchdog_kick_code() {
let wdg = WatchdogConfig::standard(500);
assert!(wdg.kick_code().contains("wdog_kick_reg"));
}
#[test]
fn test_dma_config_add_channel() {
let mut dma = DmaConfig::new(0x40008000);
dma.add_channel(DmaChannel {
channel_id: 0, source_address: 0x20000000,
destination_address: 0x40010000, transfer_size: 256,
direction: DmaDirection::MemoryToPeripheral,
burst_size: DmaBurstSize::Burst8,
circular_mode: false, trigger_source: None,
});
assert_eq!(dma.channels.len(), 1);
let code = dma.dma_init_code();
assert!(code.contains("Channel 0"));
}
#[test]
fn test_can_500k() {
let can = CanConfig::can_500k(0x40006000);
assert_eq!(can.bit_rate, 500_000);
assert!((can.sample_point_percent - 75.0).abs() < 0.01);
}
#[test]
fn test_can_send_message() {
let can = CanConfig::can_500k(0x40006000);
let msg = can.can_send_message(0x123, &[0x01, 0x02, 0x03]);
assert!(msg.contains("0x123"));
}
#[test]
fn test_lin_master() {
let lin = LinConfig::master_19200(0x40007000);
assert_eq!(lin.bit_rate, 19200);
assert_eq!(lin.mode, LinMode::Master);
}
#[test]
fn test_exti_line_config() {
let mut exti = ExtiConfig::new(0x40008000);
exti.add_line(ExtiLine {
line_number: 0, port: 'A', pin: 0,
trigger: ExtiTrigger::RisingEdge, enable: true,
});
let code = exti.exti_init_code();
assert!(code.contains("A0"));
}
#[test]
fn test_stack_protection_new() {
let sp = EmbeddedStackProtection::new(0x20010000, 8192);
assert!(sp.use_mpu_guard);
assert!(sp.use_canary);
assert_eq!(sp.canary_value, 0xDEADBEEFCAFEBABE);
}
#[test]
fn test_e2e_compute_crc() {
let e2e = E2EProtection::new(E2EProfile::Profile1, 0x100, 8);
let crc = e2e.compute_crc(&[0; 8]);
assert!(crc != 0);
}
#[test]
fn test_e2e_protect_frame() {
let e2e = E2EProtection::new(E2EProfile::Profile1, 0x200, 4);
let frame = e2e.protect_frame(&[0x01, 0x02, 0x03, 0x04]);
assert_eq!(frame.len(), 12); }
#[test]
fn test_autosar_task_new() {
let task = AutosarTask::new("TASK_10ms", 5);
assert_eq!(task.name, "TASK_10ms");
assert_eq!(task.priority, 5);
assert!(task.autostart);
}
}