// 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, //! modify, and write fields in registers. //! //! See the [README](https://github.com/adamgreig/ral-registers/blob/master/README.md) //! for further details. #![no_std] use core::ptr::NonNull; /// Describes a type that acts as a peripheral instance. /// /// If you're not sure how to implement this, try using [`Instance`]. /// /// # Safety /// /// This can only be implemented atop "smart pointer" objects that /// produce the same raw pointer to a static register block. pub unsafe trait Inst {} /// Safety: any shared reference to an instance can similarly provide /// access to the underlying static object. unsafe impl Inst for &'_ I where I: Inst {} /// Safety: see shared reference documentation. If a shared /// reference can provide this access, then so can an exclusive /// reference. unsafe impl Inst for &'_ mut I where I: Inst {} /// A pointer to a peripheral instance. /// /// This is transparently a `NonNull`. Like a `NonNull`, /// the type is `Copy` and `Clone`, so it may be shared throughout /// a single execution context. #[derive(PartialEq, Eq)] #[repr(transparent)] pub struct Instance(NonNull); /// Safety: the constructor's safety contract enforces the /// trait's safety requirements. unsafe impl Inst for Instance {} impl Instance { /// Create an instance that points to a peripheral register block. /// /// # Safety /// /// 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)] #[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) }) } /// Access the inner pointer. #[inline(always)] #[must_use] pub const fn as_ptr(self) -> *mut T { self.0.as_ptr() } } impl Clone for Instance { #[inline(always)] fn clone(&self) -> Self { *self } } impl Copy for Instance {} impl core::fmt::Debug for Instance { 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() } } /// Check expressions that may be unsafe. /// /// 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; )* } }; } #[doc(hidden)] #[allow(non_snake_case)] pub const fn must_be_ral_registers_Inst(_: &I) {} /// Write to a register. #[macro_export] macro_rules! write_reg { ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $( $field:ident : $value:expr ),+ $(,)? ) => {{ use $($periph)::+ $( :: $reg )* as __register; $crate::write_reg!($($periph)::+, $instance, $(. $reg $([$offset])* )*, $({ #[allow(unused_imports)] use __register::{$field::vals::{*}}; const { assert!(__register::$field::access.is_write()); } ($value << __register::$field::offset) & __register::$field::mask }) | * ); }}; ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $value:expr ) => {{ #[allow(unused_imports)] 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 register. #[macro_export] macro_rules! modify_reg { ( $(::)? $($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:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $fn:expr ) => {{ #[allow(unused_imports)] 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 register. #[macro_export] macro_rules! read_reg { ( $(::)? $($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 __register::$field::vals::{*}; const { assert!(__register::$field::access.is_read()); } (__value & __register::$field::mask) >> __register::$field::offset }) , *) }}; ( $(::)? $($periph:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+, $field:ident $($cmp:tt)* ) => {{ use $($periph)::+ $( :: $reg )* as __register; #[allow(unused_imports)] 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:ident)::+, $instance:expr, $( $(.)? $reg:ident $([$offset:expr])* )+ ) => {{ #[allow(unused_imports)] 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() } }}; } /// 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! 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 _) } } )*}; }