Skip to main content

peakrdl_rust/
mem.rs

1//! Memory abstraction used to read, write, and iterate over memory entries
2
3use crate::{
4    access::{Access, Read, Write},
5    endian::Endian,
6};
7use core::{
8    iter::{ExactSizeIterator, FusedIterator},
9    marker::PhantomData,
10    ops::{Bound, RangeBounds},
11};
12use num_traits::PrimInt;
13
14/// Behaviors common to all SystemRDL memories
15pub trait Memory: Sized {
16    /// Primitive integer type used to represented a memory entry
17    type Memwidth: PrimInt;
18    type Access: Access;
19    type Endian: Endian;
20
21    #[must_use]
22    fn first_entry_ptr(&self) -> *mut Self::Memwidth;
23
24    /// Number of memory entries
25    #[must_use]
26    fn num_entries(&self) -> usize;
27
28    /// Bit width of each memory entry
29    #[must_use]
30    fn width(&self) -> usize;
31
32    /// Access the memory entry at a specific index. Panics if out of bounds.
33    #[must_use]
34    fn index(&self, idx: usize) -> MemEntry<Self::Memwidth, Self::Access, Self::Endian> {
35        if idx < self.num_entries() {
36            unsafe { MemEntry::from_ptr(self.first_entry_ptr().wrapping_add(idx)) }
37        } else {
38            panic!(
39                "Tried to index {} in a memory with only {} entries",
40                idx,
41                self.num_entries()
42            );
43        }
44    }
45
46    /// Iterate over a range of memory entries
47    #[must_use]
48    fn slice(
49        &self,
50        range: impl RangeBounds<usize>,
51    ) -> MemEntryIter<Self::Memwidth, Self::Access, Self::Endian> {
52        let low_idx = match range.start_bound() {
53            Bound::Included(idx) => *idx,
54            Bound::Excluded(idx) => *idx + 1,
55            Bound::Unbounded => 0,
56        };
57        let high_idx = match range.end_bound() {
58            Bound::Included(idx) => *idx,
59            Bound::Excluded(idx) => *idx - 1,
60            Bound::Unbounded => self.num_entries() - 1,
61        };
62        let num_entries = high_idx - low_idx + 1;
63        MemEntryIter {
64            next: self.index(low_idx),
65            remaining: num_entries,
66        }
67    }
68
69    /// Iterate over all memory entries
70    #[must_use]
71    fn iter(&self) -> MemEntryIter<Self::Memwidth, Self::Access, Self::Endian> {
72        self.slice(..)
73    }
74}
75
76/// Representation of a single memory entry
77#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
78pub struct MemEntry<T: PrimInt, A: Access, E: Endian> {
79    ptr: *mut T,
80    phantom_access: PhantomData<A>,
81    phantom_endian: PhantomData<E>,
82}
83
84impl<T: PrimInt, A: Access, E: Endian> MemEntry<T, A, E> {
85    /// # Safety
86    ///
87    /// The caller must guarantee that the provided address points to a
88    /// hardware memory entry of size `T` with access `A` and endianness `E`.
89    #[must_use]
90    pub const unsafe fn from_ptr(ptr: *mut T) -> Self {
91        Self {
92            ptr,
93            phantom_access: PhantomData,
94            phantom_endian: PhantomData,
95        }
96    }
97
98    #[must_use]
99    pub const fn as_ptr(&self) -> *mut T {
100        self.ptr
101    }
102}
103
104impl<T: PrimInt, A: Read, E: Endian> MemEntry<T, A, E> {
105    /// Read the value of the hardware memory entry.
106    #[must_use]
107    pub fn read(&self) -> T {
108        // SAFETY: MemEntry can only be constructed through from_ptr(),
109        // which means the user has guaranteed the address points to
110        // a suitable hardware memory.
111        E::from_register_endian(unsafe { self.ptr.read_volatile() })
112    }
113}
114
115impl<T: PrimInt, A: Write, E: Endian> MemEntry<T, A, E> {
116    /// Write the provided value to the hardware memory entry.
117    pub fn write(&mut self, value: T) {
118        // SAFETY: MemEntry can only be constructed through from_ptr(),
119        // which means the user has guaranteed the address points to
120        // a suitable hardware memory.
121        unsafe { self.ptr.write_volatile(E::to_register_endian(value)) }
122    }
123}
124
125/// Iterator over memory entries
126#[derive(Debug)]
127pub struct MemEntryIter<T: PrimInt, A: Access, E: Endian> {
128    next: MemEntry<T, A, E>,
129    remaining: usize,
130}
131
132impl<T: PrimInt, A: Access, E: Endian> Iterator for MemEntryIter<T, A, E> {
133    type Item = MemEntry<T, A, E>;
134
135    fn next(&mut self) -> Option<Self::Item> {
136        if self.remaining == 0 {
137            None
138        } else {
139            self.remaining -= 1;
140            let new_next = unsafe { MemEntry::from_ptr(self.next.as_ptr().wrapping_add(1)) };
141            Some(core::mem::replace(&mut self.next, new_next))
142        }
143    }
144
145    fn size_hint(&self) -> (usize, Option<usize>) {
146        (self.remaining, Some(self.remaining))
147    }
148}
149
150impl<T: PrimInt, A: Access, E: Endian> DoubleEndedIterator for MemEntryIter<T, A, E> {
151    fn next_back(&mut self) -> Option<Self::Item> {
152        if self.remaining == 0 {
153            None
154        } else {
155            self.remaining -= 1;
156            unsafe {
157                Some(MemEntry::from_ptr(
158                    self.next.as_ptr().wrapping_add(self.remaining),
159                ))
160            }
161        }
162    }
163}
164
165impl<T: PrimInt, A: Access, E: Endian> ExactSizeIterator for MemEntryIter<T, A, E> {}
166impl<T: PrimInt, A: Access, E: Endian> FusedIterator for MemEntryIter<T, A, E> {}