diff --git a/.codespell-ignore b/.codespell-ignore index 703c336f..682c3983 100644 --- a/.codespell-ignore +++ b/.codespell-ignore @@ -1,3 +1,4 @@ tust fram framlayout +framBuffer diff --git a/Sts1CobcSw/CobcSoftware/CommandParser.cpp b/Sts1CobcSw/CobcSoftware/CommandParser.cpp index 390a2ec4..1ffd8bf2 100644 --- a/Sts1CobcSw/CobcSoftware/CommandParser.cpp +++ b/Sts1CobcSw/CobcSoftware/CommandParser.cpp @@ -5,8 +5,9 @@ #include #include -#include // IWYU pragma: keep +#include +#include // IWYU pragma: keep namespace sts1cobcsw { diff --git a/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.cpp b/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.cpp index dce2e23e..0c1512ca 100644 --- a/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.cpp +++ b/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.hpp b/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.hpp index ed3771e7..e99a0678 100644 --- a/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.hpp +++ b/Sts1CobcSw/CobcSoftware/EduCommunicationErrorThread.hpp @@ -1,9 +1,6 @@ #pragma once -#include - - namespace sts1cobcsw { auto ResumeEduCommunicationErrorThread() -> void; diff --git a/Sts1CobcSw/Edu/Edu.cpp b/Sts1CobcSw/Edu/Edu.cpp index aa0d9081..9806ddd4 100644 --- a/Sts1CobcSw/Edu/Edu.cpp +++ b/Sts1CobcSw/Edu/Edu.cpp @@ -432,7 +432,7 @@ auto ReceiveDataPacket() -> Result template auto Receive() -> Result { - auto buffer = Buffer{}; + auto buffer = SerialBuffer{}; OUTCOME_TRY(Receive(buffer)); return Deserialize(buffer); } diff --git a/Sts1CobcSw/Edu/EduMock.cpp b/Sts1CobcSw/Edu/EduMock.cpp index d4e7a787..6bc4ee66 100644 --- a/Sts1CobcSw/Edu/EduMock.cpp +++ b/Sts1CobcSw/Edu/EduMock.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include // IWYU pragma: keep diff --git a/Sts1CobcSw/Edu/ProgramStatusHistory.cpp b/Sts1CobcSw/Edu/ProgramStatusHistory.cpp index 0a6f75c2..5c7cfca5 100644 --- a/Sts1CobcSw/Edu/ProgramStatusHistory.cpp +++ b/Sts1CobcSw/Edu/ProgramStatusHistory.cpp @@ -22,4 +22,36 @@ auto UpdateProgramStatusHistory(ProgramId programId, RealTime startTime, Program } } } + + +using sts1cobcsw::DeserializeFrom; +using sts1cobcsw::SerializeTo; + + +template +auto DeserializeFrom(void const * source, ProgramStatusHistoryEntry * data) -> void const * +{ + source = DeserializeFrom(source, &(data->programId)); + source = DeserializeFrom(source, &(data->startTime)); + source = DeserializeFrom(source, &(data->status)); + return source; +} + + +template +auto SerializeTo(void * destination, ProgramStatusHistoryEntry const & data) -> void * +{ + destination = SerializeTo(destination, data.programId); + destination = SerializeTo(destination, data.startTime); + destination = SerializeTo(destination, data.status); + return destination; +} + + +template auto DeserializeFrom(void const *, ProgramStatusHistoryEntry *) + -> void const *; +template auto DeserializeFrom(void const *, ProgramStatusHistoryEntry *) + -> void const *; +template auto SerializeTo(void *, ProgramStatusHistoryEntry const &) -> void *; +template auto SerializeTo(void *, ProgramStatusHistoryEntry const &) -> void *; } diff --git a/Sts1CobcSw/Edu/ProgramStatusHistory.hpp b/Sts1CobcSw/Edu/ProgramStatusHistory.hpp index a378caee..4098dd43 100644 --- a/Sts1CobcSw/Edu/ProgramStatusHistory.hpp +++ b/Sts1CobcSw/Edu/ProgramStatusHistory.hpp @@ -16,6 +16,7 @@ #include // clang-format on +#include #include @@ -64,5 +65,12 @@ extern RODOS::RingBuffer void; + +template +[[nodiscard]] auto DeserializeFrom(void const * source, ProgramStatusHistoryEntry * data) + -> void const *; +template +[[nodiscard]] auto SerializeTo(void * destination, ProgramStatusHistoryEntry const & data) + -> void *; } } diff --git a/Sts1CobcSw/FramSections/FramLayout.hpp b/Sts1CobcSw/FramSections/FramLayout.hpp index 25612e0b..a0129718 100644 --- a/Sts1CobcSw/FramSections/FramLayout.hpp +++ b/Sts1CobcSw/FramSections/FramLayout.hpp @@ -13,20 +13,15 @@ namespace sts1cobcsw { inline constexpr auto framMemory = Section{}; -inline constexpr auto persistentVariablesSize = fram::Size(100); inline constexpr auto framSections = Subsections, - SubsectionInfo<"persistentVariables1", persistentVariablesSize>, - SubsectionInfo<"persistentVariables2", persistentVariablesSize>, + SubsectionInfo<"persistentVariables0", fram::Size(300)>, SubsectionInfo<"eduProgramQueue", fram::Size(20 * 8)>, SubsectionInfo<"eduProgramStatusHistory", fram::Size(50 * 7)>, SubsectionInfo<"testMemory", fram::Size(1000)>, SubsectionInfo<"telemetry", fram::Size(26'168 * 40)>>{}; inline constexpr auto persistentVariables = PersistentVariables(), - framSections.Get<"persistentVariables1">(), - framSections.Get<"persistentVariables2">(), PersistentVariableInfo<"nResetsSinceRf", std::uint16_t>, PersistentVariableInfo<"activeSecondaryFwPartition", std::int8_t>, PersistentVariableInfo<"backupSecondaryFwPartition", std::int8_t>, diff --git a/Sts1CobcSw/FramSections/PersistentVariables.hpp b/Sts1CobcSw/FramSections/PersistentVariables.hpp index 821005ea..dbce9a98 100644 --- a/Sts1CobcSw/FramSections/PersistentVariables.hpp +++ b/Sts1CobcSw/FramSections/PersistentVariables.hpp @@ -14,22 +14,18 @@ namespace sts1cobcsw { -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) class PersistentVariables { public: + static constexpr auto section = persistentVariablesSection; + template using ValueType = std::tuple_element_t< - Subsections::template Index(), + Subsections::template Index(), std::tuple>; - constexpr PersistentVariables() = default; - template [[nodiscard]] static auto Load() -> ValueType; template @@ -52,9 +48,16 @@ class PersistentVariables static std::tuple cache1; static std::tuple cache2; - static constexpr auto subsections0 = Subsections(); - static constexpr auto subsections1 = Subsections(); - static constexpr auto subsections2 = Subsections(); + static constexpr auto subsections = Subsections, + SubsectionInfo<"1", section.size / 3>, + SubsectionInfo<"2", section.size / 3>>{}; + static constexpr auto variables0 = + Subsections(), PersistentVariableInfos...>(); + static constexpr auto variables1 = + Subsections(), PersistentVariableInfos...>(); + static constexpr auto variables2 = + Subsections(), PersistentVariableInfos...>(); // With a baud rate of 48 MHz we can read 6000 bytes in 1 ms, which should be more than enough static constexpr auto spiTimeout = 1 * RODOS::MILLISECONDS; diff --git a/Sts1CobcSw/FramSections/PersistentVariables.ipp b/Sts1CobcSw/FramSections/PersistentVariables.ipp index be059a1c..a544f07a 100644 --- a/Sts1CobcSw/FramSections/PersistentVariables.ipp +++ b/Sts1CobcSw/FramSections/PersistentVariables.ipp @@ -8,29 +8,10 @@ namespace sts1cobcsw { -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) -RODOS::Semaphore PersistentVariables::semaphore = RODOS::Semaphore(); - - -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::Load() -> ValueType +auto PersistentVariables::Load() -> ValueType { auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) auto [value0, value1, value2] = @@ -47,17 +28,10 @@ auto PersistentVariables - requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::Store(ValueType const & value) +auto PersistentVariables::Store(ValueType const & value) { auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) if(fram::framIsWorking.Load()) @@ -68,17 +42,10 @@ auto PersistentVariables - requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::Increment() +auto PersistentVariables::Increment() { auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) auto [value0, value1, value2] = @@ -94,25 +61,18 @@ auto PersistentVariables - requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::ReadFromFram() +auto PersistentVariables::ReadFromFram() -> std::array, 3> { - constexpr auto address0 = subsections0.template Get().begin; - constexpr auto address1 = subsections1.template Get().begin; - constexpr auto address2 = subsections2.template Get().begin; - constexpr auto size0 = value_of(subsections0.template Get().size); - constexpr auto size1 = value_of(subsections1.template Get().size); - constexpr auto size2 = value_of(subsections2.template Get().size); + constexpr auto address0 = variables0.template Get().begin; + constexpr auto address1 = variables1.template Get().begin; + constexpr auto address2 = variables2.template Get().begin; + constexpr auto size0 = value_of(variables0.template Get().size); + constexpr auto size1 = value_of(variables1.template Get().size); + constexpr auto size2 = value_of(variables2.template Get().size); auto value0 = Deserialize>(fram::ReadFrom(address0, spiTimeout)); auto value1 = Deserialize>(fram::ReadFrom(address1, spiTimeout)); auto value2 = Deserialize>(fram::ReadFrom(address2, spiTimeout)); @@ -120,102 +80,71 @@ auto PersistentVariables - requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::WriteToFram(ValueType const & value) +auto PersistentVariables::WriteToFram( + ValueType const & value) { - constexpr auto address0 = subsections0.template Get().begin; - constexpr auto address1 = subsections1.template Get().begin; - constexpr auto address2 = subsections2.template Get().begin; + constexpr auto address0 = variables0.template Get().begin; + constexpr auto address1 = variables1.template Get().begin; + constexpr auto address2 = variables2.template Get().begin; fram::WriteTo(address0, Span(Serialize(value)), spiTimeout); fram::WriteTo(address1, Span(Serialize(value)), spiTimeout); fram::WriteTo(address2, Span(Serialize(value)), spiTimeout); } -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::ReadFromCache() +auto PersistentVariables::ReadFromCache() -> std::array, 3> { - constexpr auto index = subsections0.template Index(); + constexpr auto index = variables0.template Index(); return {get(cache0), get(cache1), get(cache2)}; } -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) template -auto PersistentVariables::WriteToCache(ValueType const & value) +auto PersistentVariables::WriteToCache( + ValueType const & value) { - constexpr auto index = subsections0.template Index(); + constexpr auto index = variables0.template Index(); get(cache0) = value; get(cache1) = value; get(cache2) = value; } -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) std::tuple PersistentVariables< - parentSection0, - parentSection1, - parentSection2, + section, PersistentVariableInfos...>::cache0 = std::tuple(typename PersistentVariableInfos::ValueType{}...); -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) std::tuple PersistentVariables< - parentSection0, - parentSection1, - parentSection2, + section, PersistentVariableInfos...>::cache1 = std::tuple(typename PersistentVariableInfos::ValueType{}...); -template
- requires(sizeof...(PersistentVariableInfos) > 0 && parentSection0.end <= parentSection1.begin - && parentSection1.end <= parentSection2.begin) +template
+ requires(sizeof...(PersistentVariableInfos) > 0) std::tuple PersistentVariables< - parentSection0, - parentSection1, - parentSection2, + section, PersistentVariableInfos...>::cache2 = std::tuple(typename PersistentVariableInfos::ValueType{}...); + + +template
+ requires(sizeof...(PersistentVariableInfos) > 0) +RODOS::Semaphore PersistentVariables::semaphore = + RODOS::Semaphore(); } diff --git a/Sts1CobcSw/Periphery/Eps.cpp b/Sts1CobcSw/Periphery/Eps.cpp index 016614c1..055005ff 100644 --- a/Sts1CobcSw/Periphery/Eps.cpp +++ b/Sts1CobcSw/Periphery/Eps.cpp @@ -230,7 +230,7 @@ auto ReadAdc(hal::GpioPin * adcCsPin) -> AdcValues RODOS::AT(RODOS::NOW() + conversionTime); // Resolution is 12 bit, sent like this: [0 0 0 0 MSB x x x], [x x x x x x x LSB] - auto adcData = Buffer{}; + auto adcData = SerialBuffer{}; adcCsPin->Reset(); hal::ReadFrom(&framEpsSpi, Span(&adcData), spiTimeout); adcCsPin->Set(); diff --git a/Sts1CobcSw/Periphery/Fram.hpp b/Sts1CobcSw/Periphery/Fram.hpp index 15154ffe..06a8279d 100644 --- a/Sts1CobcSw/Periphery/Fram.hpp +++ b/Sts1CobcSw/Periphery/Fram.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -20,11 +21,8 @@ namespace sts1cobcsw::fram { // NOLINTNEXTLINE(*magic-numbers) using DeviceId = std::array; -using Size = strong::type>; +using Size = strong:: + type>; using Address = strong::type, diff --git a/Sts1CobcSw/Periphery/FramRingArray.hpp b/Sts1CobcSw/Periphery/FramRingArray.hpp new file mode 100644 index 00000000..a27ba8fd --- /dev/null +++ b/Sts1CobcSw/Periphery/FramRingArray.hpp @@ -0,0 +1,80 @@ +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + + +// TODO: Add cache in case the FRAM is not working +// TODO: Move to FramSections +namespace sts1cobcsw +{ +template + requires(serialSize > 0) +class RingArray +{ +public: + using ValueType = T; + static constexpr auto section = ringArraySection; + + [[nodiscard]] static constexpr auto FramCapacity() -> std::size_t; + [[nodiscard]] static constexpr auto CacheCapacity() -> std::size_t; + [[nodiscard]] static auto Size() -> std::size_t; + [[nodiscard]] static auto Get(std::size_t index) -> T; + [[nodiscard]] static auto Front() -> T; + [[nodiscard]] static auto Back() -> T; + static auto Set(std::size_t index, T const & t) -> void; + static auto PushBack(T const & t) -> void; + + +private: + static constexpr auto elementSize = fram::Size(serialSize); + static constexpr auto indexesSize = fram::Size(3 * 2 * totalSerialSize); + static constexpr auto subsections = + Subsections, + SubsectionInfo<"array", section.size - indexesSize>>{}; + static constexpr auto persistentIndexes = + PersistentVariables(), + PersistentVariableInfo<"iBegin", std::size_t>, + PersistentVariableInfo<"iEnd", std::size_t>>{}; + // We reduce the FRAM capacity by one to distinguish between an empty and a full ring. See + // PushBack() for details. + static constexpr auto framCapacity = subsections.template Get<"array">().size / elementSize - 1; + static constexpr auto spiTimeout = elementSize < 300U ? 1 * ms : value_of(elementSize) * 3 * us; + + + using RingIndex = etl::cyclic_value; + + static RingIndex iEnd; + static RingIndex iBegin; + static etl::circular_buffer, nCachedElements> cache; + static RODOS::Semaphore semaphore; + + static auto LoadIndexes() -> void; + static auto StoreIndexes() -> void; + [[nodiscard]] static auto ComputeSize() -> std::size_t; + [[nodiscard]] static auto ReadElement(RingIndex index) -> T; + static auto WriteElement(RingIndex index, T const & t) -> void; +}; +} + + +#include // IWYU pragma: keep diff --git a/Sts1CobcSw/Periphery/FramRingArray.ipp b/Sts1CobcSw/Periphery/FramRingArray.ipp new file mode 100644 index 00000000..06a52db1 --- /dev/null +++ b/Sts1CobcSw/Periphery/FramRingArray.ipp @@ -0,0 +1,226 @@ +#pragma once + + +#include +#include +#include + + +namespace sts1cobcsw +{ +using fram::framIsWorking; + + +template + requires(serialSize > 0) +inline constexpr auto RingArray::FramCapacity() -> std::size_t +{ + return framCapacity; +} + + +template + requires(serialSize > 0) +inline constexpr auto RingArray::CacheCapacity() + -> std::size_t +{ + return nCachedElements; +} + + +template + requires(serialSize > 0) +auto RingArray::Size() -> std::size_t +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + if(not framIsWorking.Load()) + { + return cache.size(); + } + LoadIndexes(); + return ComputeSize(); +} + + +template + requires(serialSize > 0) +auto RingArray::Get(std::size_t index) -> T +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + auto size = []() + { + if(framIsWorking.Load()) + { + LoadIndexes(); + return ComputeSize(); + } + return cache.size(); + }(); + if(index >= size) + { + DEBUG_PRINT("Index out of bounds in RingArray::Get: %u >= %u\n", index, size); + index = size - 1; + } + if(not framIsWorking.Load()) + { + return Deserialize(cache[index]); + } + auto i = iBegin; + i.advance(static_cast(index)); + return ReadElement(i); +} + + +template + requires(serialSize > 0) +auto RingArray::Front() -> T +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + if(not framIsWorking.Load()) + { + return Deserialize(cache.front()); + } + LoadIndexes(); + return ReadElement(iBegin); +} + + +template + requires(serialSize > 0) +auto RingArray::Back() -> T +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + if(not framIsWorking.Load()) + { + return Deserialize(cache.back()); + } + LoadIndexes(); + auto i = iEnd; + i--; + return ReadElement(i); +} + + +template + requires(serialSize > 0) +auto RingArray::Set(std::size_t index, T const & t) -> void +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + auto size = []() + { + if(framIsWorking.Load()) + { + LoadIndexes(); + return ComputeSize(); + } + return cache.size(); + }(); + if(index >= size) + { + DEBUG_PRINT("Index out of bounds in RingArray::Set: %u >= %u\n", index, size); + return; + } + if(not framIsWorking.Load()) + { + cache[index] = Serialize(t); + } + auto i = iBegin; + i.advance(static_cast(index)); + WriteElement(i, t); +} + + +template + requires(serialSize > 0) +auto RingArray::PushBack(T const & t) -> void +{ + auto protector = RODOS::ScopeProtector(&semaphore); // NOLINT(google-readability-casting) + if(not framIsWorking.Load()) + { + cache.push(Serialize(t)); + return; + } + LoadIndexes(); + WriteElement(iEnd, t); + // We reduce the capacity by one to distinguish between an empty and a full ring: iEnd == iBegin + // means empty, iEnd == iBegin - 1 means full + iEnd++; + if(iEnd == iBegin) + { + iBegin++; + } + StoreIndexes(); +} + + +template + requires(serialSize > 0) +typename RingArray::RingIndex + RingArray::iEnd = {}; + + +template + requires(serialSize > 0) +typename RingArray::RingIndex + RingArray::iBegin = {}; + + +template + requires(serialSize > 0) +etl::circular_buffer, + nCachedElements> RingArray::cache = {}; + + +template + requires(serialSize > 0) +RODOS::Semaphore RingArray::semaphore = {}; + + +template + requires(serialSize > 0) +auto RingArray::LoadIndexes() -> void +{ + iBegin.set(persistentIndexes.template Load<"iBegin">()); + iEnd.set(persistentIndexes.template Load<"iEnd">()); +} + + +template + requires(serialSize > 0) +auto RingArray::StoreIndexes() -> void +{ + persistentIndexes.template Store<"iBegin">(iBegin.get()); + persistentIndexes.template Store<"iEnd">(iEnd.get()); +} + + +template + requires(serialSize > 0) +auto RingArray::ComputeSize() -> std::size_t +{ + if(iEnd.get() >= iBegin.get()) + { + return iEnd.get() - iBegin.get(); + } + return framCapacity + 1 + iEnd.get() - iBegin.get(); +} + + +template + requires(serialSize > 0) +auto RingArray::ReadElement(RingIndex index) -> T +{ + auto address = subsections.template Get<"array">().begin + index.get() * elementSize; + return Deserialize(fram::ReadFrom>(address, value_of(spiTimeout))); +} + + +template + requires(serialSize > 0) +auto RingArray::WriteElement(RingIndex index, T const & t) + -> void +{ + auto address = subsections.template Get<"array">().begin + index.get() * elementSize; + fram::WriteTo(address, Span(Serialize(t)), value_of(spiTimeout)); +} +} diff --git a/Sts1CobcSw/Serial/Serial.hpp b/Sts1CobcSw/Serial/Serial.hpp index c8a1fcaf..f7e12394 100644 --- a/Sts1CobcSw/Serial/Serial.hpp +++ b/Sts1CobcSw/Serial/Serial.hpp @@ -59,25 +59,24 @@ inline constexpr auto defaultEndianness = std::endian::little; template requires(serialSize != 0) -using Buffer = std::array>; +using SerialBuffer = std::array>; -// TODO: Maybe remove this type alias or prepend "Serial" to both again template requires(serialSize != 0) -using BufferView = std::span>; +using SerialBufferView = std::span>; template -[[nodiscard]] auto Serialize(T const & t) -> Buffer; +[[nodiscard]] auto Serialize(T const & t) -> SerialBuffer; template -[[nodiscard]] auto Serialize(T const & t) -> Buffer; +[[nodiscard]] auto Serialize(T const & t) -> SerialBuffer; template -[[nodiscard]] auto Deserialize(BufferView bufferView) -> T; +[[nodiscard]] auto Deserialize(SerialBufferView bufferView) -> T; template -[[nodiscard]] auto Deserialize(BufferView bufferView) -> T; +[[nodiscard]] auto Deserialize(SerialBufferView bufferView) -> T; // Must be overloaded for user-defined types to be serializable template diff --git a/Sts1CobcSw/Serial/Serial.ipp b/Sts1CobcSw/Serial/Serial.ipp index 111d611e..dcec45b4 100644 --- a/Sts1CobcSw/Serial/Serial.ipp +++ b/Sts1CobcSw/Serial/Serial.ipp @@ -7,30 +7,30 @@ namespace sts1cobcsw { template -inline auto Serialize(T const & t) -> Buffer +inline auto Serialize(T const & t) -> SerialBuffer { return Serialize(t); } template -inline auto Serialize(T const & t) -> Buffer +inline auto Serialize(T const & t) -> SerialBuffer { - auto buffer = Buffer{}; + auto buffer = SerialBuffer{}; (void)SerializeTo(buffer.data(), t); return buffer; } template -inline auto Deserialize(BufferView bufferView) -> T +inline auto Deserialize(SerialBufferView bufferView) -> T { return Deserialize(bufferView); } template -inline auto Deserialize(BufferView bufferView) -> T +inline auto Deserialize(SerialBufferView bufferView) -> T { auto t = T{}; (void)DeserializeFrom(bufferView.data(), &t); diff --git a/Tests/GoldenTests/CMakeLists.txt b/Tests/GoldenTests/CMakeLists.txt index ac92f597..1c4c2b61 100644 --- a/Tests/GoldenTests/CMakeLists.txt +++ b/Tests/GoldenTests/CMakeLists.txt @@ -9,6 +9,4 @@ add_golden_test(TESTFILE "HelloWorld.test.cpp" LIB rodos::rodos) add_golden_test(TESTFILE "HelloDummy.test.cpp" LIB rodos::rodos etl::etl Sts1CobcSw_Dummy) -add_golden_test(TESTFILE "UpdateRingBuffer.test.cpp" LIB rodos::rodos Sts1CobcSw_Edu) - add_custom_target(AllGoldenTests DEPENDS ${output_files}) diff --git a/Tests/GoldenTests/UpdateRingBuffer.test.cpp b/Tests/GoldenTests/UpdateRingBuffer.test.cpp deleted file mode 100644 index 7accc36c..00000000 --- a/Tests/GoldenTests/UpdateRingBuffer.test.cpp +++ /dev/null @@ -1,107 +0,0 @@ -#include -#include -#include - -#include - -// clang-format off -#include -// ringbuffer.h does not include even though it requires it -#include -// clang-format on -#include - -#include - - -std::uint32_t printfMask = 0; - - -namespace sts1cobcsw -{ -auto ToString(edu::ProgramStatus status) -> std::string_view -{ - switch(status) - { - case edu::ProgramStatus::programRunning: - return "programRunning"; - case edu::ProgramStatus::programExecutionFailed: - return "programExecutionFailed"; - case edu::ProgramStatus::programExecutionSucceeded: - return "programExecutionSucceeded"; - default: - return ""; - } -} - - -// Helper function for edu::programStatusHistory -void PrintBuffer() -{ - for(std::uint32_t i = 0; i < edu::programStatusHistory.occupiedCnt; ++i) - { - RODOS::PRINTF("Vals[%d] = .id(%d), .status(%s)\n", - i, - value_of(edu::programStatusHistory.vals[i].programId), - ToString(edu::programStatusHistory.vals[i].status).data()); - } -} - - -class UpdateRingBufferTest : public RODOS::StaticThread<> -{ - void run() override - { - printfMask = 1; - - edu::programStatusHistory.put( - edu::ProgramStatusHistoryEntry{.programId = ProgramId(1), - .startTime = RealTime(1), - .status = edu::ProgramStatus::programExecutionFailed}); - edu::programStatusHistory.put( - edu::ProgramStatusHistoryEntry{.programId = ProgramId(2), - .startTime = RealTime(1), - .status = edu::ProgramStatus::programRunning}); - edu::programStatusHistory.put( - edu::ProgramStatusHistoryEntry{.programId = ProgramId(3), - .startTime = RealTime(1), - .status = edu::ProgramStatus::programRunning}); - edu::programStatusHistory.put( - edu::ProgramStatusHistoryEntry{.programId = ProgramId(4), - .startTime = RealTime(1), - .status = edu::ProgramStatus::programRunning}); - - - auto readCnt = edu::programStatusHistory.readCnt; - auto writeCnt = edu::programStatusHistory.writeCnt; - auto occupiedCnt = edu::programStatusHistory.occupiedCnt; - - PrintBuffer(); - - edu::UpdateProgramStatusHistory( - ProgramId(2), RealTime(1), edu::ProgramStatus::programExecutionSucceeded); - edu::UpdateProgramStatusHistory( - ProgramId(4), RealTime(1), edu::ProgramStatus::programExecutionFailed); - edu::programStatusHistory.put( - edu::ProgramStatusHistoryEntry{.programId = ProgramId(5), - .startTime = RealTime(1), - .status = edu::ProgramStatus::programRunning}); - edu::UpdateProgramStatusHistory( - ProgramId(5), RealTime(1), edu::ProgramStatus::programExecutionSucceeded); - - // 1, because we did not read anything - RODOS::PRINTF("readCnt unchanged : %d\n", - static_cast(edu::programStatusHistory.readCnt == readCnt)); - // 0, because we did write - RODOS::PRINTF("writeCnt unchanged : %d\n", - static_cast(edu::programStatusHistory.readCnt == writeCnt)); - // 0 - RODOS::PRINTF("OccupiedCnt unchanged : %d\n", - static_cast(edu::programStatusHistory.occupiedCnt == occupiedCnt)); - - PrintBuffer(); - - RODOS::hwResetAndReboot(); - } -} updateRingBufferTest; -} diff --git a/Tests/UnitTests/CMakeLists.txt b/Tests/UnitTests/CMakeLists.txt index f1666690..850c71af 100644 --- a/Tests/UnitTests/CMakeLists.txt +++ b/Tests/UnitTests/CMakeLists.txt @@ -85,6 +85,13 @@ target_link_libraries( ) add_test(NAME PersistentVariables COMMAND Sts1CobcSwTests_PersistentVariables) +add_program(FramRingArray FramRingArray.test.cpp UnitTestThread.cpp) +target_link_libraries( + Sts1CobcSwTests_FramRingArray PRIVATE rodos::rodos Sts1CobcSw_Periphery Sts1CobcSw_Serial + Sts1CobcSw_Edu +) +add_test(NAME FramRingArray COMMAND Sts1CobcSwTests_FramRingArray) + # --- All unit tests --- get_property( diff --git a/Tests/UnitTests/FramRingArray.test.cpp b/Tests/UnitTests/FramRingArray.test.cpp new file mode 100644 index 00000000..babe1dba --- /dev/null +++ b/Tests/UnitTests/FramRingArray.test.cpp @@ -0,0 +1,167 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +namespace fram = sts1cobcsw::fram; +using sts1cobcsw::operator""_b; // NOLINT(misc-unused-using-decls) + + +inline constexpr auto section = + sts1cobcsw::Section{}; +inline constexpr auto charRingArray = sts1cobcsw::RingArray{}; + +static_assert(std::is_same_v); +static_assert(charRingArray.FramCapacity() == 3); +static_assert(charRingArray.CacheCapacity() == 2); +static_assert(charRingArray.section.begin == fram::Address(0)); +static_assert(charRingArray.section.end == fram::Address(28)); +static_assert(charRingArray.section.size == fram::Size(28)); + + +auto RunUnitTest() -> void +{ + using fram::ram::memory; + + fram::ram::SetAllDoFunctions(); + fram::Initialize(); + static constexpr auto ringArrayStartAddress = 3 * 2 * sizeof(std::size_t); + + + // SECTION("FRAM is working") + { + memory.fill(0x00_b); + fram::framIsWorking.Store(true); + + Require(charRingArray.Size() == 0); + + // Trying to set an element in an empty ring prints a debug message and does not set + // anything + charRingArray.Set(0, 11); + Require(std::all_of(memory.begin(), memory.end(), [](auto x) { return x == 0_b; })); + + charRingArray.PushBack(11); + Require(charRingArray.Size() == 1); + Require(charRingArray.Front() == 11); + Require(charRingArray.Back() == 11); + + charRingArray.PushBack(12); + Require(charRingArray.Size() == 2); + Require(charRingArray.Front() == 11); + Require(charRingArray.Back() == 12); + + charRingArray.PushBack(13); + Require(charRingArray.Size() == 3); + Require(charRingArray.Front() == 11); + Require(charRingArray.Back() == 13); + + Require(charRingArray.Get(0) == 11); + Require(charRingArray.Get(1) == 12); + Require(charRingArray.Get(2) == 13); + + // PushBack writes to memory + Require(fram::ram::memory[ringArrayStartAddress + 0] == 11_b); + Require(fram::ram::memory[ringArrayStartAddress + 1] == 12_b); + Require(fram::ram::memory[ringArrayStartAddress + 2] == 13_b); + + // When pushing to a full ring, the size stays the same and the oldest element is lost + charRingArray.PushBack(14); + Require(charRingArray.Size() == 3); + Require(charRingArray.Front() == 12); + Require(charRingArray.Back() == 14); + + // Only the (size + 2)th element overwrites the first one in memory because we keep a gap of + // one between begin and end indexes + charRingArray.PushBack(15); + Require(charRingArray.Size() == 3); + Require(charRingArray.Front() == 13); + Require(charRingArray.Back() == 15); + Require(fram::ram::memory[ringArrayStartAddress + 0] == 15_b); + Require(fram::ram::memory[ringArrayStartAddress + 1] == 12_b); + Require(fram::ram::memory[ringArrayStartAddress + 2] == 13_b); + Require(fram::ram::memory[ringArrayStartAddress + 3] == 14_b); + + // Set() writes to memory + charRingArray.Set(0, 21); + charRingArray.Set(1, 22); + charRingArray.Set(2, 23); + Require(charRingArray.Get(0) == 21); + Require(charRingArray.Get(1) == 22); + Require(charRingArray.Get(2) == 23); + Require(fram::ram::memory[ringArrayStartAddress + 0] == 23_b); + Require(fram::ram::memory[ringArrayStartAddress + 1] == 12_b); + Require(fram::ram::memory[ringArrayStartAddress + 2] == 21_b); + Require(fram::ram::memory[ringArrayStartAddress + 3] == 22_b); + + // Get() with out-of-bounds index prints a debug message and returns the last element + Require(charRingArray.Get(17) == 23); + // Set() with out-of-bounds index prints a debug message and does not set anything + charRingArray.Set(17, 0); + Require(charRingArray.Get(0) == 21); + Require(charRingArray.Get(1) == 22); + Require(charRingArray.Get(2) == 23); + } + + // SECTION("FRAM is not working") + { + memory.fill(0x00_b); + fram::framIsWorking.Store(false); + + Require(charRingArray.Size() == 0); + // Trying to set an element in an empty ring prints a debug message + charRingArray.Set(0, 11); + + charRingArray.PushBack(11); + Require(charRingArray.Size() == 1); + Require(charRingArray.Front() == 11); + Require(charRingArray.Back() == 11); + + charRingArray.PushBack(12); + Require(charRingArray.Size() == 2); + Require(charRingArray.Front() == 11); + Require(charRingArray.Back() == 12); + + Require(charRingArray.Get(0) == 11); + Require(charRingArray.Get(1) == 12); + + // PushBack does not write to memory + Require(fram::ram::memory[ringArrayStartAddress + 0] == 0_b); + Require(fram::ram::memory[ringArrayStartAddress + 1] == 0_b); + + // When pushing to a full ring, the size stays the same and the oldest element is lost + charRingArray.PushBack(13); + Require(charRingArray.Size() == 2); + Require(charRingArray.Front() == 12); + Require(charRingArray.Back() == 13); + + // Set() does not write to memory + charRingArray.Set(0, 21); + charRingArray.Set(1, 22); + Require(charRingArray.Get(0) == 21); + Require(charRingArray.Get(1) == 22); + Require(fram::ram::memory[ringArrayStartAddress + 0] == 0_b); + Require(fram::ram::memory[ringArrayStartAddress + 1] == 0_b); + + // Get() with out-of-bounds index prints a debug message and returns the last element + Require(charRingArray.Get(17) == 22); + // Set() with out-of-bounds index prints a debug message and does not set anything + charRingArray.Set(17, 0); + Require(charRingArray.Get(0) == 21); + Require(charRingArray.Get(1) == 22); + } + + // TODO: Add tests with custom types +} diff --git a/Tests/UnitTests/PersistentVariableInfo.test.cpp b/Tests/UnitTests/PersistentVariableInfo.test.cpp index 794cb5ed..26183217 100644 --- a/Tests/UnitTests/PersistentVariableInfo.test.cpp +++ b/Tests/UnitTests/PersistentVariableInfo.test.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -11,8 +10,6 @@ using sts1cobcsw::APersistentVariableInfo; using sts1cobcsw::isAPersistentVariableInfo; using sts1cobcsw::PersistentVariableInfo; using sts1cobcsw::SubsectionInfoLike; -using sts1cobcsw::fram::Address; -using sts1cobcsw::fram::Size; TEST_CASE("All static_asserts passed") diff --git a/Tests/UnitTests/PersistentVariables.test.cpp b/Tests/UnitTests/PersistentVariables.test.cpp index 0fe9f183..11d35597 100644 --- a/Tests/UnitTests/PersistentVariables.test.cpp +++ b/Tests/UnitTests/PersistentVariables.test.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -23,16 +24,10 @@ using sts1cobcsw::fram::Address; using sts1cobcsw::fram::Size; -constexpr auto section0 = Section(); -constexpr auto section1 = Section(); -constexpr auto section2 = Section(); - -constexpr auto pvs = PersistentVariables{}, PersistentVariableInfo<"nResets", std::uint32_t>, PersistentVariableInfo<"activeFwImage", std::uint8_t>, - PersistentVariableInfo<"somethingElse", std::int16_t>>(); + PersistentVariableInfo<"somethingElse", std::int16_t>>{}; static_assert(std::is_same_v, std::uint32_t>); static_assert(std::is_same_v, std::uint8_t>); @@ -54,8 +49,8 @@ auto RunUnitTest() -> void constexpr auto nResetsAddress0 = 0; constexpr auto activeFwImageAddress0 = 4; constexpr auto somethingElseAddress0 = 5; - constexpr auto activeFwImageAddress1 = activeFwImageAddress0 + 100; - constexpr auto activeFwImageAddress2 = activeFwImageAddress1 + 100; + constexpr auto activeFwImageAddress1 = activeFwImageAddress0 + value_of(pvs.section.size / 3); + constexpr auto activeFwImageAddress2 = activeFwImageAddress1 + value_of(pvs.section.size / 3); sts1cobcsw::fram::ram::SetAllDoFunctions(); diff --git a/iwyu.imp b/iwyu.imp index 032b215b..2b3ad285 100644 --- a/iwyu.imp +++ b/iwyu.imp @@ -47,12 +47,13 @@ # 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/FramSections/Subsections.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/Periphery/FramRingArray.ipp\"", "private", "", "public"] }, { include: ["\"Sts1CobcSw/ProgramId/ProgramId.ipp\"", "private", "", "public"] }, { include: ["\"Sts1CobcSw/Serial/Byte.ipp\"", "private", "", "public"] }, { include: ["\"Sts1CobcSw/Serial/Serial.ipp\"", "private", "", "public"] }, @@ -88,6 +89,7 @@ { include: ["\"Sts1CobcSw/Periphery/Fram.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Periphery/FramEpsSpi.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Periphery/FramMock.hpp\"", "public", "", "public"] }, + { include: ["\"Sts1CobcSw/Periphery/FramRingArray.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Periphery/Rf.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/ProgramId/ProgramId.hpp\"", "public", "", "public"] }, { include: ["\"Sts1CobcSw/Serial/Byte.hpp\"", "public", "", "public"] },