#![allow(clippy::inline_always)]
use crate::{
access::{self, Access},
endian::Endian,
};
use core::marker::PhantomData;
use num_traits::{AsPrimitive, Bounded, PrimInt, identities::ConstZero};
pub trait Register: Copy {
type Regwidth: PrimInt + AsPrimitive<Self::Accesswidth> + ConstZero + 'static;
type Accesswidth: PrimInt + AsPrimitive<Self::Regwidth> + Bounded;
type Endian: Endian;
unsafe fn from_raw(val: Self::Regwidth) -> Self;
fn to_raw(self) -> Self::Regwidth;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Reg<T: Register, A: Access> {
ptr: *mut T::Regwidth,
phantom: PhantomData<A>,
}
unsafe impl<T: Register, A: Access> Send for Reg<T, A> {}
unsafe impl<T: Register, A: Access> Sync for Reg<T, A> {}
impl<T: Register, A: Access> Reg<T, A> {
#[inline(always)]
pub const unsafe fn from_ptr(ptr: *mut T::Regwidth) -> Self {
Self {
ptr,
phantom: PhantomData,
}
}
#[inline(always)]
#[must_use]
pub const fn as_ptr(&self) -> *mut T {
self.ptr.cast()
}
}
impl<T: Register, A: access::Read> Reg<T, A> {
#[inline(always)]
#[allow(clippy::must_use_candidate)]
pub fn read(&self) -> T {
unsafe { read_register(self.ptr) }
}
}
impl<T: Register, A: access::Write> Reg<T, A> {
#[inline(always)]
pub fn write_value(&self, val: T) {
unsafe { write_register(self.ptr, val) }
}
}
impl<T: Default + Register, A: access::Write> Reg<T, A> {
#[inline(always)]
pub fn write<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut val = Default::default();
let res = f(&mut val);
self.write_value(val);
res
}
}
impl<T: Register, A: access::Read + access::Write> Reg<T, A> {
#[inline(always)]
pub fn modify<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
let mut val = self.read();
let res = f(&mut val);
self.write_value(val);
res
}
}
unsafe fn read_register<R: Register>(ptr: *const R::Regwidth) -> R {
let ptr = ptr.cast::<R::Accesswidth>();
let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
let num_subwords = regwidth / accesswidth;
let raw_value = (0..num_subwords)
.map(|i| {
unsafe { (i, ptr.wrapping_add(i).read_volatile()) }
})
.fold(R::Regwidth::ZERO, |reg, (i, subword)| {
let significance = R::Endian::address_order_to_significance(i, num_subwords);
let subword = R::Endian::from_register_endian(subword);
reg | (subword.as_() << (significance * accesswidth))
});
unsafe { R::from_raw(raw_value) }
}
unsafe fn write_register<R: Register>(ptr: *mut R::Regwidth, value: R) {
let ptr = ptr.cast::<R::Accesswidth>();
let value = value.to_raw();
let accesswidth = 8 * core::mem::size_of::<R::Accesswidth>();
let regwidth = 8 * core::mem::size_of::<R::Regwidth>();
let num_subwords = regwidth / accesswidth;
let mask = R::Accesswidth::max_value().as_();
for i in 0..num_subwords {
let significance = R::Endian::address_order_to_significance(i, num_subwords);
let subword = (value >> (significance * accesswidth)) & mask;
let subword = R::Endian::to_register_endian(subword.as_());
unsafe {
ptr.wrapping_add(i).write_volatile(subword);
}
}
}