synth-backend 0.11.9

ARM encoder, ELF builder, vector table, linker scripts, and MPU configuration
Documentation
//! ARM Cortex-M Startup Code Generation
//!
//! Generates vector table and startup code for ARM Cortex-M processors

use synth_core::{CortexMVariant, HardwareCapabilities};

/// ARM Cortex-M startup code generator
pub struct ARMStartupGenerator {
    hw_caps: HardwareCapabilities,
}

impl ARMStartupGenerator {
    /// Create a new startup generator
    pub fn new(hw_caps: HardwareCapabilities) -> Self {
        Self { hw_caps }
    }

    /// Generate vector table and startup code
    pub fn generate(&self) -> String {
        let mut code = String::new();

        code.push_str("/* Startup Code for ARM Cortex-M */\n");
        code.push_str("/* Generated by Synth WebAssembly Component Synthesizer */\n\n");
        code.push_str("#include <stdint.h>\n\n");

        // External symbols from linker script
        code.push_str("/* Linker-defined symbols */\n");
        code.push_str("extern uint32_t _sidata;\n");
        code.push_str("extern uint32_t _sdata;\n");
        code.push_str("extern uint32_t _edata;\n");
        code.push_str("extern uint32_t _sbss;\n");
        code.push_str("extern uint32_t _ebss;\n");
        code.push_str("extern uint32_t _stack_top;\n\n");

        // Forward declarations
        code.push_str("/* Core handlers */\n");
        code.push_str("void Reset_Handler(void);\n");
        code.push_str("void Default_Handler(void);\n\n");

        // Exception handlers
        code.push_str("/* Cortex-M exception handlers */\n");
        code.push_str(
            "void NMI_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void HardFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void MemManage_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void BusFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void UsageFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void SVC_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void DebugMon_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void PendSV_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
        );
        code.push_str(
            "void SysTick_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n\n",
        );

        // Device-specific interrupt handlers
        let num_irqs = self.get_irq_count();
        if num_irqs > 0 {
            code.push_str("/* Device-specific interrupt handlers */\n");
            for i in 0..num_irqs {
                code.push_str(&format!(
                    "void IRQ{}_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
                    i
                ));
            }
            code.push('\n');
        }

        // Vector table
        code.push_str("/* Vector Table */\n");
        code.push_str("__attribute__((section(\".isr_vector\")))\n");
        code.push_str("const void (*vectors[])(void) = {\n");
        code.push_str("    (void (*)(void))(&_stack_top),  /* Initial stack pointer */\n");
        code.push_str("    Reset_Handler,                  /* Reset handler */\n");
        code.push_str("    NMI_Handler,                    /* NMI handler */\n");
        code.push_str("    HardFault_Handler,              /* Hard fault handler */\n");
        code.push_str("    MemManage_Handler,              /* MPU fault handler */\n");
        code.push_str("    BusFault_Handler,               /* Bus fault handler */\n");
        code.push_str("    UsageFault_Handler,             /* Usage fault handler */\n");
        code.push_str("    0,                              /* Reserved */\n");
        code.push_str("    0,                              /* Reserved */\n");
        code.push_str("    0,                              /* Reserved */\n");
        code.push_str("    0,                              /* Reserved */\n");
        code.push_str("    SVC_Handler,                    /* SVCall handler */\n");
        code.push_str("    DebugMon_Handler,               /* Debug monitor handler */\n");
        code.push_str("    0,                              /* Reserved */\n");
        code.push_str("    PendSV_Handler,                 /* PendSV handler */\n");
        code.push_str("    SysTick_Handler,                /* SysTick handler */\n");

        // Device interrupts
        if num_irqs > 0 {
            code.push_str("\n    /* Device-specific interrupts */\n");
            for i in 0..num_irqs {
                code.push_str(&format!("    IRQ{}_Handler,\n", i));
            }
        }

        code.push_str("};\n\n");

        // Reset handler implementation
        code.push_str("/* Reset Handler */\n");
        code.push_str("void Reset_Handler(void) {\n");
        code.push_str("    uint32_t *src, *dest;\n\n");

        code.push_str("    /* Copy .data section from flash to RAM */\n");
        code.push_str("    src = &_sidata;\n");
        code.push_str("    dest = &_sdata;\n");
        code.push_str("    while (dest < &_edata) {\n");
        code.push_str("        *dest++ = *src++;\n");
        code.push_str("    }\n\n");

        code.push_str("    /* Zero out .bss section */\n");
        code.push_str("    dest = &_sbss;\n");
        code.push_str("    while (dest < &_ebss) {\n");
        code.push_str("        *dest++ = 0;\n");
        code.push_str("    }\n\n");

        // FPU initialization if available
        if self.has_fpu() {
            code.push_str("    /* Enable FPU */\n");
            code.push_str("    #define SCB_CPACR (*((volatile uint32_t*)0xE000ED88))\n");
            code.push_str(
                "    SCB_CPACR |= (0xF << 20);  /* Enable CP10 and CP11 coprocessors */\n",
            );
            code.push_str("    __asm volatile(\"dsb\\n\\tisb\");\n\n");
        }

        code.push_str("    /* Call main */\n");
        code.push_str("    extern int main(void);\n");
        code.push_str("    main();\n\n");

        code.push_str("    /* Infinite loop if main returns */\n");
        code.push_str("    while (1) {\n");
        code.push_str("        __asm volatile(\"wfi\");  /* Wait for interrupt */\n");
        code.push_str("    }\n");
        code.push_str("}\n\n");

        // Default handler implementation
        code.push_str("/* Default Handler */\n");
        code.push_str("void Default_Handler(void) {\n");
        code.push_str("    /* Trap unhandled interrupts */\n");
        code.push_str("    while (1) {\n");
        code.push_str("        __asm volatile(\"bkpt #0\");  /* Breakpoint */\n");
        code.push_str("    }\n");
        code.push_str("}\n");

        code
    }

    /// Get number of device-specific IRQs
    fn get_irq_count(&self) -> usize {
        // IRQ count varies by device
        // nRF52840 has 48 interrupts, STM32F407 has 82, etc.
        match &self.hw_caps.arch {
            synth_core::TargetArch::ARMCortexM(variant) => match variant {
                CortexMVariant::M3 | CortexMVariant::M4 | CortexMVariant::M4F => 48,
                CortexMVariant::M7 | CortexMVariant::M7DP => 64,
                CortexMVariant::M33 | CortexMVariant::M55 => 64,
            },
            _ => 0,
        }
    }

    /// Check if target has FPU
    fn has_fpu(&self) -> bool {
        self.hw_caps.has_fpu
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_startup_code_generation() {
        let hw_caps = HardwareCapabilities::nrf52840();
        let generator = ARMStartupGenerator::new(hw_caps);

        let startup_code = generator.generate();

        // Print for inspection
        println!("\nGenerated Startup Code (excerpt):");
        println!(
            "{}",
            startup_code.lines().take(50).collect::<Vec<_>>().join("\n")
        );

        // Verify key elements
        assert!(startup_code.contains("Reset_Handler"));
        assert!(startup_code.contains("Default_Handler"));
        assert!(startup_code.contains("vectors[]"));
        assert!(startup_code.contains("_stack_top"));
        assert!(startup_code.contains("Copy .data section"));
        assert!(startup_code.contains("Zero out .bss"));
        assert!(startup_code.contains("main()"));

        // Verify FPU initialization (nRF52840 has FPU)
        assert!(startup_code.contains("Enable FPU"));
        assert!(startup_code.contains("SCB_CPACR"));

        // Verify IRQ handlers
        assert!(startup_code.contains("IRQ0_Handler"));
        assert!(startup_code.contains("IRQ47_Handler"));
    }

    #[test]
    fn test_startup_code_without_fpu() {
        let hw_caps = HardwareCapabilities {
            arch: synth_core::TargetArch::ARMCortexM(CortexMVariant::M3),
            has_mpu: true,
            mpu_regions: 8,
            has_pmp: false,
            pmp_entries: 0,
            has_fpu: false, // M3 has no FPU
            fpu_precision: None,
            has_simd: false,
            simd_level: None,
            xip_capable: true,
            flash_size: 512 * 1024,
            ram_size: 128 * 1024,
        };

        let generator = ARMStartupGenerator::new(hw_caps);
        let startup_code = generator.generate();

        // Should not have FPU initialization
        assert!(!startup_code.contains("Enable FPU"));
    }
}