1use synth_core::{Component, Error, HardwareCapabilities, Result};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum SectionType {
10 Text,
12 ReadOnlyData,
14 Data,
16 Bss,
18 Stack,
20 Heap,
22}
23
24#[derive(Debug, Clone)]
26pub struct MemorySection {
27 pub section_type: SectionType,
29
30 pub name: String,
32
33 pub base_address: u32,
35
36 pub size: u32,
38
39 pub alignment: u32,
41
42 pub in_flash: bool,
44}
45
46impl MemorySection {
47 pub fn end_address(&self) -> u32 {
49 self.base_address + self.size
50 }
51
52 pub fn overlaps(&self, other: &MemorySection) -> bool {
54 let self_end = self.end_address();
55 let other_end = other.end_address();
56
57 !(self_end <= other.base_address || other_end <= self.base_address)
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct MemoryLayout {
64 hw_caps: HardwareCapabilities,
66
67 sections: Vec<MemorySection>,
69
70 flash_usage: u32,
72
73 ram_usage: u32,
75}
76
77impl MemoryLayout {
78 pub fn new(hw_caps: HardwareCapabilities) -> Self {
80 Self {
81 hw_caps,
82 sections: Vec::new(),
83 flash_usage: 0,
84 ram_usage: 0,
85 }
86 }
87
88 pub fn add_section(&mut self, section: MemorySection) -> Result<()> {
90 for existing in &self.sections {
92 if section.overlaps(existing) {
93 return Err(Error::MemoryLayoutError(format!(
94 "Section '{}' at 0x{:08X} overlaps with '{}' at 0x{:08X}",
95 section.name, section.base_address, existing.name, existing.base_address
96 )));
97 }
98 }
99
100 if section.in_flash {
102 self.flash_usage += section.size;
103 } else {
104 self.ram_usage += section.size;
105 }
106
107 self.sections.push(section);
108 Ok(())
109 }
110
111 pub fn sections(&self) -> &[MemorySection] {
113 &self.sections
114 }
115
116 pub fn flash_usage(&self) -> u32 {
118 self.flash_usage
119 }
120
121 pub fn ram_usage(&self) -> u32 {
123 self.ram_usage
124 }
125
126 pub fn validate(&self) -> Result<()> {
128 if self.flash_usage as u64 > self.hw_caps.flash_size {
130 return Err(Error::MemoryLayoutError(format!(
131 "Flash usage {} bytes exceeds capacity {} bytes",
132 self.flash_usage, self.hw_caps.flash_size
133 )));
134 }
135
136 if self.ram_usage as u64 > self.hw_caps.ram_size {
138 return Err(Error::MemoryLayoutError(format!(
139 "RAM usage {} bytes exceeds capacity {} bytes",
140 self.ram_usage, self.hw_caps.ram_size
141 )));
142 }
143
144 Ok(())
145 }
146
147 pub fn get_section(&self, section_type: SectionType) -> Option<&MemorySection> {
149 self.sections
150 .iter()
151 .find(|s| s.section_type == section_type)
152 }
153
154 pub fn generate_linker_script(&self) -> String {
156 let mut script = String::new();
157
158 script.push_str("/* Linker script for ARM Cortex-M */\n");
159 script.push_str("/* Generated by Synth WebAssembly Component Synthesizer */\n\n");
160
161 script.push_str("MEMORY\n{\n");
163 script.push_str(&format!(
164 " FLASH (rx) : ORIGIN = 0x00000000, LENGTH = {}K\n",
165 self.hw_caps.flash_size / 1024
166 ));
167 script.push_str(&format!(
168 " RAM (rwx) : ORIGIN = 0x20000000, LENGTH = {}K\n",
169 self.hw_caps.ram_size / 1024
170 ));
171 script.push_str("}\n\n");
172
173 script.push_str("ENTRY(Reset_Handler)\n\n");
175
176 let stack_section = self.get_section(SectionType::Stack);
178 let stack_size = stack_section.map(|s| s.size).unwrap_or(4096);
179 script.push_str(&format!("_stack_size = {};\n\n", stack_size));
180
181 let heap_section = self.get_section(SectionType::Heap);
183 let heap_size = heap_section.map(|s| s.size).unwrap_or(8192);
184 script.push_str(&format!("_heap_size = {};\n\n", heap_size));
185
186 script.push_str("SECTIONS\n{\n");
188
189 script.push_str(" .text :\n");
191 script.push_str(" {\n");
192 script.push_str(" KEEP(*(.isr_vector))\n");
193 script.push_str(" *(.text*)\n");
194 script.push_str(" *(.rodata*)\n");
195 script.push_str(" KEEP(*(.init))\n");
196 script.push_str(" KEEP(*(.fini))\n");
197 script.push_str(" . = ALIGN(4);\n");
198 script.push_str(" _etext = .;\n");
199 script.push_str(" } > FLASH\n\n");
200
201 script.push_str(" .ARM.exidx :\n");
203 script.push_str(" {\n");
204 script.push_str(" __exidx_start = .;\n");
205 script.push_str(" *(.ARM.exidx* .gnu.linkonce.armexidx.*)\n");
206 script.push_str(" __exidx_end = .;\n");
207 script.push_str(" } > FLASH\n\n");
208
209 script.push_str(" .data :\n");
211 script.push_str(" {\n");
212 script.push_str(" _sdata = .;\n");
213 script.push_str(" *(.data*)\n");
214 script.push_str(" . = ALIGN(4);\n");
215 script.push_str(" _edata = .;\n");
216 script.push_str(" } > RAM AT> FLASH\n\n");
217 script.push_str(" _sidata = LOADADDR(.data);\n\n");
218
219 script.push_str(" .bss :\n");
221 script.push_str(" {\n");
222 script.push_str(" _sbss = .;\n");
223 script.push_str(" *(.bss*)\n");
224 script.push_str(" *(COMMON)\n");
225 script.push_str(" . = ALIGN(4);\n");
226 script.push_str(" _ebss = .;\n");
227 script.push_str(" } > RAM\n\n");
228
229 script.push_str(" .heap :\n");
231 script.push_str(" {\n");
232 script.push_str(" _heap_start = .;\n");
233 script.push_str(" . = . + _heap_size;\n");
234 script.push_str(" _heap_end = .;\n");
235 script.push_str(" } > RAM\n\n");
236
237 script.push_str(" .stack :\n");
239 script.push_str(" {\n");
240 script.push_str(" . = . + _stack_size;\n");
241 script.push_str(" _stack_top = .;\n");
242 script.push_str(" } > RAM\n\n");
243
244 script.push_str(" /* Remove information from the standard libraries */\n");
245 script.push_str(" /DISCARD/ :\n");
246 script.push_str(" {\n");
247 script.push_str(" libc.a ( * )\n");
248 script.push_str(" libm.a ( * )\n");
249 script.push_str(" libgcc.a ( * )\n");
250 script.push_str(" }\n");
251
252 script.push_str("}\n");
253
254 script
255 }
256}
257
258pub struct MemoryLayoutAnalyzer {
260 hw_caps: HardwareCapabilities,
261}
262
263impl MemoryLayoutAnalyzer {
264 pub fn new(hw_caps: HardwareCapabilities) -> Self {
266 Self { hw_caps }
267 }
268
269 pub fn analyze(&self, component: &Component) -> Result<MemoryLayout> {
271 let mut layout = MemoryLayout::new(self.hw_caps.clone());
272
273 let text_size = self.estimate_text_size(component);
275 let rodata_size = self.estimate_rodata_size(component);
276 let data_size = self.estimate_data_size(component);
277 let bss_size = self.estimate_bss_size(component);
278 let stack_size = self.estimate_stack_size(component);
279 let heap_size = self.estimate_heap_size(component);
280
281 let flash_base = 0x00000000;
284 let ram_base = 0x20000000;
285
286 let mut current_flash = flash_base;
287 let mut current_ram = ram_base;
288
289 if text_size > 0 {
291 let text_section = MemorySection {
292 section_type: SectionType::Text,
293 name: ".text".to_string(),
294 base_address: current_flash,
295 size: text_size,
296 alignment: 4,
297 in_flash: true,
298 };
299 current_flash = align_up(text_section.end_address(), 4);
300 layout.add_section(text_section)?;
301 }
302
303 if rodata_size > 0 {
305 let rodata_section = MemorySection {
306 section_type: SectionType::ReadOnlyData,
307 name: ".rodata".to_string(),
308 base_address: current_flash,
309 size: rodata_size,
310 alignment: 4,
311 in_flash: true,
312 };
313 layout.add_section(rodata_section)?;
314 }
315
316 if data_size > 0 {
318 let data_section = MemorySection {
319 section_type: SectionType::Data,
320 name: ".data".to_string(),
321 base_address: current_ram,
322 size: data_size,
323 alignment: 4,
324 in_flash: false,
325 };
326 current_ram = align_up(data_section.end_address(), 4);
327 layout.add_section(data_section)?;
328 }
329
330 if bss_size > 0 {
332 let bss_section = MemorySection {
333 section_type: SectionType::Bss,
334 name: ".bss".to_string(),
335 base_address: current_ram,
336 size: bss_size,
337 alignment: 4,
338 in_flash: false,
339 };
340 current_ram = align_up(bss_section.end_address(), 4);
341 layout.add_section(bss_section)?;
342 }
343
344 if heap_size > 0 {
346 let heap_section = MemorySection {
347 section_type: SectionType::Heap,
348 name: ".heap".to_string(),
349 base_address: current_ram,
350 size: heap_size,
351 alignment: 8,
352 in_flash: false,
353 };
354 layout.add_section(heap_section)?;
355 }
356
357 if stack_size > 0 {
359 let stack_base = self.hw_caps.ram_size as u32 - stack_size;
360 let stack_section = MemorySection {
361 section_type: SectionType::Stack,
362 name: ".stack".to_string(),
363 base_address: stack_base,
364 size: stack_size,
365 alignment: 8,
366 in_flash: false,
367 };
368 layout.add_section(stack_section)?;
369 }
370
371 layout.validate()?;
373
374 Ok(layout)
375 }
376
377 fn estimate_text_size(&self, component: &Component) -> u32 {
379 let mut size = 0u32;
383
384 for module in &component.modules {
385 size += (module.functions.len() as u32) * 128; }
388
389 align_up(size, 4)
391 }
392
393 fn estimate_rodata_size(&self, component: &Component) -> u32 {
395 let mut size = 0u32;
396
397 for module in &component.modules {
398 size += (module.globals.len() as u32) * 4;
400 }
401
402 align_up(size, 4)
403 }
404
405 fn estimate_data_size(&self, component: &Component) -> u32 {
407 let mut size = 0u32;
408
409 for module in &component.modules {
410 for memory in &module.memories {
412 size += memory.initial * 65536; }
414 }
415
416 align_up(size, 4)
417 }
418
419 fn estimate_bss_size(&self, _component: &Component) -> u32 {
421 align_up(4096, 4)
423 }
424
425 fn estimate_stack_size(&self, _component: &Component) -> u32 {
427 let stack_size = 256 * 16;
429
430 align_up(stack_size, 8)
431 }
432
433 fn estimate_heap_size(&self, _component: &Component) -> u32 {
435 align_up(8192, 8)
438 }
439}
440
441fn align_up(value: u32, alignment: u32) -> u32 {
443 (value + alignment - 1) & !(alignment - 1)
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449 use std::collections::HashMap;
450 use synth_core::{CoreModule, Function, FunctionSignature, Global, Memory, ValueType};
451
452 fn test_component() -> Component {
453 Component {
454 name: "test".to_string(),
455 modules: vec![CoreModule {
456 id: "test_module".to_string(),
457 binary: vec![],
458 functions: vec![Function {
459 index: 0,
460 name: Some("add".to_string()),
461 signature: FunctionSignature {
462 params: vec![ValueType::I32, ValueType::I32],
463 results: vec![ValueType::I32],
464 },
465 exported: true,
466 imported: false,
467 }],
468 memories: vec![Memory {
469 index: 0,
470 initial: 1, maximum: None,
472 shared: false,
473 memory64: false,
474 }],
475 tables: vec![],
476 globals: vec![Global {
477 index: 0,
478 value_type: ValueType::I32,
479 mutable: false,
480 }],
481 }],
482 components: vec![],
483 instances: vec![],
484 interfaces: HashMap::new(),
485 imports: vec![],
486 exports: vec![],
487 }
488 }
489
490 #[test]
491 fn test_memory_section_overlap() {
492 let section1 = MemorySection {
493 section_type: SectionType::Text,
494 name: ".text".to_string(),
495 base_address: 0x00000000,
496 size: 1024,
497 alignment: 4,
498 in_flash: true,
499 };
500
501 let section2 = MemorySection {
502 section_type: SectionType::ReadOnlyData,
503 name: ".rodata".to_string(),
504 base_address: 0x00000400,
505 size: 512,
506 alignment: 4,
507 in_flash: true,
508 };
509
510 let section3 = MemorySection {
511 section_type: SectionType::Data,
512 name: ".data".to_string(),
513 base_address: 0x00000200, size: 512,
515 alignment: 4,
516 in_flash: false,
517 };
518
519 assert!(!section1.overlaps(§ion2));
520 assert!(section1.overlaps(§ion3));
521 }
522
523 #[test]
524 fn test_memory_layout_creation() {
525 let hw_caps = HardwareCapabilities::nrf52840();
526 let analyzer = MemoryLayoutAnalyzer::new(hw_caps);
527 let component = test_component();
528
529 let layout = analyzer.analyze(&component).unwrap();
530
531 assert!(!layout.sections().is_empty());
533
534 assert!(layout.flash_usage() > 0);
536
537 assert!(layout.ram_usage() > 0);
539
540 assert!(layout.validate().is_ok());
542 }
543
544 #[test]
545 fn test_memory_layout_validation() {
546 let hw_caps = HardwareCapabilities::nrf52840();
547 let analyzer = MemoryLayoutAnalyzer::new(hw_caps);
548 let component = test_component();
549
550 let layout = analyzer.analyze(&component).unwrap();
551
552 println!("\nMemory Layout:");
554 println!(
555 "Flash usage: {} / {} bytes",
556 layout.flash_usage(),
557 layout.flash_usage
558 );
559 println!(
560 "RAM usage: {} / {} bytes",
561 layout.ram_usage(),
562 layout.ram_usage
563 );
564 println!("\nSections:");
565 for section in layout.sections() {
566 println!(
567 " {} ({:?}): 0x{:08X} - 0x{:08X} ({} bytes, {})",
568 section.name,
569 section.section_type,
570 section.base_address,
571 section.end_address(),
572 section.size,
573 if section.in_flash { "flash" } else { "RAM" }
574 );
575 }
576
577 assert!(layout.validate().is_ok());
578 }
579
580 #[test]
581 fn test_linker_script_generation() {
582 let hw_caps = HardwareCapabilities::nrf52840();
583 let analyzer = MemoryLayoutAnalyzer::new(hw_caps);
584 let component = test_component();
585
586 let layout = analyzer.analyze(&component).unwrap();
587 let linker_script = layout.generate_linker_script();
588
589 println!("\nGenerated Linker Script:");
591 println!("{}", linker_script);
592
593 assert!(linker_script.contains("MEMORY"));
595 assert!(linker_script.contains("FLASH"));
596 assert!(linker_script.contains("RAM"));
597 assert!(linker_script.contains("ENTRY(Reset_Handler)"));
598 assert!(linker_script.contains(".text"));
599 assert!(linker_script.contains(".data"));
600 assert!(linker_script.contains(".bss"));
601 assert!(linker_script.contains(".heap"));
602 assert!(linker_script.contains(".stack"));
603 assert!(linker_script.contains("_sdata"));
604 assert!(linker_script.contains("_edata"));
605 assert!(linker_script.contains("_sbss"));
606 assert!(linker_script.contains("_ebss"));
607 assert!(linker_script.contains("_stack_top"));
608 }
609}