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