peripheral-register 0.1.4

Describe peripheral register
Documentation
#![no_std]
use core::convert::{From, Into};
use core::marker::PhantomData;
use core::slice::from_raw_parts_mut;

pub struct RegisterValue(usize);

impl From<u8> for RegisterValue {
    fn from(value: u8) -> RegisterValue {
        RegisterValue(value as usize)
    }
}

impl From<u16> for RegisterValue {
    fn from(value: u16) -> RegisterValue {
        RegisterValue(value as usize)
    }
}

impl From<u32> for RegisterValue {
    fn from(value: u32) -> RegisterValue {
        RegisterValue(value as usize)
    }
}

impl From<RegisterValue> for u8 {
    fn from(value: RegisterValue) -> u8 {
        value.0 as u8
    }
}

impl From<RegisterValue> for u16 {
    fn from(value: RegisterValue) -> u16 {
        value.0 as u16
    }
}

impl From<RegisterValue> for u32 {
    fn from(value: RegisterValue) -> u32 {
        value.0 as u32
    }
}

pub trait Field {
    fn get_offset_length(&self) -> (usize, usize);

    fn of(&self, value: usize) -> usize {
        let (offset, length) = self.get_offset_length();
        (value & ((1 << length) - 1)) << offset
    }
}

pub enum NoField {}

impl Field for NoField {
    fn get_offset_length(&self) -> (usize, usize) {
        return (0, 0);
    }
}

pub enum Bits {
    Of(usize, usize),
}

impl Field for Bits {
    fn get_offset_length(&self) -> (usize, usize) {
        match self {
            Bits::Of(offset, length) => (*offset, *length),
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Register<T, U: Field = NoField> {
    pub value: T,
    u: PhantomData<U>,
}

impl<T, U: Field> From<T> for Register<T, U> {
    fn from(value: T) -> Register<T, U> {
        Register {
            value,
            u: PhantomData,
        }
    }
}

impl<'a, T, U: Field> Into<&'a mut [T]> for Register<T, U> {
    fn into(mut self) -> &'a mut [T] {
        unsafe { from_raw_parts_mut(&mut self.value, 1) }
    }
}

impl<T, U> Register<T, U>
where
    T: Into<RegisterValue> + From<RegisterValue> + Copy + Default,
    U: Field,
{
    pub fn new(value: T) -> Self {
        Self {
            value: value,
            u: PhantomData,
        }
    }

    pub fn of(u: U, value: T) -> Self {
        let mut v = Self {
            value: Default::default(),
            u: PhantomData,
        };
        v.set(u, value);
        v
    }

    pub fn get(&self, u: U) -> T {
        let (offset, length) = u.get_offset_length();
        let mask = ((1 << length) - 1) << offset;
        RegisterValue((self.value.into().0 >> offset) & mask).into()
    }

    pub fn is_set(&self, u: U) -> bool {
        self.get(u).into().0 > 0
    }

    pub fn set(&mut self, u: U, value: T) {
        let (offset, length) = u.get_offset_length();
        let mask = ((1 << length) - 1) << offset;
        let to_set = self.value.into().0 & !mask;
        let usize_value = value.into().0;
        self.value = RegisterValue(to_set | (usize_value << offset) & mask).into();
    }

    pub fn reset(&mut self, u: U) {
        let (offset, length) = u.get_offset_length();
        let mask = ((1 << length) - 1) << offset;
        let to_set = self.value.into().0 & !mask;
        self.value = RegisterValue(to_set).into();
    }
}

#[macro_export]
macro_rules! register_fields {
    (
        $(
            $(#[$outer:meta])*
            enum $name:ident {
                $($field:ident = $offset:tt : $len:tt,)*
            }
        )*
    ) => {
        $(
            $(#[$outer])*
            #[repr(usize)]
            #[derive(Copy, Clone)]
            enum $name {
                $($field = $offset << 8 | $len,)*
            }

            impl Field for $name {
                fn get_offset_length(&self) -> (usize, usize) {
                    (*self as usize >> 8, *self as usize & 0xFF)
                }
            }
        )*
    };
    (
        $(
            $(#[$outer:meta])*
            pub enum $name:ident {
                $($field:ident = $offset:tt : $len:tt,)*
            }
        )*
    ) => {
        $(
            $(#[$outer])*
            #[repr(usize)]
            #[derive(Copy, Clone)]
            pub enum $name {
                $($field = $offset << 8 | $len,)*
            }

            impl Field for $name {
                fn get_offset_length(&self) -> (usize, usize) {
                    (*self as usize >> 8, *self as usize & 0xFF)
                }
            }
        )*
    }
}