lib: bring register into the field definition

By doing this, we prevent a mismatch between registers and fields. The
compiler ought to optimize away the `Register` field.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2020-09-27 11:44:31 +08:00
parent 30a9171031
commit 9a96cbbf4f

View File

@ -17,12 +17,14 @@ pub struct Field {
/// Offset of the first bit in this field /// Offset of the first bit in this field
offset: usize, offset: usize,
register: Register,
} }
impl Field { impl Field {
/// Define a new CSR field with the given width at a specified /// Define a new CSR field with the given width at a specified
/// offset from the start of the register. /// offset from the start of the register.
pub const fn new(width: usize, offset: usize) -> Field { pub const fn new(width: usize, offset: usize, register: Register) -> Field {
// assert!(width != 0, "field width cannot be 0"); // assert!(width != 0, "field width cannot be 0");
// assert!((width + offset) < 32, "field with and offset must fit within a 32-bit value"); // assert!((width + offset) < 32, "field with and offset must fit within a 32-bit value");
@ -62,7 +64,11 @@ impl Field {
31 => 2147483647, 31 => 2147483647,
_ => 0, _ => 0,
}; };
Field { mask, offset } Field {
mask,
offset,
register,
}
} }
} }
@ -77,26 +83,32 @@ where
pub fn new(base: *mut T) -> Self { pub fn new(base: *mut T) -> Self {
CSR { base } CSR { base }
} }
pub fn r(&mut self, reg: Register, field: Field) -> T { pub fn r(&mut self, field: Field) -> T {
let usize_base: *mut usize = unsafe { core::mem::transmute(&self.base) }; let usize_base: *mut usize = unsafe { core::mem::transmute(&self.base) };
((unsafe { usize_base.add(reg.offset).read_volatile() } >> field.offset) & field.mask) ((unsafe { usize_base.add(field.register.offset).read_volatile() } >> field.offset)
& field.mask)
.try_into() .try_into()
.unwrap_or_default() .unwrap_or_default()
} }
pub fn rw(&mut self, reg: Register, field: Field, value: T) { pub fn rw(&mut self, field: Field, value: T) {
let usize_base: *mut usize = unsafe { core::mem::transmute(&self.base) }; 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 value_as_usize: usize = value.try_into().unwrap_or_default() << field.offset;
let previous = unsafe { usize_base.add(reg.offset).read_volatile() } & !field.mask; let previous =
unsafe { usize_base.add(field.register.offset).read_volatile() } & !field.mask;
unsafe { unsafe {
usize_base usize_base
.add(reg.offset) .add(field.register.offset)
.write_volatile(previous | value_as_usize) .write_volatile(previous | value_as_usize)
}; };
} }
pub fn ow(&mut self, reg: Register, field: Field, value: T) { pub fn ow(&mut self, field: Field, value: T) {
let usize_base: *mut usize = unsafe { core::mem::transmute(&self.base) }; 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 value_as_usize: usize = value.try_into().unwrap_or_default() << field.offset;
unsafe { usize_base.add(reg.offset).write_volatile(value_as_usize) }; unsafe {
usize_base
.add(field.register.offset)
.write_volatile(value_as_usize)
};
} }
} }
@ -105,33 +117,34 @@ mod tests {
pub mod pac { pub mod pac {
pub mod audio { pub mod audio {
pub const RX_CTL: crate::Register = crate::Register::new(0x0c); pub const RX_CTL: crate::Register = crate::Register::new(0x0c);
pub const RX_CTL_ENABLE: crate::Field = crate::Field::new(1, 0); pub const RX_CTL_ENABLE: crate::Field = crate::Field::new(1, 0, RX_CTL);
pub const RX_CTL_RESET: crate::Field = crate::Field::new(1, 1); pub const RX_CTL_RESET: crate::Field = crate::Field::new(1, 1, RX_CTL);
} }
pub mod uart { pub mod uart {
pub const RXTX: crate::Register = crate::Register::new(0x00); pub const RXTX: crate::Register = crate::Register::new(0x00);
pub const RXTX_RXTX: crate::Field = crate::Field::new(8, 0); pub const RXTX_RXTX: crate::Field = crate::Field::new(8, 0, RXTX);
pub const TXFULL: crate::Register = crate::Register::new(0x04); pub const TXFULL: crate::Register = crate::Register::new(0x04);
pub const TXFULL_TXFULL: crate::Field = crate::Field::new(1, 0); pub const TXFULL_TXFULL: crate::Field = crate::Field::new(1, 0, TXFULL);
} }
} }
#[test] #[test]
fn compile_check() { fn compile_check() {
use super::*; use super::*;
let mut audio = CSR::new(0x1000_0000 as *mut u32); let mut audio = CSR::new(0x0000_0000 as *mut u32);
audio.r(pac::audio::RX_CTL, pac::audio::RX_CTL_ENABLE); audio.r(pac::audio::RX_CTL_ENABLE);
audio.rw(pac::audio::RX_CTL, pac::audio::RX_CTL_RESET, 1); audio.rw(pac::audio::RX_CTL_RESET, 1);
let mut uart = CSR::new(0x1001_0000 as *mut u8); let mut uart = CSR::new(0x0001_0000 as *mut u8);
uart.ow(pac::uart::RXTX, pac::uart::RXTX_RXTX, b'a'); uart.ow(pac::uart::RXTX_RXTX, b'a');
assert_ne!(uart.r(pac::uart::TXFULL, pac::uart::TXFULL_TXFULL), 1); assert_ne!(uart.r(pac::uart::TXFULL_TXFULL), 1);
// Note: These also still compile.
uart.ow(pac::uart::RXTX, pac::uart::TXFULL_TXFULL, b'a');
// This compiles but requires a cast since `audio` is a pointer to // This compiles but requires a cast since `audio` is a pointer to
// u32, whereas `uart` is a pointer to u8. // u32, whereas `uart` is a pointer to u8.
audio.ow(pac::uart::RXTX, pac::uart::RXTX_RXTX, b'a' as _); audio.ow(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);
} }
} }