aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs809
1 files changed, 221 insertions, 588 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 70cedf7..3de33d2 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
// Copyright 2018 Adam Greig
+// Copyright 2025 Ian McIntyre
// See LICENSE-APACHE and LICENSE-MIT for license details.
//! This crate contains an MMIO abstraction that uses macros to read,
@@ -9,655 +10,287 @@
#![no_std]
-use core::cell::UnsafeCell;
+use core::ptr::NonNull;
-/// A read-write register of type T.
+/// Describes a type that acts as a peripheral instance.
///
-/// Contains one value of type T and provides volatile read/write functions to it.
+/// If you're not sure how to implement this, try using [`Instance`].
///
/// # Safety
-/// This register should be used where reads and writes to this peripheral register do not
-/// lead to memory unsafety. For example, it is a poor choice for a DMA target, but less
-/// worrisome for a GPIO output data register.
///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
-#[repr(transparent)]
-pub struct RWRegister<T> {
- register: UnsafeCell<T>,
-}
+/// This can only be implemented atop "smart pointer" objects that
+/// produce the same raw pointer to a static register block.
+pub unsafe trait Inst {}
-impl<T: Copy> RWRegister<T> {
- /// Reads the value of the register.
- #[inline(always)]
- pub fn read(&self) -> T {
- unsafe { ::core::ptr::read_volatile(self.register.get()) }
- }
+/// Safety: any shared reference to an instance can similarly provide
+/// access to the underlying static object.
+unsafe impl<I> Inst for &'_ I where I: Inst {}
- /// Writes a new value to the register.
- #[inline(always)]
- pub fn write(&self, val: T) {
- unsafe { ::core::ptr::write_volatile(self.register.get(), val) }
- }
-}
+/// Safety: see shared reference documentation. If a shared
+/// reference can provide this access, then so can an exclusive
+/// reference.
+unsafe impl<I> Inst for &'_ mut I where I: Inst {}
-/// A read-write register of type T, where read/write access is unsafe.
-///
-/// Contains one value of type T and provides volatile read/write functions to it.
-///
-/// # Safety
-/// This register should be used where reads and writes to this peripheral may invoke
-/// undefined behaviour or memory unsafety. For example, any registers you write a memory
-/// address into.
+/// A pointer to a peripheral instance.
///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
+/// This is transparently a `NonNull<T>`. Like a `NonNull<T>`,
+/// the type is `Copy` and `Clone`, so it may be shared throughout
+/// a single execution context.
+#[derive(PartialEq, Eq)]
#[repr(transparent)]
-pub struct UnsafeRWRegister<T> {
- register: UnsafeCell<T>,
-}
+pub struct Instance<T: ?Sized>(NonNull<T>);
-impl<T: Copy> UnsafeRWRegister<T> {
- /// Reads the value of the register.
- ///
- /// # Safety
- /// Refer to [UnsafeRWRegister]'s Safety section.
- #[inline(always)]
- pub unsafe fn read(&self) -> T {
- ::core::ptr::read_volatile(self.register.get())
- }
+/// Safety: the constructor's safety contract enforces the
+/// trait's safety requirements.
+unsafe impl<T: ?Sized> Inst for Instance<T> {}
- /// Writes a new value to the register.
+impl<T: ?Sized> Instance<T> {
+ /// Create an instance that points to a peripheral register block.
///
/// # Safety
- /// Refer to [UnsafeRWRegister]'s Safety section.
+ ///
+ /// The pointer must not be NULL. The pointer is expected to point
+ /// to static memory that represents a block of MMIO registers.
+ /// The layout of `T` must be correct for the base address.
#[inline(always)]
- pub unsafe fn write(&self, val: T) {
- ::core::ptr::write_volatile(self.register.get(), val)
+ #[must_use]
+ pub const unsafe fn new_unchecked(ptr: *mut T) -> Self {
+ // Safety: caller claims the pointer isn't NULL.
+ Self(unsafe { NonNull::new_unchecked(ptr) })
}
-}
-
-/// A read-only register of type T.
-///
-/// Contains one value of type T and provides a volatile read function to it.
-///
-/// # Safety
-/// This register should be used where reads and writes to this peripheral register do not
-/// lead to memory unsafety.
-///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
-#[repr(transparent)]
-pub struct RORegister<T> {
- register: UnsafeCell<T>,
-}
-impl<T: Copy> RORegister<T> {
- /// Reads the value of the register.
+ /// Access the inner pointer.
#[inline(always)]
- pub fn read(&self) -> T {
- unsafe { ::core::ptr::read_volatile(self.register.get()) }
+ #[must_use]
+ pub const fn as_ptr(self) -> *mut T {
+ self.0.as_ptr()
}
}
-/// A read-only register of type T, where read access is unsafe.
-///
-/// Contains one value of type T and provides a volatile read function to it.
-///
-/// # Safety
-/// This register should be used where reads to this peripheral may invoke
-/// undefined behaviour or memory unsafety.
-///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
-#[repr(transparent)]
-pub struct UnsafeRORegister<T> {
- register: UnsafeCell<T>,
-}
-
-impl<T: Copy> UnsafeRORegister<T> {
- /// Reads the value of the register.
- ///
- /// # Safety
- /// Refer to [UnsafeRWRegister]'s Safety section.
+impl<T: ?Sized> Clone for Instance<T> {
#[inline(always)]
- pub unsafe fn read(&self) -> T {
- ::core::ptr::read_volatile(self.register.get())
+ fn clone(&self) -> Self {
+ *self
}
}
-/// A write-only register of type T.
-///
-/// Contains one value of type T and provides a volatile write function to it.
-///
-/// # Safety
-/// This register should be used where writes to this peripheral register do not lead to memory
-/// unsafety.
-///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
-#[repr(transparent)]
-pub struct WORegister<T> {
- register: UnsafeCell<T>,
-}
+impl<T: ?Sized> Copy for Instance<T> {}
-impl<T: Copy> WORegister<T> {
- /// Writes a new value to the register.
- #[inline(always)]
- pub fn write(&self, val: T) {
- unsafe { ::core::ptr::write_volatile(self.register.get(), val) }
+impl<T> core::fmt::Debug for Instance<T> {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ let ptr = self.0.as_ptr() as usize;
+ f.debug_tuple("Instance")
+ .field(&format_args!("{ptr:#X}"))
+ .finish()
}
}
-/// A write-only register of type T, where write access is unsafe.
-///
-/// Contains one value of type T and provides a volatile write function to it.
-///
-/// # Safety
-/// This register should be used where reads and writes to this peripheral may invoke
-/// undefined behaviour or memory unsafety.
+/// Check expressions that may be unsafe.
///
-/// Access to this register must be synchronised; if multiple threads (or the main thread and an
-/// interrupt service routine) are accessing it simultaneously you may encounter data races.
-#[repr(transparent)]
-pub struct UnsafeWORegister<T> {
- register: UnsafeCell<T>,
+/// Users are not expected to pass non-Copy expressions into
+/// this expansion.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! __expand_unsafe {
+ ( $( $expr:expr, )+ ) => { if false { $( let _ = $expr; )* } };
}
-impl<T: Copy> UnsafeWORegister<T> {
- /// Writes a new value to the register.
- ///
- /// # Safety
- /// Refer to [UnsafeRWRegister]'s Safety section.
- #[inline(always)]
- pub unsafe fn write(&self, val: T) {
- ::core::ptr::write_volatile(self.register.get(), val)
- }
-}
+#[doc(hidden)]
+#[allow(non_snake_case)]
+pub const fn must_be_ral_registers_Inst<I: Inst>(_: &I) {}
-/// Write to a RWRegister or UnsafeRWRegister.
-///
-/// # Examples
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // Safely acquire the peripheral instance (will panic if already acquired)
-/// let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-///
-/// // Write some value to the register.
-/// write_reg!(stm32ral::gpio, gpioa, ODR, 1<<3);
-///
-/// // Write values to specific fields. Unspecified fields are written to 0.
-/// write_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);
-///
-/// // Unsafe access without requiring you to first `take()` the instance
-/// unsafe { write_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };
-/// # }
-/// ```
-///
-/// To support register arrays, each macro form also supports one or more array indices after the
-/// register. For example, `write_reg!(stm32ral::gpio, gpioa, ODR[2], 42);` writes the value 42 to
-/// the third register in an `ODR` register array.
-///
-/// # Usage
-/// Like `modify_reg!`, this macro can be used in two ways, either with a single value to write to
-/// the whole register, or with multiple fields each with their own value.
-///
-/// In both cases, the first arguments are:
-/// * the path to the peripheral module: `stm32ral::gpio`,
-/// * a reference to the instance of that peripheral: 'gpioa' (anything which dereferences to
-/// `RegisterBlock`, such as `Instance`, `&Instance`, `&RegisterBlock`, or
-/// `*const RegisterBlock`),
-/// * the register (and offset, for arrays) you wish you access: `MODER` (a field on the
-/// `RegisterBlock`).
-///
-/// In the single-value usage, the final argument is just the value to write:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Turn on PA3 (and turn everything else off).
-/// write_reg!(stm32ral::gpio, gpioa, ODR, 1<<3);
-/// # }
-/// ```
-///
-/// Otherwise, the remaining arguments are each `Field: Value` pairs:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // Set PA3 to Output, PA4 to Analog, and everything else to 0 (which is Input).
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// write_reg!(stm32ral::gpio, gpioa, MODER, MODER3: 0b01, MODER4: 0b11);
-/// # }
-/// ```
-/// For fields with annotated values, you can also specify a named value:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // As above, but with named values.
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// write_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);
-/// # }
-/// ```
-///
-/// This macro expands to calling `(*$instance).$register.write(value)`,
-/// where in the second usage, the value is computed as the bitwise OR of
-/// each field value, which are masked and shifted appropriately for the given field.
-/// The named values are brought into scope by `use $peripheral::$register::$field::*` for
-/// each field. The same constants could just be specified manually:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // As above, but being explicit about named values.
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// write_reg!(stm32ral::gpio, gpioa, MODER, MODER3: stm32ral::gpio::MODER::MODER3::RW::Output,
-/// MODER4: stm32ral::gpio::MODER::MODER4::RW::Analog);
-/// # }
-/// ```
-///
-/// The fully expanded form is equivalent to:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // As above, but expanded.
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// (*gpioa).MODER.write(
-/// ((stm32ral::gpio::MODER::MODER3::RW::Output << stm32ral::gpio::MODER::MODER3::offset)
-/// & stm32ral::gpio::MODER::MODER3::mask)
-/// |
-/// ((stm32ral::gpio::MODER::MODER4::RW::Analog << stm32ral::gpio::MODER::MODER4::offset)
-/// & stm32ral::gpio::MODER::MODER4::mask)
-/// );
-/// # }
-/// ```
-///
-/// # Safety
-/// This macro will require an unsafe function or block when used with an UnsafeRWRegister,
-/// but not if used with RWRegister.
-///
-/// When run in an unsafe context, peripheral instances are directly accessible without requiring
-/// having called `take()` beforehand:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// unsafe { write_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };
-/// # }
-/// ```
-/// This works because `GPIOA` is a `*const RegisterBlock` in the `stm32ral::gpio` module;
-/// and the macro brings such constants into scope and then dereferences the provided reference.
+/// Write to a register.
#[macro_export]
macro_rules! write_reg {
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $( $field:ident : $value:expr ),+ $(,)? ) => {{
- #[allow(unused_imports)]
- use $periph::{*};
- #[allow(unused_imports)]
- (*$instance).$reg $([$offset])*.write(
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $( $field:ident : $value:expr ),+ $(,)? ) => {{
+ use $($periph)::+ $( :: $reg )* as __register;
+
+ $crate::write_reg!($($periph)::+, $instance, $(. $reg $([$offset])* )*,
$({
- use $periph::{$reg::$field::{W::*, RW::*}};
- ($value << { use $periph::{$reg::$field::offset}; offset })
- & { use $periph::{$reg::$field::mask}; mask }
+ #[allow(unused_imports)]
+ use __register::{$field::vals::{*}};
+ const { assert!(__register::$field::access.is_write()); }
+ ($value << __register::$field::offset)
+ & __register::$field::mask
}) | *
);
}};
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $value:expr ) => {{
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $value:expr ) => {{
#[allow(unused_imports)]
- use $periph::{*};
- (*$instance).$reg $([$offset])*.write($value);
+ use $($periph)::+ $( :: $reg )* as __register;
+ const { assert!(__register::access.is_write()); }
+
+ $crate::must_be_ral_registers_Inst(&$instance);
+ $crate::__expand_unsafe!($instance, $( $( $offset ,)* )*);
+ #[allow(unused_unsafe)]
+ unsafe { (&raw mut (*$instance.as_ptr())$(. $reg $([$offset])* )*).write_volatile($value) }
}};
}
-/// Modify a RWRegister or UnsafeRWRegister.
-///
-/// # Examples
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // Safely acquire the peripheral instance (will panic if already acquired)
-/// let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-///
-/// // Update the register to ensure bit 3 is set.
-/// modify_reg!(stm32ral::gpio, gpioa, ODR, |reg| reg | (1<<3));
-///
-/// // Write values to specific fields. Unspecified fields are left unchanged.
-/// modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);
-///
-/// // Unsafe access without requiring you to first `take()` the instance
-/// unsafe { modify_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };
-/// # }
-/// ```
-///
-/// To support register arrays, each macro form also supports one or more array indices after the
-/// register. For example, `modify_reg!(stm32ral::gpio, gpioa, ODR[2], |reg| reg | (1<<3));` sets
-/// a high bit in the third register of an `ODR` register array.
-///
-/// # Usage
-/// Like `write_reg!`, this macro can be used in two ways, either with a modification of the entire
-/// register, or by specifying which fields to change and what value to change them to.
-///
-/// In both cases, the first arguments are:
-/// * the path to the peripheral module: `stm32ral::gpio`,
-/// * a reference to the instance of that peripheral: 'gpioa' (anything which dereferences to
-/// `RegisterBlock`, such as `Instance`, `&Instance`, `&RegisterBlock`, or
-/// `*const RegisterBlock`),
-/// * the register (and offset, for arrays) you wish you access: `MODER` (a field on the
-/// `RegisterBlock`).
-///
-/// In the whole-register usage, the final argument is a closure that accepts the current value
-/// of the register and returns the new value to write:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Turn on PA3 without affecting anything else.
-/// modify_reg!(stm32ral::gpio, gpioa, ODR, |reg| reg | (1<<3));
-/// # }
-/// ```
-///
-/// Otherwise, the remaining arguments are `Field: Value` pairs:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Set PA3 to Output, PA4 to Analog, and leave everything else unchanged.
-/// modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: 0b01, MODER4: 0b11);
-/// # }
-/// ```
-///
-/// For fields with annotated values, you can also specify a named value:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // As above, but with named values.
-/// modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: Output, MODER4: Analog);
-/// # }
-/// ```
-///
-/// This macro expands to calling `(*instance).register.write(value)`.
-/// When called with a closure, `(*instance).register.read()` is called, the result
-/// passed in to the closure, and the return value of the closure is used for `value`.
-/// When called with `Field: Value` arguments, the current value is read and then masked
-/// according to the specified fields, and then ORd with the OR of each field value,
-/// each masked and shifted appropriately for the field. The named values are brought into scope
-/// by `use peripheral::register::field::*` for each field. The same constants could just be
-/// specified manually:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // As above, but being explicit about named values.
-/// modify_reg!(stm32ral::gpio, gpioa, MODER, MODER3: stm32ral::gpio::MODER::MODER3::RW::Output,
-/// MODER4: stm32ral::gpio::MODER::MODER4::RW::Analog);
-/// # }
-/// ```
-///
-/// The fully expanded form is equivalent to:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // As above, but expanded.
-/// (*gpioa).MODER.write(
-/// (
-/// // First read the current value...
-/// (*gpioa).MODER.read()
-/// // Then AND it with an appropriate mask...
-/// &
-/// !( stm32ral::gpio::MODER::MODER3::mask | stm32ral::gpio::MODER::MODER4::mask )
-/// )
-/// // Then OR with each field value.
-/// |
-/// ((stm32ral::gpio::MODER::MODER3::RW::Output << stm32ral::gpio::MODER::MODER3::offset)
-/// & stm32ral::gpio::MODER::MODER3::mask)
-/// |
-/// ((stm32ral::gpio::MODER::MODER4::RW::Analog << stm32ral::gpio::MODER::MODER3::offset)
-/// & stm32ral::gpio::MODER::MODER3::mask)
-/// );
-/// # }
-/// ```
-///
-/// # Safety
-/// This macro will require an unsafe function or block when used with an UnsafeRWRegister,
-/// but not if used with RWRegister.
-///
-/// When run in an unsafe context, peripheral instances are directly accessible without requiring
-/// having called `take()` beforehand:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// unsafe { modify_reg!(stm32ral::gpio, GPIOA, MODER, MODER3: Output, MODER4: Analog) };
-/// # }
-/// ```
-/// This works because `GPIOA` is a `*const RegisterBlock` in the `stm32ral::gpio` module;
-/// and the macro brings such constants into scope and then dereferences the provided reference.
+/// Modify a register.
#[macro_export]
macro_rules! modify_reg {
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $( $field:ident : $value:expr ),+ $(,)? ) => {{
- #[allow(unused_imports)]
- use $periph::{*};
- #[allow(unused_imports)]
- (*$instance).$reg $([$offset])*.write(
- ((*$instance).$reg $([$offset])*.read() & !( $({ use $periph::{$reg::$field::mask}; mask }) | * ))
- | $({
- use $periph::{$reg::$field::{W::*, RW::*}};
- ($value << { use $periph::{$reg::$field::offset}; offset })
- & { use $periph::{$reg::$field::mask}; mask }
- }) | *
- );
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $( $field:ident : $value:expr ),+ $(,)? ) => {{
+ $crate::__expand_unsafe!($instance, $( $( $offset ,)* )*);
+ $crate::must_be_ral_registers_Inst(&$instance);
+ use $($periph)::+ $( :: $reg )* as __register;
+ const { assert!(__register::access.is_read_write()); }
+
+ #[allow(unused_unsafe)]
+ unsafe {
+ (&raw mut (*$instance.as_ptr())$(. $reg $([$offset])* )*).write_volatile(
+ (&raw const (*$instance.as_ptr())$(. $reg $([$offset])* )*).read_volatile() & const { !( $(__register::$field::mask) | * ) }
+ | $({
+ #[allow(unused_imports)]
+ use __register::{$field::vals::{*}};
+ const { assert!(__register::$field::access.is_read_write()); }
+ ($value << __register::$field::offset )
+ & __register::$field::mask
+ }) | *
+ )
+ }
}};
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $fn:expr ) => {{
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $fn:expr ) => {{
#[allow(unused_imports)]
- use $periph::{*};
- (*$instance).$reg $([$offset])*.write($fn((*$instance).$reg $([$offset])*.read()));
+ use $($periph)::+ $( :: $reg )* as __register;
+ const { assert!(__register::access.is_read_write()); }
+
+ $crate::__expand_unsafe!($instance, $( $( $offset ,)* )*);
+ $crate::must_be_ral_registers_Inst(&$instance);
+ #[allow(unused_unsafe)]
+ unsafe {
+ (&raw mut (*$instance.as_ptr())$(. $reg $([$offset])* )*).write_volatile(
+ $fn((&raw const (*$instance.as_ptr())$(. $reg $([$offset])* )*).read_volatile())
+ )
+ }
}};
}
-/// Read the value from a RORegister, RWRegister, UnsafeRORegister, or UnsafeRWRegister.
-///
-/// # Examples
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // Safely acquire the peripheral instance (will panic if already acquired)
-/// let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-///
-/// // Read the whole register.
-/// let val = read_reg!(stm32ral::gpio, gpioa, IDR);
-///
-/// // Read one field from the register.
-/// let val = read_reg!(stm32ral::gpio, gpioa, IDR, IDR2);
-///
-/// // Read multiple fields from the register.
-/// let (val1, val2, val3) = read_reg!(stm32ral::gpio, gpioa, IDR, IDR0, IDR1, IDR2);
-///
-/// // Check if one field is equal to a specific value, with the field's named values in scope.
-/// while read_reg!(stm32ral::gpio, gpioa, IDR, IDR2 == High) {}
-///
-/// // Unsafe access without requiring you to first `take()` the instance
-/// let val = unsafe { read_reg!(stm32ral::gpio, GPIOA, IDR) };
-/// # }
-/// ```
-///
-/// To support register arrays, each macro form also supports one or more array indices after the
-/// register. For example, `read_reg!(stm32ral::gpio, gpioa, ODR[2]);` reads from the third
-/// register of an `ODR` register array.
-///
-/// # Usage
-/// Like `write_reg!`, this macro can be used multiple ways, either reading the entire register or
-/// reading a one or more fields from it and potentially performing a comparison with one field.
-///
-/// In all cases, the first arguments are:
-/// * the path to the peripheral module: `stm32ral::gpio`,
-/// * a reference to the instance of that peripheral: 'gpioa' (anything which dereferences to
-/// `RegisterBlock`, such as `Instance`, `&Instance`, `&RegisterBlock`, or
-/// `*const RegisterBlock`),
-/// * the register (and offset, for arrays) you wish to access: `IDR` (a field on the
-/// `RegisterBlock`).
-///
-/// In the whole-register usage, the macro simply returns the register's value:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Read the entire value of GPIOA.IDR into `val`.
-/// let val = read_reg!(stm32ral::gpio, gpioa, IDR);
-/// # }
-/// ```
-///
-/// For reading individual fields, the macro masks and shifts appropriately:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Read just the value of the field GPIOA.IDR2 into `val`.
-/// let val = read_reg!(stm32ral::gpio, gpioa, IDR, IDR2);
-///
-/// // As above, but expanded for exposition:
-/// let val = ((*gpioa).IDR.read() & stm32ral::gpio::IDR::IDR2::mask)
-/// >> stm32ral::gpio::IDR::IDR2::offset;
-///
-/// // Read multiple fields
-/// let (val1, val2) = read_reg!(stm32ral::gpio, gpioa, IDR, IDR2, IDR3);
-///
-/// // As above, but expanded for exposition:
-/// let (val1, val2) = { let val = (*gpioa).IDR.read();
-/// ((val & stm32ral::gpio::IDR::IDR2::mask) >> stm32ral::gpio::IDR::IDR2::offset,
-/// (val & stm32ral::gpio::IDR::IDR3::mask) >> stm32ral::gpio::IDR::IDR3::offset,
-/// )};
-/// # }
-/// ```
-///
-/// For comparing a single field, the macro masks and shifts and then performs the comparison:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// # let rcc = stm32ral::rcc::RCC::take().unwrap();
-/// // Loop while PA2 is High.
-/// while read_reg!(stm32ral::gpio, gpioa, IDR, IDR2 == High) {}
-///
-/// // Only proceed if the clock is not the HSI.
-/// if read_reg!(stm32ral::rcc, rcc, CFGR, SWS != HSI) { }
-///
-/// // Equivalent expansion:
-/// if (((*rcc).CFGR.read() & stm32ral::rcc::CFGR::SWS::mask)
-/// >> stm32ral::rcc::CFGR::SWS::offset) != stm32ral::rcc::CFGR::SWS::R::HSI { }
-/// # }
-/// ```
-///
-/// # Safety
-/// This macro will require an unsafe function or block when used with an UnsafeRWRegister or
-/// UnsafeRORegister, but not if used with RWRegister, or RORegister.
-///
-/// When run in an unsafe context, peripheral instances are directly accessible without requiring
-/// having called `take()` beforehand:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// let val = unsafe { read_reg!(stm32ral::gpio, GPIOA, MODER) };
-/// # }
-/// ```
-/// This works because `GPIOA` is a `*const RegisterBlock` in the `stm32ral::gpio` module;
-/// and the macro brings such constants into scope and then dereferences the provided reference.
+/// Read the value from a register.
#[macro_export]
macro_rules! read_reg {
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $( $field:ident ),+ $(,)? ) => {{
- #[allow(unused_imports)]
- use $periph::{*};
- let val = ((*$instance).$reg $([$offset])*.read());
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $( $field:ident ),+ $(,)? ) => {{
+ let __value = $crate::read_reg!($($periph)::+, $instance, $(. $reg $([$offset])* )*);
+ use $($periph)::+ $( :: $reg )* as __register;
+
( $({
#[allow(unused_imports)]
- use $periph::{$reg::$field::{mask, offset, R::*, RW::*}};
- (val & mask) >> offset
+ use __register::$field::vals::{*};
+ const { assert!(__register::$field::access.is_read()); }
+ (__value & __register::$field::mask) >> __register::$field::offset
}) , *)
}};
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])*, $field:ident $($cmp:tt)* ) => {{
- #[allow(unused_imports)]
- use $periph::{*};
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $field:ident $($cmp:tt)* ) => {{
+ use $($periph)::+ $( :: $reg )* as __register;
+
#[allow(unused_imports)]
- use $periph::{$reg::$field::{mask, offset, R::*, RW::*}};
- (((*$instance).$reg $([$offset])*.read() & mask) >> offset) $($cmp)*
+ use __register::$field::vals::{*};
+ const { assert!(__register::$field::access.is_read()); }
+
+ (($crate::read_reg!($($periph)::+, $instance, $(. $reg $([$offset])* )* ) & __register::$field::mask) >> __register::$field::offset) $($cmp)*
}};
- ( $periph:path, $instance:expr, $reg:ident $([$offset:expr])* ) => {{
+ ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+ ) => {{
#[allow(unused_imports)]
- use $periph::{*};
- ((*$instance).$reg $([$offset])*.read())
+ use $($periph)::+ $( :: $reg )* as __register;
+ const { assert!(__register::access.is_read()); }
+
+ $crate::__expand_unsafe!($instance, $( $( $offset ,)* )*);
+ $crate::must_be_ral_registers_Inst(&$instance);
+ #[allow(unused_unsafe)]
+ unsafe {
+ (&raw const (*$instance.as_ptr())$(. $reg $([$offset])* )*).read_volatile()
+ }
}};
}
-/// Reset a RWRegister, UnsafeRWRegister, WORegister, or UnsafeWORegister to its reset value.
-///
-/// # Examples
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// // Safely acquire the peripheral instance (will panic if already acquired)
-/// let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-///
-/// // Reset PA14 and PA15 to their reset state
-/// reset_reg!(stm32ral::gpio, gpioa, GPIOA, MODER, MODER14, MODER15);
-///
-/// // Reset the entire GPIOA.MODER to its reset state
-/// reset_reg!(stm32ral::gpio, gpioa, GPIOA, MODER);
-/// # }
-/// ```
-///
-/// To support register arrays, each macro form also supports one or more array indices after
-/// the register. For example, `reset_reg!(stm32ral::gpio, gpioa, GPIOA, ODR[2]);` resets the
-/// third register in an `ODR` register array.
-///
-/// # Usage
-/// Like `write_reg!`, this macro can be used in two ways, either resetting the entire register
-/// or just resetting specific fields within in. The register or fields are written with their
-/// reset values.
-///
-/// In both cases, the first arguments are:
-/// * the path to the peripheral module: `stm32ral::gpio`,
-/// * a reference to the instance of that peripheral: 'gpioa' (anything which dereferences to
-/// `RegisterBlock`, such as `Instance`, `&Instance`, `&RegisterBlock`, or
-/// `*const RegisterBlock`),
-/// * the module for the instance of that peripheral: `GPIOA`,
-/// * the register (and offset, for arrays) you wish to access: `MODER` (a field on the
-/// `RegisterBlock`).
-///
-/// In the whole-register usage, that's it:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Reset the entire GPIOA.MODER
-/// reset_reg!(stm32ral::gpio, gpioa, GPIOA, MODER);
-/// # }
-/// ```
-///
-/// Otherwise, the remaining arguments are each field names:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// # let gpioa = stm32ral::gpio::GPIOA::take().unwrap();
-/// // Reset the JTAG pins
-/// reset_reg!(stm32ral::gpio, gpioa, GPIOA, MODER, MODER13, MODER14, MODER15);
-/// reset_reg!(stm32ral::gpio, gpioa, GPIOB, MODER, MODER3, MODER4);
-/// # }
-/// ```
-///
-/// The second form is only available to RWRegister and UnsafeRWRegister, since `.read()` is
-/// not available for WORegister and UnsafeWORegister.
-///
-/// This macro expands to calling `(*$instance).$register.write(value)`, where
-/// `value` is either the register's reset value, or the current read value of the register
-/// masked appropriately and combined with the reset value for each field.
-///
-/// # Safety
-/// This macro will require an unsafe function or block when used with an UnsafeRWRegister or
-/// UnsafeRORegister, but not if used with RWRegister or RORegister.
-///
-/// When run in an unsafe context, peripheral instances are directly accessible without requiring
-/// having called `take()` beforehand:
-/// ```rust,no_run
-/// # use stm32ral::{read_reg, write_reg, modify_reg, reset_reg}; fn main() {
-/// unsafe { reset_reg!(stm32ral::gpio, GPIOA, GPIOA, MODER) };
-/// # }
-/// ```
-/// This works because `GPIOA` is a `*const RegisterBlock` in the `stm32ral::gpio` module;
-/// and the macro brings such constants into scope and then dereferences the provided reference.
-///
-/// Note that the second argument is a `*const` and the third is a path; despite both being written
-/// `GPIOA` they are not the same thing.
+/// Access for registers and fields.
+pub enum Access {
+ /// Read-only access.
+ RO,
+ /// Write-only access.
+ WO,
+ /// Read-write access.
+ RW,
+}
+
+impl Access {
+ /// Returns `true` if the access is read.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_read(self) -> bool {
+ matches!(self, Self::RO | Self::RW)
+ }
+ /// Returns `true` if the access is write.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_write(self) -> bool {
+ matches!(self, Self::WO | Self::RW)
+ }
+ /// Returns `true` if the access is read and write.
+ #[inline(always)]
+ #[must_use]
+ pub const fn is_read_write(self) -> bool {
+ matches!(self, Self::RW)
+ }
+}
+
#[macro_export]
-macro_rules! reset_reg {
- ( $periph:path, $instance:expr, $instancemod:path, $reg:ident $([$offset:expr])*, $( $field:ident ),+ $(,)? ) => {{
- #[allow(unused_imports)]
- use $periph::{*};
- use $periph::{$instancemod::{reset}};
- #[allow(unused_imports)]
- (*$instance).$reg $([$offset])*.write({
- let resetmask: u32 = $({ use $periph::{$reg::$field::mask}; mask }) | *;
- ((*$instance).$reg $([$offset])*.read() & !resetmask) | (reset.$reg & resetmask)
- });
- }};
- ( $periph:path, $instance:expr, $instancemod:path, $reg:ident $([$offset:expr])*) => {{
- #[allow(unused_imports)]
- use $periph::{*};
- use $periph::{$instancemod::{reset}};
- (*$instance).$reg $([$offset])*.write(reset.$reg);
- }};
+macro_rules! register {
+ (
+ $(#[$register_meta:meta])*
+ $vis:vis $register_name:ident <$ty:ty> $register_access:ident [$(
+ $(#[$field_meta:meta])*
+ $field_name:ident start($start_bit:expr) width($width:expr) $field_access:ident {$(
+ $(#[$enum_meta:meta])*
+ $enum_name:ident = $enum_val:expr $(,)?
+ )*}
+ )*]
+ ) => {
+ $(#[$register_meta])*
+ #[allow(non_snake_case, non_upper_case_globals)]
+ $vis mod $register_name {
+ #[doc(hidden)]
+ pub const access: $crate::Access = $crate::Access::$register_access;
+ $(
+ $(#[$field_meta])*
+ #[allow(non_snake_case)]
+ pub mod $field_name {
+ #[doc(hidden)]
+ pub const offset: $ty = $start_bit;
+ #[doc(hidden)]
+ pub const mask: $ty = ((1 << $width) - 1) << $start_bit;
+ #[doc(hidden)]
+ pub const access: $crate::Access = $crate::Access::$field_access;
+
+ #[doc(hidden)]
+ pub mod vals {
+ $(
+ $(#[$enum_meta])*
+ pub const $enum_name: $ty = $enum_val;
+ )*
+ }
+
+ #[doc(inline)]
+ pub use self::vals::*;
+ }
+ )*
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! instances {
+ (unsafe { $(
+ $(#[$doc:meta])*
+ $vis:vis $name:ident<$block:ty> = $base_address:expr;
+ )* }) => {$(
+ $(#[$doc])*
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ $vis const unsafe fn $name() -> $crate::Instance<$block> {
+ // Safety: Macro caller swears that the $base_address points
+ // to the given kind of register block.
+ unsafe { $crate::Instance::new_unchecked($base_address as _) }
+ }
+ )*};
}