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 35a5bfe2..0d10720f 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( @@ -42,7 +42,7 @@ pub fn render( out.push(quote! { #![doc = #doc] - #![deny(missing_docs)] + //#![deny(missing_docs)] #![deny(warnings)] #![allow(non_camel_case_types)] #![no_std] @@ -81,6 +81,8 @@ pub fn render( use core::ops::Deref; use core::marker::PhantomData; + #[allow(unused_imports)] + use generic::*; }); // Retaining the previous assumption @@ -110,6 +112,13 @@ pub fn render( ] }; + match target { + Target::CortexM => out.extend(generic::render(&[8, 16, 32])?), + Target::Msp430 => out.extend(generic::render(&[8, 16])?), + Target::RISCV => out.extend(generic::render(&[32, 64])?), + _ => out.extend(generic::render(&[8, 16, 32, 64])?), + } + let mut fields = vec![]; let mut exprs = vec![]; if target == Target::CortexM { diff --git a/src/generate/generic.rs b/src/generate/generic.rs new file mode 100644 index 00000000..59556847 --- /dev/null +++ b/src/generate/generic.rs @@ -0,0 +1,530 @@ +use quote::Tokens; + +use crate::errors::*; +use crate::util::U32Ext; + +/// Generates generic bit munging code +pub fn render(rsizes: &[u32]) -> Result> { + let mut code = vec![]; + let mut generic_items = vec![]; + + generic_items.push(quote! { + use core::marker::PhantomData; + use core::ops::Deref; + use vcell::VolatileCell; + + ///Marker trait for writable register + pub trait Writable {} + + ///Marker trait for readable register + pub trait Readable {} + + ///Reset value of the register + pub trait ResetValue { + /// Reset value of the register + fn reset_value() -> U; + } + + ///Field offset + pub trait Offset { + const OFFSET: u8; + } + + ///Mask of field + pub trait Mask { + const MASK: U; + } + + ///Marker struct for field with safe write + pub struct Safe; + ///Marker struct for field with unsafe write + pub struct Unsafe; + + pub trait ToBits { + fn _bits(&self) -> N; + } + + /// Marker trait for Enums + pub trait Variant {} + }); + + generic_items.push(quote! { + ///Value read from the register + pub struct R where T: Readable { + bits: U, + _reg: PhantomData, + } + + impl PartialEq for R + where + T: Readable, + U: PartialEq, + FI: ToBits + { + fn eq(&self, other: &FI) -> bool { + self.bits.eq(&other._bits()) + } + } + + impl R + where + T: Readable, + U: Copy + { + #[inline(always)] + pub(crate) fn new(bits: U) -> Self { + Self { + bits, + _reg: PhantomData, + } + } + #[inline(always)] + pub fn bits(&self) -> U { + self.bits + } + } + }); + + generic_items.push(quote! { + impl R + where + FI: Readable, + { + ///Value of the field as raw bits + #[inline(always)] + pub fn bit(&self) -> bool { + self.bits + } + ///Returns `true` if the bit is clear (0) + #[inline(always)] + pub fn bit_is_clear(&self) -> bool { + !self.bit() + } + ///Returns `true` if the bit is set (1) + #[inline(always)] + pub fn bit_is_set(&self) -> bool { + self.bit() + } + } + }); + + generic_items.push(quote! { + ///Value to write to the register + pub struct W where REG: Writable { + pub(crate) bits: U, + _reg: PhantomData, + } + + impl W where REG: Writable { + #[inline(always)] + pub(crate) fn new(bits: U) -> Self { + Self { + bits, + _reg: PhantomData, + } + } + } + + 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 + } + } + }); + + generic_items.push(quote! { + ///Writer Proxy + pub struct WProxy<'a, U, REG, N, FI, S> + where + REG: Writable, + FI: Writable, + { + w: &'a mut W, + _field: PhantomData<(FI, N, S)>, + } + + impl<'a, U, REG, N, FI, S> WProxy<'a, U, REG, N, FI, S> + where + REG: Writable, + FI: Writable, + { + pub(crate) fn new(w: &'a mut W) -> Self { + Self { + w, + _field: PhantomData, + } + } + } + }); + + generic_items.push(quote! { + macro_rules! impl_bit_proxy { + ($U:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, bool, FI, Safe> + where + REG: Writable, + FI: Writable + Offset, + { + ///Sets the field bit"] + pub fn set_bit(self) -> &'a mut W<$U, REG> { + self.bit(true) + } + ///Clears the field bit"] + pub fn clear_bit(self) -> &'a mut W<$U, REG> { + self.bit(false) + } + ///Writes raw bits to the field"] + #[inline(always)] + pub fn bit(self, value: bool) -> &'a mut W<$U, REG> { + self.w.bits &= !(0x01 << FI::OFFSET); + self.w.bits |= ((value as $U) & 0x01) << FI::OFFSET; + self.w + } + } + } + } + }); + + generic_items.push(quote! { + macro_rules! impl_bit_variant_proxy { + ($U:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, bool, FI, Safe> + where + REG: Writable, + FI: Writable + Offset + ToBits + Variant, + { + ///Writes `variant` to the field + #[inline] + pub fn variant(self, variant: FI) -> &'a mut W<$U, REG> { + self.bit(variant._bits()) + } + } + } + } + }); + + let max_rsize = rsizes.iter().max().unwrap(); + for fsize in &[8, 16, 32, 64] { + if fsize > max_rsize { + break; + } + let fty = fsize.to_ty()?; + generic_items.push(quote! { + impl_bit_proxy!(#fty); + impl_bit_variant_proxy!(#fty); + }); + } + + generic_items.push(quote! { + macro_rules! impl_proxy_safe { + ($U:ty | $N:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, $N, FI, Safe> + where + REG: Writable, + FI: Writable + Mask<$U> + Offset, + { + ///Writes raw bits to the field"] + #[inline] + pub fn bits(self, value: $N) -> &'a mut W<$U, REG> { + self.w.bits &= !(FI::MASK << FI::OFFSET); + self.w.bits |= ((value as $U) & FI::MASK) << FI::OFFSET; + self.w + } + } + } + } + }); + generic_items.push(quote! { + macro_rules! impl_proxy_unsafe { + ($U:ty | $N:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, $N, FI, Unsafe> + where + REG: Writable, + FI: Writable + Mask<$U> + Offset, + { + ///Writes raw bits to the field"] + #[inline] + pub unsafe fn bits(self, value: $N) -> &'a mut W<$U, REG> { + self.w.bits &= !(FI::MASK << FI::OFFSET); + self.w.bits |= ((value as $U) & FI::MASK) << FI::OFFSET; + self.w + } + } + } + } + }); + + generic_items.push(quote! { + macro_rules! impl_proxy_variant_safe { + ($U:ty | $N:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, $N, FI, Safe> + where + REG: Writable, + FI: Writable + Mask<$U> + Offset + ToBits<$N> + Variant, + { + ///Writes `variant` to the field + #[inline] + pub fn variant(self, variant: FI) -> &'a mut W<$U, REG> { + self.bits(variant._bits()) + } + } + } + } + }); + + generic_items.push(quote! { + macro_rules! impl_proxy_variant_unsafe { + ($U:ty | $N:ty) => { + impl<'a, REG, FI> WProxy<'a, $U, REG, $N, FI, Unsafe> + where + REG: Writable, + FI: Writable + Mask<$U> + Offset + ToBits<$N> + Variant, + { + ///Writes `variant` to the field + #[inline] + pub fn variant(self, variant: FI) -> &'a mut W<$U, REG> { + unsafe { self.bits(variant._bits()) } + } + } + } + } + }); + + for (i, rsize) in rsizes.iter().enumerate() { + let rty = rsize.to_ty()?; + for j in 0..=i { + let fty = rsizes[j].to_ty()?; + generic_items.push(quote! { + impl_proxy_safe!(#rty | #fty); + impl_proxy_unsafe!(#rty | #fty); + impl_proxy_variant_safe!(#rty | #fty); + impl_proxy_variant_unsafe!(#rty | #fty); + }); + } + } + + generic_items.push(quote! { + ///Writes the reset value to the register + pub trait ResetRegister: Writable + ResetValue + where + U: Copy, + { + ///Writes the reset value to the register + fn reset(&self); + } + + ///Writes the reset value to the register + impl ResetRegister for REG + where + REG: Writable + ResetValue + Deref>, + U: Copy, + { + ///Writes the reset value to the register + #[inline(always)] + fn reset(&self) { + (*self).set(Self::reset_value()) + } + } + }); + + generic_items.push(quote! { + ///Reads the contents of the register + pub trait ReadRegister + where + REG: Readable + Deref> + { + ///Reads the contents of the register + fn read(&self) -> R; + } + + impl ReadRegister for REG + where + REG: Readable + Deref>, + U: Copy + { + #[inline(always)] + fn read(&self) -> R { + R::new((*self).get()) + } + } + }); + + generic_items.push(quote! { + /// Writes to the register using `reset_value` as basis + pub trait WriteRegister + where + REG: Writable + ResetValue + Deref> + { + ///Writes to the register + fn write(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W; + } + + impl WriteRegister for REG + where + REG: Writable + ResetValue + Deref>, + U: Copy + { + ///Writes to the register + #[inline(always)] + fn write(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W + { + + (*self).set(f(&mut W::new(Self::reset_value())).bits); + } + } + }); + + generic_items.push(quote! { + ///Writes to the register using Zero as basis + pub trait WriteRegisterWithZero + where + REG: Writable + Deref>, + U: Default + { + ///Writes to the register + fn write_with_zero(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W; + } + + impl WriteRegisterWithZero for REG + where + REG: Writable + Deref>, + U: Copy + Default + { + ///Writes to the register + #[inline(always)] + fn write_with_zero(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W + { + + (*self).set(f(&mut W::new(U::default())).bits); + } + } + }); + + generic_items.push(quote! { + ///Modifies the contents of the register + pub trait ModifyRegister + where + REG: Readable + Writable + Deref> + { + ///Modifies the contents of the register + fn modify(&self, f: F) + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W; + } + + impl ModifyRegister for REG + where + REG: Readable + Writable + Deref>, + U: Copy, + { + ///Modifies the contents of the register + #[inline(always)] + fn modify(&self, f: F) + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W + { + let bits = (*self).get(); + (*self).set(f(&R::new(bits), &mut W::new(bits)).bits); + } + } + }); + + generic_items.push(quote! { + #[macro_export] + macro_rules! impl_deref { + ($U:ty, $REG:ty) => { + impl core::ops::Deref for $REG { + type Target = vcell::VolatileCell<$U>; + fn deref(&self) -> &Self::Target { + &self.register + } + } + } + } + }); + + generic_items.push(quote! { + #[macro_export] + macro_rules! impl_read { + ($U:ty, $REG:ty) => { + impl crate::Readable for $REG {} + impl $REG { + #[inline(always)] + pub fn read(&self) -> _R { + >::read(self) + } + } + } + } + }); + + generic_items.push(quote! { + #[macro_export] + macro_rules! impl_write { + ($U:ty, $REG:ty) => { + impl crate::Writable for $REG {} + impl $REG { + #[inline(always)] + pub fn write(&self, f: F) + where + F: FnOnce(&mut _W) -> &mut _W + { + >::write(self, f) + } + #[inline(always)] + pub fn write_with_zero(&self, f: F) + where + F: FnOnce(&mut _W) -> &mut _W + { + >::write_with_zero(self, f) + } + #[inline(always)] + pub fn reset(&self) { + >::reset(self) + } + } + } + } + }); + + generic_items.push(quote! { + #[macro_export] + macro_rules! impl_modify { + ($U:ty, $REG:ty) => { + impl $REG { + /// 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 + { + >::modify(self, f) + } + } + } + } + }); + + + 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 0c7eea40..41301a36 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -1,7 +1,7 @@ use cast::u64; use quote::Tokens; use crate::svd::{Access, BitRange, Defaults, EnumeratedValues, Field, Peripheral, Register, - RegisterCluster, Usage, WriteConstraint}; + RegisterCluster, WriteConstraint}; use syn::Ident; use crate::errors::*; @@ -32,10 +32,9 @@ 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(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![]; @@ -43,98 +42,36 @@ pub fn render( 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); - } + mod_items.push(quote! { + crate::impl_modify!(#rty, super::#name_pc); }); } 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() } - } - }); - 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 - } + pub type _R = crate::R<#rty, super::#name_pc>; + crate::impl_read!(#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()) - } - }); + mod_items.push(quote! { + pub type _W = crate::W<#rty, super::#name_pc>; + crate::impl_write!(#rty, super::#name_pc); - 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 + 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 @@ -146,6 +83,7 @@ pub fn render( if !cur_fields.is_empty() { fields( + &name_pc, &cur_fields, register, all_registers, @@ -162,7 +100,7 @@ pub fn render( if can_read { mod_items.push(quote! { - impl R { + impl _R { #(#r_impl_items)* } }); @@ -170,7 +108,7 @@ pub fn render( if can_write { mod_items.push(quote! { - impl W { + impl _W { #(#w_impl_items)* } }); @@ -183,6 +121,8 @@ pub fn render( register: vcell::VolatileCell<#rty> } + crate::impl_deref!(#rty, #name_pc); + #[doc = #description] pub mod #name_sc { #(#mod_items)* @@ -193,6 +133,7 @@ pub fn render( } pub fn fields( + name_pc: &Ident, fields: &[Field], parent: &Register, all_registers: &[&Register], @@ -213,10 +154,9 @@ pub fn fields( mask: Tokens, name: &'a str, offset: Tokens, + pc: Ident, pc_r: Ident, - pc_w: Ident, sc: Ident, - bits: Ident, ty: Ident, width: u32, write_constraint: Option<&'a WriteConstraint>, @@ -227,16 +167,11 @@ pub fn fields( // TODO(AJM) - do we need to do anything with this range type? let BitRange { offset, width, range_type: _ } = f.bit_range; let sc = f.name.to_sanitized_snake_case(); - let pc = f.name.to_sanitized_upper_case(); - let pc_r = Ident::new(&*format!("{}R", pc)); - let pc_w = Ident::new(&*format!("{}W", pc)); - let _pc_w = Ident::new(&*format!("_{}W", pc)); + let pc_ = f.name.to_sanitized_upper_case(); + let pc = Ident::new(&*pc_); + let pc_r = Ident::new(&*format!("_{}R", pc_)); + let _pc_w = Ident::new(&*format!("_{}W", pc_)); let _sc = Ident::new(&*format!("_{}", sc)); - let bits = if width == 1 { - Ident::new("bit") - } else { - Ident::new("bits") - }; let mut description = if width == 1 { format!("Bit {}", offset) } else { @@ -250,9 +185,8 @@ pub fn fields( _pc_w, _sc, description, + pc, pc_r, - pc_w, - bits, width, access: f.access, evs: &f.enumerated_values, @@ -268,519 +202,287 @@ pub fn fields( let fs = fields.iter().map(F::from).collect::>>()?; - // TODO enumeratedValues - if [Access::ReadOnly, Access::ReadWriteOnce, Access::ReadWrite].contains(&access) { - for f in &fs { - if f.access == Some(Access::WriteOnly) || f.access == Some(Access::WriteOnce) { - continue; - } - - let bits = &f.bits; - let mask = &f.mask; - let offset = &f.offset; - let fty = &f.ty; - let cast = if f.width == 1 { - quote! { != 0 } - } else { - quote! { as #fty } - }; - let value = quote! { - ((self.bits >> #offset) & #mask) #cast - }; + for f in &fs { + let can_read = [Access::ReadOnly, Access::ReadWriteOnce, Access::ReadWrite].contains(&access) && !(f.access == Some(Access::WriteOnly) || f.access == Some(Access::WriteOnce)); + let can_write = (access != Access::ReadOnly) && (f.access != Some(Access::ReadOnly)); - if let Some((evs, base)) = lookup( - f.evs, - fields, - parent, - all_registers, - peripheral, - all_peripherals, - Usage::Read, - )? { - struct Variant<'a> { - description: &'a str, - pc: Ident, - sc: Ident, - value: u64, - } + let fty = &f.ty; + let pc = &f.pc; + let mut unsafety = unsafety(f.write_constraint, f.width); - let has_reserved_variant = evs.values.len() != (1 << f.width); - let variants = evs.values - .iter() - // filter out all reserved variants, as we should not - // generate code for them - .filter(|field| field.name.to_lowercase() != "reserved") - .map(|ev| { - let sc = - Ident::new(&*ev.name.to_sanitized_snake_case()); - let description = ev.description - .as_ref() - .map(|s| &**s) - .unwrap_or("undocumented"); - - let value = u64(ev.value.ok_or_else(|| { - format!("EnumeratedValue {} has no field", - ev.name) - })?); - Ok(Variant { - description, - sc, - pc: Ident::new(&*ev.name - .to_sanitized_upper_case()), - value, - }) - }) - .collect::>>()?; + let mut pc_r_impl_items = vec![]; + let mut proxy_items = vec![]; - let pc_r = &f.pc_r; - if let Some(base) = &base { - let pc = base.field.to_sanitized_upper_case(); - let base_pc_r = Ident::new(&*format!("{}R", pc)); - let desc = format!("Possible values of the field `{}`", f.name,); + if let Some((evs, base)) = lookup( + f.evs, + fields, + parent, + all_registers, + peripheral, + all_peripherals, + )? { + struct Variant<'a> { + description: &'a str, + pc: Ident, + sc: Ident, + value: u64, + } - if let (Some(peripheral), Some(register)) = (&base.peripheral, &base.register) { - let pmod_ = peripheral.to_sanitized_snake_case(); - let rmod_ = register.to_sanitized_snake_case(); - let pmod_ = Ident::new(&*pmod_); - let rmod_ = Ident::new(&*rmod_); + let variants = evs.values + .iter() + // filter out all reserved variants, as we should not + // generate code for them + .filter(|field| field.name.to_lowercase() != "reserved") + .map(|ev| { + let sc = + Ident::new(&*ev.name.to_sanitized_snake_case()); + let description = ev.description + .as_ref() + .map(|s| &**s) + .unwrap_or("undocumented"); + + let value = u64(ev.value.ok_or_else(|| { + format!("EnumeratedValue {} has no field", + ev.name) + })?); + Ok(Variant { + description, + sc, + pc: Ident::new(&*ev.name + .to_sanitized_upper_case()), + value, + }) + }) + .collect::>>()?; - mod_items.push(quote! { - #[doc = #desc] - pub type #pc_r = crate::#pmod_::#rmod_::#base_pc_r; - }); - } else if let Some(register) = &base.register { - let mod_ = register.to_sanitized_snake_case(); - let mod_ = Ident::new(&*mod_); + if variants.len() == 1 << f.width { + unsafety = Ident::new("Safe"); + } - mod_items.push(quote! { - #[doc = #desc] - pub type #pc_r = super::#mod_::#base_pc_r; - }); - } else { - mod_items.push(quote! { - #[doc = #desc] - pub type #pc_r = #base_pc_r; - }); - } - } + if let Some(base) = &base { + let base_pc = Ident::new(&*base.field.to_sanitized_upper_case()); + let desc = format!("Possible values of the field `{}`", f.name); - let description = &util::escape_brackets(&f.description); - let sc = &f.sc; - r_impl_items.push(quote! { - #[doc = #description] - #[inline(always)] - pub fn #sc(&self) -> #pc_r { - #pc_r::_from( #value ) - } - }); + if let (Some(peripheral), Some(register)) = (&base.peripheral, &base.register) { + let pmod_ = peripheral.to_sanitized_snake_case(); + let rmod_ = register.to_sanitized_snake_case(); + let pmod_ = Ident::new(&*pmod_); + let rmod_ = Ident::new(&*rmod_); - if base.is_none() { - let desc = format!("Possible values of the field `{}`", f.name,); - - let mut vars = variants - .iter() - .map(|v| { - let desc = util::escape_brackets(&v.description); - let pc = &v.pc; - quote! { - #[doc = #desc] - #pc - } - }) - .collect::>(); - if has_reserved_variant { - vars.push(quote! { - /// Reserved - _Reserved(#fty) - }); - } mod_items.push(quote! { #[doc = #desc] - #[derive(Clone, Copy, Debug, PartialEq)] - pub enum #pc_r { - #(#vars),* - } - }); - - let mut enum_items = vec![]; - - let mut arms = variants - .iter() - .map(|v| { - let value = util::hex_or_bool(v.value as u32, f.width); - let pc = &v.pc; - - quote! { - #pc_r::#pc => #value - } - }) - .collect::>(); - if has_reserved_variant { - arms.push(quote! { - #pc_r::_Reserved(bits) => bits - }); - } - - if f.width == 1 { - enum_items.push(quote! { - /// Returns `true` if the bit is clear (0) - #[inline(always)] - pub fn bit_is_clear(&self) -> bool { - !self.#bits() - } - - /// Returns `true` if the bit is set (1) - #[inline(always)] - pub fn bit_is_set(&self) -> bool { - self.#bits() - } - }); - } - - enum_items.push(quote! { - /// Value of the field as raw bits - #[inline(always)] - pub fn #bits(&self) -> #fty { - match *self { - #(#arms),* - } - } + pub type #pc = crate::#pmod_::#rmod_::#base_pc; }); + } else if let Some(register) = &base.register { + let mod_ = register.to_sanitized_snake_case(); + let mod_ = Ident::new(&*mod_); - let mut arms = variants - .iter() - .map(|v| { - let i = util::unsuffixed_or_bool(v.value, f.width); - let pc = &v.pc; - - quote! { - #i => #pc_r::#pc - } - }) - .collect::>(); - - if has_reserved_variant { - arms.push(quote! { - i => #pc_r::_Reserved(i) - }); - } else if 1 << f.width.to_ty_width()? != variants.len() { - arms.push(quote! { - _ => unreachable!() - }); - } - - enum_items.push(quote! { - #[allow(missing_docs)] - #[doc(hidden)] - #[inline(always)] - pub fn _from(value: #fty) -> #pc_r { - match value { - #(#arms),*, - } - } + mod_items.push(quote! { + #[doc = #desc] + pub type #pc = super::#mod_::#base_pc; }); - - for v in &variants { - let pc = &v.pc; - let sc = &v.sc; - - let is_variant = if sc.as_ref().starts_with('_') { - Ident::new(&*format!("is{}", sc)) - } else { - Ident::new(&*format!("is_{}", sc)) - }; - - let doc = format!("Checks if the value of the field is `{}`", pc); - enum_items.push(quote! { - #[doc = #doc] - #[inline(always)] - pub fn #is_variant(&self) -> bool { - *self == #pc_r::#pc - } - }); - } - + } else { mod_items.push(quote! { - impl #pc_r { - #(#enum_items)* - } + #[doc = #desc] + pub type #pc = #base_pc; }); } } else { - let description = &util::escape_brackets(&f.description); - let pc_r = &f.pc_r; - let sc = &f.sc; - r_impl_items.push(quote! { - #[doc = #description] - #[inline(always)] - pub fn #sc(&self) -> #pc_r { - let bits = #value; - #pc_r { bits } - } - }); + let desc = format!("Possible values of the field `{}`", f.name,); - let mut pc_r_impl_items = vec![ - quote! { - /// Value of the field as raw bits - #[inline(always)] - pub fn #bits(&self) -> #fty { - self.bits - } - }, - ]; - - if f.width == 1 { - pc_r_impl_items.push(quote! { - /// Returns `true` if the bit is clear (0) - #[inline(always)] - pub fn bit_is_clear(&self) -> bool { - !self.#bits() - } - - /// Returns `true` if the bit is set (1) - #[inline(always)] - pub fn bit_is_set(&self) -> bool { - self.#bits() + let vars = variants + .iter() + .map(|v| { + let desc = util::escape_brackets(&v.description); + let pc = &v.pc; + quote! { + #[doc = #desc] + #pc } - }); - } + }) + .collect::>(); mod_items.push(quote! { - /// Value of the field - pub struct #pc_r { - bits: #fty, - } - - impl #pc_r { - #(#pc_r_impl_items)* + #[doc = #desc] + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum #pc { + #(#vars),* } }); - } - } - } - - if access != Access::ReadOnly { - for f in &fs { - if f.access == Some(Access::ReadOnly) { - continue; - } - - let mut proxy_items = vec![]; - let mut unsafety = unsafety(f.write_constraint, f.width); - let bits = &f.bits; - let fty = &f.ty; - let offset = &f.offset; - let mask = &f.mask; - let width = f.width; - - if let Some((evs, base)) = lookup( - &f.evs, - fields, - parent, - all_registers, - peripheral, - all_peripherals, - Usage::Write, - )? { - struct Variant { - doc: String, - pc: Ident, - sc: Ident, - value: u64, + if can_read { + mod_items.push(quote! { + impl crate::Readable for #pc {} + }); } - let pc_w = &f.pc_w; - let pc_w_doc = format!("Values that can be written to the field `{}`", f.name); - - let base_pc_w = base.as_ref().map(|base| { - let pc = base.field.to_sanitized_upper_case(); - let base_pc_w = Ident::new(&*format!("{}W", pc)); - - if let (Some(peripheral), Some(register)) = (&base.peripheral, &base.register) { - let pmod_ = peripheral.to_sanitized_snake_case(); - let rmod_ = register.to_sanitized_snake_case(); - let pmod_ = Ident::new(&*pmod_); - let rmod_ = Ident::new(&*rmod_); - - mod_items.push(quote! { - #[doc = #pc_w_doc] - pub type #pc_w = - crate::#pmod_::#rmod_::#base_pc_w; - }); - - quote! { - crate::#pmod_::#rmod_::#base_pc_w - } - } else if let Some(register) = &base.register { - let mod_ = register.to_sanitized_snake_case(); - let mod_ = Ident::new(&*mod_); - - mod_items.push(quote! { - #[doc = #pc_w_doc] - pub type #pc_w = - super::#mod_::#base_pc_w; - }); - - quote! { - super::#mod_::#base_pc_w + if can_write { + let offset = &f.offset; + mod_items.push(quote! { + impl crate::Writable for #pc {} + impl crate::Variant for #pc {} + impl crate::Offset for #pc { + const OFFSET: u8 = #offset; } - } else { + }); + if f.width > 1 { + let mask = &f.mask; mod_items.push(quote! { - #[doc = #pc_w_doc] - pub type #pc_w = #base_pc_w; + impl crate::Mask<#rty> for #pc { + const MASK: #rty = #mask; + } }); - - quote! { - #base_pc_w - } } - }); - - let variants = evs.values - .iter() - // filter out all reserved variants, as we should not - // generate code for them - .filter(|field| field.name.to_lowercase() != "reserved") - .map( - |ev| { - let value = u64(ev.value.ok_or_else(|| { - format!("EnumeratedValue {} has no `` field", - ev.name)})?); - - Ok(Variant { - doc: ev.description - .clone() - .unwrap_or_else(|| { - format!("`{:b}`", value) - }), - pc: Ident::new(&*ev.name - .to_sanitized_upper_case()), - sc: Ident::new(&*ev.name - .to_sanitized_snake_case()), - value, - }) - }, - ) - .collect::>>()?; - - if variants.len() == 1 << f.width { - unsafety = None; } + let mut enum_items = vec![]; - if base.is_none() { - let variants_pc = variants.iter().map(|v| &v.pc); - let variants_doc = variants.iter().map(|v| util::escape_brackets(&v.doc).to_owned()); - mod_items.push(quote! { - #[doc = #pc_w_doc] - #[derive(Clone, Copy, Debug, PartialEq)] - pub enum #pc_w { - #(#[doc = #variants_doc] - #variants_pc),* - } - }); - - let arms = variants.iter().map(|v| { - let pc = &v.pc; - let value = util::unsuffixed_or_bool(v.value, f.width); + let arms = variants.iter().map(|v| { + let pcv = &v.pc; + let value = util::unsuffixed_or_bool(v.value, f.width); - quote! { - #pc_w::#pc => #value - } - }); - - mod_items.push(quote! { - impl #pc_w { - #[allow(missing_docs)] - #[doc(hidden)] - #[inline(always)] - pub fn _bits(&self) -> #fty { - match *self { - #(#arms),* - } - } - } - }); - } + quote! { + #pc::#pcv => #value + } + }); - proxy_items.push(quote! { - /// Writes `variant` to the field + enum_items.push(quote! { + #[allow(missing_docs)] + #[doc(hidden)] #[inline(always)] - pub fn variant(self, variant: #pc_w) -> &'a mut W { - #unsafety { - self.#bits(variant._bits()) + fn _bits(&self) -> #fty { + match *self { + #(#arms),* } } }); for v in &variants { - let pc = &v.pc; - let sc = &v.sc; + let pcv = &v.pc; + let scv = &v.sc; - let doc = util::escape_brackets(util::respace(&v.doc).as_ref()); - if let Some(enum_) = base_pc_w.as_ref() { - proxy_items.push(quote! { + if can_read { + let is_variant = if scv.as_ref().starts_with('_') { + Ident::new(&*format!("is{}", scv)) + } else { + Ident::new(&*format!("is_{}", scv)) + }; + let doc = format!("Checks if the value of the field is `{}`", pcv); + pc_r_impl_items.push(quote! { #[doc = #doc] #[inline(always)] - pub fn #sc(self) -> &'a mut W { - self.variant(#enum_::#pc) + pub fn #is_variant(&self) -> bool { + *self == #pc::#pcv } }); - } else { + } + if can_write { + let doc = util::escape_brackets(util::respace(&v.description).as_ref()); proxy_items.push(quote! { #[doc = #doc] #[inline(always)] - pub fn #sc(self) -> &'a mut W { - self.variant(#pc_w::#pc) + pub fn #scv(self) -> &'a mut _W { + self.variant(#pc::#pcv) } }); } } - } - if width == 1 { - proxy_items.push(quote! { - /// Sets the field bit - pub #unsafety fn set_bit(self) -> &'a mut W { - self.bit(true) + mod_items.push(quote! { + impl crate::ToBits<#fty> for #pc { + #(#enum_items)* } + }); - /// Clears the field bit - pub #unsafety fn clear_bit(self) -> &'a mut W { - self.bit(false) + } + } else { + mod_items.push(quote! { + pub struct #pc; + }); + if can_read { + mod_items.push(quote! { + impl crate::Readable for #pc {} + }); + } + if can_write { + let offset = &f.offset; + mod_items.push(quote! { + impl crate::Writable for #pc {} + impl crate::Offset for #pc { + const OFFSET: u8 = #offset; } }); + if f.width > 1 { + let mask = &f.mask; + mod_items.push(quote! { + impl crate::Mask<#rty> for #pc { + const MASK: #rty = #mask; + } + }); + } } + } - proxy_items.push(quote! { - /// Writes raw bits to the field + if can_read { + let pc_r = &f.pc_r; + let mask = &f.mask; + let offset = &f.offset; + let cast = if f.width == 1 { + quote! { != 0 } + } else { + quote! { as #fty } + }; + let value = quote! { + ((self.bits() >> #offset) & #mask) #cast + }; + + mod_items.push(quote! { + ///Reader of the field + pub type #pc_r = crate::R<#fty, #pc>; + }); + + let description = &util::escape_brackets(&f.description); + let pc_r = &f.pc_r; + let sc = &f.sc; + r_impl_items.push(quote! { + #[doc = #description] #[inline(always)] - pub #unsafety fn #bits(self, value: #fty) -> &'a mut W { - self.w.bits &= !(#mask << #offset); - self.w.bits |= ((value as #rty) & #mask) << #offset; - self.w + pub fn #sc(&self) -> #pc_r { + #pc_r::new( #value ) } }); + if !pc_r_impl_items.is_empty() { + mod_items.push(quote! { + impl #pc_r { + #(#pc_r_impl_items)* + } + }); + } + } + + if can_write { let _pc_w = &f._pc_w; mod_items.push(quote! { - /// Proxy - pub struct #_pc_w<'a> { - w: &'a mut W, - } + ///Proxy + pub type #_pc_w<'a> = crate::WProxy<'a, #rty, super::#name_pc, #fty, #pc, crate::#unsafety>; - impl<'a> #_pc_w<'a> { - #(#proxy_items)* - } }); + if !proxy_items.is_empty() { + mod_items.push(quote! { + impl<'a> #_pc_w<'a> { + #(#proxy_items)* + } + }); + } + let description = &util::escape_brackets(&f.description); let sc = &f.sc; w_impl_items.push(quote! { #[doc = #description] #[inline(always)] pub fn #sc(&mut self) -> #_pc_w { - #_pc_w { w: self } + #_pc_w::new( self ) } }) } @@ -789,23 +491,23 @@ pub fn fields( Ok(()) } -fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> Option { - match &write_constraint { +fn unsafety(write_constraint: Option<&WriteConstraint>, 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 - None + "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 - None + "Safe" } - _ => Some(Ident::new("unsafe")), - } + _ => "Unsafe", + }) } #[derive(Clone, Debug)] @@ -822,7 +524,6 @@ fn lookup<'a>( all_registers: &'a [&'a Register], peripheral: &'a Peripheral, all_peripherals: &'a [Peripheral], - usage: Usage, ) -> Result>)>> { let evs = evs.iter() .map(|evs| { @@ -864,12 +565,6 @@ fn lookup<'a>( }) .collect::>>()?; - for (evs, base) in evs.iter() { - if evs.usage == Some(usage) { - return Ok(Some((*evs, base.clone()))); - } - } - Ok(evs.first().cloned()) } diff --git a/src/util.rs b/src/util.rs index 4eaafbf1..e78e7de1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -231,16 +231,6 @@ pub fn hex(n: u32) -> Tokens { t } -pub fn hex_or_bool(n: u32, width: u32) -> Tokens { - if width == 1 { - let mut t = Tokens::new(); - t.append(if n == 0 { "false" } else { "true" }); - t - } else { - hex(n) - } -} - /// Turns `n` into an unsuffixed token pub fn unsuffixed(n: u64) -> Tokens { let mut t = Tokens::new();