Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eeprom #51

Merged
merged 8 commits into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions examples/tiva-c-launchpad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch
use core::fmt::Write;
use cortex_m_rt::entry;
use tm4c123x_hal::{self as hal, prelude::*};
use tm4c123x_hal::eeprom::{Eeprom, Read, Write as EepromWrite, EepromAddress, EepromError, Erase, Blocks};

#[entry]
fn main() -> ! {
Expand All @@ -20,6 +21,17 @@ fn main() -> ! {

let mut porta = p.GPIO_PORTA.split(&sc.power_control);

let mut eeprom = Eeprom::new(p.EEPROM, &sc.power_control);

match eeprom_test_all(&mut eeprom) {
Ok(_) => {
// Huzzah!
}
Err(code) => {
panic!("Error detected while testing EEPROM: {}", code);
}
}

// Activate UART
let mut uart = hal::serial::Serial::uart0(
p.UART0,
Expand All @@ -43,3 +55,59 @@ fn main() -> ! {
counter = counter.wrapping_add(1);
}
}

pub fn eeprom_test_write_read(eeprom: &mut Eeprom, address: &EepromAddress, data_to_write: &[u8], read_buffer: &mut [u8]) -> Result<(), EepromError> {
eeprom.write(address, &data_to_write)?;
eeprom.read(address, data_to_write.len(), read_buffer)?;

for (i, byte) in data_to_write.iter().enumerate() {
assert_eq!(*byte, read_buffer[i], "Read data differs from written data");
}

Ok(())
}

pub fn eeprom_test_all(eeprom: &mut Eeprom) -> Result<(), EepromError> {
let mut buffer = [0 as u8; 64]; // 64 byte read buffer

// Sanity check for simple mapping from word offset to an EepromAddress
let mut address = eeprom.word_index_to_address(52).unwrap();
assert_eq!(address.block(), 3, "Word 52 should be in block 3, offset 4");
assert_eq!(address.offset(), 4, "Word 52 should be in block 3, offset 4");

// Sanity check for EepromAddress to word offset
let word_index = eeprom.address_to_word_index(&address).unwrap();
assert_eq!(word_index, 52, "Word index for block 3, offset 4 should be 52");

// Simplest case, middle of a block, no straddle
let test_array_1: [u8; 4] = [1, 2, 3, 4];
eeprom_test_write_read(eeprom, &mut address, &test_array_1, &mut buffer)?;

// Test boundry conditions for access that straddles a block
let test_array_2: [u8; 10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let address_straddle_block = EepromAddress::new(0, 15);
eeprom_test_write_read(eeprom, &address_straddle_block, &test_array_2, &mut buffer)?;

eeprom.erase(&address_straddle_block, test_array_2.len())?;
eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?;
for i in 0..test_array_2.len() {
assert_eq!(buffer[i], 0, "Buffer should be all 0's")
}

// Test the block erase using the straddle address and data
eeprom.write(&address_straddle_block, &test_array_2)?;
eeprom.erase_block(0)?;
eeprom.read(&address_straddle_block, test_array_2.len(), &mut buffer)?;
for i in 0..test_array_2.len() {
match i {
0..=3 => {
assert_eq!(buffer[i], 0, "Buffer[0..3] should be all 0's");
}
_ => {
assert_eq!(buffer[i], test_array_2[i], "Buffer[4..9] should match test_array_2")
}
}
}

Ok(())
}
138 changes: 138 additions & 0 deletions tm4c-hal/src/eeprom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//! Code for the EEProm module.
//!
//! Tested on a TM4C123 Tiva C Series Launchpad
//!
//! Note: This code manually increments the EEBLOCK and EEOFFSET registers
//! after each read and write instead of using the EERDWRINC register. The
//! debugger was giving inconsistent register results for the EEOFFSET register
//! after using EERDWRINC. Also, the EERDWRINC does not increment the block in
//! the case of a wrap of the offset, so it seems less useful for data that
//! spans blocks.
//!
//! This flexibility comes at the cost of efficiency, as the
//! datasheet calls for at least 4 cycles of delay after setting the EEBLOCK
//! register.

/// Possible errors for the Flash memory module
#[derive(Debug, PartialEq)]
pub enum EepromError {
/// Eeprom is not finished
Busy,
/// Address is out of bounds
AddressOutOfBounds,
/// Block is out of bounds
BlockOutOfBounds,
/// Offset is out of bounds
OffsetOutOfBounds,
/// Indicates that writing data would exceed the EEPROM memory space
WriteWouldOverflow,
/// Indicates that reading data would exceed the EEPROM memory space
ReadWouldOverflow,
/// Requesting to read more data than the provided buffer can hold
ReadBufferTooSmall,
}

impl core::fmt::Display for EepromError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
EepromError::Busy => write!(f, "Eeprom is busy"),
EepromError::AddressOutOfBounds => write!(f, "Address is out of bounds"),
EepromError::BlockOutOfBounds => write!(f, "Block is out of bounds"),
EepromError::OffsetOutOfBounds => write!(f, "Offset is out of bounds"),
EepromError::WriteWouldOverflow => write!(f, "Writing this data would overflow the EEPROM"),
EepromError::ReadWouldOverflow => write!(f, "Reading this data would overflow the EEPROM"),
EepromError::ReadBufferTooSmall => write!(f, "Allocated buffer too small for reading"),
}
}
}

/// Struct used to pack the block and offset
#[derive(Clone, Copy)]
pub struct EepromAddress {
/// Eeprom block
block: usize,
/// Eeprom offset in a block
offset: usize,
}

impl EepromAddress {
/// Creates a new EepromAddres with configured block and offset
pub fn new(block: usize, offset: usize) -> Self {
EepromAddress { block, offset }
}

/// Returns the block
pub fn block(&self) -> usize {
self.block
}

/// Returns the offset
pub fn offset(&self) -> usize {
self.offset
}

/// Increments the offset by one, if that would cause an overflow, increment the block. If
/// both the block and offset wrap, the output for the new block and offset
/// will both be 0.
pub fn increment(&mut self, offset_size: usize, block_size: usize) -> &mut Self {
self.offset += 1;
if self.offset >= offset_size {
self.offset = 0;
self.block += 1;
if self.block >= block_size {
self.block = 0;
}
}

self
}
}

/// Series of traits to make access blocks easier
pub trait Blocks {
/// Returns the blocksize for read / write to the flash
fn block_size(&self) -> Result<usize, EepromError> ;

/// Returns the EepromAddress for a given index. Valid indexes are 0 to
/// EEPROM_END_ADDRESS_WORDS.
fn word_index_to_address(&self, index: usize) -> Result<EepromAddress, EepromError>;

/// Gives the the word index (0 to EEPROM_END_ADDRESS_WORDS) for a
/// given EepromAddress
fn address_to_word_index(&self, block: &EepromAddress) -> Result<usize, EepromError>;
}

/// Erase functions of the EEPROM
pub trait Erase {
/// Erase (zero out) data starting at an address spanning a length of bytes (not words!)
fn erase(&mut self, address: &EepromAddress, length_bytes: usize) -> Result<(), EepromError>;

/// Erase (zero out) a block
fn erase_block(&mut self, block: usize) -> Result<(), EepromError>;
}

/// Check if the Eeprom is busy
pub trait Busy {
/// Check the EEDONE register, true if busy
fn is_busy(&self) -> bool;

/// Blocks until the EEPROM is not busy
fn wait(&self);
}

/// Write data to the EEPROM
pub trait Write {
/// Write data to a flash address
fn write(&mut self, address: &EepromAddress, data: &[u8]) -> Result<(), EepromError>;
}

/// Read data from the EEPROM
pub trait Read {
/// Eeprom Address to start reading data from
fn read(&mut self, address: &EepromAddress, bytes_to_read: usize, buffer: &mut [u8]) -> Result<(), EepromError>;
}





1 change: 1 addition & 0 deletions tm4c-hal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod i2c;
pub mod serial;
pub mod sysctl;
pub mod time;
pub mod eeprom;

///! An internal macro to implement the GPIO functionality for each port
#[macro_export]
Expand Down
Loading