qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

eeprom_legacy_emulated_flash.c (22371B)


      1 /*
      2  * This software is experimental and a work in progress.
      3  * Under no circumstances should these files be used in relation to any critical system(s).
      4  * Use of these files is at your own risk.
      5  *
      6  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      7  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
      8  * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
      9  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     10  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     11  * DEALINGS IN THE SOFTWARE.
     12  *
     13  * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by
     14  * Artur F.
     15  *
     16  * Modifications for QMK and STM32F303 by Yiancar
     17  * Modifications to add flash wear leveling by Ilya Zhuravlev
     18  * Modifications to increase flash density by Don Kjer
     19  */
     20 
     21 #include <stdio.h>
     22 #include <stdbool.h>
     23 #include "util.h"
     24 #include "debug.h"
     25 #include "eeprom_legacy_emulated_flash.h"
     26 #include "legacy_flash_ops.h"
     27 #include "eeprom_driver.h"
     28 
     29 /*
     30  * We emulate eeprom by writing a snapshot compacted view of eeprom contents,
     31  * followed by a write log of any change since that snapshot:
     32  *
     33  * === SIMULATED EEPROM CONTENTS ===
     34  *
     35  * ┌─ Compacted ┬ Write Log ─┐
     36  * │............│[BYTE][BYTE]│
     37  * │FFFF....FFFF│[WRD0][WRD1]│
     38  * │FFFFFFFFFFFF│[WORD][NEXT]│
     39  * │....FFFFFFFF│[BYTE][WRD0]│
     40  * ├────────────┼────────────┤
     41  * └──PAGE_BASE │            │
     42  *    PAGE_LAST─┴─WRITE_BASE │
     43  *                WRITE_LAST ┘
     44  *
     45  * Compacted contents are the 1's complement of the actual EEPROM contents.
     46  * e.g. An 'FFFF' represents a '0000' value.
     47  *
     48  * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom.
     49  * The size of the compacted-area and write log are configurable, and the combined
     50  * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent.
     51  * Simulated Eeprom contents are located at the end of available flash space.
     52  *
     53  * The following configuration defines can be set:
     54  *
     55  * FEE_PAGE_COUNT   # Total number of pages to use for eeprom simulation (Compact + Write log)
     56  * FEE_DENSITY_BYTES   # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT)
     57  * NOTE: The current implementation does not include page swapping,
     58  * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents.
     59  *
     60  * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals
     61  * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES.
     62  * The larger the write log, the less frequently the compacted area needs to be rewritten.
     63  *
     64  *
     65  * *** General Algorithm ***
     66  *
     67  * During initialization:
     68  * The contents of the Compacted-flash area are loaded and the 1's complement value
     69  * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache).
     70  * Write log entries are processed until a 0xFFFF is reached.
     71  * Each log entry updates a byte or word in the cache.
     72  *
     73  * During reads:
     74  * EEPROM contents are given back directly from the cache in memory.
     75  *
     76  * During writes:
     77  * The contents of the cache is updated first.
     78  * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash
     79  * Otherwise:
     80  * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area.
     81  * Otherwise a Write log entry is constructed and appended to the next free position in the Write log.
     82  *
     83  *
     84  * *** Write Log Structure ***
     85  *
     86  * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned.
     87  *
     88  * === WRITE LOG ENTRY FORMATS ===
     89  *
     90  * ╔═══ Byte-Entry ══╗
     91  * ║0XXXXXXX║YYYYYYYY║
     92  * ║ └──┬──┘║└──┬───┘║
     93  * ║ Address║ Value  ║
     94  * ╚════════╩════════╝
     95  * 0 <= Address < 0x80 (128)
     96  *
     97  * ╔ Word-Encoded 0 ╗
     98  * ║100XXXXXXXXXXXXX║
     99  * ║  │└─────┬─────┘║
    100  * ║  │Address >> 1 ║
    101  * ║  └── Value: 0  ║
    102  * ╚════════════════╝
    103  * 0 <= Address <= 0x3FFE (16382)
    104  *
    105  * ╔ Word-Encoded 1 ╗
    106  * ║101XXXXXXXXXXXXX║
    107  * ║  │└─────┬─────┘║
    108  * ║  │Address >> 1 ║
    109  * ║  └── Value: 1  ║
    110  * ╚════════════════╝
    111  * 0 <= Address <= 0x3FFE (16382)
    112  *
    113  * ╔═══ Reserved ═══╗
    114  * ║110XXXXXXXXXXXXX║
    115  * ╚════════════════╝
    116  *
    117  * ╔═══════════ Word-Next ═══════════╗
    118  * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║
    119  * ║   └─────┬─────┘║└───────┬──────┘║
    120  * ║(Address-128)>>1║     ~Value     ║
    121  * ╚════════════════╩════════════════╝
    122  * (  0 <= Address <  0x0080 (128): Reserved)
    123  * 0x80 <= Address <= 0x3FFE (16382)
    124  *
    125  * Write Log entry ranges:
    126  * 0x0000 ... 0x7FFF - Byte-Entry;     address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF)
    127  * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0
    128  * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1
    129  * 0xC000 ... 0xDFFF - Reserved
    130  * 0xE000 ... 0xFFBF - Word-Next;      address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry)
    131  * 0xFFC0 ... 0xFFFE - Reserved
    132  * 0xFFFF            - Unprogrammed
    133  *
    134  */
    135 
    136 #include "eeprom_legacy_emulated_flash_defs.h"
    137 /* These bits are used for optimizing encoding of bytes, 0 and 1 */
    138 #define FEE_WORD_ENCODING 0x8000
    139 #define FEE_VALUE_NEXT 0x6000
    140 #define FEE_VALUE_RESERVED 0x4000
    141 #define FEE_VALUE_ENCODED 0x2000
    142 #define FEE_BYTE_RANGE 0x80
    143 
    144 /* Flash word value after erase */
    145 #define FEE_EMPTY_WORD ((uint16_t)0xFFFF)
    146 
    147 #if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS)
    148 #    error "not implemented."
    149 #endif
    150 
    151 /* In-memory contents of emulated eeprom for faster access */
    152 /* *TODO: Implement page swapping */
    153 static uint16_t WordBuf[FEE_DENSITY_BYTES / 2];
    154 static uint8_t *DataBuf = (uint8_t *)WordBuf;
    155 
    156 /* Pointer to the first available slot within the write log */
    157 static uint16_t *empty_slot;
    158 
    159 // #define DEBUG_EEPROM_OUTPUT
    160 
    161 /*
    162  * Debug print utils
    163  */
    164 
    165 #if defined(DEBUG_EEPROM_OUTPUT)
    166 
    167 #    define debug_eeprom debug_enable
    168 #    define eeprom_println(s) println(s)
    169 #    define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__);
    170 
    171 #else /* NO_DEBUG */
    172 
    173 #    define debug_eeprom false
    174 #    define eeprom_println(s)
    175 #    define eeprom_printf(fmt, ...)
    176 
    177 #endif /* NO_DEBUG */
    178 
    179 void print_eeprom(void) {
    180 #ifndef NO_DEBUG
    181     int empty_rows = 0;
    182     for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) {
    183         if (i % 16 == 0) {
    184             if (i >= FEE_DENSITY_BYTES - 16) {
    185                 /* Make sure we display the last row */
    186                 empty_rows = 0;
    187             }
    188             /* Check if this row is uninitialized */
    189             ++empty_rows;
    190             for (uint16_t j = 0; j < 16; j++) {
    191                 if (DataBuf[i + j]) {
    192                     empty_rows = 0;
    193                     break;
    194                 }
    195             }
    196             if (empty_rows > 1) {
    197                 /* Repeat empty row */
    198                 if (empty_rows == 2) {
    199                     /* Only display the first repeat empty row */
    200                     println("*");
    201                 }
    202                 i += 15;
    203                 continue;
    204             }
    205             xprintf("%04x", i);
    206         }
    207         if (i % 8 == 0) print(" ");
    208 
    209         xprintf(" %02x", DataBuf[i]);
    210         if ((i + 1) % 16 == 0) {
    211             println("");
    212         }
    213     }
    214 #endif
    215 }
    216 
    217 uint16_t EEPROM_Init(void) {
    218     /* Load emulated eeprom contents from compacted flash into memory */
    219     uint16_t *src  = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS;
    220     uint16_t *dest = (uint16_t *)DataBuf;
    221     for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) {
    222         *dest = ~*src;
    223     }
    224 
    225     if (debug_eeprom) {
    226         println("EEPROM_Init Compacted Pages:");
    227         print_eeprom();
    228         println("EEPROM_Init Write Log:");
    229     }
    230 
    231     /* Replay write log */
    232     uint16_t *log_addr;
    233     for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) {
    234         uint16_t address = *log_addr;
    235         if (address == FEE_EMPTY_WORD) {
    236             break;
    237         }
    238         /* Check for lowest 128-bytes optimization */
    239         if (!(address & FEE_WORD_ENCODING)) {
    240             uint8_t bvalue = (uint8_t)address;
    241             address >>= 8;
    242             DataBuf[address] = bvalue;
    243             eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue);
    244         } else {
    245             uint16_t wvalue;
    246             /* Check if value is in next word */
    247             if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) {
    248                 /* Read value from next word */
    249                 if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
    250                     break;
    251                 }
    252                 wvalue = ~*log_addr;
    253                 if (!wvalue) {
    254                     eeprom_printf("Incomplete write at log_addr: 0x%04lx;\n", (uint32_t)log_addr);
    255                     /* Possibly incomplete write.  Ignore and continue */
    256                     continue;
    257                 }
    258                 address &= 0x1FFF;
    259                 address <<= 1;
    260                 /* Writes to addresses less than 128 are byte log entries */
    261                 address += FEE_BYTE_RANGE;
    262             } else {
    263                 /* Reserved for future use */
    264                 if (address & FEE_VALUE_RESERVED) {
    265                     eeprom_printf("Reserved encoded value at log_addr: 0x%04lx;\n", (uint32_t)log_addr);
    266                     continue;
    267                 }
    268                 /* Optimization for 0 or 1 values. */
    269                 wvalue = (address & FEE_VALUE_ENCODED) >> 13;
    270                 address &= 0x1FFF;
    271                 address <<= 1;
    272             }
    273             if (address < FEE_DENSITY_BYTES) {
    274                 eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue);
    275                 *(uint16_t *)(&DataBuf[address]) = wvalue;
    276             } else {
    277                 eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue);
    278             }
    279         }
    280     }
    281 
    282     empty_slot = log_addr;
    283 
    284     if (debug_eeprom) {
    285         println("EEPROM_Init Final DataBuf:");
    286         print_eeprom();
    287     }
    288 
    289     return FEE_DENSITY_BYTES;
    290 }
    291 
    292 /* Clear flash contents (doesn't touch in-memory DataBuf) */
    293 static void eeprom_clear(void) {
    294     FLASH_Unlock();
    295 
    296     for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) {
    297         eeprom_printf("FLASH_ErasePage(0x%04lx)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)));
    298         FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE));
    299     }
    300 
    301     FLASH_Lock();
    302 
    303     empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS;
    304     eeprom_printf("eeprom_clear empty_slot: 0x%08lx\n", (uint32_t)empty_slot);
    305 }
    306 
    307 /* Erase emulated eeprom */
    308 void EEPROM_Erase(void) {
    309     eeprom_println("EEPROM_Erase");
    310     /* Erase compacted pages and write log */
    311     eeprom_clear();
    312     /* re-initialize to reset DataBuf */
    313     EEPROM_Init();
    314 }
    315 
    316 /* Compact write log */
    317 static uint8_t eeprom_compact(void) {
    318     /* Erase compacted pages and write log */
    319     eeprom_clear();
    320 
    321     FLASH_Unlock();
    322 
    323     FLASH_Status final_status = FLASH_COMPLETE;
    324 
    325     /* Write emulated eeprom contents from memory to compacted flash */
    326     uint16_t *src  = (uint16_t *)DataBuf;
    327     uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS;
    328     uint16_t  value;
    329     for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) {
    330         value = *src;
    331         if (value) {
    332             eeprom_printf("FLASH_ProgramHalfWord(0x%04lx, 0x%04x)\n", (uint32_t)dest, ~value);
    333             FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value);
    334             if (status != FLASH_COMPLETE) final_status = status;
    335         }
    336     }
    337 
    338     FLASH_Lock();
    339 
    340     if (debug_eeprom) {
    341         println("eeprom_compacted:");
    342         print_eeprom();
    343     }
    344 
    345     return final_status;
    346 }
    347 
    348 static uint8_t eeprom_write_direct_entry(uint16_t Address) {
    349     /* Check if we can just write this directly to the compacted flash area */
    350     uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE);
    351     if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) {
    352         /* Write the value directly to the compacted area without a log entry */
    353         uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]);
    354         /* Early exit if a write isn't needed */
    355         if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE;
    356 
    357         FLASH_Unlock();
    358 
    359         eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value);
    360         FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value);
    361 
    362         FLASH_Lock();
    363         return status;
    364     }
    365     return 0;
    366 }
    367 
    368 static uint8_t eeprom_write_log_word_entry(uint16_t Address) {
    369     FLASH_Status final_status = FLASH_COMPLETE;
    370 
    371     uint16_t value = *(uint16_t *)(&DataBuf[Address]);
    372     eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value);
    373 
    374     /* MSB signifies the lowest 128-byte optimization is not in effect */
    375     uint16_t encoding = FEE_WORD_ENCODING;
    376     uint8_t  entry_size;
    377     if (value <= 1) {
    378         encoding |= value << 13;
    379         entry_size = 2;
    380     } else {
    381         encoding |= FEE_VALUE_NEXT;
    382         entry_size = 4;
    383         /* Writes to addresses less than 128 are byte log entries */
    384         Address -= FEE_BYTE_RANGE;
    385     }
    386 
    387     /* if we can't find an empty spot, we must compact emulated eeprom */
    388     if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) {
    389         /* compact the write log into the compacted flash area */
    390         return eeprom_compact();
    391     }
    392 
    393     /* Word log writes should be word-aligned.  Take back a bit */
    394     Address >>= 1;
    395     Address |= encoding;
    396 
    397     /* ok we found a place let's write our data */
    398     FLASH_Unlock();
    399 
    400     /* address */
    401     eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, Address);
    402     final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address);
    403 
    404     /* value */
    405     if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) {
    406         eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, ~value);
    407         FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value);
    408         if (status != FLASH_COMPLETE) final_status = status;
    409     }
    410 
    411     FLASH_Lock();
    412 
    413     return final_status;
    414 }
    415 
    416 static uint8_t eeprom_write_log_byte_entry(uint16_t Address) {
    417     eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]);
    418 
    419     /* if couldn't find an empty spot, we must compact emulated eeprom */
    420     if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) {
    421         /* compact the write log into the compacted flash area */
    422         return eeprom_compact();
    423     }
    424 
    425     /* ok we found a place let's write our data */
    426     FLASH_Unlock();
    427 
    428     /* Pack address and value into the same word */
    429     uint16_t value = (Address << 8) | DataBuf[Address];
    430 
    431     /* write to flash */
    432     eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, value);
    433     FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value);
    434 
    435     FLASH_Lock();
    436 
    437     return status;
    438 }
    439 
    440 uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) {
    441     /* if the address is out-of-bounds, do nothing */
    442     if (Address >= FEE_DENSITY_BYTES) {
    443         eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte);
    444         return FLASH_BAD_ADDRESS;
    445     }
    446 
    447     /* if the value is the same, don't bother writing it */
    448     if (DataBuf[Address] == DataByte) {
    449         eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte);
    450         return 0;
    451     }
    452 
    453     /* keep DataBuf cache in sync */
    454     DataBuf[Address] = DataByte;
    455     eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]);
    456 
    457     /* perform the write into flash memory */
    458     /* First, attempt to write directly into the compacted flash area */
    459     FLASH_Status status = eeprom_write_direct_entry(Address);
    460     if (!status) {
    461         /* Otherwise append to the write log */
    462         if (Address < FEE_BYTE_RANGE) {
    463             status = eeprom_write_log_byte_entry(Address);
    464         } else {
    465             status = eeprom_write_log_word_entry(Address & 0xFFFE);
    466         }
    467     }
    468     if (status != 0 && status != FLASH_COMPLETE) {
    469         eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status);
    470     }
    471     return status;
    472 }
    473 
    474 uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) {
    475     /* if the address is out-of-bounds, do nothing */
    476     if (Address >= FEE_DENSITY_BYTES) {
    477         eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord);
    478         return FLASH_BAD_ADDRESS;
    479     }
    480 
    481     /* Check for word alignment */
    482     FLASH_Status final_status = FLASH_COMPLETE;
    483     if (Address % 2) {
    484         final_status        = EEPROM_WriteDataByte(Address, DataWord);
    485         FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8);
    486         if (status != FLASH_COMPLETE) final_status = status;
    487         if (final_status != 0 && final_status != FLASH_COMPLETE) {
    488             eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
    489         }
    490         return final_status;
    491     }
    492 
    493     /* if the value is the same, don't bother writing it */
    494     uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]);
    495     if (oldValue == DataWord) {
    496         eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord);
    497         return 0;
    498     }
    499 
    500     /* keep DataBuf cache in sync */
    501     *(uint16_t *)(&DataBuf[Address]) = DataWord;
    502     eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address]));
    503 
    504     /* perform the write into flash memory */
    505     /* First, attempt to write directly into the compacted flash area */
    506     final_status = eeprom_write_direct_entry(Address);
    507     if (!final_status) {
    508         /* Otherwise append to the write log */
    509         /* Check if we need to fall back to byte write */
    510         if (Address < FEE_BYTE_RANGE) {
    511             final_status = FLASH_COMPLETE;
    512             /* Only write a byte if it has changed */
    513             if ((uint8_t)oldValue != (uint8_t)DataWord) {
    514                 final_status = eeprom_write_log_byte_entry(Address);
    515             }
    516             FLASH_Status status = FLASH_COMPLETE;
    517             /* Only write a byte if it has changed */
    518             if ((oldValue >> 8) != (DataWord >> 8)) {
    519                 status = eeprom_write_log_byte_entry(Address + 1);
    520             }
    521             if (status != FLASH_COMPLETE) final_status = status;
    522         } else {
    523             final_status = eeprom_write_log_word_entry(Address);
    524         }
    525     }
    526     if (final_status != 0 && final_status != FLASH_COMPLETE) {
    527         eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status);
    528     }
    529     return final_status;
    530 }
    531 
    532 uint8_t EEPROM_ReadDataByte(uint16_t Address) {
    533     uint8_t DataByte = 0xFF;
    534 
    535     if (Address < FEE_DENSITY_BYTES) {
    536         DataByte = DataBuf[Address];
    537     }
    538 
    539     eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte);
    540 
    541     return DataByte;
    542 }
    543 
    544 uint16_t EEPROM_ReadDataWord(uint16_t Address) {
    545     uint16_t DataWord = 0xFFFF;
    546 
    547     if (Address < FEE_DENSITY_BYTES - 1) {
    548         /* Check word alignment */
    549         if (Address % 2) {
    550             DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8);
    551         } else {
    552             DataWord = *(uint16_t *)(&DataBuf[Address]);
    553         }
    554     }
    555 
    556     eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord);
    557 
    558     return DataWord;
    559 }
    560 
    561 /*****************************************************************************
    562  *  Bind to eeprom_driver.c
    563  *******************************************************************************/
    564 void eeprom_driver_init(void) {
    565     EEPROM_Init();
    566 }
    567 
    568 void eeprom_driver_format(bool erase) {
    569     /* emulated eepron requires the write log data structures to be erased before use. */
    570     (void)erase;
    571     eeprom_driver_erase();
    572 }
    573 
    574 void eeprom_driver_erase(void) {
    575     EEPROM_Erase();
    576 }
    577 
    578 void eeprom_read_block(void *buf, const void *addr, size_t len) {
    579     const uint8_t *src  = (const uint8_t *)addr;
    580     uint8_t       *dest = (uint8_t *)buf;
    581 
    582     /* Check word alignment */
    583     if (len && (uintptr_t)src % 2) {
    584         /* Read the unaligned first byte */
    585         *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++);
    586         --len;
    587     }
    588 
    589     uint16_t value;
    590     bool     aligned = ((uintptr_t)dest % 2 == 0);
    591     while (len > 1) {
    592         value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src));
    593         if (aligned) {
    594             *(uint16_t *)dest = value;
    595             dest += 2;
    596         } else {
    597             *dest++ = value;
    598             *dest++ = value >> 8;
    599         }
    600         src += 2;
    601         len -= 2;
    602     }
    603     if (len) {
    604         *dest = EEPROM_ReadDataByte((const uintptr_t)src);
    605     }
    606 }
    607 
    608 void eeprom_write_block(const void *buf, void *addr, size_t len) {
    609     uint8_t       *dest = (uint8_t *)addr;
    610     const uint8_t *src  = (const uint8_t *)buf;
    611 
    612     /* Check word alignment */
    613     if (len && (uintptr_t)dest % 2) {
    614         /* Write the unaligned first byte */
    615         EEPROM_WriteDataByte((uintptr_t)dest++, *src++);
    616         --len;
    617     }
    618 
    619     uint16_t value;
    620     bool     aligned = ((uintptr_t)src % 2 == 0);
    621     while (len > 1) {
    622         if (aligned) {
    623             value = *(uint16_t *)src;
    624         } else {
    625             value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8);
    626         }
    627         EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value);
    628         dest += 2;
    629         src += 2;
    630         len -= 2;
    631     }
    632 
    633     if (len) {
    634         EEPROM_WriteDataByte((uintptr_t)dest, *src);
    635     }
    636 }