Skip to main content

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}