From 7c5a312d44bc5a5ea83e027d07092660e3c7de43 Mon Sep 17 00:00:00 2001 From: Andrey Zgarbul Date: Fri, 17 May 2024 21:46:07 +0300 Subject: [PATCH] always raw access --- CHANGELOG.md | 7 +- src/config.rs | 1 + src/generate/device.rs | 12 +- src/generate/generic_reg_raw.rs | 301 ++++++++++++++++++++++++++++ src/generate/peripheral.rs | 151 +++++++++----- src/generate/peripheral/accessor.rs | 74 ++++++- src/main.rs | 7 + 7 files changed, 498 insertions(+), 55 deletions(-) create mode 100644 src/generate/generic_reg_raw.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 64475b5a..65235352 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [v0.34.0] - 2024-11-05 +- Add `raw-access` options + - Revert #711 - Add `defmt` impls for `TryFromInterruptError`, riscv interrupt enums - Fix calculating `modifiedWriteValues` bitmasks with field arrays @@ -924,10 +926,7 @@ peripheral.register.write(|w| w.field().set()); - Initial version of the `svd2rust` tool -[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.34.0...HEAD -[v0.34.0]: https://github.com/rust-embedded/svd2rust/compare/v0.33.5...v0.34.0 -[v0.33.5]: https://github.com/rust-embedded/svd2rust/compare/v0.33.4...v0.33.5 -[v0.33.4]: https://github.com/rust-embedded/svd2rust/compare/v0.33.3...v0.33.4 +[Unreleased]: https://github.com/rust-embedded/svd2rust/compare/v0.33.3...HEAD [v0.33.3]: https://github.com/rust-embedded/svd2rust/compare/v0.33.2...v0.33.3 [v0.33.2]: https://github.com/rust-embedded/svd2rust/compare/v0.33.1...v0.33.2 [v0.33.1]: https://github.com/rust-embedded/svd2rust/compare/v0.33.0...v0.33.1 diff --git a/src/config.rs b/src/config.rs index 5a48105c..d8bdf96e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -36,6 +36,7 @@ pub struct Config { pub field_names_for_enums: bool, pub base_address_shift: u64, pub html_url: Option, + pub raw_access: bool, /// Path to YAML file with chip-specific settings pub settings: Option, } diff --git a/src/generate/device.rs b/src/generate/device.rs index ec2ef6c6..a13c47a1 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -76,8 +76,12 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result { + ptr: *mut u8, + _marker: marker::PhantomData, +} + +unsafe impl Send for Reg where REG::Ux: Send {} + +impl Reg { + #[inline(always)] + pub const fn new(ptr: *mut u8) -> Self { + Self { + ptr, + _marker: marker::PhantomData, + } + } + /// Returns the underlying memory address of register. + /// + /// ```ignore + /// let reg_ptr = periph.reg.as_ptr(); + /// ``` + #[inline(always)] + pub const fn as_ptr(&self) -> *mut REG::Ux { + self.ptr.cast() + } +} + +impl Reg { + /// Reads the contents of a `Readable` register. + /// + /// You can read the raw contents of a register by using `bits`: + /// ```ignore + /// let bits = periph.reg.read().bits(); + /// ``` + /// or get the content of a particular field of a register: + /// ```ignore + /// let reader = periph.reg.read(); + /// let bits = reader.field1().bits(); + /// let flag = reader.field2().bit_is_set(); + /// ``` + #[inline(always)] + pub unsafe fn read(&self) -> R { + R { + bits: self.as_ptr().read_volatile(), + _reg: marker::PhantomData, + } + } +} + +impl Reg { + /// Writes the reset value to `Writable` register. + /// + /// Resets the register to its initial state. + #[inline(always)] + pub unsafe fn reset(&self) { + self.as_ptr().write_volatile(REG::RESET_VALUE) + } + + /// Writes bits to a `Writable` register. + /// + /// You can write raw bits into a register: + /// ```ignore + /// periph.reg.write(|w| unsafe { w.bits(rawbits) }); + /// ``` + /// or write only the fields you need: + /// ```ignore + /// periph.reg.write(|w| w + /// .field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT) + /// ); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.write(|w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT) + /// }); + /// ``` + /// In the latter case, other fields will be set to their reset value. + #[inline(always)] + pub unsafe fn write(&self, f: F) -> REG::Ux + where + F: FnOnce(&mut W) -> &mut W, + { + let value = f(&mut W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }) + .bits; + self.as_ptr().write_volatile(value); + value + } + + /// Writes bits to a `Writable` register and produce a value. + /// + /// You can write raw bits into a register: + /// ```ignore + /// periph.reg.write_and(|w| unsafe { w.bits(rawbits); }); + /// ``` + /// or write only the fields you need: + /// ```ignore + /// periph.reg.write_and(|w| { + /// w.field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT); + /// }); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.write_and(|w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT); + /// }); + /// ``` + /// In the latter case, other fields will be set to their reset value. + /// + /// Values can be returned from the closure: + /// ```ignore + /// let state = periph.reg.write_and(|w| State::set(w.field1())); + /// ``` + #[inline(always)] + pub fn from_write(&self, f: F) -> T + where + F: FnOnce(&mut W) -> T, + { + let mut writer = W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }; + let result = f(&mut writer); + + self.as_ptr().write_volatile(writer.bits); + + result + } +} + +impl Reg { + /// Writes 0 to a `Writable` register. + /// + /// Similar to `write`, but unused bits will contain 0. + /// + /// # Safety + /// + /// Unsafe to use with registers which don't allow to write 0. + #[inline(always)] + pub unsafe fn write_with_zero(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + let value = f(&mut W { + bits: REG::Ux::default(), + _reg: marker::PhantomData, + }) + .bits; + self.as_ptr().write_volatile(value); + value + } + + /// Writes 0 to a `Writable` register and produces a value. + /// + /// Similar to `write`, but unused bits will contain 0. + /// + /// # Safety + /// + /// Unsafe to use with registers which don't allow to write 0. + #[inline(always)] + pub unsafe fn from_write_with_zero(&self, f: F) -> T + where + F: FnOnce(&mut W) -> T, + { + let mut writer = W { + bits: REG::Ux::default(), + _reg: marker::PhantomData, + }; + + let result = f(&mut writer); + + self.as_ptr().write_volatile(writer.bits); + + result + } +} + +impl Reg { + /// Modifies the contents of the register by reading and then writing it. + /// + /// E.g. to do a read-modify-write sequence to change parts of a register: + /// ```ignore + /// periph.reg.modify(|r, w| unsafe { w.bits( + /// r.bits() | 3 + /// ) }); + /// ``` + /// or + /// ```ignore + /// periph.reg.modify(|_, w| w + /// .field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT) + /// ); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT) + /// }); + /// ``` + /// Other fields will have the value they had before the call to `modify`. + #[inline(always)] + pub unsafe fn modify(&self, f: F) + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, + { + let bits = self.as_ptr().read_volatile(); + let value = f( + &R { + bits, + _reg: marker::PhantomData, + }, + &mut W { + bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }, + ) + .bits; + self.as_ptr().write_volatile(value); + value + } + + /// Modifies the contents of the register by reading and then writing it + /// and produces a value. + /// + /// E.g. to do a read-modify-write sequence to change parts of a register: + /// ```ignore + /// let bits = periph.reg.modify(|r, w| { + /// let new_bits = r.bits() | 3; + /// unsafe { + /// w.bits(new_bits); + /// } + /// + /// new_bits + /// }); + /// ``` + /// or + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits) + /// .field2().set_bit() + /// .field3().variant(VARIANT); + /// }); + /// ``` + /// or an alternative way of saying the same: + /// ```ignore + /// periph.reg.modify(|_, w| { + /// w.field1().bits(newfield1bits); + /// w.field2().set_bit(); + /// w.field3().variant(VARIANT); + /// }); + /// ``` + /// Other fields will have the value they had before the call to `modify`. + #[inline(always)] + pub fn from_modify(&self, f: F) -> T + where + for<'w> F: FnOnce(&R, &'w mut W) -> T, + { + let bits = self.as_ptr().read_volatile(); + + let mut writer = W { + bits: bits & !REG::ONE_TO_MODIFY_FIELDS_BITMAP | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }; + + let result = f( + &R { + bits, + _reg: marker::PhantomData, + }, + &mut writer, + ); + + self.as_ptr().write_volatile(writer.bits); + + result + } +} + +impl core::fmt::Debug for crate::generic::Reg +where + R: core::fmt::Debug, +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + unsafe { core::fmt::Debug::fmt(&self.read(), f) } + } +} diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 407415f9..ce3c78ab 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -62,7 +62,7 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result feature_attribute.extend(quote! { #[cfg(feature = #feature_name)] }); }; - let steal_fn = quote! { + let steal_docs = quote! { /// Steal an instance of this peripheral /// /// # Safety @@ -76,9 +76,6 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result /// Additionally, other software such as HALs may rely on only one /// peripheral instance existing to ensure memory safety; ensure /// no stolen instances are passed to such software. - pub unsafe fn steal() -> Self { - Self { _marker: PhantomData } - } }; let phtml = config.html_url.as_ref().map(|url| { @@ -92,40 +89,75 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result p_ty: &Ident, doc_alias: Option, address: LitInt| { - out.extend(quote! { - #[doc = #description] - #phtml - #doc_alias - #feature_attribute - pub struct #p_ty { _marker: PhantomData<*const ()> } + out.extend(if config.raw_access { + quote! { + #[doc = #description] + #phtml + #doc_alias + #feature_attribute + pub struct #p_ty { rb: #base::RegisterBlock } + + #feature_attribute + unsafe impl Send for #p_ty {} + + #feature_attribute + impl #p_ty { + #steal_docs + pub unsafe fn steal() -> Self { + Self { rb: #base::RegisterBlock::new(#address as *mut u8) } + } + } - #feature_attribute - unsafe impl Send for #p_ty {} + #feature_attribute + impl Deref for #p_ty { + type Target = #base::RegisterBlock; - #feature_attribute - impl #p_ty { - ///Pointer to the register block - pub const PTR: *const #base::RegisterBlock = #address as *const _; - - ///Return the pointer to the register block - #[inline(always)] - pub const fn ptr() -> *const #base::RegisterBlock { - Self::PTR + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.rb + } } - - #steal_fn } + } else { + quote! { + #[doc = #description] + #phtml + #doc_alias + #feature_attribute + pub struct #p_ty { _marker: PhantomData<*const ()> } + + #feature_attribute + unsafe impl Send for #p_ty {} + + #feature_attribute + impl #p_ty { + ///Pointer to the register block + pub const PTR: *const #base::RegisterBlock = #address as *const _; + + ///Return the pointer to the register block + #[inline(always)] + pub const fn ptr() -> *const #base::RegisterBlock { + Self::PTR + } - #feature_attribute - impl Deref for #p_ty { - type Target = #base::RegisterBlock; + #steal_docs + pub unsafe fn steal() -> Self { + Self { _marker: PhantomData } + } + } - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } + #feature_attribute + impl Deref for #p_ty { + type Target = #base::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } } } - + }); + out.extend(quote! { #feature_attribute impl core::fmt::Debug for #p_ty { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { @@ -641,6 +673,18 @@ fn register_or_cluster_block( Ident::new("RegisterBlock", span) }; + if config.raw_access { + accessors.extend(quote! { + pub(crate) const fn new(ptr: *mut u8) -> Self { + Self { ptr } + } + #[inline(always)] + pub const fn ptr(&self) -> *mut u8 { + self.ptr + } + }) + } + let accessors = (!accessors.is_empty()).then(|| { quote! { impl #block_ty { @@ -649,16 +693,31 @@ fn register_or_cluster_block( } }); - Ok(quote! { - #[repr(C)] - #derive_debug - #[doc = #doc] - #doc_alias - pub struct #block_ty { - #rbfs + Ok(if config.raw_access { + quote! { + #[doc = #doc] + #[repr(C)] + #derive_debug + #doc_alias + #[non_exhaustive] + pub struct #block_ty { + ptr: *mut u8, + } + + #accessors } + } else { + quote! { + #[doc = #doc] + #[repr(C)] + #derive_debug + #doc_alias + pub struct #block_ty { + #rbfs + } - #accessors + #accessors + } }) } @@ -1001,7 +1060,7 @@ fn expand_cluster(cluster: &Cluster, config: &Config) -> Result Result Result Result AccessType { - if flag { + pub fn ptr_or_rawref_if(self, ptr_flag: bool, raw_flag: bool) -> AccessType { + if ptr_flag { + AccessType::Ptr(self) + } else if raw_flag { AccessType::RawRef(self) } else { AccessType::Ref(self) @@ -29,7 +32,7 @@ impl Accessor { impl AccessType { pub fn raw(self) -> Self { match self { - Self::RawRef(_) => self, + Self::RawRef(_) | Self::Ptr(_) => self, Self::Ref(a) => Self::RawRef(a), } } @@ -62,6 +65,21 @@ impl ToTokens for AccessType { } } } + Self::Ptr(Accessor::Reg(RegAccessor { + doc, + name, + ty, + offset, + })) => { + let offset = (*offset != 0).then(|| unsuffixed(*offset)).map(|o| quote!(.add(#o))); + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> #ty { + #ty::new(unsafe { self.ptr() #offset }) + } + } + } Self::Ref(Accessor::Array(ArrayAccessor { doc, name, ty, note, .. })) => { let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); let note = note.as_ref().map(|note| quote! { @@ -118,6 +136,41 @@ impl ToTokens for AccessType { } } } + Self::Ptr(Accessor::Array(ArrayAccessor { + doc, + name, + ty, + offset, + dim, + increment, + note, + })) => { + let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); + let offset = (*offset != 0).then(|| unsuffixed(*offset)).map(|o| quote!(.add(#o))); + let dim = unsuffixed(*dim); + let increment = (*increment != 1).then(|| unsuffixed(*increment)).map(|i| quote!(#i *)); + let note = note.as_ref().map(|note| quote! { + #[doc = ""] + #[doc = #note] + }); + let cast = quote! { #ty::new(unsafe { self.ptr() #offset .add(#increment n) }) }; + quote! { + #[doc = #doc] + #note + #[inline(always)] + pub const fn #name(&self, n: usize) -> #ty { + #[allow(clippy::no_effect)] + [(); #dim][n]; + #cast + } + #[doc = "Iterator for array of:"] + #[doc = #doc] + #[inline(always)] + pub fn #name_iter(&self) -> impl Iterator + '_ { + (0..#dim).map(move |n| #cast) + } + } + } Self::RawRef(Accessor::ArrayElem(elem)) | Self::Ref(Accessor::ArrayElem(elem)) => { let ArrayElemAccessor { doc, @@ -135,6 +188,21 @@ impl ToTokens for AccessType { } } } + Self::Ptr(Accessor::ArrayElem(ArrayElemAccessor { + doc, + name, + ty, + basename, + i, + })) => { + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> #ty { + self.#basename(#i) + } + } + } } .to_tokens(tokens); } diff --git a/src/main.rs b/src/main.rs index 880bb4ab..b8b16d42 100755 --- a/src/main.rs +++ b/src/main.rs @@ -251,6 +251,13 @@ Allowed cases are `unchanged` (''), `pascal` ('p'), `constant` ('c') and `snake` .alias("source_type") .help("Specify file/stream format"), ) + .arg( + Arg::new("raw_access") + .long("raw-access") + .alias("raw_access") + .action(ArgAction::SetTrue) + .help("Always access to registers by address"), + ) .arg( Arg::new("reexport_core_peripherals") .long("reexport-core-peripherals")