You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

151 lines
7.4 KiB

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