Skip to main content

synth_backend/
vector_table.rs

1//! ARM Cortex-M Vector Table Generation
2//!
3//! Generates the interrupt vector table required for ARM Cortex-M startup
4
5use synth_core::Result;
6
7/// Vector table entry
8#[derive(Debug, Clone)]
9pub struct VectorEntry {
10    /// Handler name
11    pub name: String,
12    /// Address (0 for unresolved)
13    pub address: u32,
14    /// Is this a weak symbol
15    pub weak: bool,
16}
17
18/// ARM Cortex-M Vector Table
19pub struct VectorTable {
20    /// Initial stack pointer value
21    pub initial_sp: u32,
22    /// Reset handler address
23    pub reset_handler: u32,
24    /// Exception and interrupt handlers
25    pub handlers: Vec<VectorEntry>,
26}
27
28impl VectorTable {
29    /// Create a new vector table for Cortex-M
30    #[allow(clippy::vec_init_then_push)]
31    pub fn new_cortex_m(stack_top: u32) -> Self {
32        let mut handlers = Vec::new();
33
34        // Cortex-M standard exceptions (16 entries)
35        handlers.push(VectorEntry {
36            name: "NMI_Handler".to_string(),
37            address: 0,
38            weak: true,
39        });
40        handlers.push(VectorEntry {
41            name: "HardFault_Handler".to_string(),
42            address: 0,
43            weak: false, // HardFault is critical
44        });
45        handlers.push(VectorEntry {
46            name: "MemManage_Handler".to_string(),
47            address: 0,
48            weak: true,
49        });
50        handlers.push(VectorEntry {
51            name: "BusFault_Handler".to_string(),
52            address: 0,
53            weak: true,
54        });
55        handlers.push(VectorEntry {
56            name: "UsageFault_Handler".to_string(),
57            address: 0,
58            weak: true,
59        });
60        // Reserved entries (4)
61        for _ in 0..4 {
62            handlers.push(VectorEntry {
63                name: "Reserved".to_string(),
64                address: 0,
65                weak: true,
66            });
67        }
68        handlers.push(VectorEntry {
69            name: "SVC_Handler".to_string(),
70            address: 0,
71            weak: true,
72        });
73        handlers.push(VectorEntry {
74            name: "DebugMon_Handler".to_string(),
75            address: 0,
76            weak: true,
77        });
78        handlers.push(VectorEntry {
79            name: "Reserved".to_string(),
80            address: 0,
81            weak: true,
82        });
83        handlers.push(VectorEntry {
84            name: "PendSV_Handler".to_string(),
85            address: 0,
86            weak: true,
87        });
88        handlers.push(VectorEntry {
89            name: "SysTick_Handler".to_string(),
90            address: 0,
91            weak: true,
92        });
93
94        // External interrupts (typically 16-240 depending on device)
95        // For PoC, add 16 generic IRQ handlers
96        for i in 0..16 {
97            handlers.push(VectorEntry {
98                name: format!("IRQ{}_Handler", i),
99                address: 0,
100                weak: true,
101            });
102        }
103
104        Self {
105            initial_sp: stack_top,
106            reset_handler: 0,
107            handlers,
108        }
109    }
110
111    /// Generate vector table as binary data
112    pub fn generate_binary(&self) -> Result<Vec<u8>> {
113        let mut data = Vec::new();
114
115        // Entry 0: Initial stack pointer
116        data.extend_from_slice(&self.initial_sp.to_le_bytes());
117
118        // Entry 1: Reset handler
119        // Thumb mode requires LSB set to 1
120        let reset_addr = self.reset_handler | 1;
121        data.extend_from_slice(&reset_addr.to_le_bytes());
122
123        // Remaining handlers
124        for handler in &self.handlers {
125            let addr = if handler.address != 0 {
126                handler.address | 1 // Thumb mode bit
127            } else {
128                0 // Will be resolved by linker
129            };
130            data.extend_from_slice(&addr.to_le_bytes());
131        }
132
133        Ok(data)
134    }
135
136    /// Generate assembly source for vector table
137    pub fn generate_assembly(&self) -> String {
138        let mut asm = String::new();
139
140        asm.push_str("    .syntax unified\n");
141        asm.push_str("    .cpu cortex-m3\n");
142        asm.push_str("    .fpu softvfp\n");
143        asm.push_str("    .thumb\n\n");
144
145        asm.push_str("    .section .isr_vector,\"a\",%progbits\n");
146        asm.push_str("    .type g_pfnVectors, %object\n");
147        asm.push_str("    .size g_pfnVectors, .-g_pfnVectors\n\n");
148
149        asm.push_str("g_pfnVectors:\n");
150        asm.push_str("    .word _estack\n");
151        asm.push_str("    .word Reset_Handler\n");
152
153        for handler in &self.handlers {
154            asm.push_str(&format!("    .word {}\n", handler.name));
155        }
156
157        asm.push('\n');
158
159        // Define weak default handlers
160        asm.push_str("    .weak NMI_Handler\n");
161        asm.push_str("    .thumb_set NMI_Handler,Default_Handler\n\n");
162
163        for handler in &self.handlers {
164            if handler.weak && handler.name != "Reserved" {
165                asm.push_str(&format!("    .weak {}\n", handler.name));
166                asm.push_str(&format!(
167                    "    .thumb_set {},Default_Handler\n",
168                    handler.name
169                ));
170            }
171        }
172
173        asm.push('\n');
174        asm.push_str("    .section .text.Default_Handler,\"ax\",%progbits\n");
175        asm.push_str("Default_Handler:\n");
176        asm.push_str("Infinite_Loop:\n");
177        asm.push_str("    b Infinite_Loop\n");
178        asm.push_str("    .size Default_Handler, .-Default_Handler\n");
179
180        asm
181    }
182
183    /// Get total size in bytes
184    pub fn size_bytes(&self) -> usize {
185        // SP + Reset + handlers
186        4 + 4 + (self.handlers.len() * 4)
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_vector_table_creation() {
196        let vt = VectorTable::new_cortex_m(0x20010000);
197        assert_eq!(vt.initial_sp, 0x20010000);
198        assert!(vt.handlers.len() > 15); // At least standard exceptions
199    }
200
201    #[test]
202    fn test_vector_table_binary_generation() {
203        let mut vt = VectorTable::new_cortex_m(0x20010000);
204        vt.reset_handler = 0x08000100;
205
206        let binary = vt.generate_binary().unwrap();
207
208        // Check initial SP
209        let sp = u32::from_le_bytes([binary[0], binary[1], binary[2], binary[3]]);
210        assert_eq!(sp, 0x20010000);
211
212        // Check reset handler (with Thumb bit)
213        let reset = u32::from_le_bytes([binary[4], binary[5], binary[6], binary[7]]);
214        assert_eq!(reset, 0x08000101); // LSB set for Thumb
215    }
216
217    #[test]
218    fn test_vector_table_size() {
219        let vt = VectorTable::new_cortex_m(0x20010000);
220        let size = vt.size_bytes();
221        let binary = vt.generate_binary().unwrap();
222        assert_eq!(size, binary.len());
223    }
224
225    #[test]
226    fn test_assembly_generation() {
227        let vt = VectorTable::new_cortex_m(0x20010000);
228        let asm = vt.generate_assembly();
229
230        assert!(asm.contains(".syntax unified"));
231        assert!(asm.contains("g_pfnVectors:"));
232        assert!(asm.contains("Reset_Handler"));
233        assert!(asm.contains("Default_Handler"));
234    }
235
236    #[test]
237    fn test_thumb_mode_bit() {
238        let mut vt = VectorTable::new_cortex_m(0x20010000);
239        vt.reset_handler = 0x08000100; // Even address
240        vt.handlers[0].address = 0x08000200;
241
242        let binary = vt.generate_binary().unwrap();
243
244        // Reset handler should have bit 0 set
245        let reset = u32::from_le_bytes([binary[4], binary[5], binary[6], binary[7]]);
246        assert_eq!(reset & 1, 1);
247
248        // First handler should have bit 0 set
249        let handler = u32::from_le_bytes([binary[8], binary[9], binary[10], binary[11]]);
250        assert_eq!(handler & 1, 1);
251    }
252}