1use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
6
7pub trait RegisterValue: Sized + FromBytes + IntoBytes + FromZeros + Immutable {}
13
14pub trait RegisterBlock {
16 const ADDRESS_UNIT_BYTES: u16 = 1;
19}
20
21#[doc(hidden)]
23#[inline(always)]
24pub const fn block_address_unit_bytes<B: RegisterBlock>(_: &B) -> u16 {
25 B::ADDRESS_UNIT_BYTES
26}
27
28pub trait SpmiRegisterDef {
30 type Value: RegisterValue;
32
33 type Mode;
35
36 const ADDRESS: u16;
38}
39
40pub trait SpmiRegister<D>: SpmiRegisterDef {
44 type Accessor<'a>
46 where
47 Self: 'a,
48 D: 'a;
49
50 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a>;
52}
53
54#[doc(hidden)]
56#[inline(always)]
57pub const fn assert_contiguous<B, R1, R2>(_: &B)
58where
59 B: RegisterBlock,
60 R1: SpmiRegisterDef,
61 R2: SpmiRegisterDef,
62{
63 struct ContiguityCheck<B, R1, R2>(std::marker::PhantomData<(B, R1, R2)>);
64
65 impl<B, R1, R2> ContiguityCheck<B, R1, R2>
66 where
67 B: RegisterBlock,
68 R1: SpmiRegisterDef,
69 R2: SpmiRegisterDef,
70 {
71 const ASSERT: () = {
72 let unit = B::ADDRESS_UNIT_BYTES as u32;
73 let val_size = std::mem::size_of::<R1::Value>() as u32;
74 let r1_addr = R1::ADDRESS as u32;
75 let r2_addr = R2::ADDRESS as u32;
76 let step = (val_size + unit - 1) / unit;
77 assert!(r2_addr == r1_addr + step, "Registers are not contiguous");
78 };
79 }
80
81 let _ = ContiguityCheck::<B, R1, R2>::ASSERT;
82}
83
84pub struct ReadOnly;
91
92pub struct WriteOnly;
94
95pub struct ReadWrite;
97
98pub trait Readable {}
103impl Readable for ReadOnly {}
104impl Readable for ReadWrite {}
105
106pub trait Writable {}
111impl Writable for WriteOnly {}
112impl Writable for ReadWrite {}
113
114pub struct _Register<'a, T, M, D, const ADDR: u16> {
123 pub spmi: &'a D,
125 _marker: std::marker::PhantomData<(T, M)>,
126}
127
128impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR> {
129 pub fn new(spmi: &'a D) -> Self {
131 Self { spmi, _marker: std::marker::PhantomData }
132 }
133}
134
135impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
137where
138 M: Readable,
139 T: RegisterValue,
140 D: SpmiDevice,
141{
142 pub async fn read(&self) -> Result<T, crate::Error> {
147 let size = std::mem::size_of::<T>();
148 let bytes = self.spmi.read_reg(ADDR, size as u32).await?;
149 T::read_from_bytes(&bytes).map_err(|_| crate::Error::SizeMismatch)
150 }
151}
152
153impl<'a, T, M, D, const ADDR: u16> _Register<'a, T, M, D, ADDR>
155where
156 M: Writable,
157 T: RegisterValue,
158 D: SpmiDevice,
159{
160 pub async fn write(&self, val: T) -> Result<(), crate::Error> {
165 let bytes = val.as_bytes();
166 self.spmi.write_reg(ADDR, bytes).await?;
167 Ok(())
168 }
169}
170
171#[macro_export]
272macro_rules! spmi_register {
273 (@mode_type RO) => { $crate::ReadOnly };
275 (@mode_type WO) => { $crate::WriteOnly };
276 (@mode_type RW) => { $crate::ReadWrite };
277
278 (@byteorder_type u8, $endianness:ident) => { u8 };
280 (@byteorder_type u16, LE) => { $crate::U16<$crate::LittleEndian> };
281 (@byteorder_type u16, BE) => { $crate::U16<$crate::BigEndian> };
282 (@byteorder_type u32, LE) => { $crate::U32<$crate::LittleEndian> };
283 (@byteorder_type u32, BE) => { $crate::U32<$crate::BigEndian> };
284
285 (@new u8, $endianness:ident, $val:expr) => { Self($val) };
286 (@new u16, $endianness:ident, $val:expr) => {
287 Self($crate::U16::new($val))
288 };
289 (@new u32, $endianness:ident, $val:expr) => {
290 Self($crate::U32::new($val))
291 };
292
293 (@get u8, $endianness:ident, $val:expr) => { $val };
294 (@get u16, $endianness:ident, $val:expr) => { $val.get() };
295 (@get u32, $endianness:ident, $val:expr) => { $val.get() };
296
297 (
299 $name:ident,
300 $value_type:ident,
301 $addr:expr,
302 $mode:ident,
303 $endianness:ident,
304 {
305 $($tail:tt)*
306 }
307 ) => {
308 #[allow(unused_imports)]
309 #[allow(dead_code)]
310 pub mod $name {
311 use super::*;
312 use $crate::zerocopy_reexport as zerocopy;
313
314 pub const ADDRESS: u16 = $addr;
316
317 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
318
319 #[derive(
321 Copy,
322 Clone,
323 Debug,
324 PartialEq,
325 Eq,
326 zerocopy::FromBytes,
327 zerocopy::IntoBytes,
328 zerocopy::Immutable,
329 )]
330 #[repr(transparent)]
331 pub struct Value(
332 pub spmi_register!(@byteorder_type $value_type, $endianness),
333 );
334
335 impl Value {
336 pub const fn new(val: $value_type) -> Self {
337 spmi_register!(@new $value_type, $endianness, val)
338 }
339 pub const fn reg_value(&self) -> $value_type {
340 spmi_register!(@get $value_type, $endianness, self.0)
341 }
342
343 pub fn from_bytes(bytes: &[u8]) -> Result<Self, $crate::Error> {
345 zerocopy::FromBytes::read_from_bytes(bytes)
346 .map_err(|_| $crate::Error::SizeMismatch)
347 }
348
349 pub fn to_bytes(
351 &self,
352 ) -> [u8; std::mem::size_of::<$value_type>()] {
353 let mut arr = [0u8; std::mem::size_of::<$value_type>()];
354 arr.copy_from_slice($crate::IntoBytes::as_bytes(self));
355 arr
356 }
357
358 $crate::spmi_register_fields!($value_type, $($tail)*);
359 }
360
361 impl $crate::RegisterValue for Value {}
362
363 impl Default for Value {
364 fn default() -> Self {
365 Self::new(0 as $value_type)
366 }
367 }
368
369 impl $crate::SpmiRegisterDef for Value {
370 type Value = Self;
371 type Mode = spmi_register!(@mode_type $mode);
372 const ADDRESS: u16 = $addr;
373 }
374
375 impl<D> $crate::SpmiRegister<D> for Value {
376 type Accessor<'a> =
377 $crate::_Register<'a, Self, spmi_register!(@mode_type $mode), D, $addr>
378 where
379 Self: 'a,
380 D: 'a;
381
382 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
383 $crate::_Register::new(spmi)
384 }
385 }
386
387 pub type Register<'a, D = $crate::DeviceType> =
389 $crate::_Register<'a, Value, spmi_register!(@mode_type $mode), D, $addr>;
390 }
391 };
392 (
394 $name:ident, u8, $addr:expr, $mode:ident, {
395 $($tail:tt)*
396 }
397 ) => {
398 spmi_register!($name, u8, $addr, $mode, LE, { $($tail)* });
399 };
400 (@is_big BE) => { true };
401 (@is_big LE) => { false };
402}
403
404#[macro_export]
406#[doc(hidden)]
407macro_rules! spmi_register_extract_enums {
408 (
410 $value_type:ty,
411 $(#[$attr:meta])*
412 $vis:vis enum $enum_name:ident {
413 $( $variant_name:ident = $variant_val:expr ),* $(,)?
414 },
415 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
416 $($tail:tt)*
417 ) => {
418 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
419 #[repr($value_type)]
420 $vis enum $enum_name {
421 $( $variant_name = $variant_val ),*
422 }
423 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
424 };
425
426 (
428 $value_type:ty,
429 $(#[$attr:meta])*
430 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
431 $($tail:tt)*
432 ) => {
433 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
434 };
435 (
436 $value_type:ty,
437 $(#[$attr:meta])*
438 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
439 $($tail:tt)*
440 ) => {
441 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
442 };
443 (
444 $value_type:ty,
445 $(#[$attr:meta])*
446 $vis:vis enum $enum_type:ty,
447 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
448 $($tail:tt)*
449 ) => {
450 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
451 };
452 (
453 $value_type:ty,
454 pub const $name:ident : $type:ty = $val:expr;
455 $($tail:tt)*
456 ) => {
457 $crate::spmi_register_extract_enums!($value_type, $($tail)*);
458 };
459 ($value_type:ty) => {};
460 ($value_type:ty,) => {};
461}
462
463#[macro_export]
465#[doc(hidden)]
466macro_rules! spmi_register_fields {
467 ($value_type:ty) => {};
469 ($value_type:ty,) => {};
470
471 (
473 $value_type:ty,
474 $(#[$attr:meta])*
475 $vis:vis $field:ident $(, $setter:ident)? : $bit:expr;
476 $($tail:tt)*
477 ) => {
478 $(#[$attr])*
479 #[allow(non_snake_case)]
480 #[allow(dead_code)]
481 $vis const fn $field(&self) -> bool {
482 const _: () = assert!(
483 $bit < <$value_type>::BITS as u8,
484 "Bit index out of bounds"
485 );
486 let bit_mask = (1 as $value_type) << $bit;
487 (self.reg_value() & bit_mask) != 0
488 }
489 $(
490 #[allow(non_snake_case)]
491 #[allow(dead_code)]
492 $vis const fn $setter(self, val: bool) -> Self {
493 const _: () = assert!(
494 $bit < <$value_type>::BITS as u8,
495 "Bit index out of bounds"
496 );
497 let bit_mask = (1 as $value_type) << $bit;
498 let raw = (self.reg_value() & !bit_mask)
499 | ((val as $value_type) << $bit);
500 Self::new(raw)
501 }
502 )?
503 $crate::spmi_register_fields!($value_type, $($tail)*);
504 };
505
506 (
508 $value_type:ty,
509 $(#[$attr:meta])*
510 $vis:vis $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
511 $($tail:tt)*
512 ) => {
513 $(#[$attr])*
514 #[allow(non_snake_case)]
515 #[allow(dead_code)]
516 $vis const fn $field(&self) -> $value_type {
517 const _: () = assert!(
518 $msb < <$value_type>::BITS as u8,
519 "MSB index out of bounds"
520 );
521 const _: () = assert!(
522 $lsb < $msb,
523 "LSB must be strictly less than MSB. \
524 Use single-bit syntax (e.g., 'field: bit;') for 1-bit fields."
525 );
526 let bit_count = $msb - $lsb + 1;
527 let bit_mask = ((!0 as $value_type)
528 >> (<$value_type>::BITS as u8 - bit_count))
529 << $lsb;
530 (self.reg_value() & bit_mask) >> $lsb
531 }
532 $(
533 #[allow(non_snake_case)]
534 #[allow(dead_code)]
535 $vis const fn $setter(self, val: $value_type) -> Self {
536 const _: () = assert!(
537 $msb < <$value_type>::BITS as u8,
538 "MSB index out of bounds"
539 );
540 const _: () = assert!(
541 $lsb < $msb,
542 "LSB must be strictly less than MSB. \
543 Use single-bit syntax (e.g., 'field: bit;') \
544 for 1-bit fields."
545 );
546 let bit_count = $msb - $lsb + 1;
547 let bit_mask = ((!0 as $value_type)
548 >> (<$value_type>::BITS as u8 - bit_count))
549 << $lsb;
550 let raw = (self.reg_value() & !bit_mask)
551 | ((val << $lsb) & bit_mask);
552 Self::new(raw)
553 }
554 )?
555 $crate::spmi_register_fields!($value_type, $($tail)*);
556 };
557
558 (
560 $value_type:ty,
561 $(#[$attr:meta])*
562 $vis:vis enum $enum_type:ty,
563 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
564 $($tail:tt)*
565 ) => {
566 $(#[$attr])*
567 #[allow(non_snake_case)]
568 #[allow(dead_code)]
569 $vis const fn $field(&self) -> $enum_type {
570 const _: () = assert!(
571 $msb < <$value_type>::BITS as u8,
572 "MSB index out of bounds"
573 );
574 const _: () = assert!(
575 $lsb <= $msb,
576 "LSB must be less than or equal to MSB"
577 );
578 let bit_count = $msb - $lsb + 1;
579 let bit_mask = ((!0 as $value_type)
580 >> (<$value_type>::BITS as u8 - bit_count))
581 << $lsb;
582 <$enum_type>::from_val((self.reg_value() & bit_mask) >> $lsb)
583 }
584 $(
585 #[allow(non_snake_case)]
586 #[allow(dead_code)]
587 $vis const fn $setter(self, val: $enum_type) -> Self {
588 const _: () = assert!(
589 $msb < <$value_type>::BITS as u8,
590 "MSB index out of bounds"
591 );
592 const _: () = assert!(
593 $lsb <= $msb,
594 "LSB must be less than or equal to MSB"
595 );
596 let bit_count = $msb - $lsb + 1;
597 let bit_mask = ((!0 as $value_type)
598 >> (<$value_type>::BITS as u8 - bit_count))
599 << $lsb;
600 let raw = (self.reg_value() & !bit_mask)
601 | (((val as $value_type) << $lsb) & bit_mask);
602 Self::new(raw)
603 }
604 )?
605 $crate::spmi_register_fields!($value_type, $($tail)*);
606 };
607
608 (
610 $value_type:ty,
611 $(#[$attr:meta])*
612 $vis:vis enum $enum_name:ident {
613 $( $variant_name:ident = $variant_val:expr ),* $(,)?
614 },
615 $field:ident $(, $setter:ident)? : $msb:expr, $lsb:expr;
616 $($tail:tt)*
617 ) => {
618 $(#[$attr])*
619 #[allow(non_snake_case)]
620 #[allow(dead_code)]
621 $vis const fn $field(&self) -> Result<$enum_name, $value_type> {
622 const _: () = assert!(
623 $msb < <$value_type>::BITS as u8,
624 "MSB index out of bounds"
625 );
626 const _: () = assert!(
627 $lsb <= $msb,
628 "LSB must be less than or equal to MSB"
629 );
630 let bit_count = $msb - $lsb + 1;
631 let bit_mask = ((!0 as $value_type)
632 >> (<$value_type>::BITS as u8 - bit_count))
633 << $lsb;
634 let val = (self.reg_value() & bit_mask) >> $lsb;
635 $(
636 if val == $enum_name::$variant_name as $value_type {
637 return Ok($enum_name::$variant_name);
638 }
639 )*
640 Err(val)
641 }
642 $(
643 #[allow(non_snake_case)]
644 #[allow(dead_code)]
645 $vis const fn $setter(self, val: $enum_name) -> Self {
646 const _: () = assert!(
647 $msb < <$value_type>::BITS as u8,
648 "MSB index out of bounds"
649 );
650 const _: () = assert!(
651 $lsb <= $msb,
652 "LSB must be less than or equal to MSB"
653 );
654 let bit_count = $msb - $lsb + 1;
655 let bit_mask = ((!0 as $value_type)
656 >> (<$value_type>::BITS as u8 - bit_count))
657 << $lsb;
658 let raw = (self.reg_value() & !bit_mask)
659 | (((val as $value_type) << $lsb) & bit_mask);
660 Self::new(raw)
661 }
662 )?
663 $crate::spmi_register_fields!($value_type, $($tail)*);
664 };
665
666 (
668 $value_type:ty,
669 pub const $name:ident : $type:ty = $val:expr;
670 $($tail:tt)*
671 ) => {
672 pub const $name: $type = $val;
673 $crate::spmi_register_fields!($value_type, $($tail)*);
674 };
675}
676
677#[macro_export]
679#[doc(hidden)]
680macro_rules! assert_contiguous {
681 ($regs:expr, $prev:ident, $curr:ident $(, $rest:ident)*) => {
682 $crate::assert_contiguous::<_, $prev::Value, $curr::Value>($regs);
683 $crate::assert_contiguous!($regs, $curr $(, $rest)*);
684 };
685 ($regs:expr, $last:ident) => {};
686}
687
688#[macro_export]
737macro_rules! spmi_register_block {
738 (
740 address_unit: $unit_type:ty,
741 $struct_vis:vis struct $name:ident {
742 $($tail:tt)*
743 }
744 ) => {
745 $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
746
747 impl<D> $crate::RegisterBlock for $name<D> {
748 const ADDRESS_UNIT_BYTES: u16 = std::mem::size_of::<$unit_type>() as u16;
749 }
750 };
751
752 (
754 $struct_vis:vis struct $name:ident {
755 $($tail:tt)*
756 }
757 ) => {
758 $crate::spmi_register_block!(@struct $struct_vis, $name, $($tail)*);
759
760 impl<D> $crate::RegisterBlock for $name<D> {
761 const ADDRESS_UNIT_BYTES: u16 = 1;
762 }
763 };
764
765 (
767 @struct $struct_vis:vis, $name:ident,
768 $($tail:tt)*
769 ) => {
770 #[derive(Clone)]
771 $struct_vis struct $name<D = $crate::DeviceType> {
772 pub spmi: D,
773 }
774
775 #[allow(dead_code)]
776 impl<D: $crate::SpmiDevice> $name<D> {
777 $struct_vis fn new(spmi: D) -> Self {
778 Self { spmi }
779 }
780
781 #[doc(hidden)]
787 #[allow(dead_code)]
788 pub async fn read_bulk(
789 &self,
790 address: u16,
791 size: u32,
792 ) -> Result<Vec<u8>, $crate::Error> {
793 let data = $crate::SpmiDevice::read_reg(
794 &self.spmi,
795 address,
796 size,
797 ).await?;
798 if data.len() == size as usize {
799 Ok(data)
800 } else {
801 Err($crate::Error::SizeMismatch)
802 }
803 }
804
805 #[doc(hidden)]
812 #[allow(dead_code)]
813 pub async fn read_bulk_into(
814 &self,
815 address: u16,
816 out: &mut [u8],
817 ) -> Result<(), $crate::Error> {
818 let data = $crate::SpmiDevice::read_reg(
819 &self.spmi,
820 address,
821 out.len() as u32,
822 ).await?;
823 if data.len() == out.len() {
824 out.copy_from_slice(&data);
825 Ok(())
826 } else {
827 Err($crate::Error::SizeMismatch)
828 }
829 }
830
831 #[doc(hidden)]
837 #[allow(dead_code)]
838 pub async fn write_bulk(
839 &self,
840 address: u16,
841 data: &[u8],
842 ) -> Result<(), $crate::Error> {
843 $crate::SpmiDevice::write_reg(&self.spmi, address, data).await?;
844 Ok(())
845 }
846
847 $crate::spmi_register_block!(@fields D, $($tail)*);
848 }
849 };
850
851 (@fields $d:ident) => {};
852 (@fields $d:ident,) => {};
853
854 (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident, $($tail:tt)*) => {
856 $(#[$meta])*
857 #[allow(dead_code)]
858 $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
859 <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
860 }
861 $crate::spmi_register_block!(@fields $d, $($tail)*);
862 };
863
864 (@fields $d:ident, $(#[$meta:meta])* $vis:vis $field:ident => $reg_mod:ident) => {
866 $(#[$meta])*
867 #[allow(dead_code)]
868 $vis fn $field(&self) -> <$reg_mod::Value as $crate::SpmiRegister<$d>>::Accessor<'_> {
869 <$reg_mod::Value as $crate::SpmiRegister<$d>>::get_accessor(&self.spmi)
870 }
871 };
872}
873
874#[doc(hidden)]
908#[macro_export]
909macro_rules! spmi_contiguous_layout {
910 (@last $head:ident) => {
911 ($head::ADDRESS, std::mem::size_of::<$head::Value>())
912 };
913
914 (@last $head:ident, $( $tail:ident ),+) => {
915 $crate::spmi_contiguous_layout!(@last $( $tail ),+)
916 };
917
918 (@last_val $head:ident) => {
919 $head::Value
920 };
921
922 (@last_val $head:ident, $( $tail:ident ),+) => {
923 $crate::spmi_contiguous_layout!(@last_val $( $tail ),+)
924 };
925
926 ($regs_ref:expr, $head:ident $(, $tail:ident )*) => {{
927 let unit = $crate::block_address_unit_bytes($regs_ref) as usize;
928 let base_addr = $head::ADDRESS;
929 let (last_addr, last_size) = $crate::spmi_contiguous_layout!(@last $head $(, $tail)*);
930 let last_step = (last_size + unit - 1) / unit;
931 let total_units = (last_addr - base_addr) as usize + last_step;
932 let total_bytes = total_units * unit;
933 (unit, base_addr, total_bytes)
934 }};
935}
936
937#[macro_export]
960macro_rules! spmi_read_contiguous {
961 (
962 $regs:expr,
963 $head:ident $(, $tail:ident )* $(,)?
964 ) => {
965 async {
966 #[allow(dead_code, non_camel_case_types)]
967 struct ReadAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
968 std::marker::PhantomData<($head, $( $tail ),*)>,
969 )
970 where
971 $head::Mode: $crate::Readable,
972 $( $tail::Mode: $crate::Readable, )*;
973
974 let _ = ReadAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
975
976 let regs_ref = $regs;
977 $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
978 let res: Result<
979 ($head::Value, $( $tail::Value ),*),
980 $crate::Error
981 > = async move {
982 let (unit, base_addr, total_bytes) =
983 $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
984
985 let data = regs_ref.read_bulk(
986 base_addr,
987 total_bytes as u32,
988 ).await?;
989
990 let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
991 let $head = $head::Value::from_bytes(
992 &data[head_offset..head_offset + std::mem::size_of::<$head::Value>()],
993 )?;
994 $(
995 let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
996 let $tail = $tail::Value::from_bytes(
997 &data[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()],
998 )?;
999 )*
1000
1001 Ok(($head, $( $tail ),*))
1002 }.await;
1003 res
1004 }
1005 };
1006}
1007
1008#[macro_export]
1036macro_rules! spmi_write_contiguous {
1037 (
1038 $regs:expr,
1039 $head:ident => $head_val:expr $(, $tail:ident => $tail_val:expr )* $(,)?
1040 ) => {
1041 async {
1042 #[allow(dead_code, non_camel_case_types)]
1043 struct WriteAccessCheck<$head: $crate::SpmiRegisterDef, $( $tail: $crate::SpmiRegisterDef ),*>(
1044 std::marker::PhantomData<($head, $( $tail ),*)>,
1045 )
1046 where
1047 $head::Mode: $crate::Writable,
1048 $( $tail::Mode: $crate::Writable, )*;
1049
1050 let _ = WriteAccessCheck::<$head::Value, $( $tail::Value ),*>(std::marker::PhantomData);
1051
1052 let regs_ref = $regs;
1053 $crate::assert_contiguous!(regs_ref, $head $(, $tail)*);
1054 let res: Result<(), $crate::Error> = async move {
1055 let (unit, base_addr, total_bytes) =
1056 $crate::spmi_contiguous_layout!(regs_ref, $head $(, $tail)*);
1057
1058 let mut bytes = vec![0u8; total_bytes];
1059
1060 let head_offset = ($head::ADDRESS - base_addr) as usize * unit;
1061 bytes[head_offset..head_offset + std::mem::size_of::<$head::Value>()]
1062 .copy_from_slice(&$head_val.to_bytes());
1063 $(
1064 let tail_offset = ($tail::ADDRESS - base_addr) as usize * unit;
1065 bytes[tail_offset..tail_offset + std::mem::size_of::<$tail::Value>()]
1066 .copy_from_slice(&$tail_val.to_bytes());
1067 )*
1068
1069 regs_ref.write_bulk(base_addr, &bytes).await?;
1070
1071 Ok(())
1072 }.await;
1073 res
1074 }
1075 };
1076}
1077
1078#[allow(async_fn_in_trait)]
1087pub trait SpmiDevice {
1088 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error>;
1100
1101 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error>;
1112}
1113
1114#[cfg(test)]
1115pub struct TestSpmiDevice {
1118 registers: std::sync::Mutex<std::collections::HashMap<u16, u8>>,
1119}
1120
1121#[cfg(test)]
1122impl TestSpmiDevice {
1123 pub fn new() -> Self {
1125 Self { registers: std::sync::Mutex::new(std::collections::HashMap::new()) }
1126 }
1127}
1128
1129#[cfg(test)]
1130impl SpmiDevice for TestSpmiDevice {
1131 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1132 let regs = self.registers.lock().unwrap();
1133 let mut data = Vec::new();
1134 for i in 0..size {
1135 let addr = address + i as u16;
1136 let val = regs.get(&addr).copied().unwrap_or(0);
1137 data.push(val);
1138 }
1139 Ok(data)
1140 }
1141
1142 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1143 let mut regs = self.registers.lock().unwrap();
1144 for (i, &val) in data.iter().enumerate() {
1145 let addr = address + i as u16;
1146 regs.insert(addr, val);
1147 }
1148 Ok(())
1149 }
1150}
1151
1152#[cfg(test)]
1153mod tests {
1154 use super::*;
1155
1156 spmi_register! {
1159 test_u8_reg, u8, 0xCD, RW, {
1160 pub flag, set_flag: 4;
1161 pub field, set_field: 3, 0;
1162 }
1163 }
1164
1165 spmi_register_block! {
1166 pub struct MockU8Regs {
1167 pub test => test_u8_reg,
1168 }
1169 }
1170
1171 spmi_register! {
1172 test_u16_from_bytes_reg, u16, 0x99, RW, LE, {
1173 pub field, set_field: 15, 0;
1174 }
1175 }
1176
1177 spmi_register_block! {
1178 pub struct MockU16FromBytesRegs {
1179 pub test => test_u16_from_bytes_reg,
1180 }
1181 }
1182
1183 spmi_register! {
1184 test_u16_contig_reg, u16, 0xCE, RW, LE, {
1185 pub field, set_field: 15, 0;
1186 }
1187 }
1188
1189 #[fuchsia::test]
1190 async fn test_u16_from_bytes() {
1191 let device = TestSpmiDevice::new();
1192 device.write_reg(0x99, &[0x34, 0x12]).await.unwrap();
1193
1194 let regs = MockU16FromBytesRegs::new(device);
1195 let val = regs.test().read().await.unwrap();
1196 assert_eq!(val.reg_value(), 0x1234);
1197 }
1198
1199 #[fuchsia::test]
1200 async fn test_u8_register() {
1201 let device = TestSpmiDevice::new();
1202 device.write_reg(0xCD, &[0x1A]).await.unwrap();
1203
1204 let regs = MockU8Regs::new(device);
1205 let val = regs.test().read().await.unwrap();
1206 assert_eq!(val.reg_value(), 0x1A);
1207 assert_eq!(val.flag(), true);
1208 assert_eq!(val.field(), 0x0A);
1209 }
1210
1211 spmi_register! {
1212 test_reg, u16, 0xAB, RW, LE, {
1213 pub test_bit, set_test_bit: 7;
1214 pub test_field, set_test_field: 3, 0;
1215 }
1216 }
1217
1218 spmi_register_block! {
1219 pub struct MockRegs {
1220 pub test => test_reg,
1221 }
1222 }
1223
1224 #[fuchsia::test]
1225 async fn test_read() {
1226 let device = TestSpmiDevice::new();
1227 device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1228
1229 let regs = MockRegs::new(device);
1230 let val = regs.test().read().await.unwrap();
1231 assert_eq!(val.reg_value(), 0x008A);
1232 assert_eq!(val.test_bit(), true);
1233 assert_eq!(val.test_field(), 0x0A);
1234 }
1235
1236 #[fuchsia::test]
1237 async fn test_write() {
1238 let device = TestSpmiDevice::new();
1239
1240 let regs = MockRegs::new(device);
1241 let v = test_reg::Value::new(0x008A);
1242 regs.test().write(v).await.unwrap();
1243
1244 let data = regs.spmi.read_reg(0xAB, 2).await.unwrap();
1245 assert_eq!(data, &[0x8A, 0x00]);
1246 }
1247
1248 struct MockWrapper {
1249 device: TestSpmiDevice,
1250 }
1251
1252 impl SpmiDevice for MockWrapper {
1253 async fn read_reg(&self, address: u16, size: u32) -> Result<Vec<u8>, crate::Error> {
1254 self.device.read_reg(address, size).await
1255 }
1256 async fn write_reg(&self, address: u16, data: &[u8]) -> Result<(), crate::Error> {
1257 self.device.write_reg(address, data).await
1258 }
1259 }
1260
1261 spmi_register_block! {
1262 pub struct MockWrappedRegs {
1263 pub test => test_reg,
1264 }
1265 }
1266
1267 #[fuchsia::test]
1268 async fn test_wrapped_read() {
1269 let device = TestSpmiDevice::new();
1270 device.write_reg(0xAB, &[0x8A, 0x00]).await.unwrap();
1271
1272 let wrapper = MockWrapper { device };
1273 let regs = MockWrappedRegs::new(wrapper);
1274 let val = regs.test().read().await.unwrap();
1275 assert_eq!(val.reg_value(), 0x008A);
1276 assert_eq!(val.test_bit(), true);
1277 }
1278
1279 spmi_register! {
1280 test_be_reg, u16, 0xEF, RW, BE, {
1281 pub flag, set_flag: 4;
1282 pub field, set_field: 3, 0;
1283 }
1284 }
1285
1286 spmi_register_block! {
1287 pub struct MockBERegs {
1288 pub test => test_be_reg,
1289 }
1290 }
1291
1292 #[fuchsia::test]
1293 async fn test_be_register() {
1294 let device = TestSpmiDevice::new();
1295 device.write_reg(0xEF, &[0x00, 0x8A]).await.unwrap();
1296
1297 let regs = MockBERegs::new(device);
1298 let val = regs.test().read().await.unwrap();
1299 assert_eq!(val.reg_value(), 0x8A);
1300 }
1301
1302 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
1303 #[repr(u16)]
1304 pub enum PowerMode {
1305 Normal = 0,
1306 Hibernate = 1,
1307 LowPower = 2,
1308 Unknown = 0xFFFF,
1309 }
1310
1311 impl PowerMode {
1312 pub const fn from_val(val: u16) -> Self {
1313 match val {
1314 0 => PowerMode::Normal,
1315 1 => PowerMode::Hibernate,
1316 2 => PowerMode::LowPower,
1317 _ => PowerMode::Unknown,
1318 }
1319 }
1320 }
1321
1322 spmi_register! {
1323 test_enum_reg, u16, 0x44, RW, LE, {
1324 pub enum PowerMode, mode, set_mode: 3, 2;
1325 }
1326 }
1327
1328 spmi_register_block! {
1329 pub struct MockEnumRegs {
1330 pub test => test_enum_reg,
1331 }
1332 }
1333
1334 #[fuchsia::test]
1335 async fn test_enum_register() {
1336 let device = TestSpmiDevice::new();
1337
1338 let regs = MockEnumRegs::new(device);
1339 let v = test_enum_reg::Value::new(0).set_mode(PowerMode::Hibernate);
1340 regs.test().write(v).await.unwrap();
1341
1342 let data = regs.spmi.read_reg(0x44, 2).await.unwrap();
1343 assert_eq!(data, &[0x04, 0x00]);
1344 }
1345
1346 spmi_register! {
1347 test_inline_enum_reg, u16, 0x55, RW, LE, {
1348 pub enum InlineMode {
1349 A = 0,
1350 B = 1,
1351 }, mode, set_mode: 1, 0;
1352 }
1353 }
1354
1355 spmi_register_block! {
1356 pub struct MockInlineEnumRegs {
1357 pub test => test_inline_enum_reg,
1358 }
1359 }
1360
1361 #[fuchsia::test]
1362 async fn test_inline_enum() {
1363 let device = TestSpmiDevice::new();
1364
1365 let regs = MockInlineEnumRegs::new(device);
1366 let v = test_inline_enum_reg::Value::new(1).set_mode(test_inline_enum_reg::InlineMode::B);
1367 assert_eq!(v.mode(), Ok(test_inline_enum_reg::InlineMode::B));
1368 regs.test().write(v).await.unwrap();
1369
1370 let data = regs.spmi.read_reg(0x55, 2).await.unwrap();
1371 assert_eq!(data, &[0x01, 0x00]);
1372 }
1373
1374 #[fuchsia::test]
1375 async fn test_contiguous_read_write() {
1376 let device = TestSpmiDevice::new();
1377 device.write_reg(0xCD, &[0x1A, 0x34, 0x12]).await.unwrap();
1378
1379 spmi_register_block! {
1380 pub struct ContiguousRegs {
1381 pub r1 => test_u8_reg,
1382 pub r2 => test_u16_contig_reg,
1383 }
1384 }
1385 let regs = ContiguousRegs::new(device);
1386
1387 let (val_1, val_2) =
1388 spmi_read_contiguous!(®s, test_u8_reg, test_u16_contig_reg,).await.unwrap();
1389
1390 assert_eq!(val_1.reg_value(), 0x1A);
1391 assert_eq!(val_2.reg_value(), 0x1234);
1392
1393 spmi_write_contiguous!(
1394 ®s,
1395 test_u8_reg => val_1,
1396 test_u16_contig_reg => val_2
1397 )
1398 .await
1399 .unwrap();
1400
1401 let data = regs.spmi.read_reg(0xCD, 3).await.unwrap();
1402 assert_eq!(data, &[0x1A, 0x34, 0x12]);
1403 }
1404
1405 spmi_register! {
1406 test_word_reg_1, u16, 0x10, RW, LE, {}
1407 }
1408 spmi_register! {
1409 test_word_reg_2, u16, 0x11, RW, LE, {}
1410 }
1411 spmi_register! {
1412 test_word_reg_3, u16, 0x12, RW, LE, {}
1413 }
1414
1415 spmi_register_block! {
1416 address_unit: u16,
1417 pub struct WordAddressedRegs {
1418 pub r1 => test_word_reg_1,
1419 pub r2 => test_word_reg_2,
1420 pub r3 => test_word_reg_3,
1421 }
1422 }
1423
1424 #[fuchsia::test]
1425 async fn test_word_addressed_contiguous() {
1426 let device = TestSpmiDevice::new();
1427 device.write_reg(0x10, &[0x34, 0x12, 0x78, 0x56]).await.unwrap();
1428
1429 let regs = WordAddressedRegs::new(device);
1430
1431 let (val_1, val_2) =
1438 spmi_read_contiguous!(®s, test_word_reg_1, test_word_reg_2).await.unwrap();
1439 assert_eq!(val_1.reg_value(), 0x1234);
1440 assert_eq!(val_2.reg_value(), 0x5678);
1441
1442 spmi_write_contiguous!(
1444 ®s,
1445 test_word_reg_1 => val_1,
1446 test_word_reg_2 => val_2
1447 )
1448 .await
1449 .unwrap();
1450
1451 let data = regs.spmi.read_reg(0x10, 4).await.unwrap();
1452 assert_eq!(data, &[0x34, 0x12, 0x78, 0x56]);
1453 }
1454
1455 #[fuchsia::test]
1456 async fn test_read_write_bulk() {
1457 let device = TestSpmiDevice::new();
1458 device.write_reg(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1459
1460 let regs = MockU8Regs::new(device);
1461 let bytes = regs.read_bulk(0x88, 3).await.unwrap();
1462 assert_eq!(bytes, vec![0x1A, 0x2B, 0x3C]);
1463
1464 let mut buf = [0u8; 3];
1465 regs.read_bulk_into(0x88, &mut buf).await.unwrap();
1466 assert_eq!(buf, [0x1A, 0x2B, 0x3C]);
1467
1468 regs.write_bulk(0x88, &[0x1A, 0x2B, 0x3C]).await.unwrap();
1469 let data = regs.spmi.read_reg(0x88, 3).await.unwrap();
1470 assert_eq!(data, &[0x1A, 0x2B, 0x3C]);
1471 }
1472
1473 spmi_register! {
1474 test_mixed_u8, u8, 0x20, RW, {}
1475 }
1476 spmi_register! {
1477 test_mixed_u16, u16, 0x21, RW, LE, {}
1478 }
1479
1480 spmi_register_block! {
1481 address_unit: u16,
1482 pub struct WordAddressedMixedRegs {
1483 pub u8_reg => test_mixed_u8,
1484 pub u16_reg => test_mixed_u16,
1485 }
1486 }
1487
1488 #[fuchsia::test]
1489 async fn test_word_addressed_mixed_sizes_contiguous() {
1490 let device = TestSpmiDevice::new();
1491 device.write_reg(0x20, &[0xAB, 0x00, 0x34, 0x12]).await.unwrap();
1493
1494 let regs = WordAddressedMixedRegs::new(device);
1495 let (val_u8, val_u16) =
1496 spmi_read_contiguous!(®s, test_mixed_u8, test_mixed_u16).await.unwrap();
1497
1498 assert_eq!(val_u8.reg_value(), 0xAB);
1499 assert_eq!(val_u16.reg_value(), 0x1234);
1500
1501 spmi_write_contiguous!(
1502 ®s,
1503 test_mixed_u8 => val_u8,
1504 test_mixed_u16 => val_u16
1505 )
1506 .await
1507 .unwrap();
1508
1509 let data = regs.spmi.read_reg(0x20, 4).await.unwrap();
1510 assert_eq!(data, &[0xAB, 0x00, 0x34, 0x12]);
1511 }
1512
1513 #[derive(
1514 Copy,
1515 Clone,
1516 Debug,
1517 PartialEq,
1518 Eq,
1519 zerocopy::FromBytes,
1520 zerocopy::IntoBytes,
1521 zerocopy::KnownLayout,
1522 zerocopy::Immutable,
1523 )]
1524 #[repr(C)]
1525 pub struct Reg3Byte([u8; 3]);
1526 impl RegisterValue for Reg3Byte {}
1527
1528 impl Reg3Byte {
1529 fn from_bytes(bytes: &[u8]) -> Result<Self, crate::Error> {
1530 let slice: [u8; 3] = bytes.try_into().unwrap();
1531 Ok(Self(slice))
1532 }
1533 fn to_bytes(&self) -> [u8; 3] {
1534 self.0
1535 }
1536 fn reg_value(&self) -> Self {
1537 *self
1538 }
1539 }
1540
1541 mod test_3byte_reg {
1542 use super::*;
1543 pub type Value = Reg3Byte;
1544 pub const ADDRESS: u16 = 0x30;
1545 }
1546
1547 impl SpmiRegisterDef for Reg3Byte {
1548 type Value = Self;
1549 type Mode = crate::ReadWrite;
1550 const ADDRESS: u16 = 0x30;
1551 }
1552
1553 impl<D> SpmiRegister<D> for Reg3Byte {
1554 type Accessor<'a>
1555 = &'a D
1556 where
1557 Self: 'a,
1558 D: 'a;
1559 fn get_accessor<'a>(spmi: &'a D) -> Self::Accessor<'a> {
1560 spmi
1561 }
1562 }
1563 spmi_register! {
1564 test_after_3byte_reg, u16, 0x32, RW, LE, {}
1565 }
1566
1567 spmi_register_block! {
1568 address_unit: u16,
1569 pub struct WordAddressed3ByteRegs {
1570 pub r3b => test_3byte_reg,
1571 pub r_next => test_after_3byte_reg,
1572 }
1573 }
1574
1575 #[fuchsia::test]
1576 async fn test_word_addressed_non_multiple_size_contiguous() {
1577 let device = TestSpmiDevice::new();
1578 device.write_reg(0x30, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]).await.unwrap();
1581
1582 let regs = WordAddressed3ByteRegs::new(device);
1583 let (val_3b, val_next) =
1584 spmi_read_contiguous!(®s, test_3byte_reg, test_after_3byte_reg).await.unwrap();
1585
1586 assert_eq!(val_3b.reg_value(), Reg3Byte([0x11, 0x22, 0x33]));
1587 assert_eq!(val_next.reg_value(), 0x7856);
1588
1589 spmi_write_contiguous!(
1590 ®s,
1591 test_3byte_reg => val_3b,
1592 test_after_3byte_reg => val_next
1593 )
1594 .await
1595 .unwrap();
1596
1597 let data = regs.spmi.read_reg(0x30, 6).await.unwrap();
1598 assert_eq!(data, &[0x11, 0x22, 0x33, 0x00, 0x56, 0x78]);
1599 }
1600
1601 spmi_register! {
1602 synth_u8_reg, u8, 0x60, RW, {
1603 pub enum SynthU8Mode {
1604 Mode0 = 0,
1605 Mode1 = 1,
1606 Mode2 = 2,
1607 Mode3 = 3,
1608 }, mode, set_mode: 2, 1;
1609 pub enable, set_enable: 3;
1610 pub multi, set_multi: 6, 5;
1611 }
1612 }
1613
1614 spmi_register! {
1615 synth_u16_le_reg, u16, 0x62, RW, LE, {
1616 pub flag_low, set_flag_low: 1;
1617 pub enum SynthU16LeMode {
1618 Off = 0,
1619 On = 1,
1620 Standby = 2,
1621 Active = 3,
1622 }, mode, set_mode: 5, 4;
1623 pub data, set_data: 11, 8;
1624 pub flag_high, set_flag_high: 14;
1625 }
1626 }
1627
1628 spmi_register! {
1629 synth_u16_be_reg, u16, 0x64, RW, BE, {
1630 pub flag_low, set_flag_low: 2;
1631 pub enum SynthU16BeMode {
1632 Alpha = 0,
1633 Beta = 1,
1634 Gamma = 2,
1635 Delta = 3,
1636 }, mode, set_mode: 6, 5;
1637 pub count, set_count: 12, 9;
1638 pub flag_high, set_flag_high: 15;
1639 }
1640 }
1641
1642 spmi_register! {
1643 synth_u32_le_reg, u32, 0x70, RW, LE, {
1644 pub flag_low, set_flag_low: 3;
1645 pub enum SynthU32LeMode {
1646 Zero = 0,
1647 One = 1,
1648 Two = 2,
1649 Three = 3,
1650 }, mode, set_mode: 8, 7;
1651 pub range, set_range: 22, 16;
1652 pub flag_high, set_flag_high: 30;
1653 }
1654 }
1655
1656 spmi_register! {
1657 synth_u32_be_reg, u32, 0x74, RW, BE, {
1658 pub flag_low, set_flag_low: 2;
1659 pub enum SynthU32BeMode {
1660 Primary = 0,
1661 Secondary = 1,
1662 Tertiary = 2,
1663 Quaternary = 3,
1664 }, mode, set_mode: 10, 9;
1665 pub chunk, set_chunk: 23, 17;
1666 pub flag_high, set_flag_high: 31;
1667 }
1668 }
1669
1670 spmi_register_block! {
1671 pub struct SynthRegs {
1672 pub u8_reg => synth_u8_reg,
1673 pub u16_le_reg => synth_u16_le_reg,
1674 pub u16_be_reg => synth_u16_be_reg,
1675 pub u32_le_reg => synth_u32_le_reg,
1676 pub u32_be_reg => synth_u32_be_reg,
1677 }
1678 }
1679
1680 const U8_MODE_MASK: u8 = 0x06;
1683 const U8_ENABLE_MASK: u8 = 0x08;
1684 const U8_MULTI_MASK: u8 = 0x60;
1685 const U8_RESERVED_MASK: u8 = !(U8_MODE_MASK | U8_ENABLE_MASK | U8_MULTI_MASK);
1686 const U8_MULTI_FIELD_MASK: u8 = 0x03;
1687
1688 const U16_LE_FLAG_LOW_MASK: u16 = 0x0002;
1692 const U16_LE_MODE_MASK: u16 = 0x0030;
1693 const U16_LE_DATA_MASK: u16 = 0x0F00;
1694 const U16_LE_FLAG_HIGH_MASK: u16 = 0x4000;
1695 const U16_LE_RESERVED_MASK: u16 =
1696 !(U16_LE_FLAG_LOW_MASK | U16_LE_MODE_MASK | U16_LE_DATA_MASK | U16_LE_FLAG_HIGH_MASK);
1697 const U16_LE_DATA_FIELD_MASK: u16 = 0x0F;
1698
1699 const U16_BE_FLAG_LOW_MASK: u16 = 0x0004;
1703 const U16_BE_MODE_MASK: u16 = 0x0060;
1704 const U16_BE_COUNT_MASK: u16 = 0x1E00;
1705 const U16_BE_FLAG_HIGH_MASK: u16 = 0x8000;
1706 const U16_BE_RESERVED_MASK: u16 =
1707 !(U16_BE_FLAG_LOW_MASK | U16_BE_MODE_MASK | U16_BE_COUNT_MASK | U16_BE_FLAG_HIGH_MASK);
1708 const U16_BE_COUNT_FIELD_MASK: u16 = 0x0F;
1709
1710 const U32_LE_FLAG_LOW_MASK: u32 = 0x0000_0008;
1714 const U32_LE_MODE_MASK: u32 = 0x0000_0180;
1715 const U32_LE_RANGE_MASK: u32 = 0x007F_0000;
1716 const U32_LE_FLAG_HIGH_MASK: u32 = 0x4000_0000;
1717 const U32_LE_RESERVED_MASK: u32 =
1718 !(U32_LE_FLAG_LOW_MASK | U32_LE_MODE_MASK | U32_LE_RANGE_MASK | U32_LE_FLAG_HIGH_MASK);
1719 const U32_LE_RANGE_FIELD_MASK: u32 = 0x7F;
1720
1721 const U32_BE_FLAG_LOW_MASK: u32 = 0x0000_0004;
1725 const U32_BE_MODE_MASK: u32 = 0x0000_0600;
1726 const U32_BE_CHUNK_MASK: u32 = 0x00FE_0000;
1727 const U32_BE_FLAG_HIGH_MASK: u32 = 0x8000_0000;
1728 const U32_BE_RESERVED_MASK: u32 =
1729 !(U32_BE_FLAG_LOW_MASK | U32_BE_MODE_MASK | U32_BE_CHUNK_MASK | U32_BE_FLAG_HIGH_MASK);
1730 const U32_BE_CHUNK_FIELD_MASK: u32 = 0x7F;
1731
1732 const U8_ALTERNATING_BITS_HIGH: u8 = 0xAA;
1734 const U8_ALTERNATING_BITS_LOW: u8 = 0x55;
1735 const U8_TEST_PATTERNS: [u8; 4] =
1736 [u8::MIN, u8::MAX, U8_ALTERNATING_BITS_HIGH, U8_ALTERNATING_BITS_LOW];
1737
1738 const U16_ALTERNATING_BITS_HIGH: u16 = 0xAAAA;
1739 const U16_ALTERNATING_BITS_LOW: u16 = 0x5555;
1740 const U16_TEST_PATTERNS: [u16; 4] =
1741 [u16::MIN, u16::MAX, U16_ALTERNATING_BITS_HIGH, U16_ALTERNATING_BITS_LOW];
1742
1743 const U32_ALTERNATING_BITS_HIGH: u32 = 0xAAAA_AAAA;
1744 const U32_ALTERNATING_BITS_LOW: u32 = 0x5555_5555;
1745 const U32_TEST_PATTERNS: [u32; 4] =
1746 [u32::MIN, u32::MAX, U32_ALTERNATING_BITS_HIGH, U32_ALTERNATING_BITS_LOW];
1747
1748 const U8_REG_BYTES: u32 = std::mem::size_of::<u8>() as u32;
1750 const U16_REG_BYTES: u32 = std::mem::size_of::<u16>() as u32;
1751 const U32_REG_BYTES: u32 = std::mem::size_of::<u32>() as u32;
1752
1753 #[test]
1755 fn test_bitfield_isolation_and_reserved_bits_u8() {
1756 assert_eq!(U8_RESERVED_MASK | U8_ENABLE_MASK | U8_MULTI_MASK | U8_MODE_MASK, u8::MAX);
1757
1758 let u8_chained_multi = 2;
1759
1760 for &base in &U8_TEST_PATTERNS {
1761 let v = synth_u8_reg::Value::new(base);
1762 let reserved = base & U8_RESERVED_MASK;
1763
1764 let v_en = v.set_enable(!v.enable());
1765 assert_eq!(v_en.enable(), !v.enable());
1766 assert_eq!(v_en.reg_value() & U8_RESERVED_MASK, reserved);
1767 assert_eq!(v_en.multi(), v.multi());
1768 assert_eq!(v_en.mode(), v.mode());
1769 assert_eq!(v_en.reg_value() ^ v.reg_value(), U8_ENABLE_MASK);
1770
1771 let new_multi = (v.multi() + 1) & U8_MULTI_FIELD_MASK;
1772 let v_multi = v.set_multi(new_multi);
1773 assert_eq!(v_multi.multi(), new_multi);
1774 assert_eq!(v_multi.reg_value() & U8_RESERVED_MASK, reserved);
1775 assert_eq!(v_multi.enable(), v.enable());
1776 assert_eq!(v_multi.mode(), v.mode());
1777 assert_eq!((v_multi.reg_value() ^ v.reg_value()) & !U8_MULTI_MASK, 0);
1778
1779 let new_mode = synth_u8_reg::SynthU8Mode::Mode2;
1780 let v_mode = v.set_mode(new_mode);
1781 assert_eq!(v_mode.mode(), Ok(new_mode));
1782 assert_eq!(v_mode.reg_value() & U8_RESERVED_MASK, reserved);
1783 assert_eq!(v_mode.enable(), v.enable());
1784 assert_eq!(v_mode.multi(), v.multi());
1785 assert_eq!((v_mode.reg_value() ^ v.reg_value()) & !U8_MODE_MASK, 0);
1786
1787 let v_chained = v
1788 .set_enable(true)
1789 .set_multi(u8_chained_multi)
1790 .set_mode(synth_u8_reg::SynthU8Mode::Mode1);
1791 assert!(v_chained.enable());
1792 assert_eq!(v_chained.multi(), u8_chained_multi);
1793 assert_eq!(v_chained.mode(), Ok(synth_u8_reg::SynthU8Mode::Mode1));
1794 assert_eq!(v_chained.reg_value() & U8_RESERVED_MASK, reserved);
1795 }
1796 }
1797
1798 #[test]
1800 fn test_bitfield_isolation_and_reserved_bits_u16() {
1801 assert_eq!(
1803 U16_LE_RESERVED_MASK
1804 | U16_LE_FLAG_LOW_MASK
1805 | U16_LE_MODE_MASK
1806 | U16_LE_DATA_MASK
1807 | U16_LE_FLAG_HIGH_MASK,
1808 u16::MAX
1809 );
1810
1811 let u16_le_chained_data = 0x7;
1812
1813 for &base in &U16_TEST_PATTERNS {
1814 let v = synth_u16_le_reg::Value::new(base);
1815 let reserved = base & U16_LE_RESERVED_MASK;
1816
1817 let v_fl = v.set_flag_low(!v.flag_low());
1818 assert_eq!(v_fl.flag_low(), !v.flag_low());
1819 assert_eq!(v_fl.reg_value() & U16_LE_RESERVED_MASK, reserved);
1820 assert_eq!(v_fl.mode(), v.mode());
1821 assert_eq!(v_fl.data(), v.data());
1822 assert_eq!(v_fl.flag_high(), v.flag_high());
1823 assert_eq!(v_fl.reg_value() ^ v.reg_value(), U16_LE_FLAG_LOW_MASK);
1824
1825 let new_mode = synth_u16_le_reg::SynthU16LeMode::Active;
1826 let v_mode = v.set_mode(new_mode);
1827 assert_eq!(v_mode.mode(), Ok(new_mode));
1828 assert_eq!(v_mode.reg_value() & U16_LE_RESERVED_MASK, reserved);
1829 assert_eq!(v_mode.flag_low(), v.flag_low());
1830 assert_eq!(v_mode.data(), v.data());
1831 assert_eq!(v_mode.flag_high(), v.flag_high());
1832 assert_eq!((v_mode.reg_value() ^ v.reg_value()) & !U16_LE_MODE_MASK, 0);
1833
1834 let new_data = (v.data() + 1) & U16_LE_DATA_FIELD_MASK;
1835 let v_data = v.set_data(new_data);
1836 assert_eq!(v_data.data(), new_data);
1837 assert_eq!(v_data.reg_value() & U16_LE_RESERVED_MASK, reserved);
1838 assert_eq!(v_data.flag_low(), v.flag_low());
1839 assert_eq!(v_data.mode(), v.mode());
1840 assert_eq!(v_data.flag_high(), v.flag_high());
1841 assert_eq!((v_data.reg_value() ^ v.reg_value()) & !U16_LE_DATA_MASK, 0);
1842
1843 let v_fh = v.set_flag_high(!v.flag_high());
1844 assert_eq!(v_fh.flag_high(), !v.flag_high());
1845 assert_eq!(v_fh.reg_value() & U16_LE_RESERVED_MASK, reserved);
1846 assert_eq!(v_fh.flag_low(), v.flag_low());
1847 assert_eq!(v_fh.mode(), v.mode());
1848 assert_eq!(v_fh.data(), v.data());
1849 assert_eq!(v_fh.reg_value() ^ v.reg_value(), U16_LE_FLAG_HIGH_MASK);
1850
1851 let v_chained = v
1852 .set_flag_low(true)
1853 .set_mode(synth_u16_le_reg::SynthU16LeMode::Standby)
1854 .set_data(u16_le_chained_data)
1855 .set_flag_high(false);
1856 assert!(v_chained.flag_low());
1857 assert_eq!(v_chained.mode(), Ok(synth_u16_le_reg::SynthU16LeMode::Standby));
1858 assert_eq!(v_chained.data(), u16_le_chained_data);
1859 assert!(!v_chained.flag_high());
1860 assert_eq!(v_chained.reg_value() & U16_LE_RESERVED_MASK, reserved);
1861 }
1862
1863 assert_eq!(
1865 U16_BE_RESERVED_MASK
1866 | U16_BE_FLAG_LOW_MASK
1867 | U16_BE_MODE_MASK
1868 | U16_BE_COUNT_MASK
1869 | U16_BE_FLAG_HIGH_MASK,
1870 u16::MAX
1871 );
1872
1873 let u16_be_chained_count = 0xC;
1874
1875 for &base in &U16_TEST_PATTERNS {
1876 let v = synth_u16_be_reg::Value::new(base);
1877 let reserved = base & U16_BE_RESERVED_MASK;
1878
1879 let v_fl = v.set_flag_low(!v.flag_low());
1880 assert_eq!(v_fl.flag_low(), !v.flag_low());
1881 assert_eq!(v_fl.reg_value() & U16_BE_RESERVED_MASK, reserved);
1882 assert_eq!(v_fl.reg_value() ^ v.reg_value(), U16_BE_FLAG_LOW_MASK);
1883
1884 let new_mode = synth_u16_be_reg::SynthU16BeMode::Gamma;
1885 let v_mode = v.set_mode(new_mode);
1886 assert_eq!(v_mode.mode(), Ok(new_mode));
1887 assert_eq!(v_mode.reg_value() & U16_BE_RESERVED_MASK, reserved);
1888 assert_eq!((v_mode.reg_value() ^ v.reg_value()) & !U16_BE_MODE_MASK, 0);
1889
1890 let new_count = (v.count() + 1) & U16_BE_COUNT_FIELD_MASK;
1891 let v_count = v.set_count(new_count);
1892 assert_eq!(v_count.count(), new_count);
1893 assert_eq!(v_count.reg_value() & U16_BE_RESERVED_MASK, reserved);
1894 assert_eq!((v_count.reg_value() ^ v.reg_value()) & !U16_BE_COUNT_MASK, 0);
1895
1896 let v_fh = v.set_flag_high(!v.flag_high());
1897 assert_eq!(v_fh.flag_high(), !v.flag_high());
1898 assert_eq!(v_fh.reg_value() & U16_BE_RESERVED_MASK, reserved);
1899 assert_eq!(v_fh.reg_value() ^ v.reg_value(), U16_BE_FLAG_HIGH_MASK);
1900
1901 let v_chained = v
1902 .set_flag_low(false)
1903 .set_mode(synth_u16_be_reg::SynthU16BeMode::Beta)
1904 .set_count(u16_be_chained_count)
1905 .set_flag_high(true);
1906 assert!(!v_chained.flag_low());
1907 assert_eq!(v_chained.mode(), Ok(synth_u16_be_reg::SynthU16BeMode::Beta));
1908 assert_eq!(v_chained.count(), u16_be_chained_count);
1909 assert!(v_chained.flag_high());
1910 assert_eq!(v_chained.reg_value() & U16_BE_RESERVED_MASK, reserved);
1911 }
1912 }
1913
1914 #[test]
1916 fn test_bitfield_isolation_and_reserved_bits_u32() {
1917 assert_eq!(
1919 U32_LE_RESERVED_MASK
1920 | U32_LE_FLAG_LOW_MASK
1921 | U32_LE_MODE_MASK
1922 | U32_LE_RANGE_MASK
1923 | U32_LE_FLAG_HIGH_MASK,
1924 u32::MAX
1925 );
1926
1927 let u32_le_chained_range = 0x3B;
1928
1929 for &base in &U32_TEST_PATTERNS {
1930 let v = synth_u32_le_reg::Value::new(base);
1931 let reserved = base & U32_LE_RESERVED_MASK;
1932
1933 let v_fl = v.set_flag_low(!v.flag_low());
1934 assert_eq!(v_fl.flag_low(), !v.flag_low());
1935 assert_eq!(v_fl.reg_value() & U32_LE_RESERVED_MASK, reserved);
1936 assert_eq!(v_fl.mode(), v.mode());
1937 assert_eq!(v_fl.range(), v.range());
1938 assert_eq!(v_fl.flag_high(), v.flag_high());
1939 assert_eq!(v_fl.reg_value() ^ v.reg_value(), U32_LE_FLAG_LOW_MASK);
1940
1941 let new_mode = synth_u32_le_reg::SynthU32LeMode::Two;
1942 let v_mode = v.set_mode(new_mode);
1943 assert_eq!(v_mode.mode(), Ok(new_mode));
1944 assert_eq!(v_mode.reg_value() & U32_LE_RESERVED_MASK, reserved);
1945 assert_eq!(v_mode.flag_low(), v.flag_low());
1946 assert_eq!(v_mode.range(), v.range());
1947 assert_eq!(v_mode.flag_high(), v.flag_high());
1948 assert_eq!((v_mode.reg_value() ^ v.reg_value()) & !U32_LE_MODE_MASK, 0);
1949
1950 let new_range = (v.range() + 1) & U32_LE_RANGE_FIELD_MASK;
1951 let v_range = v.set_range(new_range);
1952 assert_eq!(v_range.range(), new_range);
1953 assert_eq!(v_range.reg_value() & U32_LE_RESERVED_MASK, reserved);
1954 assert_eq!(v_range.flag_low(), v.flag_low());
1955 assert_eq!(v_range.mode(), v.mode());
1956 assert_eq!(v_range.flag_high(), v.flag_high());
1957 assert_eq!((v_range.reg_value() ^ v.reg_value()) & !U32_LE_RANGE_MASK, 0);
1958
1959 let v_fh = v.set_flag_high(!v.flag_high());
1960 assert_eq!(v_fh.flag_high(), !v.flag_high());
1961 assert_eq!(v_fh.reg_value() & U32_LE_RESERVED_MASK, reserved);
1962 assert_eq!(v_fh.flag_low(), v.flag_low());
1963 assert_eq!(v_fh.mode(), v.mode());
1964 assert_eq!(v_fh.range(), v.range());
1965 assert_eq!(v_fh.reg_value() ^ v.reg_value(), U32_LE_FLAG_HIGH_MASK);
1966
1967 let v_chained = v
1968 .set_flag_low(true)
1969 .set_mode(synth_u32_le_reg::SynthU32LeMode::Three)
1970 .set_range(u32_le_chained_range)
1971 .set_flag_high(false);
1972 assert!(v_chained.flag_low());
1973 assert_eq!(v_chained.mode(), Ok(synth_u32_le_reg::SynthU32LeMode::Three));
1974 assert_eq!(v_chained.range(), u32_le_chained_range);
1975 assert!(!v_chained.flag_high());
1976 assert_eq!(v_chained.reg_value() & U32_LE_RESERVED_MASK, reserved);
1977 }
1978
1979 assert_eq!(
1981 U32_BE_RESERVED_MASK
1982 | U32_BE_FLAG_LOW_MASK
1983 | U32_BE_MODE_MASK
1984 | U32_BE_CHUNK_MASK
1985 | U32_BE_FLAG_HIGH_MASK,
1986 u32::MAX
1987 );
1988
1989 let u32_be_chained_chunk = 0x6E;
1990
1991 for &base in &U32_TEST_PATTERNS {
1992 let v = synth_u32_be_reg::Value::new(base);
1993 let reserved = base & U32_BE_RESERVED_MASK;
1994
1995 let v_fl = v.set_flag_low(!v.flag_low());
1996 assert_eq!(v_fl.flag_low(), !v.flag_low());
1997 assert_eq!(v_fl.reg_value() & U32_BE_RESERVED_MASK, reserved);
1998 assert_eq!(v_fl.reg_value() ^ v.reg_value(), U32_BE_FLAG_LOW_MASK);
1999
2000 let new_mode = synth_u32_be_reg::SynthU32BeMode::Quaternary;
2001 let v_mode = v.set_mode(new_mode);
2002 assert_eq!(v_mode.mode(), Ok(new_mode));
2003 assert_eq!(v_mode.reg_value() & U32_BE_RESERVED_MASK, reserved);
2004 assert_eq!((v_mode.reg_value() ^ v.reg_value()) & !U32_BE_MODE_MASK, 0);
2005
2006 let new_chunk = (v.chunk() + 1) & U32_BE_CHUNK_FIELD_MASK;
2007 let v_chunk = v.set_chunk(new_chunk);
2008 assert_eq!(v_chunk.chunk(), new_chunk);
2009 assert_eq!(v_chunk.reg_value() & U32_BE_RESERVED_MASK, reserved);
2010 assert_eq!((v_chunk.reg_value() ^ v.reg_value()) & !U32_BE_CHUNK_MASK, 0);
2011
2012 let v_fh = v.set_flag_high(!v.flag_high());
2013 assert_eq!(v_fh.flag_high(), !v.flag_high());
2014 assert_eq!(v_fh.reg_value() & U32_BE_RESERVED_MASK, reserved);
2015 assert_eq!(v_fh.reg_value() ^ v.reg_value(), U32_BE_FLAG_HIGH_MASK);
2016
2017 let v_chained = v
2018 .set_flag_low(false)
2019 .set_mode(synth_u32_be_reg::SynthU32BeMode::Secondary)
2020 .set_chunk(u32_be_chained_chunk)
2021 .set_flag_high(true);
2022 assert!(!v_chained.flag_low());
2023 assert_eq!(v_chained.mode(), Ok(synth_u32_be_reg::SynthU32BeMode::Secondary));
2024 assert_eq!(v_chained.chunk(), u32_be_chained_chunk);
2025 assert!(v_chained.flag_high());
2026 assert_eq!(v_chained.reg_value() & U32_BE_RESERVED_MASK, reserved);
2027 }
2028 }
2029
2030 #[fuchsia::test]
2032 async fn test_spmi_read_modify_write_preserves_reserved_bits_le() {
2033 let device = TestSpmiDevice::new();
2034 let regs = SynthRegs::new(device);
2035
2036 let u8_write_multi = 3;
2038 for u8_reserved_seed in [
2039 U8_RESERVED_MASK & U8_ALTERNATING_BITS_HIGH,
2040 U8_RESERVED_MASK & U8_ALTERNATING_BITS_LOW,
2041 ] {
2042 assert!(u8_reserved_seed != 0 && u8_reserved_seed != U8_RESERVED_MASK);
2044
2045 regs.spmi.write_reg(synth_u8_reg::ADDRESS, &[u8_reserved_seed]).await.unwrap();
2046 let val_u8 = regs.u8_reg().read().await.unwrap();
2047 let updated_u8 = val_u8
2048 .set_enable(true)
2049 .set_multi(u8_write_multi)
2050 .set_mode(synth_u8_reg::SynthU8Mode::Mode1);
2051 regs.u8_reg().write(updated_u8).await.unwrap();
2052
2053 let raw_u8 = regs.spmi.read_reg(synth_u8_reg::ADDRESS, U8_REG_BYTES).await.unwrap()[0];
2054 assert_eq!(
2055 raw_u8 & U8_RESERVED_MASK,
2056 u8_reserved_seed,
2057 "u8 reserved bits mismatch for seed {u8_reserved_seed:#04x}"
2058 );
2059 let final_u8 = regs.u8_reg().read().await.unwrap();
2060 assert!(final_u8.enable());
2061 assert_eq!(final_u8.multi(), u8_write_multi);
2062 assert_eq!(final_u8.mode(), Ok(synth_u8_reg::SynthU8Mode::Mode1));
2063 }
2064
2065 let u16_le_write_data = 0x9;
2067 for u16_le_reserved_seed in [
2068 U16_LE_RESERVED_MASK & U16_ALTERNATING_BITS_HIGH,
2069 U16_LE_RESERVED_MASK & U16_ALTERNATING_BITS_LOW,
2070 ] {
2071 assert!(u16_le_reserved_seed != 0 && u16_le_reserved_seed != U16_LE_RESERVED_MASK);
2073
2074 regs.spmi
2075 .write_reg(synth_u16_le_reg::ADDRESS, &u16_le_reserved_seed.to_le_bytes())
2076 .await
2077 .unwrap();
2078 let val_u16 = regs.u16_le_reg().read().await.unwrap();
2079 let updated_u16 = val_u16
2080 .set_flag_low(true)
2081 .set_flag_high(true)
2082 .set_data(u16_le_write_data)
2083 .set_mode(synth_u16_le_reg::SynthU16LeMode::Active);
2084 regs.u16_le_reg().write(updated_u16).await.unwrap();
2085
2086 let raw_u16_bytes =
2087 regs.spmi.read_reg(synth_u16_le_reg::ADDRESS, U16_REG_BYTES).await.unwrap();
2088 let raw_u16 = u16::from_le_bytes(raw_u16_bytes.try_into().unwrap());
2089 assert_eq!(
2090 raw_u16 & U16_LE_RESERVED_MASK,
2091 u16_le_reserved_seed,
2092 "u16 LE reserved bits mismatch for seed {u16_le_reserved_seed:#06x}"
2093 );
2094 let final_u16 = regs.u16_le_reg().read().await.unwrap();
2095 assert!(final_u16.flag_low());
2096 assert!(final_u16.flag_high());
2097 assert_eq!(final_u16.data(), u16_le_write_data);
2098 assert_eq!(final_u16.mode(), Ok(synth_u16_le_reg::SynthU16LeMode::Active));
2099 }
2100
2101 let u32_le_write_range = 0x42;
2103 for u32_le_reserved_seed in [
2104 U32_LE_RESERVED_MASK & U32_ALTERNATING_BITS_HIGH,
2105 U32_LE_RESERVED_MASK & U32_ALTERNATING_BITS_LOW,
2106 ] {
2107 assert!(u32_le_reserved_seed != 0 && u32_le_reserved_seed != U32_LE_RESERVED_MASK);
2109
2110 regs.spmi
2111 .write_reg(synth_u32_le_reg::ADDRESS, &u32_le_reserved_seed.to_le_bytes())
2112 .await
2113 .unwrap();
2114 let val_u32 = regs.u32_le_reg().read().await.unwrap();
2115 let updated_u32 = val_u32
2116 .set_flag_low(true)
2117 .set_flag_high(true)
2118 .set_range(u32_le_write_range)
2119 .set_mode(synth_u32_le_reg::SynthU32LeMode::Two);
2120 regs.u32_le_reg().write(updated_u32).await.unwrap();
2121
2122 let raw_u32_bytes =
2123 regs.spmi.read_reg(synth_u32_le_reg::ADDRESS, U32_REG_BYTES).await.unwrap();
2124 let raw_u32 = u32::from_le_bytes(raw_u32_bytes.try_into().unwrap());
2125 assert_eq!(
2126 raw_u32 & U32_LE_RESERVED_MASK,
2127 u32_le_reserved_seed,
2128 "u32 LE reserved bits mismatch for seed {u32_le_reserved_seed:#010x}"
2129 );
2130 let final_u32 = regs.u32_le_reg().read().await.unwrap();
2131 assert!(final_u32.flag_low());
2132 assert!(final_u32.flag_high());
2133 assert_eq!(final_u32.range(), u32_le_write_range);
2134 assert_eq!(final_u32.mode(), Ok(synth_u32_le_reg::SynthU32LeMode::Two));
2135 }
2136 }
2137
2138 #[fuchsia::test]
2140 async fn test_spmi_read_modify_write_preserves_reserved_bits_be() {
2141 let device = TestSpmiDevice::new();
2142 let regs = SynthRegs::new(device);
2143
2144 let u16_be_write_count = 0xB;
2146 for u16_be_reserved_seed in [
2147 U16_BE_RESERVED_MASK & U16_ALTERNATING_BITS_HIGH,
2148 U16_BE_RESERVED_MASK & U16_ALTERNATING_BITS_LOW,
2149 ] {
2150 assert!(u16_be_reserved_seed != 0 && u16_be_reserved_seed != U16_BE_RESERVED_MASK);
2152
2153 regs.spmi
2154 .write_reg(synth_u16_be_reg::ADDRESS, &u16_be_reserved_seed.to_be_bytes())
2155 .await
2156 .unwrap();
2157 let val_u16 = regs.u16_be_reg().read().await.unwrap();
2158 let updated_u16 = val_u16
2159 .set_flag_low(true)
2160 .set_flag_high(true)
2161 .set_count(u16_be_write_count)
2162 .set_mode(synth_u16_be_reg::SynthU16BeMode::Delta);
2163 regs.u16_be_reg().write(updated_u16).await.unwrap();
2164
2165 let raw_u16_bytes =
2166 regs.spmi.read_reg(synth_u16_be_reg::ADDRESS, U16_REG_BYTES).await.unwrap();
2167 let raw_u16 = u16::from_be_bytes(raw_u16_bytes.try_into().unwrap());
2168 assert_eq!(
2169 raw_u16 & U16_BE_RESERVED_MASK,
2170 u16_be_reserved_seed,
2171 "u16 BE reserved bits mismatch for seed {u16_be_reserved_seed:#06x}"
2172 );
2173 let final_u16 = regs.u16_be_reg().read().await.unwrap();
2174 assert!(final_u16.flag_low());
2175 assert!(final_u16.flag_high());
2176 assert_eq!(final_u16.count(), u16_be_write_count);
2177 assert_eq!(final_u16.mode(), Ok(synth_u16_be_reg::SynthU16BeMode::Delta));
2178 }
2179
2180 let u32_be_write_chunk = 0x55;
2182 for u32_be_reserved_seed in [
2183 U32_BE_RESERVED_MASK & U32_ALTERNATING_BITS_HIGH,
2184 U32_BE_RESERVED_MASK & U32_ALTERNATING_BITS_LOW,
2185 ] {
2186 assert!(u32_be_reserved_seed != 0 && u32_be_reserved_seed != U32_BE_RESERVED_MASK);
2188
2189 regs.spmi
2190 .write_reg(synth_u32_be_reg::ADDRESS, &u32_be_reserved_seed.to_be_bytes())
2191 .await
2192 .unwrap();
2193 let val_u32 = regs.u32_be_reg().read().await.unwrap();
2194 let updated_u32 = val_u32
2195 .set_flag_low(true)
2196 .set_flag_high(true)
2197 .set_chunk(u32_be_write_chunk)
2198 .set_mode(synth_u32_be_reg::SynthU32BeMode::Tertiary);
2199 regs.u32_be_reg().write(updated_u32).await.unwrap();
2200
2201 let raw_u32_bytes =
2202 regs.spmi.read_reg(synth_u32_be_reg::ADDRESS, U32_REG_BYTES).await.unwrap();
2203 let raw_u32 = u32::from_be_bytes(raw_u32_bytes.try_into().unwrap());
2204 assert_eq!(
2205 raw_u32 & U32_BE_RESERVED_MASK,
2206 u32_be_reserved_seed,
2207 "u32 BE reserved bits mismatch for seed {u32_be_reserved_seed:#010x}"
2208 );
2209 let final_u32 = regs.u32_be_reg().read().await.unwrap();
2210 assert!(final_u32.flag_low());
2211 assert!(final_u32.flag_high());
2212 assert_eq!(final_u32.chunk(), u32_be_write_chunk);
2213 assert_eq!(final_u32.mode(), Ok(synth_u32_be_reg::SynthU32BeMode::Tertiary));
2214 }
2215 }
2216}