use crate::{
access::{Read, Write},
endian::Endian,
reg::{RegInt, Register},
};
use core::cell::RefCell;
use num_traits::{AsPrimitive, Bounded, ConstZero};
pub trait RawRegisterIO {
type Error;
#[allow(clippy::missing_errors_doc)]
unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error>;
#[allow(clippy::missing_errors_doc)]
unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error>;
}
pub trait RegisterIO {
type Error;
#[allow(clippy::missing_errors_doc)]
unsafe fn try_read_register<R: Register>(
&self,
ptr: *const R::Regwidth,
) -> Result<R, Self::Error>
where
R::Access: Read;
#[allow(clippy::missing_errors_doc)]
unsafe fn try_write_register<R: Register>(
&self,
ptr: *mut R::Regwidth,
value: R,
) -> Result<(), Self::Error>
where
R::Access: Write;
}
impl<T> RegisterIO for T
where
T: RawRegisterIO,
{
type Error = T::Error;
unsafe fn try_read_register<R: Register>(
&self,
ptr: *const R::Regwidth,
) -> Result<R, Self::Error>
where
R::Access: Read,
{
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, self.try_read(ptr.wrapping_add(i))) }
})
.try_fold(R::Regwidth::ZERO, |reg, (i, subword)| {
let significance = R::WordEndian::address_order_to_significance(i, num_subwords);
let subword = R::ByteEndian::from_register_endian(subword?);
Ok(reg | (subword.as_() << (significance * accesswidth)))
})?;
unsafe { Ok(R::from_raw(raw_value)) }
}
unsafe fn try_write_register<R: Register>(
&self,
ptr: *mut R::Regwidth,
value: R,
) -> Result<(), Self::Error>
where
R::Access: Write,
{
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::WordEndian::address_order_to_significance(i, num_subwords);
let subword = (value >> (significance * accesswidth)) & mask;
let subword = R::ByteEndian::to_register_endian(subword.as_());
unsafe {
self.try_write(ptr.wrapping_add(i), subword)?;
}
}
Ok(())
}
}
pub struct PtrIO;
impl RawRegisterIO for PtrIO {
type Error = core::convert::Infallible;
unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error> {
Ok(unsafe { ptr.read_volatile() })
}
unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error> {
unsafe { ptr.write_volatile(value) };
Ok(())
}
}
pub struct MockIO<const N: usize>(RefCell<[u8; N]>);
impl<const N: usize> MockIO<N> {
#[must_use]
pub fn new_zeroed() -> Self {
Self(RefCell::new([0; N]))
}
pub fn base_ptr(&self) -> *mut () {
0 as _
}
}
impl<const N: usize> RawRegisterIO for MockIO<N> {
type Error = core::convert::Infallible;
unsafe fn try_read<T: RegInt>(&self, ptr: *const T) -> Result<T, Self::Error> {
let addr = ptr.addr();
let size = core::mem::size_of::<T>();
let data = self.0.borrow();
let bytes = &data[addr..addr + size];
Ok(T::from_ne_bytes(
&bytes.try_into().expect("Incorrect slice length"),
))
}
unsafe fn try_write<T: RegInt>(&self, ptr: *mut T, value: T) -> Result<(), Self::Error> {
let addr = ptr.addr();
let size = core::mem::size_of::<T>();
let mut data = self.0.borrow_mut();
let bytes = &mut data[addr..addr + size];
bytes.copy_from_slice(value.to_ne_bytes().as_ref());
Ok(())
}
}