Skip to main content

synth_backend/
reset_handler.rs

1//! Reset Handler Code Generation
2//!
3//! Generates the Reset_Handler startup code for ARM Cortex-M
4
5use crate::arm_encoder::ArmEncoder;
6use synth_core::Result;
7use synth_synthesis::{ArmOp, MemAddr, Operand2, Reg};
8
9/// Reset handler generator
10pub struct ResetHandlerGenerator {
11    /// Stack top address
12    stack_top: u32,
13    /// Data section start in RAM
14    data_start: u32,
15    /// Data section end in RAM
16    data_end: u32,
17    /// Data section load address in Flash
18    data_load_addr: u32,
19    /// BSS section start
20    bss_start: u32,
21    /// BSS section end
22    bss_end: u32,
23}
24
25impl ResetHandlerGenerator {
26    /// Create a new reset handler generator
27    pub fn new() -> Self {
28        Self {
29            stack_top: 0x20010000,      // 64KB RAM top
30            data_start: 0x20000000,     // RAM start
31            data_end: 0x20000100,       // 256 bytes data
32            data_load_addr: 0x08001000, // Flash location
33            bss_start: 0x20000100,      // After data
34            bss_end: 0x20001000,        // 3.75KB BSS
35        }
36    }
37
38    /// Configure memory regions
39    pub fn with_memory_layout(
40        mut self,
41        stack_top: u32,
42        data_start: u32,
43        data_end: u32,
44        data_load: u32,
45        bss_start: u32,
46        bss_end: u32,
47    ) -> Self {
48        self.stack_top = stack_top;
49        self.data_start = data_start;
50        self.data_end = data_end;
51        self.data_load_addr = data_load;
52        self.bss_start = bss_start;
53        self.bss_end = bss_end;
54        self
55    }
56
57    /// Generate ARM instructions for reset handler
58    #[allow(clippy::vec_init_then_push)]
59    pub fn generate_instructions(&self) -> Vec<ArmOp> {
60        let mut instrs = Vec::new();
61
62        // Copy .data section from Flash to RAM
63        // R0 = source (Flash load address)
64        // R1 = destination (RAM data start)
65        // R2 = end address (RAM data end)
66
67        // Load data_load_addr into R0 using MOVW/MOVT (full 32-bit)
68        instrs.push(ArmOp::Movw {
69            rd: Reg::R0,
70            imm16: (self.data_load_addr & 0xFFFF) as u16,
71        });
72        instrs.push(ArmOp::Movt {
73            rd: Reg::R0,
74            imm16: (self.data_load_addr >> 16) as u16,
75        });
76
77        // Load data_start into R1
78        instrs.push(ArmOp::Movw {
79            rd: Reg::R1,
80            imm16: (self.data_start & 0xFFFF) as u16,
81        });
82        instrs.push(ArmOp::Movt {
83            rd: Reg::R1,
84            imm16: (self.data_start >> 16) as u16,
85        });
86
87        // Load data_end into R2
88        instrs.push(ArmOp::Movw {
89            rd: Reg::R2,
90            imm16: (self.data_end & 0xFFFF) as u16,
91        });
92        instrs.push(ArmOp::Movt {
93            rd: Reg::R2,
94            imm16: (self.data_end >> 16) as u16,
95        });
96
97        // .data copy loop: copy words from Flash (R0) to RAM (R1) until R1 == R2
98        // .Lcopy_check:
99        instrs.push(ArmOp::Label {
100            name: ".Lcopy_check".to_string(),
101        });
102        instrs.push(ArmOp::Cmp {
103            rn: Reg::R1,
104            op2: Operand2::Reg(Reg::R2),
105        });
106        // BHS .Lcopy_done (branch if R1 >= R2, i.e., done)
107        instrs.push(ArmOp::Bhs {
108            label: ".Lcopy_done".to_string(),
109        });
110        // LDR R3, [R0], #4 — load word from source, post-increment
111        // Using explicit load + add since post-increment may not be available
112        instrs.push(ArmOp::Ldr {
113            rd: Reg::R3,
114            addr: MemAddr {
115                base: Reg::R0,
116                offset: 0,
117                offset_reg: None,
118            },
119        });
120        instrs.push(ArmOp::Add {
121            rd: Reg::R0,
122            rn: Reg::R0,
123            op2: Operand2::Imm(4),
124        });
125        // STR R3, [R1], #4 — store word to dest, post-increment
126        instrs.push(ArmOp::Str {
127            rd: Reg::R3,
128            addr: MemAddr {
129                base: Reg::R1,
130                offset: 0,
131                offset_reg: None,
132            },
133        });
134        instrs.push(ArmOp::Add {
135            rd: Reg::R1,
136            rn: Reg::R1,
137            op2: Operand2::Imm(4),
138        });
139        // B .Lcopy_check
140        instrs.push(ArmOp::B {
141            label: ".Lcopy_check".to_string(),
142        });
143        instrs.push(ArmOp::Label {
144            name: ".Lcopy_done".to_string(),
145        });
146
147        // Zero .bss section
148        // R0 = bss start, R1 = bss end, R2 = zero value
149        instrs.push(ArmOp::Movw {
150            rd: Reg::R0,
151            imm16: (self.bss_start & 0xFFFF) as u16,
152        });
153        instrs.push(ArmOp::Movt {
154            rd: Reg::R0,
155            imm16: (self.bss_start >> 16) as u16,
156        });
157
158        instrs.push(ArmOp::Movw {
159            rd: Reg::R1,
160            imm16: (self.bss_end & 0xFFFF) as u16,
161        });
162        instrs.push(ArmOp::Movt {
163            rd: Reg::R1,
164            imm16: (self.bss_end >> 16) as u16,
165        });
166
167        instrs.push(ArmOp::Mov {
168            rd: Reg::R2,
169            op2: Operand2::Imm(0),
170        });
171
172        // .bss zero loop: zero words from R0 to R1
173        // .Lzero_check:
174        instrs.push(ArmOp::Label {
175            name: ".Lzero_check".to_string(),
176        });
177        instrs.push(ArmOp::Cmp {
178            rn: Reg::R0,
179            op2: Operand2::Reg(Reg::R1),
180        });
181        // BHS .Lzero_done (branch if R0 >= R1)
182        instrs.push(ArmOp::Bhs {
183            label: ".Lzero_done".to_string(),
184        });
185        instrs.push(ArmOp::Str {
186            rd: Reg::R2,
187            addr: MemAddr {
188                base: Reg::R0,
189                offset: 0,
190                offset_reg: None,
191            },
192        });
193        instrs.push(ArmOp::Add {
194            rd: Reg::R0,
195            rn: Reg::R0,
196            op2: Operand2::Imm(4),
197        });
198        // B .Lzero_check
199        instrs.push(ArmOp::B {
200            label: ".Lzero_check".to_string(),
201        });
202        instrs.push(ArmOp::Label {
203            name: ".Lzero_done".to_string(),
204        });
205
206        // Call main
207        instrs.push(ArmOp::Bl {
208            label: "main".to_string(),
209        });
210
211        // Infinite loop after main returns
212        instrs.push(ArmOp::B {
213            label: ".".to_string(), // Branch to self
214        });
215
216        instrs
217    }
218
219    /// Generate complete reset handler assembly
220    pub fn generate_assembly(&self) -> String {
221        let mut asm = String::new();
222
223        asm.push_str("    .syntax unified\n");
224        asm.push_str("    .cpu cortex-m3\n");
225        asm.push_str("    .fpu softvfp\n");
226        asm.push_str("    .thumb\n\n");
227
228        asm.push_str("    .section .text.Reset_Handler\n");
229        asm.push_str("    .weak Reset_Handler\n");
230        asm.push_str("    .type Reset_Handler, %function\n");
231        asm.push_str("Reset_Handler:\n");
232
233        // Copy .data section
234        asm.push_str("    /* Copy data section from Flash to RAM */\n");
235        asm.push_str("    ldr r0, =_sidata      /* start of .data in Flash */\n");
236        asm.push_str("    ldr r1, =_sdata       /* start of .data in RAM */\n");
237        asm.push_str("    ldr r2, =_edata       /* end of .data in RAM */\n");
238        asm.push_str("    movs r3, #0\n");
239        asm.push_str("    b LoopCopyDataInit\n\n");
240
241        asm.push_str("CopyDataInit:\n");
242        asm.push_str("    ldr r4, [r0, r3]\n");
243        asm.push_str("    str r4, [r1, r3]\n");
244        asm.push_str("    adds r3, r3, #4\n\n");
245
246        asm.push_str("LoopCopyDataInit:\n");
247        asm.push_str("    adds r4, r1, r3\n");
248        asm.push_str("    cmp r4, r2\n");
249        asm.push_str("    bcc CopyDataInit\n\n");
250
251        // Zero .bss section
252        asm.push_str("    /* Zero fill .bss section */\n");
253        asm.push_str("    ldr r2, =_sbss\n");
254        asm.push_str("    ldr r4, =_ebss\n");
255        asm.push_str("    movs r3, #0\n");
256        asm.push_str("    b LoopFillZerobss\n\n");
257
258        asm.push_str("FillZerobss:\n");
259        asm.push_str("    str r3, [r2]\n");
260        asm.push_str("    adds r2, r2, #4\n\n");
261
262        asm.push_str("LoopFillZerobss:\n");
263        asm.push_str("    cmp r2, r4\n");
264        asm.push_str("    bcc FillZerobss\n\n");
265
266        // Call static constructors (C++)
267        asm.push_str("    /* Call static constructors */\n");
268        asm.push_str("    bl __libc_init_array\n\n");
269
270        // Call main
271        asm.push_str("    /* Call main() */\n");
272        asm.push_str("    bl main\n\n");
273
274        // Infinite loop
275        asm.push_str("LoopForever:\n");
276        asm.push_str("    b LoopForever\n\n");
277
278        asm.push_str("    .size Reset_Handler, .-Reset_Handler\n");
279
280        asm
281    }
282
283    /// Generate binary code for reset handler
284    pub fn generate_binary(&self) -> Result<Vec<u8>> {
285        self.generate_binary_for_isa(false)
286    }
287
288    /// Generate binary code for reset handler with ISA selection
289    pub fn generate_binary_for_isa(&self, thumb_mode: bool) -> Result<Vec<u8>> {
290        let encoder = if thumb_mode {
291            ArmEncoder::new_thumb2()
292        } else {
293            ArmEncoder::new_arm32()
294        };
295        let instrs = self.generate_instructions();
296
297        let mut code = Vec::new();
298        for instr in &instrs {
299            let encoded = encoder.encode(instr)?;
300            code.extend_from_slice(&encoded);
301        }
302
303        Ok(code)
304    }
305}
306
307impl Default for ResetHandlerGenerator {
308    fn default() -> Self {
309        Self::new()
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316
317    #[test]
318    fn test_reset_handler_creation() {
319        let handler = ResetHandlerGenerator::new();
320        assert_eq!(handler.stack_top, 0x20010000);
321    }
322
323    #[test]
324    fn test_reset_handler_instructions() {
325        let handler = ResetHandlerGenerator::new();
326        let instrs = handler.generate_instructions();
327        assert!(!instrs.is_empty());
328
329        // Should end with branch to main and infinite loop
330        assert!(matches!(instrs[instrs.len() - 2], ArmOp::Bl { .. }));
331        assert!(matches!(instrs[instrs.len() - 1], ArmOp::B { .. }));
332    }
333
334    #[test]
335    fn test_reset_handler_binary() {
336        let handler = ResetHandlerGenerator::new();
337        let binary = handler.generate_binary().unwrap();
338        assert!(!binary.is_empty());
339        assert_eq!(binary.len() % 4, 0); // ARM32 instructions are 4 bytes
340    }
341
342    #[test]
343    fn test_reset_handler_assembly() {
344        let handler = ResetHandlerGenerator::new();
345        let asm = handler.generate_assembly();
346
347        assert!(asm.contains("Reset_Handler:"));
348        assert!(asm.contains("CopyDataInit"));
349        assert!(asm.contains("FillZerobss"));
350        assert!(asm.contains("bl main"));
351    }
352
353    #[test]
354    fn test_custom_memory_layout() {
355        let handler = ResetHandlerGenerator::new().with_memory_layout(
356            0x20020000, // 128KB RAM
357            0x20000000, 0x20000200, 0x08002000, 0x20000200, 0x20002000,
358        );
359
360        assert_eq!(handler.stack_top, 0x20020000);
361        assert_eq!(handler.data_start, 0x20000000);
362    }
363}