esp_hal/soc/
efuse_field.rs

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