Skip to main content

probe_rs_target/
memory.rs

1use crate::serialize::{hex_range, hex_u_int};
2use serde::{Deserialize, Serialize};
3use std::{iter::Peekable, ops::Range};
4
5/// Represents a region in non-volatile memory (e.g. flash or EEPROM).
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(deny_unknown_fields)]
8pub struct NvmRegion {
9    /// A name to describe the region
10    pub name: Option<String>,
11    /// Address range of the region
12    #[serde(serialize_with = "hex_range")]
13    pub range: Range<u64>,
14    /// List of cores that can access this region
15    pub cores: Vec<String>,
16    /// True if the memory region is an alias of a different memory region.
17    #[serde(default)]
18    pub is_alias: bool,
19    /// Access permissions for the region.
20    #[serde(default)]
21    pub access: Option<MemoryAccess>,
22}
23
24impl NvmRegion {
25    /// Returns whether the region is accessible by the given core.
26    pub fn accessible_by(&self, core_name: &str) -> bool {
27        self.cores.iter().any(|c| c == core_name)
28    }
29
30    /// Returns the access permissions for the region.
31    pub fn access(&self) -> MemoryAccess {
32        self.access.unwrap_or_default()
33    }
34
35    /// Returns whether the region is readable.
36    pub fn is_readable(&self) -> bool {
37        self.access().read
38    }
39
40    /// Returns whether the region is writable.
41    pub fn is_writable(&self) -> bool {
42        self.access().write
43    }
44
45    /// Returns whether the region is executable.
46    pub fn is_executable(&self) -> bool {
47        self.access().execute
48    }
49
50    /// Returns whether the region is boot memory.
51    pub fn is_boot_memory(&self) -> bool {
52        self.access().boot
53    }
54}
55
56impl NvmRegion {
57    /// Returns the necessary information about the NVM.
58    pub fn nvm_info(&self) -> NvmInfo {
59        NvmInfo {
60            rom_start: self.range.start,
61        }
62    }
63}
64
65fn default_true() -> bool {
66    true
67}
68
69/// Represents access permissions of a region in RAM.
70#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
71pub struct MemoryAccess {
72    /// True if the region is readable.
73    #[serde(default = "default_true")]
74    pub read: bool,
75    /// True if the region is writable.
76    #[serde(default = "default_true")]
77    pub write: bool,
78    /// True if the region is executable.
79    #[serde(default = "default_true")]
80    pub execute: bool,
81    /// True if the chip boots from this memory
82    #[serde(default)]
83    pub boot: bool,
84}
85
86impl Default for MemoryAccess {
87    fn default() -> Self {
88        MemoryAccess {
89            read: true,
90            write: true,
91            execute: true,
92            boot: false,
93        }
94    }
95}
96
97/// Represents a region in RAM.
98#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
99#[serde(deny_unknown_fields)]
100pub struct RamRegion {
101    /// A name to describe the region
102    pub name: Option<String>,
103    /// Address range of the region
104    #[serde(serialize_with = "hex_range")]
105    pub range: Range<u64>,
106    /// List of cores that can access this region
107    pub cores: Vec<String>,
108    /// True if the memory region is an alias of a different memory region.
109    #[serde(default)]
110    pub is_alias: bool,
111    /// Access permissions for the region.
112    #[serde(default)]
113    pub access: Option<MemoryAccess>,
114}
115
116impl RamRegion {
117    /// Returns whether the region is accessible by the given core.
118    pub fn accessible_by(&self, core_name: &str) -> bool {
119        self.cores.iter().any(|c| c == core_name)
120    }
121
122    /// Returns the access permissions for the region.
123    pub fn access(&self) -> MemoryAccess {
124        self.access.unwrap_or_default()
125    }
126
127    /// Returns whether the region is readable.
128    pub fn is_readable(&self) -> bool {
129        self.access().read
130    }
131
132    /// Returns whether the region is writable.
133    pub fn is_writable(&self) -> bool {
134        self.access().write
135    }
136
137    /// Returns whether the region is executable.
138    pub fn is_executable(&self) -> bool {
139        self.access().execute
140    }
141
142    /// Returns whether the region is boot memory.
143    pub fn is_boot_memory(&self) -> bool {
144        self.access().boot
145    }
146}
147
148/// Merges adjacent regions if they have the same access permissions.
149pub trait RegionMergeIterator: Iterator {
150    /// Merge adjacent regions.
151    fn merge_consecutive(self) -> MergeConsecutive<Self>
152    where
153        Self: Sized;
154}
155
156impl<'a, I> RegionMergeIterator for I
157where
158    I: Iterator<Item = &'a RamRegion>,
159    I: Sized,
160{
161    fn merge_consecutive(self) -> MergeConsecutive<Self>
162    where
163        Self: Sized,
164    {
165        MergeConsecutive::new(self)
166    }
167}
168
169pub struct MergeConsecutive<I>
170where
171    I: Iterator,
172{
173    iter: Peekable<I>,
174}
175
176impl<I> MergeConsecutive<I>
177where
178    I: Iterator,
179{
180    fn new(iter: I) -> Self {
181        MergeConsecutive {
182            iter: iter.peekable(),
183        }
184    }
185}
186
187impl<I: Clone> Clone for MergeConsecutive<I>
188where
189    I: Iterator,
190    Peekable<I>: Clone,
191{
192    fn clone(&self) -> Self {
193        Self {
194            iter: self.iter.clone(),
195        }
196    }
197}
198
199impl<'iter, I> Iterator for MergeConsecutive<I>
200where
201    I: Iterator<Item = &'iter RamRegion>,
202{
203    type Item = RamRegion;
204
205    fn next(&mut self) -> Option<Self::Item> {
206        let mut region = self.iter.next()?.clone();
207        while let Some(next) = self.iter.peek() {
208            if region.range.end != next.range.start
209                || region.access != next.access
210                || region.is_alias != next.is_alias
211            {
212                break;
213            }
214
215            let common_cores = region
216                .cores
217                .iter()
218                .filter(|core| next.cores.contains(core))
219                .cloned()
220                .collect::<Vec<_>>();
221
222            // Do not return inaccessible regions.
223            if common_cores.is_empty() {
224                break;
225            }
226
227            region.cores = common_cores;
228            region.range.end = next.range.end;
229
230            self.iter.next();
231        }
232
233        Some(region)
234    }
235}
236
237/// Represents a generic region.
238#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
239#[serde(deny_unknown_fields)]
240pub struct GenericRegion {
241    /// A name to describe the region
242    pub name: Option<String>,
243    /// Address range of the region
244    #[serde(serialize_with = "hex_range")]
245    pub range: Range<u64>,
246    /// List of cores that can access this region
247    pub cores: Vec<String>,
248    /// Access permissions for the region.
249    #[serde(default)]
250    pub access: Option<MemoryAccess>,
251}
252
253impl GenericRegion {
254    /// Returns whether the region is accessible by the given core.
255    pub fn accessible_by(&self, core_name: &str) -> bool {
256        self.cores.iter().any(|c| c == core_name)
257    }
258
259    /// Returns the access permissions for the region.
260    pub fn access(&self) -> MemoryAccess {
261        self.access.unwrap_or_default()
262    }
263
264    /// Returns whether the region is readable.
265    pub fn is_readable(&self) -> bool {
266        self.access().read
267    }
268
269    /// Returns whether the region is writable.
270    pub fn is_writable(&self) -> bool {
271        self.access().write
272    }
273
274    /// Returns whether the region is executable.
275    pub fn is_executable(&self) -> bool {
276        self.access().execute
277    }
278}
279
280/// Holds information about a specific, individual flash
281/// sector.
282#[derive(Debug, Copy, Clone, PartialEq, Eq)]
283pub struct SectorInfo {
284    /// Base address of the flash sector
285    pub base_address: u64,
286    /// Size of the flash sector
287    pub size: u64,
288}
289
290impl SectorInfo {
291    /// Returns the address range of the sector.
292    pub fn address_range(&self) -> Range<u64> {
293        self.base_address..self.base_address + self.size
294    }
295}
296
297/// Information about a group of flash sectors, which
298/// is used as part of the [`FlashProperties`] struct.
299///
300/// The SectorDescription means that, starting at the
301/// flash address `address`, all following sectors will
302/// have a size of `size`. This is valid until either the
303/// end of the flash, or until another `SectorDescription`
304/// changes the sector size.
305///
306/// [`FlashProperties`]: crate::FlashProperties
307#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
308pub struct SectorDescription {
309    /// Size of each individual flash sector
310    #[serde(serialize_with = "hex_u_int")]
311    pub size: u64,
312    /// Start address of the group of flash sectors, relative
313    /// to the start address of the flash.
314    #[serde(serialize_with = "hex_u_int")]
315    pub address: u64,
316}
317
318/// Holds information about a page in flash.
319#[derive(Debug, Copy, Clone)]
320pub struct PageInfo {
321    /// Base address of the page in flash.
322    pub base_address: u64,
323    /// Size of the page
324    pub size: u32,
325}
326
327impl PageInfo {
328    /// Returns the address range of the sector.
329    pub fn address_range(&self) -> Range<u64> {
330        self.base_address..self.base_address + self.size as u64
331    }
332}
333
334/// Holds information about the entire flash.
335#[derive(Debug, Copy, Clone)]
336pub struct NvmInfo {
337    pub rom_start: u64,
338}
339
340/// Enables the user to do range intersection testing.
341pub trait MemoryRange {
342    /// Returns true if `self` contains `range` fully.
343    fn contains_range(&self, range: &Range<u64>) -> bool;
344
345    /// Returns true if `self` intersects `range` partially.
346    fn intersects_range(&self, range: &Range<u64>) -> bool;
347
348    /// Ensure memory reads using this memory range, will be aligned to 32 bits.
349    /// This may result in slightly more memory being read than requested.
350    fn align_to_32_bits(&mut self);
351}
352
353impl MemoryRange for Range<u64> {
354    fn contains_range(&self, range: &Range<u64>) -> bool {
355        if range.end == 0 {
356            false
357        } else {
358            self.contains(&range.start) && self.contains(&(range.end - 1))
359        }
360    }
361
362    fn intersects_range(&self, range: &Range<u64>) -> bool {
363        if range.end == 0 {
364            false
365        } else {
366            self.contains(&range.start) && !self.contains(&(range.end - 1))
367                || !self.contains(&range.start) && self.contains(&(range.end - 1))
368                || self.contains_range(range)
369                || range.contains_range(self)
370        }
371    }
372
373    fn align_to_32_bits(&mut self) {
374        if !self.start.is_multiple_of(4) {
375            self.start -= self.start % 4;
376        }
377        if !self.end.is_multiple_of(4) {
378            // Try to align the end to 32 bits, but don't overflow.
379            if let Some(new_end) = self.end.checked_add(4 - self.end % 4) {
380                self.end = new_end;
381            }
382        }
383    }
384}
385
386/// Declares the type of a memory region.
387#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
388pub enum MemoryRegion {
389    /// Memory region describing RAM.
390    Ram(RamRegion),
391    /// Generic memory region, which is neither
392    /// flash nor RAM.
393    Generic(GenericRegion),
394    /// Memory region describing flash, EEPROM or other non-volatile memory.
395    #[serde(alias = "Flash")] // Keeping the "Flash" name this for backwards compatibility
396    Nvm(NvmRegion),
397}
398
399impl MemoryRegion {
400    /// Returns the RAM region if this is a RAM region, otherwise None.
401    pub fn as_ram_region(&self) -> Option<&RamRegion> {
402        match self {
403            MemoryRegion::Ram(region) => Some(region),
404            _ => None,
405        }
406    }
407
408    /// Returns the NVM region if this is a NVM region, otherwise None.
409    pub fn as_nvm_region(&self) -> Option<&NvmRegion> {
410        match self {
411            MemoryRegion::Nvm(region) => Some(region),
412            _ => None,
413        }
414    }
415
416    /// Returns the address range of the memory region.
417    pub fn address_range(&self) -> Range<u64> {
418        match self {
419            MemoryRegion::Ram(rr) => rr.range.clone(),
420            MemoryRegion::Generic(gr) => gr.range.clone(),
421            MemoryRegion::Nvm(nr) => nr.range.clone(),
422        }
423    }
424
425    /// Returns whether the memory region contains the given address.
426    pub fn contains(&self, address: u64) -> bool {
427        self.address_range().contains(&address)
428    }
429
430    /// Get the cores to which this memory region belongs.
431    pub fn cores(&self) -> &[String] {
432        match self {
433            MemoryRegion::Ram(region) => &region.cores,
434            MemoryRegion::Generic(region) => &region.cores,
435            MemoryRegion::Nvm(region) => &region.cores,
436        }
437    }
438
439    /// Returns `true` if the memory region is [`Ram`].
440    ///
441    /// [`Ram`]: MemoryRegion::Ram
442    #[must_use]
443    pub fn is_ram(&self) -> bool {
444        matches!(self, Self::Ram(..))
445    }
446
447    /// Returns `true` if the memory region is [`Nvm`].
448    ///
449    /// [`Nvm`]: MemoryRegion::Nvm
450    #[must_use]
451    pub fn is_nvm(&self) -> bool {
452        matches!(self, Self::Nvm(..))
453    }
454}
455
456#[cfg(test)]
457mod test {
458    use super::*;
459
460    #[test]
461    fn contains_range1() {
462        let range1 = 0..1;
463        let range2 = 0..1;
464        assert!(range1.contains_range(&range2));
465    }
466
467    #[test]
468    fn contains_range2() {
469        let range1 = 0..1;
470        let range2 = 0..2;
471        assert!(!range1.contains_range(&range2));
472    }
473
474    #[test]
475    fn contains_range3() {
476        let range1 = 0..4;
477        let range2 = 0..1;
478        assert!(range1.contains_range(&range2));
479    }
480
481    #[test]
482    fn contains_range4() {
483        let range1 = 4..8;
484        let range2 = 3..9;
485        assert!(!range1.contains_range(&range2));
486    }
487
488    #[test]
489    fn contains_range5() {
490        let range1 = 4..8;
491        let range2 = 0..1;
492        assert!(!range1.contains_range(&range2));
493    }
494
495    #[test]
496    fn contains_range6() {
497        let range1 = 4..8;
498        let range2 = 6..8;
499        assert!(range1.contains_range(&range2));
500    }
501
502    #[test]
503    fn intersects_range1() {
504        let range1 = 0..1;
505        let range2 = 0..1;
506        assert!(range1.intersects_range(&range2));
507    }
508
509    #[test]
510    fn intersects_range2() {
511        let range1 = 0..1;
512        let range2 = 0..2;
513        assert!(range1.intersects_range(&range2));
514    }
515
516    #[test]
517    fn intersects_range3() {
518        let range1 = 0..4;
519        let range2 = 0..1;
520        assert!(range1.intersects_range(&range2));
521    }
522
523    #[test]
524    fn intersects_range4() {
525        let range1 = 4..8;
526        let range2 = 3..9;
527        assert!(range1.intersects_range(&range2));
528    }
529
530    #[test]
531    fn intersects_range5() {
532        let range1 = 4..8;
533        let range2 = 0..1;
534        assert!(!range1.intersects_range(&range2));
535    }
536
537    #[test]
538    fn intersects_range6() {
539        let range1 = 4..8;
540        let range2 = 6..8;
541        assert!(range1.intersects_range(&range2));
542    }
543
544    #[test]
545    fn intersects_range7() {
546        let range1 = 4..8;
547        let range2 = 3..4;
548        assert!(!range1.intersects_range(&range2));
549    }
550
551    #[test]
552    fn intersects_range8() {
553        let range1 = 8..9;
554        let range2 = 6..8;
555        assert!(!range1.intersects_range(&range2));
556    }
557
558    #[test]
559    fn intersects_range9() {
560        let range1 = 2..4;
561        let range2 = 6..8;
562        assert!(!range1.intersects_range(&range2));
563    }
564
565    #[test]
566    fn test_align_to_32_bits_case1() {
567        // Test case 1: start and end are already aligned
568        let mut range = Range { start: 0, end: 8 };
569        range.align_to_32_bits();
570        assert_eq!(range.start, 0);
571        assert_eq!(range.end, 8);
572    }
573
574    #[test]
575    fn test_align_to_32_bits_case2() {
576        // Test case 2: start is not aligned, end is aligned
577        let mut range = Range { start: 3, end: 12 };
578        range.align_to_32_bits();
579        assert_eq!(range.start, 0);
580        assert_eq!(range.end, 12);
581    }
582
583    #[test]
584    fn test_align_to_32_bits_case3() {
585        // Test case 3: start is aligned, end is not aligned
586        let mut range = Range { start: 16, end: 23 };
587        range.align_to_32_bits();
588        assert_eq!(range.start, 16);
589        assert_eq!(range.end, 24);
590    }
591
592    #[test]
593    fn test_align_to_32_bits_case4() {
594        // Test case 4: start and end are not aligned
595        let mut range = Range { start: 5, end: 13 };
596        range.align_to_32_bits();
597        assert_eq!(range.start, 4);
598        assert_eq!(range.end, 16);
599    }
600
601    #[test]
602    fn merge_consecutive_outputs_single_region() {
603        let regions = [RamRegion {
604            name: None,
605            range: 0..4,
606            cores: vec!["core0".to_string()],
607            access: None,
608            is_alias: false,
609        }];
610
611        let merged_regions: Vec<RamRegion> = regions.iter().merge_consecutive().collect();
612
613        assert_eq!(
614            merged_regions,
615            vec![RamRegion {
616                name: None,
617                range: 0..4,
618                cores: vec!["core0".to_string()],
619                access: None,
620                is_alias: false,
621            },]
622        );
623    }
624
625    #[test]
626    fn merge_consecutive_separates_ranges_with_different_cores() {
627        let regions = [
628            RamRegion {
629                name: None,
630                range: 0..4,
631                cores: vec!["core0".to_string()],
632                access: None,
633                is_alias: false,
634            },
635            RamRegion {
636                name: None,
637                range: 4..8,
638                cores: vec!["core1".to_string()],
639                access: None,
640                is_alias: false,
641            },
642            RamRegion {
643                name: None,
644                range: 8..12,
645                cores: vec!["core1".to_string()],
646                access: None,
647                is_alias: false,
648            },
649            RamRegion {
650                name: None,
651                range: 16..20,
652                cores: vec!["core1".to_string()],
653                access: None,
654                is_alias: false,
655            },
656        ];
657
658        let merged_regions: Vec<RamRegion> = regions.iter().merge_consecutive().collect();
659
660        assert_eq!(
661            merged_regions,
662            vec![
663                RamRegion {
664                    name: None,
665                    range: 0..4,
666                    cores: vec!["core0".to_string()],
667                    access: None,
668                    is_alias: false,
669                },
670                RamRegion {
671                    name: None,
672                    range: 4..12,
673                    cores: vec!["core1".to_string()],
674                    access: None,
675                    is_alias: false,
676                },
677                RamRegion {
678                    name: None,
679                    range: 16..20,
680                    cores: vec!["core1".to_string()],
681                    access: None,
682                    is_alias: false,
683                },
684            ]
685        );
686    }
687}