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:
parent
30a9171031
commit
9a96cbbf4f
59
src/lib.rs
59
src/lib.rs
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user