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 #[cfg(feature = "build-rs")]
64 pub fn to_cargo_outdir(&self, filename: &str) -> std::io::Result<()> {
65 use std::path::PathBuf;
66
67 let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
68 self.to_file(out.join(filename))?;
69
70 println!("cargo:rustc-link-search={}", out.display());
71 Ok(())
72 }
73}
74
75#[derive(Debug)]
76pub struct MemorySection {
77 name: String,
78 attrs: Option<String>,
79 origin: u64,
80 length: u64,
81 pagesize: u64,
82}
83
84impl Default for Memory {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90impl MemorySection {
91 pub fn new(name: impl Into<String>, origin: u64, length: u64) -> MemorySection {
92 Self {
93 name: name.into(),
94 origin,
95 length,
96 attrs: None,
97 pagesize: 1,
98 }
99 }
100
101 pub fn offset(self, offset: u64) -> MemorySection {
102 Self {
103 name: self.name,
104 origin: self.origin + offset,
105 length: self.length - offset,
106 attrs: self.attrs,
107 pagesize: self.pagesize,
108 }
109 }
110
111 pub fn pagesize(self, pagesize: u64) -> MemorySection {
112 Self {
113 name: self.name,
114 origin: self.origin,
115 length: self.length,
116 attrs: self.attrs,
117 pagesize,
118 }
119 }
120
121 pub fn slot(self, slot: usize, num_slots: usize) -> MemorySection {
128 assert!(slot < num_slots);
129
130 fn align_add(val: u64, alignment: u64) -> u64 {
131 if val % alignment != 0 {
132 (val + alignment) - val % alignment
133 } else {
134 val
135 }
136 }
137
138 fn align_sub(mut val: u64, alignment: u64) -> u64 {
139 val -= val % alignment;
140 val
141 }
142
143 let origin = align_add(self.origin, self.pagesize);
145 let end = align_sub(self.origin + self.length, self.pagesize);
146
147 let slot_length = align_sub((end - origin) / num_slots as u64, self.pagesize);
148 let slot_origin = origin + (slot as u64 * slot_length);
149
150 Self {
151 name: self.name,
152 origin: slot_origin,
153 length: slot_length,
154 attrs: self.attrs,
155 pagesize: self.pagesize,
156 }
157 }
158
159 pub fn from_env(self) -> MemorySection {
186 self.from_env_with_prefix("LDMEMORY")
187 }
188
189 pub fn from_env_with_prefix(self, prefix: &str) -> MemorySection {
193 use std::env::var;
194 let offset_env = &[prefix, "OFFSET"].join("_");
195 let num_slots_env = &[prefix, "NUM_SLOTS"].join("_");
196 let slot_env = &[prefix, "SLOT"].join("_");
197 let pagesize_env = &[prefix, "PAGESIZE"].join("_");
198 let slot_offset_env = &[prefix, "SLOT_OFFSET"].join("_");
199
200 let mut res = self;
201 if let Ok(offset) = var(offset_env) {
202 let offset = offset
203 .parse_dec_or_hex()
204 .unwrap_or_else(|_| panic!("parsing {}", &offset_env));
205 res = res.offset(offset);
206 }
207
208 if let Ok(pagesize) = var(pagesize_env) {
209 let pagesize = pagesize
210 .parse_dec_or_hex()
211 .unwrap_or_else(|_| panic!("parsing {}", &pagesize_env));
212 res = res.pagesize(pagesize);
213 }
214
215 if let Ok(slot) = var(slot_env) {
216 let slot: usize = slot
217 .parse::<usize>()
218 .unwrap_or_else(|_| panic!("parsing {}", slot_env));
219 let num_slots: usize = var(num_slots_env)
220 .unwrap_or("2".into())
221 .parse()
222 .unwrap_or_else(|_| panic!("parsing {}", &num_slots_env));
223 let slot_offset = var(slot_offset_env)
224 .unwrap_or("0".into())
225 .parse_dec_or_hex()
226 .unwrap_or_else(|_| panic!("parsing {}", &slot_offset_env));
227
228 res = res.slot(slot, num_slots);
229
230 if slot_offset > 0 {
231 res = res.offset(slot_offset);
232 }
233 }
234
235 if env::var("CARGO").is_ok() && env::var("OUT_DIR").is_ok() {
240 for var in [
241 offset_env,
242 num_slots_env,
243 slot_env,
244 slot_offset_env,
245 pagesize_env,
246 ]
247 .iter()
248 {
249 println!("cargo:rerun-if-env-changed={}", var);
250 }
251 }
252 res
253 }
254
255 pub fn attrs(self, attrs: &str) -> MemorySection {
256 Self {
257 name: self.name,
258 origin: self.origin,
259 length: self.length,
260 attrs: Some(attrs.into()),
261 pagesize: self.pagesize,
262 }
263 }
264
265 pub fn to_ldmemory(&self) -> String {
266 format!(
267 " {} {}: ORIGIN = {:#X}, LENGTH = {:#X}\n",
268 self.name,
269 self.attrs
270 .as_ref()
271 .map_or_else(|| "".to_string(), |attrs| format!("({})", attrs)),
272 self.origin,
273 self.length
274 )
275 }
276
277 pub fn start_symbol(&self) -> String {
285 format!("_{}_start", self.name)
286 }
287
288 pub fn length_symbol(&self) -> String {
296 format!("_{}_length", self.name)
297 }
298
299 pub fn ldmemory_start_symbol(&self) -> String {
307 format!("{} = {:#X};", self.start_symbol(), self.origin)
308 }
309
310 pub fn ldmemory_length_symbol(&self) -> String {
318 format!("{} = {:#X};", self.length_symbol(), self.length)
319 }
320}
321
322pub trait ParseDecOrHex {
324 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError>;
325}
326
327impl ParseDecOrHex for str {
328 fn parse_dec_or_hex(&self) -> Result<u64, ParseIntError> {
329 if let Some(hex) = self.strip_prefix("0x") {
330 u64::from_str_radix(hex, 16)
331 } else {
332 self.parse::<u64>()
333 }
334 }
335}
336
337#[cfg(feature = "parse")]
338pub mod parse {
339 pub fn parse_section(section_str: &str) -> std::result::Result<crate::MemorySection, String> {
340 let components = section_str.split(":").collect::<Vec<&str>>();
341 if components.len() < 3 {
342 return Err("invalid section spec (\"<NAME>:<START>:<SIZE>[:<OFFSET>]\")".into());
343 }
344
345 let (name, attrs) = parse_name_attrs(components[0]);
346 let mut section =
347 crate::MemorySection::new(name, parse_expr(components[1])?, parse_expr(components[2])?);
348
349 if !attrs.is_empty() {
350 section = section.attrs(attrs);
351 }
352
353 if components.len() == 4 {
354 section = section.offset(parse_expr(components[3])?);
355 }
356
357 Ok(section)
358 }
359
360 fn parse_name_attrs(input: &str) -> (&str, &str) {
361 let start_index = input.find('(');
362 let end_index = input.rfind(')');
363
364 if let (Some(start), Some(end)) = (start_index, end_index) {
365 let name = &input[..start];
366 let value = &input[start + 1..end];
367 (name.trim(), value)
368 } else {
369 (input, "")
370 }
371 }
372
373 fn parse_expr(expr: &str) -> Result<u64, String> {
374 if expr.is_empty() {
375 return Ok(0);
376 }
377
378 let expr = &apply_kilobyte(expr);
379
380 evalexpr::eval_int(expr)
381 .map_err(|e| e.to_string())
382 .and_then(|v| {
383 if v >= 0 {
384 Ok(v as u64)
385 } else {
386 Err("expression evaluates to negative integer".into())
387 }
388 })
389 }
390
391 fn apply_kilobyte(s: &str) -> String {
392 let mut result = String::new();
393 let mut chars = s.chars().peekable();
394
395 while let Some(c) = chars.next() {
396 if c.is_ascii_digit() {
397 let mut num_str = String::new();
398 num_str.push(c);
399
400 while let Some(next_c) = chars.peek() {
401 if next_c.is_ascii_digit() {
402 num_str.push(*next_c);
403 chars.next();
404 } else {
405 break;
406 }
407 }
408
409 if let Some('K') = chars.peek() {
410 chars.next();
411 let num = num_str.parse::<i32>().unwrap();
412 let replacement = format!("({} * 1024)", num);
413 result.push_str(&replacement);
414 } else {
415 result.push_str(&num_str);
416 }
417 } else {
418 result.push(c);
419 }
420 }
421
422 result
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use super::{Memory, MemorySection};
429 #[test]
430 fn basic_memory() {
431 let memory = Memory::new();
432 assert_eq!(memory.to_ldmemory(), "MEMORY\n{\n}\n");
433 }
434
435 #[test]
436 fn basic_section() {
437 let section = MemorySection::new("SectionName", 0, 0xFFFF);
438 assert_eq!(
439 section.to_ldmemory(),
440 " SectionName : ORIGIN = 0x0, LENGTH = 0xFFFF\n"
441 );
442 }
443
444 #[test]
445 fn section_offset() {
446 let section = MemorySection::new("SectionName", 0, 0x10000).offset(0x1000);
447 assert_eq!(
448 section.to_ldmemory(),
449 " SectionName : ORIGIN = 0x1000, LENGTH = 0xF000\n"
450 );
451 }
452
453 #[test]
454 fn section_attrs() {
455 let section = MemorySection::new("SectionName", 0, 0x10000).attrs("r!w!x");
456 assert_eq!(
457 section.to_ldmemory(),
458 " SectionName (r!w!x): ORIGIN = 0x0, LENGTH = 0x10000\n"
459 );
460 }
461
462 #[test]
463 fn complex() {
464 let memory = Memory::new().add_section(
465 MemorySection::new("SectionName", 0, 0x10000)
466 .offset(0x1000)
467 .attrs("rw!x"),
468 );
469
470 assert_eq!(
471 memory.to_ldmemory(),
472 concat!(
473 "_SectionName_start = 0x1000;\n",
474 "_SectionName_length = 0xF000;\n",
475 "\n",
476 "MEMORY\n{\n",
477 " SectionName (rw!x): ORIGIN = 0x1000, LENGTH = 0xF000\n",
478 "}\n"
479 )
480 );
481 }
482}