use std::sync::OnceLock;
thread_local! {
pub(crate) static READ_FN: OnceLock<fn(usize,usize)->u64> = OnceLock::new();
pub (crate) static WRITE_FN: OnceLock<fn(usize,usize,u64)> = OnceLock::new();
pub (crate) static LDMST: OnceLock<fn(usize,u64)> = OnceLock::new();
}
macro_rules! set_access_fn {
($CONST_ID:ident,$fn_id:ident,$fn_literal:literal,$access_fn_type:ty,$doc:literal) => {
#[doc=$doc]
pub fn $fn_id(fun: $access_fn_type) -> Result<(), String> {
$CONST_ID.with(|function| {
function.set(fun).or_else(|_| {
Err(format!(
"The thread local {} can only be set once.",
$fn_literal
))
})
})
}
};
}
set_access_fn!(
READ_FN,
set_read_fn,
"read_fn",
fn(usize, usize) -> u64,
"Set the function that is called when a read to a register happens\n through the PAC API.\n The function is called with the following arguments (in order):\n - a u64 representing the register address\n - a u64 representing the read mask (i.e. how many bits are read)\n This is necessary due to the way that the generated PACs handle\n generic register sizes.\n"
);
set_access_fn!(
WRITE_FN,
set_write_fn,
"write_fn",
fn(usize, usize, u64),
"Set the function that is called when a write to a register happens\n through the PAC API.\n The function is called with the following arguments (in order):\n - a u64 representing the register address\n - a u64 representing the write mask (i.e. how many bits are read)\n This is necessary due to the way that the generated PACs handle\n generic register sizes.\n - a u64 representing the value that gets written to the register\n"
);
#[cfg(feature = "tracing")]
pub mod insanely_unsafe {
use crate::common::sealed::{CastFrom, RegSpec};
use crate::common::{Access, R, Read, Reg, W, Write};
use crate::{RegValueT, RegisterValue};
pub trait WriteOnlyRead: Access {}
impl WriteOnlyRead for W {}
pub trait ReadOnlyWrite: Access {}
impl ReadOnlyWrite for R {}
impl<T: RegSpec, A: WriteOnlyRead> Reg<T, A> {
#[inline(always)]
pub unsafe fn read_write_only(&self) -> RegValueT<T> {
let val = {
let mut buf: u64 = 0x0;
super::READ_FN.with(|rf| {
buf = rf.get().unwrap()(self.addr(), std::mem::size_of::<T::DataType>());
});
T::DataType::cast_from(buf)
};
<RegValueT<_> as RegisterValue<_>>::new(val)
}
}
impl<T: RegSpec, A: ReadOnlyWrite> Reg<T, A> {
#[inline(always)]
pub unsafe fn write_read_only(&self, reg_value: RegValueT<T>) {
super::WRITE_FN.with(|wf| {
wf.get().unwrap()(
self.addr(),
std::mem::size_of::<T::DataType>(),
reg_value.data.into(),
)
});
}
}
impl<T: Default + RegSpec, A: ReadOnlyWrite> Reg<T, A>
where
RegValueT<T>: Default,
{
#[inline(always)]
pub unsafe fn init_read_only(&self, f: impl FnOnce(RegValueT<T>) -> RegValueT<T>) {
let val = RegValueT::<T>::default();
let res = f(val);
self.write_read_only(res);
}
}
impl<T: RegSpec, A: WriteOnlyRead + Write> Reg<T, A> {
#[inline(always)]
pub unsafe fn modify_write_only(&self, f: impl FnOnce(RegValueT<T>) -> RegValueT<T>) {
let val = self.read_write_only();
let res = f(val);
self.write(res);
}
}
impl<T: RegSpec, A: Read + ReadOnlyWrite> Reg<T, A> {
#[inline(always)]
pub unsafe fn modify_read_only(&self, f: impl FnOnce(RegValueT<T>) -> RegValueT<T>) {
let val = self.read();
let res = f(val);
self.write_read_only(res);
}
}
}