Skip to main content

kernel/io/
register.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! Macro to define register layout and accessors.
4//!
5//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for
6//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such
7//! type comes with its own field accessors that can return an error if a field's value is invalid.
8//!
9//! Note: most of the items in this module are public so they can be referenced by the macro, but
10//! most are not to be used directly by users. Outside of the `register!` macro itself, the only
11//! items you might want to import from this module are [`WithBase`] and [`Array`].
12//!
13//! # Simple example
14//!
15//! ```no_run
16//! use kernel::io::register;
17//!
18//! register! {
19//!     /// Basic information about the chip.
20//!     pub BOOT_0(u32) @ 0x00000100 {
21//!         /// Vendor ID.
22//!         15:8 vendor_id;
23//!         /// Major revision of the chip.
24//!         7:4 major_revision;
25//!         /// Minor revision of the chip.
26//!         3:0 minor_revision;
27//!     }
28//! }
29//! ```
30//!
31//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
32//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
33//! least significant bits of the type.
34//!
35//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
36//! getter method, which is named after them. They also have setter methods prefixed with `with_`
37//! for runtime values and `with_const_` for constant values. All setters return the updated
38//! register value.
39//!
40//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
41//! `?=>` syntaxes.
42//!
43//! If present, doc comments above register or fields definitions are added to the relevant item
44//! they document (the register type itself, or the field's setter and getter methods).
45//!
46//! Note that multiple registers can be defined in a single `register!` invocation. This can be
47//! useful to group related registers together.
48//!
49//! Here is how the register defined above can be used in code:
50//!
51//!
52//! ```no_run
53//! use kernel::{
54//!     io::{
55//!         register,
56//!         Io,
57//!         IoLoc,
58//!     },
59//!     num::Bounded,
60//! };
61//! # use kernel::io::Mmio;
62//! # register! {
63//! #     pub BOOT_0(u32) @ 0x00000100 {
64//! #         15:8 vendor_id;
65//! #         7:4 major_revision;
66//! #         3:0 minor_revision;
67//! #     }
68//! # }
69//! # fn test(io: &Mmio<0x1000>) {
70//! # fn obtain_vendor_id() -> u8 { 0xff }
71//!
72//! // Read from the register's defined offset (0x100).
73//! let boot0 = io.read(BOOT_0);
74//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
75//!
76//! // Update some fields and write the new value back.
77//! let new_boot0 = boot0
78//!     // Constant values.
79//!     .with_const_major_revision::<3>()
80//!     .with_const_minor_revision::<10>()
81//!     // Runtime value.
82//!     .with_vendor_id(obtain_vendor_id());
83//! io.write_reg(new_boot0);
84//!
85//! // Or, build a new value from zero and write it:
86//! io.write_reg(BOOT_0::zeroed()
87//!     .with_const_major_revision::<3>()
88//!     .with_const_minor_revision::<10>()
89//!     .with_vendor_id(obtain_vendor_id())
90//! );
91//!
92//! // Or, read and update the register in a single step.
93//! io.update(BOOT_0, |r| r
94//!     .with_const_major_revision::<3>()
95//!     .with_const_minor_revision::<10>()
96//!     .with_vendor_id(obtain_vendor_id())
97//! );
98//!
99//! // Constant values can also be built using the const setters.
100//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
101//!     .with_const_major_revision::<3>()
102//!     .with_const_minor_revision::<10>();
103//! # }
104//! ```
105//!
106//! For more extensive documentation about how to define registers, see the
107//! [`register!`](kernel::io::register!) macro.
108
109use core::marker::PhantomData;
110
111use crate::{
112    build_assert::build_assert,
113    io::IoLoc, //
114};
115
116/// Trait implemented by all registers.
117pub trait Register: Sized {
118    /// Backing primitive type of the register.
119    type Storage: Into<Self> + From<Self>;
120
121    /// Start offset of the register.
122    ///
123    /// The interpretation of this offset depends on the type of the register.
124    const OFFSET: usize;
125}
126
127/// Trait implemented by registers with a fixed offset.
128pub trait FixedRegister: Register {}
129
130/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
131/// passing a [`FixedRegister`] value.
132impl<T> IoLoc<T> for ()
133where
134    T: FixedRegister,
135{
136    type IoType = T::Storage;
137
138    #[inline(always)]
139    fn offset(self) -> usize {
140        T::OFFSET
141    }
142}
143
144/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
145/// as an [`IoLoc`].
146impl<T> IoLoc<T> for T
147where
148    T: FixedRegister,
149{
150    type IoType = T::Storage;
151
152    #[inline(always)]
153    fn offset(self) -> usize {
154        T::OFFSET
155    }
156}
157
158/// Location of a fixed register.
159pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
160
161impl<T: FixedRegister> FixedRegisterLoc<T> {
162    /// Returns the location of `T`.
163    #[inline(always)]
164    // We do not implement `Default` so we can be const.
165    #[expect(clippy::new_without_default)]
166    pub const fn new() -> Self {
167        Self(PhantomData)
168    }
169}
170
171impl<T> IoLoc<T> for FixedRegisterLoc<T>
172where
173    T: FixedRegister,
174{
175    type IoType = T::Storage;
176
177    #[inline(always)]
178    fn offset(self) -> usize {
179        T::OFFSET
180    }
181}
182
183/// Trait providing a base address to be added to the offset of a relative register to obtain
184/// its actual offset.
185///
186/// The `T` generic argument is used to distinguish which base to use, in case a type provides
187/// several bases. It is given to the `register!` macro to restrict the use of the register to
188/// implementors of this particular variant.
189pub trait RegisterBase<T> {
190    /// Base address to which register offsets are added.
191    const BASE: usize;
192}
193
194/// Trait implemented by all registers that are relative to a base.
195pub trait WithBase {
196    /// Family of bases applicable to this register.
197    type BaseFamily;
198
199    /// Returns the absolute location of this type when using `B` as its base.
200    #[inline(always)]
201    fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
202    where
203        Self: Register,
204    {
205        RelativeRegisterLoc::new()
206    }
207}
208
209/// Trait implemented by relative registers.
210pub trait RelativeRegister: Register + WithBase {}
211
212/// Location of a relative register.
213///
214/// This can either be an immediately accessible regular [`RelativeRegister`], or a
215/// [`RelativeRegisterArray`] that needs one additional resolution through
216/// [`RelativeRegisterLoc::at`].
217pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>);
218
219impl<T, B> RelativeRegisterLoc<T, B>
220where
221    T: Register + WithBase,
222    B: RegisterBase<T::BaseFamily> + ?Sized,
223{
224    /// Returns the location of a relative register or register array.
225    #[inline(always)]
226    // We do not implement `Default` so we can be const.
227    #[expect(clippy::new_without_default)]
228    pub const fn new() -> Self {
229        Self(PhantomData, PhantomData)
230    }
231
232    // Returns the absolute offset of the relative register using base `B`.
233    //
234    // This is implemented as a private const method so it can be reused by the [`IoLoc`]
235    // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].
236    #[inline]
237    const fn offset(self) -> usize {
238        B::BASE + T::OFFSET
239    }
240}
241
242impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>
243where
244    T: RelativeRegister,
245    B: RegisterBase<T::BaseFamily> + ?Sized,
246{
247    type IoType = T::Storage;
248
249    #[inline(always)]
250    fn offset(self) -> usize {
251        RelativeRegisterLoc::offset(self)
252    }
253}
254
255/// Trait implemented by arrays of registers.
256pub trait RegisterArray: Register {
257    /// Number of elements in the registers array.
258    const SIZE: usize;
259    /// Number of bytes between the start of elements in the registers array.
260    const STRIDE: usize;
261}
262
263/// Location of an array register.
264pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
265
266impl<T: RegisterArray> RegisterArrayLoc<T> {
267    /// Returns the location of register `T` at position `idx`, with build-time validation.
268    #[inline(always)]
269    pub fn new(idx: usize) -> Self {
270        build_assert!(idx < T::SIZE);
271
272        Self(idx, PhantomData)
273    }
274
275    /// Attempts to return the location of register `T` at position `idx`, with runtime validation.
276    #[inline(always)]
277    pub fn try_new(idx: usize) -> Option<Self> {
278        if idx < T::SIZE {
279            Some(Self(idx, PhantomData))
280        } else {
281            None
282        }
283    }
284}
285
286impl<T> IoLoc<T> for RegisterArrayLoc<T>
287where
288    T: RegisterArray,
289{
290    type IoType = T::Storage;
291
292    #[inline(always)]
293    fn offset(self) -> usize {
294        T::OFFSET + self.0 * T::STRIDE
295    }
296}
297
298/// Trait providing location builders for [`RegisterArray`]s.
299pub trait Array {
300    /// Returns the location of the register at position `idx`, with build-time validation.
301    #[inline(always)]
302    fn at(idx: usize) -> RegisterArrayLoc<Self>
303    where
304        Self: RegisterArray,
305    {
306        RegisterArrayLoc::new(idx)
307    }
308
309    /// Returns the location of the register at position `idx`, with runtime validation.
310    #[inline(always)]
311    fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
312    where
313        Self: RegisterArray,
314    {
315        RegisterArrayLoc::try_new(idx)
316    }
317}
318
319/// Trait implemented by arrays of relative registers.
320pub trait RelativeRegisterArray: RegisterArray + WithBase {}
321
322/// Location of a relative array register.
323pub struct RelativeRegisterArrayLoc<
324    T: RelativeRegisterArray,
325    B: RegisterBase<T::BaseFamily> + ?Sized,
326>(RelativeRegisterLoc<T, B>, usize);
327
328impl<T, B> RelativeRegisterArrayLoc<T, B>
329where
330    T: RelativeRegisterArray,
331    B: RegisterBase<T::BaseFamily> + ?Sized,
332{
333    /// Returns the location of register `T` from the base `B` at index `idx`, with build-time
334    /// validation.
335    #[inline(always)]
336    pub fn new(idx: usize) -> Self {
337        build_assert!(idx < T::SIZE);
338
339        Self(RelativeRegisterLoc::new(), idx)
340    }
341
342    /// Attempts to return the location of register `T` from the base `B` at index `idx`, with
343    /// runtime validation.
344    #[inline(always)]
345    pub fn try_new(idx: usize) -> Option<Self> {
346        if idx < T::SIZE {
347            Some(Self(RelativeRegisterLoc::new(), idx))
348        } else {
349            None
350        }
351    }
352}
353
354/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].
355impl<T, B> RelativeRegisterLoc<T, B>
356where
357    T: RelativeRegisterArray,
358    B: RegisterBase<T::BaseFamily> + ?Sized,
359{
360    /// Returns the location of the register at position `idx`, with build-time validation.
361    #[inline(always)]
362    pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
363        RelativeRegisterArrayLoc::new(idx)
364    }
365
366    /// Returns the location of the register at position `idx`, with runtime validation.
367    #[inline(always)]
368    pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
369        RelativeRegisterArrayLoc::try_new(idx)
370    }
371}
372
373impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>
374where
375    T: RelativeRegisterArray,
376    B: RegisterBase<T::BaseFamily> + ?Sized,
377{
378    type IoType = T::Storage;
379
380    #[inline(always)]
381    fn offset(self) -> usize {
382        self.0.offset() + self.1 * T::STRIDE
383    }
384}
385
386/// Trait implemented by items that contain both a register value and the absolute I/O location at
387/// which to write it.
388///
389/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
390pub trait LocatedRegister {
391    /// Register value to write.
392    type Value: Register;
393    /// Full location information at which to write the value.
394    type Location: IoLoc<Self::Value>;
395
396    /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
397    /// operation.
398    fn into_io_op(self) -> (Self::Location, Self::Value);
399}
400
401impl<T> LocatedRegister for T
402where
403    T: FixedRegister,
404{
405    type Location = FixedRegisterLoc<Self::Value>;
406    type Value = T;
407
408    #[inline(always)]
409    fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
410        (FixedRegisterLoc::new(), self)
411    }
412}
413
414/// Defines a dedicated type for a register, including getter and setter methods for its fields and
415/// methods to read and write it from an [`Io`](kernel::io::Io) region.
416///
417/// This documentation focuses on how to declare registers. See the [module-level
418/// documentation](mod@kernel::io::register) for examples of how to access them.
419///
420/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of
421/// registers, and relative arrays of registers.
422///
423/// ## Fixed offset registers
424///
425/// These are the simplest kind of registers. Their location is simply an offset inside the I/O
426/// region. For instance:
427///
428/// ```ignore
429/// register! {
430///     pub FIXED_REG(u16) @ 0x80 {
431///         ...
432///     }
433/// }
434/// ```
435///
436/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
437///
438/// These registers' location can be built simply by referencing their name:
439///
440/// ```no_run
441/// use kernel::{
442///     io::{
443///         register,
444///         Io,
445///     },
446/// };
447/// # use kernel::io::Mmio;
448///
449/// register! {
450///     FIXED_REG(u32) @ 0x100 {
451///         16:8 high_byte;
452///         7:0  low_byte;
453///     }
454/// }
455///
456/// # fn test(io: &Mmio<0x1000>) {
457/// let val = io.read(FIXED_REG);
458///
459/// // Write from an already-existing value.
460/// io.write(FIXED_REG, val.with_low_byte(0xff));
461///
462/// // Create a register value from scratch.
463/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
464///
465/// // The location of fixed offset registers is already contained in their type. Thus, the
466/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
467/// io.write((), val2);
468///
469/// // Or, the single-argument `Io::write_reg` can be used.
470/// io.write_reg(val2);
471/// # }
472///
473/// ```
474///
475/// It is possible to create an alias of an existing register with new field definitions by using
476/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
477/// the context:
478///
479/// ```no_run
480/// use kernel::io::register;
481///
482/// register! {
483///     /// Scratch register.
484///     pub SCRATCH(u32) @ 0x00000200 {
485///         31:0 value;
486///     }
487///
488///     /// Boot status of the firmware.
489///     pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
490///         0:0 completed;
491///     }
492/// }
493/// ```
494///
495/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
496/// its own `completed` field.
497///
498/// ## Relative registers
499///
500/// Relative registers can be instantiated several times at a relative offset of a group of bases.
501/// For instance, imagine the following I/O space:
502///
503/// ```text
504///           +-----------------------------+
505///           |             ...             |
506///           |                             |
507///  0x100--->+------------CPU0-------------+
508///           |                             |
509///  0x110--->+-----------------------------+
510///           |           CPU_CTL           |
511///           +-----------------------------+
512///           |             ...             |
513///           |                             |
514///           |                             |
515///  0x200--->+------------CPU1-------------+
516///           |                             |
517///  0x210--->+-----------------------------+
518///           |           CPU_CTL           |
519///           +-----------------------------+
520///           |             ...             |
521///           +-----------------------------+
522/// ```
523///
524/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
525/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
526/// them twice and would prefer a way to select which one to use from a single definition.
527///
528/// This can be done using the `Base + Offset` syntax when specifying the register's address:
529///
530/// ```ignore
531/// register! {
532///     pub RELATIVE_REG(u32) @ Base + 0x80 {
533///         ...
534///     }
535/// }
536/// ```
537///
538/// This creates a register with an offset of `0x80` from a given base.
539///
540/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
541/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
542/// this register needs to implement `RegisterBase<Base>`.
543///
544/// The location of relative registers can be built using the [`WithBase::of`] method to specify
545/// its base. All relative registers implement [`WithBase`].
546///
547/// Here is the above layout translated into code:
548///
549/// ```no_run
550/// use kernel::{
551///     io::{
552///         register,
553///         register::{
554///             RegisterBase,
555///             WithBase,
556///         },
557///         Io,
558///     },
559/// };
560/// # use kernel::io::Mmio;
561///
562/// // Type used to identify the base.
563/// pub struct CpuCtlBase;
564///
565/// // ZST describing `CPU0`.
566/// struct Cpu0;
567/// impl RegisterBase<CpuCtlBase> for Cpu0 {
568///     const BASE: usize = 0x100;
569/// }
570///
571/// // ZST describing `CPU1`.
572/// struct Cpu1;
573/// impl RegisterBase<CpuCtlBase> for Cpu1 {
574///     const BASE: usize = 0x200;
575/// }
576///
577/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
578/// register! {
579///     /// CPU core control.
580///     pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
581///         0:0 start;
582///     }
583/// }
584///
585/// # fn test(io: Mmio<0x1000>) {
586/// // Read the status of `Cpu0`.
587/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
588///
589/// // Stop `Cpu0`.
590/// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());
591/// # }
592///
593/// // Aliases can also be defined for relative register.
594/// register! {
595///     /// Alias to CPU core control.
596///     pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
597///         /// Start the aliased CPU core.
598///         1:1 alias_start;
599///     }
600/// }
601///
602/// # fn test2(io: Mmio<0x1000>) {
603/// // Start the aliased `CPU0`, leaving its other fields untouched.
604/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
605/// # }
606/// ```
607///
608/// ## Arrays of registers
609///
610/// Some I/O areas contain consecutive registers that share the same field layout. These areas can
611/// be defined as an array of identical registers, allowing them to be accessed by index with
612/// compile-time or runtime bound checking:
613///
614/// ```ignore
615/// register! {
616///     pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
617///         ...
618///     }
619/// }
620/// ```
621///
622/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
623/// register is separated from its neighbor by 4 bytes.
624///
625/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
626/// each other.
627///
628/// A location for a register in a register array is built using the [`Array::at`] trait method.
629/// All arrays of registers implement [`Array`].
630///
631/// ```no_run
632/// use kernel::{
633///     io::{
634///         register,
635///         register::Array,
636///         Io,
637///     },
638/// };
639/// # use kernel::io::Mmio;
640/// # fn get_scratch_idx() -> usize {
641/// #   0x15
642/// # }
643///
644/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
645/// register! {
646///     /// Scratch registers.
647///     pub SCRATCH(u32)[64] @ 0x00000080 {
648///         31:0 value;
649///     }
650/// }
651///
652/// # fn test(io: &Mmio<0x1000>)
653/// #     -> Result<(), Error>{
654/// // Read scratch register 0, i.e. I/O address `0x80`.
655/// let scratch_0 = io.read(SCRATCH::at(0)).value();
656///
657/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
658/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
659///
660/// // This is out of bounds and won't build.
661/// // let scratch_128 = io.read(SCRATCH::at(128)).value();
662///
663/// // Runtime-obtained array index.
664/// let idx = get_scratch_idx();
665/// // Access on a runtime index returns an error if it is out-of-bounds.
666/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
667///
668/// // Alias to a specific register in an array.
669/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
670/// register! {
671///     /// Firmware exit status code.
672///     pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
673///         7:0 status;
674///     }
675/// }
676///
677/// let status = io.read(FIRMWARE_STATUS).status();
678///
679/// // Non-contiguous register arrays can be defined by adding a stride parameter.
680/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
681/// // registers of the two declarations below are interleaved.
682/// register! {
683///     /// Scratch registers bank 0.
684///     pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
685///         31:0 value;
686///     }
687///
688///     /// Scratch registers bank 1.
689///     pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
690///         31:0 value;
691///     }
692/// }
693/// # Ok(())
694/// # }
695/// ```
696///
697/// ## Relative arrays of registers
698///
699/// Combining the two features described in the sections above, arrays of registers accessible from
700/// a base can also be defined:
701///
702/// ```ignore
703/// register! {
704///     pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
705///         ...
706///     }
707/// }
708/// ```
709///
710/// Like relative registers, they implement the [`WithBase`] trait. However the return value of
711/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the
712/// [`at`](RelativeRegisterLoc::at) method.
713///
714/// ```no_run
715/// use kernel::{
716///     io::{
717///         register,
718///         register::{
719///             RegisterBase,
720///             WithBase,
721///         },
722///         Io,
723///     },
724/// };
725/// # use kernel::io::Mmio;
726/// # fn get_scratch_idx() -> usize {
727/// #   0x15
728/// # }
729///
730/// // Type used as parameter of `RegisterBase` to specify the base.
731/// pub struct CpuCtlBase;
732///
733/// // ZST describing `CPU0`.
734/// struct Cpu0;
735/// impl RegisterBase<CpuCtlBase> for Cpu0 {
736///     const BASE: usize = 0x100;
737/// }
738///
739/// // ZST describing `CPU1`.
740/// struct Cpu1;
741/// impl RegisterBase<CpuCtlBase> for Cpu1 {
742///     const BASE: usize = 0x200;
743/// }
744///
745/// // 64 per-cpu scratch registers, arranged as a contiguous array.
746/// register! {
747///     /// Per-CPU scratch registers.
748///     pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
749///         31:0 value;
750///     }
751/// }
752///
753/// # fn test(io: &Mmio<0x1000>) -> Result<(), Error> {
754/// // Read scratch register 0 of CPU0.
755/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
756///
757/// // Write the retrieved value into scratch register 15 of CPU1.
758/// io.write(WithBase::of::<Cpu1>().at(15), scratch);
759///
760/// // This won't build.
761/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();
762///
763/// // Runtime-obtained array index.
764/// let scratch_idx = get_scratch_idx();
765/// // Access on a runtime index returns an error if it is out-of-bounds.
766/// let cpu0_scratch = io.read(
767///     CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
768/// ).value();
769/// # Ok(())
770/// # }
771///
772/// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
773/// register! {
774///     /// Per-CPU firmware exit status code.
775///     pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
776///         7:0 status;
777///     }
778/// }
779///
780/// // Non-contiguous relative register arrays can be defined by adding a stride parameter.
781/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
782/// // registers of the two declarations below are interleaved.
783/// register! {
784///     /// Scratch registers bank 0.
785///     pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
786///         31:0 value;
787///     }
788///
789///     /// Scratch registers bank 1.
790///     pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
791///         31:0 value;
792///     }
793/// }
794///
795/// # fn test2(io: &Mmio<0x1000>) -> Result<(), Error> {
796/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
797/// # Ok(())
798/// # }
799/// ```
800#[macro_export]
801macro_rules! register {
802    // Entry point for the macro, allowing multiple registers to be defined in one call.
803    // It matches all possible register declaration patterns to dispatch them to corresponding
804    // `@reg` rule that defines a single register.
805    (
806        $(
807            $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
808                $([ $size:expr $(, stride = $stride:expr)? ])?
809                $(@ $($base:ident +)? $offset:literal)?
810                $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
811            { $($fields:tt)* }
812        )*
813    ) => {
814        $(
815        $crate::register!(
816            @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
817                $(@ $($base +)? $offset)?
818                $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
819            { $($fields)* }
820        );
821        )*
822    };
823
824    // All the rules below are private helpers.
825
826    // Creates a register at a fixed offset of the MMIO space.
827    (
828        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
829            { $($fields:tt)* }
830    ) => {
831        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
832        $crate::register!(@io_base $name($storage) @ $offset);
833        $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
834    };
835
836    // Creates an alias register of fixed offset register `alias` with its own fields.
837    (
838        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
839            { $($fields:tt)* }
840    ) => {
841        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
842        $crate::register!(
843            @io_base $name($storage) @
844            <$alias as $crate::io::register::Register>::OFFSET
845        );
846        $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
847    };
848
849    // Creates a register at a relative offset from a base address provider.
850    (
851        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
852            { $($fields:tt)* }
853    ) => {
854        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
855        $crate::register!(@io_base $name($storage) @ $offset);
856        $crate::register!(@io_relative $vis $name($storage) @ $base);
857    };
858
859    // Creates an alias register of relative offset register `alias` with its own fields.
860    (
861        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
862            { $($fields:tt)* }
863    ) => {
864        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
865        $crate::register!(
866            @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET
867        );
868        $crate::register!(@io_relative $vis $name($storage) @ $base);
869    };
870
871    // Creates an array of registers at a fixed offset of the MMIO space.
872    (
873        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
874            [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
875    ) => {
876        $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
877
878        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
879        $crate::register!(@io_base $name($storage) @ $offset);
880        $crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]);
881    };
882
883    // Shortcut for contiguous array of registers (stride == size of element).
884    (
885        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
886            { $($fields:tt)* }
887    ) => {
888        $crate::register!(
889            $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
890                @ $offset { $($fields)* }
891        );
892    };
893
894    // Creates an alias of register `idx` of array of registers `alias` with its own fields.
895    (
896        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
897            { $($fields:tt)* }
898    ) => {
899        $crate::build_assert::static_assert!(
900            $idx < <$alias as $crate::io::register::RegisterArray>::SIZE
901        );
902
903        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
904        $crate::register!(
905            @io_base $name($storage) @
906            <$alias as $crate::io::register::Register>::OFFSET
907                + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
908        );
909        $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage));
910    };
911
912    // Creates an array of registers at a relative offset from a base address provider.
913    (
914        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
915            [ $size:expr, stride = $stride:expr ]
916            @ $base:ident + $offset:literal { $($fields:tt)* }
917    ) => {
918        $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
919
920        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
921        $crate::register!(@io_base $name($storage) @ $offset);
922        $crate::register!(
923            @io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset
924        );
925    };
926
927    // Shortcut for contiguous array of relative registers (stride == size of element).
928    (
929        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
930            @ $base:ident + $offset:literal { $($fields:tt)* }
931    ) => {
932        $crate::register!(
933            $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ]
934                @ $base + $offset { $($fields)* }
935        );
936    };
937
938    // Creates an alias of register `idx` of relative array of registers `alias` with its own
939    // fields.
940    (
941        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
942            => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
943    ) => {
944        $crate::build_assert::static_assert!(
945            $idx < <$alias as $crate::io::register::RegisterArray>::SIZE
946        );
947
948        $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
949        $crate::register!(
950            @io_base $name($storage) @
951                <$alias as $crate::io::register::Register>::OFFSET +
952                $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
953        );
954        $crate::register!(@io_relative $vis $name($storage) @ $base);
955    };
956
957    // Generates the bitfield for the register.
958    //
959    // `#[allow(non_camel_case_types)]` is added since register names typically use
960    // `SCREAMING_CASE`.
961    (
962        @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
963    ) => {
964        $crate::bitfield!(
965            #[allow(non_camel_case_types)]
966            $(#[$attr])* $vis struct $name($storage) { $($fields)* }
967        );
968    };
969
970    // Implementations shared by all registers types.
971    (@io_base $name:ident($storage:ty) @ $offset:expr) => {
972        impl $crate::io::register::Register for $name {
973            type Storage = $storage;
974
975            const OFFSET: usize = $offset;
976        }
977    };
978
979    // Implementations of fixed registers.
980    (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => {
981        impl $crate::io::register::FixedRegister for $name {}
982
983        $(#[$attr])*
984        $vis const $name: $crate::io::register::FixedRegisterLoc<$name> =
985            $crate::io::register::FixedRegisterLoc::<$name>::new();
986    };
987
988    // Implementations of relative registers.
989    (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => {
990        impl $crate::io::register::WithBase for $name {
991            type BaseFamily = $base;
992        }
993
994        impl $crate::io::register::RelativeRegister for $name {}
995    };
996
997    // Implementations of register arrays.
998    (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => {
999        impl $crate::io::register::Array for $name {}
1000
1001        impl $crate::io::register::RegisterArray for $name {
1002            const SIZE: usize = $size;
1003            const STRIDE: usize = $stride;
1004        }
1005    };
1006
1007    // Implementations of relative array registers.
1008    (
1009        @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]
1010            @ $base:ident + $offset:literal
1011    ) => {
1012        impl $crate::io::register::WithBase for $name {
1013            type BaseFamily = $base;
1014        }
1015
1016        impl $crate::io::register::RegisterArray for $name {
1017            const SIZE: usize = $size;
1018            const STRIDE: usize = $stride;
1019        }
1020
1021        impl $crate::io::register::RelativeRegisterArray for $name {}
1022    };
1023}