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///     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/// NOTE: If you use this macro, you must add a dependency on the bitfield crate. (At the time of
278/// writing, it isn't easy to avoid this.)
279#[macro_export]
280macro_rules! register {
281    ($name:ident, $val_type:ty, $offset:expr, RO, { $($field_spec:tt)* }) => {
282        ::bitfield::bitfield! {
283            #[derive(Copy, Clone, PartialEq, Eq, Default)]
284            pub struct $name($val_type);
285            impl Debug;
286            $($field_spec)*
287        }
288
289        impl $crate::Register for $name {
290            type Value = $val_type;
291            const OFFSET: usize = $offset;
292
293            fn from_raw(value: Self::Value) -> Self {
294                $name(value)
295            }
296
297            fn to_raw(&self) -> Self::Value {
298                self.0
299            }
300        }
301
302        impl $crate::ReadableRegister for $name {}
303
304        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
305            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
306            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> {
307                $crate::RegisterProxy::new(mmio)
308            }
309        }
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> {
314                $crate::RegisterProxyMut::new(mmio)
315            }
316        }
317    };
318    ($name:ident, $val_type:ty, $offset:expr, WO, { $($field_spec:tt)* }) => {
319        ::bitfield::bitfield! {
320            #[derive(Copy, Clone, PartialEq, Eq, Default)]
321            pub struct $name($val_type);
322            impl Debug;
323            $($field_spec)*
324        }
325
326        impl $crate::Register for $name {
327            type Value = $val_type;
328            const OFFSET: usize = $offset;
329
330            fn from_raw(value: Self::Value) -> Self {
331                $name(value)
332            }
333
334            fn to_raw(&self) -> Self::Value {
335                self.0
336            }
337        }
338
339        impl $crate::WritableRegister for $name {}
340
341        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
342            type ReadProxy<'a> = $crate::RegisterProxy<'a, M, $name> where M: 'a;
343            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> {
344                $crate::RegisterProxy::new(mmio)
345            }
346        }
347
348        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
349            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
350            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> {
351                $crate::RegisterProxyMut::new(mmio)
352            }
353        }
354    };
355    ($name:ident, $val_type:ty, $offset:expr, RW, { $($field_spec:tt)* }) => {
356        ::bitfield::bitfield! {
357            #[derive(Copy, Clone, PartialEq, Eq, Default)]
358            pub struct $name($val_type);
359            impl Debug;
360            $($field_spec)*
361        }
362
363        impl $crate::Register for $name {
364            type Value = $val_type;
365            const OFFSET: usize = $offset;
366
367            fn from_raw(value: Self::Value) -> Self {
368                $name(value)
369            }
370
371            fn to_raw(&self) -> Self::Value {
372                self.0
373            }
374        }
375
376        impl $crate::ReadableRegister for $name {}
377        impl $crate::WritableRegister 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> {
382                $crate::RegisterProxy::new(mmio)
383            }
384        }
385
386        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
387            type WriteProxy<'a> = $crate::RegisterProxyMut<'a, M, $name> where M: 'a;
388            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> {
389                $crate::RegisterProxyMut::new(mmio)
390            }
391        }
392    };
393}
394
395/// A macro for defining an [`IndexedRegister`] and its bitfields.
396///
397/// This macro generates a bitfield struct that implements the [`IndexedRegister`],
398/// [`RegisterReadAccess`] and [`RegisterWriteAccess`] traits. The access mode (RO, WO, RW)
399/// determines which of the [`ReadableIndexedRegister`] and [`WritableIndexedRegister`]
400/// traits are implemented.
401///
402/// # Examples
403///
404/// ```rust
405/// indexed_register! {
406///     DataReg, u32, 0x100, 4, 16, RO, {
407///         pub value, _: 31, 0;
408///     }
409/// }
410/// ```
411///
412/// NOTE: If you use this macro, you must add a dependency on the bitfield crate. (At the time of
413/// writing, it isn't easy to avoid this.)
414#[macro_export]
415macro_rules! indexed_register {
416    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, RO, { $($field_spec:tt)* }) => {
417        ::bitfield::bitfield! {
418            #[derive(Copy, Clone, PartialEq, Eq, Default)]
419            pub struct $name($val_type);
420            impl Debug;
421            $($field_spec)*
422        }
423
424        impl $crate::IndexedRegister for $name {
425            type Value = $val_type;
426            const BASE_OFFSET: usize = $base_offset;
427            const STRIDE: usize = $stride;
428            const COUNT: usize = $count;
429
430            fn from_raw(value: Self::Value) -> Self {
431                $name(value)
432            }
433
434            fn to_raw(&self) -> Self::Value {
435                self.0
436            }
437        }
438
439        impl $crate::ReadableIndexedRegister for $name {}
440
441        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
442            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
443            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> {
444                $crate::IndexedRegisterProxy::new(mmio)
445            }
446        }
447
448        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
449            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
450            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> {
451                $crate::IndexedRegisterProxyMut::new(mmio)
452            }
453        }
454    };
455    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, WO, { $($field_spec:tt)* }) => {
456        ::bitfield::bitfield! {
457            #[derive(Copy, Clone, PartialEq, Eq, Default)]
458            pub struct $name($val_type);
459            impl Debug;
460            $($field_spec)*
461        }
462
463        impl $crate::IndexedRegister for $name {
464            type Value = $val_type;
465            const BASE_OFFSET: usize = $base_offset;
466            const STRIDE: usize = $stride;
467            const COUNT: usize = $count;
468
469            fn from_raw(value: Self::Value) -> Self {
470                $name(value)
471            }
472
473            fn to_raw(&self) -> Self::Value {
474                self.0
475            }
476        }
477
478        impl $crate::WritableIndexedRegister for $name {}
479
480        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
481            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
482            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> {
483                $crate::IndexedRegisterProxy::new(mmio)
484            }
485        }
486
487        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
488            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
489            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> {
490                $crate::IndexedRegisterProxyMut::new(mmio)
491            }
492        }
493    };
494    ($name:ident, $val_type:ty, $base_offset:expr, $stride:expr, $count:expr, RW, { $($field_spec:tt)* }) => {
495        ::bitfield::bitfield! {
496            #[derive(Copy, Clone, PartialEq, Eq, Default)]
497            pub struct $name($val_type);
498            impl Debug;
499            $($field_spec)*
500        }
501
502        impl $crate::IndexedRegister for $name {
503            type Value = $val_type;
504            const BASE_OFFSET: usize = $base_offset;
505            const STRIDE: usize = $stride;
506            const COUNT: usize = $count;
507
508            fn from_raw(value: Self::Value) -> Self {
509                $name(value)
510            }
511
512            fn to_raw(&self) -> Self::Value {
513                self.0
514            }
515        }
516
517        impl $crate::ReadableIndexedRegister for $name {}
518        impl $crate::WritableIndexedRegister for $name {}
519
520        impl<M: $crate::Mmio + ?Sized> $crate::RegisterReadAccess<M> for $name {
521            type ReadProxy<'a> = $crate::IndexedRegisterProxy<'a, M, $name> where M: 'a;
522            fn get_read_proxy<'a>(mmio: &'a M) -> Self::ReadProxy<'a> {
523                $crate::IndexedRegisterProxy::new(mmio)
524            }
525        }
526
527        impl<M: $crate::Mmio + ?Sized> $crate::RegisterWriteAccess<M> for $name {
528            type WriteProxy<'a> = $crate::IndexedRegisterProxyMut<'a, M, $name> where M: 'a;
529            fn get_write_proxy<'a>(mmio: &'a mut M) -> Self::WriteProxy<'a> {
530                $crate::IndexedRegisterProxyMut::new(mmio)
531            }
532        }
533    };
534}
535
536/// A macro for generating a block of registers over an MMIO region.
537///
538/// This generates a wrapper struct that contains an MMIO region, and provides
539/// proxy methods to interact with the registers defined in the block.
540///
541/// Access modes (read/write and indexed) are automatically determined from each
542/// register's definition using the [`RegisterReadAccess`] and [`RegisterWriteAccess`]
543/// traits.
544///
545/// # Examples
546///
547/// ```rust
548/// register_block! {
549///     pub struct MyBlock<M> {
550///         pub status: StatusReg,
551///         pub control: ControlReg,
552///         pub data: DataReg, // Can be an IndexedRegister
553///     }
554/// }
555///
556/// // Usage:
557/// let block = MyBlock::new(mmio);
558/// let status = block.status().read();
559/// ```
560#[macro_export]
561macro_rules! register_block {
562    (
563        $vis:vis struct $name:ident <$mmio:ident> {
564            $(
565                $(#[$attr:meta])*
566                $field_vis:vis $field:ident : $reg_type:ident
567            ),* $(,)?
568        }
569    ) => {
570        $vis struct $name<$mmio> {
571            pub mmio: $mmio,
572        }
573
574        impl<$mmio: $crate::Mmio> $name<$mmio> {
575            /// Creates a new register block wrapping the given MMIO region.
576            pub fn new(mmio: $mmio) -> Self {
577                Self { mmio }
578            }
579
580            $(
581                $(#[$attr])*
582                $crate::paste::paste! {
583                    #[allow(dead_code)]
584                    $field_vis fn $field(&self) -> <$reg_type as $crate::RegisterReadAccess<$mmio>>::ReadProxy<'_> {
585                        <$reg_type as $crate::RegisterReadAccess<$mmio>>::get_read_proxy(&self.mmio)
586                    }
587
588                    $(#[$attr])*
589                    #[allow(dead_code)]
590                    $field_vis fn [<$field _mut>](&mut self) -> <$reg_type as $crate::RegisterWriteAccess<$mmio>>::WriteProxy<'_> {
591                        <$reg_type as $crate::RegisterWriteAccess<$mmio>>::get_write_proxy(&mut self.mmio)
592                    }
593                }
594            )*
595        }
596    };
597}
598
599#[cfg(test)]
600mod tests {
601    use super::*;
602    use crate::memory::Memory;
603    use core::mem::MaybeUninit;
604
605    register! {
606        TestReg, u32, 4, RW, {
607            pub field1, set_field1: 7, 0;
608            pub field2, set_field2: 15, 8;
609        }
610    }
611
612    register! {
613        ReadOnlyReg, u32, 8, RO, {
614            pub field1, _: 7, 0;
615        }
616    }
617
618    register! {
619        WriteOnlyReg, u32, 12, WO, {
620            _, set_field1: 7, 0;
621        }
622    }
623
624    indexed_register! {
625        TestIndexedReg, u32, 16, 4, 2, RW, {
626            pub field1, set_field1: 7, 0;
627        }
628    }
629
630    register_block! {
631        pub struct TestBlock<M> {
632            pub test_reg: TestReg,
633            pub ro_reg: ReadOnlyReg,
634            pub wo_reg: WriteOnlyReg,
635            pub indexed: TestIndexedReg,
636        }
637    }
638
639    #[test]
640    fn test_register_read_write() {
641        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
642        let mut mmio = Memory::borrow_uninit(&mut mem);
643
644        let mut reg = TestReg::default();
645        reg.set_field1(0x12);
646        reg.set_field2(0x34);
647
648        reg.write(&mut mmio);
649
650        let reg2 = TestReg::read(&mmio);
651        assert_eq!(reg, reg2);
652        assert_eq!(reg2.field1(), 0x12);
653        assert_eq!(reg2.field2(), 0x34);
654    }
655
656    #[test]
657    fn test_update_reg() {
658        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
659        let mut mmio = Memory::borrow_uninit(&mut mem);
660
661        mmio.update_reg::<TestReg, _>(|reg| {
662            reg.set_field1(0xab);
663        });
664
665        let reg = mmio.read_reg::<TestReg>();
666        assert_eq!(reg.field1(), 0xab);
667        assert_eq!(reg.field2(), 0);
668    }
669
670    #[test]
671    fn test_register_proxy() {
672        let mut mem = MaybeUninit::<[u32; 8]>::zeroed();
673        let mut mmio = Memory::borrow_uninit(&mut mem);
674
675        // Test RegisterProxy (read-only)
676        mmio.store32(4, 0x1234);
677        let proxy = mmio.reg::<TestReg>();
678        assert_eq!(proxy.read().field1(), 0x34);
679
680        // Test RegisterProxyMut (read-write)
681        {
682            let mut proxy_mut = mmio.reg_mut::<TestReg>();
683            proxy_mut.update(|reg| {
684                reg.set_field1(0x56);
685            });
686            assert_eq!(proxy_mut.read().field1(), 0x56);
687
688            proxy_mut.write(TestReg(0x78));
689        }
690        assert_eq!(mmio.load32(4), 0x78);
691    }
692
693    #[test]
694    fn test_indexed_register() {
695        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
696        let mut mmio = Memory::borrow_uninit(&mut mem);
697
698        let mut reg = TestIndexedReg::default();
699        reg.set_field1(0x11);
700        reg.write_index(&mut mmio, 0);
701
702        reg.set_field1(0x22);
703        reg.write_index(&mut mmio, 1);
704
705        assert_eq!(mmio.load32(16), 0x11);
706        assert_eq!(mmio.load32(20), 0x22);
707
708        let reg0 = TestIndexedReg::read_index(&mmio, 0);
709        assert_eq!(reg0.field1(), 0x11);
710
711        let reg1 = TestIndexedReg::read_index(&mmio, 1);
712        assert_eq!(reg1.field1(), 0x22);
713    }
714
715    #[test]
716    fn test_indexed_register_proxy() {
717        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
718        let mut mmio = Memory::borrow_uninit(&mut mem);
719
720        {
721            let mut proxy = mmio.indexed_reg_mut::<TestIndexedReg>();
722            proxy.write(0, TestIndexedReg(0xaa));
723            proxy.update(1, |reg| reg.set_field1(0xbb));
724
725            assert_eq!(proxy.read(0).field1(), 0xaa);
726            assert_eq!(proxy.read(1).field1(), 0xbb);
727        }
728
729        assert_eq!(mmio.load32(16), 0xaa);
730        assert_eq!(mmio.load32(20), 0xbb);
731    }
732
733    #[test]
734    #[should_panic(expected = "Register index out of bounds")]
735    fn test_indexed_register_out_of_bounds_read() {
736        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
737        let mmio = Memory::borrow_uninit(&mut mem);
738        let _ = TestIndexedReg::read_index(&mmio, 2);
739    }
740
741    #[test]
742    #[should_panic(expected = "Register index out of bounds")]
743    fn test_indexed_register_out_of_bounds_write() {
744        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
745        let mut mmio = Memory::borrow_uninit(&mut mem);
746        let reg = TestIndexedReg::default();
747        reg.write_index(&mut mmio, 2);
748    }
749
750    #[test]
751    fn test_register_block() {
752        let mut mem = MaybeUninit::<[u32; 12]>::zeroed();
753        let mmio = Memory::borrow_uninit(&mut mem);
754        let mut block = TestBlock::new(mmio);
755
756        block.test_reg_mut().write(TestReg(0x1234));
757        assert_eq!(block.mmio.load32(4), 0x1234);
758
759        block.mmio.store32(8, 0xabcd);
760        assert_eq!(block.ro_reg().read().field1(), 0xcd);
761
762        block.wo_reg_mut().write(WriteOnlyReg(0x55));
763        assert_eq!(block.mmio.load32(12), 0x55);
764
765        block.indexed_mut().write(0, TestIndexedReg(0x11));
766        block.indexed_mut().write(1, TestIndexedReg(0x22));
767        assert_eq!(block.mmio.load32(16), 0x11);
768        assert_eq!(block.mmio.load32(20), 0x22);
769
770        // Test immutable access
771        let block = block;
772        assert_eq!(block.test_reg().read().field1(), 0x34);
773        assert_eq!(block.ro_reg().read().field1(), 0xcd);
774        assert_eq!(block.indexed().read(0).field1(), 0x11);
775    }
776}