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}