#![allow(clippy::suspicious_op_assign_impl)]
#![allow(clippy::suspicious_arithmetic_impl)]
use core::marker::PhantomData;
use core::ops::{Add, AddAssign};
use crate::{RegisterLongName, UIntLike};
pub struct Field<T: UIntLike, R: RegisterLongName> {
pub mask: T,
pub shift: usize,
associated_register: PhantomData<R>,
}
impl<T: UIntLike, R: RegisterLongName> Field<T, R> {
pub const fn new(mask: T, shift: usize) -> Field<T, R> {
Field {
mask: mask,
shift: shift,
associated_register: PhantomData,
}
}
#[inline]
pub fn read(self, val: T) -> T {
(val & (self.mask << self.shift)) >> self.shift
}
#[inline]
pub fn is_set(self, val: T) -> bool {
val & (self.mask << self.shift) != T::zero()
}
#[inline]
pub fn read_as_enum<E: TryFromValue<T, EnumType = E>>(self, val: T) -> Option<E> {
E::try_from_value(self.read(val))
}
}
impl<T: UIntLike, R: RegisterLongName> Clone for Field<T, R> {
fn clone(&self) -> Self {
Field {
mask: self.mask,
shift: self.shift,
associated_register: self.associated_register,
}
}
}
impl<T: UIntLike, R: RegisterLongName> Copy for Field<T, R> {}
macro_rules! Field_impl_for {
($type:ty) => {
impl<R: RegisterLongName> Field<$type, R> {
pub const fn val(&self, value: $type) -> FieldValue<$type, R> {
FieldValue::<$type, R>::new(self.mask, self.shift, value)
}
}
};
}
Field_impl_for!(u8);
Field_impl_for!(u16);
Field_impl_for!(u32);
Field_impl_for!(u64);
Field_impl_for!(u128);
Field_impl_for!(usize);
#[derive(Copy, Clone)]
pub struct FieldValue<T: UIntLike, R: RegisterLongName> {
mask: T,
pub value: T,
associated_register: PhantomData<R>,
}
macro_rules! FieldValue_impl_for {
($type:ty) => {
impl<R: RegisterLongName> FieldValue<$type, R> {
pub const fn new(mask: $type, shift: usize, value: $type) -> Self {
FieldValue {
mask: mask << shift,
value: (value & mask) << shift,
associated_register: PhantomData,
}
}
}
impl<R: RegisterLongName> From<FieldValue<$type, R>> for $type {
fn from(val: FieldValue<$type, R>) -> $type {
val.value
}
}
};
}
FieldValue_impl_for!(u8);
FieldValue_impl_for!(u16);
FieldValue_impl_for!(u32);
FieldValue_impl_for!(u64);
FieldValue_impl_for!(u128);
FieldValue_impl_for!(usize);
impl<T: UIntLike, R: RegisterLongName> FieldValue<T, R> {
#[inline]
pub fn none() -> Self {
Self {
mask: T::zero(),
value: T::zero(),
associated_register: PhantomData,
}
}
#[inline]
pub const fn mask(&self) -> T {
self.mask as T
}
#[inline]
pub fn read(&self, field: Field<T, R>) -> T {
field.read(self.value)
}
#[inline]
pub fn modify(self, val: T) -> T {
(val & !self.mask) | self.value
}
#[inline]
pub fn matches_any(&self, val: T) -> bool {
val & self.mask != T::zero()
}
#[inline]
pub fn matches_all(&self, val: T) -> bool {
val & self.mask == self.value
}
}
impl<T: UIntLike, R: RegisterLongName> Add for FieldValue<T, R> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
FieldValue {
mask: self.mask | rhs.mask,
value: self.value | rhs.value,
associated_register: PhantomData,
}
}
}
impl<T: UIntLike, R: RegisterLongName> AddAssign for FieldValue<T, R> {
#[inline]
fn add_assign(&mut self, rhs: FieldValue<T, R>) {
self.mask |= rhs.mask;
self.value |= rhs.value;
}
}
pub trait TryFromValue<V> {
type EnumType;
fn try_from_value(v: V) -> Option<Self::EnumType>;
}
#[macro_export]
macro_rules! bitmask {
($numbits:expr) => {
(1 << ($numbits - 1)) + ((1 << ($numbits - 1)) - 1)
};
}
#[macro_export]
macro_rules! register_bitmasks {
{
$(#[$outer:meta])*
$valtype:ident, $reg_desc:ident, [
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr)),+ $(,)?
]
} => {
$(#[$outer])*
$( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, 1, []); )*
};
{
$(#[$outer:meta])*
$valtype:ident, $reg_desc:ident, [
$( $(#[$inner:meta])* $field:ident $offset:expr ),+ $(,)?
]
} => {
$(#[$outer])*
$( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, 1, []); )*
};
{
$(#[$outer:meta])*
$valtype:ident, $reg_desc:ident, [
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr) ),+ $(,)?
]
} => {
$(#[$outer])*
$( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, $numbits, []); )*
};
{
$(#[$outer:meta])*
$valtype:ident, $reg_desc:ident, [
$( $(#[$inner:meta])* $field:ident OFFSET($offset:expr) NUMBITS($numbits:expr)
$values:tt ),+ $(,)?
]
} => {
$(#[$outer])*
$( $crate::register_bitmasks!($valtype, $reg_desc, $(#[$inner])* $field, $offset, $numbits,
$values); )*
};
{
$valtype:ident, $reg_desc:ident, $(#[$outer:meta])* $field:ident,
$offset:expr, $numbits:expr,
[$( $(#[$inner:meta])* $valname:ident = $value:expr ),+ $(,)?]
} => { #[allow(non_upper_case_globals)]
#[allow(unused)]
pub const $field: Field<$valtype, $reg_desc> =
Field::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), $offset);
#[allow(non_snake_case)]
#[allow(unused)]
$(#[$outer])*
pub mod $field {
#[allow(unused_imports)]
use $crate::fields::{TryFromValue, FieldValue};
use super::$reg_desc;
$(
#[allow(non_upper_case_globals)]
#[allow(unused)]
$(#[$inner])*
pub const $valname: FieldValue<$valtype, $reg_desc> =
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
$offset, $value);
)*
#[allow(non_upper_case_globals)]
#[allow(unused)]
pub const SET: FieldValue<$valtype, $reg_desc> =
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
$offset, $crate::bitmask!($numbits));
#[allow(non_upper_case_globals)]
#[allow(unused)]
pub const CLEAR: FieldValue<$valtype, $reg_desc> =
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
$offset, 0);
#[allow(dead_code)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Eq, PartialEq)]
#[repr($valtype)] $(#[$outer])*
pub enum Value {
$(
$(#[$inner])*
$valname = $value,
)*
}
impl TryFromValue<$valtype> for Value {
type EnumType = Value;
fn try_from_value(v: $valtype) -> Option<Self::EnumType> {
match v {
$(
$(#[$inner])*
x if x == Value::$valname as $valtype => Some(Value::$valname),
)*
_ => Option::None
}
}
}
impl From<Value> for FieldValue<$valtype, $reg_desc> {
fn from(v: Value) -> Self {
Self::new($crate::bitmask!($numbits), $offset, v as $valtype)
}
}
}
};
{
$valtype:ident, $reg_desc:ident, $(#[$outer:meta])* $field:ident,
$offset:expr, $numbits:expr,
[]
} => { #[allow(non_upper_case_globals)]
#[allow(unused)]
pub const $field: Field<$valtype, $reg_desc> =
Field::<$valtype, $reg_desc>::new($crate::bitmask!($numbits), $offset);
#[allow(non_snake_case)]
#[allow(unused)]
$(#[$outer])*
pub mod $field {
#[allow(unused_imports)]
use $crate::fields::{FieldValue, TryFromValue};
use super::$reg_desc;
#[allow(non_upper_case_globals)]
#[allow(unused)]
pub const SET: FieldValue<$valtype, $reg_desc> =
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
$offset, $crate::bitmask!($numbits));
#[allow(non_upper_case_globals)]
#[allow(unused)]
pub const CLEAR: FieldValue<$valtype, $reg_desc> =
FieldValue::<$valtype, $reg_desc>::new($crate::bitmask!($numbits),
$offset, 0);
#[allow(dead_code)]
#[allow(non_camel_case_types)]
$(#[$outer])*
pub enum Value {}
impl TryFromValue<$valtype> for Value {
type EnumType = Value;
fn try_from_value(_v: $valtype) -> Option<Self::EnumType> {
Option::None
}
}
}
};
}
#[macro_export]
macro_rules! register_bitfields {
{
$valtype:ident, $( $(#[$inner:meta])* $vis:vis $reg:ident $fields:tt ),* $(,)?
} => {
$(
#[allow(non_snake_case)]
$(#[$inner])*
$vis mod $reg {
#[derive(Clone, Copy)]
pub struct Register;
impl $crate::RegisterLongName for Register {}
use $crate::fields::Field;
$crate::register_bitmasks!( $valtype, Register, $fields );
}
)*
}
}
#[cfg(feature = "std_unit_tests")]
#[cfg(test)]
mod tests {
#[derive(Debug, PartialEq, Eq)]
enum Foo {
Foo0,
Foo1,
Foo2,
Foo3,
Foo4,
Foo5,
Foo6,
Foo7,
}
impl crate::fields::TryFromValue<u16> for Foo {
type EnumType = Foo;
fn try_from_value(v: u16) -> Option<Self::EnumType> {
Self::try_from_value(v as u32)
}
}
impl crate::fields::TryFromValue<u32> for Foo {
type EnumType = Foo;
fn try_from_value(v: u32) -> Option<Self::EnumType> {
match v {
0 => Some(Foo::Foo0),
1 => Some(Foo::Foo1),
2 => Some(Foo::Foo2),
3 => Some(Foo::Foo3),
4 => Some(Foo::Foo4),
5 => Some(Foo::Foo5),
6 => Some(Foo::Foo6),
7 => Some(Foo::Foo7),
_ => None,
}
}
}
mod field {
use super::Foo;
use crate::fields::{Field, TryFromValue};
#[test]
fn test_new() {
let field8 = Field::<u8, ()>::new(0x12, 3);
assert_eq!(field8.mask, 0x12_u8);
assert_eq!(field8.shift, 3);
let field16 = Field::<u16, ()>::new(0x1234, 5);
assert_eq!(field16.mask, 0x1234_u16);
assert_eq!(field16.shift, 5);
let field32 = Field::<u32, ()>::new(0x12345678, 9);
assert_eq!(field32.mask, 0x12345678_u32);
assert_eq!(field32.shift, 9);
let field64 = Field::<u64, ()>::new(0x12345678_9abcdef0, 1);
assert_eq!(field64.mask, 0x12345678_9abcdef0_u64);
assert_eq!(field64.shift, 1);
let field128 = Field::<u128, ()>::new(0x12345678_9abcdef0_0fedcba9_87654321, 1);
assert_eq!(field128.mask, 0x12345678_9abcdef0_0fedcba9_87654321_u128);
assert_eq!(field128.shift, 1);
}
#[test]
fn test_read() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(field.read(0x123), 0x12);
let field = Field::<u32, ()>::new(0xF0F, 4);
assert_eq!(field.read(0x1234), 0x103);
}
#[test]
fn test_is_set() {
let field = Field::<u16, ()>::new(0xFF, 4);
assert_eq!(field.is_set(0), false);
assert_eq!(field.is_set(0xFFFF), true);
assert_eq!(field.is_set(0x0FF0), true);
assert_eq!(field.is_set(0x1000), false);
assert_eq!(field.is_set(0x0100), true);
assert_eq!(field.is_set(0x0010), true);
assert_eq!(field.is_set(0x0001), false);
for shift in 0..24 {
let field = Field::<u32, ()>::new(0xFF, shift);
for x in 1..=0xFF {
assert_eq!(field.is_set(x << shift), true);
}
assert_eq!(field.is_set(!(0xFF << shift)), false);
}
}
#[test]
fn test_read_as_enum() {
let field = Field::<u16, ()>::new(0x7, 4);
assert_eq!(field.read_as_enum(0x1234), Some(Foo::Foo3));
assert_eq!(field.read_as_enum(0x5678), Some(Foo::Foo7));
assert_eq!(field.read_as_enum(0xFFFF), Some(Foo::Foo7));
assert_eq!(field.read_as_enum(0x0000), Some(Foo::Foo0));
assert_eq!(field.read_as_enum(0x0010), Some(Foo::Foo1));
assert_eq!(field.read_as_enum(0x1204), Some(Foo::Foo0));
for shift in 0..29 {
let field = Field::<u32, ()>::new(0x7, shift);
for x in 0..8 {
assert_eq!(field.read_as_enum(x << shift), Foo::try_from_value(x));
}
}
}
}
mod field_value {
use crate::fields::Field;
#[test]
fn test_from() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(u32::from(field.val(0)), 0);
assert_eq!(u32::from(field.val(0xFFFFFFFF)), 0xFF0);
assert_eq!(u32::from(field.val(0x12)), 0x120);
assert_eq!(u32::from(field.val(0x123)), 0x230);
for shift in 0..32 {
let field = Field::<u32, ()>::new(0xFF, shift);
for x in 0..=0xFF {
assert_eq!(u32::from(field.val(x)), x << shift);
}
}
}
#[test]
fn test_read_same_field() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(field.val(0).read(field), 0);
assert_eq!(field.val(0xFFFFFFFF).read(field), 0xFF);
assert_eq!(field.val(0x12).read(field), 0x12);
assert_eq!(field.val(0x123).read(field), 0x23);
for shift in 0..24 {
let field = Field::<u32, ()>::new(0xFF, shift);
for x in 0..=0xFF {
assert_eq!(field.val(x).read(field), x);
}
}
}
#[test]
fn test_read_disjoint_fields() {
for shift in 0..24 {
let field1 = Field::<u32, ()>::new(0xF0, shift);
let field2 = Field::<u32, ()>::new(0x0F, shift);
for x in 0..=0xFF {
assert_eq!(field1.val(x).read(field2), 0);
assert_eq!(field2.val(x).read(field1), 0);
}
}
for shift in 0..24 {
let field1 = Field::<u32, ()>::new(0xF, shift);
let field2 = Field::<u32, ()>::new(0xF, shift + 4);
for x in 0..=0xFF {
assert_eq!(field1.val(x).read(field2), 0);
assert_eq!(field2.val(x).read(field1), 0);
}
}
}
#[test]
fn test_modify() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(field.val(0x23).modify(0x0000), 0x0230);
assert_eq!(field.val(0x23).modify(0xFFFF), 0xF23F);
assert_eq!(field.val(0x23).modify(0x1234), 0x1234);
assert_eq!(field.val(0x23).modify(0x5678), 0x5238);
}
#[test]
fn test_matches_any() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(field.val(0x23).matches_any(0x1234), true);
assert_eq!(field.val(0x23).matches_any(0x5678), true);
assert_eq!(field.val(0x23).matches_any(0x5008), false);
for shift in 0..24 {
let field = Field::<u32, ()>::new(0xFF, shift);
for x in 0..=0xFF {
let field_value = field.val(x);
for y in 1..=0xFF {
assert_eq!(field_value.matches_any(y << shift), true);
}
assert_eq!(field_value.matches_any(0), false);
assert_eq!(field_value.matches_any(!(0xFF << shift)), false);
}
}
}
#[test]
fn test_matches_all() {
let field = Field::<u32, ()>::new(0xFF, 4);
assert_eq!(field.val(0x23).matches_all(0x1234), true);
assert_eq!(field.val(0x23).matches_all(0x5678), false);
for shift in 0..24 {
let field = Field::<u32, ()>::new(0xFF, shift);
for x in 0..=0xFF {
assert_eq!(field.val(x).matches_all(x << shift), true);
assert_eq!(field.val(x + 1).matches_all(x << shift), false);
}
}
}
#[test]
fn test_add_disjoint_fields() {
let field1 = Field::<u32, ()>::new(0xFF, 24);
let field2 = Field::<u32, ()>::new(0xFF, 16);
let field3 = Field::<u32, ()>::new(0xFF, 8);
let field4 = Field::<u32, ()>::new(0xFF, 0);
assert_eq!(
u32::from(
field1.val(0x12) + field2.val(0x34) + field3.val(0x56) + field4.val(0x78)
),
0x12345678
);
for shift in 0..24 {
let field1 = Field::<u32, ()>::new(0xF, shift);
let field2 = Field::<u32, ()>::new(0xF, shift + 4);
for x in 0..=0xF {
for y in 0..=0xF {
assert_eq!(
u32::from(field1.val(x) + field2.val(y)),
(x | (y << 4)) << shift
);
}
}
}
}
#[test]
fn test_add_assign_disjoint_fields() {
let field1 = Field::<u32, ()>::new(0xFF, 24);
let field2 = Field::<u32, ()>::new(0xFF, 16);
let field3 = Field::<u32, ()>::new(0xFF, 8);
let field4 = Field::<u32, ()>::new(0xFF, 0);
let mut value = field1.val(0x12);
value += field2.val(0x34);
value += field3.val(0x56);
value += field4.val(0x78);
assert_eq!(u32::from(value), 0x12345678);
for shift in 0..24 {
let field1 = Field::<u32, ()>::new(0xF, shift);
let field2 = Field::<u32, ()>::new(0xF, shift + 4);
for x in 0..=0xF {
for y in 0..=0xF {
let mut value = field1.val(x);
value += field2.val(y);
assert_eq!(u32::from(value), (x | (y << 4)) << shift);
}
}
}
}
}
}