Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

- Add `feature_peripheral` option which generates cfg features for each peripheral
- Use register aliases in `RegisterBlock` (both structure and mod)
- Create aliases for derived registers & clusters
- Move cluster struct inside mod
Expand Down
22 changes: 18 additions & 4 deletions src/generate/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,18 +248,21 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<Toke
// in the `Peripherals` struct
continue;
}
let feature_attribute = if config.feature_group && p.group_name.is_some() {
let mut feature_attribute = TokenStream::new();
if config.feature_group && p.group_name.is_some() {
let feature_name = p.group_name.as_ref().unwrap().to_sanitized_snake_case();
quote!(#[cfg(feature = #feature_name)])
} else {
quote!()
feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] })
};

match p {
Peripheral::Single(_p) => {
let p_name = util::name_of(p, config.ignore_groups);
let p_snake = p_name.to_sanitized_snake_case();
let p = p_name.to_sanitized_constant_case();
let id = Ident::new(&p, Span::call_site());
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
fields.extend(quote! {
#[doc = #p]
#feature_attribute
Expand All @@ -272,6 +275,17 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result<Toke
let p = p_names.iter().map(|p| p.to_sanitized_constant_case());
let ids_f = p.clone().map(|p| Ident::new(&p, Span::call_site()));
let ids_e = ids_f.clone();
let feature_attribute = p_names
.iter()
.map(|p_name| {
let p_snake = p_name.to_sanitized_snake_case();
let mut feature_attribute = feature_attribute.clone();
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
feature_attribute
})
.collect::<Vec<_>>();
fields.extend(quote! {
#(
#[doc = #p]
Expand Down
48 changes: 30 additions & 18 deletions src/generate/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt::Write;

use crate::svd::Peripheral;
use cast::u64;
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro2::{Span, TokenStream};
use quote::quote;

use crate::util::{self, ToSanitizedCase};
Expand All @@ -19,8 +19,19 @@ pub fn render(
) -> Result<TokenStream> {
let interrupts = peripherals
.iter()
.flat_map(|p| p.interrupt.iter().map(move |i| (i, p.group_name.clone())))
.map(|i| (i.0.value, (i.0, i.1)))
.flat_map(|p| {
p.interrupt.iter().map(move |i| {
(i, p.group_name.clone(), {
match p {
Peripheral::Single(info) => info.name.clone(),
Peripheral::Array(info, dim_element) => {
svd_rs::array::names(info, dim_element).next().unwrap()
}
}
})
})
})
.map(|i| (i.0.value, (i.0, i.1, i.2)))
.collect::<HashMap<_, _>>();

let mut interrupts = interrupts.into_iter().map(|(_, v)| v).collect::<Vec<_>>();
Expand All @@ -43,10 +54,7 @@ pub fn render(
}
pos += 1;

let name_constant_case = Ident::new(
&interrupt.0.name.to_sanitized_constant_case(),
Span::call_site(),
);
let name_constant_case = interrupt.0.name.to_constant_case_ident(Span::call_site());
let description = format!(
"{} - {}",
interrupt.0.value,
Expand All @@ -63,17 +71,21 @@ pub fn render(
let value = util::unsuffixed(u64(interrupt.0.value));

let mut feature_attribute_flag = false;
let (feature_attribute, not_feature_attribute) =
if config.feature_group && interrupt.1.is_some() {
let feature_name = interrupt.1.as_ref().unwrap().to_sanitized_snake_case();
feature_attribute_flag = true;
(
quote!(#[cfg(feature = #feature_name)]),
quote!(#[cfg(not(feature = #feature_name))]),
)
} else {
(quote!(), quote!())
};
let mut feature_attribute = TokenStream::new();
let mut not_feature_attribute = TokenStream::new();
if config.feature_group && interrupt.1.is_some() {
let feature_name = interrupt.1.as_ref().unwrap().to_sanitized_snake_case();
feature_attribute_flag = true;
feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] });
not_feature_attribute.extend(quote! { feature = #feature_name, });
}
if config.feature_peripheral {
let feature_name = interrupt.2.to_sanitized_snake_case();
feature_attribute_flag = true;
feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] });
not_feature_attribute.extend(quote! { feature = #feature_name, });
}
let not_feature_attribute = quote! { #[cfg(not(all(#not_feature_attribute)))] };

variants.extend(quote! {
#[doc = #description]
Expand Down
73 changes: 50 additions & 23 deletions src/generate/peripheral.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result
(false, name_snake_case.clone(), BlockPath::new(&p.name))
};

let feature_attribute = if config.feature_group && p.group_name.is_some() {
let mut feature_attribute = TokenStream::new();
if config.feature_group && p.group_name.is_some() {
let feature_name = p.group_name.as_ref().unwrap().to_sanitized_snake_case();
quote! (#[cfg(feature = #feature_name)])
} else {
quote! {}
feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] });
};

match &p {
Expand All @@ -54,18 +53,28 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result
let names_constant_case = names_str.clone().map(|n| Ident::new(&n, span));
let addresses =
(0..=dim.dim).map(|i| util::hex(p.base_address + (i * dim.dim_increment) as u64));

let snake_names = names
.iter()
.map(|p_name| p_name.to_sanitized_snake_case())
.collect::<Vec<_>>();
let feature_attribute_n = snake_names.iter().map(|p_snake| {
let mut feature_attribute = feature_attribute.clone();
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
feature_attribute
});
// Insert the peripherals structure
out.extend(quote! {
#(
#[doc = #description]
#feature_attribute
#feature_attribute_n
pub struct #names_constant_case { _marker: PhantomData<*const ()> }

#feature_attribute
#feature_attribute_n
unsafe impl Send for #names_constant_case {}

#feature_attribute
#feature_attribute_n
impl #names_constant_case {
///Pointer to the register block
pub const PTR: *const #base::RegisterBlock = #addresses as *const _;
Expand All @@ -77,7 +86,7 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result
}
}

#feature_attribute
#feature_attribute_n
impl Deref for #names_constant_case {
type Target = #base::RegisterBlock;

Expand All @@ -87,16 +96,34 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result
}
}

#feature_attribute
#feature_attribute_n
impl core::fmt::Debug for #names_constant_case {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct(#names_str).finish()
}
}
)*
});

let feature_any_attribute = quote! {#[cfg(any(#(feature = #snake_names),*))]};

// Derived peripherals may not require re-implementation, and will instead
// use a single definition of the non-derived version.
if derive_regs {
// re-export the base module to allow deriveFrom this one
out.extend(quote! {
#[doc = #description]
#feature_any_attribute
pub use #base as #name_snake_case;
});
return Ok(out);
}
}
_ => {
Peripheral::Single(_) => {
let p_snake = name.to_sanitized_snake_case();
if config.feature_peripheral {
feature_attribute.extend(quote! { #[cfg(feature = #p_snake)] })
};
// Insert the peripheral structure
out.extend(quote! {
#[doc = #description]
Expand Down Expand Up @@ -135,19 +162,19 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result
}
}
});
}
}

// Derived peripherals may not require re-implementation, and will instead
// use a single definition of the non-derived version.
if derive_regs {
// re-export the base module to allow deriveFrom this one
out.extend(quote! {
#[doc = #description]
#feature_attribute
pub use #base as #name_snake_case;
});
return Ok(out);
// Derived peripherals may not require re-implementation, and will instead
// use a single definition of the non-derived version.
if derive_regs {
// re-export the base module to allow deriveFrom this one
out.extend(quote! {
#[doc = #description]
#feature_attribute
pub use #base as #name_snake_case;
});
return Ok(out);
}
}
}

let description = util::escape_brackets(
Expand Down
46 changes: 39 additions & 7 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use clap::{App, Arg};

use svd2rust::{
generate, load_from,
util::{build_rs, group_names, Config, SourceType, Target},
util::{self, build_rs, Config, SourceType, Target},
};

fn run() -> Result<()> {
Expand Down Expand Up @@ -80,6 +80,11 @@ fn run() -> Result<()> {
.long("feature_group")
.help("Use group_name of peripherals as feature"),
)
.arg(
Arg::with_name("feature_peripheral")
.long("feature_peripheral")
.help("Use independent cfg feature flags for each peripheral"),
)
.arg(
Arg::with_name("make_mod")
.long("make_mod")
Expand Down Expand Up @@ -176,6 +181,8 @@ fn run() -> Result<()> {
cfg.bool_flag("derive_more", Filter::Arg) || cfg.bool_flag("derive_more", Filter::Conf);
let feature_group =
cfg.bool_flag("feature_group", Filter::Arg) || cfg.bool_flag("feature_group", Filter::Conf);
let feature_peripheral = cfg.bool_flag("feature_peripheral", Filter::Arg)
|| cfg.bool_flag("feature_peripheral", Filter::Conf);

let mut source_type = cfg
.grab()
Expand All @@ -201,6 +208,7 @@ fn run() -> Result<()> {
pascal_enum_values,
derive_more,
feature_group,
feature_peripheral,
output_dir: path.clone(),
source_type,
};
Expand Down Expand Up @@ -229,18 +237,42 @@ fn run() -> Result<()> {
writeln!(File::create(path.join("build.rs"))?, "{}", build_rs())?;
}

if feature_group {
let group_names: Vec<String> = group_names(&device)
.iter()
.map(|s| format!("{s} = []\n"))
.collect();
if feature_group || feature_peripheral {
let mut features = Vec::new();
if feature_group {
features.extend(
util::group_names(&device)
.iter()
.map(|s| format!("{s} = []\n")),
);
let add_groups: Vec<_> = util::group_names(&device)
.iter()
.map(|s| format!("\"{s}\""))
.collect();
features.push(format!("all-groups = [{}]\n", add_groups.join(",")))
}
if feature_peripheral {
features.extend(
util::peripheral_names(&device)
.iter()
.map(|s| format!("{s} = []\n")),
);
let add_peripherals: Vec<_> = util::peripheral_names(&device)
.iter()
.map(|s| format!("\"{s}\""))
.collect();
features.push(format!(
"all-peripherals = [{}]\n",
add_peripherals.join(",")
))
}
write!(
File::create(path.join("features.toml"))?,
"# Below are the FEATURES generated by svd2rust base on groupName in SVD file.\n\
# Please copy them to Cargo.toml.\n\
[features]\n\
{}",
group_names.join("")
features.join("")
)?;
}

Expand Down
30 changes: 26 additions & 4 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use quote::quote;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use svd_parser::expand::BlockPath;
use svd_rs::{MaybeArray, PeripheralInfo};
use svd_rs::{MaybeArray, Peripheral, PeripheralInfo};

use syn::{
punctuated::Punctuated, token::Colon2, AngleBracketedGenericArguments, GenericArgument, Lit,
Expand Down Expand Up @@ -35,6 +35,7 @@ pub struct Config {
pub pascal_enum_values: bool,
pub derive_more: bool,
pub feature_group: bool,
pub feature_peripheral: bool,
pub output_dir: PathBuf,
pub source_type: SourceType,
}
Expand All @@ -53,6 +54,7 @@ impl Default for Config {
pascal_enum_values: false,
derive_more: false,
feature_group: false,
feature_peripheral: false,
output_dir: PathBuf::from("."),
source_type: SourceType::default(),
}
Expand Down Expand Up @@ -536,10 +538,30 @@ impl FullName for PeripheralInfo {
}
}

pub fn group_names(d: &Device) -> HashSet<Cow<str>> {
d.peripherals
pub fn group_names(d: &Device) -> Vec<Cow<str>> {
let set: HashSet<_> = d
.peripherals
.iter()
.filter_map(|p| p.group_name.as_ref())
.map(|name| name.to_sanitized_snake_case())
.collect()
.collect();
let mut v: Vec<_> = set.into_iter().collect();
v.sort();
v
}

pub fn peripheral_names(d: &Device) -> Vec<String> {
let mut v = Vec::new();
for p in &d.peripherals {
match p {
Peripheral::Single(info) => {
v.push(replace_suffix(&info.name.to_sanitized_snake_case(), ""))
}
Peripheral::Array(info, dim) => v.extend(
svd_rs::array::names(info, dim).map(|n| n.to_sanitized_snake_case().into()),
),
}
}
v.sort();
v
}