Skip to content

Instantly share code, notes, and snippets.

@dragonlock2

dragonlock2/WLPROM.cpp

Last active Jan 8, 2021
Embed
What would you like to do?
Simple EEPROM wear leveling algorithm
// 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);
}
}
#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);
}
}
#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