Skip to content

use #[derive()] attributes for read/write/etc #285

@burrbull

Description

@burrbull
Member

External traits crate:

pub trait ResetValue {
    #[doc = r" Reset value of the register"]
    const RESET_VALUE: Self;
}

pub trait BitOffset {
    #[doc = r" Bit offset of the field"]
    const OFFSET: u8;
}

pub trait WriteRegister<W>
where
    W: ResetValue,
{
    #[doc = r" Writes to the register"]
    #[inline]
    fn write<F>(&self, f: F)
    // &self
    where
        F: FnOnce(&W) -> &mut W;
}

pub trait ReadRegister<R> {
    #[doc = r" Reads the contents of the register"]
    fn read(&self) -> R;
}

pub trait ResetRegister<W>: WriteRegister<W>
where
    W: ResetValue,
{
    #[doc = r" Writes the reset value to the register"]
    fn reset(&self) {
        // &self
        self.write(|w| w)
    }
}

pub trait ModifyRegister<R, W> {
    #[doc = r" Modifies the contents of the register"]
    #[inline]
    fn modify<F>(&self, f: F)
    // &self
    where
        for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W;
}

pub trait SingleBit<'a, W>
where
    Self: core::marker::Sized + BitOffset,
{
    #[doc = r" Sets the field bit"]
    fn set_bit(self) -> &'a mut W {
        self.bit(true)
    }
    #[doc = r" Clears the field bit"]
    fn clear_bit(self) -> &'a mut W {
        self.bit(false)
    }
    #[doc = r" Writes raw bits to the field"]
    #[inline]
    fn bit(self, value: bool) -> &'a mut W;
}

External macro crate:

extern crate proc_macro;

use crate::proc_macro::TokenStream;

use quote::quote;

use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(WriteRegister)]
pub fn write_register_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let regname = &input.ident;

    let expanded = quote! {
        impl crate::WriteRegister<W> for #regname {
            #[inline]
            fn write<F>(&self, f: F) // &self
            where
                F: FnOnce(&mut W) -> &mut W,
            {
                let mut w = W::RESET_VALUE;
                f(&mut w);
                self.register.set(w.bits);
            }
        }
    };
    TokenStream::from(expanded)
}

#[proc_macro_derive(ResetRegister)]
pub fn reset_register_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let regname = &input.ident;

    let expanded = quote! {
        impl ResetRegister<W> for #regname {}
    };
    TokenStream::from(expanded)
}

#[proc_macro_derive(ReadRegister)]
pub fn read_register_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let regname = &input.ident;

    let expanded = quote! {
        impl crate::ReadRegister<R> for #regname {
            #[inline]
            fn read(&self) -> R {
                R {
                    bits: self.register.get(),
                }
            }
        }
    };
    TokenStream::from(expanded)
}

#[proc_macro_derive(ModifyRegister)]
pub fn modify_register_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let regname = &input.ident;

    let expanded = quote! {
        impl crate::ModifyRegister<R, W> for #regname {
            #[inline]
            fn modify<F>(&self, f: F) // &self
            where
                for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W,
            {
                let bits = self.register.get();
                let r = R { bits };
                let mut w = W { bits };
                f(&r, &mut w);
                self.register.set(w.bits);
            }
        }
    };
    TokenStream::from(expanded)
}

#[proc_macro_derive(SingleBit)]
pub fn single_bit_derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);

    let regname = &input.ident;

    let expanded = quote! {
        impl<'a> SingleBit<'a, W> for #regname<'a> {
            #[inline]
            fn bit(self, value: bool) -> &'a mut W {
                const MASK: bool = true;
                self.w.bits &= !((MASK as u32) << Self::OFFSET);
                self.w.bits |= ((value & MASK) as u32) << Self::OFFSET;
                self.w
            }
        }
    };
    TokenStream::from(expanded)
}

Usage:

#[derive(ReadRegister, WriteRegister, ModifyRegister, ResetRegister)]
pub struct JDR {
    register: ::vcell::VolatileCell<u32>,
}

#[doc = r" Value read from the register"]
struct R {
    bits: u32,
}
#[doc = r" Value to write to the register"]
struct W {
    bits: u32,
}

impl crate::ResetValue for W {
    const RESET_VALUE: Self = Self { bits: 0 };
}

impl W {
    #[doc = "Bit 9 - Enable the watchdog on a single channel in scan mode"]
    #[inline]
    pub fn awdsgl(&mut self) -> _AWDSGLW {
        _AWDSGLW { w: self }
    }
    ...
}

impl<'a> BitOffset for _AWDSGLW<'a> {
    const OFFSET: u8 = 9;
}

#[doc = r" Proxy"]
#[derive(SingleBit)]
pub struct _AWDSGLW<'a> {
    w: &'a mut W,
}

Activity

therealprof

therealprof commented on Feb 23, 2019

@therealprof
Contributor

Interesting idea to reduce code size. I'm kind of wondering what this will do to our biggest pain point: compile time.

burrbull

burrbull commented on Feb 23, 2019

@burrbull
MemberAuthor

I don't have answer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @burrbull@therealprof

        Issue actions

          use #[derive()] attributes for read/write/etc · Issue #285 · rust-embedded/svd2rust