peakrdl_rust/
io.rs

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