use crate::svd::{
Access, BitRange, EnumeratedValues, Field, MaybeArray, ModifiedWriteValues, ReadAction,
Register, RegisterProperties, Usage, WriteConstraint,
};
use core::u64;
use log::warn;
use proc_macro2::{Ident, Punct, Spacing, Span, TokenStream};
use quote::{quote, ToTokens};
use std::borrow::Cow;
use std::collections::HashSet;
use svd_parser::expand::{
derive_enumerated_values, derive_field, BlockPath, EnumPath, FieldPath, Index, RegisterPath,
};
use crate::util::{
self, ident_to_path, path_segment, replace_suffix, type_path, Config, FullName,
ToSanitizedCase, U32Ext,
};
use anyhow::{anyhow, Result};
use syn::punctuated::Punctuated;
pub fn render(
register: &Register,
path: &BlockPath,
dpath: Option<RegisterPath>,
index: &Index,
config: &Config,
) -> Result<TokenStream> {
let mut name = util::name_of(register, config.ignore_groups);
if dpath.is_some() {
if let MaybeArray::Array(info, array_info) = register {
if let Some(dim_index) = &array_info.dim_index {
let index: Cow<str> = dim_index.first().unwrap().into();
name = replace_suffix(&info.fullname(config.ignore_groups), &index).into()
}
}
}
let span = Span::call_site();
let name_constant_case = name.to_constant_case_ident(span);
let name_snake_case = name.to_snake_case_ident(span);
let description = util::escape_brackets(
util::respace(®ister.description.clone().unwrap_or_else(|| {
warn!("Missing description for register {}", register.name);
Default::default()
}))
.as_ref(),
);
if let Some(dpath) = dpath.as_ref() {
let mut derived = if &dpath.block == path {
type_path(Punctuated::new())
} else {
util::block_path_to_ty(&dpath.block, span)
};
let dname = util::name_of(index.registers.get(dpath).unwrap(), config.ignore_groups);
let mut mod_derived = derived.clone();
derived
.path
.segments
.push(path_segment(dname.to_constant_case_ident(span)));
mod_derived
.path
.segments
.push(path_segment(dname.to_snake_case_ident(span)));
Ok(quote! {
pub use #derived as #name_constant_case;
pub use #mod_derived as #name_snake_case;
})
} else {
let regspec_ident = format!("{name}_SPEC").to_constant_case_ident(span);
let access = util::access_of(®ister.properties, register.fields.as_deref());
let accs = if access.can_read() && access.can_write() {
"rw"
} else if access.can_write() {
"w"
} else if access.can_read() {
"r"
} else {
return Err(anyhow!("Incorrect access of register {}", register.name));
};
let alias_doc =
format!("{name} ({accs}) register accessor: an alias for `Reg<{regspec_ident}>`");
let mut out = TokenStream::new();
out.extend(quote! {
#[doc = #alias_doc]
pub type #name_constant_case = crate::Reg<#name_snake_case::#regspec_ident>;
});
let mod_items = render_register_mod(
register,
access,
&path.new_register(®ister.name),
index,
config,
)?;
out.extend(quote! {
#[doc = #description]
pub mod #name_snake_case {
#mod_items
}
});
Ok(out)
}
}
pub fn render_register_mod(
register: &Register,
access: Access,
path: &RegisterPath,
index: &Index,
config: &Config,
) -> Result<TokenStream> {
let properties = ®ister.properties;
let name = util::name_of(register, config.ignore_groups);
let span = Span::call_site();
let regspec_ident = format!("{name}_SPEC").to_constant_case_ident(span);
let name_snake_case = name.to_snake_case_ident(span);
let rsize = properties
.size
.ok_or_else(|| anyhow!("Register {} has no `size` field", register.name))?;
let rsize = if rsize < 8 {
8
} else if rsize.is_power_of_two() {
rsize
} else {
rsize.next_power_of_two()
};
let rty = rsize.to_ty()?;
let description = util::escape_brackets(
util::respace(®ister.description.clone().unwrap_or_else(|| {
warn!("Missing description for register {}", register.name);
Default::default()
}))
.as_ref(),
);
let mut mod_items = TokenStream::new();
let mut methods = vec![];
let can_read = access.can_read();
let can_write = access.can_write();
let can_reset = properties.reset_value.is_some();
if can_read {
let desc = format!("Register `{}` reader", register.name);
let derive = if config.derive_more {
Some(quote! { #[derive(derive_more::Deref, derive_more::From)] })
} else {
None
};
mod_items.extend(quote! {
#[doc = #desc]
#derive
pub struct R(crate::R<#regspec_ident>);
});
if !config.derive_more {
mod_items.extend(quote! {
impl core::ops::Deref for R {
type Target = crate::R<#regspec_ident>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<crate::R<#regspec_ident>> for R {
#[inline(always)]
fn from(reader: crate::R<#regspec_ident>) -> Self {
R(reader)
}
}
});
}
methods.push("read");
}
if can_write {
let desc = format!("Register `{}` writer", register.name);
let derive = if config.derive_more {
Some(quote! { #[derive(derive_more::Deref, derive_more::DerefMut, derive_more::From)] })
} else {
None
};
mod_items.extend(quote! {
#[doc = #desc]
#derive
pub struct W(crate::W<#regspec_ident>);
});
if !config.derive_more {
mod_items.extend(quote! {
impl core::ops::Deref for W {
type Target = crate::W<#regspec_ident>;
#[inline(always)]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl core::ops::DerefMut for W {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<crate::W<#regspec_ident>> for W {
#[inline(always)]
fn from(writer: crate::W<#regspec_ident>) -> Self {
W(writer)
}
}
});
}
methods.push("write_with_zero");
if can_reset {
methods.push("reset");
methods.push("write");
}
}
if can_read && can_write {
methods.push("modify");
}
let mut r_impl_items = TokenStream::new();
let mut w_impl_items = TokenStream::new();
let mut zero_to_modify_fields_bitmap = 0;
let mut one_to_modify_fields_bitmap = 0;
if let Some(cur_fields) = register.fields.as_ref() {
let cur_fields: Vec<&Field> = cur_fields
.iter()
.filter(|field| field.name.to_lowercase() != "reserved")
.collect();
if !cur_fields.is_empty() {
(
r_impl_items,
w_impl_items,
zero_to_modify_fields_bitmap,
one_to_modify_fields_bitmap,
) = fields(
cur_fields,
®spec_ident,
&rty,
register.modified_write_values,
access,
properties,
&mut mod_items,
path,
index,
config,
)?;
}
}
let open = Punct::new('{', Spacing::Alone);
let close = Punct::new('}', Spacing::Alone);
if can_read && !r_impl_items.is_empty() {
mod_items.extend(quote! {
impl R #open #r_impl_items #close
});
}
if can_write {
mod_items.extend(quote! {
impl W #open
});
mod_items.extend(w_impl_items);
let can_write_safe = !unsafety(
register
.fields
.as_ref()
.and_then(|fields| fields.first())
.and_then(|field| field.write_constraint)
.as_ref(),
rsize,
) || !unsafety(register.write_constraint.as_ref(), rsize);
if can_write_safe {
mod_items.extend(quote! {
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub fn bits(&mut self, bits: #rty) -> &mut Self {
unsafe { self.0.bits(bits) };
self
}
});
} else {
mod_items.extend(quote! {
#[doc = "Writes raw bits to the register."]
#[inline(always)]
pub unsafe fn bits(&mut self, bits: #rty) -> &mut Self {
self.0.bits(bits);
self
}
});
}
close.to_tokens(&mut mod_items);
}
let methods = methods
.iter()
.map(|s| format!("[`{0}`](crate::generic::Reg::{0})", s))
.collect::<Vec<_>>();
let mut doc = format!("{description}\n\nThis register you can {}. See [API](https://docs.rs/svd2rust/#read--modify--write-api).", methods.join(", "));
if name_snake_case != "cfg" {
doc += format!(
"\n\nFor information about available fields see [{name_snake_case}](index.html) module"
)
.as_str();
}
if can_read {
if let Some(action) = register.read_action {
doc += match action {
ReadAction::Clear => "\n\nThe register is **cleared** (set to zero) following a read operation.",
ReadAction::Set => "\n\nThe register is **set** (set to ones) following a read operation.",
ReadAction::Modify => "\n\nThe register is **modified** in some way after a read operation.",
ReadAction::ModifyExternal => "\n\nOne or more dependent resources other than the current register are immediately affected by a read operation.",
};
}
}
mod_items.extend(quote! {
#[doc = #doc]
pub struct #regspec_ident;
impl crate::RegisterSpec for #regspec_ident {
type Ux = #rty;
}
});
if can_read {
let doc = format!("`read()` method returns [{name_snake_case}::R](R) reader structure",);
mod_items.extend(quote! {
#[doc = #doc]
impl crate::Readable for #regspec_ident {
type Reader = R;
}
});
}
if can_write {
let doc =
format!("`write(|w| ..)` method takes [{name_snake_case}::W](W) writer structure",);
let zero_to_modify_fields_bitmap = util::hex(zero_to_modify_fields_bitmap);
let one_to_modify_fields_bitmap = util::hex(one_to_modify_fields_bitmap);
mod_items.extend(quote! {
#[doc = #doc]
impl crate::Writable for #regspec_ident {
type Writer = W;
const ZERO_TO_MODIFY_FIELDS_BITMAP: Self::Ux = #zero_to_modify_fields_bitmap;
const ONE_TO_MODIFY_FIELDS_BITMAP: Self::Ux = #one_to_modify_fields_bitmap;
}
});
}
if let Some(rv) = properties.reset_value.map(util::hex) {
let doc = format!("`reset()` method sets {} to value {rv}", register.name);
mod_items.extend(quote! {
#[doc = #doc]
impl crate::Resettable for #regspec_ident {
const RESET_VALUE: Self::Ux = #rv;
}
});
}
Ok(mod_items)
}
#[allow(clippy::too_many_arguments)]
pub fn fields(
mut fields: Vec<&Field>,
regspec_ident: &Ident,
rty: &Ident,
rmwv: Option<ModifiedWriteValues>,
access: Access,
properties: &RegisterProperties,
mod_items: &mut TokenStream,
rpath: &RegisterPath,
index: &Index,
config: &Config,
) -> Result<(TokenStream, TokenStream, u64, u64)> {
let mut r_impl_items = TokenStream::new();
let mut w_impl_items = TokenStream::new();
let mut zero_to_modify_fields_bitmap = 0u64;
let mut one_to_modify_fields_bitmap = 0u64;
let span = Span::call_site();
let can_read = access.can_read();
let can_write = access.can_write();
fields.sort_by_key(|f| f.bit_offset());
let mut enum_derives = HashSet::new();
let mut reader_derives = HashSet::new();
let mut writer_enum_derives = HashSet::new();
let mut writer_derives = HashSet::new();
let inline = quote! { #[inline(always)] };
for &f in fields.iter() {
let mut f = f.clone();
let mut fpath = None;
let dpath = f.derived_from.take();
if let Some(dpath) = dpath {
fpath = derive_field(&mut f, &dpath, rpath, index)?;
}
let fpath = fpath.unwrap_or_else(|| rpath.new_field(&f.name));
let BitRange { offset, width, .. } = f.bit_range;
if f.is_single() && f.name.contains("%s") {
return Err(anyhow!("incorrect field {}", f.name));
}
let name = util::replace_suffix(&f.name, "");
let name_snake_case = name.to_snake_case_ident(span);
let name_constant_case = name.to_sanitized_constant_case();
let description_raw = f.description.as_deref().unwrap_or(""); let description = util::respace(&util::escape_brackets(description_raw));
let can_read = can_read
&& (f.access != Some(Access::WriteOnly))
&& (f.access != Some(Access::WriteOnce));
let can_write = can_write && (f.access != Some(Access::ReadOnly));
let mask = u64::MAX >> (64 - width);
let hexmask = &util::digit_or_hex(mask);
let offset = u64::from(offset);
let rv = properties.reset_value.map(|rv| (rv >> offset) & mask);
let fty = width.to_ty()?;
let use_mask;
let use_cast;
if let Some(size) = properties.size {
let size = size.to_ty_width()?;
use_cast = size != width.to_ty_width()?;
use_mask = size != width;
} else {
use_cast = true;
use_mask = true;
};
let mut lookup_results = Vec::new();
for mut ev in f.enumerated_values.clone().into_iter() {
let mut epath = None;
let dpath = ev.derived_from.take();
if let Some(dpath) = dpath {
epath = Some(derive_enumerated_values(&mut ev, &dpath, &fpath, index)?);
}
if let Some(epath) = epath.as_ref() {
ev = (*index.evs.get(epath).unwrap()).clone();
}
lookup_results.push((ev, epath));
}
let mut evs_r = None;
let brief_suffix = if let Field::Array(_, de) = &f {
if let Some(range) = de.indexes_as_range() {
format!("[{}-{}]", *range.start(), *range.end())
} else {
let suffixes: Vec<_> = de.indexes().collect();
format!("[{}]", suffixes.join(","))
}
} else {
String::new()
};
if can_read {
let cast = if width == 1 {
quote! { != 0 }
} else {
quote! { as #fty }
};
let value = if offset != 0 {
let offset = &util::unsuffixed(offset);
quote! { (self.bits >> #offset) }
} else {
quote! { self.bits }
};
let value = if use_mask && use_cast {
quote! { (#value & #hexmask) #cast }
} else if use_mask {
quote! { #value & #hexmask }
} else {
value
};
let field_reader_brief = format!("Field `{name}{brief_suffix}` reader - {description}");
let value_read_ty = if let Some((evs, _)) = lookup_filter(&lookup_results, Usage::Read)
{
if let Some(enum_name) = &evs.name {
format!("{enum_name}_A").to_constant_case_ident(span)
} else {
Ident::new(&format!("{name_constant_case}_A"), span)
}
} else {
fty.clone()
};
let reader_ty = Ident::new(&(name_constant_case.clone() + "_R"), span);
let should_derive_reader = match lookup_filter(&lookup_results, Usage::Read) {
Some((_evs, Some(_base))) => false,
Some((_evs, None)) => true,
None => true,
};
if should_derive_reader {
let reader = if width == 1 {
quote! { crate::BitReader<#value_read_ty> }
} else {
quote! { crate::FieldReader<#fty, #value_read_ty> }
};
let mut readerdoc = field_reader_brief.clone();
if let Some(action) = f.read_action {
readerdoc += match action {
ReadAction::Clear => "\n\nThe field is **cleared** (set to zero) following a read operation.",
ReadAction::Set => "\n\nThe field is **set** (set to ones) following a read operation.",
ReadAction::Modify => "\n\nThe field is **modified** in some way after a read operation.",
ReadAction::ModifyExternal => "\n\nOne or more dependent resources other than the current field are immediately affected by a read operation.",
};
}
mod_items.extend(quote! {
#[doc = #readerdoc]
pub type #reader_ty = #reader;
});
}
let mut enum_items = TokenStream::new();
if let Some((evs, None)) = lookup_filter(&lookup_results, Usage::Read) {
evs_r = Some(evs);
let has_reserved_variant = evs.values.len() != (1 << width);
let variants = Variant::from_enumerated_values(evs, config.pascal_enum_values)?;
if variants.is_empty() {
add_with_no_variants(mod_items, &value_read_ty, &fty, &description, rv);
} else {
add_from_variants(mod_items, &variants, &value_read_ty, &fty, &description, rv);
let mut arms = TokenStream::new();
for v in variants.iter().map(|v| {
let i = util::unsuffixed_or_bool(v.value, width);
let pc = &v.pc;
if has_reserved_variant {
quote! { #i => Some(#value_read_ty::#pc), }
} else {
quote! { #i => #value_read_ty::#pc, }
}
}) {
arms.extend(v);
}
if has_reserved_variant {
arms.extend(quote! {
_ => None,
});
} else if 1 << width.to_ty_width()? != variants.len() {
arms.extend(quote! {
_ => unreachable!(),
});
}
if has_reserved_variant {
enum_items.extend(quote! {
#[doc = "Get enumerated values variant"]
#inline
pub fn variant(&self) -> Option<#value_read_ty> {
match self.bits {
#arms
}
}
});
} else {
enum_items.extend(quote! {
#[doc = "Get enumerated values variant"]
#inline
pub fn variant(&self) -> #value_read_ty {
match self.bits {
#arms
}
}});
}
for v in &variants {
let pc = &v.pc;
let sc = &v.nksc;
let is_variant = Ident::new(
&if sc.to_string().starts_with('_') {
format!("is{sc}")
} else {
format!("is_{sc}")
},
span,
);
let doc = format!("Checks if the value of the field is `{pc}`");
enum_items.extend(quote! {
#[doc = #doc]
#inline
pub fn #is_variant(&self) -> bool {
*self == #value_read_ty::#pc
}
});
}
}
}
if let Some((evs, Some(base))) = lookup_filter(&lookup_results, Usage::Read) {
evs_r = Some(evs);
let base_field = util::replace_suffix(&base.field.name, "");
let base_r = (base_field + "_R").to_constant_case_ident(span);
if !reader_derives.contains(&reader_ty) {
let base_path = base_syn_path(base, &fpath, &base_r)?;
mod_items.extend(quote! {
#[doc = #field_reader_brief]
pub use #base_path as #reader_ty;
});
reader_derives.insert(reader_ty.clone());
}
if base.register() != fpath.register() {
if !enum_derives.contains(&value_read_ty) {
let base_path = base_syn_path(base, &fpath, &value_read_ty)?;
mod_items.extend(quote! {
#[doc = #description]
pub use #base_path as #value_read_ty;
});
enum_derives.insert(value_read_ty.clone());
}
}
}
if let Field::Array(_, de) = &f {
let increment = de.dim_increment;
let doc = &util::replace_suffix(&description, &brief_suffix);
if let Some(range) = de.indexes_as_range() {
let first = *range.start();
let offset_calc = calculate_offset(first, increment, offset, true);
let value = quote! { ((self.bits >> #offset_calc) & #hexmask) #cast };
r_impl_items.extend(quote! {
#[doc = #doc]
#inline
pub unsafe fn #name_snake_case(&self, n: u8) -> #reader_ty {
#reader_ty::new ( #value )
}
});
}
for (i, suffix) in de.indexes().enumerate() {
let sub_offset = offset + (i as u64) * (increment as u64);
let value = if sub_offset != 0 {
let sub_offset = &util::unsuffixed(sub_offset);
quote! { (self.bits >> #sub_offset) }
} else {
quote! { self.bits }
};
let value = if use_mask && use_cast {
quote! { (#value & #hexmask) #cast }
} else if use_mask {
quote! { #value & #hexmask }
} else {
value
};
let name_snake_case_n = util::replace_suffix(&f.name, &suffix)
.to_snake_case_ident(Span::call_site());
let doc = util::replace_suffix(
&description_with_bits(description_raw, sub_offset, width),
&suffix,
);
r_impl_items.extend(quote! {
#[doc = #doc]
#inline
pub fn #name_snake_case_n(&self) -> #reader_ty {
#reader_ty::new ( #value )
}
});
}
} else {
let doc = description_with_bits(description_raw, offset, width);
r_impl_items.extend(quote! {
#[doc = #doc]
#inline
pub fn #name_snake_case(&self) -> #reader_ty {
#reader_ty::new ( #value )
}
});
}
if !enum_items.is_empty() {
mod_items.extend(quote! {
impl #reader_ty {
#enum_items
}
});
}
}
if can_write {
let mwv = f.modified_write_values.or(rmwv).unwrap_or_default();
let field_writer_brief = format!("Field `{name}{brief_suffix}` writer - {description}");
let value_write_ty =
if let Some((evs, _)) = lookup_filter(&lookup_results, Usage::Write) {
let writer_reader_different_enum = evs_r != Some(evs);
let ty_suffix = if writer_reader_different_enum {
"AW"
} else {
"A"
};
if let Some(enum_name) = &evs.name {
format!("{enum_name}_{ty_suffix}").to_constant_case_ident(span)
} else {
Ident::new(&format!("{name_constant_case}_{ty_suffix}"), span)
}
} else {
fty.clone()
};
let writer_ty = Ident::new(&(name_constant_case.clone() + "_W"), span);
let mut proxy_items = TokenStream::new();
let mut unsafety = unsafety(f.write_constraint.as_ref(), width);
if let Some((evs, None)) = lookup_filter(&lookup_results, Usage::Write) {
let variants = Variant::from_enumerated_values(evs, config.pascal_enum_values)?;
if variants.len() == 1 << width {
unsafety = false;
}
let writer_reader_different_enum = evs_r != Some(evs);
if writer_reader_different_enum {
if variants.is_empty() {
add_with_no_variants(mod_items, &value_write_ty, &fty, &description, rv);
} else {
add_from_variants(
mod_items,
&variants,
&value_write_ty,
&fty,
&description,
rv,
);
}
}
for v in &variants {
let pc = &v.pc;
let sc = &v.sc;
let doc = util::escape_brackets(&util::respace(&v.doc));
proxy_items.extend(quote! {
#[doc = #doc]
#inline
pub fn #sc(self) -> &'a mut W {
self.variant(#value_write_ty::#pc)
}
});
}
}
let should_derive_writer = match lookup_filter(&lookup_results, Usage::Write) {
Some((_evs, Some(base))) => base.register() != fpath.register(),
Some((_evs, None)) => true,
None => true,
};
if should_derive_writer {
let proxy = if width == 1 {
use ModifiedWriteValues::*;
let wproxy = Ident::new(
match mwv {
Modify | Set | Clear => "BitWriter",
OneToSet => "BitWriter1S",
ZeroToClear => "BitWriter0C",
OneToClear => "BitWriter1C",
ZeroToSet => "BitWriter0C",
OneToToggle => "BitWriter1T",
ZeroToToggle => "BitWriter0T",
},
span,
);
quote! { crate::#wproxy<'a, #rty, #regspec_ident, #value_write_ty, O> }
} else {
let wproxy = Ident::new(
if unsafety {
"FieldWriter"
} else {
"FieldWriterSafe"
},
span,
);
let width = &util::unsuffixed(width as _);
quote! { crate::#wproxy<'a, #rty, #regspec_ident, #fty, #value_write_ty, #width, O> }
};
mod_items.extend(quote! {
#[doc = #field_writer_brief]
pub type #writer_ty<'a, const O: u8> = #proxy;
});
}
if !proxy_items.is_empty() {
mod_items.extend(quote! {
impl<'a, const O: u8> #writer_ty<'a, O> {
#proxy_items
}
});
}
if let Some((evs, Some(base))) = lookup_filter(&lookup_results, Usage::Write) {
if base.register() != fpath.register() {
let writer_reader_different_enum = evs_r != Some(evs);
if writer_reader_different_enum {
if !writer_enum_derives.contains(&value_write_ty) {
let base_path = base_syn_path(base, &fpath, &value_write_ty)?;
mod_items.extend(quote! {
#[doc = #description]
pub use #base_path as #value_write_ty;
});
writer_enum_derives.insert(value_write_ty.clone());
}
}
} else {
let base_field = util::replace_suffix(&base.field.name, "");
let base_w = (base_field + "_W").to_constant_case_ident(span);
if !writer_derives.contains(&writer_ty) {
let base_path = base_syn_path(base, &fpath, &base_w)?;
mod_items.extend(quote! {
#[doc = #field_writer_brief]
pub use #base_path as #writer_ty;
});
writer_derives.insert(writer_ty.clone());
}
}
}
if let Field::Array(_, de) = &f {
let increment = de.dim_increment;
let doc = &util::replace_suffix(&description, &brief_suffix);
w_impl_items.extend(quote! {
#[doc = #doc]
#inline
#[must_use]
pub unsafe fn #name_snake_case<const O: u8>(&mut self) -> #writer_ty<O> {
#writer_ty::new(self)
}
});
for (i, suffix) in de.indexes().enumerate() {
let sub_offset = offset + (i as u64) * (increment as u64);
let name_snake_case_n = &util::replace_suffix(&f.name, &suffix)
.to_snake_case_ident(Span::call_site());
let doc = util::replace_suffix(
&description_with_bits(description_raw, sub_offset, width),
&suffix,
);
let sub_offset = util::unsuffixed(sub_offset);
w_impl_items.extend(quote! {
#[doc = #doc]
#inline
#[must_use]
pub fn #name_snake_case_n(&mut self) -> #writer_ty<#sub_offset> {
#writer_ty::new(self)
}
});
}
} else {
let doc = description_with_bits(description_raw, offset, width);
let offset = util::unsuffixed(offset);
w_impl_items.extend(quote! {
#[doc = #doc]
#inline
#[must_use]
pub fn #name_snake_case(&mut self) -> #writer_ty<#offset> {
#writer_ty::new(self)
}
});
}
let bitmask = (u64::MAX >> (64 - width)) << offset;
use ModifiedWriteValues::*;
match mwv {
Modify | Set | Clear => {}
OneToSet | OneToClear | OneToToggle => {
one_to_modify_fields_bitmap |= bitmask;
}
ZeroToClear | ZeroToSet | ZeroToToggle => {
zero_to_modify_fields_bitmap |= bitmask;
}
}
}
}
Ok((
r_impl_items,
w_impl_items,
zero_to_modify_fields_bitmap,
one_to_modify_fields_bitmap,
))
}
fn unsafety(write_constraint: Option<&WriteConstraint>, width: u32) -> bool {
match &write_constraint {
Some(&WriteConstraint::Range(range))
if range.min == 0 && range.max == u64::MAX >> (64 - width) =>
{
false
}
None if width == 1 => {
false
}
_ => true,
}
}
struct Variant {
doc: String,
pc: Ident,
nksc: Ident,
sc: Ident,
value: u64,
}
impl Variant {
fn from_enumerated_values(evs: &EnumeratedValues, pc: bool) -> Result<Vec<Self>> {
let span = Span::call_site();
evs.values
.iter()
.filter(|field| field.name.to_lowercase() != "reserved" && field.is_default.is_none())
.map(|ev| {
let value = ev
.value
.ok_or_else(|| anyhow!("EnumeratedValue {} has no `<value>` field", ev.name))?;
let nksc = ev.name.to_sanitized_not_keyword_snake_case();
let sc = util::sanitize_keyword(nksc.clone());
Ok(Variant {
doc: ev
.description
.clone()
.unwrap_or_else(|| format!("`{value:b}`")),
pc: if pc {
ev.name.to_pascal_case_ident(span)
} else {
ev.name.to_constant_case_ident(span)
},
nksc: Ident::new(&nksc, span),
sc: Ident::new(&sc, span),
value,
})
})
.collect::<Result<Vec<_>>>()
}
}
fn add_with_no_variants(
mod_items: &mut TokenStream,
pc: &Ident,
fty: &Ident,
desc: &str,
reset_value: Option<u64>,
) {
let cast = if fty == "bool" {
quote! { val.0 as u8 != 0 }
} else {
quote! { val.0 as _ }
};
let desc = if let Some(rv) = reset_value {
format!("{desc}\n\nValue on reset: {rv}")
} else {
desc.to_string()
};
mod_items.extend(quote! {
#[doc = #desc]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct #pc(#fty);
impl From<#pc> for #fty {
#[inline(always)]
fn from(val: #pc) -> Self {
#cast
}
}
});
}
fn add_from_variants(
mod_items: &mut TokenStream,
variants: &[Variant],
pc: &Ident,
fty: &Ident,
desc: &str,
reset_value: Option<u64>,
) {
let (repr, cast) = if fty == "bool" {
(quote! {}, quote! { variant as u8 != 0 })
} else {
(quote! { #[repr(#fty)] }, quote! { variant as _ })
};
let mut vars = TokenStream::new();
for v in variants.iter().map(|v| {
let desc = util::escape_brackets(&util::respace(&format!("{}: {}", v.value, v.doc)));
let pcv = &v.pc;
let pcval = &util::unsuffixed(v.value);
quote! {
#[doc = #desc]
#pcv = #pcval,
}
}) {
vars.extend(v);
}
let desc = if let Some(rv) = reset_value {
format!("{desc}\n\nValue on reset: {rv}")
} else {
desc.to_string()
};
mod_items.extend(quote! {
#[doc = #desc]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#repr
pub enum #pc {
#vars
}
impl From<#pc> for #fty {
#[inline(always)]
fn from(variant: #pc) -> Self {
#cast
}
}
});
}
fn calculate_offset(
first: u32,
increment: u32,
offset: u64,
with_parentheses: bool,
) -> TokenStream {
let mut res = if first != 0 {
let first = util::unsuffixed(first as u64);
quote! { n - #first }
} else {
quote! { n }
};
if increment != 1 {
let increment = util::unsuffixed(increment as u64);
res = if first != 0 {
quote! { (#res) * #increment }
} else {
quote! { #res * #increment }
};
}
if offset != 0 {
let offset = &util::unsuffixed(offset);
res = quote! { #res + #offset };
}
let single_ident = (first == 0) && (increment == 1) && (offset == 0);
if with_parentheses && !single_ident {
quote! { (#res) }
} else {
res
}
}
fn description_with_bits(description: &str, offset: u64, width: u32) -> String {
let mut res = if width == 1 {
format!("Bit {offset}")
} else {
format!("Bits {offset}:{}", offset + width as u64 - 1)
};
if !description.is_empty() {
res.push_str(" - ");
res.push_str(&util::respace(&util::escape_brackets(description)));
}
res
}
fn base_syn_path(
base: &EnumPath,
fpath: &FieldPath,
base_ident: &Ident,
) -> Result<syn::TypePath, syn::Error> {
let span = Span::call_site();
let path = if base.register() == fpath.register() {
ident_to_path(base_ident.clone())
} else if base.register().block == fpath.register().block {
let mut segments = Punctuated::new();
segments.push(path_segment(Ident::new("super", span)));
segments.push(path_segment(base.register().name.to_snake_case_ident(span)));
segments.push(path_segment(base_ident.clone()));
type_path(segments)
} else {
let mut rmod_ = crate::util::register_path_to_ty(base.register(), span);
rmod_.path.segments.push(path_segment(base_ident.clone()));
rmod_
};
Ok(path)
}
fn lookup_filter(
evs: &[(EnumeratedValues, Option<EnumPath>)],
usage: Usage,
) -> Option<&(EnumeratedValues, Option<EnumPath>)> {
evs.iter()
.find(|evsbase| evsbase.0.usage == Some(usage))
.or_else(|| evs.first())
}