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/// indexed_register! {
197///     DataReg, u32, 0x100, 4, 16, RW, {
198///         pub value, set_value: 31, 0;
199///     }
200/// }
201/// ```
202pub trait IndexedRegister: Sized {
203    /// The underlying integer type (e.g., u32) that holds the register bits.
204    type Value: MmioOperand;
205
206    /// The byte offset of the first element in the register array.
207    const BASE_OFFSET: usize;
208
209    /// The byte distance between successive elements in the register array.
210    const STRIDE: usize;
211
212    /// The maximum number of valid elements in the register array.
213    const COUNT: usize;
214
215    /// Initializes the register type with a raw value, typically after reading from MMIO.
216    fn from_raw(value: Self::Value) -> Self;
217
218    /// Converts the register type back to its raw bits, typically before writing to MMIO.
219    fn to_raw(&self) -> Self::Value;
220}
221
222/// A trait for indexed registers that can be read.
223pub trait ReadableIndexedRegister: IndexedRegister {
224    /// Loads the register value at `index` from the MMIO region.
225    ///
226    /// The offset is calculated as `BASE_OFFSET + (index * STRIDE)`.
227    ///
228    /// # Panics
229    ///
230    /// This method will panic if `index` is greater than or equal to `COUNT`.
231    fn read_index<M: Mmio + ?Sized>(mmio: &M, index: usize) -> Self {
232        assert!(index < Self::COUNT, "Register index out of bounds");
233        let offset = Self::BASE_OFFSET + (index * Self::STRIDE);
234        Self::from_raw(mmio.load::<Self::Value>(offset))
235    }
236}
237
238/// A trait for indexed registers that can be written.
239pub trait WritableIndexedRegister: IndexedRegister {
240    /// Stores the register's state at `index` into the MMIO region.
241    ///
242    /// The offset is calculated as `BASE_OFFSET + (index * STRIDE)`.
243    ///
244    /// # Panics
245    ///
246    /// This method will panic if `index` is greater than or equal to `COUNT`.
247    fn write_index<M: Mmio + ?Sized>(&self, mmio: &mut M, index: usize) {
248        assert!(index < Self::COUNT, "Register index out of bounds");
249        let offset = Self::BASE_OFFSET + (index * Self::STRIDE);
250        mmio.store::<Self::Value>(offset, self.to_raw())
251    }
252}
253
254/// A macro for defining a [`Register`] and its bitfields.
255///
256/// This macro generates a bitfield struct that implements the [`Register`],
257/// [`RegisterReadAccess`] and [`RegisterWriteAccess`] traits. The access mode (RO, WO, RW)
258/// determines which of the [`ReadableRegister`] and [`WritableRegister`] traits are implemented.
259///
260/// Access modes:
261/// * `RO`: Read-Only (implements [`ReadableRegister`]).
262/// * `WO`: Write-Only (implements [`WritableRegister`]).
263/// * `RW`: Read-Write (implements both [`ReadableRegister`] and [`WritableRegister`]).
264///
265/// # Examples
266///
267/// ```rust
268/// register! {
269///     pub struct StatusReg(u32) @ 0x10, RW {
270///         pub enabled, set_enabled: 0;
271///         pub error, _: 1;
272///         pub value, set_value: 15, 8;
273///     }
274/// }
275/// ```
276///
277/// Full-width register (no bitfields):
278///
279/// ```rust
280/// register! {
281///     pub struct ControlReg(u32) @ 0x14, RW;
282/// }
283/// ```
284///
285/// NOTE: If you use this macro, you must add a dependency on the bitfield crate. (At the time of
286/// writing, it isn't easy to avoid this.)
287#[macro_export]
288macro_rules! register {
289    // New syntax: Read-Only with block
290    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, RO { $($field_spec:tt)* }) => {
291        ::bitfield::bitfield! {
292            $(#[$attr])*
293            #[derive(Copy, Clone, PartialEq, Eq, Default)]
294            pub struct $name($val_type);
295            impl Debug;
296            $($field_spec)*
297        }
298
299        impl $crate::Register for $name {
300            type Value = $val_type;
301            const OFFSET: usize = $offset;
302            fn from_raw(value: Self::Value) -> Self { $name(value) }
303            fn to_raw(&self) -> Self::Value { self.0 }
304        }
305        impl $crate::ReadableRegister for $name {}
306
307        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
308            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
309            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
310        }
311        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
312            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
313            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
314        }
315    };
316
317    // New syntax: Write-Only with block
318    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, WO { $($field_spec:tt)* }) => {
319        ::bitfield::bitfield! {
320            $(#[$attr])*
321            #[derive(Copy, Clone, PartialEq, Eq, Default)]
322            pub struct $name($val_type);
323            impl Debug;
324            $($field_spec)*
325        }
326
327        impl $crate::Register for $name {
328            type Value = $val_type;
329            const OFFSET: usize = $offset;
330            fn from_raw(value: Self::Value) -> Self { $name(value) }
331            fn to_raw(&self) -> Self::Value { self.0 }
332        }
333        impl $crate::WritableRegister for $name {}
334
335        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
336            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
337            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
338        }
339        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
340            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
341            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
342        }
343    };
344
345    // New syntax: Read-Write with block
346    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, RW { $($field_spec:tt)* }) => {
347        ::bitfield::bitfield! {
348            $(#[$attr])*
349            #[derive(Copy, Clone, PartialEq, Eq, Default)]
350            pub struct $name($val_type);
351            impl Debug;
352            $($field_spec)*
353        }
354
355        impl $crate::Register for $name {
356            type Value = $val_type;
357            const OFFSET: usize = $offset;
358            fn from_raw(value: Self::Value) -> Self { $name(value) }
359            fn to_raw(&self) -> Self::Value { self.0 }
360        }
361        impl $crate::ReadableRegister for $name {}
362        impl $crate::WritableRegister for $name {}
363
364        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
365            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
366            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
367        }
368        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
369            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
370            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
371        }
372    };
373
374    // New syntax: Read-Only full-width
375    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, RO ;) => {
376        ::bitfield::bitfield! {
377            $(#[$attr])*
378            #[derive(Copy, Clone, PartialEq, Eq, Default)]
379            pub struct $name($val_type);
380            impl Debug;
381        }
382
383        impl $name {
384            pub fn value(&self) -> $val_type { self.0 }
385        }
386
387        impl $crate::Register for $name {
388            type Value = $val_type;
389            const OFFSET: usize = $offset;
390            fn from_raw(value: Self::Value) -> Self { $name(value) }
391            fn to_raw(&self) -> Self::Value { self.0 }
392        }
393        impl $crate::ReadableRegister for $name {}
394
395        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
396            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
397            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
398        }
399        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
400            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
401            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
402        }
403    };
404
405    // New syntax: Write-Only full-width
406    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, WO ;) => {
407        ::bitfield::bitfield! {
408            $(#[$attr])*
409            #[derive(Copy, Clone, PartialEq, Eq, Default)]
410            pub struct $name($val_type);
411            impl Debug;
412        }
413
414        impl $name {
415            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
416        }
417
418        impl $crate::Register for $name {
419            type Value = $val_type;
420            const OFFSET: usize = $offset;
421            fn from_raw(value: Self::Value) -> Self { $name(value) }
422            fn to_raw(&self) -> Self::Value { self.0 }
423        }
424        impl $crate::WritableRegister for $name {}
425
426        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
427            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
428            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
429        }
430        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
431            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
432            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
433        }
434    };
435
436    // New syntax: Read-Write full-width
437    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $offset:expr, RW ;) => {
438        ::bitfield::bitfield! {
439            $(#[$attr])*
440            #[derive(Copy, Clone, PartialEq, Eq, Default)]
441            pub struct $name($val_type);
442            impl Debug;
443        }
444
445        impl $name {
446            pub fn value(&self) -> $val_type { self.0 }
447            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
448        }
449
450        impl $crate::Register for $name {
451            type Value = $val_type;
452            const OFFSET: usize = $offset;
453            fn from_raw(value: Self::Value) -> Self { $name(value) }
454            fn to_raw(&self) -> Self::Value { self.0 }
455        }
456        impl $crate::ReadableRegister for $name {}
457        impl $crate::WritableRegister for $name {}
458
459        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
460            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
461            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
462        }
463        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
464            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
465            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
466        }
467    };
468
469    // Backward compatibility: Read-Only
470    ($name:ident, $val_type:ty, $offset:expr, RO, { $($field_spec:tt)* }) => {
471        ::bitfield::bitfield! {
472            #[derive(Copy, Clone, PartialEq, Eq, Default)]
473            pub struct $name($val_type);
474            impl Debug;
475            $($field_spec)*
476        }
477
478        impl $crate::Register for $name {
479            type Value = $val_type;
480            const OFFSET: usize = $offset;
481            fn from_raw(value: Self::Value) -> Self { $name(value) }
482            fn to_raw(&self) -> Self::Value { self.0 }
483        }
484        impl $crate::ReadableRegister for $name {}
485
486        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
487            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
488            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
489        }
490        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
491            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
492            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
493        }
494    };
495
496    // Backward compatibility: Write-Only
497    ($name:ident, $val_type:ty, $offset:expr, WO, { $($field_spec:tt)* }) => {
498        ::bitfield::bitfield! {
499            #[derive(Copy, Clone, PartialEq, Eq, Default)]
500            pub struct $name($val_type);
501            impl Debug;
502            $($field_spec)*
503        }
504
505        impl $crate::Register for $name {
506            type Value = $val_type;
507            const OFFSET: usize = $offset;
508            fn from_raw(value: Self::Value) -> Self { $name(value) }
509            fn to_raw(&self) -> Self::Value { self.0 }
510        }
511        impl $crate::WritableRegister for $name {}
512
513        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
514            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
515            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
516        }
517        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
518            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
519            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
520        }
521    };
522
523    // Backward compatibility: Read-Write
524    ($name:ident, $val_type:ty, $offset:expr, RW, { $($field_spec:tt)* }) => {
525        ::bitfield::bitfield! {
526            #[derive(Copy, Clone, PartialEq, Eq, Default)]
527            pub struct $name($val_type);
528            impl Debug;
529            $($field_spec)*
530        }
531
532        impl $crate::Register for $name {
533            type Value = $val_type;
534            const OFFSET: usize = $offset;
535            fn from_raw(value: Self::Value) -> Self { $name(value) }
536            fn to_raw(&self) -> Self::Value { self.0 }
537        }
538        impl $crate::ReadableRegister for $name {}
539        impl $crate::WritableRegister for $name {}
540
541        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
542            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
543            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::RegisterProxy::new(mmio) }
544        }
545        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
546            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
547            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::RegisterProxyMut::new(mmio) }
548        }
549    };
550}
551
552/// A macro for defining an [`IndexedRegister`] and its bitfields.
553///
554/// This macro generates a bitfield struct that implements the [`IndexedRegister`],
555/// [`RegisterReadAccess`] and [`RegisterWriteAccess`] traits. The access mode (RO, WO, RW)
556/// determines which of the [`ReadableIndexedRegister`] and [`WritableIndexedRegister`]
557/// traits are implemented.
558///
559/// # Examples
560///
561/// ```rust
562/// indexed_register! {
563///     pub struct DataReg(u32) @ 0x100, stride 4, count 16, RO {
564///         pub value, _: 31, 0;
565///     }
566/// }
567/// ```
568///
569/// Full-width indexed register:
570///
571/// ```rust
572/// indexed_register! {
573///     pub struct ValueReg(u32) @ 0x200, stride 4, count 8, RW;
574/// }
575/// ```
576///
577/// NOTE: If you use this macro, you must add a dependency on the bitfield crate. (At the time of
578/// writing, it isn't easy to avoid this.)
579#[macro_export]
580macro_rules! indexed_register {
581    // New syntax: Read-Only with block
582    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, RO { $($field_spec:tt)* }) => {
583        ::bitfield::bitfield! {
584            $(#[$attr])*
585            #[derive(Copy, Clone, PartialEq, Eq, Default)]
586            pub struct $name($val_type);
587            impl Debug;
588            $($field_spec)*
589        }
590
591        impl $crate::IndexedRegister for $name {
592            type Value = $val_type;
593            const BASE_OFFSET: usize = $base_offset;
594            const STRIDE: usize = $stride;
595            const COUNT: usize = $count;
596            fn from_raw(value: Self::Value) -> Self { $name(value) }
597            fn to_raw(&self) -> Self::Value { self.0 }
598        }
599        impl $crate::ReadableIndexedRegister for $name {}
600
601        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
602            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
603            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
604        }
605        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
606            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
607            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
608        }
609    };
610
611    // New syntax: Write-Only with block
612    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, WO { $($field_spec:tt)* }) => {
613        ::bitfield::bitfield! {
614            $(#[$attr])*
615            #[derive(Copy, Clone, PartialEq, Eq, Default)]
616            pub struct $name($val_type);
617            impl Debug;
618            $($field_spec)*
619        }
620
621        impl $crate::IndexedRegister for $name {
622            type Value = $val_type;
623            const BASE_OFFSET: usize = $base_offset;
624            const STRIDE: usize = $stride;
625            const COUNT: usize = $count;
626            fn from_raw(value: Self::Value) -> Self { $name(value) }
627            fn to_raw(&self) -> Self::Value { self.0 }
628        }
629        impl $crate::WritableIndexedRegister for $name {}
630
631        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
632            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
633            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
634        }
635        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
636            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
637            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
638        }
639    };
640
641    // New syntax: Read-Write with block
642    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, RW { $($field_spec:tt)* }) => {
643        ::bitfield::bitfield! {
644            $(#[$attr])*
645            #[derive(Copy, Clone, PartialEq, Eq, Default)]
646            pub struct $name($val_type);
647            impl Debug;
648            $($field_spec)*
649        }
650
651        impl $crate::IndexedRegister for $name {
652            type Value = $val_type;
653            const BASE_OFFSET: usize = $base_offset;
654            const STRIDE: usize = $stride;
655            const COUNT: usize = $count;
656            fn from_raw(value: Self::Value) -> Self { $name(value) }
657            fn to_raw(&self) -> Self::Value { self.0 }
658        }
659        impl $crate::ReadableIndexedRegister for $name {}
660        impl $crate::WritableIndexedRegister for $name {}
661
662        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
663            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
664            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
665        }
666        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
667            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
668            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
669        }
670    };
671
672    // New syntax: Read-Only full-width
673    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, RO ;) => {
674        ::bitfield::bitfield! {
675            $(#[$attr])*
676            #[derive(Copy, Clone, PartialEq, Eq, Default)]
677            pub struct $name($val_type);
678            impl Debug;
679        }
680
681        impl $name {
682            pub fn value(&self) -> $val_type { self.0 }
683        }
684
685        impl $crate::IndexedRegister for $name {
686            type Value = $val_type;
687            const BASE_OFFSET: usize = $base_offset;
688            const STRIDE: usize = $stride;
689            const COUNT: usize = $count;
690            fn from_raw(value: Self::Value) -> Self { $name(value) }
691            fn to_raw(&self) -> Self::Value { self.0 }
692        }
693        impl $crate::ReadableIndexedRegister for $name {}
694
695        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
696            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
697            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
698        }
699        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
700            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
701            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
702        }
703    };
704
705    // New syntax: Write-Only full-width
706    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, WO ;) => {
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    // New syntax: Read-Write full-width
739    ($(#[$attr:meta])* pub struct $name:ident ($val_type:ty) @ $base_offset:expr, stride $stride:expr, count $count:expr, RW ;) => {
740        ::bitfield::bitfield! {
741            $(#[$attr])*
742            #[derive(Copy, Clone, PartialEq, Eq, Default)]
743            pub struct $name($val_type);
744            impl Debug;
745        }
746
747        impl $name {
748            pub fn value(&self) -> $val_type { self.0 }
749            pub fn set_value(&mut self, val: $val_type) { self.0 = val; }
750        }
751
752        impl $crate::IndexedRegister for $name {
753            type Value = $val_type;
754            const BASE_OFFSET: usize = $base_offset;
755            const STRIDE: usize = $stride;
756            const COUNT: usize = $count;
757            fn from_raw(value: Self::Value) -> Self { $name(value) }
758            fn to_raw(&self) -> Self::Value { self.0 }
759        }
760        impl $crate::ReadableIndexedRegister for $name {}
761        impl $crate::WritableIndexedRegister for $name {}
762
763        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
764            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
765            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
766        }
767        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
768            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
769            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
770        }
771    };
772
773    // Backward compatibility: Read-Only
774    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, RO, { $($field_spec:tt)* }) => {
775        ::bitfield::bitfield! {
776            #[derive(Copy, Clone, PartialEq, Eq, Default)]
777            pub struct $name($val_type);
778            impl Debug;
779            $($field_spec)*
780        }
781
782        impl $crate::IndexedRegister for $name {
783            type Value = $val_type;
784            const BASE_OFFSET: usize = $base_offset;
785            const STRIDE: usize = $stride;
786            const COUNT: usize = $count;
787            fn from_raw(value: Self::Value) -> Self { $name(value) }
788            fn to_raw(&self) -> Self::Value { self.0 }
789        }
790        impl $crate::ReadableIndexedRegister for $name {}
791
792        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
793            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
794            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
795        }
796        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
797            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
798            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
799        }
800    };
801
802    // Backward compatibility: Write-Only
803    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, WO, { $($field_spec:tt)* }) => {
804        ::bitfield::bitfield! {
805            #[derive(Copy, Clone, PartialEq, Eq, Default)]
806            pub struct $name($val_type);
807            impl Debug;
808            $($field_spec)*
809        }
810
811        impl $crate::IndexedRegister for $name {
812            type Value = $val_type;
813            const BASE_OFFSET: usize = $base_offset;
814            const STRIDE: usize = $stride;
815            const COUNT: usize = $count;
816            fn from_raw(value: Self::Value) -> Self { $name(value) }
817            fn to_raw(&self) -> Self::Value { self.0 }
818        }
819        impl $crate::WritableIndexedRegister for $name {}
820
821        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
822            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
823            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
824        }
825        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
826            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
827            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
828        }
829    };
830
831    // Backward compatibility: Read-Write
832    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, RW, { $($field_spec:tt)* }) => {
833        ::bitfield::bitfield! {
834            #[derive(Copy, Clone, PartialEq, Eq, Default)]
835            pub struct $name($val_type);
836            impl Debug;
837            $($field_spec)*
838        }
839
840        impl $crate::IndexedRegister for $name {
841            type Value = $val_type;
842            const BASE_OFFSET: usize = $base_offset;
843            const STRIDE: usize = $stride;
844            const COUNT: usize = $count;
845            fn from_raw(value: Self::Value) -> Self { $name(value) }
846            fn to_raw(&self) -> Self::Value { self.0 }
847        }
848        impl $crate::ReadableIndexedRegister for $name {}
849        impl $crate::WritableIndexedRegister for $name {}
850
851        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
852            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
853            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> { $crate::IndexedRegisterProxy::new(mmio) }
854        }
855        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
856            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
857            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> { $crate::IndexedRegisterProxyMut::new(mmio) }
858        }
859    };
860}
861
862/// A macro for generating a block of registers over an MMIO region.
863///
864/// This generates a wrapper struct that contains an MMIO region, and provides
865/// proxy methods to interact with the registers defined in the block.
866///
867/// Access modes (read/write and indexed) are automatically determined from each
868/// register's definition using the [`RegisterReadAccess`] and [`RegisterWriteAccess`]
869/// traits.
870///
871/// # Examples
872///
873/// ```rust
874/// register_block! {
875///     pub struct MyBlock<M> {
876///         pub status: StatusReg,
877///         pub control: ControlReg,
878///         pub data: DataReg, // Can be an IndexedRegister
879///     }
880/// }
881///
882/// // Usage:
883/// let block = MyBlock::new(mmio);
884/// let status = block.status().read();
885/// ```
886#[macro_export]
887macro_rules! register_block {
888    (
889        $vis:vis struct $name:ident <$mmio:ident> {
890            $(
891                $(#[$attr:meta])*
892                $field_vis:vis $field:ident : $reg_type:ident
893            ),* $(,)?
894        }
895    ) => {
896        $vis struct $name<$mmio> {
897            pub mmio: $mmio,
898        }
899
900        impl<$mmio: $crate::Mmio> $name<$mmio> {
901            /// Creates a new register block wrapping the given MMIO region.
902            pub fn new(mmio: $mmio) -> Self {
903                Self { mmio }
904            }
905
906            $(
907                $(#[$attr])*
908                $crate::paste::paste! {
909                    #[allow(dead_code)]
910                    $field_vis fn $field(&self) -> <$reg_type as $crate::RegisterReadAccess<$mmio>>::ReadProxy<'_> {
911                        <$reg_type as $crate::RegisterReadAccess<$mmio>>::get_read_proxy(&self.mmio)
912                    }
913
914                    $(#[$attr])*
915                    #[allow(dead_code)]
916                    $field_vis fn [<$field _mut>](&mut self) -> <$reg_type as $crate::RegisterWriteAccess<$mmio>>::WriteProxy<'_> {
917                        <$reg_type as $crate::RegisterWriteAccess<$mmio>>::get_write_proxy(&mut self.mmio)
918                    }
919                }
920            )*
921        }
922    };
923}
924
925#[cfg(test)]
926mod tests {
927    use super::*;
928    use crate::memory::Memory;
929    use core::mem::MaybeUninit;
930
931    register! {
932        TestReg, u32, 4, RW, {
933            pub field1, set_field1: 7, 0;
934            pub field2, set_field2: 15, 8;
935        }
936    }
937
938    register! {
939        ReadOnlyReg, u32, 8, RO, {
940            pub field1, _: 7, 0;
941        }
942    }
943
944    register! {
945        WriteOnlyReg, u32, 12, WO, {
946            _, set_field1: 7, 0;
947        }
948    }
949
950    indexed_register! {
951        TestIndexedReg, u32, 16, 4, 2, RW, {
952            pub field1, set_field1: 7, 0;
953        }
954    }
955
956    register_block! {
957        pub struct TestBlock<M> {
958            pub test_reg: TestReg,
959            pub ro_reg: ReadOnlyReg,
960            pub wo_reg: WriteOnlyReg,
961            pub indexed: TestIndexedReg,
962        }
963    }
964
965    #[test]
966    fn test_register_read_write() {
967        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
968        let mut mmio = Memory::borrow_uninit(&mut mem);
969
970        let mut reg = TestReg::default();
971        reg.set_field1(0x12);
972        reg.set_field2(0x34);
973
974        reg.write(&mut mmio);
975
976        let reg2 = TestReg::read(&mmio);
977        assert_eq!(reg, reg2);
978        assert_eq!(reg2.field1(), 0x12);
979        assert_eq!(reg2.field2(), 0x34);
980    }
981
982    #[test]
983    fn test_update_reg() {
984        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
985        let mut mmio = Memory::borrow_uninit(&mut mem);
986
987        mmio.update_reg::<TestReg, _>(|reg| {
988            reg.set_field1(0xab);
989        });
990
991        let reg = mmio.read_reg::<TestReg>();
992        assert_eq!(reg.field1(), 0xab);
993        assert_eq!(reg.field2(), 0);
994    }
995
996    #[test]
997    fn test_register_proxy() {
998        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
999        let mut mmio = Memory::borrow_uninit(&mut mem);
1000
1001        // Test RegisterProxy (read-only)
1002        mmio.store32(4, 0x1234);
1003        let proxy = mmio.reg::<TestReg>();
1004        assert_eq!(proxy.read().field1(), 0x34);
1005
1006        // Test RegisterProxyMut (read-write)
1007        {
1008            let mut proxy_mut = mmio.reg_mut::<TestReg>();
1009            proxy_mut.update(|reg| {
1010                reg.set_field1(0x56);
1011            });
1012            assert_eq!(proxy_mut.read().field1(), 0x56);
1013
1014            proxy_mut.write(TestReg(0x78));
1015        }
1016        assert_eq!(mmio.load32(4), 0x78);
1017    }
1018
1019    #[test]
1020    fn test_indexed_register() {
1021        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1022        let mut mmio = Memory::borrow_uninit(&mut mem);
1023
1024        let mut reg = TestIndexedReg::default();
1025        reg.set_field1(0x11);
1026        reg.write_index(&mut mmio, 0);
1027
1028        reg.set_field1(0x22);
1029        reg.write_index(&mut mmio, 1);
1030
1031        assert_eq!(mmio.load32(16), 0x11);
1032        assert_eq!(mmio.load32(20), 0x22);
1033
1034        let reg0 = TestIndexedReg::read_index(&mmio, 0);
1035        assert_eq!(reg0.field1(), 0x11);
1036
1037        let reg1 = TestIndexedReg::read_index(&mmio, 1);
1038        assert_eq!(reg1.field1(), 0x22);
1039    }
1040
1041    #[test]
1042    fn test_indexed_register_proxy() {
1043        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1044        let mut mmio = Memory::borrow_uninit(&mut mem);
1045
1046        {
1047            let mut proxy = mmio.indexed_reg_mut::<TestIndexedReg>();
1048            proxy.write(0, TestIndexedReg(0xaa));
1049            proxy.update(1, |reg| reg.set_field1(0xbb));
1050
1051            assert_eq!(proxy.read(0).field1(), 0xaa);
1052            assert_eq!(proxy.read(1).field1(), 0xbb);
1053        }
1054
1055        assert_eq!(mmio.load32(16), 0xaa);
1056        assert_eq!(mmio.load32(20), 0xbb);
1057    }
1058
1059    #[test]
1060    #[should_panic(expected = "Register index out of bounds")]
1061    fn test_indexed_register_out_of_bounds_read() {
1062        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1063        let mmio = Memory::borrow_uninit(&mut mem);
1064        let _ = TestIndexedReg::read_index(&mmio, 2);
1065    }
1066
1067    #[test]
1068    #[should_panic(expected = "Register index out of bounds")]
1069    fn test_indexed_register_out_of_bounds_write() {
1070        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1071        let mut mmio = Memory::borrow_uninit(&mut mem);
1072        let reg = TestIndexedReg::default();
1073        reg.write_index(&mut mmio, 2);
1074    }
1075
1076    #[test]
1077    fn test_register_block() {
1078        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
1079        let mmio = Memory::borrow_uninit(&mut mem);
1080        let mut block = TestBlock::new(mmio);
1081
1082        block.test_reg_mut().write(TestReg(0x1234));
1083        assert_eq!(block.mmio.load32(4), 0x1234);
1084
1085        block.mmio.store32(8, 0xabcd);
1086        assert_eq!(block.ro_reg().read().field1(), 0xcd);
1087
1088        block.wo_reg_mut().write(WriteOnlyReg(0x55));
1089        assert_eq!(block.mmio.load32(12), 0x55);
1090
1091        block.indexed_mut().write(0, TestIndexedReg(0x11));
1092        block.indexed_mut().write(1, TestIndexedReg(0x22));
1093        assert_eq!(block.mmio.load32(16), 0x11);
1094        assert_eq!(block.mmio.load32(20), 0x22);
1095
1096        // Test immutable access
1097        let block = block;
1098        assert_eq!(block.test_reg().read().field1(), 0x34);
1099        assert_eq!(block.ro_reg().read().field1(), 0xcd);
1100        assert_eq!(block.indexed().read(0).field1(), 0x11);
1101    }
1102
1103    register! {
1104        /// New syntax test register
1105        pub struct NewSyntaxReg(u32) @ 4, RW {
1106            pub field1, set_field1: 7, 0;
1107        }
1108    }
1109
1110    register! {
1111        /// Full width test register
1112        pub struct FullWidthReg(u32) @ 8, RW;
1113    }
1114
1115    indexed_register! {
1116        /// New syntax indexed register
1117        pub struct NewSyntaxIndexedReg(u32) @ 12, stride 4, count 2, RW {
1118            pub field1, set_field1: 7, 0;
1119        }
1120    }
1121
1122    #[test]
1123    fn test_new_syntax() {
1124        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1125        let mut mmio = Memory::borrow_uninit(&mut mem);
1126
1127        let mut reg = NewSyntaxReg::default();
1128        reg.set_field1(0x55);
1129        reg.write(&mut mmio);
1130
1131        let reg2 = NewSyntaxReg::read(&mmio);
1132        assert_eq!(reg2.field1(), 0x55);
1133    }
1134
1135    #[test]
1136    fn test_full_width() {
1137        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1138        let mut mmio = Memory::borrow_uninit(&mut mem);
1139
1140        let mut reg = FullWidthReg::default();
1141        reg.set_value(0x12345678);
1142        reg.write(&mut mmio);
1143
1144        let reg2 = FullWidthReg::read(&mmio);
1145        assert_eq!(reg2.value(), 0x12345678);
1146    }
1147
1148    #[test]
1149    fn test_new_syntax_indexed() {
1150        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
1151        let mut mmio = Memory::borrow_uninit(&mut mem);
1152
1153        let mut reg = NewSyntaxIndexedReg::default();
1154        reg.set_field1(0xaa);
1155        reg.write_index(&mut mmio, 0);
1156
1157        reg.set_field1(0xbb);
1158        reg.write_index(&mut mmio, 1);
1159
1160        let reg0 = NewSyntaxIndexedReg::read_index(&mmio, 0);
1161        assert_eq!(reg0.field1(), 0xaa);
1162
1163        let reg1 = NewSyntaxIndexedReg::read_index(&mmio, 1);
1164        assert_eq!(reg1.field1(), 0xbb);
1165    }
1166}