1use crate::mpu::{MPUAttributes, MPUPermissions, MPURegion, MPUSize};
6use synth_core::{Error, HardwareCapabilities, Memory, Result};
7
8#[derive(Debug, Clone)]
10pub struct MPUAllocationRequest {
11 pub memory: Memory,
13
14 pub permissions: MPUPermissions,
16
17 pub attributes: MPUAttributes,
19
20 pub preferred_base: Option<u32>,
22}
23
24pub struct MPUAllocator {
26 hw_caps: HardwareCapabilities,
28
29 allocated: Vec<MPURegion>,
31}
32
33impl MPUAllocator {
34 pub fn new(hw_caps: HardwareCapabilities) -> Self {
36 Self {
37 hw_caps,
38 allocated: Vec::new(),
39 }
40 }
41
42 pub fn allocate(&mut self, request: MPUAllocationRequest) -> Result<Vec<MPURegion>> {
44 let size_bytes = request.memory.initial as u64 * 65536; if self.allocated.len() >= self.hw_caps.mpu_regions as usize {
49 return Err(Error::HardwareProtectionError(format!(
50 "No MPU regions available (max: {})",
51 self.hw_caps.mpu_regions
52 )));
53 }
54
55 let mpu_size = MPUSize::from_bytes(size_bytes)?;
57 let actual_size = mpu_size.bytes();
58
59 let base_address = request.preferred_base.unwrap_or(0x20000000);
61
62 let alignment = actual_size as u32;
64 let aligned_base = (base_address + alignment - 1) & !(alignment - 1);
65
66 let region_number = self.allocated.len() as u8;
68 let mut region = MPURegion::new(region_number, aligned_base, mpu_size);
69 region.permissions = request.permissions;
70 region.attributes = request.attributes;
71
72 region.validate()?;
74
75 for existing in &self.allocated {
77 if self.regions_overlap(®ion, existing) {
78 return Err(Error::HardwareProtectionError(format!(
79 "Region overlap detected: 0x{:08X} overlaps with existing region at 0x{:08X}",
80 region.base_address, existing.base_address
81 )));
82 }
83 }
84
85 self.allocated.push(region.clone());
87
88 Ok(vec![region])
89 }
90
91 fn regions_overlap(&self, r1: &MPURegion, r2: &MPURegion) -> bool {
93 let r1_start = r1.base_address as u64;
94 let r1_end = r1_start + r1.size.bytes();
95 let r2_start = r2.base_address as u64;
96 let r2_end = r2_start + r2.size.bytes();
97
98 !(r1_end <= r2_start || r2_end <= r1_start)
100 }
101
102 pub fn allocated_regions(&self) -> &[MPURegion] {
104 &self.allocated
105 }
106
107 pub fn available_regions(&self) -> u8 {
109 self.hw_caps.mpu_regions - self.allocated.len() as u8
110 }
111
112 pub fn generate_init_code(&self) -> String {
114 let mut code = String::new();
115
116 code.push_str("/* MPU Initialization Code */\n");
117 code.push_str("/* Generated by Synth WebAssembly Component Synthesizer */\n\n");
118 code.push_str("#include <stdint.h>\n\n");
119 code.push_str("/* MPU Register Addresses (ARM Cortex-M) */\n");
120 code.push_str("#define MPU_TYPE (*((volatile uint32_t*)0xE000ED90))\n");
121 code.push_str("#define MPU_CTRL (*((volatile uint32_t*)0xE000ED94))\n");
122 code.push_str("#define MPU_RNR (*((volatile uint32_t*)0xE000ED98))\n");
123 code.push_str("#define MPU_RBAR (*((volatile uint32_t*)0xE000ED9C))\n");
124 code.push_str("#define MPU_RASR (*((volatile uint32_t*)0xE000EDA0))\n\n");
125 code.push_str("/* MPU Control Register bits */\n");
126 code.push_str("#define MPU_CTRL_ENABLE (1 << 0)\n");
127 code.push_str("#define MPU_CTRL_HFNMIENA (1 << 1)\n");
128 code.push_str("#define MPU_CTRL_PRIVDEFENA (1 << 2)\n\n");
129 code.push_str("void mpu_init(void) {\n");
130 code.push_str(" /* Disable MPU during configuration */\n");
131 code.push_str(" MPU_CTRL = 0;\n\n");
132
133 for region in &self.allocated {
134 code.push_str(&format!(
135 " /* Region {}: 0x{:08X} - {} bytes */\n",
136 region.number,
137 region.base_address,
138 region.size.bytes()
139 ));
140 code.push_str(&format!(" MPU_RNR = {};\n", region.number));
141 code.push_str(&format!(" MPU_RBAR = 0x{:08X};\n", region.rbar()));
142 code.push_str(&format!(" MPU_RASR = 0x{:08X};\n\n", region.rasr()));
143 }
144
145 code.push_str(" /* Enable MPU with default memory map for privileged access */\n");
146 code.push_str(" MPU_CTRL = MPU_CTRL_ENABLE | MPU_CTRL_PRIVDEFENA;\n");
147 code.push_str("}\n");
148
149 code
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156 use synth_core::{CortexMVariant, TargetArch};
157
158 fn test_hardware() -> HardwareCapabilities {
159 HardwareCapabilities {
160 arch: TargetArch::ARMCortexM(CortexMVariant::M4F),
161 has_mpu: true,
162 mpu_regions: 8,
163 has_pmp: false,
164 pmp_entries: 0,
165 has_fpu: true,
166 fpu_precision: Some(synth_core::FPUPrecision::Single),
167 has_simd: false,
168 simd_level: None,
169 xip_capable: true,
170 flash_size: 1024 * 1024,
171 ram_size: 256 * 1024,
172 }
173 }
174
175 #[test]
176 fn test_allocate_single_region() {
177 let mut allocator = MPUAllocator::new(test_hardware());
178
179 let request = MPUAllocationRequest {
180 memory: Memory {
181 index: 0,
182 initial: 1, maximum: None,
184 shared: false,
185 memory64: false,
186 },
187 permissions: MPUPermissions::FullRW,
188 attributes: MPUAttributes::normal(),
189 preferred_base: Some(0x20000000),
190 };
191
192 let regions = allocator.allocate(request).unwrap();
193 assert_eq!(regions.len(), 1);
194 assert_eq!(regions[0].size, MPUSize::Size64KB);
195 }
196
197 #[test]
198 fn test_available_regions() {
199 let mut allocator = MPUAllocator::new(test_hardware());
200 assert_eq!(allocator.available_regions(), 8);
201
202 let request = MPUAllocationRequest {
203 memory: Memory {
204 index: 0,
205 initial: 1,
206 maximum: None,
207 shared: false,
208 memory64: false,
209 },
210 permissions: MPUPermissions::FullRW,
211 attributes: MPUAttributes::normal(),
212 preferred_base: Some(0x20000000),
213 };
214
215 allocator.allocate(request).unwrap();
216 assert_eq!(allocator.available_regions(), 7);
217 }
218
219 #[test]
220 fn test_generate_init_code() {
221 let mut allocator = MPUAllocator::new(test_hardware());
222
223 let request = MPUAllocationRequest {
224 memory: Memory {
225 index: 0,
226 initial: 1,
227 maximum: None,
228 shared: false,
229 memory64: false,
230 },
231 permissions: MPUPermissions::FullRW,
232 attributes: MPUAttributes::normal(),
233 preferred_base: Some(0x20000000),
234 };
235
236 allocator.allocate(request).unwrap();
237 let code = allocator.generate_init_code();
238
239 assert!(code.contains("void mpu_init(void)"));
240 assert!(code.contains("MPU_CTRL"));
241 assert!(code.contains("Region 0"));
242 }
243
244 #[test]
245 fn test_nrf52840_configuration() {
246 let hw_caps = HardwareCapabilities::nrf52840();
248 let mut allocator = MPUAllocator::new(hw_caps);
249
250 let text_request = MPUAllocationRequest {
255 memory: Memory {
256 index: 0,
257 initial: 2, maximum: None,
259 shared: false,
260 memory64: false,
261 },
262 permissions: MPUPermissions::FullRO,
263 attributes: MPUAttributes {
264 shareable: false,
265 cacheable: true,
266 bufferable: false,
267 execute_never: false, },
269 preferred_base: Some(0x00000000), };
271
272 let text_regions = allocator.allocate(text_request).unwrap();
273 assert_eq!(text_regions.len(), 1);
274 assert_eq!(text_regions[0].base_address, 0x00000000);
275 assert!(text_regions[0].size.bytes() >= 128 * 1024);
276
277 let rodata_request = MPUAllocationRequest {
279 memory: Memory {
280 index: 1,
281 initial: 1, maximum: None,
283 shared: false,
284 memory64: false,
285 },
286 permissions: MPUPermissions::FullRO,
287 attributes: MPUAttributes {
288 shareable: false,
289 cacheable: true,
290 bufferable: false,
291 execute_never: true, },
293 preferred_base: Some(0x00020000), };
295
296 let rodata_regions = allocator.allocate(rodata_request).unwrap();
297 assert_eq!(rodata_regions.len(), 1);
298
299 let data_request = MPUAllocationRequest {
302 memory: Memory {
303 index: 2,
304 initial: 1, maximum: None,
306 shared: false,
307 memory64: false,
308 },
309 permissions: MPUPermissions::FullRW,
310 attributes: MPUAttributes::normal(),
311 preferred_base: Some(0x20000000), };
313
314 let data_regions = allocator.allocate(data_request).unwrap();
315 assert_eq!(data_regions.len(), 1);
316 assert_eq!(data_regions[0].base_address, 0x20000000);
317 assert_eq!(data_regions[0].permissions, MPUPermissions::FullRW);
318
319 assert_eq!(allocator.available_regions(), 5);
321 assert_eq!(allocator.allocated_regions().len(), 3);
322
323 let init_code = allocator.generate_init_code();
325
326 assert!(init_code.contains("Region 0"));
328 assert!(init_code.contains("Region 1"));
329 assert!(init_code.contains("Region 2"));
330 assert!(init_code.contains("0x00000000")); assert!(init_code.contains("0x20000000")); println!("\nGenerated MPU initialization code for nRF52840:");
335 println!("{}", init_code);
336
337 for region in allocator.allocated_regions() {
339 assert!(region.validate().is_ok());
340 }
341 }
342
343 #[test]
344 fn test_imxrt1062_has_16_regions() {
345 let hw_caps = HardwareCapabilities::imxrt1062();
347 assert_eq!(hw_caps.mpu_regions, 16);
348
349 let allocator = MPUAllocator::new(hw_caps);
350 assert_eq!(allocator.available_regions(), 16);
351 }
352
353 #[test]
354 fn test_m7_can_allocate_more_than_8_regions() {
355 let mut allocator = MPUAllocator::new(HardwareCapabilities::imxrt1062());
357
358 for i in 0u32..16 {
359 let request = MPUAllocationRequest {
360 memory: Memory {
361 index: i,
362 initial: 1,
363 maximum: None,
364 shared: false,
365 memory64: false,
366 },
367 permissions: MPUPermissions::FullRW,
368 attributes: MPUAttributes::normal(),
369 preferred_base: Some(0x20000000 + i * 0x10000),
370 };
371 allocator.allocate(request).unwrap_or_else(|e| {
372 panic!("region {} allocation failed: {:?}", i, e);
373 });
374 }
375
376 assert_eq!(allocator.available_regions(), 0);
377 assert_eq!(allocator.allocated_regions().len(), 16);
378 }
379
380 #[test]
381 fn test_m4_class_caps_at_8_regions() {
382 let mut allocator = MPUAllocator::new(HardwareCapabilities::nrf52840());
384
385 for i in 0u32..8 {
386 let request = MPUAllocationRequest {
387 memory: Memory {
388 index: i,
389 initial: 1,
390 maximum: None,
391 shared: false,
392 memory64: false,
393 },
394 permissions: MPUPermissions::FullRW,
395 attributes: MPUAttributes::normal(),
396 preferred_base: Some(0x20000000 + i * 0x10000),
397 };
398 allocator.allocate(request).unwrap();
399 }
400
401 let overflow = MPUAllocationRequest {
403 memory: Memory {
404 index: 8,
405 initial: 1,
406 maximum: None,
407 shared: false,
408 memory64: false,
409 },
410 permissions: MPUPermissions::FullRW,
411 attributes: MPUAttributes::normal(),
412 preferred_base: Some(0x20100000),
413 };
414 assert!(allocator.allocate(overflow).is_err());
415 }
416
417 #[test]
418 fn test_stm32h743_has_16_regions_and_double_fpu() {
419 let caps = HardwareCapabilities::stm32h743();
420 assert_eq!(caps.mpu_regions, 16);
421 assert!(caps.has_fpu);
422 assert_eq!(caps.fpu_precision, Some(synth_core::FPUPrecision::Double));
423 }
424}