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