//! Implement the Field structure. use core::ptr::{read_volatile, write_volatile}; /// Structure representing the location of a register. /// This structure does not provide any kind of safety, it /// is only descriptif. /// /// Fields represent a continuous set of bits in an alligned /// `u32`. This means that the address is a multiple of 4, /// the offset is < 32, and offset + size <= 32. /// /// The constructor of the is class ensure that the field is /// always inside the `u32` at `address`, and that `address` /// is alligned. /// /// TODO: Mark write method as unsafe? pub struct Field { /// The address of the discribed field. address: usize, /// The offset of the first bit of the field relative to the /// address. offset: usize, /// The size of the field in bit. size: usize, /// The mask for the field in the `u32` at `address`. mask: u32 } impl Field { /// Constructor for a Field. /// The field is defined by the `size` bits at `offset` bits after /// `address`. (**`address` is in bytes, not bits**) pub const fn new(address: usize, offset: usize, size: usize) -> Self { // align the address let offset = offset + 8 * (address % 4); let address = address - (address % 4); // make sure the field is in the u32 at address let address = address + 4 * (offset / 32); let offset = offset % 32; // make sure the field does not overlap with the next u32 if offset + size > 32 { panic!("A field can not overlap two aligned `u32`"); } let mask = Self::compute_mask(offset, size); Self { address, offset, size, mask } } /// Compute mask for the field. const fn compute_mask(offset: usize, size: usize) -> u32 { let mut mask = 0u32; // Const functions don't allow for loops. let mut i = 0; while i < size { mask <<= 1; mask |= 1; i += 1; } mask <<= offset; mask } /// Return the address of the `u32` containing the field. pub const fn get_address(&self) -> usize { self.address } /// Return the offset of the field in relation to the address, in bits. pub const fn get_offset(&self) -> usize { self.offset } /// Return the size of the field in bit. #[allow(dead_code)] pub const fn get_size(&self) -> usize { self.size } /// Return the mask of the field in the `u32` at `address`. pub const fn get_mask(&self) -> u32 { self.mask } /// Set the value of the field in the provided u32. #[inline] pub fn read_and_write_to_u32(&self, val: u32, register: u32) -> u32 { assert_eq!(val & !(self.mask >> self.offset), 0, "Value greater than the field"); register & !self.mask | (val << self.offset) } /// Get the value of the field from the provided u32. #[inline] pub fn read_from_u32(&self, register: u32) -> u32 { (register & self.mask) >> self.offset } /// Write the value in memory. pub fn read_and_write(&self, val: u32) { let register = unsafe { read_volatile(self.address as *mut u32) }; let register = self.read_and_write_to_u32(val, register); unsafe { write_volatile(self.address as *mut u32, register) }; } /// Write the value in memory but override every other value of the /// u32 (replace them by 0s). pub fn write_without_read(&self, val: u32) { let register = self.read_and_write_to_u32(val, 0); unsafe { write_volatile(self.address as *mut u32, register) }; } /// Read the value from memory. pub fn read(&self) -> u32 { let register = unsafe { read_volatile(self.address as *mut u32) }; self.read_from_u32(register) } } #[cfg(test)] mod test { use super::*; #[test] fn test_get_address() { // Test when the address is alligned assert_eq!( Field::new(0x1234_5678, 0, 1).get_address(), 0x1234_5678, ); } #[test] fn test_get_offset() { assert_eq!( Field::new(0x1234_5678, 18, 1).get_offset(), 18, ); } #[test] fn test_get_size() { assert_eq!( Field::new(0x1234_5678, 18, 1).get_size(), 1, ); } #[test] fn test_alignment_enforcement() { let field = Field::new(0x1111_1110, 0, 1); assert_eq!(field.get_address(), 0x1111_1110); assert_eq!(field.get_offset(), 0); let field = Field::new(0x1111_1111, 0, 1); assert_eq!(field.get_address(), 0x1111_1110); assert_eq!(field.get_offset(), 8); let field = Field::new(0x1111_1112, 0, 1); assert_eq!(field.get_address(), 0x1111_1110); assert_eq!(field.get_offset(), 16); let field = Field::new(0x1111_1113, 0, 1); assert_eq!(field.get_address(), 0x1111_1110); assert_eq!(field.get_offset(), 24); } #[test] fn test_closeness_enforcement() { let field = Field::new(0x1111_1110, 32, 1); assert_eq!(field.get_address(), 0x1111_1114); assert_eq!(field.get_offset(), 0); let field = Field::new(0x1111_1111, 32, 1); assert_eq!(field.get_address(), 0x1111_1114); assert_eq!(field.get_offset(), 8); let field = Field::new(0x1111_1112, 32+16, 1); assert_eq!(field.get_address(), 0x1111_1118); assert_eq!(field.get_offset(), 0); } #[test] #[should_panic] fn test_prevent_overlaps() { let _ = Field::new(0, 24, 16); } #[test] fn test_get_mask() { assert_eq!( Field::new(0, 0, 1).get_mask(), 0b0000_0001, ); assert_eq!( Field::new(0, 0, 3).get_mask(), 0b0000_0111, ); assert_eq!( Field::new(0, 4, 1).get_mask(), 0b0001_0000, ); assert_eq!( Field::new(0, 4, 3).get_mask(), 0b0111_0000, ); } #[test] fn test_read_and_write_to_u32() { let field = Field::new(0, 3, 4); let val = 0b0101_0101; assert_eq!( field.read_and_write_to_u32(0b0101, val), 0b0010_1101, ); } #[test] #[should_panic] fn test_read_and_write_to_u32_too_big() { let field = Field::new(0, 0, 3); let _ = field.read_and_write_to_u32(0b1111, 0); } #[test] fn test_read_from_u32() { let field = Field::new(0, 3, 4); let val = 0b1100_1111; assert_eq!( field.read_from_u32(val), 0b1001, ); } }