From f8e5628b718497ac45ed1d05de245229533adfdb Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Thu, 15 Apr 2021 21:19:43 +0300 Subject: [PATCH 1/3] support of alternateGroup registers --- CHANGELOG.md | 2 + src/generate/peripheral.rs | 99 ++++++++++++++++++++------------------ src/util.rs | 20 ++++++-- 3 files changed, 71 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db9719f3..118abdf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- Support for registers with alternateGroup + - New `-m` switch generates a `mod.rs` file instead of `lib.rs`, which can be used as a module inside a crate without further modification. diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 50f5456c..6dd02787 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -10,7 +10,7 @@ use quote::{quote, ToTokens}; use svd_parser::derive_from::DeriveFrom; use syn::{parse_str, Token}; -use crate::util::{self, ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYTE}; +use crate::util::{self, FullName, ToSanitizedSnakeCase, ToSanitizedUpperCase, BITS_PER_BYTE}; use anyhow::{anyhow, bail, Context, Result}; use crate::generate::register; @@ -223,17 +223,17 @@ struct RegisterBlockField { #[derive(Clone, Debug)] struct Region { - fields: Vec, + rbfs: Vec, offset: u32, end: u32, - /// This is only used for regions with `fields.len() > 1` + /// This is only used for regions with `rbfs.len() > 1` pub ident: Option, } impl Region { fn shortest_ident(&self) -> Option { let mut idents: Vec<_> = self - .fields + .rbfs .iter() .filter_map(|f| match &f.field.ident { None => None, @@ -274,7 +274,7 @@ impl Region { } let idents: Vec<_> = self - .fields + .rbfs .iter() .filter_map(|f| match &f.field.ident { None => None, @@ -304,10 +304,13 @@ impl Region { } if index <= 1 { None - } else if first.get(index).is_some() && first[index].chars().all(|c| c.is_numeric()) { - Some(first.iter().take(index).cloned().collect()) } else { - Some(first.iter().take(index - 1).cloned().collect()) + Some(match first.get(index) { + Some(elem) if elem.chars().all(|c| c.is_numeric()) => { + first.iter().take(index).cloned().collect() + } + _ => first.iter().take(index - 1).cloned().collect(), + }) } } @@ -320,12 +323,12 @@ impl Region { } fn is_union(&self) -> bool { - self.fields.len() > 1 + self.rbfs.len() > 1 } } /// FieldRegions keeps track of overlapping field regions, -/// merging fields into appropriate regions as we process them. +/// merging rbfs into appropriate regions as we process them. /// This allows us to reason about when to create a union /// rather than a struct. #[derive(Default, Debug)] @@ -338,19 +341,19 @@ struct FieldRegions { impl FieldRegions { /// Track a field. If the field overlaps with 1 or more existing /// entries, they will be merged together. - fn add(&mut self, field: &RegisterBlockField) -> Result<()> { + fn add(&mut self, rbf: &RegisterBlockField) -> Result<()> { // When merging, this holds the indices in self.regions - // that the input `field` will be merging with. + // that the input `rbf` will be merging with. let mut indices = Vec::new(); - let field_start = field.offset; - let field_end = field_start + (field.size + BITS_PER_BYTE - 1) / BITS_PER_BYTE; + let rbf_start = rbf.offset; + let rbf_end = rbf_start + (rbf.size + BITS_PER_BYTE - 1) / BITS_PER_BYTE; // The region that we're going to insert let mut new_region = Region { - fields: vec![field.clone()], - offset: field.offset, - end: field_end, + rbfs: vec![rbf.clone()], + offset: rbf.offset, + end: rbf_end, ident: None, }; @@ -363,8 +366,8 @@ impl FieldRegions { let f_end = f.end; // Compute intersection range - let begin = f_start.max(field_start); - let end = f_end.min(field_end); + let begin = f_start.max(rbf_start); + let end = f_end.min(rbf_end); if end > begin { // We're going to remove this element and fold it @@ -375,8 +378,8 @@ impl FieldRegions { new_region.offset = new_region.offset.min(f_start); new_region.end = new_region.end.max(f_end); - // And merge in the fields - new_region.fields.append(&mut f.fields); + // And merge in the rbfs + new_region.rbfs.append(&mut f.rbfs); } } @@ -387,7 +390,7 @@ impl FieldRegions { self.regions.remove(*idx); } - new_region.fields.sort_by_key(|f| f.offset); + new_region.rbfs.sort_by_key(|f| f.offset); // maintain the regions ordered by starting offset let idx = self @@ -413,7 +416,7 @@ impl FieldRegions { let idents: Vec<_> = { self.regions .iter_mut() - .filter(|r| r.fields.len() > 1) + .filter(|r| r.rbfs.len() > 1) .map(|r| { r.ident = r.compute_ident(); r.ident.clone() @@ -424,15 +427,15 @@ impl FieldRegions { .iter_mut() .filter(|r| r.ident.is_some()) .filter(|r| { - r.fields.len() > 1 && (idents.iter().filter(|ident| **ident == r.ident).count() > 1) + r.rbfs.len() > 1 && (idents.iter().filter(|&ident| ident == &r.ident).count() > 1) }) .for_each(|r| { + let new_ident = r.shortest_ident(); warn!( "Found type name conflict with region {:?}, renamed to {:?}", - r.ident, - r.shortest_ident() + r.ident, new_ident ); - r.ident = r.shortest_ident(); + r.ident = new_ident; }); Ok(()) } @@ -444,7 +447,7 @@ fn register_or_cluster_block( name: Option<&str>, _nightly: bool, ) -> Result { - let mut fields = TokenStream::new(); + let mut rbfs = TokenStream::new(); let mut accessors = TokenStream::new(); let mut have_accessors = false; @@ -459,7 +462,7 @@ fn register_or_cluster_block( // We need to compute the idents of each register/union block first to make sure no conflicts exists. regions.resolve_idents()?; - // The end of the region for which we previously emitted a field into `fields` + // The end of the region for which we previously emitted a rbf into `rbfs` let mut last_end = 0; let span = Span::call_site(); @@ -469,15 +472,15 @@ fn register_or_cluster_block( if pad != 0 { let name = Ident::new(&format!("_reserved{}", i), span); let pad = pad as usize; - fields.extend(quote! { + rbfs.extend(quote! { #name : [u8; #pad], }); } - let mut region_fields = TokenStream::new(); + let mut region_rbfs = TokenStream::new(); let is_region_a_union = region.is_union(); - for reg_block_field in ®ion.fields { + for reg_block_field in ®ion.rbfs { let comment = &format!( "0x{:02x} - {}", reg_block_field.offset, @@ -499,20 +502,20 @@ fn register_or_cluster_block( } }); } else { - region_fields.extend(quote! { + region_rbfs.extend(quote! { #[doc = #comment] }); - reg_block_field.field.to_tokens(&mut region_fields); - Punct::new(',', Spacing::Alone).to_tokens(&mut region_fields); + reg_block_field.field.to_tokens(&mut region_rbfs); + Punct::new(',', Spacing::Alone).to_tokens(&mut region_rbfs); } } if !is_region_a_union { - fields.extend(region_fields); + rbfs.extend(region_rbfs); } else { // Emit padding for the items that we're not emitting - // as fields so that subsequent fields have the correct + // as rbfs so that subsequent rbfs have the correct // alignment in the struct. We could omit this and just // not updated `last_end`, so that the padding check in // the outer loop kicks in, but it is nice to be able to @@ -534,7 +537,7 @@ fn register_or_cluster_block( span, ); let pad = (region.end - region.offset) as usize; - fields.extend(quote! { + rbfs.extend(quote! { #name: [u8; #pad], }) } @@ -563,7 +566,7 @@ fn register_or_cluster_block( ///Register block #[repr(C)] pub struct #name { - #fields + #rbfs } #accessors @@ -809,10 +812,10 @@ fn expand_svd_register( ) }); - let ty_name = util::replace_suffix(&info.name, ""); + let ty_name = util::replace_suffix(&info.fullname(), ""); for (idx, _i) in indices.iter().zip(0..) { - let nb_name = util::replace_suffix(&info.name, idx); + let nb_name = util::replace_suffix(&info.fullname(), idx); let ty = name_to_wrapped_ty(&ty_name, name)?; @@ -826,13 +829,15 @@ fn expand_svd_register( /// Convert a parsed `Register` into its `Field` equivalent fn convert_svd_register(register: &Register, name: Option<&str>) -> Result { Ok(match register { - Register::Single(info) => new_syn_field( - &info.name.to_sanitized_snake_case(), - name_to_wrapped_ty(&info.name, name)?, - ), + Register::Single(info) => { + let info_name = info.fullname(); + new_syn_field( + &info_name.to_sanitized_snake_case(), + name_to_wrapped_ty(&info_name, name)?, + ) + } Register::Array(info, array_info) => { - let nb_name = util::replace_suffix(&info.name, ""); - + let nb_name = util::replace_suffix(&info.fullname(), ""); let ty = syn::Type::Array(parse_str::(&format!( "[{};{}]", name_to_wrapped_ty_str(&nb_name, name), diff --git a/src/util.rs b/src/util.rs index bb770972..c8146980 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use crate::svd::{Access, Cluster, Register, RegisterCluster}; +use crate::svd::{Access, Cluster, Register, RegisterCluster, RegisterInfo}; use inflections::Inflect; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{quote, ToTokens}; @@ -146,8 +146,8 @@ pub fn escape_brackets(s: &str) -> String { pub fn name_of(register: &Register) -> Cow { match register { - Register::Single(info) => Cow::from(&info.name), - Register::Array(info, _) => replace_suffix(&info.name, "").into(), + Register::Single(info) => info.fullname(), + Register::Array(info, _) => replace_suffix(&info.fullname(), "").into(), } } @@ -331,3 +331,17 @@ pub fn build_rs() -> TokenStream { } } } + +pub trait FullName { + fn fullname(&self) -> Cow; +} + +impl FullName for RegisterInfo { + fn fullname(&self) -> Cow { + if let Some(group) = &self.alternate_group { + format!("{}_{}", group, self.name).into() + } else { + self.name.as_str().into() + } + } +} From 7fe3a52026ef5f66b65ee6516c670d931b5101a6 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Sun, 12 Jan 2020 18:07:09 +0300 Subject: [PATCH 2/3] Add RegisterBlock trait --- CHANGELOG.md | 3 +++ src/generate/generic.rs | 9 +++++++++ src/generate/peripheral.rs | 9 +++++++++ 3 files changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 118abdf2..1913fbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added +- RegisterBlock trait (like `Deref`, but don't require `self` instance, + only for fixed in memory peripherals) + - Support for registers with alternateGroup - New `-m` switch generates a `mod.rs` file instead of `lib.rs`, which can diff --git a/src/generate/generic.rs b/src/generate/generic.rs index b2863807..3488e1b5 100644 --- a/src/generate/generic.rs +++ b/src/generate/generic.rs @@ -1,5 +1,14 @@ use core::marker; +///This trait allows to get raw pointer on derived peripheral +///as block of registers of base peripheral (like unsafe `Deref`) +pub trait RegisterBlock { + ///Type of RegisterBlock of base peripheral + type RB; + ///Take peripheral address as raw pointer + fn rb() -> *const Self::RB; +} + /// Raw register type pub trait RegisterSpec { /// Raw register type (`u8`, `u16`, `u32`, ...). diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 6dd02787..8d0dcb15 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -79,6 +79,15 @@ pub fn render( } } + impl crate::RegisterBlock for #name_pc { + type RB = #base::RegisterBlock; + + #[inline(always)] + fn rb() -> *const Self::RB { + #name_pc::ptr() + } + } + impl core::fmt::Debug for #name_pc { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { f.debug_struct(#name_str).finish() From 8b068f14fc59c7b0ddf2e3feb84342d5ec755441 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Wed, 14 Apr 2021 00:18:39 +0200 Subject: [PATCH 3/3] Bump version to 0.18 and add missing CHANGELOG.md entries Signed-off-by: Daniel Egger --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++-- Cargo.toml | 4 ++-- README.md | 4 ++-- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1913fbce..8f647bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,16 +7,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +## [v0.18.0] - 2021-04-17 + ### Added - RegisterBlock trait (like `Deref`, but don't require `self` instance, - only for fixed in memory peripherals) + only for memory fixed peripherals) - Support for registers with alternateGroup - New `-m` switch generates a `mod.rs` file instead of `lib.rs`, which can be used as a module inside a crate without further modification. +- ESP32/XtensaLX6 support. + +- Field array support. + +- Add repr(transparent) to Reg struct + - Generated crates now contain the git commit hash and date of svd2rust compilation. @@ -46,8 +54,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - [breaking-change] make `write_with_zero` method `unsafe` because the way it is +- Use complete path for cluster names + +- Rename some generated variables. + +- [breaking-change] Publishes the register spec zero-sized type and move all relevant register traits to that struct. + +- [breaking-change] Removes the extra type parameter on Reg, making the register spec the sole authority on the shape of the register. + +- Wrap register reader/writer and field readers in newtype wrappers, which significantly improves the documentation output. + +- Improve documentation on generated registers and fields + - [breaking-change] remove `Variant`, use `Option` instead +- [breaking-change] Update `svd-parser` to `0.10` + - split out register size type (`RawType`) from `ResetValue` trait - `anyhow` crate is used for error handling @@ -66,6 +88,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). interrupt number handling. The minimum supported `cortex-m` version is now **0.7** and `bare-metal` is not a dependency anymore. +### Removed + +- Generated use of the register type aliases in favor of directly referencing `Reg` + ## [v0.17.0] - 2019-12-31 ### Fixed @@ -560,7 +586,8 @@ peripheral.register.write(|w| w.field().set()); - Initial version of the `svd2rust` tool -[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.17.0...HEAD +[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.18.0...HEAD +[v0.18.0]: https://github.com/rust-embedded/svd2rust/compare/v0.17.0...v0.18.0 [v0.17.0]: https://github.com/rust-embedded/svd2rust/compare/v0.16.1...v0.17.0 [v0.16.1]: https://github.com/rust-embedded/svd2rust/compare/v0.16.0...v0.16.1 [v0.16.0]: https://github.com/rust-embedded/svd2rust/compare/v0.15.2...v0.16.0 diff --git a/Cargo.toml b/Cargo.toml index bf482ce6..6051d318 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ keywords = [ license = "MIT OR Apache-2.0" name = "svd2rust" repository = "https://github.com/rust-embedded/svd2rust/" -version = "0.17.0" +version = "0.18.0" readme = "README.md" [[bin]] @@ -43,7 +43,7 @@ anyhow = "1.0" thiserror = "1.0" [dependencies.svd-parser] -version = "0.10" +version = "0.10.1" features = ["derive-from"] [dependencies.syn] diff --git a/README.md b/README.md index 2ecc1f02..c5aa4a4e 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ This project is developed and maintained by the [Tools team][team]. ## Minimum Supported Rust Version (MSRV) -The **generated code** is guaranteed to compile on stable Rust 1.37.0 and up. +The **generated code** is guaranteed to compile on stable Rust 1.40.0 and up. -If you encounter compilation errors on any stable version newer than 1.37.0, please open an issue. +If you encounter compilation errors on any stable version newer than 1.40.0, please open an issue. # Testing Locally