diff --git a/src/generate/mod.rs b/src/generate.rs similarity index 81% rename from src/generate/mod.rs rename to src/generate.rs index 54216bfb..6500943f 100644 --- a/src/generate/mod.rs +++ b/src/generate.rs @@ -1,4 +1,5 @@ pub mod device; +pub mod generic; pub mod interrupt; pub mod peripheral; pub mod register; diff --git a/src/generate/device.rs b/src/generate/device.rs index 1cc63db0..abe353f5 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -6,7 +6,7 @@ use crate::errors::*; use crate::util::{self, ToSanitizedUpperCase}; use crate::Target; -use crate::generate::{interrupt, peripheral}; +use crate::generate::{interrupt, peripheral, generic}; /// Whole device generation pub fn render( @@ -136,6 +136,8 @@ pub fn render( } } + out.extend(generic::render()?); + for p in &d.peripherals { if target == Target::CortexM && core_peripherals.contains(&&*p.name.to_uppercase()) { // Core peripherals are handled above diff --git a/src/generate/generic.rs b/src/generate/generic.rs new file mode 100644 index 00000000..3e3603de --- /dev/null +++ b/src/generate/generic.rs @@ -0,0 +1,197 @@ +use quote::Tokens; + +use crate::errors::*; + +/// Generates generic bit munging code +pub fn render() -> Result> { + let mut code = vec![]; + let mut generic_items = vec![]; + + generic_items.push(quote! { + use core::marker; + use core::ops::Deref; + use vcell::VolatileCell; + + ///Marker trait for readable register/field + pub trait Readable {} + + ///Marker trait for writable register/field + pub trait Writable {} + + /// Marker struct for register/field with safe write + pub struct Safe; + + /// Marker struct for register/field with unsafe write + pub struct Unsafe; + + ///Reset value of the register + pub trait ResetValue { + ///Reset value of the register + fn reset_value() -> U; + } + }); + + generic_items.push(quote! { + ///Wrapper for registers + pub struct Reg(pub(crate) REG); + + impl Reg + where + REG: Readable + Deref>, + U: Copy + { + ///Reads the contents of the register + #[inline(always)] + pub fn read(&self) -> R { + R::new((*self.0).get()) + } + } + + impl Reg + where + Self: ResetValue, + REG: Writable + Deref>, + U: Copy, + { + ///Writes the reset value to the register + #[inline(always)] + pub fn reset(&self) { + (*self.0).set(Self::reset_value()) + } + } + }); + + generic_items.push(quote! { + impl Reg + where + Self: ResetValue, + REG: Writable + Deref>, + U: Copy + { + ///Writes to the register + #[inline(always)] + pub fn write(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W + { + + (*self.0).set(f(&mut W::new(Self::reset_value())).bits); + } + } + }); + + generic_items.push(quote! { + impl Reg + where + REG: Writable + Deref>, + U: Copy + Default + { + ///Writes Zero to the register + #[inline(always)] + pub fn write_with_zero(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W + { + + (*self.0).set(f(&mut W::new(U::default())).bits); + } + } + }); + + generic_items.push(quote! { + impl Reg + where + REG: Readable + Writable + Deref>, + U: Copy, + { + ///Modifies the contents of the register + #[inline(always)] + pub fn modify(&self, f: F) + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W + { + let bits = (*self.0).get(); + (*self.0).set(f(&R::new(bits), &mut W::new(bits)).bits); + } + } + }); + + generic_items.push(quote! { + ///Register reader + pub struct R where T: Readable { + bits: U, + _reg: marker::PhantomData, + } + + impl R + where + T: Readable, + U: Copy + { + ///Create new instance of reader + #[inline(always)] + pub fn new(bits: U) -> Self { + Self { + bits, + _reg: marker::PhantomData, + } + } + ///Read raw bits from register + #[inline(always)] + pub fn bits(&self) -> U { + self.bits + } + } + }); + + generic_items.push(quote! { + ///Register writer + pub struct W where REG: Writable { + ///Writable bits + pub bits: U, + _reg: marker::PhantomData<(REG, S)>, + } + + impl W where REG: Writable { + ///Create new instance of reader + #[inline(always)] + pub(crate) fn new(bits: U) -> Self { + Self { + bits, + _reg: marker::PhantomData, + } + } + } + }); + + generic_items.push(quote! { + impl W where REG: Writable { + ///Writes raw bits to the register + #[inline(always)] + pub fn bits(&mut self, bits: U) -> &mut Self { + self.bits = bits; + self + } + } + + impl W where REG: Writable { + ///Writes raw bits to the register + #[inline(always)] + pub unsafe fn bits(&mut self, bits: U) -> &mut Self { + self.bits = bits; + self + } + } + }); + + code.push(quote! { + #[allow(unused_imports)] + use generic::*; + ///Common register and bit access and modify traits + pub mod generic { + #(#generic_items)* + } + }); + + Ok(code) +} diff --git a/src/generate/register.rs b/src/generate/register.rs index e2e66052..a348f848 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -17,6 +17,7 @@ pub fn render( let access = util::access_of(register); let name = util::name_of(register); let name_pc = Ident::from(&*name.to_sanitized_upper_case()); + let _name_pc = Ident::from(format!("_{}", &*name.to_sanitized_upper_case())); let name_sc = Ident::from(&*name.to_sanitized_snake_case()); let rsize = register .size @@ -32,109 +33,41 @@ pub fn render( let rty = rsize.to_ty()?; let description = util::escape_brackets(util::respace(®ister.description.clone().unwrap()).as_ref()); - let unsafety = unsafety(register.write_constraint.as_ref(), rsize); + let unsafety = unsafety_marker(register.write_constraint.as_ref(), rsize); let mut mod_items = vec![]; - let mut reg_impl_items = vec![]; let mut r_impl_items = vec![]; let mut w_impl_items = vec![]; let can_read = [Access::ReadOnly, Access::ReadWriteOnce, Access::ReadWrite].contains(&access); let can_write = access != Access::ReadOnly; - if access == Access::ReadWrite || access == Access::ReadWriteOnce { - reg_impl_items.push(quote! { - ///Modifies the contents of the register - #[inline(always)] - pub fn modify(&self, f: F) - where - for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W - { - let bits = self.register.get(); - self.register.set(f(&R { bits }, &mut W { bits }).bits); - } - }); - } - if can_read { - reg_impl_items.push(quote! { - ///Reads the contents of the register - #[inline(always)] - pub fn read(&self) -> R { - R { bits: self.register.get() } - } - }); - + let desc = format!("Reader for register {}", register.name); mod_items.push(quote! { - ///Value read from the register - pub struct R { - bits: #rty, - } - }); - - r_impl_items.push(quote! { - ///Value of the register as raw bits - #[inline(always)] - pub fn bits(&self) -> #rty { - self.bits - } + #[doc = #desc] + pub type R = crate::R<#rty, super::#_name_pc>; }); } if can_write { - reg_impl_items.push(quote! { - ///Writes to the register - #[inline(always)] - pub fn write(&self, f: F) - where - F: FnOnce(&mut W) -> &mut W - { - self.register.set(f(&mut W { bits: Self::reset_value() }).bits); - } - }); - - mod_items.push(quote! { - ///Value to write to the register - pub struct W { - bits: #rty, - } - }); - let rv = register .reset_value .or(defs.reset_value) .map(util::hex) .ok_or_else(|| format!("Register {} has no reset value", register.name))?; - reg_impl_items.push(quote! { - ///Reset value of the register - #[inline(always)] - pub const fn reset_value() -> #rty { - #rv - } - ///Writes the reset value to the register - #[inline(always)] - pub fn reset(&self) { - self.register.set(Self::reset_value()) - } - }); - - w_impl_items.push(quote! { - ///Writes raw bits to the register - #[inline(always)] - pub #unsafety fn bits(&mut self, bits: #rty) -> &mut Self { - self.bits = bits; - self + let desc = format!("Writer for register {}", register.name); + mod_items.push(quote! { + #[doc = #desc] + pub type W = crate::W<#rty, super::#_name_pc, crate::#unsafety>; + impl crate::ResetValue<#rty> for super::#name_pc { + #[inline(always)] + fn reset_value() -> #rty { #rv } } }); } - mod_items.push(quote! { - impl super::#name_pc { - #(#reg_impl_items)* - } - }); - if let Some(cur_fields) = register.fields.as_ref() { // filter out all reserved fields, as we should not generate code for // them @@ -179,9 +112,33 @@ pub fn render( let mut out = vec![]; out.push(quote! { #[doc = #description] - pub struct #name_pc { + pub type #name_pc = crate::Reg<#_name_pc>; + + #[allow(missing_docs)] + pub struct #_name_pc { register: vcell::VolatileCell<#rty> } + }); + + if can_read { + out.push(quote! { + impl crate::Readable for #_name_pc {} + }); + } + if can_write { + out.push(quote! { + impl crate::Writable for #_name_pc {} + }); + } + + out.push(quote! { + impl core::ops::Deref for #_name_pc { + type Target = vcell::VolatileCell<#rty>; + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.register + } + } #[doc = #description] pub mod #name_sc { @@ -296,7 +253,7 @@ pub fn fields( quote! { as #fty } }; let value = quote! { - ((self.bits >> #offset) & #mask) #cast + ((self.bits() >> #offset) & #mask) #cast }; if let Some((evs, base)) = lookup_filter(&lookup_results, Usage::Read) { @@ -728,6 +685,25 @@ fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> Option, width: u32) -> Ident { + Ident::new(match &write_constraint { + Some(&WriteConstraint::Range(range)) + if u64::from(range.min) == 0 && u64::from(range.max) == (1u64 << width) - 1 => + { + // the SVD has acknowledged that it's safe to write + // any value that can fit in the field + "Safe" + } + None if width == 1 => { + // the field is one bit wide, so we assume it's legal to write + // either value into it or it wouldn't exist; despite that + // if a writeConstraint exists then respect it + "Safe" + } + _ => "Unsafe", + }) +} + struct Variant { doc: String, pc: Ident,