Skip to main content

synth_backend/
arm_startup.rs

1//! ARM Cortex-M Startup Code Generation
2//!
3//! Generates vector table and startup code for ARM Cortex-M processors
4
5use synth_core::{CortexMVariant, HardwareCapabilities};
6
7/// ARM Cortex-M startup code generator
8pub struct ARMStartupGenerator {
9    hw_caps: HardwareCapabilities,
10}
11
12impl ARMStartupGenerator {
13    /// Create a new startup generator
14    pub fn new(hw_caps: HardwareCapabilities) -> Self {
15        Self { hw_caps }
16    }
17
18    /// Generate vector table and startup code
19    pub fn generate(&self) -> String {
20        let mut code = String::new();
21
22        code.push_str("/* Startup Code for ARM Cortex-M */\n");
23        code.push_str("/* Generated by Synth WebAssembly Component Synthesizer */\n\n");
24        code.push_str("#include <stdint.h>\n\n");
25
26        // External symbols from linker script
27        code.push_str("/* Linker-defined symbols */\n");
28        code.push_str("extern uint32_t _sidata;\n");
29        code.push_str("extern uint32_t _sdata;\n");
30        code.push_str("extern uint32_t _edata;\n");
31        code.push_str("extern uint32_t _sbss;\n");
32        code.push_str("extern uint32_t _ebss;\n");
33        code.push_str("extern uint32_t _stack_top;\n\n");
34
35        // Forward declarations
36        code.push_str("/* Core handlers */\n");
37        code.push_str("void Reset_Handler(void);\n");
38        code.push_str("void Default_Handler(void);\n\n");
39
40        // Exception handlers
41        code.push_str("/* Cortex-M exception handlers */\n");
42        code.push_str(
43            "void NMI_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
44        );
45        code.push_str(
46            "void HardFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
47        );
48        code.push_str(
49            "void MemManage_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
50        );
51        code.push_str(
52            "void BusFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
53        );
54        code.push_str(
55            "void UsageFault_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
56        );
57        code.push_str(
58            "void SVC_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
59        );
60        code.push_str(
61            "void DebugMon_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
62        );
63        code.push_str(
64            "void PendSV_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
65        );
66        code.push_str(
67            "void SysTick_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n\n",
68        );
69
70        // Device-specific interrupt handlers
71        let num_irqs = self.get_irq_count();
72        if num_irqs > 0 {
73            code.push_str("/* Device-specific interrupt handlers */\n");
74            for i in 0..num_irqs {
75                code.push_str(&format!(
76                    "void IRQ{}_Handler(void) __attribute__((weak, alias(\"Default_Handler\")));\n",
77                    i
78                ));
79            }
80            code.push('\n');
81        }
82
83        // Vector table
84        code.push_str("/* Vector Table */\n");
85        code.push_str("__attribute__((section(\".isr_vector\")))\n");
86        code.push_str("const void (*vectors[])(void) = {\n");
87        code.push_str("    (void (*)(void))(&_stack_top),  /* Initial stack pointer */\n");
88        code.push_str("    Reset_Handler,                  /* Reset handler */\n");
89        code.push_str("    NMI_Handler,                    /* NMI handler */\n");
90        code.push_str("    HardFault_Handler,              /* Hard fault handler */\n");
91        code.push_str("    MemManage_Handler,              /* MPU fault handler */\n");
92        code.push_str("    BusFault_Handler,               /* Bus fault handler */\n");
93        code.push_str("    UsageFault_Handler,             /* Usage fault handler */\n");
94        code.push_str("    0,                              /* Reserved */\n");
95        code.push_str("    0,                              /* Reserved */\n");
96        code.push_str("    0,                              /* Reserved */\n");
97        code.push_str("    0,                              /* Reserved */\n");
98        code.push_str("    SVC_Handler,                    /* SVCall handler */\n");
99        code.push_str("    DebugMon_Handler,               /* Debug monitor handler */\n");
100        code.push_str("    0,                              /* Reserved */\n");
101        code.push_str("    PendSV_Handler,                 /* PendSV handler */\n");
102        code.push_str("    SysTick_Handler,                /* SysTick handler */\n");
103
104        // Device interrupts
105        if num_irqs > 0 {
106            code.push_str("\n    /* Device-specific interrupts */\n");
107            for i in 0..num_irqs {
108                code.push_str(&format!("    IRQ{}_Handler,\n", i));
109            }
110        }
111
112        code.push_str("};\n\n");
113
114        // Reset handler implementation
115        code.push_str("/* Reset Handler */\n");
116        code.push_str("void Reset_Handler(void) {\n");
117        code.push_str("    uint32_t *src, *dest;\n\n");
118
119        code.push_str("    /* Copy .data section from flash to RAM */\n");
120        code.push_str("    src = &_sidata;\n");
121        code.push_str("    dest = &_sdata;\n");
122        code.push_str("    while (dest < &_edata) {\n");
123        code.push_str("        *dest++ = *src++;\n");
124        code.push_str("    }\n\n");
125
126        code.push_str("    /* Zero out .bss section */\n");
127        code.push_str("    dest = &_sbss;\n");
128        code.push_str("    while (dest < &_ebss) {\n");
129        code.push_str("        *dest++ = 0;\n");
130        code.push_str("    }\n\n");
131
132        // FPU initialization if available
133        if self.has_fpu() {
134            code.push_str("    /* Enable FPU */\n");
135            code.push_str("    #define SCB_CPACR (*((volatile uint32_t*)0xE000ED88))\n");
136            code.push_str(
137                "    SCB_CPACR |= (0xF << 20);  /* Enable CP10 and CP11 coprocessors */\n",
138            );
139            code.push_str("    __asm volatile(\"dsb\\n\\tisb\");\n\n");
140        }
141
142        code.push_str("    /* Call main */\n");
143        code.push_str("    extern int main(void);\n");
144        code.push_str("    main();\n\n");
145
146        code.push_str("    /* Infinite loop if main returns */\n");
147        code.push_str("    while (1) {\n");
148        code.push_str("        __asm volatile(\"wfi\");  /* Wait for interrupt */\n");
149        code.push_str("    }\n");
150        code.push_str("}\n\n");
151
152        // Default handler implementation
153        code.push_str("/* Default Handler */\n");
154        code.push_str("void Default_Handler(void) {\n");
155        code.push_str("    /* Trap unhandled interrupts */\n");
156        code.push_str("    while (1) {\n");
157        code.push_str("        __asm volatile(\"bkpt #0\");  /* Breakpoint */\n");
158        code.push_str("    }\n");
159        code.push_str("}\n");
160
161        code
162    }
163
164    /// Get number of device-specific IRQs
165    fn get_irq_count(&self) -> usize {
166        // IRQ count varies by device
167        // nRF52840 has 48 interrupts, STM32F407 has 82, etc.
168        match &self.hw_caps.arch {
169            synth_core::TargetArch::ARMCortexM(variant) => match variant {
170                CortexMVariant::M3 | CortexMVariant::M4 | CortexMVariant::M4F => 48,
171                CortexMVariant::M7 | CortexMVariant::M7DP => 64,
172                CortexMVariant::M33 | CortexMVariant::M55 => 64,
173            },
174            _ => 0,
175        }
176    }
177
178    /// Check if target has FPU
179    fn has_fpu(&self) -> bool {
180        self.hw_caps.has_fpu
181    }
182}
183
184#[cfg(test)]
185mod tests {
186    use super::*;
187
188    #[test]
189    fn test_startup_code_generation() {
190        let hw_caps = HardwareCapabilities::nrf52840();
191        let generator = ARMStartupGenerator::new(hw_caps);
192
193        let startup_code = generator.generate();
194
195        // Print for inspection
196        println!("\nGenerated Startup Code (excerpt):");
197        println!(
198            "{}",
199            startup_code.lines().take(50).collect::<Vec<_>>().join("\n")
200        );
201
202        // Verify key elements
203        assert!(startup_code.contains("Reset_Handler"));
204        assert!(startup_code.contains("Default_Handler"));
205        assert!(startup_code.contains("vectors[]"));
206        assert!(startup_code.contains("_stack_top"));
207        assert!(startup_code.contains("Copy .data section"));
208        assert!(startup_code.contains("Zero out .bss"));
209        assert!(startup_code.contains("main()"));
210
211        // Verify FPU initialization (nRF52840 has FPU)
212        assert!(startup_code.contains("Enable FPU"));
213        assert!(startup_code.contains("SCB_CPACR"));
214
215        // Verify IRQ handlers
216        assert!(startup_code.contains("IRQ0_Handler"));
217        assert!(startup_code.contains("IRQ47_Handler"));
218    }
219
220    #[test]
221    fn test_startup_code_without_fpu() {
222        let hw_caps = HardwareCapabilities {
223            arch: synth_core::TargetArch::ARMCortexM(CortexMVariant::M3),
224            has_mpu: true,
225            mpu_regions: 8,
226            has_pmp: false,
227            pmp_entries: 0,
228            has_fpu: false, // M3 has no FPU
229            fpu_precision: None,
230            has_simd: false,
231            simd_level: None,
232            xip_capable: true,
233            flash_size: 512 * 1024,
234            ram_size: 128 * 1024,
235        };
236
237        let generator = ARMStartupGenerator::new(hw_caps);
238        let startup_code = generator.generate();
239
240        // Should not have FPU initialization
241        assert!(!startup_code.contains("Enable FPU"));
242    }
243}