mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-08 04:56:43 +00:00
add the 'SharedCsr' type object
this is a CSR object that presents writes as an immutable on the parent object. This simplifies traits, etc. On the one hand, the CSR itself is truly immutable - the address represented by the CSR does not change. On the other hand, the underlying data inside the CSR is changing, so, the CSR actually represesnts an immutable pointer to a mutable piece of data. For the case of the UDMA, we have a bank of globally shared state, it is much more convenient to think of the CSR as immutable, and we manage the underlying global shared state through other checks, e.g. through dynamic allocators that track when various blocks or resources have been parceled out.
This commit is contained in:
parent
c2ff7c759d
commit
b6532ffd39
1 changed files with 79 additions and 0 deletions
79
libs/cramium-hal/src/shared_csr.rs
Normal file
79
libs/cramium-hal/src/shared_csr.rs
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
use utralib::*;
|
||||
|
||||
/// Create an immutable-friendly shared CSR object for the I/O ports. The consequence
|
||||
/// of this is that we don't get the borrow checker to check the global shared state of
|
||||
/// the I/O pins status, but the truth is, it's global shared state and there's nothing
|
||||
/// you can do about it. Might as well make the APIs cleaner so we have less work to
|
||||
/// do maintaining APIs and can pay attention to sharing/allocating the shared state
|
||||
/// correctly.
|
||||
#[derive(Debug)]
|
||||
pub struct SharedCsr<T> {
|
||||
pub base: *const T,
|
||||
}
|
||||
impl<T> SharedCsr<T>
|
||||
where
|
||||
T: core::convert::TryFrom<usize> + core::convert::TryInto<usize> + core::default::Default,
|
||||
{
|
||||
pub fn new(base: *const T) -> Self { SharedCsr { base: base as *const T } }
|
||||
|
||||
pub unsafe fn base(&self) -> *mut T { self.base as *mut T }
|
||||
|
||||
pub fn clone(&self) -> Self { SharedCsr { base: self.base.clone() } }
|
||||
|
||||
/// Read the contents of this register
|
||||
pub fn r(&self, reg: Register) -> T {
|
||||
// prevent re-ordering
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
unsafe { usize_base.add(reg.offset()).read_volatile() }.try_into().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Read a field from this CSR
|
||||
pub fn rf(&self, field: Field) -> T {
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
((unsafe { usize_base.add(field.register().offset()).read_volatile() } >> field.offset())
|
||||
& field.mask())
|
||||
.try_into()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Read-modify-write a given field in this CSR
|
||||
pub fn rmwf(&self, field: Field, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default() << field.offset();
|
||||
let previous = unsafe { usize_base.add(field.register().offset()).read_volatile() }
|
||||
& !(field.mask() << field.offset());
|
||||
unsafe { usize_base.add(field.register().offset()).write_volatile(previous | value_as_usize) };
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Write a given field without reading it first
|
||||
pub fn wfo(&self, field: Field, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = (value.try_into().unwrap_or_default() & field.mask()) << field.offset();
|
||||
unsafe { usize_base.add(field.register().offset()).write_volatile(value_as_usize) };
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Write the entire contents of a register without reading it first
|
||||
pub fn wo(&self, reg: Register, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
unsafe { usize_base.add(reg.offset()).write_volatile(value_as_usize) };
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Zero a field from a provided value
|
||||
pub fn zf(&self, field: Field, value: T) -> T {
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
(value_as_usize & !(field.mask() << field.offset())).try_into().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Shift & mask a value to its final field position
|
||||
pub fn ms(&self, field: Field, value: T) -> T {
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
((value_as_usize & field.mask()) << field.offset()).try_into().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue