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 }