esp-hal/esp-hal-common/src/soc/efuse_field.rs
Kayo Phoenix c6bdf8b8c0
Unified efuse field access (#567)
* Add efuse field tables from IDF

* Add efuse fields constants generator

* Fix MAC field in efuse tables

* Add common efuse field impl

* Add efuse fields support for chips

* Add efuse changes to changelog
2023-06-05 06:15:00 -07:00

109 lines
3.4 KiB
Rust

use crate::soc::efuse::{Efuse, EfuseBlock};
include!(concat!(env!("OUT_DIR"), "/efuse_fields.rs"));
/// The bit field for get access to efuse data
#[derive(Clone, Copy)]
pub struct EfuseField {
blk: EfuseBlock,
bit_off: u16,
bit_len: u16,
}
impl EfuseField {
pub(crate) const fn new(blk: EfuseBlock, bit_off: u16, bit_len: u16) -> Self {
Self {
blk,
bit_off,
bit_len,
}
}
}
impl Efuse {
/// Read field value in a little-endian order
#[inline(always)]
pub fn read_field_le<T: Sized + 'static>(field: EfuseField) -> T {
let mut output = core::mem::MaybeUninit::<T>::uninit();
// represent output value as a bytes slice
let mut bytes = unsafe {
core::slice::from_raw_parts_mut(
output.as_mut_ptr() as *mut u8,
core::mem::size_of::<T>(),
)
};
// get block address
let block_address = field.blk.address();
let bit_off = field.bit_off as usize;
let bit_end = (field.bit_len as usize).min(bytes.len() * 8) + bit_off;
let mut last_word_off = bit_off / 32;
let mut last_word = unsafe { block_address.add(last_word_off).read_volatile() };
let word_bit_off = bit_off % 32;
let word_bit_ext = 32 - word_bit_off;
let mut word_off = last_word_off;
for bit_off in (bit_off..bit_end).step_by(32) {
let word_bit_len = 32.min(bit_end - bit_off);
if word_off != last_word_off {
// read new word
last_word_off = word_off;
last_word = unsafe { block_address.add(last_word_off).read_volatile() };
}
let mut word = last_word >> word_bit_off;
word_off += 1;
if word_bit_len > word_bit_ext {
// read the next word
last_word_off = word_off;
last_word = unsafe { block_address.add(last_word_off).read_volatile() };
// append bits from a beginning of the next word
word |= last_word.wrapping_shl((32 - word_bit_off) as u32);
};
if word_bit_len < 32 {
// mask only needed bits of a word
word &= u32::MAX >> (32 - word_bit_len);
}
// get data length in bytes (ceil)
let byte_len = (word_bit_len + 7) / 8;
// represent word as a byte slice
let word_bytes =
unsafe { core::slice::from_raw_parts(&word as *const u32 as *const u8, byte_len) };
// copy word bytes to output value bytes
bytes[..byte_len].copy_from_slice(word_bytes);
// move read window forward
bytes = &mut bytes[byte_len..];
}
// fill untouched bytes with zeros
bytes.fill(0);
unsafe { output.assume_init() }
}
/// Read field value in a big-endian order
#[inline(always)]
pub fn read_field_be<T: Sized + 'static>(field: EfuseField) -> T {
// read value in a little-endian order
let mut output = Self::read_field_le::<T>(field);
// represent output value as a byte slice
let bytes = unsafe {
core::slice::from_raw_parts_mut(
&mut output as *mut T as *mut u8,
core::mem::size_of::<T>(),
)
};
// reverse byte order
bytes.reverse();
output
}
}