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