Skip to main content

mmio/
register.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Register abstractions for MMIO.
6//!
7//! This module provides the [`Register`] trait which simplifies interacting with
8//! bit-level registers at a fixed offset within an MMIO region.
9
10use crate::{Mmio, MmioExt, MmioOperand};
11
12use std::marker::PhantomData;
13
14/// A proxy struct for interacting with a specific `Register` over a specific `Mmio`.
15///
16/// This provides a more ergonomic API than calling methods directly on the `MmioExt`
17/// trait.
18pub struct RegisterProxy<'a, M: Mmio + ?Sized, R: Register> {
19    mmio: &'a M,
20    _phantom: PhantomData<R>,
21}
22
23impl<'a, M: Mmio + ?Sized, R: Register> RegisterProxy<'a, M, R> {
24    /// Creates a new proxy.
25    pub fn new(mmio: &'a M) -> Self {
26        Self { mmio, _phantom: PhantomData }
27    }
28}
29
30impl<'a, M: Mmio + ?Sized, R: ReadableRegister> RegisterProxy<'a, M, R> {
31    /// Reads the register from MMIO.
32    pub fn read(&self) -> R {
33        R::read(self.mmio)
34    }
35}
36
37/// A mutable proxy struct for interacting with a specific `Register` over a specific `Mmio`.
38pub struct RegisterProxyMut<'a, M: Mmio + ?Sized, R: Register> {
39    mmio: &'a mut M,
40    _phantom: PhantomData<R>,
41}
42
43impl<'a, M: Mmio + ?Sized, R: Register> RegisterProxyMut<'a, M, R> {
44    /// Creates a new proxy.
45    pub fn new(mmio: &'a mut M) -> Self {
46        Self { mmio, _phantom: PhantomData }
47    }
48}
49
50impl<'a, M: Mmio + ?Sized, R: ReadableRegister> RegisterProxyMut<'a, M, R> {
51    /// Reads the register from MMIO.
52    pub fn read(&self) -> R {
53        R::read(self.mmio)
54    }
55}
56
57impl<'a, M: Mmio + ?Sized, R: WritableRegister> RegisterProxyMut<'a, M, R> {
58    /// Writes the register to MMIO.
59    pub fn write(&mut self, val: R) {
60        val.write(self.mmio)
61    }
62}
63
64impl<'a, M: Mmio + ?Sized, R: ReadableRegister + WritableRegister> RegisterProxyMut<'a, M, R> {
65    /// Reads, modifies with the closure, and writes the register back to MMIO.
66    pub fn update<F: FnOnce(&mut R)>(&mut self, f: F) {
67        let mut reg = self.read();
68        f(&mut reg);
69        self.write(reg);
70    }
71}
72
73/// A proxy struct for interacting with a specific `IndexedRegister` over a specific `Mmio`.
74pub struct IndexedRegisterProxy<'a, M: Mmio + ?Sized, R: IndexedRegister> {
75    mmio: &'a M,
76    _phantom: PhantomData<R>,
77}
78
79impl<'a, M: Mmio + ?Sized, R: IndexedRegister> IndexedRegisterProxy<'a, M, R> {
80    /// Creates a new proxy.
81    pub fn new(mmio: &'a M) -> Self {
82        Self { mmio, _phantom: PhantomData }
83    }
84}
85
86impl<'a, M: Mmio + ?Sized, R: ReadableIndexedRegister> IndexedRegisterProxy<'a, M, R> {
87    /// Reads the register from MMIO at the specified index.
88    pub fn read(&self, index: usize) -> R {
89        R::read_index(self.mmio, index)
90    }
91}
92
93/// A mutable proxy struct for interacting with a specific `IndexedRegister` over a specific `Mmio`.
94pub struct IndexedRegisterProxyMut<'a, M: Mmio + ?Sized, R: IndexedRegister> {
95    mmio: &'a mut M,
96    _phantom: PhantomData<R>,
97}
98
99impl<'a, M: Mmio + ?Sized, R: IndexedRegister> IndexedRegisterProxyMut<'a, M, R> {
100    /// Creates a new proxy.
101    pub fn new(mmio: &'a mut M) -> Self {
102        Self { mmio, _phantom: PhantomData }
103    }
104}
105
106impl<'a, M: Mmio + ?Sized, R: ReadableIndexedRegister> IndexedRegisterProxyMut<'a, M, R> {
107    /// Reads the register from MMIO at the specified index.
108    pub fn read(&self, index: usize) -> R {
109        R::read_index(self.mmio, index)
110    }
111}
112
113impl<'a, M: Mmio + ?Sized, R: WritableIndexedRegister> IndexedRegisterProxyMut<'a, M, R> {
114    /// Writes the register to MMIO at the specified index.
115    pub fn write(&mut self, index: usize, val: R) {
116        val.write_index(self.mmio, index)
117    }
118}
119
120impl<'a, M: Mmio + ?Sized, R: ReadableIndexedRegister + WritableIndexedRegister>
121    IndexedRegisterProxyMut<'a, M, R>
122{
123    /// Reads, modifies with the closure, and writes the register back to MMIO at the specified index.
124    pub fn update<F: FnOnce(&mut R)>(&mut self, index: usize, f: F) {
125        let mut reg = self.read(index);
126        f(&mut reg);
127        self.write(index, reg);
128    }
129}
130
131/// A trait that allows a register to define its default read proxy type.
132pub trait RegisterReadAccess<M: Mmio + ?Sized> {
133    /// The proxy type used to read this register.
134    type ReadProxy<'a>
135    where
136        M: 'a;
137
138    /// Creates a new read proxy for this register using the provided MMIO region.
139    fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a>;
140}
141
142/// A trait that allows a register to define its default write proxy type.
143pub trait RegisterWriteAccess<M: Mmio + ?Sized> {
144    /// The proxy type used to write this register.
145    type WriteProxy<'a>
146    where
147        M: 'a;
148
149    /// Creates a new write proxy for this register using the provided MMIO region.
150    fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a>;
151}
152
153/// A trait for types representing a register at a fixed offset.
154///
155/// Types implementing this trait are typically wrappers around fundamental
156/// types (u8, u16, u32, u64) that provide bit-level access to fields within
157/// the register.
158pub trait Register: Sized {
159    /// The underlying integer type (e.g., u32) that holds the register bits.
160    type Value: MmioOperand;
161
162    /// The byte offset of this register within the MMIO region.
163    const OFFSET: usize;
164
165    /// Initializes the register type with a raw value, typically after reading from MMIO.
166    fn from_raw(value: Self::Value) -> Self;
167
168    /// Converts the register type back to its raw bits, typically before writing to MMIO.
169    fn to_raw(&self) -> Self::Value;
170}
171
172/// A trait for registers that can be read.
173pub trait ReadableRegister: Register {
174    /// Loads the register's value from the MMIO region at its defined `OFFSET`.
175    fn read<M: Mmio + ?Sized>(mmio: &M) -> Self {
176        Self::from_raw(mmio.load::<Self::Value>(Self::OFFSET))
177    }
178}
179
180/// A trait for registers that can be written.
181pub trait WritableRegister: Register {
182    /// Stores the register's current state into the MMIO region at its defined `OFFSET`.
183    fn write<M: Mmio + ?Sized>(&self, mmio: &mut M) {
184        mmio.store::<Self::Value>(Self::OFFSET, self.to_raw())
185    }
186}
187
188/// A trait for types representing an array (block) of registers in MMIO.
189///
190/// Indexed registers are located at a base offset and repeat at a fixed stride.
191/// They are typically accessed using a zero-based index.
192///
193/// # Examples
194///
195/// ```rust
196/// register! {
197///     #[indexed_register(offset = 0x100, stride = 4, count = 16, mode = RW)]
198///     pub struct DataReg(u32) {
199///         pub value, set_value: 31, 0;
200///     }
201/// }
202/// ```
203pub trait IndexedRegister: Sized {
204    /// The underlying integer type (e.g., u32) that holds the register bits.
205    type Value: MmioOperand;
206
207    /// The byte offset of the first element in the register array.
208    const BASE_OFFSET: usize;
209
210    /// The byte distance between successive elements in the register array.
211    const STRIDE: usize;
212
213    /// The maximum number of valid elements in the register array.
214    const COUNT: usize;
215
216    /// Initializes the register type with a raw value, typically after reading from MMIO.
217    fn from_raw(value: Self::Value) -> Self;
218
219    /// Converts the register type back to its raw bits, typically before writing to MMIO.
220    fn to_raw(&self) -> Self::Value;
221}
222
223/// A trait for indexed registers that can be read.
224pub trait ReadableIndexedRegister: IndexedRegister {
225    /// Loads the register value at `index` from the MMIO region.
226    ///
227    /// The offset is calculated as `BASE_OFFSET + (index * STRIDE)`.
228    ///
229    /// # Panics
230    ///
231    /// This method will panic if `index` is greater than or equal to `COUNT`.
232    fn read_index<M: Mmio + ?Sized>(mmio: &M, index: usize) -> Self {
233        assert!(index < Self::COUNT, "Register index out of bounds");
234        let offset = Self::BASE_OFFSET + (index * Self::STRIDE);
235        Self::from_raw(mmio.load::<Self::Value>(offset))
236    }
237}
238
239/// A trait for indexed registers that can be written.
240pub trait WritableIndexedRegister: IndexedRegister {
241    /// Stores the register's state at `index` into the MMIO region.
242    ///
243    /// The offset is calculated as `BASE_OFFSET + (index * STRIDE)`.
244    ///
245    /// # Panics
246    ///
247    /// This method will panic if `index` is greater than or equal to `COUNT`.
248    fn write_index<M: Mmio + ?Sized>(&self, mmio: &mut M, index: usize) {
249        assert!(index < Self::COUNT, "Register index out of bounds");
250        let offset = Self::BASE_OFFSET + (index * Self::STRIDE);
251        mmio.store::<Self::Value>(offset, self.to_raw())
252    }
253}
254
255/// A macro for defining a [`Register`] or [`IndexedRegister`] and its bitfields.
256///
257/// This macro generates a bitfield struct that implements the [`Register`] or [`IndexedRegister`],
258/// [`RegisterReadAccess`] and [`RegisterWriteAccess`] traits. The access mode (RO, WO, RW)
259/// determines which of the [`ReadableRegister`]/[`ReadableIndexedRegister`] and
260/// [`WritableRegister`]/[`WritableIndexedRegister`] traits are implemented.
261///
262/// Access modes:
263/// * `RO`: Read-Only.
264/// * `WO`: Write-Only.
265/// * `RW`: Read-Write.
266///
267/// # Examples
268///
269/// ```rust
270/// register! {
271///     #[register(offset = 0x10, mode = RW)]
272///     pub struct StatusReg(u32) {
273///         pub enabled, set_enabled: 0, 0;
274///         pub error, _: 1, 1;
275///         pub value, set_value: 15, 8;
276///     }
277/// }
278/// ```
279///
280/// Full-width register (no bitfields):
281///
282/// ```rust
283/// register! {
284///     #[register(offset = 0x14, mode = RW)]
285///     pub struct ControlReg(u32);
286/// }
287/// ```
288///
289/// Indexed register:
290///
291/// ```rust
292/// register! {
293///     #[indexed_register(offset = 0x100, stride = 4, count = 16, mode = RO)]
294///     pub struct DataReg(u32) {
295///         pub value, _: 31, 0;
296///     }
297/// }
298/// ```
299///
300/// Full-width indexed register:
301///
302/// ```rust
303/// register! {
304///     #[indexed_register(offset = 0x200, stride = 4, count = 8, mode = RW)]
305///     pub struct ValueReg(u32);
306/// }
307/// ```
308///
309/// NOTE: If you use this macro, you must add a dependency on the bitfield crate. (At the time of
310/// writing, it isn't easy to avoid this.)
311#[macro_export]
312macro_rules! register {
313    // Multiple definitions support (with block)
314    (
315        #[register(offset = $offset:expr, mode = $mode:ident)]
316        $(#[$attr:meta])*
317        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
318        $($tail:tt)+
319    ) => {
320        $crate::register! { #[register(offset = $offset, mode = $mode)] $(#[$attr])* pub struct $name($val_type) { $($field_spec)* } }
321        $crate::register! { $($tail)+ }
322    };
323
324    // Multiple definitions support (full-width)
325    (
326        #[register(offset = $offset:expr, mode = $mode:ident)]
327        $(#[$attr:meta])*
328        pub struct $name:ident ($val_type:ty) ;
329        $($tail:tt)+
330    ) => {
331        $crate::register! { #[register(offset = $offset, mode = $mode)] $(#[$attr])* pub struct $name($val_type) ; }
332        $crate::register! { $($tail)+ }
333    };
334
335    // Multiple definitions support (with block, indexed)
336    (
337        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = $mode:ident)]
338        $(#[$attr:meta])*
339        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
340        $($tail:tt)+
341    ) => {
342        $crate::register! { #[indexed_register(offset = $base_offset, stride = $stride, count = $count, mode = $mode)] $(#[$attr])* pub struct $name($val_type) { $($field_spec)* } }
343        $crate::register! { $($tail)+ }
344    };
345
346    // Multiple definitions support (full-width, indexed)
347    (
348        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = $mode:ident)]
349        $(#[$attr:meta])*
350        pub struct $name:ident ($val_type:ty) ;
351        $($tail:tt)+
352    ) => {
353        $crate::register! { #[indexed_register(offset = $base_offset, stride = $stride, count = $count, mode = $mode)] $(#[$attr])* pub struct $name($val_type) ; }
354        $crate::register! { $($tail)+ }
355    };
356
357    // Read-Only with block
358    (
359        #[register(offset = $offset:expr, mode = RO)]
360        $(#[$attr:meta])*
361        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
362    ) => {
363        ::bitfield::bitfield! {
364            $(#[$attr])*
365            #[derive(Copy, Clone, PartialEq, Eq, Default)]
366            pub struct $name($val_type);
367            impl Debug;
368            $($field_spec)*
369        }
370
371        impl $crate::Register for $name {
372            type Value = $val_type;
373            const OFFSET: usize = $offset;
374            fn from_raw(value: Self::Value) -> Self { $name(value) }
375            fn to_raw(&self) -> Self::Value { self.0 }
376        }
377        impl $crate::ReadableRegister for $name {}
378
379        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
380            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
381            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
382        }
383        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
384            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
385            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
386        }
387    };
388
389    // Write-Only with block
390    (
391        #[register(offset = $offset:expr, mode = WO)]
392        $(#[$attr:meta])*
393        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
394    ) => {
395        ::bitfield::bitfield! {
396            $(#[$attr])*
397            #[derive(Copy, Clone, PartialEq, Eq, Default)]
398            pub struct $name($val_type);
399            impl Debug;
400            $($field_spec)*
401        }
402
403        impl $crate::Register for $name {
404            type Value = $val_type;
405            const OFFSET: usize = $offset;
406            fn from_raw(value: Self::Value) -> Self { $name(value) }
407            fn to_raw(&self) -> Self::Value { self.0 }
408        }
409        impl $crate::WritableRegister for $name {}
410
411        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
412            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
413            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
414        }
415        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
416            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
417            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
418        }
419    };
420
421    // Read-Write with block
422    (
423        #[register(offset = $offset:expr, mode = RW)]
424        $(#[$attr:meta])*
425        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
426    ) => {
427        ::bitfield::bitfield! {
428            $(#[$attr])*
429            #[derive(Copy, Clone, PartialEq, Eq, Default)]
430            pub struct $name($val_type);
431            impl Debug;
432            $($field_spec)*
433        }
434
435        impl $crate::Register for $name {
436            type Value = $val_type;
437            const OFFSET: usize = $offset;
438            fn from_raw(value: Self::Value) -> Self { $name(value) }
439            fn to_raw(&self) -> Self::Value { self.0 }
440        }
441        impl $crate::ReadableRegister for $name {}
442        impl $crate::WritableRegister for $name {}
443
444        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
445            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
446            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
447        }
448        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
449            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
450            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
451        }
452    };
453
454    // Read-Only full-width
455    (
456        #[register(offset = $offset:expr, mode = RO)]
457        $(#[$attr:meta])*
458        pub struct $name:ident ($val_type:ty) ;
459    ) => {
460        ::bitfield::bitfield! {
461            $(#[$attr])*
462            #[derive(Copy, Clone, PartialEq, Eq, Default)]
463            pub struct $name($val_type);
464            impl Debug;
465        }
466
467        impl $name {
468            pub fn value(&self) -> $val_type { self.0 }
469        }
470
471        impl $crate::Register for $name {
472            type Value = $val_type;
473            const OFFSET: usize = $offset;
474            fn from_raw(value: Self::Value) -> Self { $name(value) }
475            fn to_raw(&self) -> Self::Value { self.0 }
476        }
477        impl $crate::ReadableRegister for $name {}
478
479        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
480            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
481            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
482        }
483        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
484            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
485            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
486        }
487    };
488
489    // Write-Only full-width
490    (
491        #[register(offset = $offset:expr, mode = WO)]
492        $(#[$attr:meta])*
493        pub struct $name:ident ($val_type:ty) ;
494    ) => {
495        ::bitfield::bitfield! {
496            $(#[$attr])*
497            #[derive(Copy, Clone, PartialEq, Eq, Default)]
498            pub struct $name($val_type);
499            impl Debug;
500        }
501
502        impl $name {
503            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
504        }
505
506        impl $crate::Register for $name {
507            type Value = $val_type;
508            const OFFSET: usize = $offset;
509            fn from_raw(value: Self::Value) -> Self { $name(value) }
510            fn to_raw(&self) -> Self::Value { self.0 }
511        }
512        impl $crate::WritableRegister for $name {}
513
514        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
515            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
516            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
517        }
518        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
519            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
520            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
521        }
522    };
523
524    // Read-Write full-width
525    (
526        #[register(offset = $offset:expr, mode = RW)]
527        $(#[$attr:meta])*
528        pub struct $name:ident ($val_type:ty) ;
529    ) => {
530        ::bitfield::bitfield! {
531            $(#[$attr])*
532            #[derive(Copy, Clone, PartialEq, Eq, Default)]
533            pub struct $name($val_type);
534            impl Debug;
535        }
536
537        impl $name {
538            pub fn value(&self) -> $val_type { self.0 }
539            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
540        }
541
542        impl $crate::Register for $name {
543            type Value = $val_type;
544            const OFFSET: usize = $offset;
545            fn from_raw(value: Self::Value) -> Self { $name(value) }
546            fn to_raw(&self) -> Self::Value { self.0 }
547        }
548        impl $crate::ReadableRegister for $name {}
549        impl $crate::WritableRegister for $name {}
550
551        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
552            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
553            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
554        }
555        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
556            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
557            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
558        }
559    };
560
561    // Read-Only with block, indexed
562    (
563        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = RO)]
564        $(#[$attr:meta])*
565        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
566    ) => {
567        ::bitfield::bitfield! {
568            $(#[$attr])*
569            #[derive(Copy, Clone, PartialEq, Eq, Default)]
570            pub struct $name($val_type);
571            impl Debug;
572            $($field_spec)*
573        }
574
575        impl $crate::IndexedRegister for $name {
576            type Value = $val_type;
577            const BASE_OFFSET: usize = $base_offset;
578            const STRIDE: usize = $stride;
579            const COUNT: usize = $count;
580            fn from_raw(value: Self::Value) -> Self { $name(value) }
581            fn to_raw(&self) -> Self::Value { self.0 }
582        }
583        impl $crate::ReadableIndexedRegister for $name {}
584
585        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
586            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
587            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
588        }
589        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
590            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
591            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
592        }
593    };
594
595    // Write-Only with block, indexed
596    (
597        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = WO)]
598        $(#[$attr:meta])*
599        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
600    ) => {
601        ::bitfield::bitfield! {
602            $(#[$attr])*
603            #[derive(Copy, Clone, PartialEq, Eq, Default)]
604            pub struct $name($val_type);
605            impl Debug;
606            $($field_spec)*
607        }
608
609        impl $crate::IndexedRegister for $name {
610            type Value = $val_type;
611            const BASE_OFFSET: usize = $base_offset;
612            const STRIDE: usize = $stride;
613            const COUNT: usize = $count;
614            fn from_raw(value: Self::Value) -> Self { $name(value) }
615            fn to_raw(&self) -> Self::Value { self.0 }
616        }
617        impl $crate::WritableIndexedRegister for $name {}
618
619        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
620            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
621            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
622        }
623        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
624            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
625            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
626        }
627    };
628
629    // Read-Write with block, indexed
630    (
631        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = RW)]
632        $(#[$attr:meta])*
633        pub struct $name:ident ($val_type:ty) { $($field_spec:tt)* }
634    ) => {
635        ::bitfield::bitfield! {
636            $(#[$attr])*
637            #[derive(Copy, Clone, PartialEq, Eq, Default)]
638            pub struct $name($val_type);
639            impl Debug;
640            $($field_spec)*
641        }
642
643        impl $crate::IndexedRegister for $name {
644            type Value = $val_type;
645            const BASE_OFFSET: usize = $base_offset;
646            const STRIDE: usize = $stride;
647            const COUNT: usize = $count;
648            fn from_raw(value: Self::Value) -> Self { $name(value) }
649            fn to_raw(&self) -> Self::Value { self.0 }
650        }
651        impl $crate::ReadableIndexedRegister for $name {}
652        impl $crate::WritableIndexedRegister for $name {}
653
654        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
655            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
656            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
657        }
658        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
659            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
660            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
661        }
662    };
663
664    // Read-Only full-width, indexed
665    (
666        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = RO)]
667        $(#[$attr:meta])*
668        pub struct $name:ident ($val_type:ty) ;
669    ) => {
670        ::bitfield::bitfield! {
671            $(#[$attr])*
672            #[derive(Copy, Clone, PartialEq, Eq, Default)]
673            pub struct $name($val_type);
674            impl Debug;
675        }
676
677        impl $name {
678            pub fn value(&self) -> $val_type { self.0 }
679        }
680
681        impl $crate::IndexedRegister for $name {
682            type Value = $val_type;
683            const BASE_OFFSET: usize = $base_offset;
684            const STRIDE: usize = $stride;
685            const COUNT: usize = $count;
686            fn from_raw(value: Self::Value) -> Self { $name(value) }
687            fn to_raw(&self) -> Self::Value { self.0 }
688        }
689        impl $crate::ReadableIndexedRegister for $name {}
690
691        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
692            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
693            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
694        }
695        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
696            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
697            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
698        }
699    };
700
701    // Write-Only full-width, indexed
702    (
703        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = WO)]
704        $(#[$attr:meta])*
705        pub struct $name:ident ($val_type:ty) ;
706    ) => {
707        ::bitfield::bitfield! {
708            $(#[$attr])*
709            #[derive(Copy, Clone, PartialEq, Eq, Default)]
710            pub struct $name($val_type);
711            impl Debug;
712        }
713
714        impl $name {
715            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
716        }
717
718        impl $crate::IndexedRegister for $name {
719            type Value = $val_type;
720            const BASE_OFFSET: usize = $base_offset;
721            const STRIDE: usize = $stride;
722            const COUNT: usize = $count;
723            fn from_raw(value: Self::Value) -> Self { $name(value) }
724            fn to_raw(&self) -> Self::Value { self.0 }
725        }
726        impl $crate::WritableIndexedRegister for $name {}
727
728        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
729            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
730            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
731        }
732        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
733            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
734            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
735        }
736    };
737
738    // Read-Write full-width, indexed
739    (
740        #[indexed_register(offset = $base_offset:expr, stride = $stride:expr, count = $count:expr, mode = RW)]
741        $(#[$attr:meta])*
742        pub struct $name:ident ($val_type:ty) ;
743    ) => {
744        ::bitfield::bitfield! {
745            $(#[$attr])*
746            #[derive(Copy, Clone, PartialEq, Eq, Default)]
747            pub struct $name($val_type);
748            impl Debug;
749        }
750
751        impl $name {
752            pub fn value(&self) -> $val_type { self.0 }
753            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
754        }
755
756        impl $crate::IndexedRegister for $name {
757            type Value = $val_type;
758            const BASE_OFFSET: usize = $base_offset;
759            const STRIDE: usize = $stride;
760            const COUNT: usize = $count;
761            fn from_raw(value: Self::Value) -> Self { $name(value) }
762            fn to_raw(&self) -> Self::Value { self.0 }
763        }
764        impl $crate::ReadableIndexedRegister for $name {}
765        impl $crate::WritableIndexedRegister for $name {}
766
767        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
768            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
769            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
770        }
771        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
772            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
773            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
774        }
775    };
776}
777
778/// A macro for generating a block of registers over an MMIO region.
779///
780/// This generates a wrapper struct that contains an MMIO region, and provides
781/// proxy methods to interact with the registers defined in the block.
782///
783/// Access modes (read/write and indexed) are automatically determined from each
784/// register's definition using the [`RegisterReadAccess`] and [`RegisterWriteAccess`]
785/// traits.
786///
787/// # Examples
788///
789/// ```rust
790/// register_block! {
791///     pub struct MyBlock<M> {
792///         pub status: StatusReg,
793///         pub control: ControlReg,
794///         pub data: DataReg, // Can be an IndexedRegister
795///     }
796/// }
797///
798/// // Usage:
799/// let block = MyBlock::new(mmio);
800/// let status = block.status().read();
801/// ```
802#[macro_export]
803macro_rules! register_block {
804    (
805        $(#[$attr:meta])*
806        $vis:vis struct $name:ident <$mmio:ident> {
807            $(
808                $(#[$field_attr:meta])*
809                $field_vis:vis $field:ident : $reg_type:ident
810            ),* $(,)?
811        }
812    ) => {
813        $(#[$attr])*
814        $vis struct $name<$mmio> {
815            pub mmio: $mmio,
816        }
817
818        impl<$mmio: $crate::Mmio> $name<$mmio> {
819            /// Creates a new register block wrapping the given MMIO region.
820            #[allow(dead_code)]
821            pub fn new(mmio: $mmio) -> Self {
822                Self { mmio }
823            }
824
825            $(
826                $crate::paste::paste! {
827                    $(#[$field_attr])*
828                    #[allow(dead_code)]
829                    $field_vis fn $field(&self) -> <$reg_type as $crate::RegisterReadAccess<$mmio>>::ReadProxy<'_> {
830                        <$reg_type as $crate::RegisterReadAccess<$mmio>>::get_read_proxy(&self.mmio)
831                    }
832
833                    $(#[$field_attr])*
834                    #[allow(dead_code)]
835                    $field_vis fn [<$field _mut>](&mut self) -> <$reg_type as $crate::RegisterWriteAccess<$mmio>>::WriteProxy<'_> {
836                        <$reg_type as $crate::RegisterWriteAccess<$mmio>>::get_write_proxy(&mut self.mmio)
837                    }
838                }
839            )*
840        }
841    };
842}
843
844#[cfg(test)]
845mod tests {
846    use super::*;
847    use crate::memory::Memory;
848    use core::mem::MaybeUninit;
849
850    register! {
851        #[register(offset = 4, mode = RW)]
852        pub struct TestReg(u32) {
853            pub field1, set_field1: 7, 0;
854            pub field2, set_field2: 15, 8;
855        }
856    }
857
858    register! {
859        #[register(offset = 8, mode = RO)]
860        pub struct ReadOnlyReg(u32) {
861            pub field1, _: 7, 0;
862        }
863    }
864
865    register! {
866        #[register(offset = 12, mode = WO)]
867        pub struct WriteOnlyReg(u32) {
868            _, set_field1: 7, 0;
869        }
870    }
871
872    register! {
873        #[indexed_register(offset = 16, stride = 4, count = 2, mode = RW)]
874        pub struct TestIndexedReg(u32) {
875            pub field1, set_field1: 7, 0;
876        }
877    }
878
879    register_block! {
880        pub struct TestBlock<M> {
881            pub test_reg: TestReg,
882            pub ro_reg: ReadOnlyReg,
883            pub wo_reg: WriteOnlyReg,
884            pub indexed: TestIndexedReg,
885        }
886    }
887
888    register_block! {
889        /// Will not compile if the attribute below does not pass through.
890        #[expect(dead_code)]
891        pub struct TestBlockWithAttributtes<M> {
892            pub test_reg: TestReg,
893
894            /// Documented field.
895            pub ro_reg: ReadOnlyReg,
896
897            /// Another documented field.
898            pub wo_reg: WriteOnlyReg,
899        }
900    }
901
902    #[test]
903    fn test_register_read_write() {
904        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
905        let mut mmio = Memory::borrow_uninit(&mut mem);
906
907        let mut reg = TestReg::default();
908        reg.set_field1(0x12);
909        reg.set_field2(0x34);
910
911        reg.write(&mut mmio);
912
913        let reg2 = TestReg::read(&mmio);
914        assert_eq!(reg, reg2);
915        assert_eq!(reg2.field1(), 0x12);
916        assert_eq!(reg2.field2(), 0x34);
917    }
918
919    #[test]
920    fn test_update_reg() {
921        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
922        let mut mmio = Memory::borrow_uninit(&mut mem);
923
924        mmio.update_reg::<TestReg, _>(|reg| {
925            reg.set_field1(0xab);
926        });
927
928        let reg = mmio.read_reg::<TestReg>();
929        assert_eq!(reg.field1(), 0xab);
930        assert_eq!(reg.field2(), 0);
931    }
932
933    #[test]
934    fn test_register_proxy() {
935        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
936        let mut mmio = Memory::borrow_uninit(&mut mem);
937
938        // Test RegisterProxy (read-only)
939        mmio.store32(4, 0x1234);
940        let proxy = mmio.reg::<TestReg>();
941        assert_eq!(proxy.read().field1(), 0x34);
942
943        // Test RegisterProxyMut (read-write)
944        {
945            let mut proxy_mut = mmio.reg_mut::<TestReg>();
946            proxy_mut.update(|reg| {
947                reg.set_field1(0x56);
948            });
949            assert_eq!(proxy_mut.read().field1(), 0x56);
950
951            proxy_mut.write(TestReg(0x78));
952        }
953        assert_eq!(mmio.load32(4), 0x78);
954    }
955
956    #[test]
957    fn test_indexed_register() {
958        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
959        let mut mmio = Memory::borrow_uninit(&mut mem);
960
961        let mut reg = TestIndexedReg::default();
962        reg.set_field1(0x11);
963        reg.write_index(&mut mmio, 0);
964
965        reg.set_field1(0x22);
966        reg.write_index(&mut mmio, 1);
967
968        assert_eq!(mmio.load32(16), 0x11);
969        assert_eq!(mmio.load32(20), 0x22);
970
971        let reg0 = TestIndexedReg::read_index(&mmio, 0);
972        assert_eq!(reg0.field1(), 0x11);
973
974        let reg1 = TestIndexedReg::read_index(&mmio, 1);
975        assert_eq!(reg1.field1(), 0x22);
976    }
977
978    #[test]
979    fn test_indexed_register_proxy() {
980        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
981        let mut mmio = Memory::borrow_uninit(&mut mem);
982
983        {
984            let mut proxy = mmio.indexed_reg_mut::<TestIndexedReg>();
985            proxy.write(0, TestIndexedReg(0xaa));
986            proxy.update(1, |reg| reg.set_field1(0xbb));
987
988            assert_eq!(proxy.read(0).field1(), 0xaa);
989            assert_eq!(proxy.read(1).field1(), 0xbb);
990        }
991
992        assert_eq!(mmio.load32(16), 0xaa);
993        assert_eq!(mmio.load32(20), 0xbb);
994    }
995
996    #[test]
997    #[should_panic(expected = "Register index out of bounds")]
998    fn test_indexed_register_out_of_bounds_read() {
999        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1000        let mmio = Memory::borrow_uninit(&mut mem);
1001        let _ = TestIndexedReg::read_index(&mmio, 2);
1002    }
1003
1004    #[test]
1005    #[should_panic(expected = "Register index out of bounds")]
1006    fn test_indexed_register_out_of_bounds_write() {
1007        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1008        let mut mmio = Memory::borrow_uninit(&mut mem);
1009        let reg = TestIndexedReg::default();
1010        reg.write_index(&mut mmio, 2);
1011    }
1012
1013    #[test]
1014    fn test_register_block() {
1015        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1016        let mmio = Memory::borrow_uninit(&mut mem);
1017        let mut block = TestBlock::new(mmio);
1018
1019        block.test_reg_mut().write(TestReg(0x1234));
1020        assert_eq!(block.mmio.load32(4), 0x1234);
1021
1022        block.mmio.store32(8, 0xabcd);
1023        assert_eq!(block.ro_reg().read().field1(), 0xcd);
1024
1025        block.wo_reg_mut().write(WriteOnlyReg(0x55));
1026        assert_eq!(block.mmio.load32(12), 0x55);
1027
1028        block.indexed_mut().write(0, TestIndexedReg(0x11));
1029        block.indexed_mut().write(1, TestIndexedReg(0x22));
1030        assert_eq!(block.mmio.load32(16), 0x11);
1031        assert_eq!(block.mmio.load32(20), 0x22);
1032
1033        // Test immutable access
1034        let block = block;
1035        assert_eq!(block.test_reg().read().field1(), 0x34);
1036        assert_eq!(block.ro_reg().read().field1(), 0xcd);
1037        assert_eq!(block.indexed().read(0).field1(), 0x11);
1038    }
1039
1040    register! {
1041        #[register(offset = 4, mode = RW)]
1042        /// New syntax test register
1043        pub struct NewSyntaxReg(u32) {
1044            pub field1, set_field1: 7, 0;
1045        }
1046    }
1047
1048    register! {
1049        #[register(offset = 8, mode = RW)]
1050        /// Full width test register
1051        pub struct FullWidthReg(u32);
1052    }
1053
1054    register! {
1055        #[indexed_register(offset = 12, stride = 4, count = 2, mode = RW)]
1056        /// New syntax indexed register
1057        pub struct NewSyntaxIndexedReg(u32) {
1058            pub field1, set_field1: 7, 0;
1059        }
1060    }
1061
1062    register! {
1063        #[register(offset = 0x20, mode = RW)]
1064        pub struct MultiReg1(u32);
1065        #[register(offset = 0x24, mode = RO)]
1066        pub struct MultiReg2(u32) {
1067            pub field1, _: 7, 0;
1068        }
1069    }
1070
1071    #[test]
1072    fn test_multi_register() {
1073        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1074        let mut mmio = Memory::borrow_uninit(&mut mem);
1075
1076        let mut reg1 = MultiReg1::default();
1077        reg1.set_value(0x12345678);
1078        reg1.write(&mut mmio);
1079
1080        mmio.store32(0x24, 0xabcd);
1081
1082        let reg1_read = MultiReg1::read(&mmio);
1083        assert_eq!(reg1_read.value(), 0x12345678);
1084
1085        let reg2_read = MultiReg2::read(&mmio);
1086        assert_eq!(reg2_read.field1(), 0xcd);
1087    }
1088
1089    #[test]
1090    fn test_new_syntax() {
1091        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1092        let mut mmio = Memory::borrow_uninit(&mut mem);
1093
1094        let mut reg = NewSyntaxReg::default();
1095        reg.set_field1(0x55);
1096        reg.write(&mut mmio);
1097
1098        let reg2 = NewSyntaxReg::read(&mmio);
1099        assert_eq!(reg2.field1(), 0x55);
1100    }
1101
1102    #[test]
1103    fn test_full_width() {
1104        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1105        let mut mmio = Memory::borrow_uninit(&mut mem);
1106
1107        let mut reg = FullWidthReg::default();
1108        reg.set_value(0x12345678);
1109        reg.write(&mut mmio);
1110
1111        let reg2 = FullWidthReg::read(&mmio);
1112        assert_eq!(reg2.value(), 0x12345678);
1113    }
1114
1115    #[test]
1116    fn test_new_syntax_indexed() {
1117        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1118        let mut mmio = Memory::borrow_uninit(&mut mem);
1119
1120        let mut reg = NewSyntaxIndexedReg::default();
1121        reg.set_field1(0xaa);
1122        reg.write_index(&mut mmio, 0);
1123
1124        reg.set_field1(0xbb);
1125        reg.write_index(&mut mmio, 1);
1126
1127        let reg0 = NewSyntaxIndexedReg::read_index(&mmio, 0);
1128        assert_eq!(reg0.field1(), 0xaa);
1129
1130        let reg1 = NewSyntaxIndexedReg::read_index(&mmio, 1);
1131        assert_eq!(reg1.field1(), 0xbb);
1132    }
1133}