Skip to content

Commit

Permalink
field::set with range assert
Browse files Browse the repository at this point in the history
  • Loading branch information
burrbull committed Apr 16, 2024
1 parent 08dd33e commit dc9fbbf
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
57 changes: 57 additions & 0 deletions src/generate/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,9 @@ impl<FI> BitReader<FI> {
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<const MIN: u64, const MAX: u64>;
pub struct RangeFrom<const MIN: u64>;
pub struct RangeTo<const MAX: u64>;

/// Write field Proxy
pub type FieldWriter<'a, REG, const WI: u8, FI = u8, Safety = Unsafe> = raw::FieldWriter<'a, REG, WI, FI, Safety>;
Expand Down Expand Up @@ -533,6 +536,60 @@ where
}
}

impl<'a, REG, const WI: u8, FI, const MIN: u64, const MAX: u64> FieldWriter<'a, REG, WI, FI, Range<MIN, MAX>>
where
REG: Writable + RegisterSpec,
FI: FieldSpec,
REG::Ux: From<FI::Ux>,
u64: From<FI::Ux>,
{
/// Writes raw bits to the field
#[inline(always)]
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
{
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<MIN>>
where
REG: Writable + RegisterSpec,
FI: FieldSpec,
REG::Ux: From<FI::Ux>,
u64: From<FI::Ux>,
{
/// Writes raw bits to the field
#[inline(always)]
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
{
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<MAX>>
where
REG: Writable + RegisterSpec,
FI: FieldSpec,
REG::Ux: From<FI::Ux>,
u64: From<FI::Ux>,
{
/// Writes raw bits to the field
#[inline(always)]
pub fn set(self, value: FI::Ux) -> &'a mut W<REG> {
{
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,
Expand Down
49 changes: 28 additions & 21 deletions src/generate/register.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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",);

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -1371,9 +1369,10 @@ pub fn fields(
))
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Safety {
Unsafe,
Range(WriteConstraintRange),
Safe,
}

Expand All @@ -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>)
}
}
}
}
}

Expand Down

0 comments on commit dc9fbbf

Please sign in to comment.