diff --git a/src/utils/field.rs b/src/utils/field.rs index 4746ae5..0437d3f 100644 --- a/src/utils/field.rs +++ b/src/utils/field.rs @@ -1,5 +1,7 @@ //! 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. @@ -23,7 +25,6 @@ pub struct Field { mask: u32 } -// TODO: test impl Field { /// Constructor for a Field. /// The field is defined by the `size` bits at `offset` bits after @@ -65,7 +66,6 @@ impl Field { } /// Return the offset of the field in relation to the address, in bits. - #[allow(dead_code)] pub const fn get_offset(&self) -> usize { self.offset } @@ -80,6 +80,39 @@ impl Field { 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)] @@ -87,8 +120,114 @@ mod test { use super::*; #[test] - fn test() { - let f = Field::new(0, 0, 1); - assert_eq!(1, f.get_mask()); + 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, + ); } }