1use std::env;
10use std::num::ParseIntError;
11use std::path::Path;
12use std::result::Result;
13
14#[derive(Debug)]
15pub struct Memory {
16 sections: Vec<MemorySection>,
17}
18
19impl Memory {
20 pub fn new() -> Memory {
21 Memory {
22 sections: Vec::new(),
23 }
24 }
25
26 pub fn add_section(self, section: MemorySection) -> Memory {
27 let mut sections = self.sections;
28 sections.push(section);
29 Memory { sections }
30 }
31
32 pub fn to_ldmemory(&self) -> String {
33 let mut out = String::new();
34
35 out.extend(self.sections.iter().flat_map(|section| {
37 [
38 section.ldmemory_start_symbol(),
39 "\n".into(),
40 section.ldmemory_length_symbol(),
41 "\n".into(),
42 ]
43 }));
44
45 if !&self.sections.is_empty() {
48 out.push('\n');
49 }
50
51 out.push_str("MEMORY\n{\n");
52 for section in &self.sections {
53 out.push_str(§ion.to_ldmemory());
54 }
55 out.push_str("}\n");
56 out
57 }
58
59 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> std::io::Result<()> {
60 std::fs::write(path, self.to_ldmemory())
61 }
62
63 pub fn sections(&self) -> &Vec<MemorySection> {
65 &self.sections
66 }
67
68 #[cfg(feature = "build-rs")]
69 pub fn to_cargo_outdir(&self, filename: &str) -> std::io::Result<()> {
70 use std::path::PathBuf;
71
72 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
73 self.to_file(out.join(filename))?;
74
75 println!("cargo:rustc-link-search={}", out.display());
76 Ok(())
77 }
78}
79
80#[derive(Debug)]
81pub struct MemorySection {
82 name: String,
83 attrs: Option<String>,
84 origin: u64,
85 length: u64,
86 pagesize: u64,
87}
88
89impl Default for Memory {
90 fn default() -> Self {
91 Self::new()
92 }
93}
94
95impl MemorySection {
96 pub fn new(name: impl Into<String>, origin: u64, length: u64) -> MemorySection {
97 Self {
98 name: name.into(),
99 origin,
100 length,
101 attrs: None,
102 pagesize: 1,
103 }
104 }
105
106 pub fn offset(self, offset: u64) -> MemorySection {
107 Self {
108 name: self.name,
109 origin: self.origin + offset,
110 length: self.length - offset,
111 attrs: self.attrs,
112 pagesize: self.pagesize,
113 }
114 }
115
116 pub fn pagesize(self, pagesize: u64) -> MemorySection {
117 Self {
118 name: self.name,
119 origin: self.origin,
120 length: self.length,
121 attrs: self.attrs,
122 pagesize,
123 }
124 }
125
126 pub fn slot(self, slot: usize, num_slots: usize) -> MemorySection {
133 assert!(slot < num_slots);
134
135 fn align_add(val: u64, alignment: u64) -> u64 {
136 if val % alignment != 0 {
137 (val + alignment) - val % alignment
138 } else {
139 val
140 }
141 }
142
143 fn align_sub(mut val: u64, alignment: u64) -> u64 {
144 val -= val % alignment;
145 val
146 }
147
148 let origin = align_add(self.origin, self.pagesize);
150 let end = align_sub(self.origin + self.length, self.pagesize);
151
152 let slot_length = align_sub((end - origin) / num_slots as u64, self.pagesize);
153 let slot_origin = origin + (slot as u64 * slot_length);
154
155 Self {
156 name: self.name,
157 origin: slot_origin,
158 length: slot_length,
159 attrs: self.attrs,
160 pagesize: self.pagesize,
161 }
162 }
163
164 pub fn from_env(self) -> MemorySection {
191 self.from_env_with_prefix("LDMEMORY")
192 }
193
194 pub fn from_env_with_prefix(self, prefix: &str) -> MemorySection {
198 use std::env::var;
199 let offset_env = &[prefix, "OFFSET"].join("_");
200 let num_slots_env = &[prefix, "NUM_SLOTS"].join("_");
201 let slot_env = &[prefix, "SLOT"].join("_");
202 let pagesize_env = &[prefix, "PAGESIZE"].join("_");
203 let slot_offset_env = &[prefix, "SLOT_OFFSET"].join("_");
204
205 let mut res = self;
206 if let Ok(offset) = var(offset_env) {
207 let offset = offset
208 .parse_dec_or_hex()
209 .unwrap_or_else(|_| panic!("parsing {}", &offset_env));
210 res = res.offset(offset);
211 }
212
213 if let Ok(pagesize) = var(pagesize_env) {
214 let pagesize = pagesize
215 .parse_dec_or_hex()
216 .unwrap_or_else(|_| panic!("parsing {}", &pagesize_env));
217 res = res.pagesize(pagesize);
218 }
219
220 if let Ok(slot) = var(slot_env) {
221 let slot: usize = slot
222 .parse::<usize>()
223 .unwrap_or_else(|_| panic!("parsing {}", slot_env));
224 let num_slots: usize = var(num_slots_env)
225 .unwrap_or("2".into())
226 .parse()
227 .unwrap_or_else(|_| panic!("parsing {}", &num_slots_env));
228 let slot_offset = var(slot_offset_env)
229 .unwrap_or("0".into())
230 .parse_dec_or_hex()
231 .unwrap_or_else(|_| panic!("parsing {}", &slot_offset_env));
232
233 res = res.slot(slot, num_slots);
234
235 if slot_offset > 0 {
236 res = res.offset(slot_offset);
237 }
238 }
239
240 if env::var("CARGO").is_ok() && env::var("OUT_DIR").is_ok() {
245 for var in [
246 offset_env,
247 num_slots_env,
248 slot_env,
249 slot_offset_env,
250 pagesize_env,
251 ]
252 .iter()
253 {
254 println!("cargo:rerun-if-env-changed={}", var);
255 }
256 }
257 res
258 }
259
260 pub fn attrs(self, attrs: &str) -> MemorySection {
261 Self {
262 name: self.name,
263 origin: self.origin,
264 length: self.length,
265 attrs: Some(attrs.into()),
266 pagesize: self.pagesize,
267 }
268 }
269
270 pub fn to_ldmemory(&self) -> String {
271 format!(
272 " {} {}: ORIGIN = {:#X}, LENGTH = {:#X}\n",
273 self.name,
274 self.attrs
275 .as_ref()
276 .map_or_else(|| "".to_string(), |attrs| format!("({})", attrs)),
277 self.origin,
278 self.length
279 )
280 }
281
282 pub fn start_symbol(&self) -> String {
290 format!("_{}_start", self.name)
291 }
292
293 pub fn length_symbol(&self) -> String {
301 format!("_{}_length", self.name)
302 }
303
304 pub fn ldmemory_start_symbol(&self) -> String {
312 format!("{} = {:#X};", self.start_symbol(), self.origin)
313 }
314
315 pub fn ldmemory_length_symbol(&self) -> String {
323 format!("{} = {:#X};", self.length_symbol(), self.length)
324 }
325
326 pub fn get_name(&self) -> &String {
328 &self.name
329 }
330
331 pub fn get_length(&self) -> u64 {
333 self.length
334 }
335
336 pub fn get_origin(&self) -> u64 {
338 self.origin
339 }
340
341 pub fn get_attrs(&self) -> Option<&String> {
343 self.attrs.as_ref()
344 }
345
346 pub fn get_pagesize(&self) -> u64 {
348 self.pagesize
349 }
350}
351
352pub trait ParseDecOrHex {
354 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError>;
355}
356
357impl ParseDecOrHex for str {
358 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError> {
359 if let Some(hex) = self.strip_prefix("0x") {
360 u64::from_str_radix(hex, 16)
361 } else {
362 self.parse::<u64>()
363 }
364 }
365}
366
367#[cfg(feature = "parse")]
368pub mod parse {
369 pub fn parse_section(section_str: &str) -> std::result::Result<crate::MemorySection, String> {
370 let components = section_str.split(":").collect::<Vec<&str>>();
371 if components.len() < 3 {
372 return Err("invalid section spec (\"<NAME>:<START>:<SIZE>[:<OFFSET>]\")".into());
373 }
374
375 let (name, attrs) = parse_name_attrs(components[0]);
376 let mut section =
377 crate::MemorySection::new(name, parse_expr(components[1])?, parse_expr(components[2])?);
378
379 if !attrs.is_empty() {
380 section = section.attrs(attrs);
381 }
382
383 if components.len() == 4 {
384 section = section.offset(parse_expr(components[3])?);
385 }
386
387 Ok(section)
388 }
389
390 fn parse_name_attrs(input: &str) -> (&str, &str) {
391 let start_index = input.find('(');
392 let end_index = input.rfind(')');
393
394 if let (Some(start), Some(end)) = (start_index, end_index) {
395 let name = &input[..start];
396 let value = &input[start + 1..end];
397 (name.trim(), value)
398 } else {
399 (input, "")
400 }
401 }
402
403 fn parse_expr(expr: &str) -> Result<u64, String> {
404 if expr.is_empty() {
405 return Ok(0);
406 }
407
408 let expr = &apply_kilobyte(expr);
409
410 evalexpr::eval_int(expr)
411 .map_err(|e| e.to_string())
412 .and_then(|v| {
413 if v >= 0 {
414 Ok(v as u64)
415 } else {
416 Err("expression evaluates to negative integer".into())
417 }
418 })
419 }
420
421 fn apply_kilobyte(s: &str) -> String {
422 let mut result = String::new();
423 let mut chars = s.chars().peekable();
424
425 while let Some(c) = chars.next() {
426 if c.is_ascii_digit() {
427 let mut num_str = String::new();
428 num_str.push(c);
429
430 while let Some(next_c) = chars.peek() {
431 if next_c.is_ascii_digit() {
432 num_str.push(*next_c);
433 chars.next();
434 } else {
435 break;
436 }
437 }
438
439 if let Some('K') = chars.peek() {
440 chars.next();
441 let num = num_str.parse::<i32>().unwrap();
442 let replacement = format!("({} * 1024)", num);
443 result.push_str(&replacement);
444 } else {
445 result.push_str(&num_str);
446 }
447 } else {
448 result.push(c);
449 }
450 }
451
452 result
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::{Memory, MemorySection};
459 #[test]
460 fn basic_memory() {
461 let memory = Memory::new();
462 assert_eq!(memory.to_ldmemory(), "MEMORY\n{\n}\n");
463 }
464
465 #[test]
466 fn basic_section() {
467 let section = MemorySection::new("SectionName", 0, 0xFFFF);
468 assert_eq!(
469 section.to_ldmemory(),
470 " SectionName : ORIGIN = 0x0, LENGTH = 0xFFFF\n"
471 );
472 }
473
474 #[test]
475 fn section_offset() {
476 let section = MemorySection::new("SectionName", 0, 0x10000).offset(0x1000);
477 assert_eq!(
478 section.to_ldmemory(),
479 " SectionName : ORIGIN = 0x1000, LENGTH = 0xF000\n"
480 );
481 }
482
483 #[test]
484 fn section_attrs() {
485 let section = MemorySection::new("SectionName", 0, 0x10000).attrs("r!w!x");
486 assert_eq!(
487 section.to_ldmemory(),
488 " SectionName (r!w!x): ORIGIN = 0x0, LENGTH = 0x10000\n"
489 );
490 }
491
492 #[test]
493 fn complex() {
494 let memory = Memory::new().add_section(
495 MemorySection::new("SectionName", 0, 0x10000)
496 .offset(0x1000)
497 .attrs("rw!x"),
498 );
499
500 assert_eq!(
501 memory.to_ldmemory(),
502 concat!(
503 "_SectionName_start = 0x1000;\n",
504 "_SectionName_length = 0xF000;\n",
505 "\n",
506 "MEMORY\n{\n",
507 " SectionName (rw!x): ORIGIN = 0x1000, LENGTH = 0xF000\n",
508 "}\n"
509 )
510 );
511 }
512}