#[macro_export]
macro_rules! register {
{
$(#[$attrs:meta])*
$name:ident,
$width:ty,
$mode:ident,
Fields [$($fields:tt)*]
} => {
#[allow(unused)]
#[allow(non_snake_case)]
pub mod $name {
use typenum::consts::*;
use core::marker::PhantomData;
use typenum::{Unsigned, IsGreater};
use $crate::{Field as F, Pointer, Positioned};
use $crate::bounds::{ReifyTo, Reifier};
use core::ptr;
type Width = $width;
#[repr(C)]
$(#[$attrs])*
pub struct Register(Width);
mode!($mode);
fields!($($fields)*);
}
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! fields {
{
$(#[$outer:meta])*
$name:ident WIDTH($width:ident) OFFSET($offset:ident) [ $($enums:tt)* ] $($rest:tt)*
} => {
#[allow(unused)]
#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
pub mod $name {
use super::*;
type _Offset = $offset;
type _FieldWidth = $width;
$(#[$outer])*
pub type Field = F<super::Width, op!(((U1 << $width) - U1) << $offset), $offset, op!((U1 << $width) - U1), Register>;
pub const Read: Field = Field::checked::<U0>();
pub const Set: Field = Field::checked::<op!((U1 << $width) - U1)>();
pub const Clear: Field = Read;
enums!($($enums)*);
}
fields!($($rest)*);
};
{
$(#[$outer:meta])*
$name:ident WIDTH($width:ident) OFFSET($offset:ident) $($rest:tt)*
} => {
#[allow(unused)]
#[allow(non_upper_case_globals)]
#[allow(non_snake_case)]
pub mod $name {
use super::*;
$(#[$outer])*
pub type Field = F<super::Width, op!(((U1 << $width) - U1) << $offset), $offset, op!((U1 << $width) - U1), Register>;
pub const Read: Field = Field::checked::<U0>();
pub const Set: Field = Field::checked::<op!((U1 << $width) - U1)>();
pub const Clear: Field = Read;
}
fields!($($rest)*);
};
(, $($rest:tt)*) => (fields!($($rest)*););
() => ()
}
#[macro_export]
#[doc(hidden)]
macro_rules! enums {
{
$(
$(#[$outer:meta])*
$name:ident = $val:ident
),*
} => {
$(
$(#[$outer])*
pub const $name: Field = Field::checked::<$val>();
)*
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! mode {
(RO) => {
impl Register {
pub fn new(init: Width) -> Self {
Register(init)
}
pub fn get_field<M: Unsigned, O: Unsigned, U: Unsigned>(
&self,
f: F<Width, M, O, U, Register>,
) -> Option<F<Width, M, O, U, Register>>
where
U: IsGreater<U0, Output = True> + ReifyTo<Width>,
M: ReifyTo<Width>,
O: ReifyTo<Width>,
U0: ReifyTo<Width>,
{
f.set(
(unsafe { ptr::read_volatile(&self.0 as *const Width) } & M::reify())
>> O::reify(),
)
}
pub fn read(&self) -> Width {
unsafe { ptr::read_volatile(&self.0 as *const Width) }
}
pub fn extract(&self) -> $crate::ReadOnlyCopy<Width, Register> {
$crate::ReadOnlyCopy(
unsafe { ptr::read_volatile(&self.0 as *const Width) },
PhantomData,
)
}
pub fn is_set<M: Unsigned, O: Unsigned, U: Unsigned>(
&self,
f: F<Width, M, O, U, Register>,
) -> bool
where
U: IsGreater<U0, Output = True>,
U: ReifyTo<Width>,
M: ReifyTo<Width>,
O: ReifyTo<Width>,
{
((unsafe { ptr::read_volatile(&self.0 as *const Width) } & M::reify())
>> O::reify())
== U::reify()
}
pub fn matches_any<V: Positioned<Width = Width>>(&self, val: V) -> bool {
(val.in_position() & unsafe { ptr::read_volatile(&self.0 as *const Width) }) != 0
}
fn matches_all<V: Positioned<Width = Width>>(&self, val: V) -> bool {
(val.in_position() & unsafe { ptr::read_volatile(&self.0 as *const Width) })
== val.in_position()
}
}
};
(WO) => {
impl Register {
pub fn new(init: Width) -> Self {
Register(init)
}
pub fn modify<V: Positioned<Width = Width>>(&mut self, val: V) {
unsafe {
ptr::write_volatile(
&mut self.0 as *mut Width,
(ptr::read_volatile(&self.0 as *const Width) & !val.mask())
| val.in_position(),
);
};
}
fn write(&mut self, val: Width) {
unsafe { ptr::write_volatile(&mut self.0 as *mut Width, val) };
}
}
};
(RW) => {
impl Register {
pub fn new(init: Width) -> Self {
Register(init)
}
pub fn get_field<M: Unsigned, O: Unsigned, U: Unsigned>(
&self,
f: F<Width, M, O, U, Register>,
) -> Option<F<Width, M, O, U, Register>>
where
U: IsGreater<U0, Output = True> + ReifyTo<Width>,
M: ReifyTo<Width>,
O: ReifyTo<Width>,
U0: ReifyTo<Width>,
{
f.set(
(unsafe { ptr::read_volatile(&self.0 as *const Width) } & M::reify())
>> O::reify(),
)
}
pub fn read(&self) -> Width {
unsafe { ptr::read_volatile(&self.0 as *const Width) }
}
pub fn extract(&self) -> $crate::ReadOnlyCopy<Width, Register> {
$crate::ReadOnlyCopy(
unsafe { ptr::read_volatile(&self.0 as *const Width) },
PhantomData,
)
}
pub fn is_set<M: Unsigned, O: Unsigned, U: Unsigned>(
&self,
f: F<Width, M, O, U, Register>,
) -> bool
where
U: IsGreater<U0, Output = True>,
U: ReifyTo<Width>,
M: ReifyTo<Width>,
O: ReifyTo<Width>,
{
((unsafe { ptr::read_volatile(&self.0 as *const Width) } & M::reify())
>> O::reify())
== U::reify()
}
pub fn matches_any<V: Positioned<Width = Width>>(&self, val: V) -> bool {
(val.in_position() & unsafe { ptr::read_volatile(&self.0 as *const Width) }) != 0
}
pub fn matches_all<V: Positioned<Width = Width>>(&self, val: V) -> bool {
(val.in_position() & unsafe { ptr::read_volatile(&self.0 as *const Width) })
== val.in_position()
}
pub fn modify<V: Positioned<Width = Width>>(&mut self, val: V) {
unsafe {
ptr::write_volatile(
&mut self.0 as *mut Width,
(ptr::read_volatile(&self.0 as *const Width) & !val.mask())
| val.in_position(),
);
};
}
pub fn write(&mut self, val: Width) {
unsafe { ptr::write_volatile(&mut self.0 as *mut Width, val) };
}
}
};
}
#[cfg(test)]
mod test {
use typenum::consts::U1;
register! {
#[derive(Debug)]
Status,
u8,
RW,
Fields [
On WIDTH(U1) OFFSET(U0),
Dead WIDTH(U1) OFFSET(U1),
Color WIDTH(U3) OFFSET(U2) [
Red = U1,
Blue = U2,
Green = U3,
Yellow = U4
],
]
}
#[test]
fn test_rw_macro() {
let mut reg = Status::Register::new(0);
reg.modify(Status::Dead::Field::checked::<U1>());
assert_eq!(reg.read(), 2);
}
#[test]
fn test_matches_any() {
let mut reg = Status::Register::new(0);
reg.modify(Status::Dead::Set);
assert!(reg.matches_any(Status::On::Set + Status::Dead::Set));
reg.modify(Status::Dead::Clear);
assert!(!reg.matches_any(Status::On::Set + Status::Dead::Set));
}
#[test]
fn test_matches_all() {
let mut reg = Status::Register::new(0);
reg.modify(Status::Dead::Set + Status::On::Set);
assert!(reg.matches_all(Status::On::Set + Status::Dead::Set));
reg.modify(Status::Dead::Clear);
assert!(!reg.matches_all(Status::On::Set + Status::Dead::Set));
}
register! {
#[derive(Debug)]
RNG,
u8,
RO,
Fields [
Working WIDTH(U1) OFFSET(U0),
NumWidth WIDTH(U2) OFFSET(U1) [
Four = U0,
Eight = U1,
Sixteen = U2
]
]
}
#[test]
fn test_ro_macro() {
let reg = RNG::Register::new(4);
let width = reg.get_field(RNG::NumWidth::Read).unwrap();
assert_eq!(width, RNG::NumWidth::Sixteen);
}
#[test]
fn test_field_disj() {
let mut reg = Status::Register::new(0);
reg.modify(Status::Dead::Set + Status::Color::Blue + Status::On::Clear);
assert_eq!(reg.read(), 10);
}
}