Merge pull request #4 from madcow/eeprom-sentinel-v2
Use reference sentinel to efficiently reset dirty flags on rollover
This commit is contained in:
@@ -2,140 +2,163 @@
|
|||||||
|
|
||||||
#include <util/atomic.h>
|
#include <util/atomic.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define MEM_SIZE 1024
|
#define MEM_SIZE 1024
|
||||||
|
#define MEM_START 0x00
|
||||||
#define MEM_BLOCK_SIZE ((int) sizeof(mem_block_t))
|
#define MEM_BLOCK_SIZE ((int) sizeof(mem_block_t))
|
||||||
#define MEM_MAX_BLOCKS (MEM_SIZE / MEM_BLOCK_SIZE)
|
#define MEM_MAX_BLOCKS (MEM_SIZE / MEM_BLOCK_SIZE)
|
||||||
#define MEM_IS_DIRTY 0x80 // Dirty block marker
|
|
||||||
|
|
||||||
static int GetUsedBlock(void);
|
static byte GetSentinel(void);
|
||||||
static int GetFreeBlock(void);
|
static int GetFreeBlock(void);
|
||||||
|
static int GetUsedBlock(void);
|
||||||
static void WriteBlock(int n, mem_block_t *in);
|
static void WriteBlock(int n, mem_block_t *in);
|
||||||
static void ReadBlock(int n, mem_block_t *out);
|
static void ReadBlock(int n, mem_block_t *out);
|
||||||
static void WriteRaw(int addr, byte data);
|
static void WriteRaw(int addr, byte data);
|
||||||
static byte ReadRaw(int addr);
|
static byte ReadRaw(int addr);
|
||||||
|
|
||||||
// TODO: Update and clean up documentation.
|
// The memory manager divides the memory space into blocks
|
||||||
// TODO: Invert sentinel value when memory is full
|
// with each block starting with a sentinel byte. This byte
|
||||||
// and always set first block as "used". This allows
|
// indicates if the block was already used in this cycle.
|
||||||
// us to reset the flags without erasing any memory.
|
|
||||||
|
|
||||||
void MEM_Init(void)
|
// /--- used
|
||||||
{
|
// 80 50 DD EE FF 60 AA BB CC
|
||||||
byte flag;
|
// 00 00 00 00 00 00 00 00 00
|
||||||
|
// \--- free
|
||||||
|
|
||||||
flag = ReadRaw(0);
|
// The first block is always considered used and therefore
|
||||||
|
// its sentinel value can act as a reference for the "used"
|
||||||
// We divide the memory space into blocks and
|
// flag. This means a block is considered free, only if its
|
||||||
// provide each block with a "dirty" flag to
|
// sentinel value differs from the first block.
|
||||||
// indicate if the block is in use.
|
|
||||||
|
|
||||||
// /--- used
|
|
||||||
// 80 50 DD EE FF 60 AA BB CC
|
|
||||||
// 00 00 00 00 00 00 00 00 00
|
|
||||||
// \--- free
|
|
||||||
|
|
||||||
// We initialize the memory first if we don't find
|
|
||||||
// a valid flag at the first sentinel position. This
|
|
||||||
// value can be either a 0x00 or 0x80. Empty memory
|
|
||||||
// on Atmel chips is set to 0xFF so we must not use
|
|
||||||
// that as a flag.
|
|
||||||
|
|
||||||
if (flag != 0x00 && flag != MEM_IS_DIRTY) {
|
|
||||||
Info("Initializing fresh memory...");
|
|
||||||
MEM_Free(); // Reset all dirty flags
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MEM_Write(mem_block_t *in)
|
void MEM_Write(mem_block_t *in)
|
||||||
{
|
{
|
||||||
int n;
|
int head;
|
||||||
|
byte flag;
|
||||||
|
|
||||||
// As we add entries it's trivial to keep track of
|
// As we add entries we check the flags for each
|
||||||
// the last one: We check the flags for each block in
|
// block in sequence until we find a free one and
|
||||||
// sequence until we find a "free" one and go back
|
// go back one step.
|
||||||
// one step.
|
|
||||||
|
head = GetFreeBlock();
|
||||||
|
flag = GetSentinel();
|
||||||
|
|
||||||
// When we have to write new data but there are no
|
// When we have to write new data but there are no
|
||||||
// "free" entries, we start writing from the first
|
// free entries, we change the sentinel bit value
|
||||||
// entry and only reset the flags (for now).
|
// for the reference block and all other blocks are
|
||||||
|
// considered free again without erasing any flags.
|
||||||
|
|
||||||
// EEPROM cells are written by the hardware in a two
|
// /--- used
|
||||||
// step operation: First, the cell is set to all ones,
|
// 00 50 DD EE FF 60 AA BB CC
|
||||||
// then the bits to be written (effectively only those
|
// 80 20 FF CC 25 00 EE FF CC
|
||||||
// that are 0) are actually written.
|
// \--- free
|
||||||
|
|
||||||
n = GetFreeBlock();
|
if (head == 0) {
|
||||||
if (n < 0) { // Full?
|
// Clear all "used" flags
|
||||||
MEM_Free();
|
flag = flag ? 0x00 : 0x80;
|
||||||
n = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
in->flag = MEM_IS_DIRTY;
|
in->flag = flag;
|
||||||
WriteBlock(n, in);
|
WriteBlock(head, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
int MEM_Read(mem_block_t *out)
|
int MEM_Read(mem_block_t *out)
|
||||||
{
|
{
|
||||||
int n;
|
int head;
|
||||||
|
|
||||||
// To get the current block we search for the next
|
head = GetUsedBlock();
|
||||||
// free one and go back by one. If we reach the end
|
|
||||||
// we return a negative value to indicate empty
|
|
||||||
// memory.
|
|
||||||
|
|
||||||
n = GetUsedBlock();
|
if (head < 0) { // Empty?
|
||||||
if (n < 0) { // Empty?
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReadBlock(n, out);
|
ReadBlock(head, out);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MEM_Free(void)
|
void MEM_Free(void)
|
||||||
{
|
{
|
||||||
int n;
|
int head;
|
||||||
|
|
||||||
// For now we clear all dirty block markers but it
|
for (head = 0; head < MEM_MAX_BLOCKS; head++) {
|
||||||
// would be better to invert the sentinel value when
|
// Set sentinel values to unitialized
|
||||||
// we've reached the end (and set the first block to
|
WriteRaw(head * MEM_BLOCK_SIZE, 0xFF);
|
||||||
// always be "used"). This means we don't have to
|
|
||||||
// erase any flags below.
|
|
||||||
|
|
||||||
for (n = 0; n < MEM_MAX_BLOCKS; n++) {
|
if ((head % 64) == 0) {
|
||||||
WriteRaw(n * MEM_BLOCK_SIZE, 0x0);
|
WDT_Reset();
|
||||||
if ((n % 64) == 0) WDT_Reset();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MEM_Dump(void)
|
||||||
|
{
|
||||||
|
int n, m;
|
||||||
|
char buf[MEM_BLOCK_SIZE*3+1];
|
||||||
|
char *ptr = buf;
|
||||||
|
|
||||||
|
// Used for debugging purposes
|
||||||
|
for (n = 0, m = 0; n < MEM_SIZE; n++) {
|
||||||
|
ptr += sprintf(ptr, "%02X ", ReadRaw(n));
|
||||||
|
if (++m == MEM_BLOCK_SIZE || n == (MEM_SIZE - 1)) {
|
||||||
|
Print("%s\r\n", buf);
|
||||||
|
WDT_Reset();
|
||||||
|
ptr = buf;
|
||||||
|
m = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte GetSentinel(void)
|
||||||
|
{
|
||||||
|
return ReadRaw(MEM_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetFreeBlock(void)
|
||||||
|
{
|
||||||
|
int head;
|
||||||
|
byte ref, flag;
|
||||||
|
|
||||||
|
if ((ref = GetSentinel()) == 0xFF) {
|
||||||
|
return 0; // Fresh memory
|
||||||
|
}
|
||||||
|
|
||||||
|
for (head = 0; head < MEM_MAX_BLOCKS; head++) {
|
||||||
|
flag = ReadRaw(head * MEM_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (flag != ref) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (head == MEM_MAX_BLOCKS) {
|
||||||
|
return 0; // Rollover
|
||||||
|
}
|
||||||
|
|
||||||
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetUsedBlock(void)
|
static int GetUsedBlock(void)
|
||||||
{
|
{
|
||||||
int bl;
|
int next, last;
|
||||||
|
byte ref;
|
||||||
|
|
||||||
bl = GetFreeBlock();
|
if ((ref = GetSentinel()) == 0xFF) {
|
||||||
|
return -1; // Fresh memory
|
||||||
|
}
|
||||||
|
|
||||||
if (bl == 0) {
|
if ((next = GetFreeBlock()) == 0) {
|
||||||
return -1;
|
// Important edge case: Rollover or empty?
|
||||||
} else if (bl < 0) {
|
last = (MEM_MAX_BLOCKS - 1) * MEM_BLOCK_SIZE;
|
||||||
return MEM_MAX_BLOCKS;
|
|
||||||
|
if (ReadRaw(last) == GetSentinel()) {
|
||||||
|
return MEM_MAX_BLOCKS - 1;
|
||||||
} else {
|
} else {
|
||||||
return bl - 1;
|
return -1; // None
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void ReadBlock(int n, mem_block_t *out)
|
|
||||||
{
|
|
||||||
int addr;
|
|
||||||
byte block[MEM_BLOCK_SIZE];
|
|
||||||
|
|
||||||
addr = n * MEM_BLOCK_SIZE;
|
|
||||||
for (int i = 0; i < MEM_BLOCK_SIZE; i++) {
|
|
||||||
block[i] = ReadRaw(addr + i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(out, block, sizeof(mem_block_t));
|
return next - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteBlock(int n, mem_block_t *in)
|
static void WriteBlock(int n, mem_block_t *in)
|
||||||
@@ -151,24 +174,17 @@ static void WriteBlock(int n, mem_block_t *in)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetFreeBlock(void)
|
static void ReadBlock(int n, mem_block_t *out)
|
||||||
{
|
{
|
||||||
int n;
|
int addr;
|
||||||
byte flag;
|
byte block[MEM_BLOCK_SIZE];
|
||||||
bool is_free;
|
|
||||||
|
|
||||||
for (n = 0; n < MEM_MAX_BLOCKS; n++) {
|
addr = n * MEM_BLOCK_SIZE;
|
||||||
// Only read first member of struct
|
for (int i = 0; i < MEM_BLOCK_SIZE; i++) {
|
||||||
flag = ReadRaw(n * MEM_BLOCK_SIZE);
|
block[i] = ReadRaw(addr + i);
|
||||||
|
|
||||||
if (flag != MEM_IS_DIRTY) {
|
|
||||||
is_free = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there any space left?
|
memcpy(out, block, sizeof(mem_block_t));
|
||||||
return (is_free) ? n : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteRaw(int addr, byte data)
|
static void WriteRaw(int addr, byte data)
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ struct mem_block_s {
|
|||||||
float temp, dewp;
|
float temp, dewp;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
void MEM_Init(void);
|
|
||||||
void MEM_Write(mem_block_t *in);
|
void MEM_Write(mem_block_t *in);
|
||||||
int MEM_Read(mem_block_t *out);
|
int MEM_Read(mem_block_t *out);
|
||||||
void MEM_Free(void);
|
void MEM_Free(void);
|
||||||
|
void MEM_Dump(void);
|
||||||
|
|
||||||
#endif // MAD_CORE_COMMON_MEMORY_H
|
#endif // MAD_CORE_COMMON_MEMORY_H
|
||||||
|
|||||||
@@ -74,11 +74,10 @@ static int Init(void)
|
|||||||
// be some sanity checking before these values are
|
// be some sanity checking before these values are
|
||||||
// used.
|
// used.
|
||||||
|
|
||||||
MEM_Init();
|
// mem.temp = 20.50f;
|
||||||
|
// mem.dewp = 10.25f;
|
||||||
// mem.temp = 30.50f;
|
|
||||||
// mem.dewp = 15.25f;
|
|
||||||
// MEM_Write(&mem);
|
// MEM_Write(&mem);
|
||||||
|
// MEM_Dump();
|
||||||
// MEM_Free();
|
// MEM_Free();
|
||||||
|
|
||||||
if (MEM_Read(&mem) == 0) {
|
if (MEM_Read(&mem) == 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user