From 4a19cdbffc8f022decf2e76e1bc5e7fc09db7f6a Mon Sep 17 00:00:00 2001 From: Patrick Kappl Date: Sun, 25 Aug 2024 22:45:42 +0000 Subject: [PATCH] Add `EdacVariable<>` We will need that later for `framIsWorking`. --- .../Utility/ErrorDetectionAndCorrection.hpp | 43 +++++++---- .../Utility/ErrorDetectionAndCorrection.ipp | 58 ++++++++++++++ Tests/UnitTests/CMakeLists.txt | 2 + .../ErrorDetectionAndCorrection.test.cpp | 75 +++++++++++++++++++ iwyu.imp | 34 +++++---- 5 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 Sts1CobcSw/Utility/ErrorDetectionAndCorrection.ipp diff --git a/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.hpp b/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.hpp index e70b811c..5a94cfdc 100644 --- a/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.hpp +++ b/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.hpp @@ -6,20 +6,37 @@ namespace sts1cobcsw { -// I won't make a separate .ipp file just for this simple function +template +class EdacVariable +{ +public: + constexpr EdacVariable() = default; + explicit constexpr EdacVariable(T const & value); + EdacVariable(EdacVariable const &) = delete; + EdacVariable(EdacVariable &&) = delete; + auto operator=(EdacVariable const &) -> EdacVariable & = delete; + auto operator=(EdacVariable &&) -> EdacVariable & = delete; + ~EdacVariable() = default; + + [[nodiscard]] constexpr auto Load() const -> T; + constexpr auto Store(T const & value) -> void; + + +private: + // This function must be const, because it is called from Load() which is const + constexpr auto SetAllValues(T const & value) const -> void; + + mutable T value0_ = T{}; + mutable T value1_ = T{}; + mutable T value2_ = T{}; +}; + + template [[nodiscard]] constexpr auto ComputeMajorityVote(T const & value0, T const & value1, - T const & value2) -> std::optional -{ - if(value0 == value1 or value0 == value2) - { - return value0; - } - if(value1 == value2) - { - return value1; - } - return std::nullopt; -} + T const & value2) -> std::optional; } + + +#include // IWYU pragma: keep diff --git a/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.ipp b/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.ipp new file mode 100644 index 00000000..532a241d --- /dev/null +++ b/Sts1CobcSw/Utility/ErrorDetectionAndCorrection.ipp @@ -0,0 +1,58 @@ +#pragma once + + +#include + + +namespace sts1cobcsw +{ +template +constexpr EdacVariable::EdacVariable(T const & value) + : value0_(value), value1_(value), value2_(value) +{ +} + + +template +constexpr auto EdacVariable::Load() const -> T +{ + // TODO: Make Load() thread-safe/atomic + auto voteResult = ComputeMajorityVote(value0_, value1_, value2_); + auto value = voteResult.value_or(value0_); + SetAllValues(value); + return value; +} + + +template +constexpr auto EdacVariable::Store(T const & value) -> void +{ + // TODO: Make Store() thread-safe/atomic + SetAllValues(value); +} + + +template +constexpr auto EdacVariable::SetAllValues(T const & value) const -> void +{ + value0_ = value; + value1_ = value; + value2_ = value; +} + + +template +constexpr auto ComputeMajorityVote(T const & value0, T const & value1, T const & value2) + -> std::optional +{ + if(value0 == value1 or value0 == value2) + { + return value0; + } + if(value1 == value2) + { + return value1; + } + return std::nullopt; +} +} diff --git a/Tests/UnitTests/CMakeLists.txt b/Tests/UnitTests/CMakeLists.txt index 22ad097a..5b88ac5c 100644 --- a/Tests/UnitTests/CMakeLists.txt +++ b/Tests/UnitTests/CMakeLists.txt @@ -9,6 +9,8 @@ add_program(ErrorDetectionAndCorrection ErrorDetectionAndCorrection.test.cpp) target_link_libraries( Sts1CobcSwTests_ErrorDetectionAndCorrection PRIVATE Catch2::Catch2WithMain Sts1CobcSw_Utility ) +# Disable warnings about memcpy() of non-trivially copiable type EdacVariable<> +target_compile_options(Sts1CobcSwTests_ErrorDetectionAndCorrection PRIVATE -Wno-class-memaccess) add_program(FlatArray FlatArray.test.cpp) target_link_libraries(Sts1CobcSwTests_FlatArray PRIVATE Catch2::Catch2WithMain Sts1CobcSw_Utility) diff --git a/Tests/UnitTests/ErrorDetectionAndCorrection.test.cpp b/Tests/UnitTests/ErrorDetectionAndCorrection.test.cpp index f23ec87c..83e22909 100644 --- a/Tests/UnitTests/ErrorDetectionAndCorrection.test.cpp +++ b/Tests/UnitTests/ErrorDetectionAndCorrection.test.cpp @@ -2,7 +2,11 @@ #include +#include +#include #include +#include +#include TEST_CASE("Majority vote") @@ -28,3 +32,74 @@ TEST_CASE("Majority vote") voteResult = ComputeMajorityVote(17, 173, -2); CHECK(not voteResult.has_value()); } + + +struct S +{ + char c = 's'; + + [[nodiscard]] friend constexpr auto operator==(S const & lhs, S const & rhs) -> bool + { + return lhs.c == rhs.c; + } +}; + + +TEST_CASE("EdacVariable") +{ + using sts1cobcsw::EdacVariable; + + + SECTION("Construction") + { + auto variable1 = EdacVariable(); + auto variable2 = EdacVariable(); + auto variable3 = EdacVariable(-3.14); + CHECK(variable1.Load().c == 's'); + CHECK(variable2.Load() == 0); + CHECK(variable3.Load() == -3.14); + } + + SECTION("You load what you store") + { + auto variable = EdacVariable(17); + CHECK(variable.Load() == 17); + variable.Store(-123); + CHECK(variable.Load() == -123); + } + + SECTION("Error correction") + { + auto variable = EdacVariable('a'); + CHECK(variable.Load() == 'a'); + + // Those memcpy's are unspecified behavior but I can't think of a better way (= one that is + // not undefined behavior) and it seems to work + auto data = std::array{}; + std::memcpy(data.data(), &variable, sizeof(variable)); + data[0] = 'x'; + std::memcpy(&variable, data.data(), sizeof(variable)); + CHECK(variable.Load() == 'a'); + std::memcpy(data.data(), &variable, sizeof(variable)); + CHECK(data == std::array{'a', 'a', 'a'}); + + data[1] = 'x'; + std::memcpy(&variable, data.data(), sizeof(variable)); + CHECK(variable.Load() == 'a'); + std::memcpy(data.data(), &variable, sizeof(variable)); + CHECK(data == std::array{'a', 'a', 'a'}); + + data[2] = 'x'; + std::memcpy(&variable, data.data(), sizeof(variable)); + CHECK(variable.Load() == 'a'); + std::memcpy(data.data(), &variable, sizeof(variable)); + CHECK(data == std::array{'a', 'a', 'a'}); + + data[0] = 'x'; + data[1] = 'x'; + std::memcpy(&variable, data.data(), sizeof(variable)); + CHECK(variable.Load() == 'x'); + std::memcpy(data.data(), &variable, sizeof(variable)); + CHECK(data == std::array{'x', 'x', 'x'}); + } +} diff --git a/iwyu.imp b/iwyu.imp index 313ec15f..81c009e8 100644 --- a/iwyu.imp +++ b/iwyu.imp @@ -45,21 +45,22 @@ { include: ["", "private", "", "public"] }, # Instead of all our .ipp files include the .hpp ones - { include: ["\"Sts1CobcSw/Edu/Types.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/FileSystem/FileSystem.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/FramSections/Subsections.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/FramSections/PersistentVariables.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Hal/GpioPin.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Hal/Spi.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Hal/Uart.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Periphery/Fram.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/PersistentState/PersistentVariable.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/ProgramId/ProgramId.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Serial/Byte.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Serial/Serial.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Utility/Span.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Utility/StringLiteral.ipp\"", "private", "", "public"] }, - { include: ["\"Sts1CobcSw/Utility/Time.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Edu/Types.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/FileSystem/FileSystem.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/FramSections/Subsections.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/FramSections/PersistentVariables.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Hal/GpioPin.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Hal/Spi.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Hal/Uart.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Periphery/Fram.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/PersistentState/PersistentVariable.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/ProgramId/ProgramId.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Serial/Byte.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Serial/Serial.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Utility/ErrorDetectionAndCorrection.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Utility/Span.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Utility/StringLiteral.ipp\"", "private", "", "public"] }, + { include: ["\"Sts1CobcSw/Utility/Time.ipp\"", "private", "", "public"] }, # Include all our headers with <> instead of "" { include: ["\"Sts1CobcSw/Edu/Edu.hpp\"", "public", "", "public"] }, @@ -91,9 +92,10 @@ { include: ["\"Sts1CobcSw/PersistentState/PersistentVariable.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/ProgramId/ProgramId.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Serial/Byte.hpp\"", "public", "", "public"] }, - { include: ["\"Sts1CobcSw/Serial/Serial.hpp\"", "public", "", "public"] }, + { include: ["\"Sts1CobcSw/Serial/Serial.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Utility/Crc32.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Utility/Debug.hpp\"", "public", "", "public"] }, + { include: ["\"Sts1CobcSw/Utility/ErrorDetectionAndCorrection.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Utility/FlatArray.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Utility/Span.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Utility/StringLiteral.hpp\"", "public", "", "public"] },