diff --git a/CHANGELOG.md b/CHANGELOG.md index bbeae62e..953772ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +- Add checked `set` for not full safe fields + ## [v0.33.0] - 2024-03-26 - Add `IsEnum` constraint for `FieldWriter`s (fix `variant` safety) diff --git a/src/generate/generic.rs b/src/generate/generic.rs index 563065eb..ed23dc8e 100644 --- a/src/generate/generic.rs +++ b/src/generate/generic.rs @@ -476,6 +476,9 @@ impl BitReader { pub struct Safe; /// You should check that value is allowed to pass to register/field writer marked with this pub struct Unsafe; +pub struct Range; +pub struct RangeFrom; +pub struct RangeTo; /// Write field Proxy pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> = raw::FieldWriter<'a, REG, WI, FI, Safety>; @@ -533,6 +536,60 @@ where } } +impl<'a, REG, const WI: u8, FI, const MIN: u64, const MAX: u64> FieldWriter<'a, REG, WI, FI, Range> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, +{ + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN && value <= MAX); + } + unsafe { self.bits(value) } + } +} + +impl<'a, REG, const WI: u8, FI, const MIN: u64> FieldWriter<'a, REG, WI, FI, RangeFrom> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, +{ + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value >= MIN); + } + unsafe { self.bits(value) } + } +} + +impl<'a, REG, const WI: u8, FI, const MAX: u64> FieldWriter<'a, REG, WI, FI, RangeTo> +where + REG: Writable + RegisterSpec, + FI: FieldSpec, + REG::Ux: From, + u64: From, +{ + /// Writes raw bits to the field + #[inline(always)] + pub fn set(self, value: FI::Ux) -> &'a mut W { + { + let value = u64::from(value); + assert!(value <= MAX); + } + unsafe { self.bits(value) } + } +} + impl<'a, REG, const WI: u8, FI, Safety> FieldWriter<'a, REG, WI, FI, Safety> where REG: Writable + RegisterSpec, diff --git a/src/generate/register.rs b/src/generate/register.rs index 614d3c21..5305d7a8 100644 --- a/src/generate/register.rs +++ b/src/generate/register.rs @@ -1,6 +1,7 @@ use crate::svd::{ self, Access, BitRange, DimElement, EnumeratedValue, EnumeratedValues, Field, MaybeArray, ModifiedWriteValues, ReadAction, Register, RegisterProperties, Usage, WriteConstraint, + WriteConstraintRange, }; use core::u64; use log::warn; @@ -408,7 +409,7 @@ pub fn render_register_mod( } else { Safety::Unsafe }; - let safe_ty = safe_ty.ident(span); + let safe_ty = safe_ty.ident(rsize); let doc = format!("`write(|w| ..)` method takes [`{mod_ty}::W`](W) writer structure",); @@ -1138,17 +1139,14 @@ pub fn fields( .map(|v| Variant::from_value(v, def, config)) }) .transpose()?; + // if the write structure is finite, it can be safely written. if variants.len() == 1 << width { + safety = Safety::Safe; } else if let Some(def) = def.take() { variants.push(def); safety = Safety::Safe; } - // if the write structure is finite, it can be safely written. - if variants.len() == 1 << width { - safety = Safety::Safe; - } - // generate write value structure and From conversation if we can't reuse read value structure. if rwenum.generate_write_enum() { if variants.is_empty() { @@ -1241,14 +1239,14 @@ pub fn fields( } } else { let wproxy = Ident::new("FieldWriter", span); - let width = &unsuffixed(width); + let uwidth = &unsuffixed(width); if value_write_ty == "u8" && safety != Safety::Safe { - quote! { crate::#wproxy<'a, REG, #width> } + quote! { crate::#wproxy<'a, REG, #uwidth> } } else if safety != Safety::Safe { - quote! { crate::#wproxy<'a, REG, #width, #value_write_ty> } + quote! { crate::#wproxy<'a, REG, #uwidth, #value_write_ty> } } else { - let safe_ty = safety.ident(span); - quote! { crate::#wproxy<'a, REG, #width, #value_write_ty, crate::#safe_ty> } + let safe_ty = safety.ident(width); + quote! { crate::#wproxy<'a, REG, #uwidth, #value_write_ty, crate::#safe_ty> } } }; mod_items.extend(quote! { @@ -1371,9 +1369,10 @@ pub fn fields( )) } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Safety { Unsafe, + Range(WriteConstraintRange), Safe, } @@ -1393,18 +1392,26 @@ impl Safety { // if a writeConstraint exists then respect it Self::Safe } + Some(&WriteConstraint::Range(range)) => Self::Range(range), _ => Self::Unsafe, } } - fn ident(&self, span: Span) -> Ident { - Ident::new( - if let Self::Safe = self { - "Safe" - } else { - "Unsafe" - }, - span, - ) + fn ident(&self, width: u32) -> TokenStream { + match self { + Self::Safe => quote!(Safe), + Self::Unsafe => quote!(Unsafe), + Self::Range(range) => { + let min = unsuffixed(range.min); + let max = unsuffixed(range.max); + if range.min == 0 { + quote!(RangeTo<#max>) + } else if range.max == u64::MAX >> (64 - width) { + quote!(RangeFrom<#min>) + } else { + quote!(Range<#min, #max>) + } + } + } } }