esp_hal/soc/
efuse_field.rs

1use bytemuck::AnyBitPattern;
2
3use crate::soc::efuse::{Efuse, EfuseBlock};
4
5/// The bit field for get access to efuse data
6#[derive(Clone, Copy)]
7pub struct EfuseField {
8    blk: EfuseBlock,
9    bit_off: u16,
10    bit_len: u16,
11}
12
13impl EfuseField {
14    pub(crate) const fn new(blk: EfuseBlock, bit_off: u16, bit_len: u16) -> Self {
15        Self {
16            blk,
17            bit_off,
18            bit_len,
19        }
20    }
21}
22
23impl Efuse {
24    /// Read field value in a little-endian order
25    #[inline(always)]
26    pub fn read_field_le<T: AnyBitPattern>(field: EfuseField) -> T {
27        let mut output = core::mem::MaybeUninit::<T>::uninit();
28        // represent output value as a bytes slice
29        let mut bytes = unsafe {
30            core::slice::from_raw_parts_mut(
31                output.as_mut_ptr() as *mut u8,
32                core::mem::size_of::<T>(),
33            )
34        };
35        // get block address
36        let block_address = field.blk.address();
37
38        let bit_off = field.bit_off as usize;
39        let bit_end = (field.bit_len as usize).min(bytes.len() * 8) + bit_off;
40
41        let mut last_word_off = bit_off / 32;
42        let mut last_word = unsafe { block_address.add(last_word_off).read_volatile() };
43        let word_bit_off = bit_off % 32;
44        let word_bit_ext = 32 - word_bit_off;
45        let mut word_off = last_word_off;
46
47        for bit_off in (bit_off..bit_end).step_by(32) {
48            let word_bit_len = 32.min(bit_end - bit_off);
49
50            if word_off != last_word_off {
51                // read new word
52                last_word_off = word_off;
53                last_word = unsafe { block_address.add(last_word_off).read_volatile() };
54            }
55
56            let mut word = last_word >> word_bit_off;
57
58            word_off += 1;
59
60            if word_bit_len > word_bit_ext {
61                // read the next word
62                last_word_off = word_off;
63                last_word = unsafe { block_address.add(last_word_off).read_volatile() };
64                // append bits from a beginning of the next word
65                word |= last_word.wrapping_shl((32 - word_bit_off) as u32);
66            };
67
68            if word_bit_len < 32 {
69                // mask only needed bits of a word
70                word &= u32::MAX >> (32 - word_bit_len);
71            }
72
73            // get data length in bytes (ceil)
74            let byte_len = word_bit_len.div_ceil(8);
75            // represent word as a byte slice
76            let word_bytes =
77                unsafe { core::slice::from_raw_parts(&word as *const u32 as *const u8, byte_len) };
78
79            // copy word bytes to output value bytes
80            bytes[..byte_len].copy_from_slice(word_bytes);
81
82            // move read window forward
83            bytes = &mut bytes[byte_len..];
84        }
85
86        // fill untouched bytes with zeros
87        bytes.fill(0);
88
89        unsafe { output.assume_init() }
90    }
91
92    /// Read field value in a big-endian order
93    #[inline(always)]
94    pub fn read_field_be<T: AnyBitPattern>(field: EfuseField) -> T {
95        // read value in a little-endian order
96        let mut output = Self::read_field_le::<T>(field);
97        // represent output value as a byte slice
98        let bytes = unsafe {
99            core::slice::from_raw_parts_mut(
100                &mut output as *mut T as *mut u8,
101                core::mem::size_of::<T>(),
102            )
103        };
104        // reverse byte order
105        bytes.reverse();
106        output
107    }
108
109    /// Read bit value.
110    ///
111    /// This function panics if the field's bit length is not equal to 1.
112    #[inline(always)]
113    #[cfg_attr(not(feature = "unstable"), allow(unused))]
114    pub fn read_bit(field: EfuseField) -> bool {
115        assert_eq!(field.bit_len, 1);
116        Self::read_field_le::<u8>(field) != 0
117    }
118}