diff options
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 809 |
1 files changed, 221 insertions, 588 deletions
@@ -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 _) } + } + )*}; } |
