From 01ecf332ff9f0c8f61e493cdca116f58d80bb5fb Mon Sep 17 00:00:00 2001 From: Nick Brassel Date: Mon, 27 Jun 2022 07:18:21 +1000 Subject: Generic wear-leveling algorithm (#16996) * Initial import of wear-leveling algorithm. * Alignment. * Docs tweaks. * Lock/unlock. * Update quantum/wear_leveling/wear_leveling_internal.h Co-authored-by: Stefan Kerkmann * More tests, fix issue with consolidation when unlocked. * More tests. * Review comments. * Add plumbing for FNV1a. * Another test checking that checksum mismatch clears the cache. * Check that the write log still gets played back. Co-authored-by: Stefan Kerkmann --- quantum/wear_leveling/tests/backing_mocks.hpp | 210 ++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 quantum/wear_leveling/tests/backing_mocks.hpp (limited to 'quantum/wear_leveling/tests/backing_mocks.hpp') diff --git a/quantum/wear_leveling/tests/backing_mocks.hpp b/quantum/wear_leveling/tests/backing_mocks.hpp new file mode 100644 index 0000000000..e7af7895f3 --- /dev/null +++ b/quantum/wear_leveling/tests/backing_mocks.hpp @@ -0,0 +1,210 @@ +// Copyright 2022 Nick Brassel (@tzarc) +// SPDX-License-Identifier: GPL-2.0-or-later +#pragma once +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "fnv.h" +#include "wear_leveling.h" +#include "wear_leveling_internal.h" +}; + +// Maximum number of mock write log entries to keep +using MOCK_WRITE_LOG_MAX_ENTRIES = std::integral_constant; +// Complement to the backing store integral, for emulating flash erases of all bytes=0xFF +using BACKING_STORE_INTEGRAL_COMPLEMENT = std::integral_constant; +// Total number of elements stored in the backing arrays +using BACKING_STORE_ELEMENT_COUNT = std::integral_constant; + +class MockBackingStoreElement { + private: + backing_store_int_t value; + std::size_t writes; + std::size_t erases; + + public: + MockBackingStoreElement() : value(BACKING_STORE_INTEGRAL_COMPLEMENT::value), writes(0), erases(0) {} + void reset() { + erase(); + writes = 0; + erases = 0; + } + void erase() { + if (!is_erased()) { + ++erases; + } + value = BACKING_STORE_INTEGRAL_COMPLEMENT::value; + } + backing_store_int_t get() const { + return value; + } + void set(const backing_store_int_t& v) { + EXPECT_TRUE(is_erased()) << "Attempted write at index which isn't empty."; + value = v; + ++writes; + } + std::size_t num_writes() const { + return writes; + } + std::size_t num_erases() const { + return erases; + } + bool is_erased() const { + return value == BACKING_STORE_INTEGRAL_COMPLEMENT::value; + } +}; + +struct MockBackingStoreLogEntry { + MockBackingStoreLogEntry(uint32_t address, backing_store_int_t value) : address(address), value(value), erased(false) {} + MockBackingStoreLogEntry(bool erased) : address(0), value(0), erased(erased) {} + uint32_t address = 0; // The address of the operation + backing_store_int_t value = 0; // The value of the operation + bool erased = false; // Whether the entire backing store was erased +}; + +class MockBackingStore { + private: + MockBackingStore() { + reset_instance(); + } + + // Type containing each of the entries and the write counts + using storage_t = std::array; + + // Whether the backing store is locked + bool locked; + // The actual data stored in the emulated flash + storage_t backing_storage; + // The number of erase cycles that have occurred + std::uint64_t backing_erasure_count; + // The max number of writes to an element of the backing store + std::uint64_t backing_max_write_count; + // The total number of writes to all elements of the backing store + std::uint64_t backing_total_write_count; + // The write log for the backing store + std::vector write_log; + + // The number of times each API was invoked + std::uint64_t backing_init_invoke_count; + std::uint64_t backing_unlock_invoke_count; + std::uint64_t backing_erase_invoke_count; + std::uint64_t backing_write_invoke_count; + std::uint64_t backing_lock_invoke_count; + + // Whether init should succeed + std::function init_success_callback; + // Whether erase should succeed + std::function erase_success_callback; + // Whether unlocks should succeed + std::function unlock_success_callback; + // Whether writes should succeed + std::function write_success_callback; + // Whether locks should succeed + std::function lock_success_callback; + + template + void append_log(Args&&... args) { + if (write_log.size() < MOCK_WRITE_LOG_MAX_ENTRIES::value) { + write_log.emplace_back(std::forward(args)...); + } + } + + public: + static MockBackingStore& Instance() { + static MockBackingStore instance; + return instance; + } + + std::uint64_t erasure_count() const { + return backing_erasure_count; + } + std::uint64_t max_write_count() const { + return backing_max_write_count; + } + std::uint64_t total_write_count() const { + return backing_total_write_count; + } + + // The number of times each API was invoked + std::uint64_t init_invoke_count() const { + return backing_init_invoke_count; + } + std::uint64_t unlock_invoke_count() const { + return backing_unlock_invoke_count; + } + std::uint64_t erase_invoke_count() const { + return backing_erase_invoke_count; + } + std::uint64_t write_invoke_count() const { + return backing_write_invoke_count; + } + std::uint64_t lock_invoke_count() const { + return backing_lock_invoke_count; + } + + // Clear out the internal data for the next run + void reset_instance(); + + bool is_locked() const { + return locked; + } + + // APIs for the backing store + bool init(); + bool unlock(); + bool erase(); + bool write(std::uint32_t address, backing_store_int_t value); + bool lock(); + bool read(std::uint32_t address, backing_store_int_t& value) const; + + // Control over when init/writes/erases should succeed + void set_init_callback(std::function callback) { + init_success_callback = callback; + } + void set_erase_callback(std::function callback) { + erase_success_callback = callback; + } + void set_unlock_callback(std::function callback) { + unlock_success_callback = callback; + } + void set_write_callback(std::function callback) { + write_success_callback = callback; + } + void set_lock_callback(std::function callback) { + lock_success_callback = callback; + } + + auto storage_begin() const -> decltype(backing_storage.begin()) { + return backing_storage.begin(); + } + auto storage_end() const -> decltype(backing_storage.end()) { + return backing_storage.end(); + } + + auto storage_begin() -> decltype(backing_storage.begin()) { + return backing_storage.begin(); + } + auto storage_end() -> decltype(backing_storage.end()) { + return backing_storage.end(); + } + + auto log_begin() -> decltype(write_log.begin()) { + return write_log.begin(); + } + auto log_end() -> decltype(write_log.end()) { + return write_log.end(); + } + + auto log_begin() const -> decltype(write_log.begin()) { + return write_log.begin(); + } + auto log_end() const -> decltype(write_log.end()) { + return write_log.end(); + } +}; -- cgit v1.2.3