Last active
January 8, 2021 01:20
-
-
Save dragonlock2/d97fd3b2cd98ce1b930640b9e46618cf to your computer and use it in GitHub Desktop.
Simple EEPROM wear leveling algorithm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Wear-leveling algorithm based on https://sites.google.com/site/dannychouinard/Home/atmel-avr-stuff/eeprom-longevity | |
#include "mbed.h" | |
#include "WLPROM.h" | |
#include "EEPROM.h" | |
DigitalOut led(PC_13, 1); | |
BufferedSerial pc(PA_9, PA_10, 115200); | |
I2C i2c(PB_7, PB_6); | |
FileHandle *mbed::mbed_override_console(int fd) { | |
return &pc; | |
} | |
// Test Variables | |
AnalogIn rand_seed(ADC_TEMP); | |
#define NUM_WRITE 100 | |
#define NUM_READ 100 | |
#define NUM_ENDURANCE 1000000 | |
WLPROM::WLData orig, cop; | |
int main() { | |
i2c.frequency(1000000); // 1MHz max clock rate | |
printf("Starting Tests!\n"); | |
EEPROM::resetEEPROM(); | |
// Start Tests | |
srand(rand_seed.read_u16()); | |
printf("First block at %04X\n", WLPROM::findData()); | |
WLPROM::read(&orig); // read first to make sure sentinel good | |
WLPROM::read(&cop); | |
// Write Speed Test | |
Timer t; | |
printf("Starting Write Speed Test...\n"); | |
t.start(); | |
for (uint32_t i = 0; i < NUM_WRITE; i++) { | |
WLPROM::write(&orig); | |
} | |
t.stop(); | |
printf("Done! %u writes took %.3f secs, %.3f KB/s\n\n", NUM_WRITE, t.read(), | |
NUM_WRITE * sizeof(WLPROM::WLData) / 1024.0 / t.read()); | |
// Read Speed Test | |
t.reset(); | |
printf("Starting Read Speed Test...\n"); | |
t.start(); | |
for (uint32_t i = 0; i < NUM_WRITE; i++) { | |
WLPROM::read(&cop); | |
} | |
t.stop(); | |
printf("Done! %u reads took %.3f secs, %.3f KB/s\n\n", NUM_WRITE, t.read(), | |
NUM_WRITE * sizeof(WLPROM::WLData) / 1024.0 / t.read()); | |
// Endurance Test | |
printf("Starting Endurance Test...\n"); | |
for (uint64_t i = 0; i < NUM_ENDURANCE; i++) { | |
printf("Test %llu - ", i); | |
for (int i = 0; i < sizeof(WLPROM::WLData::dat) / sizeof(WLPROM::WLData::dat[0]); i++) { | |
orig.dat[i] = rand() * rand(); | |
} | |
WLPROM::write(&orig); | |
printf("written at %04X - ", WLPROM::findData()); | |
WLPROM::read(&cop); | |
for (int i = 0; i < sizeof(WLPROM::WLData::dat) / sizeof(WLPROM::WLData::dat[0]); i++) { | |
if (orig.dat[i] != cop.dat[i]) { | |
EEPROM::dumpEEPROM(); | |
printf("Fail! Expected: %08X, Actual: %08X\n", orig.dat[i], cop.dat[i]); | |
return -1; | |
} | |
} | |
printf("Pass!\n"); | |
} | |
printf("Done! \n\n"); | |
while (true) { | |
led = !led; | |
ThisThread::sleep_for(500ms); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "WLPROM.h" | |
namespace WLPROM { | |
void read(WLData* dat) { | |
EEPROM::readBites(findData(), (char*) dat, sizeof(WLData)); | |
} | |
void write(WLData* dat) { | |
uint16_t next_addr = findData() + sizeof(WLData); | |
if (next_addr + sizeof(WLData) > EEPROM::EEPROM_SIZE) { | |
next_addr = 0; | |
dat->sentinel ^= SENTINEL_MASK; | |
} | |
EEPROM::writeBites(next_addr, (char*) dat, sizeof(WLData)); | |
} | |
uint16_t findData() { | |
WLData dat; | |
EEPROM::readBites(0, (char*) &dat, sizeof(WLData)); | |
sentinel_t sentinel = dat.sentinel & SENTINEL_MASK; | |
// binary search! | |
uint16_t left = 0, right = MAX_WL_BLOCKS; | |
while (left + 1 != right) { | |
uint16_t mid = (left + right) / 2; | |
EEPROM::readBites(mid * sizeof(WLData), (char*) &dat, sizeof(WLData)); | |
sentinel != (dat.sentinel & SENTINEL_MASK) ? right = mid : left = mid; | |
} | |
return left * sizeof(WLData); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef WLPROM_H | |
#define WLPROM_H | |
#include "EEPROM.h" | |
namespace WLPROM { | |
typedef uint8_t sentinel_t; | |
typedef struct { | |
sentinel_t sentinel; // careful not to touch leftmost bit | |
uint32_t dat[4]; | |
} WLData; | |
const sentinel_t SENTINEL_MASK = 1 << (sizeof(sentinel_t) * 8 - 1); | |
const uint16_t MAX_WL_BLOCKS = EEPROM::EEPROM_SIZE / sizeof(WLData); | |
void write(WLData* dat); | |
void read(WLData* dat); | |
uint16_t findData(); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment