diff --git a/src/common/memory.c b/src/common/memory.c index 371cec2..95fd9fa 100644 --- a/src/common/memory.c +++ b/src/common/memory.c @@ -2,140 +2,163 @@ #include #include +#include #define MEM_SIZE 1024 +#define MEM_START 0x00 #define MEM_BLOCK_SIZE ((int) sizeof(mem_block_t)) #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 GetUsedBlock(void); static void WriteBlock(int n, mem_block_t *in); static void ReadBlock(int n, mem_block_t *out); static void WriteRaw(int addr, byte data); static byte ReadRaw(int addr); -// TODO: Update and clean up documentation. -// TODO: Invert sentinel value when memory is full -// and always set first block as "used". This allows -// us to reset the flags without erasing any memory. +// The memory manager divides the memory space into blocks +// with each block starting with a sentinel byte. This byte +// indicates if the block was already used in this cycle. -void MEM_Init(void) -{ - byte flag; +// /--- used +// 80 50 DD EE FF 60 AA BB CC +// 00 00 00 00 00 00 00 00 00 +// \--- free - flag = ReadRaw(0); - - // We divide the memory space into blocks and - // provide each block with a "dirty" flag to - // 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 - } -} +// The first block is always considered used and therefore +// its sentinel value can act as a reference for the "used" +// flag. This means a block is considered free, only if its +// sentinel value differs from the first block. void MEM_Write(mem_block_t *in) { - int n; + int head; + byte flag; - // As we add entries it's trivial to keep track of - // the last one: We check the flags for each block in - // sequence until we find a "free" one and go back - // one step. + // As we add entries we check the flags for each + // block in sequence until we find a free one and + // go back one step. + + head = GetFreeBlock(); + flag = GetSentinel(); // When we have to write new data but there are no - // "free" entries, we start writing from the first - // entry and only reset the flags (for now). + // free entries, we change the sentinel bit value + // 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 - // step operation: First, the cell is set to all ones, - // then the bits to be written (effectively only those - // that are 0) are actually written. + // /--- used + // 00 50 DD EE FF 60 AA BB CC + // 80 20 FF CC 25 00 EE FF CC + // \--- free - n = GetFreeBlock(); - if (n < 0) { // Full? - MEM_Free(); - n = 0; + if (head == 0) { + // Clear all "used" flags + flag = flag ? 0x00 : 0x80; } - in->flag = MEM_IS_DIRTY; - WriteBlock(n, in); + in->flag = flag; + WriteBlock(head, in); } int MEM_Read(mem_block_t *out) { - int n; + int head; - // To get the current block we search for the next - // free one and go back by one. If we reach the end - // we return a negative value to indicate empty - // memory. + head = GetUsedBlock(); - n = GetUsedBlock(); - if (n < 0) { // Empty? + if (head < 0) { // Empty? return -1; } - ReadBlock(n, out); + ReadBlock(head, out); return 0; } void MEM_Free(void) { - int n; + int head; - // For now we clear all dirty block markers but it - // would be better to invert the sentinel value when - // we've reached the end (and set the first block to - // always be "used"). This means we don't have to - // erase any flags below. + for (head = 0; head < MEM_MAX_BLOCKS; head++) { + // Set sentinel values to unitialized + WriteRaw(head * MEM_BLOCK_SIZE, 0xFF); - for (n = 0; n < MEM_MAX_BLOCKS; n++) { - WriteRaw(n * MEM_BLOCK_SIZE, 0x0); - if ((n % 64) == 0) WDT_Reset(); + if ((head % 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) { - int bl; + int next, last; + byte ref; - bl = GetFreeBlock(); - - if (bl == 0) { - return -1; - } else if (bl < 0) { - return MEM_MAX_BLOCKS; - } else { - return bl - 1; - } -} - -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); + if ((ref = GetSentinel()) == 0xFF) { + return -1; // Fresh memory } - memcpy(out, block, sizeof(mem_block_t)); + if ((next = GetFreeBlock()) == 0) { + // Important edge case: Rollover or empty? + last = (MEM_MAX_BLOCKS - 1) * MEM_BLOCK_SIZE; + + if (ReadRaw(last) == GetSentinel()) { + return MEM_MAX_BLOCKS - 1; + } else { + return -1; // None + } + } + + return next - 1; } 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; - byte flag; - bool is_free; + int addr; + byte block[MEM_BLOCK_SIZE]; - for (n = 0; n < MEM_MAX_BLOCKS; n++) { - // Only read first member of struct - flag = ReadRaw(n * MEM_BLOCK_SIZE); - - if (flag != MEM_IS_DIRTY) { - is_free = true; - break; - } + addr = n * MEM_BLOCK_SIZE; + for (int i = 0; i < MEM_BLOCK_SIZE; i++) { + block[i] = ReadRaw(addr + i); } - // Is there any space left? - return (is_free) ? n : -1; + memcpy(out, block, sizeof(mem_block_t)); } static void WriteRaw(int addr, byte data) @@ -235,7 +251,6 @@ static byte ReadRaw(int addr) // When the EEPROM is read, the CPU is halted for // four cycles before the next instruction is // executed. - // No interrupts during EEPROM read ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { diff --git a/src/common/memory.h b/src/common/memory.h index 350822c..feeed22 100644 --- a/src/common/memory.h +++ b/src/common/memory.h @@ -12,9 +12,9 @@ struct mem_block_s { float temp, dewp; } __attribute__((packed)); -void MEM_Init(void); void MEM_Write(mem_block_t *in); int MEM_Read(mem_block_t *out); void MEM_Free(void); +void MEM_Dump(void); #endif // MAD_CORE_COMMON_MEMORY_H diff --git a/src/main.c b/src/main.c index 349a4b1..e9abfbf 100644 --- a/src/main.c +++ b/src/main.c @@ -74,11 +74,10 @@ static int Init(void) // be some sanity checking before these values are // used. - MEM_Init(); - - // mem.temp = 30.50f; - // mem.dewp = 15.25f; + // mem.temp = 20.50f; + // mem.dewp = 10.25f; // MEM_Write(&mem); + // MEM_Dump(); // MEM_Free(); if (MEM_Read(&mem) == 0) {