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