peakrdl_rust/reg.rs
1//! Register abstraction used to read, write, and modify register values
2#![allow(clippy::inline_always)]
3
4use crate::{
5 access::{self, Access},
6 endian::Endian,
7};
8use core::marker::PhantomData;
9use num_traits::{AsPrimitive, Bounded, PrimInt, identities::ConstZero};
10
11/// Trait implemented by all register types.
12pub trait Register: Copy {
13 // NOTE: SystemRDL guarantees accesswidth <= regwidth, and both are 2^N bits where N >= 3
14 /// Primitive integer type representing the size of the full register value.
15 type Regwidth: PrimInt + AsPrimitive<Self::Accesswidth> + ConstZero + 'static;
16 /// Primitive integer type representing the size of memory accesses used when
17 /// reading/writing this register.
18 type Accesswidth: PrimInt + AsPrimitive<Self::Regwidth> + Bounded;
19 /// Endianness of this register.
20 type Endian: Endian;
21
22 /// Convert a raw bit value into a Register instance.
23 ///
24 /// # Safety
25 ///
26 /// The caller must ensure the raw bit value is valid for the given register.
27 /// For example, by reading it directly from hardware.
28 unsafe fn from_raw(val: Self::Regwidth) -> Self;
29
30 /// Convert a Register instance into its raw bit value.
31 fn to_raw(self) -> Self::Regwidth;
32}
33
34/// Register abstraction used to read, write, and modify register values
35#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36pub struct Reg<T: Register, A: Access> {
37 ptr: *mut T::Regwidth,
38 phantom: PhantomData<A>,
39}
40
41unsafe impl<T: Register, A: Access> Send for Reg<T, A> {}
42unsafe impl<T: Register, A: Access> Sync for Reg<T, A> {}
43
44// pointer conversion functions
45impl<T: Register, A: Access> Reg<T, A> {
46 /// # Safety
47 ///
48 /// The caller must guarantee that the provided address points to a
49 /// hardware register of type `T` with access `A`.
50 #[inline(always)]
51 pub const unsafe fn from_ptr(ptr: *mut T::Regwidth) -> Self {
52 Self {
53 ptr,
54 phantom: PhantomData,
55 }
56 }
57
58 #[inline(always)]
59 #[must_use]
60 pub const fn as_ptr(&self) -> *mut T {
61 self.ptr.cast()
62 }
63}
64
65// read access
66impl<T: Register, A: access::Read> Reg<T, A> {
67 /// Read a register value.
68 ///
69 /// If the register is to be modified (i.e., a read-modify-write), use the
70 /// [`Reg::modify`] method instead.
71 ///
72 /// # Example
73 ///
74 /// ```ignore
75 /// let reg1_val = registers.regfile().register1().read();
76 /// let field1_val = reg1_val.field1();
77 /// let field2_val = reg1_val.field2();
78 /// ```
79 #[inline(always)]
80 #[allow(clippy::must_use_candidate)]
81 pub fn read(&self) -> T {
82 unsafe { read_register(self.ptr) }
83 }
84}
85
86// write access
87impl<T: Register, A: access::Write> Reg<T, A> {
88 /// Write a register value.
89 ///
90 /// Typically one would use [`Reg::write`] or [`Reg::modify`] to update a
91 /// register's contents, but this method has a few different use cases such
92 /// as updating a register with a stored value, or updating one register with
93 /// the contents of another.
94 ///
95 /// # Example
96 ///
97 /// ```ignore
98 /// let reg0 = registers.regfile().reg_array()[0].read();
99 /// registers.regfile().reg_array()[1].write_value(reg0);
100 /// ```
101 #[inline(always)]
102 pub fn write_value(&self, val: T) {
103 unsafe { write_register(self.ptr, val) }
104 }
105}
106
107impl<T: Default + Register, A: access::Write> Reg<T, A> {
108 /// Write a register.
109 ///
110 /// This method takes a closure. The input to the closure is a mutable reference
111 /// to the default value of the register. It can be updated in the closure. The
112 /// updated value is then written to the hardware register.
113 ///
114 /// # Example
115 ///
116 /// ```ignore
117 /// registers.regfile().register1().write(|r| {
118 /// // r contains the default (reset) value of the register
119 /// r.set_field1(0x1);
120 /// r.set_field2(0x0);
121 /// });
122 /// ```
123 #[inline(always)]
124 pub fn write<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
125 let mut val = Default::default();
126 let res = f(&mut val);
127 self.write_value(val);
128 res
129 }
130}
131
132// read/write access
133impl<T: Register, A: access::Read + access::Write> Reg<T, A> {
134 /// Modify a register.
135 ///
136 /// This method takes a closure. The input to the closure is a mutable reference
137 /// to the current value of the register. It can be updated in the closure. The
138 /// updated value is then written back to the hardware register.
139 ///
140 /// # Example
141 ///
142 /// ```ignore
143 /// let orig_r = registers.regfile().register1().modify(|r| {
144 /// // r contains the current value of the register
145 /// orig_r = r.clone()
146 /// r.set_field1(r.field1());
147 /// r.set_field2(0x0);
148 /// // whatever value the closure returns is returned by the .modify() method
149 /// orig_r
150 /// });
151 /// ```
152 #[inline(always)]
153 pub fn modify<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
154 let mut val = self.read();
155 let res = f(&mut val);
156 self.write_value(val);
157 res
158 }
159}
160
161unsafe fn read_register<R: Register>(ptr: *const R::Regwidth) -> R {
162 let ptr = ptr.cast::<R::Accesswidth>();
163
164 let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
165 let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
166 let num_subwords = regwidth / accesswidth;
167
168 // read one subword at a time, starting at the lowest address
169 let raw_value = (0..num_subwords)
170 .map(|i| {
171 // SAFETY: SystemRDL guarantees accesswidth <= regwidth, so we won't
172 // read outside the bounds of the original pointer.
173 unsafe { (i, ptr.wrapping_add(i).read_volatile()) }
174 })
175 .fold(R::Regwidth::ZERO, |reg, (i, subword)| {
176 let significance = R::Endian::address_order_to_significance(i, num_subwords);
177 let subword = R::Endian::from_register_endian(subword);
178 reg | (subword.as_() << (significance * accesswidth))
179 });
180 // SAFETY: The value was just read directly from hardware, and should
181 // therefore be a valid register value.
182 unsafe { R::from_raw(raw_value) }
183}
184
185unsafe fn write_register<R: Register>(ptr: *mut R::Regwidth, value: R) {
186 let ptr = ptr.cast::<R::Accesswidth>();
187 let value = value.to_raw();
188
189 let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
190 let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
191 let num_subwords = regwidth / accesswidth;
192 let mask = R::Accesswidth::max_value().as_();
193
194 // write one subword at a time, starting at the lowest address
195 for i in 0..num_subwords {
196 let significance = R::Endian::address_order_to_significance(i, num_subwords);
197 let subword = (value >> (significance * accesswidth)) & mask;
198 let subword = R::Endian::to_register_endian(subword.as_());
199 // SAFETY: SystemRDL guarantees accesswidth <= regwidth, so we won't
200 // write outside the bounds of the original pointer.
201 unsafe {
202 ptr.wrapping_add(i).write_volatile(subword);
203 }
204 }
205}