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}