diff --git a/CHANGELOG.md b/CHANGELOG.md index 878d2f1f..65b1533a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] +- Add `raw-access` options - Refactor `Accessor` ## [v0.33.3] - 2024-05-10 diff --git a/src/generate/device.rs b/src/generate/device.rs index 25d6bdd2..86c40abd 100644 --- a/src/generate/device.rs +++ b/src/generate/device.rs @@ -63,8 +63,12 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result Result Result Result Result { for p_name in names(p, dim_element) { @@ -264,7 +273,7 @@ pub fn render(d: &Device, config: &Config, device_x: &mut String) -> Result { - register: vcell::VolatileCell, - _marker: marker::PhantomData, -} - -unsafe impl Send for Reg where REG::Ux: Send {} - -impl Reg { - /// Returns the underlying memory address of register. - /// - /// ```ignore - /// let reg_ptr = periph.reg.as_ptr(); - /// ``` - #[inline(always)] - pub fn as_ptr(&self) -> *mut REG::Ux { - self.register.as_ptr() - } -} - -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 fn read(&self) -> R { - R { - bits: self.register.get(), - _reg: marker::PhantomData, - } - } -} - -impl Reg { - /// Writes the reset value to `Writable` register. - /// - /// Resets the register to its initial state. - #[inline(always)] - pub fn reset(&self) { - self.register.set(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 fn write(&self, f: F) - where - F: FnOnce(&mut W) -> &mut W, - { - self.register.set( - f(&mut W { - bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP - | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, - _reg: marker::PhantomData, - }) - .bits, - ); - } -} - -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, - { - self.register.set( - f(&mut W { - bits: REG::Ux::default(), - _reg: marker::PhantomData, - }) - .bits, - ); - } -} - -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 fn modify(&self, f: F) - where - for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, - { - let bits = self.register.get(); - self.register.set( - 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, - ); - } -} - -impl core::fmt::Debug for crate::generic::Reg -where - R: core::fmt::Debug -{ - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - core::fmt::Debug::fmt(&self.read(), f) - } -} - #[doc(hidden)] pub mod raw { use super::{marker, BitM, FieldSpec, RegisterSpec, Unsafe, Writable}; diff --git a/src/generate/generic_reg_raw.rs b/src/generate/generic_reg_raw.rs new file mode 100644 index 00000000..39e741d3 --- /dev/null +++ b/src/generate/generic_reg_raw.rs @@ -0,0 +1,177 @@ +/// This structure provides unsafe volatile access to registers. +pub struct Reg { + 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) + where + F: FnOnce(&mut W) -> &mut W, + { + self.as_ptr().write_volatile( + f(&mut W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }) + .bits, + ); + } +} + +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, + { + self.as_ptr().write_volatile( + f(&mut W { + bits: REG::Ux::default(), + _reg: marker::PhantomData, + }) + .bits, + ); + } +} + +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(); + self.as_ptr().write_volatile( + 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, + ); + } +} + +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/generic_reg_vcell.rs b/src/generate/generic_reg_vcell.rs new file mode 100644 index 00000000..5081ae20 --- /dev/null +++ b/src/generate/generic_reg_vcell.rs @@ -0,0 +1,171 @@ +/// This structure provides volatile access to registers. +#[repr(transparent)] +pub struct Reg { + register: vcell::VolatileCell, + _marker: marker::PhantomData, +} + +unsafe impl Send for Reg where REG::Ux: Send {} + +impl Reg { + /// Returns the underlying memory address of register. + /// + /// ```ignore + /// let reg_ptr = periph.reg.as_ptr(); + /// ``` + #[inline(always)] + pub fn as_ptr(&self) -> *mut REG::Ux { + self.register.as_ptr() + } +} + +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 fn read(&self) -> R { + R { + bits: self.register.get(), + _reg: marker::PhantomData, + } + } +} + +impl Reg { + /// Writes the reset value to `Writable` register. + /// + /// Resets the register to its initial state. + #[inline(always)] + pub fn reset(&self) { + self.register.set(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 fn write(&self, f: F) + where + F: FnOnce(&mut W) -> &mut W, + { + self.register.set( + f(&mut W { + bits: REG::RESET_VALUE & !REG::ONE_TO_MODIFY_FIELDS_BITMAP + | REG::ZERO_TO_MODIFY_FIELDS_BITMAP, + _reg: marker::PhantomData, + }) + .bits, + ); + } +} + +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, + { + self.register.set( + f(&mut W { + bits: REG::Ux::default(), + _reg: marker::PhantomData, + }) + .bits, + ); + } +} + +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 fn modify(&self, f: F) + where + for<'w> F: FnOnce(&R, &'w mut W) -> &'w mut W, + { + let bits = self.register.get(); + self.register.set( + 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, + ); + } +} + +impl core::fmt::Debug for crate::generic::Reg +where + R: core::fmt::Debug +{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Debug::fmt(&self.read(), f) + } +} diff --git a/src/generate/peripheral.rs b/src/generate/peripheral.rs index 60da97f6..2a244c9c 100644 --- a/src/generate/peripheral.rs +++ b/src/generate/peripheral.rs @@ -61,7 +61,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 @@ -75,9 +75,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 } - } }; match &p { @@ -97,39 +94,74 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result feature_attribute_n.extend(quote! { #[cfg(feature = #p_feature)] }) }; // Insert the peripherals structure - out.extend(quote! { - #[doc = #description] - #doc_alias - #feature_attribute_n - pub struct #p_ty { _marker: PhantomData<*const ()> } + out.extend(if config.raw_access { + quote! { + #[doc = #description] + #doc_alias + #feature_attribute_n + pub struct #p_ty { rb: #base::RegisterBlock } + + #feature_attribute_n + unsafe impl Send for #p_ty {} + + #feature_attribute_n + impl #p_ty { + #steal_docs + pub unsafe fn steal() -> Self { + Self { rb: #base::RegisterBlock::new(#address as *mut u8) } + } + } - #feature_attribute_n - unsafe impl Send for #p_ty {} + #feature_attribute_n + impl Deref for #p_ty { + type Target = #base::RegisterBlock; - #feature_attribute_n - 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] + #doc_alias + #feature_attribute_n + pub struct #p_ty { _marker: PhantomData<*const ()> } + + #feature_attribute_n + unsafe impl Send for #p_ty {} + + #feature_attribute_n + 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_n - 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_n + impl Deref for #p_ty { + type Target = #base::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { &*Self::PTR } + } } } + }); + out.extend(quote! { #feature_attribute_n impl core::fmt::Debug for #p_ty { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { @@ -159,38 +191,72 @@ pub fn render(p_original: &Peripheral, index: &Index, config: &Config) -> Result feature_attribute.extend(quote! { #[cfg(feature = #p_feature)] }) }; // Insert the peripheral structure - out.extend(quote! { - #[doc = #description] - #feature_attribute - pub struct #p_ty { _marker: PhantomData<*const ()> } + out.extend(if config.raw_access { + quote! { + #[doc = #description] + #feature_attribute + #[repr(transparent)] + pub struct #p_ty { rb: #base::RegisterBlock } - #feature_attribute - unsafe impl Send for #p_ty {} + #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 #p_ty { + #steal_docs + pub unsafe fn steal() -> Self { + Self { rb: #base::RegisterBlock::new(#address as *mut u8) } + } } - #steal_fn + #feature_attribute + impl Deref for #p_ty { + type Target = #base::RegisterBlock; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.rb + } + } } + } else { + quote! { + #[doc = #description] + #feature_attribute + pub struct #p_ty { _marker: PhantomData<*const ()> } - #feature_attribute - impl Deref for #p_ty { - type Target = #base::RegisterBlock; + #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 + } - #[inline(always)] - fn deref(&self) -> &Self::Target { - unsafe { &*Self::PTR } + #steal_docs + pub unsafe fn steal() -> Self { + Self { _marker: PhantomData } + } } - } + #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 { @@ -649,6 +715,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 { @@ -657,16 +735,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 + } }) } @@ -1009,7 +1102,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) @@ -27,7 +30,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), } } @@ -59,6 +62,20 @@ impl ToTokens for AccessType { } } } + Self::Ptr(Accessor::Reg(RegAccessor { + doc, + name, + ty, + offset, + })) => { + quote! { + #[doc = #doc] + #[inline(always)] + pub const fn #name(&self) -> #ty { + #ty::new(unsafe { self.ptr().add(#offset) }) + } + } + } Self::Ref(Accessor::Array(ArrayAccessor { doc, name, ty, .. })) => { let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); quote! { @@ -101,6 +118,32 @@ impl ToTokens for AccessType { } } } + Self::Ptr(Accessor::Array(ArrayAccessor { + doc, + name, + ty, + offset, + dim, + increment, + })) => { + let name_iter = Ident::new(&format!("{name}_iter"), Span::call_site()); + let cast = quote! { #ty::new(unsafe { self.ptr().add(#offset).add(#increment * n) }) }; + quote! { + #[doc = #doc] + #[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, @@ -117,6 +160,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); }