From b6532ffd3924bef1dfa1e31586dcf0da405ada96 Mon Sep 17 00:00:00 2001 From: bunnie Date: Fri, 4 Oct 2024 13:01:03 +0800 Subject: [PATCH] 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. --- libs/cramium-hal/src/shared_csr.rs | 79 ++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 libs/cramium-hal/src/shared_csr.rs diff --git a/libs/cramium-hal/src/shared_csr.rs b/libs/cramium-hal/src/shared_csr.rs new file mode 100644 index 000000000..bbaab1962 --- /dev/null +++ b/libs/cramium-hal/src/shared_csr.rs @@ -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 { + pub base: *const T, +} +impl SharedCsr +where + T: core::convert::TryFrom + core::convert::TryInto + 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() + } +}