qmk_firmware

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

wear_leveling_internal.h (7533B)


      1 // Copyright 2022 Nick Brassel (@tzarc)
      2 // SPDX-License-Identifier: GPL-2.0-or-later
      3 #pragma once
      4 
      5 #include "compiler_support.h"
      6 
      7 #include <stdint.h>
      8 #include <string.h>
      9 
     10 #if BACKING_STORE_WRITE_SIZE == 2
     11 typedef uint16_t backing_store_int_t;
     12 #elif BACKING_STORE_WRITE_SIZE == 4
     13 typedef uint32_t backing_store_int_t;
     14 #elif BACKING_STORE_WRITE_SIZE == 8
     15 typedef uint64_t backing_store_int_t;
     16 #else
     17 #    error Invalid BACKING_STORE_WRITE_SIZE, needs to be 2/4/8.
     18 #endif
     19 
     20 #ifndef WEAR_LEVELING_BACKING_SIZE
     21 #    error WEAR_LEVELING_BACKING_SIZE was not set.
     22 #endif
     23 
     24 #ifndef WEAR_LEVELING_LOGICAL_SIZE
     25 #    error WEAR_LEVELING_LOGICAL_SIZE was not set.
     26 #endif
     27 
     28 #ifdef WEAR_LEVELING_DEBUG_OUTPUT
     29 #    include <debug.h>
     30 #    define bs_dprintf(...) dprintf("Backing store: " __VA_ARGS__)
     31 #    define wl_dprintf(...) dprintf("Wear leveling: " __VA_ARGS__)
     32 #    define wl_dump(address, value, length)             \
     33         do {                                            \
     34             dprintf("[0x%04X]: ", (int)(address));      \
     35             const uint8_t* p = (const uint8_t*)(value); \
     36             for (int i = 0; i < (length); ++i) {        \
     37                 dprintf(" %02X", (int)p[i]);            \
     38             }                                           \
     39             dprintf("\n");                              \
     40         } while (0)
     41 #else
     42 #    define wl_dprintf(...) \
     43         do {                \
     44         } while (0)
     45 #    define bs_dprintf(...) \
     46         do {                \
     47         } while (0)
     48 #    define wl_dump(...) \
     49         do {             \
     50         } while (0)
     51 #endif // WEAR_LEVELING_DEBUG_OUTPUT
     52 
     53 #ifdef WEAR_LEVELING_ASSERTS
     54 #    include <assert.h>
     55 #    define wl_assert(...) assert(__VA_ARGS__)
     56 #else
     57 #    define wl_assert(...) \
     58         do {               \
     59         } while (0)
     60 #endif // WEAR_LEVELING_ASSERTS
     61 
     62 // Compile-time validation of configurable options
     63 STATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE >= (WEAR_LEVELING_LOGICAL_SIZE * 2), "Total backing size must be at least twice the size of the logical size");
     64 STATIC_ASSERT(WEAR_LEVELING_LOGICAL_SIZE % BACKING_STORE_WRITE_SIZE == 0, "Logical size must be a multiple of write size");
     65 STATIC_ASSERT(WEAR_LEVELING_BACKING_SIZE % WEAR_LEVELING_LOGICAL_SIZE == 0, "Backing size must be a multiple of logical size");
     66 
     67 // Backing Store API, to be implemented elsewhere by flash driver etc.
     68 bool backing_store_init(void);
     69 bool backing_store_unlock(void);
     70 bool backing_store_erase(void);
     71 bool backing_store_write(uint32_t address, backing_store_int_t value);
     72 bool backing_store_write_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver
     73 bool backing_store_lock(void);
     74 bool backing_store_read(uint32_t address, backing_store_int_t* value);
     75 bool backing_store_read_bulk(uint32_t address, backing_store_int_t* values, size_t item_count); // weak implementation already provided, optimized implementation can be implemented by driver
     76 
     77 /**
     78  * Helper type used to contain a write log entry.
     79  */
     80 typedef union write_log_entry_t {
     81     uint64_t raw64;
     82     uint32_t raw32[2];
     83     uint16_t raw16[4];
     84     uint8_t  raw8[8];
     85 } write_log_entry_t;
     86 
     87 STATIC_ASSERT(sizeof(write_log_entry_t) == 8, "Wear leveling write log entry size was not 8");
     88 
     89 /**
     90  * Log entry type discriminator.
     91  */
     92 enum {
     93     // 0x00 -- Multi-byte storage type
     94     LOG_ENTRY_TYPE_MULTIBYTE,
     95 
     96     // 0x01 -- 2-byte backing store write optimization: address < 64
     97     LOG_ENTRY_TYPE_OPTIMIZED_64,
     98 
     99     // 0x02 -- 2-byte backing store write optimization: word-encoded 0/1 values
    100     LOG_ENTRY_TYPE_WORD_01,
    101 
    102     LOG_ENTRY_TYPES
    103 };
    104 
    105 STATIC_ASSERT(LOG_ENTRY_TYPES <= (1 << 2), "Too many log entry types to fit into 2 bits of storage");
    106 
    107 #define BITMASK_FOR_BITCOUNT(n) ((1 << (n)) - 1)
    108 
    109 #define LOG_ENTRY_GET_TYPE(entry) (((entry).raw8[0] >> 6) & BITMASK_FOR_BITCOUNT(2))
    110 
    111 #define LOG_ENTRY_MULTIBYTE_MAX_BYTES 5
    112 #define LOG_ENTRY_MULTIBYTE_GET_ADDRESS(entry) (((((uint32_t)((entry).raw8[0])) & BITMASK_FOR_BITCOUNT(3)) << 16) | (((uint32_t)((entry).raw8[1])) << 8) | (entry).raw8[2])
    113 #define LOG_ENTRY_MULTIBYTE_GET_LENGTH(entry) ((uint8_t)(((entry).raw8[0] >> 3) & BITMASK_FOR_BITCOUNT(3)))
    114 #define LOG_ENTRY_MAKE_MULTIBYTE(address, length)                                                       \
    115     (write_log_entry_t) {                                                                               \
    116         .raw8 = {                                                                                       \
    117             [0] = (((((uint8_t)LOG_ENTRY_TYPE_MULTIBYTE) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \
    118                    | ((((uint8_t)(length)) & BITMASK_FOR_BITCOUNT(3)) << 3)               /* length */  \
    119                    | ((((uint8_t)((address) >> 16))) & BITMASK_FOR_BITCOUNT(3))           /* address */ \
    120                    ),                                                                                   \
    121             [1] = (((uint8_t)((address) >> 8)) & BITMASK_FOR_BITCOUNT(8)), /* address */                \
    122             [2] = (((uint8_t)(address)) & BITMASK_FOR_BITCOUNT(8)),        /* address */                \
    123         }                                                                                               \
    124     }
    125 
    126 #define LOG_ENTRY_OPTIMIZED_64_GET_ADDRESS(entry) ((uint32_t)((entry).raw8[0] & BITMASK_FOR_BITCOUNT(6)))
    127 #define LOG_ENTRY_OPTIMIZED_64_GET_VALUE(entry) ((entry).raw8[1])
    128 #define LOG_ENTRY_MAKE_OPTIMIZED_64(address, value)                                                        \
    129     (write_log_entry_t) {                                                                                  \
    130         .raw8 = {                                                                                          \
    131             [0] = (((((uint8_t)LOG_ENTRY_TYPE_OPTIMIZED_64) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \
    132                    | ((((uint8_t)(address))) & BITMASK_FOR_BITCOUNT(6))                      /* address */ \
    133                    ),                                                                                      \
    134             [1] = ((uint8_t)(value)), /* value */                                                          \
    135         }                                                                                                  \
    136     }
    137 
    138 #define LOG_ENTRY_WORD_01_GET_ADDRESS(entry) ((((uint32_t)(((entry).raw8[0]) & BITMASK_FOR_BITCOUNT(5))) << 9) | (((uint32_t)((entry).raw8[1])) << 1))
    139 #define LOG_ENTRY_WORD_01_GET_VALUE(entry) ((uint8_t)((entry).raw8[0] >> 5) & BITMASK_FOR_BITCOUNT(1))
    140 #define LOG_ENTRY_MAKE_WORD_01(address, value)                                                        \
    141     (write_log_entry_t) {                                                                             \
    142         .raw8 = {                                                                                     \
    143             [0] = (((((uint8_t)LOG_ENTRY_TYPE_WORD_01) & BITMASK_FOR_BITCOUNT(2)) << 6) /* type */    \
    144                    | (((((uint8_t)((value) ? 1 : 0))) & BITMASK_FOR_BITCOUNT(1)) << 5)  /* value */   \
    145                    | ((((uint8_t)((address) >> 9))) & BITMASK_FOR_BITCOUNT(5))          /* address */ \
    146                    ),                                                                                 \
    147             [1] = (uint8_t)((address) >> 1), /* address */                                            \
    148         }                                                                                             \
    149     }