diff --git a/src/lib.rs b/src/lib.rs index 9fc025b..82d214d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,6 +19,9 @@ pub struct Field { /// Offset of the first bit in this field offset: usize, + /// A copy of the register address that this field + /// is a member of. Ideally this is optimized out by the + /// compiler. register: Register, } @@ -26,6 +29,7 @@ impl Field { /// Define a new CSR field with the given width at a specified /// offset from the start of the register. pub const fn new(width: usize, offset: usize, register: Register) -> Field { + // Asserts don't work in const fn yet. // assert!(width != 0, "field width cannot be 0"); // assert!((width + offset) < 32, "field with and offset must fit within a 32-bit value"); @@ -84,14 +88,26 @@ where pub fn new(base: *mut T) -> Self { CSR { base } } - pub fn r(&mut self, field: Field) -> T { + + /// Read the contents of this register + pub fn r(&mut self, reg: Register) -> T { + 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(&mut self, field: Field) -> T { 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() } - pub fn rw(&mut self, field: Field, value: T) { + + /// Read-modify-write a given field in this CSR + pub fn rmwf(&mut 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 = @@ -102,7 +118,9 @@ where .write_volatile(previous | value_as_usize) }; } - pub fn ow(&mut self, field: Field, value: T) { + + /// Write a given field without reading it first + pub fn wf(&mut 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; unsafe { @@ -111,6 +129,17 @@ where .write_volatile(value_as_usize) }; } + + /// Write the entire contents of a register without reading it first + pub fn w(&mut 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) + }; + } } #[cfg(test)] @@ -132,20 +161,41 @@ mod tests { #[test] fn compile_check() { use super::*; - let mut audio = CSR::new(0x1000_0000 as *mut u32); - audio.r(pac::audio::RX_CTL_ENABLE); - audio.rw(pac::audio::RX_CTL_RESET, 1); + // Audio tests + + // The audio block is a pointer to *mut 32. + let mut audio = CSR::new(0x1000_0000 as *mut u32); + + // Read the entire contents of the RX_CTL register + audio.r(pac::audio::RX_CTL); + + // Or read just one field + audio.rf(pac::audio::RX_CTL_ENABLE); + + // Do a read-modify-write of the specified field + audio.rmwf(pac::audio::RX_CTL_RESET, 1); + + // UART tests + + // Create the UART register as a pointer to *mut u8 let mut uart = CSR::new(0x1001_0000 as *mut u8); - uart.ow(pac::uart::RXTX_RXTX, b'a'); - assert_ne!(uart.r(pac::uart::TXFULL_TXFULL), 1); + + // Write the RXTX field of the RXTX register + uart.wf(pac::uart::RXTX_RXTX, b'a'); + + // Or you can write the whole UART register + uart.w(pac::uart::RXTX, b'a'); + assert_ne!(uart.rf(pac::uart::TXFULL_TXFULL), 1); + + // Anomalies // This compiles but requires a cast since `audio` is a pointer to // u32, whereas `uart` is a pointer to u8. - audio.ow(pac::uart::RXTX_RXTX, b'a' as _); + audio.wf(pac::uart::RXTX_RXTX, b'a' as _); // This also compiles, despite the fact that the register offset is // mismatched and nonsensical - audio.ow(pac::uart::TXFULL_TXFULL, 1); + audio.wf(pac::uart::TXFULL_TXFULL, 1); } }