Implement memory wear leveling algorithm
This commit is contained in:
@@ -1,48 +1,147 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <util/atomic.h>
|
#include <util/atomic.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#define MEM_SIZE 1024
|
#define MEM_SIZE 1024
|
||||||
#define BLOCK_SIZE sizeof(mem_data_t)
|
#define MEM_NUM_CELLS 2
|
||||||
#define MAX_BLOCKS (MEM_SIZE / BLOCK_SIZE)
|
#define MEM_CELL_SIZE ((int) sizeof(word))
|
||||||
#define SENTINEL_MASK BIT(8)
|
#define MEM_BLOCK_SIZE (MEM_NUM_CELLS * MEM_CELL_SIZE + 1)
|
||||||
|
#define MEM_MAX_BLOCKS (MEM_SIZE / MEM_BLOCK_SIZE)
|
||||||
|
#define MEM_IS_DIRTY 0x80 // Dirty block marker
|
||||||
|
|
||||||
static byte ReadRaw(word addr);
|
// TODO: Automated testing /w multiple write cycles
|
||||||
static int WriteRaw(word addr, byte data);
|
// TODO: Accept structure containing variable data
|
||||||
static word GetBlockAddress(void);
|
|
||||||
|
|
||||||
int MEM_Read(mem_data_t *out)
|
static int GetUsedBlock(void);
|
||||||
|
static int GetFreeBlock(void);
|
||||||
|
static byte ReadRaw(int addr);
|
||||||
|
static void WriteRaw(int addr, byte data);
|
||||||
|
|
||||||
|
void MEM_Init(void)
|
||||||
{
|
{
|
||||||
// TODO
|
// Valid first sentinel?
|
||||||
UNUSED(out);
|
if (ReadRaw(0) == 0xFF) {
|
||||||
UNUSED(ReadRaw);
|
Info("Initializing fresh memory...");
|
||||||
UNUSED(GetBlockAddress);
|
MEM_Free(); // Set dirty block markers
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int MEM_Write(mem_data_t *in)
|
void MEM_Read(word *temp, word *dewp)
|
||||||
{
|
{
|
||||||
// TODO
|
int bl;
|
||||||
UNUSED(in);
|
word addr;
|
||||||
UNUSED(WriteRaw);
|
byte msb[2], lsb[2];
|
||||||
return 0;
|
|
||||||
|
bl = GetUsedBlock();
|
||||||
|
|
||||||
|
if (bl < 0) {
|
||||||
|
*temp = 0;
|
||||||
|
*dewp = 0;
|
||||||
|
return; // Empty
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = bl * MEM_BLOCK_SIZE;
|
||||||
|
|
||||||
|
msb[0] = ReadRaw(addr + 1);
|
||||||
|
lsb[0] = ReadRaw(addr + 2);
|
||||||
|
msb[1] = ReadRaw(addr + MEM_CELL_SIZE + 1);
|
||||||
|
lsb[1] = ReadRaw(addr + MEM_CELL_SIZE + 2);
|
||||||
|
|
||||||
|
*temp = ((msb[0] & 0xFF) << 8) | (lsb[0] & 0xFF);
|
||||||
|
*dewp = ((msb[1] & 0xFF) << 8) | (lsb[1] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MEM_Write(word temp, word dewp)
|
||||||
|
{
|
||||||
|
int bl;
|
||||||
|
word addr;
|
||||||
|
byte msb[2], lsb[2];
|
||||||
|
|
||||||
|
bl = GetFreeBlock();
|
||||||
|
|
||||||
|
if (bl < 0) {
|
||||||
|
// Full
|
||||||
|
MEM_Free();
|
||||||
|
bl = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = bl * MEM_BLOCK_SIZE;
|
||||||
|
|
||||||
|
msb[0] = (temp >> 8) & 0xff;
|
||||||
|
lsb[0] = temp & 0xff;
|
||||||
|
msb[1] = (dewp >> 8) & 0xff;
|
||||||
|
lsb[1] = dewp & 0xff;
|
||||||
|
|
||||||
|
WriteRaw(addr, MEM_IS_DIRTY);
|
||||||
|
WriteRaw(addr + 1, msb[0]);
|
||||||
|
WriteRaw(addr + 2, lsb[0]);
|
||||||
|
WriteRaw(addr + MEM_CELL_SIZE + 1, msb[1]);
|
||||||
|
WriteRaw(addr + MEM_CELL_SIZE + 2, lsb[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MEM_Free(void)
|
||||||
|
{
|
||||||
|
int bl;
|
||||||
|
|
||||||
|
// Clear all dirty block markers
|
||||||
|
for (bl = 0; bl < MEM_MAX_BLOCKS; bl++) {
|
||||||
|
WriteRaw(bl * MEM_BLOCK_SIZE, 0x0);
|
||||||
|
if ((bl % 64) == 0) WDT_Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MEM_Dump(void)
|
void MEM_Dump(void)
|
||||||
{
|
{
|
||||||
// byte rom[1024];
|
byte raw;
|
||||||
// Info("Dumping EEPROM memory:");
|
char buf[3*16+1];
|
||||||
// for (int i = 0; i < 1024; i++) {
|
|
||||||
// rom[i] = ReadRaw(i);
|
Info("Dumping EEPROM memory:");
|
||||||
// }
|
for (int i = 0; i < 64; i++) {
|
||||||
|
for (int j = 0; j < 16; j++) {
|
||||||
|
raw = ReadRaw(i * 16 + j);
|
||||||
|
sprintf(buf + j * 3, "%02X ", raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
static word GetBlockAddress(void)
|
WDT_Reset();
|
||||||
|
Info(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int GetUsedBlock(void)
|
||||||
{
|
{
|
||||||
return 0;
|
int bl;
|
||||||
|
|
||||||
|
bl = GetFreeBlock();
|
||||||
|
|
||||||
|
if (bl == 0)
|
||||||
|
return -1;
|
||||||
|
else if (bl < 0)
|
||||||
|
return MEM_MAX_BLOCKS;
|
||||||
|
else
|
||||||
|
return bl - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int WriteRaw(word addr, byte data)
|
static int GetFreeBlock(void)
|
||||||
|
{
|
||||||
|
int bl;
|
||||||
|
byte flag;
|
||||||
|
bool is_free;
|
||||||
|
|
||||||
|
for (bl = 0; bl < MEM_MAX_BLOCKS; bl++) {
|
||||||
|
// Check dirty flag for current block
|
||||||
|
flag = ReadRaw(bl * MEM_BLOCK_SIZE);
|
||||||
|
if (flag != MEM_IS_DIRTY) {
|
||||||
|
is_free = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there any space left?
|
||||||
|
return (is_free) ? bl : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteRaw(int addr, byte data)
|
||||||
{
|
{
|
||||||
// The EEMWE bit determines whether setting EEWE to
|
// The EEMWE bit determines whether setting EEWE to
|
||||||
// one causes the EEPROM to be written. When EEMWE
|
// one causes the EEPROM to be written. When EEMWE
|
||||||
@@ -68,12 +167,12 @@ static int WriteRaw(word addr, byte data)
|
|||||||
// Flag cleared during all the steps to avoid these
|
// Flag cleared during all the steps to avoid these
|
||||||
// problems.
|
// problems.
|
||||||
|
|
||||||
// Wait until ready
|
|
||||||
while (EECR & BIT(EEWE));
|
|
||||||
|
|
||||||
// No interrupts during EEPROM write
|
// No interrupts during EEPROM write
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
|
|
||||||
|
// Wait until ready
|
||||||
|
while (EECR & BIT(EEWE));
|
||||||
|
|
||||||
// The EEPROM Address Registers – EEARH and
|
// The EEPROM Address Registers – EEARH and
|
||||||
// EEARL – specify the EEPROM address in the
|
// EEARL – specify the EEPROM address in the
|
||||||
// 1024bytes EEPROM space. The EEPROM data
|
// 1024bytes EEPROM space. The EEPROM data
|
||||||
@@ -89,31 +188,33 @@ static int WriteRaw(word addr, byte data)
|
|||||||
EECR |= BIT(EEMWE);
|
EECR |= BIT(EEMWE);
|
||||||
EECR |= BIT(EEWE);
|
EECR |= BIT(EEWE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static byte ReadRaw(word addr)
|
static byte ReadRaw(int addr)
|
||||||
{
|
{
|
||||||
// Wait until ready
|
|
||||||
while (EECR & BIT(EEWE));
|
|
||||||
|
|
||||||
EEAR = addr;
|
|
||||||
|
|
||||||
// The EEPROM Read Enable Signal EERE is the read
|
// The EEPROM Read Enable Signal EERE is the read
|
||||||
// strobe to the EEPROM. When the correct address
|
// strobe to the EEPROM. When the correct address
|
||||||
// is set up in the EEAR Register, the EERE bit
|
// is set up in the EEAR Register, the EERE bit
|
||||||
// must be written to a logic one to trigger the
|
// must be written to a logic one to trigger the
|
||||||
// EEPROM read.
|
// EEPROM read.
|
||||||
|
|
||||||
// Read from address
|
|
||||||
EECR |= BIT(EERE);
|
|
||||||
|
|
||||||
// The EEPROM read access takes one instruction,
|
// The EEPROM read access takes one instruction,
|
||||||
// and the requested data is available immediately.
|
// and the requested data is available immediately.
|
||||||
// When the EEPROM is read, the CPU is halted for
|
// When the EEPROM is read, the CPU is halted for
|
||||||
// four cycles before the next instruction is
|
// four cycles before the next instruction is
|
||||||
// executed.
|
// executed.
|
||||||
|
|
||||||
|
// No interrupts during EEPROM read
|
||||||
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
|
||||||
|
|
||||||
|
// Wait until ready
|
||||||
|
while (EECR & BIT(EEWE));
|
||||||
|
|
||||||
|
EEAR = addr;
|
||||||
|
|
||||||
|
// Read from address
|
||||||
|
EECR |= BIT(EERE);
|
||||||
|
}
|
||||||
|
|
||||||
return EEDR;
|
return EEDR;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
#ifndef MAD_CORE_COMMON_MEMORY_H
|
#ifndef MAD_CORE_COMMON_MEMORY_H
|
||||||
#define MAD_CORE_COMMON_MEMORY_H
|
#define MAD_CORE_COMMON_MEMORY_H
|
||||||
|
|
||||||
typedef struct mem_data_s mem_data_t;
|
#define M_TEMPERATURE 0
|
||||||
|
#define M_DEWPOINT 1
|
||||||
|
|
||||||
struct mem_data_s {
|
void MEM_Init(void);
|
||||||
byte sentinel; // Data marker bit
|
void MEM_Write(word temp, word dewp);
|
||||||
word value[2]; // Values to be written
|
void MEM_Read(word *temp, word *dewp);
|
||||||
};
|
void MEM_Free(void);
|
||||||
|
|
||||||
int MEM_Read(mem_data_t *out);
|
|
||||||
int MEM_Write(mem_data_t *in);
|
|
||||||
void MEM_Dump(void);
|
void MEM_Dump(void);
|
||||||
|
|
||||||
#endif // MAD_CORE_COMMON_MEMORY_H
|
#endif // MAD_CORE_COMMON_MEMORY_H
|
||||||
|
|||||||
17
src/main.c
17
src/main.c
@@ -7,6 +7,7 @@
|
|||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
|
|
||||||
// TODO: Config header for chip specifics like EEPROM size.
|
// TODO: Config header for chip specifics like EEPROM size.
|
||||||
|
// TODO: Make sure wear leveling resets memory correctly.
|
||||||
// TODO: Implement primary state machine for update loop.
|
// TODO: Implement primary state machine for update loop.
|
||||||
// TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer.
|
// TODO: Migrate to ATMega 1284P-PU for 2nd 16-bit timer.
|
||||||
// TODO: Keep persistent TEMP and DEWP targets in EEPROM.
|
// TODO: Keep persistent TEMP and DEWP targets in EEPROM.
|
||||||
@@ -26,8 +27,8 @@ enum state_e
|
|||||||
};
|
};
|
||||||
|
|
||||||
static enum state_e state;
|
static enum state_e state;
|
||||||
static float temp, temp_target;
|
static word temp, temp_target;
|
||||||
static float dewp, dewp_target;
|
static word dewp, dewp_target;
|
||||||
|
|
||||||
static int Init(void)
|
static int Init(void)
|
||||||
{
|
{
|
||||||
@@ -67,6 +68,18 @@ static int Init(void)
|
|||||||
WDT_Enable();
|
WDT_Enable();
|
||||||
WDT_SetTimeoutFlag(WDT2000); // 2 seconds
|
WDT_SetTimeoutFlag(WDT2000); // 2 seconds
|
||||||
|
|
||||||
|
// Test persistent settings from EEPROM. There must
|
||||||
|
// be some sanity checking before these values are
|
||||||
|
// used.
|
||||||
|
|
||||||
|
MEM_Init();
|
||||||
|
MEM_Read(&temp_target, &dewp_target);
|
||||||
|
Info("Persistent memory contains [%d, %d]",
|
||||||
|
temp_target, dewp_target);
|
||||||
|
|
||||||
|
// MEM_Write(20, 60);
|
||||||
|
// MEM_Dump();
|
||||||
|
|
||||||
// There is a possiblity to use interrupt signals
|
// There is a possiblity to use interrupt signals
|
||||||
// for I2C communication but only as one large
|
// for I2C communication but only as one large
|
||||||
// branching routine for the whole I2C system.
|
// branching routine for the whole I2C system.
|
||||||
|
|||||||
Reference in New Issue
Block a user