Skip to main content

peakrdl_rust/
io.rs

1use crate::{
2    access::{Read, Write},
3    endian::Endian,
4    reg::{RegInt, Register},
5};
6use core::cell::RefCell;
7use num_traits::{AsPrimitive, Bounded, ConstZero};
8
9/// Raw register I/O trait
10///
11/// Types that implement this trait also automatically implement [`RegisterIO`].
12pub trait RawRegisterIO {
13    /// The error type this register transport returns. For infallible transports
14    /// (e.g., direct volatile pointer accesses), this should be [`core::convert::Infallible`]
15    /// so that the [`Reg`][crate::reg::Reg] type can provide an infallible API in addition
16    /// to the `try_*` API.
17    type Error;
18
19    /// Try to read a primitive integer from memory.
20    ///
21    /// The returned value is in the register's native endianness (not necessarily
22    /// the host's endianness).
23    ///
24    /// # Safety
25    ///
26    /// This method may dereference raw pointer. The caller must ensure the pointer
27    /// is valid and points to a valid memory location.
28    #[allow(clippy::missing_errors_doc)]
29    unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error>;
30
31    /// Try to write primitive integer to memory
32    ///
33    /// The provided value is in the register's native endianness (not necessarily
34    /// the host's endianness).
35    ///
36    /// # Safety
37    ///
38    /// This method may dereference a raw pointer. The caller must ensure the pointer
39    /// is valid and points to a valid writeable memory location.
40    #[allow(clippy::missing_errors_doc)]
41    unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error>;
42}
43
44/// Register I/O
45///
46/// Register accesses are performed through implementers of this trait. This trait's
47/// methods handle details like endianness, multi-word writes, etc.
48///
49/// Most user I/O interfaces should simply implement the [`RawRegisterIO`] trait, since a
50/// blanket implementation exists for all implementers of [`RawRegisterIO`].
51pub trait RegisterIO {
52    type Error;
53
54    /// Read a register value
55    ///
56    /// This method must respect the register's endianness and accesswidth,
57    /// as encoded in the generic [`Register`]'s associated types.
58    ///
59    /// # Safety
60    ///
61    /// This method may dereference a raw pointer. The caller must ensure the pointer
62    /// is valid and points to a valid memory location.
63    #[allow(clippy::missing_errors_doc)]
64    unsafe fn try_read_register<R: Register>(
65        &self,
66        ptr: *const R::Regwidth,
67    ) -> Result<R, Self::Error>
68    where
69        R::Access: Read;
70
71    /// Write a register value
72    ///
73    /// This method must respect the register's endianness and accesswidth,
74    /// as encoded in the generic [`Register`]'s associated types.
75    ///
76    /// # Safety
77    ///
78    /// This method may dereference a raw pointer. The caller must ensure the pointer
79    /// is valid and points to a valid writeable memory location.
80    #[allow(clippy::missing_errors_doc)]
81    unsafe fn try_write_register<R: Register>(
82        &self,
83        ptr: *mut R::Regwidth,
84        value: R,
85    ) -> Result<(), Self::Error>
86    where
87        R::Access: Write;
88}
89
90impl<T> RegisterIO for T
91where
92    T: RawRegisterIO,
93{
94    type Error = T::Error;
95
96    unsafe fn try_read_register<R: Register>(
97        &self,
98        ptr: *const R::Regwidth,
99    ) -> Result<R, Self::Error>
100    where
101        R::Access: Read,
102    {
103        let ptr = ptr.cast::<R::Accesswidth>();
104
105        let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
106        let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
107        let num_subwords = regwidth / accesswidth;
108
109        // read one subword at a time, starting at the lowest address
110        let raw_value = (0..num_subwords)
111            .map(|i| {
112                // SAFETY: SystemRDL guarantees accesswidth <= regwidth, so we won't
113                // read outside the bounds of the original pointer.
114                unsafe { (i, self.try_read(ptr.wrapping_add(i))) }
115            })
116            .try_fold(R::Regwidth::ZERO, |reg, (i, subword)| {
117                let significance = R::WordEndian::address_order_to_significance(i, num_subwords);
118                let subword = R::ByteEndian::from_register_endian(subword?);
119                Ok(reg | (subword.as_() << (significance * accesswidth)))
120            })?;
121        // SAFETY: The value was just read directly from hardware, and should
122        // therefore be a valid register value.
123        unsafe { Ok(R::from_raw(raw_value)) }
124    }
125
126    unsafe fn try_write_register<R: Register>(
127        &self,
128        ptr: *mut R::Regwidth,
129        value: R,
130    ) -> Result<(), Self::Error>
131    where
132        R::Access: Write,
133    {
134        let ptr = ptr.cast::<R::Accesswidth>();
135        let value = value.to_raw();
136
137        let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
138        let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
139        let num_subwords = regwidth / accesswidth;
140        let mask = R::Accesswidth::max_value().as_();
141
142        // write one subword at a time, starting at the lowest address
143        for i in 0..num_subwords {
144            let significance = R::WordEndian::address_order_to_significance(i, num_subwords);
145            let subword = (value >> (significance * accesswidth)) & mask;
146            let subword = R::ByteEndian::to_register_endian(subword.as_());
147            // SAFETY: SystemRDL guarantees accesswidth <= regwidth, so we won't
148            // write outside the bounds of the original pointer.
149            unsafe {
150                self.try_write(ptr.wrapping_add(i), subword)?;
151            }
152        }
153        Ok(())
154    }
155}
156
157/// Default [`RegisterIO`] implementation.
158///
159/// Provides infallible register access through volatile pointer reads
160/// and writes.
161pub struct PtrIO;
162
163impl RawRegisterIO for PtrIO {
164    type Error = core::convert::Infallible;
165
166    unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error> {
167        Ok(unsafe { ptr.read_volatile() })
168    }
169
170    unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error> {
171        unsafe { ptr.write_volatile(value) };
172        Ok(())
173    }
174}
175
176/// Mocked [`RegisterIO`] implementation.
177///
178/// Implemented as an array of N bytes, register writes and reads
179/// simply write to/from the internal array.
180pub struct MockIO<const N: usize>(RefCell<[u8; N]>);
181
182impl<const N: usize> MockIO<N> {
183    /// Construct a new zeroed instance of the mocked register memory.
184    #[must_use]
185    pub fn new_zeroed() -> Self {
186        Self(RefCell::new([0; N]))
187    }
188
189    /// Get the base register address of the instance (always 0).
190    pub fn base_ptr(&self) -> *mut () {
191        0 as _
192    }
193}
194
195impl<const N: usize> RawRegisterIO for MockIO<N> {
196    type Error = core::convert::Infallible;
197
198    unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error> {
199        let addr = ptr.addr();
200        let size = core::mem::size_of::<T>();
201        let data = self.0.borrow();
202        let bytes = &data[addr..addr + size];
203        Ok(T::from_ne_bytes(
204            &bytes.try_into().expect("Incorrect slice length"),
205        ))
206    }
207
208    unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error> {
209        let addr = ptr.addr();
210        let size = core::mem::size_of::<T>();
211        let mut data = self.0.borrow_mut();
212        let bytes = &mut data[addr..addr + size];
213        bytes.copy_from_slice(value.to_ne_bytes().as_ref());
214        Ok(())
215    }
216}