From 384e0795a26190c5597c98abcaa71d7c102a8eea Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 6 Nov 2025 12:44:52 +0100 Subject: [PATCH 001/166] feat(MdmaPacket): Implement API and base structure, lacks integration with MDMA --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 75 +++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Inc/HALAL/Models/Packets/MdmaPacket.hpp diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp new file mode 100644 index 000000000..8127e118b --- /dev/null +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -0,0 +1,75 @@ +/* + * MdmaPacket.hpp + * + * Created on: 06 nov. 2025 + * Author: Boris + */ + +#ifndef MDMA_PACKET_HPP +#define MDMA_PACKET_HPP + +#include "HALAL/Models/Packets/Packet.hpp" + + +/** + * @brief Packet that uses MDMA to transfer its data + * @note Non-atomic write operations may lead to corrupted / half-updated data if variables change during transfer + */ +template +class MdmaPacket : public StackPacket { +public: + using Base = StackPacket; + + MdmaPacket(uint16_t id, Types*... values) + : Base(id, values...) { + if constexpr (BufferLength == 0) { + // Cache variable sizes to detect changes later + // (TODO) + } + + // Create descriptor linked list in MDMA + // (TODO) + } + + /** + * Change buffer pointer to build data into + */ + void set_buffer(uint8_t* new_buffer) { + this->buffer = new_buffer; + } + + /** + * @brief Build the packet data into internal buffer + */ + uint8_t* build() override { + return this->build(this->buffer); // Calls build(uint8_t*) + } + + /** + * @brief Build the packet data into provided buffer using MDMA + */ + uint8_t* build(uint8_t* buffer) { + if constexpr (BufferLength == 0) { + // Check if any variable size has changed and update descriptors + // (TODO) + } + + // Trigger MDMA transfer + // (TODO) + + // Fallback until MDMA is implemented + return Base::build(); + } + +private: + // MDMA descriptor management + // (TODO) +}; + +#if __cpp_deduction_guides >= 201606 +template +MdmaPacket(uint16_t, Types*... values) + -> MdmaPacket<(!has_container::value)*total_sizeof::value, Types...>; +#endif + +#endif // MDMA_PACKET_HPP \ No newline at end of file From e57cee7dfe5e6edf1c5f4911d67d029000646f77 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 6 Nov 2025 15:31:57 +0100 Subject: [PATCH 002/166] feat(MdmaPacket): Drop support for containers and add mdma instance as parameter --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 34 +++++++++---------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 8127e118b..97ed8a8ae 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -10,32 +10,25 @@ #include "HALAL/Models/Packets/Packet.hpp" +class Mdma; // Placeholder, remove later /** * @brief Packet that uses MDMA to transfer its data * @note Non-atomic write operations may lead to corrupted / half-updated data if variables change during transfer + * @note Don't use with containers of variable size unless sizes are fixed */ template class MdmaPacket : public StackPacket { public: + static_assert(!has_container::value, "MdmaPacket does not support containers"); using Base = StackPacket; - - MdmaPacket(uint16_t id, Types*... values) - : Base(id, values...) { - if constexpr (BufferLength == 0) { - // Cache variable sizes to detect changes later - // (TODO) - } - // Create descriptor linked list in MDMA - // (TODO) - } + MdmaPacket(uint16_t id, Types*... values) = delete; - /** - * Change buffer pointer to build data into - */ - void set_buffer(uint8_t* new_buffer) { - this->buffer = new_buffer; + MdmaPacket(uint16_t id, Mdma* mdma, Types*... values) : Base(id, values...), mdma(mdma) { + + // Inscribe MDMA list and save id or handle or whatever of the list + // (TODO) } /** @@ -49,11 +42,6 @@ class MdmaPacket : public StackPacket { * @brief Build the packet data into provided buffer using MDMA */ uint8_t* build(uint8_t* buffer) { - if constexpr (BufferLength == 0) { - // Check if any variable size has changed and update descriptors - // (TODO) - } - // Trigger MDMA transfer // (TODO) @@ -62,14 +50,16 @@ class MdmaPacket : public StackPacket { } private: + Mdma* mdma; // MDMA descriptor management // (TODO) }; #if __cpp_deduction_guides >= 201606 template -MdmaPacket(uint16_t, Types*... values) - -> MdmaPacket<(!has_container::value)*total_sizeof::value, Types...>; +MdmaPacket(uint16_t, Mdma*, Types*... values) + -> MdmaPacket::value, Types...>; + #endif #endif // MDMA_PACKET_HPP \ No newline at end of file From 3c79d80c291f7d1e0584e678201047cc1ecf1ec7 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 6 Nov 2025 17:00:08 +0100 Subject: [PATCH 003/166] feat(MdmaPacket): Now can specify Mdma instance --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 97ed8a8ae..6c19c584e 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -25,7 +25,7 @@ class MdmaPacket : public StackPacket { MdmaPacket(uint16_t id, Types*... values) = delete; - MdmaPacket(uint16_t id, Mdma* mdma, Types*... values) : Base(id, values...), mdma(mdma) { + MdmaPacket(uint16_t id, Mdma& mdma, Types*... values) : Base(id, values...), mdma(mdma) { // Inscribe MDMA list and save id or handle or whatever of the list // (TODO) @@ -41,8 +41,8 @@ class MdmaPacket : public StackPacket { /** * @brief Build the packet data into provided buffer using MDMA */ - uint8_t* build(uint8_t* buffer) { - // Trigger MDMA transfer + uint8_t* build(uint8_t* buffer, Mdma& mdma = nullptr) { + // Trigger MDMA transfer with the provided MDMA instance or the one stored in the packet (default channel) // (TODO) // Fallback until MDMA is implemented @@ -50,7 +50,7 @@ class MdmaPacket : public StackPacket { } private: - Mdma* mdma; + Mdma& mdma; // MDMA descriptor management // (TODO) }; From 14fe90bb5643db574f562a5ecd0a47bfe9101a1e Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 6 Nov 2025 17:28:07 +0100 Subject: [PATCH 004/166] feat(MdmaPacket): MDMA manages buffers, can now change mdma instance --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 6c19c584e..5f163cd0b 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -35,19 +35,20 @@ class MdmaPacket : public StackPacket { * @brief Build the packet data into internal buffer */ uint8_t* build() override { - return this->build(this->buffer); // Calls build(uint8_t*) - } - - /** - * @brief Build the packet data into provided buffer using MDMA - */ - uint8_t* build(uint8_t* buffer, Mdma& mdma = nullptr) { - // Trigger MDMA transfer with the provided MDMA instance or the one stored in the packet (default channel) + // Trigger MDMA transfer with the stored MDMA instance, buffer is managed by the MDMA // (TODO) // Fallback until MDMA is implemented return Base::build(); } + + /** + * @brief Change the MDMA instance used for transfers + */ + void set_mdma(Mdma& new_mdma) { + mdma = new_mdma; + } + private: Mdma& mdma; From 0f96017158e7e79e8364ef9ef4850509b57add02 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 6 Nov 2025 19:36:00 +0100 Subject: [PATCH 005/166] Fist draft of the MDMA --- .vscode/settings.json | 92 +++++++++++++++++++++++++++- Inc/HALAL/HALAL.hpp | 1 + Inc/HALAL/Models/MDMA/MDMA.hpp | 52 ++++++++++++++++ Src/HALAL/Models/MDMA/MDMA.cpp | 107 +++++++++++++++++++++++++++++++++ 4 files changed, 251 insertions(+), 1 deletion(-) create mode 100644 Inc/HALAL/Models/MDMA/MDMA.hpp create mode 100644 Src/HALAL/Models/MDMA/MDMA.cpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 948f9e647..411724e55 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,96 @@ "C_Cpp.formatting": "clangFormat", "C_Cpp.clang_format_style": "{BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 80}", "C_Cpp.clang_format_sortIncludes": true, - "C_Cpp.intelliSenseCacheSize": 0 + "C_Cpp.intelliSenseCacheSize": 0, + "files.associations": { + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "barrier": "cpp", + "bit": "cpp", + "bitset": "cpp", + "cctype": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cuchar": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "expected": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "source_location": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "latch": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "ranges": "cpp", + "scoped_allocator": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "spanstream": "cpp", + "sstream": "cpp", + "stacktrace": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "syncstream": "cpp", + "thread": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp" + } } \ No newline at end of file diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index dc216434c..399cc5374 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -39,6 +39,7 @@ #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" #include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" #else #include "HALALMock/Services/DigitalOutputService/DigitalOutputService.hpp" #include "HALALMock/Services/DigitalInputService/DigitalInputService.hpp" diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp new file mode 100644 index 000000000..07814b819 --- /dev/null +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -0,0 +1,52 @@ +#pragma once + +#include "C++Utilities/CppUtils.hpp" +#include "stm32h7xx_hal.h" + +class MDMA{ + + private: + static std::unordered_map,uint8_t> linked_lists; + static std::unordered_map instances; + inline static number_of_packets{0}; + + class Instance{ + public: + MDMA_HandleTypeDef handle; + uint8_t id; + uint8_t *data_buffer; + Instance() = default; + Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer): handle(handle), id(id), data_buffer(data_buffer) {} + + + }; + + const static uint32_t get_flag(const uint8_t size); + + static void start(); + + + public: + + /** + * @brief A method to add MDMA channels in linked list mode. + + * This method has to be invoked before the ST-LIB::start() + * + * @param data_buffer the buffer where the MDMA will write the data, very important to be a non-cached buffer + * + * @return the id that represents the MDMA channel with its designated buffer inside this utility class, used in all its functions. + */ + + static uint8_t inscribe(uint8_t* data_buffer); + + template + static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values);//Utilizar std::apply y std::dcltype + + static uint8_t merge_packets(const uint8_t packet_id1, const uint8_t packet_id2); + + + static void transfer_data(const uint8_t MDMA_id, const uint8_t packet_id); + + +}; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp new file mode 100644 index 000000000..ae8343032 --- /dev/null +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -0,0 +1,107 @@ +#include "HALAL/Models/MDMA/MDMA.hpp" + +std::unordered_map,uint8_t> MDMA::linked_lists = {}; +std::unordered_map MDMA::instances = {}; + +uint8_t MDMA::inscribe(uint8_t* data_buffer) +{ + MDMA_HandleTypeDef mdma_handle = {}; + mdma_handle.Instance = MDMA_Channel0; + mdma_handle.Init.Request = MDMA_REQUEST_SW; + mdma_handle.Init.TriggerMode = MDMA_TRIGGERMODE_HW_LINK; + mdma_handle.Init.Priority = MDMA_PRIORITY_HIGH; + mdma_handle.Init.Endianness = MDMA_ENDIANNESS_LITTLE; + mdma_handle.Init.DataMux = MDMA_DATA_MUX_ENABLED; + mdma_handle.Init.DataAlign = MDMA_DATA_ALIGN_PACKENABLE; + mdma_handle.Init.BufferTransferLength = 248; + mdma_handle.Init.TransferEndMode = MDMA_BUFFER_TRANSFER; + mdma_handle.Init.SourceDataSize = MDMA_DATA_SIZE_BYTE; + mdma_handle.Init.DestDataSize = MDMA_DATA_SIZE_BYTE; + mdma_handle.Init.SourceInc = MDMA_SOURCE_INC_BYTE; + mdma_handle.Init.DestinationInc = MDMA_DEST_INC_BYTE; + + uint8_t id = instances.size(); + Instance instance(mdma_handle, id, data_buffer); + instances[instance] = id; + + return id; +} + +void MDMA::start() +{ + __HAL_RCC_MDMA_CLK_ENABLE(); + + //demas +} + +template +uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& values) +{ + Instance& intance = instances[MDMA_id]; + uint8_t i{0} + std::apply([&](auto... ptrs) + { + auto create_node = [&](auto ptr) + { + if (ptr == nullptr) { + ErrorHandler("Nullptr given to MDMA") + } + + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + constexpr size_t type_size = sizeof(UnderlyingType); + + static_assert(tipo_size == 1 || tipo_size == 2 || tipo_size == 4, + "MDMA::add_packet: Passed a variable with type size > 4 "); + + MDMA_LinkNodeTypeDef node = {}; + node.CTCR = get_ctcr_flags(tipo_size); + node.CBNDTR = 1; + node.CSAR = (uint32_t)ptr; + node.CDAR = instance.data_buffer; + if(i==0) + { + node.CLAR = 0; + } + node.CLAR = (uint32_t)&(m_nodes[i-1]); + i++; + m_nodes.push_back(node); + }; + + create_node(ptrs); + + }, values); + + if (m_nodes.empty()) + { + ErrorHandler("Error creating linked list in MDMA") + } + + m_nodes.back().CLAR = 0; + m_nodes[0].CLAR = (uint32_t)&(m_nodes[1]); + + + return number_of_packets++; +} + +const uint32_t MDMA::get_flag(const uint8_t size) +{ + uint32_t flags = MDMA_TRANSFER_TRIGGER_MODE_LINK | + MDMA_SOURCE_INC_DISABLE; + + switch (size) { + case 1: + flags |= (MDMA_DATA_SIZE_BYTE | MDMA_DEST_INC_BYTE); + break; + case 2: + flags |= (MDMA_DATA_SIZE_HALFWORD | MDMA_DEST_INC_HALFWORD); + break; + case 4: + flags |= (MDMA_DATA_SIZE_WORD | MDMA_DEST_INC_WORD); + break; + default: + flags |= (MDMA_DATA_SIZE_BYTE | MDMA_DEST_INC_BYTE); + break; + } + return flags; +} \ No newline at end of file From 714eeaa8cb391aebeab75b54893b4aa3e8cd316c Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 6 Nov 2025 19:37:57 +0100 Subject: [PATCH 006/166] Some fixes, there are some problems with linking i think --- Inc/HALAL/Models/MDMA/MDMA.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 07814b819..4815bad9b 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -6,10 +6,6 @@ class MDMA{ private: - static std::unordered_map,uint8_t> linked_lists; - static std::unordered_map instances; - inline static number_of_packets{0}; - class Instance{ public: MDMA_HandleTypeDef handle; @@ -20,6 +16,9 @@ class MDMA{ }; + static std::unordered_map,uint8_t> linked_lists; + static std::unordered_map instances; + inline static uint8_t number_of_packets{0}; const static uint32_t get_flag(const uint8_t size); From 31ac058a9546e25483e96f5484414c1bc0bbbfec Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 7 Nov 2025 00:59:36 +0100 Subject: [PATCH 007/166] Second draft of the mdma, now creates linked list (I think?) --- Inc/HALAL/Models/MDMA/MDMA.hpp | 6 +-- Src/HALAL/Models/MDMA/MDMA.cpp | 84 +++++++++++++++++++++------------- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 4815bad9b..94e83f321 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -6,7 +6,7 @@ class MDMA{ private: - class Instance{ + struct Instance{ public: MDMA_HandleTypeDef handle; uint8_t id; @@ -20,7 +20,7 @@ class MDMA{ static std::unordered_map instances; inline static uint8_t number_of_packets{0}; - const static uint32_t get_flag(const uint8_t size); + const static uint32_t get_size(const uint8_t size); static void start(); @@ -45,7 +45,7 @@ class MDMA{ static uint8_t merge_packets(const uint8_t packet_id1, const uint8_t packet_id2); - static void transfer_data(const uint8_t MDMA_id, const uint8_t packet_id); + static void transfer_data(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index ae8343032..2e4535cbf 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -8,17 +8,6 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer) MDMA_HandleTypeDef mdma_handle = {}; mdma_handle.Instance = MDMA_Channel0; mdma_handle.Init.Request = MDMA_REQUEST_SW; - mdma_handle.Init.TriggerMode = MDMA_TRIGGERMODE_HW_LINK; - mdma_handle.Init.Priority = MDMA_PRIORITY_HIGH; - mdma_handle.Init.Endianness = MDMA_ENDIANNESS_LITTLE; - mdma_handle.Init.DataMux = MDMA_DATA_MUX_ENABLED; - mdma_handle.Init.DataAlign = MDMA_DATA_ALIGN_PACKENABLE; - mdma_handle.Init.BufferTransferLength = 248; - mdma_handle.Init.TransferEndMode = MDMA_BUFFER_TRANSFER; - mdma_handle.Init.SourceDataSize = MDMA_DATA_SIZE_BYTE; - mdma_handle.Init.DestDataSize = MDMA_DATA_SIZE_BYTE; - mdma_handle.Init.SourceInc = MDMA_SOURCE_INC_BYTE; - mdma_handle.Init.DestinationInc = MDMA_DEST_INC_BYTE; uint8_t id = instances.size(); Instance instance(mdma_handle, id, data_buffer); @@ -37,8 +26,11 @@ void MDMA::start() template uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& values) { - Instance& intance = instances[MDMA_id]; - uint8_t i{0} + Instance& instance = instances[MDMA_id]; + uint32_t offset{0}; + uint8_t i = 0; + std::vector m_nodes; + MDMA_LinkNodeConfTypeDef nodeConfig; std::apply([&](auto... ptrs) { auto create_node = [&](auto ptr) @@ -51,24 +43,35 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va using UnderlyingType = std::remove_pointer_t; constexpr size_t type_size = sizeof(UnderlyingType); - static_assert(tipo_size == 1 || tipo_size == 2 || tipo_size == 4, + static_assert(type_size == 1 || type_size == 2 || type_size == 4, "MDMA::add_packet: Passed a variable with type size > 4 "); MDMA_LinkNodeTypeDef node = {}; - node.CTCR = get_ctcr_flags(tipo_size); - node.CBNDTR = 1; - node.CSAR = (uint32_t)ptr; - node.CDAR = instance.data_buffer; - if(i==0) + nodeConfig.SourceAddress = (uint32_t)ptr; + nodeConfig.DestinationAddress = (uint32_t)instance.data_buffer + offset; + nodeConfig.BlockDataLength = type_size; + nodeConfig.SourceInc = MDMA_SRC_INC_DISABLE; + nodeConfig.DestinationInc = MDMA_DEST_INC_DISABLE; + nodeConfig.SourceDataSize = get_size(type_size); + nodeConfig.DestinationDataSize = get_size(type_size); + nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + + HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) { - node.CLAR = 0; + ErrorHandler("Error creating linked list in MDMA"); } - node.CLAR = (uint32_t)&(m_nodes[i-1]); - i++; + offset += type_size; m_nodes.push_back(node); }; create_node(ptrs); + if(i !=0){ + HAL_MDMA_LinkedList_AddNode(&instance.handle, &m_nodes[i-1], &m_nodes[i]); + } + i++; + } }, values); @@ -77,30 +80,45 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va ErrorHandler("Error creating linked list in MDMA") } - m_nodes.back().CLAR = 0; - m_nodes[0].CLAR = (uint32_t)&(m_nodes[1]); - + MDMA_LinkNodeTypeDef node = {}; + nodeConfig.SourceAddress = (uint32_t)instance.data_buffer; + nodeConfig.DestinationAddress = (uint32_t)instance.data_buffer + offset; + nodeConfig.BlockDataLength = offset; + nodeConfig.SourceInc = MDMA_SRC_INC_ENABLE; + nodeConfig.DestinationInc = MDMA_DEST_INC_ENABLE; + nodeConfig.SourceDataSize = MDMA_DATA_SIZE_BYTE; + nodeConfig.DestinationDataSize = MDMA_DATA_SIZE_BYTE; + nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + + HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } + m_nodes.push_back(node); + HAL_MDMA_LinkedList_AddNode(&instance.handle, &m_nodes[m_nodes.size()-2], &m_nodes[m_nodes.size()-1]); - return number_of_packets++; + linked_lists[number_of_packets++] = m_nodes; + return number_of_packets; } -const uint32_t MDMA::get_flag(const uint8_t size) +const uint32_t MDMA::get_size(const uint8_t size) { - uint32_t flags = MDMA_TRANSFER_TRIGGER_MODE_LINK | - MDMA_SOURCE_INC_DISABLE; + uint32_t flags = 0; switch (size) { case 1: - flags |= (MDMA_DATA_SIZE_BYTE | MDMA_DEST_INC_BYTE); + flags = MDMA_DATA_SIZE_BYTE; break; case 2: - flags |= (MDMA_DATA_SIZE_HALFWORD | MDMA_DEST_INC_HALFWORD); + flags = MDMA_DATA_SIZE_HALFWORD; break; case 4: - flags |= (MDMA_DATA_SIZE_WORD | MDMA_DEST_INC_WORD); + flags = MDMA_DATA_SIZE_WORD; break; default: - flags |= (MDMA_DATA_SIZE_BYTE | MDMA_DEST_INC_BYTE); + flags = MDMA_DATA_SIZE_BYTE; break; } return flags; From 5adf7d4b642834daff4bea6659101cd8c55dba20 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 7 Nov 2025 09:57:36 +0100 Subject: [PATCH 008/166] Fixed some stuff :) --- Inc/HALAL/Models/MDMA/MDMA.hpp | 14 ++++++++---- Src/HALAL/Models/MDMA/MDMA.cpp | 42 ++++++++++++---------------------- 2 files changed, 24 insertions(+), 32 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 94e83f321..85a7f604c 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -3,6 +3,10 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" +#ifdef MDMA +#undef MDMA +#endif + class MDMA{ private: @@ -16,11 +20,11 @@ class MDMA{ }; - static std::unordered_map,uint8_t> linked_lists; - static std::unordered_map instances; + inline static std::unordered_map> linked_lists{}; + inline static std::unordered_map instances{}; inline static uint8_t number_of_packets{0}; - - const static uint32_t get_size(const uint8_t size); + static std::unordered_map MDMA::dst_size_to_flags ; + static std::unordered_map MDMA::src_size_to_flags ; static void start(); @@ -40,7 +44,7 @@ class MDMA{ static uint8_t inscribe(uint8_t* data_buffer); template - static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values);//Utilizar std::apply y std::dcltype + static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values); static uint8_t merge_packets(const uint8_t packet_id1, const uint8_t packet_id2); diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 2e4535cbf..90e95925a 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -2,6 +2,16 @@ std::unordered_map,uint8_t> MDMA::linked_lists = {}; std::unordered_map MDMA::instances = {}; +std::unordered_map MDMA::src_size_to_flags = { + {1, MDMA_SRC_DATASIZE_BYTE}, + {2, MDMA_SRC_DATASIZE_HALFWORD}, + {4, MDMA_SRC_DATASIZE_WORD} +}; +std::unordered_map MDMA::dst_size_to_flags = { + {1, MDMA_DEST_DATASIZE_BYTE}, + {2, MDMA_DEST_DATASIZE_HALFWORD}, + {4, MDMA_DEST_DATASIZE_WORD} +}; uint8_t MDMA::inscribe(uint8_t* data_buffer) { @@ -11,7 +21,7 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer) uint8_t id = instances.size(); Instance instance(mdma_handle, id, data_buffer); - instances[instance] = id; + instances[id] = instance; return id; } @@ -52,8 +62,8 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va nodeConfig.BlockDataLength = type_size; nodeConfig.SourceInc = MDMA_SRC_INC_DISABLE; nodeConfig.DestinationInc = MDMA_DEST_INC_DISABLE; - nodeConfig.SourceDataSize = get_size(type_size); - nodeConfig.DestinationDataSize = get_size(type_size); + nodeConfig.SourceDataSize = src_size_to_flags[type_size]; + nodeConfig.DestinationDataSize = dst_size_to_flags[type_size]; nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; @@ -71,13 +81,12 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va HAL_MDMA_LinkedList_AddNode(&instance.handle, &m_nodes[i-1], &m_nodes[i]); } i++; - } - + }, values); if (m_nodes.empty()) { - ErrorHandler("Error creating linked list in MDMA") + ErrorHandler("Error creating linked list in MDMA"); } MDMA_LinkNodeTypeDef node = {}; @@ -102,24 +111,3 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va linked_lists[number_of_packets++] = m_nodes; return number_of_packets; } - -const uint32_t MDMA::get_size(const uint8_t size) -{ - uint32_t flags = 0; - - switch (size) { - case 1: - flags = MDMA_DATA_SIZE_BYTE; - break; - case 2: - flags = MDMA_DATA_SIZE_HALFWORD; - break; - case 4: - flags = MDMA_DATA_SIZE_WORD; - break; - default: - flags = MDMA_DATA_SIZE_BYTE; - break; - } - return flags; -} \ No newline at end of file From b1cd5626a79cbf6ac04ac36dd310c13a0e38e73a Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sat, 8 Nov 2025 00:16:11 +0100 Subject: [PATCH 009/166] More changes, i think the api is finally done (hope so) --- Inc/HALAL/Models/MDMA/MDMA.hpp | 11 +++++++---- Src/HALAL/Models/MDMA/MDMA.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 85a7f604c..d75772bd1 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -2,6 +2,7 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" +#include "main.h" #ifdef MDMA #undef MDMA @@ -23,8 +24,9 @@ class MDMA{ inline static std::unordered_map> linked_lists{}; inline static std::unordered_map instances{}; inline static uint8_t number_of_packets{0}; - static std::unordered_map MDMA::dst_size_to_flags ; - static std::unordered_map MDMA::src_size_to_flags ; + static std::unordered_map dst_size_to_flags; + static std::unordered_map src_size_to_flags; + inline static MDMA_LinkNodeTypeDef transfer_node = {}; static void start(); @@ -36,7 +38,7 @@ class MDMA{ * This method has to be invoked before the ST-LIB::start() * - * @param data_buffer the buffer where the MDMA will write the data, very important to be a non-cached buffer + * @param data_buffer the buffer where the MDMA will write the data, must be a non-cached buffer * * @return the id that represents the MDMA channel with its designated buffer inside this utility class, used in all its functions. */ @@ -48,8 +50,9 @@ class MDMA{ static uint8_t merge_packets(const uint8_t packet_id1, const uint8_t packet_id2); + static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length); - static void transfer_data(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address); + static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 90e95925a..c7a509ef8 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -23,6 +23,25 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer) Instance instance(mdma_handle, id, data_buffer); instances[id] = instance; + MDMA_LinkNodeConfTypeDef nodeConfig; + + //Both source and destination, as well as block data length will change with the use of the transfer method, this is just a dummy initialisation + nodeConfig.SourceAddress = (uint32_t)data_buffer; + nodeConfig.DestinationAddress = (uint32_t)data_buffer; + nodeConfig.BlockDataLength = 1; + nodeConfig.SourceInc = MDMA_SRC_INC_ENABLE; + nodeConfig.DestinationInc = MDMA_DEST_INC_ENABLE; + nodeConfig.SourceDataSize = MDMA_DATA_SIZE_BYTE; + nodeConfig.DestinationDataSize = MDMA_DATA_SIZE_BYTE; + nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + + HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } + return id; } @@ -111,3 +130,18 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va linked_lists[number_of_packets++] = m_nodes; return number_of_packets; } + +void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length) +{ + Instance& instance = instances[MDMA_id]; + + transfer_node.CSAR = (uint32_t)source_address; + transfer_node.CDAR = (uint32_t)destination_address; + transfer_node.CBNDTR = data_length; + + while(//Chequear que no esté funcionando); + + HAL_MDMA_LinkedList_AddNode(&instance.handle, nullptr, &transfer_node); + + //Algo.... +} \ No newline at end of file From 06fa58c0c6efb6bb8697d8d5b6183205b3de9e76 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 10 Nov 2025 19:40:59 +0100 Subject: [PATCH 010/166] More changes, this fucking sucks --- Inc/HALAL/Models/MDMA/MDMA.hpp | 18 +++--- Src/HALAL/Models/MDMA/MDMA.cpp | 113 +++++++++++++++++++++------------ 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index d75772bd1..f7a36d90e 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -2,7 +2,6 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" -#include "main.h" #ifdef MDMA #undef MDMA @@ -16,8 +15,9 @@ class MDMA{ MDMA_HandleTypeDef handle; uint8_t id; uint8_t *data_buffer; + uint8_t* destination_address; Instance() = default; - Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer): handle(handle), id(id), data_buffer(data_buffer) {} + Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address=nullptr): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address) {} }; @@ -26,7 +26,7 @@ class MDMA{ inline static uint8_t number_of_packets{0}; static std::unordered_map dst_size_to_flags; static std::unordered_map src_size_to_flags; - inline static MDMA_LinkNodeTypeDef transfer_node = {}; + inline static MDMA_LinkNodeTypeDef transfer_node{}; static void start(); @@ -38,21 +38,23 @@ class MDMA{ * This method has to be invoked before the ST-LIB::start() * - * @param data_buffer the buffer where the MDMA will write the data, must be a non-cached buffer + * @param data_buffer the buffer where the MDMA will write the data, very important to be a non-cached buffer + * @param destination_address the address where the MDMA will read the data from, if nullptr it will make it so that the destination varies dinamically * * @return the id that represents the MDMA channel with its designated buffer inside this utility class, used in all its functions. */ - static uint8_t inscribe(uint8_t* data_buffer); + static uint8_t inscribe(uint8_t* data_buffer, uint8_t* destination_address=nullptr); template static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values); - - static uint8_t merge_packets(const uint8_t packet_id1, const uint8_t packet_id2); + + template + static uint8_t merge_packets(const uint8_t base_packet_id, const PacketIds... packets_id); static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length); - static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address); + static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address=nullptr); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index c7a509ef8..475662c2e 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -1,7 +1,5 @@ #include "HALAL/Models/MDMA/MDMA.hpp" -std::unordered_map,uint8_t> MDMA::linked_lists = {}; -std::unordered_map MDMA::instances = {}; std::unordered_map MDMA::src_size_to_flags = { {1, MDMA_SRC_DATASIZE_BYTE}, {2, MDMA_SRC_DATASIZE_HALFWORD}, @@ -13,7 +11,7 @@ std::unordered_map MDMA::dst_size_to_flags = { {4, MDMA_DEST_DATASIZE_WORD} }; -uint8_t MDMA::inscribe(uint8_t* data_buffer) +uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) { MDMA_HandleTypeDef mdma_handle = {}; mdma_handle.Instance = MDMA_Channel0; @@ -26,20 +24,29 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer) MDMA_LinkNodeConfTypeDef nodeConfig; //Both source and destination, as well as block data length will change with the use of the transfer method, this is just a dummy initialisation - nodeConfig.SourceAddress = (uint32_t)data_buffer; - nodeConfig.DestinationAddress = (uint32_t)data_buffer; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + nodeConfig.Init.SourceDataSize = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; nodeConfig.BlockDataLength = 1; - nodeConfig.SourceInc = MDMA_SRC_INC_ENABLE; - nodeConfig.DestinationInc = MDMA_DEST_INC_ENABLE; - nodeConfig.SourceDataSize = MDMA_DATA_SIZE_BYTE; - nodeConfig.DestinationDataSize = MDMA_DATA_SIZE_BYTE; - nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; + nodeConfig.DstAddress = (uint32_t) instance.data_buffer; HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); if (status != HAL_OK) { - ErrorHandler("Error creating linked list in MDMA"); + //ErrorHandler("Error creating linked list in MDMA"); } return id; @@ -58,14 +65,27 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va Instance& instance = instances[MDMA_id]; uint32_t offset{0}; uint8_t i = 0; - std::vector m_nodes; + std::vector nodes; MDMA_LinkNodeConfTypeDef nodeConfig; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_DISABLE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_DISABLE; std::apply([&](auto... ptrs) { auto create_node = [&](auto ptr) { if (ptr == nullptr) { - ErrorHandler("Nullptr given to MDMA") + //ErrorHandler("Nullptr given to MDMA") } using PointerType = std::decay_t; @@ -76,61 +96,72 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va "MDMA::add_packet: Passed a variable with type size > 4 "); MDMA_LinkNodeTypeDef node = {}; - nodeConfig.SourceAddress = (uint32_t)ptr; - nodeConfig.DestinationAddress = (uint32_t)instance.data_buffer + offset; + nodeConfig.SrcAddress = (uint32_t)ptr; + nodeConfig.DstAddress = (uint32_t)instance.data_buffer + offset; nodeConfig.BlockDataLength = type_size; - nodeConfig.SourceInc = MDMA_SRC_INC_DISABLE; - nodeConfig.DestinationInc = MDMA_DEST_INC_DISABLE; - nodeConfig.SourceDataSize = src_size_to_flags[type_size]; - nodeConfig.DestinationDataSize = dst_size_to_flags[type_size]; - nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + nodeConfig.Init.SourceDataSize = src_size_to_flags[type_size]; + nodeConfig.Init.DestDataSize = dst_size_to_flags[type_size]; + HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); if (status != HAL_OK) { - ErrorHandler("Error creating linked list in MDMA"); + //ErrorHandler("Error creating linked list in MDMA"); } offset += type_size; - m_nodes.push_back(node); + nodes.push_back(node); }; - create_node(ptrs); + create_node(ptrs...); if(i !=0){ - HAL_MDMA_LinkedList_AddNode(&instance.handle, &m_nodes[i-1], &m_nodes[i]); + HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[i-1], &nodes[i]); } i++; }, values); - if (m_nodes.empty()) + if (nodes.empty()) { - ErrorHandler("Error creating linked list in MDMA"); + //ErrorHandler("Error creating linked list in MDMA"); } + if(instance.destination_address == nullptr) + { MDMA_LinkNodeTypeDef node = {}; - nodeConfig.SourceAddress = (uint32_t)instance.data_buffer; - nodeConfig.DestinationAddress = (uint32_t)instance.data_buffer + offset; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + nodeConfig.Init.SourceDataSize = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; nodeConfig.BlockDataLength = offset; - nodeConfig.SourceInc = MDMA_SRC_INC_ENABLE; - nodeConfig.DestinationInc = MDMA_DEST_INC_ENABLE; - nodeConfig.SourceDataSize = MDMA_DATA_SIZE_BYTE; - nodeConfig.DestinationDataSize = MDMA_DATA_SIZE_BYTE; - nodeConfig.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.DestinationBurst = MDMA_DESTINATION_BURST_SINGLE; + nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; + nodeConfig.DstAddress = (uint32_t) instance.data_buffer + offset; HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); if (status != HAL_OK) { - ErrorHandler("Error creating linked list in MDMA"); + //ErrorHandler("Error creating linked list in MDMA"); + } + nodes.push_back(node); + HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[nodes.size()-2], &nodes[nodes.size()-1]); } - m_nodes.push_back(node); - HAL_MDMA_LinkedList_AddNode(&instance.handle, &m_nodes[m_nodes.size()-2], &m_nodes[m_nodes.size()-1]); - linked_lists[number_of_packets++] = m_nodes; + linked_lists[number_of_packets++] = nodes; return number_of_packets; } +void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address) +{ + Instance& instance = instances[MDMA_id]; + if(destination_address != nullptr) + { + std::vector& nodes = linked_lists[packet_id]; + auto transfer_node =nodes.back(); + transfer_node.CDAR = (uint32_t)destination_address; + } + //More to do... +} + +//Habria que ver si renta esta funcion: void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length) { Instance& instance = instances[MDMA_id]; @@ -139,7 +170,7 @@ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* transfer_node.CDAR = (uint32_t)destination_address; transfer_node.CBNDTR = data_length; - while(//Chequear que no esté funcionando); + while(false)//Chequear que no esté funcionando); HAL_MDMA_LinkedList_AddNode(&instance.handle, nullptr, &transfer_node); From 6d527ee03a901471ce03d853c1f8de0308be581f Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 14:05:57 +0100 Subject: [PATCH 011/166] feat(Promises): Add Arena memory pool implementation --- Inc/C++Utilities/Arena.hpp | 114 +++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 Inc/C++Utilities/Arena.hpp diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp new file mode 100644 index 000000000..b0a3cfc10 --- /dev/null +++ b/Inc/C++Utilities/Arena.hpp @@ -0,0 +1,114 @@ +/* + * Arena.hpp + * + * Created on: 15 nov. 2025 + * Author: Boris + */ + +#ifndef ARENA_HPP +#define ARENA_HPP + +#include "CppImports.hpp" +#include "RingBuffer.hpp" + +/** + * @brief A simple memory arena for fixed-size allocations. + * @note It works like a heap but with a fixed maximum number of elements, and all the items are of the same type. + * @tparam S The maximum number of elements in the arena. + * @tparam T The type of elements stored in the arena. + */ +template +class Arena { + public: + + /** + * @brief Acquire an element from the arena. + * @return Pointer to the acquired element, or nullptr if the arena is full. + */ + T* acquire() { + if (freeIndexes.empty()) { + return nullptr; + } + size_t index = freeIndexes.front(); + freeIndexes.pop(); + usedIndexesSet[index] = true; + return &elements[index]; + } + + /** + * @brief Acquire and construct an element in-place in the arena. + * @param args The constructor arguments. + * @return Pointer to the constructed element, or nullptr if the arena is full. + */ + template + T* construct(Args&&... args) { + T* ele = acquire(); + if (ele) { + new (ele) T(std::forward(args)...); // Placement new + } + return ele; + } + + /** + * @brief Release an element back to the arena. + * @param ele Pointer to the element to release. + * @return True if the element was successfully released, false otherwise. + */ + bool release(T* ele) { + if (ele < &elements[0] || ele >= &elements[S] || !usedIndexesSet[ele - &elements[0]]) { + return false; + } + size_t index = ele - &elements[0]; + freeIndexes.push(index); + usedIndexesSet[index] = false; + return true; + } + + /** + * @brief Destroy an element and release it back to the arena. + * @param ele Pointer to the element to destroy. + * @return True if the element was successfully destroyed and released, false otherwise. + */ + bool destroy(T* ele) { + if (release(ele)) { + ele->~T(); + return true; + } + return false; + } + + size_t capacity() const { return S; } + size_t available() const { return freeIndexes.size(); } + size_t used() const { return S - freeIndexes.size(); } + + Arena() { + std::iota(freeIndexes.begin(), freeIndexes.end(), 0); // Initialize free indexes, {0, 1, 2, ..., S-1} + } + Arena(const Arena&) = delete; + Arena& operator=(const Arena&) = delete; + Arena(Arena&& other) noexcept : freeIndexes(std::move(other.freeIndexes)) { + std::copy(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), std::begin(usedIndexesSet)); + std::move(std::begin(other.elements), std::end(other.elements), std::begin(elements)); + + std::fill(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), false); + std::iota(other.freeIndexes.begin(), other.freeIndexes.end(), 0); + } + Arena& operator=(Arena&& other) noexcept { + if (this != &other) { + freeIndexes = std::move(other.freeIndexes); + std::copy(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), std::begin(usedIndexesSet)); + std::move(std::begin(other.elements), std::end(other.elements), std::begin(elements)); + + std::fill(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), false); + std::iota(other.freeIndexes.begin(), other.freeIndexes.end(), 0); + } + return *this; + } + + private: + T elements[S]; + RingBuffer freeIndexes; + bool usedIndexesSet[S]{false}; +}; + +#endif // ARENA_HPP \ No newline at end of file From 4d94ea3d42647918f89964ff7ad121b2d1f5a83e Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 14:13:29 +0100 Subject: [PATCH 012/166] fix(Primises): Remove move semantics from Arena class, since they can be problematic with resource management. --- Inc/C++Utilities/Arena.hpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp index b0a3cfc10..e38a2a424 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Arena.hpp @@ -86,24 +86,8 @@ class Arena { } Arena(const Arena&) = delete; Arena& operator=(const Arena&) = delete; - Arena(Arena&& other) noexcept : freeIndexes(std::move(other.freeIndexes)) { - std::copy(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), std::begin(usedIndexesSet)); - std::move(std::begin(other.elements), std::end(other.elements), std::begin(elements)); - - std::fill(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), false); - std::iota(other.freeIndexes.begin(), other.freeIndexes.end(), 0); - } - Arena& operator=(Arena&& other) noexcept { - if (this != &other) { - freeIndexes = std::move(other.freeIndexes); - std::copy(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), std::begin(usedIndexesSet)); - std::move(std::begin(other.elements), std::end(other.elements), std::begin(elements)); - - std::fill(std::begin(other.usedIndexesSet), std::end(other.usedIndexesSet), false); - std::iota(other.freeIndexes.begin(), other.freeIndexes.end(), 0); - } - return *this; - } + Arena(Arena&& other) noexcept = delete; + Arena& operator=(Arena&& other) = delete; private: T elements[S]; From f6767570eb5b095673fc782ad90bac425e4ef3b7 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 15:26:38 +0100 Subject: [PATCH 013/166] feat(Promises): Implement Promises with fixed-size arenas --- Inc/HALAL/Utils/Promise.hpp | 218 ++++++++++++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 Inc/HALAL/Utils/Promise.hpp diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp new file mode 100644 index 000000000..f08f9930b --- /dev/null +++ b/Inc/HALAL/Utils/Promise.hpp @@ -0,0 +1,218 @@ +/* + * Promise.hpp + * + * Created on: 15 nov. 2025 + * Author: Boris + */ + +#ifndef PROMISE_HPP +#define PROMISE_HPP + +#include "C++Utilities/Arena.hpp" +#include "C++Utilities/RingBuffer.hpp" + +/** + * @brief A simple Promise implementation for asynchronous programming. + * @note Promises are allocated from a fixed-size memory arena, so you don't own the memory. Use Promise::release() to release them back to the arena if needed. + * @tparam S The maximum number of Promises that can be allocated simultaneously. Use the default "PromiseDefault" alias for a standard size. + */ +template +class Promise { + using Callback = void(*)(void*); + using ChainedCallback = Promise*(*)(void*); + + public: + + /** + * @brief Create a new Promise. + * @return Pointer to the newly created Promise, or nullptr if allocation failed. + * @note The returned Promise must be released using Promise::release(). + * @note The Promise lives in a memory arena with a fixed maximum number of Promises (S), so you don't own the memory. + */ + static Promise* inscribe() { + Promise* p = Promise::arena.acquire(); + if (!p) { + return nullptr; + } + p->isResolved = false; + p->callback = nullptr; + p->context = nullptr; + return p; + } + + /** + * @brief Release a Promise back to the arena. + * @param p Pointer to the Promise to release. + * @return True if the Promise was successfully released, false otherwise. + * @note After calling this function, the Promise pointer is no longer valid and must not be used. + */ + static bool release(Promise* p) { + return Promise::arena.release(p); + } + + /** + * @brief Register a callback to be called when the Promise is resolved. + * @param cb The callback function. + * @param ctx The context to be passed to the callback, can only be a pointer, you must manage the memory yourself. You could use an Arena for that. + * @note If the Promise is already resolved, the callback is scheduled to be called in the next update cycle. You can call then whenever you want, but only one callback can be registered per Promise. + */ + void then(Callback cb, void* ctx = nullptr) { + callback = cb; + context = ctx; + if (isResolved) { + readyList.push(this); + } + } + /** + * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. + * @param cb The chained callback function that returns a new Promise. + * @param ctx The context to be passed to the chained callback, can only be a pointer, you must manage the memory yourself. + * @return Pointer to the newly created chained Promise. + * @note If the Promise is already resolved, the chained callback is scheduled to be called in the next update cycle. You can call then whenever you want, but only one chained callback can be registered per Promise. + * @example + * ```cpp + * Promise* p1 = Promise::inscribe(); + * p1->then([](void* ctx) { + * std::cout << "Promise 1 resolved!" << std::endl; + * auto p2 = Promise::inscribe(); // Return a new Promise + * // Simulate some async work + * })->then([](void* ctx) { + * std::cout << "Chained Promise resolved!" << std::endl; + * }); + * p1->resolve(); // This will trigger the first callback, and when the second Promise resolves, the chained callback will be called. + * ``` + */ + Promise* then(ChainedCallback cb, void* ctx = nullptr) { + next = Promise::inscribe(); + if (!next) { + return nullptr; + } + chainedCallback = cb; + chainedContext = ctx; + context = this; + callback = [](void* thisPtr) { + Promise* p = static_cast(thisPtr); + Promise* chained = p->chainedCallback(p->chainedContext); + chained->then(p->next->callback, p->next->context); + release(p->next); + }; + if (isResolved) { + readyList.push(this); + } + return next; + } + + /** + * @brief Resolve the Promise, triggering the registered callback. Works in interruptions. + * @note Calling this after the Promise has been handled can be dangerous, as the Promise may have already been released back to the arena. Just remove the reference to the Promise after resolving it. + * @note If the Promise is already resolved and the callback has not been called yet, calling this function has no effect. + */ + void resolve() { + if (isResolved) { + return; + } + isResolved = true; + if (callback) { + readyList.push(this); + } + } + + /** + * @brief Update the Promise system, processing all resolved Promises and calling their callbacks. + * @note This function should be called regularly in the main loop of your application. + */ + static void update() { + while (!readyList.empty()) { + Promise *p = readyList.front(); + readyList.pop(); + p->callback(p->context); + Promise::arena.release(p); + } + } + + /** + * @brief Create a new Promise that resolves when all the given Promises are resolved. + * @param promises The Promises to wait for. + * @return Pointer to the newly created Promise that resolves when all given Promises are resolved. + * @note The promises will fail to create the all Promise and return nullptr if any of them already have a callback registered. + */ + template + static Promise* all(Promises*... promises) { + auto chained = Promise::inscribe(); + chained->counter = sizeof...(promises); + for (Promise* p : {promises...}) { + if (p->callback != nullptr || p->chainedCallback != nullptr) { + release(chained); + for (Promise* released : {promises...}) { + if (released == p) { + break; + } + release(released); + } + return nullptr; + } + p->then([](void* ctx) { + Promise* chained = static_cast(ctx); + chained->counter--; + if (chained->counter == 0) { + chained->resolve(); + } + }, chained); + } + return chained; + } + + /** + * @brief Create a new Promise that resolves when any of the given Promises is resolved. + * @param promises The Promises to wait for. + * @return Pointer to the newly created Promise that resolves when any given Promise is resolved. + * @note The promises will fail to create the any Promise and return nullptr if any of them already have a callback registered. + */ + template + static Promise* any(Args*... promises) { + auto anyPromise = Promise::inscribe(); + for (Promise* p : {promises...}) { + if (p->callback != nullptr || p->chainedCallback != nullptr) { + release(anyPromise); + for (Promise* released : {promises...}) { + if (released == p) { + break; + } + release(released); + } + return nullptr; + } + p->then([](void* ctx) { + Promise* anyPromise = static_cast(ctx); + anyPromise->resolve(); + }, anyPromise); + } + return anyPromise; + } + + Promise() = default; + ~Promise() = default; + Promise(Promise&&) = delete; + Promise(const Promise&) = delete; + Promise& operator=(Promise&&) = delete; + Promise& operator=(const Promise&) = delete; + + private: + Callback callback; + ChainedCallback chainedCallback; + void* context; + void* chainedContext; + bool isResolved = false; + int counter = 0; + Promise* next = nullptr; + static Arena> arena; + static RingBuffer*, S> readyList; +}; +template +inline Arena> Promise::arena; +template +inline RingBuffer*, S> Promise::readyList; + +using PromiseDefault = Promise<200>; + +#endif // PROMISE_HPP \ No newline at end of file From 4c40984846371ce7d2540797a3ac39a93291107a Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 15:26:56 +0100 Subject: [PATCH 014/166] chore(Promises): Add Promises to HALAL. --- Inc/HALAL/HALAL.hpp | 1 + Src/HALAL/HALAL.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index dc216434c..3ea093f07 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -39,6 +39,7 @@ #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" #include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" +#include "HALAL/Utils/Promise.hpp" #else #include "HALALMock/Services/DigitalOutputService/DigitalOutputService.hpp" #include "HALALMock/Services/DigitalInputService/DigitalInputService.hpp" diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 4d7903f0d..943645d0f 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -87,6 +87,7 @@ void HALAL::start(MAC mac, IPV4 ip, IPV4 subnet_mask, IPV4 gateway, Watchdog::start(); #endif #endif + PromiseDefault::update(); } #else // Simulator start From b4df6ca180a1073e51410b1b3eb64d7e69d6dd60 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 15:56:24 +0100 Subject: [PATCH 015/166] fix(Promises): Fix circular dependency and incorrect RingBuffer usage --- Inc/C++Utilities/Arena.hpp | 8 +++++--- Inc/HALAL/Utils/Promise.hpp | 20 ++++++++----------- .../LwIP/src/include/lwip/apps/sntp_opts.h | 12 +++++++---- Src/HALAL/HALAL.cpp | 2 +- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp index e38a2a424..ed5403781 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Arena.hpp @@ -26,10 +26,10 @@ class Arena { * @return Pointer to the acquired element, or nullptr if the arena is full. */ T* acquire() { - if (freeIndexes.empty()) { + if (freeIndexes.size() == 0) { return nullptr; } - size_t index = freeIndexes.front(); + size_t index = freeIndexes.first(); freeIndexes.pop(); usedIndexesSet[index] = true; return &elements[index]; @@ -82,7 +82,9 @@ class Arena { size_t used() const { return S - freeIndexes.size(); } Arena() { - std::iota(freeIndexes.begin(), freeIndexes.end(), 0); // Initialize free indexes, {0, 1, 2, ..., S-1} + for (size_t i = 0; i < S; ++i) { + freeIndexes.push(i); + } } Arena(const Arena&) = delete; Arena& operator=(const Arena&) = delete; diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index f08f9930b..bc239e439 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -11,12 +11,12 @@ #include "C++Utilities/Arena.hpp" #include "C++Utilities/RingBuffer.hpp" +#define PROMISE_MAX_CONCURRENT 200 + /** * @brief A simple Promise implementation for asynchronous programming. * @note Promises are allocated from a fixed-size memory arena, so you don't own the memory. Use Promise::release() to release them back to the arena if needed. - * @tparam S The maximum number of Promises that can be allocated simultaneously. Use the default "PromiseDefault" alias for a standard size. */ -template class Promise { using Callback = void(*)(void*); using ChainedCallback = Promise*(*)(void*); @@ -122,8 +122,8 @@ class Promise { * @note This function should be called regularly in the main loop of your application. */ static void update() { - while (!readyList.empty()) { - Promise *p = readyList.front(); + while (readyList.size() > 0) { + Promise *p = readyList.first(); readyList.pop(); p->callback(p->context); Promise::arena.release(p); @@ -205,14 +205,10 @@ class Promise { bool isResolved = false; int counter = 0; Promise* next = nullptr; - static Arena> arena; - static RingBuffer*, S> readyList; + static Arena arena; + static RingBuffer readyList; }; -template -inline Arena> Promise::arena; -template -inline RingBuffer*, S> Promise::readyList; - -using PromiseDefault = Promise<200>; +inline Arena Promise::arena; +inline RingBuffer Promise::readyList; #endif // PROMISE_HPP \ No newline at end of file diff --git a/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h b/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h index 0fdf173c6..97844fb20 100644 --- a/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h +++ b/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h @@ -41,12 +41,16 @@ #include "lwip/prot/iana.h" #ifdef __cplusplus +extern "C" { +#endif -extern "C" void set_time(uint32_t sec, uint32_t us); -extern "C" void set_rtc(uint16_t counter, uint8_t second, uint8_t minute, uint8_t hour, uint8_t day, uint8_t month, uint16_t year); -extern "C" u32_t get_rtc_s(); -extern "C" u32_t get_rtc_us(); +void set_time(uint32_t sec, uint32_t us); +void set_rtc(uint16_t counter, uint8_t second, uint8_t minute, uint8_t hour, uint8_t day, uint8_t month, uint16_t year); +u32_t get_rtc_s(); +u32_t get_rtc_us(); +#ifdef __cplusplus +} #endif #define SNTP_STARTUP_DELAY 0 diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 943645d0f..d18526a57 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -87,7 +87,7 @@ void HALAL::start(MAC mac, IPV4 ip, IPV4 subnet_mask, IPV4 gateway, Watchdog::start(); #endif #endif - PromiseDefault::update(); + Promise::update(); } #else // Simulator start From 7b775b0dadf6294c60c7e97dabc47e9dd6d3ed2b Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 15 Nov 2025 16:02:27 +0100 Subject: [PATCH 016/166] fix(Promises): Fix possible race condition in destroy method Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Inc/C++Utilities/Arena.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp index ed5403781..869ff1592 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Arena.hpp @@ -70,11 +70,11 @@ class Arena { * @return True if the element was successfully destroyed and released, false otherwise. */ bool destroy(T* ele) { - if (release(ele)) { - ele->~T(); - return true; + if (ele < &elements[0] || ele >= &elements[S] || !usedIndexesSet[ele - &elements[0]]) { + return false; } - return false; + ele->~T(); + return release(ele); } size_t capacity() const { return S; } From fb50fe3e4fdace9e86dab89a5731bf9956d97c81 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 16:04:54 +0100 Subject: [PATCH 017/166] fix(Promises): Fix all and any methods to check correctly for existing callbacks --- Inc/HALAL/Utils/Promise.hpp | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index bc239e439..fb9f3a9f5 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -140,17 +140,15 @@ class Promise { static Promise* all(Promises*... promises) { auto chained = Promise::inscribe(); chained->counter = sizeof...(promises); + + // Check if any promise already has a callback registered for (Promise* p : {promises...}) { if (p->callback != nullptr || p->chainedCallback != nullptr) { - release(chained); - for (Promise* released : {promises...}) { - if (released == p) { - break; - } - release(released); - } return nullptr; } + } + + for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* chained = static_cast(ctx); chained->counter--; @@ -171,17 +169,13 @@ class Promise { template static Promise* any(Args*... promises) { auto anyPromise = Promise::inscribe(); + // Check if any promise already has a callback registered for (Promise* p : {promises...}) { if (p->callback != nullptr || p->chainedCallback != nullptr) { - release(anyPromise); - for (Promise* released : {promises...}) { - if (released == p) { - break; - } - release(released); - } return nullptr; } + } + for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* anyPromise = static_cast(ctx); anyPromise->resolve(); From 56912d34c2c0f78708fcc765713d35432d79ca23 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 16:09:31 +0100 Subject: [PATCH 018/166] style(Promises): Double free detection in Arena is now in another if --- Inc/C++Utilities/Arena.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp index 869ff1592..968c50f55 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Arena.hpp @@ -55,10 +55,13 @@ class Arena { * @return True if the element was successfully released, false otherwise. */ bool release(T* ele) { - if (ele < &elements[0] || ele >= &elements[S] || !usedIndexesSet[ele - &elements[0]]) { + if (ele < &elements[0] || ele >= &elements[S]) { return false; } size_t index = ele - &elements[0]; + if (!usedIndexesSet[index]) { + return false; // Double free detected + } freeIndexes.push(index); usedIndexesSet[index] = false; return true; @@ -70,9 +73,13 @@ class Arena { * @return True if the element was successfully destroyed and released, false otherwise. */ bool destroy(T* ele) { - if (ele < &elements[0] || ele >= &elements[S] || !usedIndexesSet[ele - &elements[0]]) { + if (ele < &elements[0] || ele >= &elements[S]) { return false; } + size_t index = ele - &elements[0]; + if (!usedIndexesSet[index]) { + return false; // Double free detected + } ele->~T(); return release(ele); } From 0771a119d8d1be0190bd4ab51e009b35713e7b69 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 16:25:43 +0100 Subject: [PATCH 019/166] fix(Promises): Use atomic operations for isResolved flag --- Inc/HALAL/Utils/Promise.hpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index fb9f3a9f5..4aeb7f280 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -8,6 +8,7 @@ #ifndef PROMISE_HPP #define PROMISE_HPP +#include #include "C++Utilities/Arena.hpp" #include "C++Utilities/RingBuffer.hpp" @@ -59,7 +60,7 @@ class Promise { void then(Callback cb, void* ctx = nullptr) { callback = cb; context = ctx; - if (isResolved) { + if (isResolved.load(std::memory_order_acquire)) { readyList.push(this); } } @@ -96,7 +97,7 @@ class Promise { chained->then(p->next->callback, p->next->context); release(p->next); }; - if (isResolved) { + if (isResolved.load(std::memory_order_acquire)) { readyList.push(this); } return next; @@ -108,10 +109,10 @@ class Promise { * @note If the Promise is already resolved and the callback has not been called yet, calling this function has no effect. */ void resolve() { - if (isResolved) { + bool expected = false; + if (!isResolved.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) { return; } - isResolved = true; if (callback) { readyList.push(this); } @@ -151,8 +152,8 @@ class Promise { for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* chained = static_cast(ctx); - chained->counter--; - if (chained->counter == 0) { + int remaining = chained->counter.fetch_sub(1, std::memory_order_acq_rel) - 1; + if (remaining == 0) { chained->resolve(); } }, chained); @@ -196,8 +197,8 @@ class Promise { ChainedCallback chainedCallback; void* context; void* chainedContext; - bool isResolved = false; - int counter = 0; + std::atomic isResolved{false}; + std::atomic counter{0}; Promise* next = nullptr; static Arena arena; static RingBuffer readyList; From 8e2297cc617bceaa414715d87b51150979421c5d Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 16:30:25 +0100 Subject: [PATCH 020/166] fix(Promises): Add critical sections and maximum updates per cycle --- Inc/HALAL/Utils/Promise.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 4aeb7f280..50fc86343 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -13,6 +13,7 @@ #include "C++Utilities/RingBuffer.hpp" #define PROMISE_MAX_CONCURRENT 200 +#define PROMISE_MAX_UPDATES_PER_CYCLE 50 /** * @brief A simple Promise implementation for asynchronous programming. @@ -114,7 +115,9 @@ class Promise { return; } if (callback) { + __disable_irq(); readyList.push(this); + __enable_irq(); } } @@ -123,11 +126,16 @@ class Promise { * @note This function should be called regularly in the main loop of your application. */ static void update() { - while (readyList.size() > 0) { + uint16_t count = 0; + while (readyList.size() > 0 && count < PROMISE_MAX_UPDATES_PER_CYCLE) { + __disable_irq(); Promise *p = readyList.first(); readyList.pop(); + __enable_irq(); + p->callback(p->context); Promise::arena.release(p); + count++; } } From 93e8c7af905cde4dca9f7a5b0995af5d3ede4407 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sat, 15 Nov 2025 16:46:01 +0100 Subject: [PATCH 021/166] MDMA v1.0 baby --- Inc/HALAL/Models/MDMA/MDMA.hpp | 54 +++++++-- Src/HALAL/HALAL.cpp | 4 + Src/HALAL/Models/MDMA/MDMA.cpp | 215 +++++++++++++++++++++++++++++---- 3 files changed, 241 insertions(+), 32 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index f7a36d90e..0bf3bf58f 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -2,6 +2,7 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" +#include "ErrorHandler/ErrorHandler.hpp" #ifdef MDMA #undef MDMA @@ -16,23 +17,30 @@ class MDMA{ uint8_t id; uint8_t *data_buffer; uint8_t* destination_address; + Promise* promise; + bool using_promise{false}; Instance() = default; - Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address=nullptr): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address) {} + Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address) {} }; inline static std::unordered_map> linked_lists{}; inline static std::unordered_map instances{}; - inline static uint8_t number_of_packets{0}; static std::unordered_map dst_size_to_flags; static std::unordered_map src_size_to_flags; + static std::unordered_map instance_to_channel; + static std::unordered_map channel_to_instance; inline static MDMA_LinkNodeTypeDef transfer_node{}; - static void start(); + inline static uint8_t number_of_packets{0}; + static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); public: + static void start(); + + /** * @brief A method to add MDMA channels in linked list mode. @@ -46,15 +54,47 @@ class MDMA{ static uint8_t inscribe(uint8_t* data_buffer, uint8_t* destination_address=nullptr); + /** + * @brief A method to create a linked list from a tuple of pointers that come from packets, this linked list are global for all instances. + + * @param MDMA_id the id that represents the MDMA channel instance + * @param values the tuple of pointers that will be added to the linked list + * + * @return the id that represents the linked list associated to the packet. + */ template static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values); - + + /** + * @brief A method to merge multiple packets into a single linked list. + * + * @param base_packet_id The ID of the base packet to which other packets will be merged. + * @param packets_id The IDs of the packets to be merged into the base packet. + * + * @return The ID of the merged linked list. + */ template static uint8_t merge_packets(const uint8_t base_packet_id, const PacketIds... packets_id); - static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length); + /** + * @brief A method to start a transfer from source to destination using MDMA linked list + * + * @param MDMA_id The ID of the MDMA channel instance. + * @param packet_id The ID of the linked list packet to be transferred. + * @param destination_address The destination address for the transfer. If nullptr, the default destination associated with the instance will be used. + * @param promise An optional promise to be fulfilled upon transfer completion. + */ + static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address=nullptr,Promise* promise=nullptr); - static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address=nullptr); + /** + * @brief A method to transfer a packet using MDMA linked list + * + * @param MDMA_id The ID of the MDMA channel instance. + * @param packet_id The ID of the linked list packet to be transferred. + * @param destination_address The destination address for the transfer. If nullptr, the default destination associated with the instance will be used. + * @param promise An optional promise to be fulfilled upon transfer completion. + */ + static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id, uint8_t* destination_address=nullptr, Promise* promise=nullptr); +}; -}; \ No newline at end of file diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 4d7903f0d..e2011b5dd 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -35,6 +35,10 @@ void HALAL::start(MAC mac, IPV4 ip, IPV4 subnet_mask, IPV4 gateway, DMA::start(); #endif +#ifdef HAL_MDMA_MODULE_ENABLED + MDMA::start(); +#endif + #ifdef HAL_FMAC_MODULE_ENABLED MultiplierAccelerator::start(); #endif diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 475662c2e..fc4fa487c 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -11,14 +11,52 @@ std::unordered_map MDMA::dst_size_to_flags = { {4, MDMA_DEST_DATASIZE_WORD} }; +std::unordered_map MDMA::instance_to_channel = { + {0, MDMA_Channel0}, + {1, MDMA_Channel1}, + {2, MDMA_Channel2}, + {3, MDMA_Channel3}, + {4, MDMA_Channel4}, + {5, MDMA_Channel5}, + {6, MDMA_Channel6}, + {7, MDMA_Channel7} +}; + +std::unordered_map MDMA::channel_to_instance = { + {MDMA_Channel0, 0}, + {MDMA_Channel1, 1}, + {MDMA_Channel2, 2}, + {MDMA_Channel3, 3}, + {MDMA_Channel4, 4}, + {MDMA_Channel5, 5}, + {MDMA_Channel6, 6}, + {MDMA_Channel7, 7} +}; + + + uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) { + uint8_t id = instances.size(); MDMA_HandleTypeDef mdma_handle = {}; - mdma_handle.Instance = MDMA_Channel0; - mdma_handle.Init.Request = MDMA_REQUEST_SW; + mdma_handle.Instance = instance_to_channel[id]; + mdma_handle.Init.Request = MDMA_REQUEST_SW; + mdma_handle.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; + mdma_handle.Init.Priority = MDMA_PRIORITY_VERY_HIGH; + mdma_handle.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + mdma_handle.Init.SourceInc = MDMA_SRC_INC_BYTE; + mdma_handle.Init.DestinationInc = MDMA_DEST_INC_BYTE; + mdma_handle.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + mdma_handle.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + mdma_handle.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + mdma_handle.Init.BufferTransferLength = 1; + mdma_handle.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + mdma_handle.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + mdma_handle.Init.SourceBlockAddressOffset = 0; + mdma_handle.Init.DestBlockAddressOffset = 0; - uint8_t id = instances.size(); - Instance instance(mdma_handle, id, data_buffer); + + Instance instance(mdma_handle, id, data_buffer, destination_address); instances[id] = instance; MDMA_LinkNodeConfTypeDef nodeConfig; @@ -41,12 +79,19 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; nodeConfig.BlockDataLength = 1; nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; - nodeConfig.DstAddress = (uint32_t) instance.data_buffer; + if(destination_address == nullptr) + { + nodeConfig.DstAddress = (uint32_t) instance.data_buffer; + } + else + { + nodeConfig.DstAddress = (uint32_t) destination_address; + } HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); if (status != HAL_OK) { - //ErrorHandler("Error creating linked list in MDMA"); + ErrorHandler("Error creating linked list in MDMA"); } return id; @@ -56,6 +101,24 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); + for(auto& [id, instance] : instances) + { + HAL_StatusTypeDef status = HAL_MDMA_Init(&instance.handle); + if (status != HAL_OK) + { + ErrorHandler("Error initialising MDMA instance"); + } + HAL_MDMA_RegisterCallback(&instance.handle, HAL_MDMA_XFER_CPLT_CB_ID, MDMA::TransferCompleteCallback); + + status = HAL_MDMA_Start_IT(&instance.handle, (uint32_t)instance.data_buffer, (uint32_t)instance.destination_address, 1,1); + if( status != HAL_OK) + { + ErrorHandler("Error starting MDMA instance"); + } + } + HAL_NVIC_SetPriority(MDMA_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(MDMA_IRQn); + //demas } @@ -65,6 +128,7 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va Instance& instance = instances[MDMA_id]; uint32_t offset{0}; uint8_t i = 0; + HAL_StatusTypeDef status; std::vector nodes; MDMA_LinkNodeConfTypeDef nodeConfig; nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; @@ -85,7 +149,7 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va auto create_node = [&](auto ptr) { if (ptr == nullptr) { - //ErrorHandler("Nullptr given to MDMA") + ErrorHandler("Nullptr given to MDMA"); } using PointerType = std::decay_t; @@ -103,10 +167,10 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va nodeConfig.Init.DestDataSize = dst_size_to_flags[type_size]; - HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); if (status != HAL_OK) { - //ErrorHandler("Error creating linked list in MDMA"); + ErrorHandler("Error creating linked list in MDMA"); } offset += type_size; nodes.push_back(node); @@ -114,7 +178,11 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va create_node(ptrs...); if(i !=0){ - HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[i-1], &nodes[i]); + status =HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[i-1], &nodes[i]); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } } i++; @@ -122,7 +190,7 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va if (nodes.empty()) { - //ErrorHandler("Error creating linked list in MDMA"); + ErrorHandler("Error creating linked list in MDMA"); } if(instance.destination_address == nullptr) @@ -136,43 +204,140 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; nodeConfig.DstAddress = (uint32_t) instance.data_buffer + offset; - HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); if (status != HAL_OK) { - //ErrorHandler("Error creating linked list in MDMA"); + ErrorHandler("Error creating linked list in MDMA"); } nodes.push_back(node); - HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[nodes.size()-2], &nodes[nodes.size()-1]); + status = HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[nodes.size()-2], &nodes[nodes.size()-1]); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } } linked_lists[number_of_packets++] = nodes; return number_of_packets; } -void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address) +uint8_t MDMA::merge_packets(const uint8_t base_packet_id, const auto... packets_id) +{ + std::vector merged_nodes = linked_lists[base_packet_id]; + uint32_t offset = 0; + for(const auto& node : merged_nodes) + { + offset += node.CBNDTR; + } + + merged_nodes.pop_back(); //Remove last auxilary node + std::array packet_ids{static_cast(packets_id)...}; + for (size_t idx = 0; idx < packet_ids.size(); ++idx) { + uint8_t pid = packet_ids[idx]; + std::vector nodes_to_merge = linked_lists[pid]; + for(auto& node : nodes_to_merge) + { + node.CSAR = node.CSAR + offset; + offset += node.CBNDTR; + } + + if (idx != packet_ids.size() - 1) { + nodes_to_merge.pop_back(); + } + + nodes_to_merge.back().CLAR = 0; + merged_nodes.back().CLAR = (uint32_t)&nodes_to_merge[0]; + merged_nodes.insert(merged_nodes.end(), nodes_to_merge.begin(), nodes_to_merge.end()); + } + + linked_lists[number_of_packets++] = merged_nodes; + return number_of_packets; +} + +void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address,Promise* promise) { Instance& instance = instances[MDMA_id]; + + while((instance.handle.Instance->CISR & MDMA_CISR_CRQA) != 0U) + { //Active wait in case there is an error with the sinchronization + __NOP(); + } + + if(promise == nullptr) + { + instance.using_promise = false; + } + else + { + instance.promise = promise; + instance.using_promise = true; + } + if(destination_address != nullptr) { + if(instance.destination_address == nullptr) + { + ErrorHandler("No destination address provided for MDMA transfer"); + } std::vector& nodes = linked_lists[packet_id]; - auto transfer_node =nodes.back(); - transfer_node.CDAR = (uint32_t)destination_address; + auto& packet_transfer_node =nodes.back(); + packet_transfer_node.CDAR = (uint32_t)destination_address; } - //More to do... + + instance.handle.Instance->CLAR = (uint32_t)&linked_lists[packet_id][0]; + HAL_MDMA_GenerateSWRequest(&instance.handle); } -//Habria que ver si renta esta funcion: -void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address,uint8_t* destination_address, const uint32_t data_length) +void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) { Instance& instance = instances[MDMA_id]; + while((instance.handle.Instance->CISR & MDMA_CISR_CRQA) != 0U) + { //Active wait in case there is an error with the sinchronization + __NOP(); + } + + if(promise == nullptr) + { + instance.using_promise = false; + } + else + { + instance.promise = promise; + instance.using_promise = true; + } + transfer_node.CSAR = (uint32_t)source_address; - transfer_node.CDAR = (uint32_t)destination_address; transfer_node.CBNDTR = data_length; + if(destination_address == nullptr) + { + if(instance.destination_address ==nullptr) + { + ErrorHandler("No destination address provided for MDMA transfer"); + } + transfer_node.CDAR = (uint32_t)instance.destination_address; + } + else + { + transfer_node.CDAR = (uint32_t)destination_address; + } + + instance.handle.Instance->CLAR = (uint32_t)&transfer_node; + + HAL_MDMA_GenerateSWRequest(&instance.handle); + +} + + +void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) +{ + Instance& instance = instances[channel_to_instance[hmdma->Instance]]; + if(instance.using_promise) + { + instance.promise->resolve(); + instance.using_promise = false; + } +} - while(false)//Chequear que no esté funcionando); - HAL_MDMA_LinkedList_AddNode(&instance.handle, nullptr, &transfer_node); - //Algo.... -} \ No newline at end of file From ee32863124bbe0ac8cc0248e2e56cfc782b64511 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sat, 15 Nov 2025 17:01:51 +0100 Subject: [PATCH 022/166] Merged the promises, testing on course --- Inc/HALAL/HALAL.hpp | 1 + Inc/HALAL/Models/MDMA/MDMA.hpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 3ea093f07..390a6b266 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -40,6 +40,7 @@ #include "HALAL/Models/Concepts/Concepts.hpp" #include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" #include "HALAL/Utils/Promise.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" #else #include "HALALMock/Services/DigitalOutputService/DigitalOutputService.hpp" #include "HALALMock/Services/DigitalInputService/DigitalInputService.hpp" diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 0bf3bf58f..994fb5f68 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -3,6 +3,7 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/Utils/Promise.hpp" #ifdef MDMA #undef MDMA From 2749970f663c8e08a202e505f02074176694a801 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:20:41 +0100 Subject: [PATCH 023/166] fix(Promises): Revert mistake change in sntp_opts.h in commit b4df6ca180a1073e51410b1b3eb64d7e69d6dd60 --- .../LwIP/src/include/lwip/apps/sntp_opts.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h b/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h index 97844fb20..151383420 100644 --- a/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h +++ b/Middlewares/Third_Party/LwIP/src/include/lwip/apps/sntp_opts.h @@ -41,16 +41,12 @@ #include "lwip/prot/iana.h" #ifdef __cplusplus -extern "C" { -#endif -void set_time(uint32_t sec, uint32_t us); -void set_rtc(uint16_t counter, uint8_t second, uint8_t minute, uint8_t hour, uint8_t day, uint8_t month, uint16_t year); -u32_t get_rtc_s(); -u32_t get_rtc_us(); +extern "C" void set_time(uint32_t sec, uint32_t us); +extern "C" void set_rtc(uint16_t counter, uint8_t second, uint8_t minute, uint8_t hour, uint8_t day, uint8_t month, uint16_t year); +extern "C" u32_t get_rtc_s(); +extern "C" u32_t get_rtc_us(); -#ifdef __cplusplus -} #endif #define SNTP_STARTUP_DELAY 0 @@ -231,4 +227,4 @@ u32_t get_rtc_us(); * @} */ -#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */ +#endif /* LWIP_HDR_APPS_SNTP_OPTS_H */ \ No newline at end of file From 440bcec1c191ca9b43a1b8c0bbe07fedd05b336e Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sat, 15 Nov 2025 17:21:21 +0100 Subject: [PATCH 024/166] Fixes --- Inc/HALAL/Models/MDMA/MDMA.hpp | 4 +-- Src/HALAL/Models/MDMA/MDMA.cpp | 57 ++++++++++++++++------------------ 2 files changed, 28 insertions(+), 33 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 994fb5f68..6accf0069 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -20,8 +20,9 @@ class MDMA{ uint8_t* destination_address; Promise* promise; bool using_promise{false}; + MDMA_LinkNodeTypeDef transfer_node{}; Instance() = default; - Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address) {} + Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address,MDMA_LinkNodeTypeDef transfer_node): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address), transfer_node(transfer_node) {} }; @@ -31,7 +32,6 @@ class MDMA{ static std::unordered_map src_size_to_flags; static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; - inline static MDMA_LinkNodeTypeDef transfer_node{}; inline static uint8_t number_of_packets{0}; diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index fc4fa487c..689c9c159 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -56,11 +56,8 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) mdma_handle.Init.DestBlockAddressOffset = 0; - Instance instance(mdma_handle, id, data_buffer, destination_address); - instances[id] = instance; - MDMA_LinkNodeConfTypeDef nodeConfig; - + MDMA_LinkNodeTypeDef transfer_node; //Both source and destination, as well as block data length will change with the use of the transfer method, this is just a dummy initialisation nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; @@ -78,10 +75,10 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) nodeConfig.Init.SourceDataSize = MDMA_SRC_INC_BYTE; nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; nodeConfig.BlockDataLength = 1; - nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; + nodeConfig.SrcAddress = (uint32_t) data_buffer; if(destination_address == nullptr) { - nodeConfig.DstAddress = (uint32_t) instance.data_buffer; + nodeConfig.DstAddress = (uint32_t) data_buffer; } else { @@ -93,6 +90,8 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) { ErrorHandler("Error creating linked list in MDMA"); } + Instance instance(mdma_handle, id, data_buffer, destination_address, transfer_node); + instances[id] = instance; return id; } @@ -110,11 +109,11 @@ void MDMA::start() } HAL_MDMA_RegisterCallback(&instance.handle, HAL_MDMA_XFER_CPLT_CB_ID, MDMA::TransferCompleteCallback); - status = HAL_MDMA_Start_IT(&instance.handle, (uint32_t)instance.data_buffer, (uint32_t)instance.destination_address, 1,1); - if( status != HAL_OK) - { - ErrorHandler("Error starting MDMA instance"); - } + // status = HAL_MDMA_Start_IT(&instance.handle, (uint32_t)instance.data_buffer, (uint32_t)instance.destination_address, 1,1); + // if( status != HAL_OK) + // { + // ErrorHandler("Error starting MDMA instance"); + // } } HAL_NVIC_SetPriority(MDMA_IRQn, 0, 0); HAL_NVIC_EnableIRQ(MDMA_IRQn); @@ -127,9 +126,10 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va { Instance& instance = instances[MDMA_id]; uint32_t offset{0}; - uint8_t i = 0; HAL_StatusTypeDef status; - std::vector nodes; + uint8_t current_packet_id = number_of_packets++; + linked_lists[current_packet_id] = std::vector(); + std::vector& nodes = linked_lists[current_packet_id]; MDMA_LinkNodeConfTypeDef nodeConfig; nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; @@ -177,15 +177,6 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va }; create_node(ptrs...); - if(i !=0){ - status =HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[i-1], &nodes[i]); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - } - i++; - }, values); if (nodes.empty()) @@ -193,8 +184,14 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va ErrorHandler("Error creating linked list in MDMA"); } - if(instance.destination_address == nullptr) - { + for(size_t i = 0; i < nodes.size() - 1; i++) { + status = HAL_MDMA_LinkedList_AddNode(&nodes[i], &nodes[i+1]); + if (status != HAL_OK) + { + ErrorHandler("Error linking list nodes in MDMA"); + } + } + MDMA_LinkNodeTypeDef node = {}; nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; @@ -215,9 +212,7 @@ uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& va { ErrorHandler("Error creating linked list in MDMA"); } - } - linked_lists[number_of_packets++] = nodes; return number_of_packets; } @@ -307,22 +302,22 @@ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const ui instance.using_promise = true; } - transfer_node.CSAR = (uint32_t)source_address; - transfer_node.CBNDTR = data_length; + instance.transfer_node.CSAR = (uint32_t)source_address; + instance.transfer_node.CBNDTR = data_length; if(destination_address == nullptr) { if(instance.destination_address ==nullptr) { ErrorHandler("No destination address provided for MDMA transfer"); } - transfer_node.CDAR = (uint32_t)instance.destination_address; + instance.transfer_node.CDAR = (uint32_t)instance.destination_address; } else { - transfer_node.CDAR = (uint32_t)destination_address; + instance.transfer_node.CDAR = (uint32_t)destination_address; } - instance.handle.Instance->CLAR = (uint32_t)&transfer_node; + instance.handle.Instance->CLAR = (uint32_t)&instance.transfer_node; HAL_MDMA_GenerateSWRequest(&instance.handle); From abd7dec1db0bf175acf06b63f9de768eebf3980c Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 15 Nov 2025 17:23:34 +0100 Subject: [PATCH 025/166] style(Promises): Make maximums overwrittable in Promises.hpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Inc/HALAL/Utils/Promise.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 50fc86343..7f89cd393 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -12,8 +12,19 @@ #include "C++Utilities/Arena.hpp" #include "C++Utilities/RingBuffer.hpp" +// Maximum number of concurrent Promises allowed in the arena. +// Default is 200, which should be sufficient for most use cases. Increase if you expect higher concurrency. +// You can override this value by defining PROMISE_MAX_CONCURRENT before including this header. +#ifndef PROMISE_MAX_CONCURRENT #define PROMISE_MAX_CONCURRENT 200 +#endif + +// Maximum number of Promise updates processed per cycle. +// Default is 50, which balances throughput and responsiveness. Tune this for your workload. +// You can override this value by defining PROMISE_MAX_UPDATES_PER_CYCLE before including this header. +#ifndef PROMISE_MAX_UPDATES_PER_CYCLE #define PROMISE_MAX_UPDATES_PER_CYCLE 50 +#endif /** * @brief A simple Promise implementation for asynchronous programming. From d74809ab8fb6c100548b53f65108da94058cec35 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:25:42 +0100 Subject: [PATCH 026/166] fix(Promises): Ensure no memroy leaks in Promise::all and Promise::any --- Inc/HALAL/Utils/Promise.hpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 50fc86343..febfbbcee 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -147,9 +147,6 @@ class Promise { */ template static Promise* all(Promises*... promises) { - auto chained = Promise::inscribe(); - chained->counter = sizeof...(promises); - // Check if any promise already has a callback registered for (Promise* p : {promises...}) { if (p->callback != nullptr || p->chainedCallback != nullptr) { @@ -157,16 +154,20 @@ class Promise { } } + auto allPromise = Promise::inscribe(); + allPromise->counter = sizeof...(promises); + + for (Promise* p : {promises...}) { p->then([](void* ctx) { - Promise* chained = static_cast(ctx); - int remaining = chained->counter.fetch_sub(1, std::memory_order_acq_rel) - 1; + Promise* allPromise = static_cast(ctx); + int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 1; if (remaining == 0) { - chained->resolve(); + allPromise->resolve(); } - }, chained); + }, allPromise); } - return chained; + return allPromise; } /** @@ -177,13 +178,15 @@ class Promise { */ template static Promise* any(Args*... promises) { - auto anyPromise = Promise::inscribe(); // Check if any promise already has a callback registered for (Promise* p : {promises...}) { if (p->callback != nullptr || p->chainedCallback != nullptr) { return nullptr; } } + + auto anyPromise = Promise::inscribe(); + for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* anyPromise = static_cast(ctx); From c078fafe152936240e7288ec6ce583f9f513c436 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:28:26 +0100 Subject: [PATCH 027/166] fix(Promises): Disable interrupts when adding to ready list --- Inc/HALAL/Utils/Promise.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index febfbbcee..f385c9bdf 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -61,9 +61,11 @@ class Promise { void then(Callback cb, void* ctx = nullptr) { callback = cb; context = ctx; + __disable_irq(); if (isResolved.load(std::memory_order_acquire)) { readyList.push(this); } + __enable_irq(); } /** * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. @@ -98,9 +100,11 @@ class Promise { chained->then(p->next->callback, p->next->context); release(p->next); }; + __disable_irq(); if (isResolved.load(std::memory_order_acquire)) { readyList.push(this); } + __enable_irq(); return next; } @@ -186,7 +190,7 @@ class Promise { } auto anyPromise = Promise::inscribe(); - + for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* anyPromise = static_cast(ctx); From 7101a175b7c4984bedcbe0493b791dbcf03247e2 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:33:24 +0100 Subject: [PATCH 028/166] fix(Promises): Remove Promise::update() from HALAL::start() --- Src/HALAL/HALAL.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index d18526a57..4d7903f0d 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -87,7 +87,6 @@ void HALAL::start(MAC mac, IPV4 ip, IPV4 subnet_mask, IPV4 gateway, Watchdog::start(); #endif #endif - Promise::update(); } #else // Simulator start From bc7b36f28a73fd14a1268c2e7c74b1ca9f3c912e Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:44:46 +0100 Subject: [PATCH 029/166] feat(Promises): Add Stack class utility, doesn't use heap --- Inc/C++Utilities/Stack.hpp | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Inc/C++Utilities/Stack.hpp diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp new file mode 100644 index 000000000..e6afdd3fa --- /dev/null +++ b/Inc/C++Utilities/Stack.hpp @@ -0,0 +1,40 @@ +/* + * Stack.hpp + * + * Created on: 15 nov. 2025 + * Author: Boris + */ + +#ifndef STACK_HPP +#define STACK_HPP + +#include "CppImports.hpp" + +/** + * @brief A simple fixed-size stack for arena free list management. + * @tparam S The maximum number of elements. + */ +template +class Stack { +public: + Stack() : top(0) {} + + void push(size_t value) { + if (top < S) { + data[top++] = value; + } + } + + size_t pop() { + return data[--top]; + } + + size_t size() const { return top; } + bool empty() const { return top == 0; } + +private: + size_t data[S]; + size_t top; +}; + +#endif // STACK_HPP \ No newline at end of file From 930c31f9821ff514fb2bc443d5e5dd6380bacaf3 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 17:47:31 +0100 Subject: [PATCH 030/166] feat(Promises): Use Stack for Arena (better locality), and add iterators to Arena --- Inc/C++Utilities/Arena.hpp | 84 +++++++++++++++++++++++++++++++++++--- 1 file changed, 78 insertions(+), 6 deletions(-) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Arena.hpp index 968c50f55..999d833ea 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Arena.hpp @@ -9,7 +9,8 @@ #define ARENA_HPP #include "CppImports.hpp" -#include "RingBuffer.hpp" +#include "Stack.hpp" + /** * @brief A simple memory arena for fixed-size allocations. @@ -26,11 +27,10 @@ class Arena { * @return Pointer to the acquired element, or nullptr if the arena is full. */ T* acquire() { - if (freeIndexes.size() == 0) { + if (freeIndexes.empty()) { return nullptr; } - size_t index = freeIndexes.first(); - freeIndexes.pop(); + size_t index = freeIndexes.pop(); usedIndexesSet[index] = true; return &elements[index]; } @@ -88,8 +88,80 @@ class Arena { size_t available() const { return freeIndexes.size(); } size_t used() const { return S - freeIndexes.size(); } + /** + * @brief Iterator for traversing used elements in the arena. + */ + class Iterator { + public: + Iterator(Arena* arena, size_t index) : arena(arena), index(index) { + // Skip to the first used element + while (index < S && !arena->usedIndexesSet[index]) { + ++index; + } + this->index = index; + } + + T& operator*() { return arena->elements[index]; } + T* operator->() { return &arena->elements[index]; } + + Iterator& operator++() { + do { + ++index; + } while (index < S && !arena->usedIndexesSet[index]); + return *this; + } + + bool operator!=(const Iterator& other) const { + return index != other.index; + } + + private: + Arena* arena; + size_t index; + }; + + /** + * @brief Const iterator for traversing used elements in the arena. + */ + class ConstIterator { + public: + ConstIterator(const Arena* arena, size_t index) : arena(arena), index(index) { + // Skip to the first used element + while (index < S && !arena->usedIndexesSet[index]) { + ++index; + } + this->index = index; + } + + const T& operator*() const { return arena->elements[index]; } + const T* operator->() const { return &arena->elements[index]; } + + ConstIterator& operator++() { + do { + ++index; + } while (index < S && !arena->usedIndexesSet[index]); + return *this; + } + + bool operator!=(const ConstIterator& other) const { + return index != other.index; + } + + private: + const Arena* arena; + size_t index; + }; + + Iterator begin() { return Iterator(this, 0); } + Iterator end() { return Iterator(this, S); } + ConstIterator begin() const { return ConstIterator(this, 0); } + ConstIterator end() const { return ConstIterator(this, S); } + ConstIterator cbegin() const { return ConstIterator(this, 0); } + ConstIterator cend() const { return ConstIterator(this, S); } + Arena() { - for (size_t i = 0; i < S; ++i) { + // Push indices in reverse order so index 0 is allocated first + for (int i = S - 1; i >= 0; --i) { freeIndexes.push(i); } } @@ -100,7 +172,7 @@ class Arena { private: T elements[S]; - RingBuffer freeIndexes; + Stack freeIndexes; bool usedIndexesSet[S]{false}; }; From 17de1f9e344b2b35f936db9008b32b4d459b71f6 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 18:00:32 +0100 Subject: [PATCH 031/166] feat(Promises): Update Promises handling to avoid synchronization issues, now iterates over promises to check their state --- Inc/HALAL/Utils/Promise.hpp | 65 +++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 31 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index ba165eb49..1d5074eff 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -10,7 +10,6 @@ #include #include "C++Utilities/Arena.hpp" -#include "C++Utilities/RingBuffer.hpp" // Maximum number of concurrent Promises allowed in the arena. // Default is 200, which should be sufficient for most use cases. Increase if you expect higher concurrency. @@ -34,6 +33,13 @@ class Promise { using Callback = void(*)(void*); using ChainedCallback = Promise*(*)(void*); + enum class State : uint8_t { + Pending, + Resolved, + Ready, + Completed + }; + public: /** @@ -47,7 +53,7 @@ class Promise { if (!p) { return nullptr; } - p->isResolved = false; + p->state.store(State::Pending, std::memory_order_release); p->callback = nullptr; p->context = nullptr; return p; @@ -72,12 +78,11 @@ class Promise { void then(Callback cb, void* ctx = nullptr) { callback = cb; context = ctx; - __disable_irq(); - if (isResolved.load(std::memory_order_acquire)) { - readyList.push(this); - } - __enable_irq(); + + State expected = State::Resolved; + state.compare_exchange_strong(expected, State::Ready, std::memory_order_acq_rel); // If already resolved, mark as ready } + /** * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. * @param cb The chained callback function that returns a new Promise. @@ -111,11 +116,10 @@ class Promise { chained->then(p->next->callback, p->next->context); release(p->next); }; - __disable_irq(); - if (isResolved.load(std::memory_order_acquire)) { - readyList.push(this); - } - __enable_irq(); + + State expected = State::Resolved; + state.compare_exchange_strong(expected, State::Ready, std::memory_order_acq_rel); + return next; } @@ -125,14 +129,13 @@ class Promise { * @note If the Promise is already resolved and the callback has not been called yet, calling this function has no effect. */ void resolve() { - bool expected = false; - if (!isResolved.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) { + State expected = State::Pending; + if (!state.compare_exchange_strong(expected, State::Resolved, std::memory_order_acq_rel)) { return; } + if (callback) { - __disable_irq(); - readyList.push(this); - __enable_irq(); + state.store(State::Ready, std::memory_order_release); } } @@ -142,15 +145,18 @@ class Promise { */ static void update() { uint16_t count = 0; - while (readyList.size() > 0 && count < PROMISE_MAX_UPDATES_PER_CYCLE) { - __disable_irq(); - Promise *p = readyList.first(); - readyList.pop(); - __enable_irq(); - - p->callback(p->context); - Promise::arena.release(p); - count++; + + for (Promise& p : arena) { + if (count >= PROMISE_MAX_UPDATES_PER_CYCLE) { + break; + } + + State expected = State::Ready; + if (p.state.compare_exchange_strong(expected, State::Completed, std::memory_order_acq_rel)) { + p.callback(p.context); + Promise::arena.release(&p); + count++; + } } } @@ -170,8 +176,7 @@ class Promise { } auto allPromise = Promise::inscribe(); - allPromise->counter = sizeof...(promises); - + allPromise->counter.store(sizeof...(promises), std::memory_order_release); for (Promise* p : {promises...}) { p->then([](void* ctx) { @@ -223,13 +228,11 @@ class Promise { ChainedCallback chainedCallback; void* context; void* chainedContext; - std::atomic isResolved{false}; + std::atomic state{State::Pending}; std::atomic counter{0}; Promise* next = nullptr; static Arena arena; - static RingBuffer readyList; }; inline Arena Promise::arena; -inline RingBuffer Promise::readyList; #endif // PROMISE_HPP \ No newline at end of file From f86c7abfde9c4b0d8d02509c9ca12dc0f5c0f373 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 18:05:23 +0100 Subject: [PATCH 032/166] fix(Promises): Defer release of chained promises in update method --- Inc/HALAL/Utils/Promise.hpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 1d5074eff..e2f45f031 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -145,6 +145,8 @@ class Promise { */ static void update() { uint16_t count = 0; + Promise* toRelease[PROMISE_MAX_UPDATES_PER_CYCLE]; + uint16_t releaseCount = 0; for (Promise& p : arena) { if (count >= PROMISE_MAX_UPDATES_PER_CYCLE) { @@ -154,10 +156,15 @@ class Promise { State expected = State::Ready; if (p.state.compare_exchange_strong(expected, State::Completed, std::memory_order_acq_rel)) { p.callback(p.context); - Promise::arena.release(&p); + toRelease[releaseCount++] = &p; count++; } } + + // Release all completed Promises after iteration + for (uint16_t i = 0; i < releaseCount; i++) { + Promise::arena.release(toRelease[i]); + } } /** From 132960da588d3379b4928572b7591228f4a2d1e0 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sat, 15 Nov 2025 18:15:48 +0100 Subject: [PATCH 033/166] fix(Promises): Defer chained promise cleanup to update --- Inc/HALAL/Utils/Promise.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index e2f45f031..d17b23bb1 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -84,11 +84,12 @@ class Promise { } /** - * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. + * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. Be extremely careful with memory management when using chained Promises. * @param cb The chained callback function that returns a new Promise. * @param ctx The context to be passed to the chained callback, can only be a pointer, you must manage the memory yourself. * @return Pointer to the newly created chained Promise. * @note If the Promise is already resolved, the chained callback is scheduled to be called in the next update cycle. You can call then whenever you want, but only one chained callback can be registered per Promise. + * @note You should not store the returned Promise pointer for long-term use, as it is managed by the Promise system. Call then on the returned Promise to register further callbacks. * @example * ```cpp * Promise* p1 = Promise::inscribe(); @@ -114,7 +115,9 @@ class Promise { Promise* p = static_cast(thisPtr); Promise* chained = p->chainedCallback(p->chainedContext); chained->then(p->next->callback, p->next->context); - release(p->next); + p->next->state.store(State::Ready, std::memory_order_release); + p->next->callback = nullptr; + p->next->context = nullptr; }; State expected = State::Resolved; @@ -155,7 +158,9 @@ class Promise { State expected = State::Ready; if (p.state.compare_exchange_strong(expected, State::Completed, std::memory_order_acq_rel)) { - p.callback(p.context); + if (p.callback) { + p.callback(p.context); + } toRelease[releaseCount++] = &p; count++; } From 881d87acc286d48c83b90e559ca8459899503dc0 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 17 Nov 2025 11:54:01 +0100 Subject: [PATCH 034/166] Packets now work, merge_packets now on progress --- Inc/HALAL/Models/MDMA/MDMA.hpp | 199 +++++++++++++++++++- Src/HALAL/Models/MDMA/MDMA.cpp | 324 ++++++++++++++------------------- 2 files changed, 333 insertions(+), 190 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 6accf0069..6ffd203fe 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -4,6 +4,12 @@ #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" #include "HALAL/Utils/Promise.hpp" +#include +#include +#include +#include +#include +#include #ifdef MDMA #undef MDMA @@ -21,13 +27,17 @@ class MDMA{ Promise* promise; bool using_promise{false}; MDMA_LinkNodeTypeDef transfer_node{}; + uint32_t last_destination{0}; + uint32_t last_size{0}; Instance() = default; Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address,MDMA_LinkNodeTypeDef transfer_node): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address), transfer_node(transfer_node) {} }; - inline static std::unordered_map> linked_lists{}; + static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); + inline static std::unordered_map> linked_lists{}; inline static std::unordered_map instances{}; + inline static std::unordered_map packet_sizes{}; static std::unordered_map dst_size_to_flags; static std::unordered_map src_size_to_flags; static std::unordered_map instance_to_channel; @@ -36,10 +46,12 @@ class MDMA{ inline static uint8_t number_of_packets{0}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); + static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); public: static void start(); + static void irq_handler(); /** @@ -69,13 +81,14 @@ class MDMA{ /** * @brief A method to merge multiple packets into a single linked list. * + * @param MDMA_id The ID of the MDMA channel instance. * @param base_packet_id The ID of the base packet to which other packets will be merged. * @param packets_id The IDs of the packets to be merged into the base packet. * * @return The ID of the merged linked list. */ template - static uint8_t merge_packets(const uint8_t base_packet_id, const PacketIds... packets_id); + static uint8_t merge_packets(const uint8_t MDMA_id,const uint8_t base_packet_id, const PacketIds... packets_id); /** * @brief A method to start a transfer from source to destination using MDMA linked list @@ -99,3 +112,185 @@ class MDMA{ }; +template +inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple& values) +{ + Instance& instance = instances[MDMA_id]; + uint32_t offset{0}; + HAL_StatusTypeDef status; + const uint8_t current_packet_id = number_of_packets++; + + std::vector& nodes = linked_lists[current_packet_id]; + nodes.clear(); + nodes.reserve(sizeof...(pointers) + 5U); + + MDMA_LinkNodeConfTypeDef nodeConfig; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + + std::apply([ + &nodes, + &nodeConfig, + &offset, + &instance, + &status + ](auto... ptrs) + { + auto create_node = [&](auto ptr) + { + if (ptr == nullptr) + { + ErrorHandler("Nullptr given to MDMA"); + } + + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + constexpr size_t type_size = sizeof(UnderlyingType); + + static_assert(type_size == 1 || type_size == 2 || type_size == 4, + "MDMA::add_packet: Passed a variable with type size > 4 "); + + MDMA_LinkNodeTypeDef node = {}; + nodeConfig.SrcAddress = reinterpret_cast(ptr); + nodeConfig.DstAddress = reinterpret_cast(instance.data_buffer) + offset; + nodeConfig.BlockDataLength = static_cast(type_size); + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + + status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } + + offset += type_size; + nodes.push_back(node); + }; + + (create_node(ptrs), ...); + }, values); + + if (nodes.empty()) + { + ErrorHandler("Error creating linked list in MDMA"); + } + + MDMA_LinkNodeTypeDef node = {}; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.BlockDataLength = offset; + nodeConfig.SrcAddress = reinterpret_cast(instance.data_buffer); + nodeConfig.DstAddress = reinterpret_cast(instance.data_buffer) + offset; + + status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) + { + ErrorHandler("Error creating linked list in MDMA"); + } + + nodes.push_back(node); + + packet_sizes[current_packet_id] = offset; + + for (size_t i = 0; i + 1 < nodes.size(); ++i) + { + nodes[i].CLAR = reinterpret_cast(&nodes[i + 1]); + } + nodes.back().CLAR = 0; + + return current_packet_id; +} + +template +inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id,const uint8_t base_packet_id, const PacketIds... packets_id) +{ + Instance& instance = instances[MDMA_id]; + + uint8_t new_packet_id = number_of_packets++; + std::vector& merged_nodes = linked_lists[new_packet_id]; + + uint32_t offset = 0; + + std::vector base_nodes = linked_lists[base_packet_id]; + if (!base_nodes.empty()) + { + base_nodes.pop_back(); + for (const auto& node : base_nodes) + { + merged_nodes.push_back(node); + offset += node.CBNDTR; + } + } + + std::array packet_ids{static_cast(packets_id)...}; + for (size_t idx = 0; idx < packet_ids.size(); ++idx) + { + uint8_t pid = packet_ids[idx]; + std::vector nodes_to_merge = linked_lists[pid]; + + if (nodes_to_merge.empty()) continue; + + nodes_to_merge.pop_back(); + + for (auto& node : nodes_to_merge) + { + + offset += node.CBNDTR; + node.CDAR = (uint32_t)instance.data_buffer + offset; + + merged_nodes.push_back(node); + } + } + + if (merged_nodes.empty()) + { + ErrorHandler("Error merging linked list in MDMA"); + } + + MDMA_LinkNodeTypeDef aux_node = {}; + MDMA_LinkNodeConfTypeDef nodeConfig{}; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.BlockDataLength = 1; + nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; + nodeConfig.DstAddress = (uint32_t) instance.data_buffer + offset; + + HAL_MDMA_LinkedList_CreateNode(&aux_node, &nodeConfig); + merged_nodes.push_back(aux_node); + + for (size_t i = 0; i < merged_nodes.size() - 1; ++i) + { + merged_nodes[i].CLAR = reinterpret_cast(&merged_nodes[i + 1]); + } + merged_nodes.back().CLAR = 0; + + SCB_CleanDCache_by_Addr((uint32_t*)merged_nodes.data(), sizeof(MDMA_LinkNodeTypeDef) * merged_nodes.size()); + + return new_packet_id; +} \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 689c9c159..aa40038b8 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -1,5 +1,7 @@ #include "HALAL/Models/MDMA/MDMA.hpp" + + std::unordered_map MDMA::src_size_to_flags = { {1, MDMA_SRC_DATASIZE_BYTE}, {2, MDMA_SRC_DATASIZE_HALFWORD}, @@ -34,14 +36,65 @@ std::unordered_map MDMA::channel_to_instance = { }; +void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) +{ + if (instance.handle.State == HAL_MDMA_STATE_BUSY || instance.handle.Lock == HAL_LOCKED) + { + ErrorHandler("MDMA transfer already in progress"); + return; + } + + instance.handle.Lock = HAL_LOCKED; + + instance.handle.State = HAL_MDMA_STATE_BUSY; + instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; + instance.handle.FirstLinkedListNodeAddress = first_node; + + __HAL_MDMA_DISABLE(&instance.handle); + while ((instance.handle.Instance->CCR & MDMA_CCR_EN) != 0U) + { + __NOP(); + } + + MDMA_Channel_TypeDef* channel = instance.handle.Instance; + + channel->CTCR = first_node->CTCR; + channel->CBNDTR = first_node->CBNDTR; + channel->CSAR = first_node->CSAR; + channel->CDAR = first_node->CDAR; + channel->CBRUR = first_node->CBRUR; + channel->CTBR = first_node->CTBR; + channel->CMAR = first_node->CMAR; + channel->CMDR = first_node->CMDR; + channel->CLAR = first_node->CLAR; + + const uint32_t clear_flags = MDMA_FLAG_TE | MDMA_FLAG_CTC | MDMA_FLAG_BT | MDMA_FLAG_BFTC | MDMA_FLAG_BRT; + __HAL_MDMA_CLEAR_FLAG(&instance.handle, clear_flags); -uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) + __HAL_MDMA_ENABLE_IT(&instance.handle, MDMA_IT_TE | MDMA_IT_CTC); + + __HAL_MDMA_ENABLE(&instance.handle); + + if (HAL_MDMA_GenerateSWRequest(&instance.handle) != HAL_OK) + { + instance.handle.State = HAL_MDMA_STATE_READY; + instance.handle.Lock = HAL_UNLOCKED; + ErrorHandler("Error generating MDMA SW request"); + return; + } + + instance.handle.Lock = HAL_UNLOCKED; +} + + + +uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) { - uint8_t id = instances.size(); - MDMA_HandleTypeDef mdma_handle = {}; + const uint8_t id = static_cast(instances.size()); + MDMA_HandleTypeDef mdma_handle{}; mdma_handle.Instance = instance_to_channel[id]; mdma_handle.Init.Request = MDMA_REQUEST_SW; - mdma_handle.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; + mdma_handle.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; mdma_handle.Init.Priority = MDMA_PRIORITY_VERY_HIGH; mdma_handle.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; mdma_handle.Init.SourceInc = MDMA_SRC_INC_BYTE; @@ -55,41 +108,35 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer,uint8_t* destination_address) mdma_handle.Init.SourceBlockAddressOffset = 0; mdma_handle.Init.DestBlockAddressOffset = 0; + MDMA_LinkNodeConfTypeDef nodeConfig{}; + MDMA_LinkNodeTypeDef transfer_node{}; - MDMA_LinkNodeConfTypeDef nodeConfig; - MDMA_LinkNodeTypeDef transfer_node; - //Both source and destination, as well as block data length will change with the use of the transfer method, this is just a dummy initialisation - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; nodeConfig.Init.SourceBlockAddressOffset = 0; nodeConfig.Init.DestBlockAddressOffset = 0; nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - nodeConfig.Init.SourceDataSize = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; - nodeConfig.BlockDataLength = 1; - nodeConfig.SrcAddress = (uint32_t) data_buffer; - if(destination_address == nullptr) - { - nodeConfig.DstAddress = (uint32_t) data_buffer; - } - else - { - nodeConfig.DstAddress = (uint32_t) destination_address; - } - - HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.BlockDataLength = 1; + nodeConfig.SrcAddress = reinterpret_cast(data_buffer); + uint8_t* dst_ptr = (destination_address == nullptr) ? data_buffer : destination_address; + nodeConfig.DstAddress = reinterpret_cast(dst_ptr); + + const HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); if (status != HAL_OK) { ErrorHandler("Error creating linked list in MDMA"); } + Instance instance(mdma_handle, id, data_buffer, destination_address, transfer_node); instances[id] = instance; @@ -100,164 +147,36 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); - for(auto& [id, instance] : instances) + for (auto& entry : instances) { - HAL_StatusTypeDef status = HAL_MDMA_Init(&instance.handle); + Instance& instance = entry.second; + const HAL_StatusTypeDef status = HAL_MDMA_Init(&instance.handle); if (status != HAL_OK) { ErrorHandler("Error initialising MDMA instance"); } - HAL_MDMA_RegisterCallback(&instance.handle, HAL_MDMA_XFER_CPLT_CB_ID, MDMA::TransferCompleteCallback); - // status = HAL_MDMA_Start_IT(&instance.handle, (uint32_t)instance.data_buffer, (uint32_t)instance.destination_address, 1,1); - // if( status != HAL_OK) - // { - // ErrorHandler("Error starting MDMA instance"); - // } + HAL_MDMA_RegisterCallback(&instance.handle, HAL_MDMA_XFER_CPLT_CB_ID, MDMA::TransferCompleteCallback); + HAL_MDMA_RegisterCallback(&instance.handle, HAL_MDMA_XFER_ERROR_CB_ID, MDMA::TransferErrorCallback); + __HAL_MDMA_ENABLE_IT(&instance.handle, MDMA_IT_TE | MDMA_IT_CTC); } + HAL_NVIC_SetPriority(MDMA_IRQn, 0, 0); HAL_NVIC_EnableIRQ(MDMA_IRQn); - - //demas -} - -template -uint8_t MDMA::add_packet(const uint8_t MDMA_id,const std::tuple& values) -{ - Instance& instance = instances[MDMA_id]; - uint32_t offset{0}; - HAL_StatusTypeDef status; - uint8_t current_packet_id = number_of_packets++; - linked_lists[current_packet_id] = std::vector(); - std::vector& nodes = linked_lists[current_packet_id]; - MDMA_LinkNodeConfTypeDef nodeConfig; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_BLOCK_TRANSFER; - nodeConfig.Init.SourceBlockAddressOffset = 0; - nodeConfig.Init.DestBlockAddressOffset = 0; - nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_DISABLE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_DISABLE; - std::apply([&](auto... ptrs) - { - auto create_node = [&](auto ptr) - { - if (ptr == nullptr) { - ErrorHandler("Nullptr given to MDMA"); - } - - using PointerType = std::decay_t; - using UnderlyingType = std::remove_pointer_t; - constexpr size_t type_size = sizeof(UnderlyingType); - - static_assert(type_size == 1 || type_size == 2 || type_size == 4, - "MDMA::add_packet: Passed a variable with type size > 4 "); - - MDMA_LinkNodeTypeDef node = {}; - nodeConfig.SrcAddress = (uint32_t)ptr; - nodeConfig.DstAddress = (uint32_t)instance.data_buffer + offset; - nodeConfig.BlockDataLength = type_size; - nodeConfig.Init.SourceDataSize = src_size_to_flags[type_size]; - nodeConfig.Init.DestDataSize = dst_size_to_flags[type_size]; - - - status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - offset += type_size; - nodes.push_back(node); - }; - - create_node(ptrs...); - }, values); - - if (nodes.empty()) - { - ErrorHandler("Error creating linked list in MDMA"); - } - - for(size_t i = 0; i < nodes.size() - 1; i++) { - status = HAL_MDMA_LinkedList_AddNode(&nodes[i], &nodes[i+1]); - if (status != HAL_OK) - { - ErrorHandler("Error linking list nodes in MDMA"); - } - } - - MDMA_LinkNodeTypeDef node = {}; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - nodeConfig.Init.SourceDataSize = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_INC_BYTE; - nodeConfig.BlockDataLength = offset; - nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; - nodeConfig.DstAddress = (uint32_t) instance.data_buffer + offset; - - status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - nodes.push_back(node); - status = HAL_MDMA_LinkedList_AddNode(&instance.handle, &nodes[nodes.size()-2], &nodes[nodes.size()-1]); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - - return number_of_packets; } -uint8_t MDMA::merge_packets(const uint8_t base_packet_id, const auto... packets_id) +void MDMA::irq_handler() { - std::vector merged_nodes = linked_lists[base_packet_id]; - uint32_t offset = 0; - for(const auto& node : merged_nodes) + for (auto& entry : instances) { - offset += node.CBNDTR; + HAL_MDMA_IRQHandler(&entry.second.handle); } - - merged_nodes.pop_back(); //Remove last auxilary node - std::array packet_ids{static_cast(packets_id)...}; - for (size_t idx = 0; idx < packet_ids.size(); ++idx) { - uint8_t pid = packet_ids[idx]; - std::vector nodes_to_merge = linked_lists[pid]; - for(auto& node : nodes_to_merge) - { - node.CSAR = node.CSAR + offset; - offset += node.CBNDTR; - } - - if (idx != packet_ids.size() - 1) { - nodes_to_merge.pop_back(); - } - - nodes_to_merge.back().CLAR = 0; - merged_nodes.back().CLAR = (uint32_t)&nodes_to_merge[0]; - merged_nodes.insert(merged_nodes.end(), nodes_to_merge.begin(), nodes_to_merge.end()); - } - - linked_lists[number_of_packets++] = merged_nodes; - return number_of_packets; } void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address,Promise* promise) { Instance& instance = instances[MDMA_id]; - while((instance.handle.Instance->CISR & MDMA_CISR_CRQA) != 0U) - { //Active wait in case there is an error with the sinchronization - __NOP(); - } - if(promise == nullptr) { instance.using_promise = false; @@ -268,30 +187,40 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ instance.using_promise = true; } - if(destination_address != nullptr) + auto& nodes = linked_lists[packet_id]; + if (nodes.empty()) { - if(instance.destination_address == nullptr) - { - ErrorHandler("No destination address provided for MDMA transfer"); - } - std::vector& nodes = linked_lists[packet_id]; - auto& packet_transfer_node =nodes.back(); - packet_transfer_node.CDAR = (uint32_t)destination_address; + ErrorHandler("No linked list nodes for MDMA packet"); } + + uint8_t* final_destination = destination_address; + if (final_destination == nullptr) + { + final_destination = instance.destination_address; + } + + if (final_destination == nullptr) + { + ErrorHandler("No destination address provided for MDMA transfer"); + } + + nodes.back().CDAR = reinterpret_cast(final_destination); + nodes.back().CBNDTR = packet_sizes[packet_id]; + nodes.back().CSAR = reinterpret_cast(instance.data_buffer); + instance.last_destination = nodes.back().CDAR; + instance.last_size = nodes.back().CBNDTR; + - instance.handle.Instance->CLAR = (uint32_t)&linked_lists[packet_id][0]; - HAL_MDMA_GenerateSWRequest(&instance.handle); + + SCB_CleanDCache_by_Addr((uint32_t*)&nodes.back(), sizeof(MDMA_LinkNodeTypeDef) * nodes.size()); + + prepare_transfer(instance, &nodes[0]); } void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) { Instance& instance = instances[MDMA_id]; - while((instance.handle.Instance->CISR & MDMA_CISR_CRQA) != 0U) - { //Active wait in case there is an error with the sinchronization - __NOP(); - } - if(promise == nullptr) { instance.using_promise = false; @@ -302,7 +231,7 @@ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const ui instance.using_promise = true; } - instance.transfer_node.CSAR = (uint32_t)source_address; + instance.transfer_node.CSAR = reinterpret_cast(source_address); instance.transfer_node.CBNDTR = data_length; if(destination_address == nullptr) { @@ -310,28 +239,47 @@ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const ui { ErrorHandler("No destination address provided for MDMA transfer"); } - instance.transfer_node.CDAR = (uint32_t)instance.destination_address; + instance.transfer_node.CDAR = reinterpret_cast(instance.destination_address); } else { - instance.transfer_node.CDAR = (uint32_t)destination_address; + instance.transfer_node.CDAR = reinterpret_cast(destination_address); } - instance.handle.Instance->CLAR = (uint32_t)&instance.transfer_node; - - HAL_MDMA_GenerateSWRequest(&instance.handle); + SCB_CleanDCache_by_Addr((uint32_t*)&instance.transfer_node, sizeof(MDMA_LinkNodeTypeDef)); + prepare_transfer(instance, &instance.transfer_node); } void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) { Instance& instance = instances[channel_to_instance[hmdma->Instance]]; + SCB_InvalidateDCache_by_Addr((uint32_t*)instance.last_destination, instance.last_size); if(instance.using_promise) { instance.promise->resolve(); instance.using_promise = false; } + +} + +void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) +{ + Instance& instance = instances[channel_to_instance[hmdma->Instance]]; + if(instance.using_promise) + { + instance.using_promise = false; + } + + + const unsigned long error_code = static_cast(hmdma->ErrorCode); + ErrorHandler("MDMA Transfer Error, code: " + std::to_string(error_code)); +} + +extern "C" void MDMA_IRQHandler(void) +{ + MDMA::irq_handler(); } From 9937ec59b1957be6979809e77d4fe4013af7c591 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 17 Nov 2025 12:13:39 +0100 Subject: [PATCH 035/166] Promises to be tested, the rest works just fine :p --- Inc/HALAL/Models/MDMA/MDMA.hpp | 108 ++++++++++++++++----------------- Src/HALAL/Models/MDMA/MDMA.cpp | 10 +-- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 6ffd203fe..a326b17ab 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -215,82 +215,76 @@ inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple -inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id,const uint8_t base_packet_id, const PacketIds... packets_id) +inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id, const uint8_t base_packet_id, const PacketIds... packets_id) { - Instance& instance = instances[MDMA_id]; + Instance& instance = instances[MDMA_id]; - uint8_t new_packet_id = number_of_packets++; - std::vector& merged_nodes = linked_lists[new_packet_id]; + auto& base_nodes = linked_lists[base_packet_id]; + if (base_nodes.size() < 2) + { + ErrorHandler("MDMA packet merge requires packets with at least one data node"); + } - uint32_t offset = 0; + const uint32_t buffer_address = reinterpret_cast(instance.data_buffer); - std::vector base_nodes = linked_lists[base_packet_id]; - if (!base_nodes.empty()) + size_t total_data_nodes = 0; + auto count_nodes = [&](uint8_t packet_id) { - base_nodes.pop_back(); - for (const auto& node : base_nodes) + auto& nodes = linked_lists[packet_id]; + if (nodes.size() < 2) { - merged_nodes.push_back(node); - offset += node.CBNDTR; + ErrorHandler("MDMA packet merge requires packets with at least one data node"); } - } - std::array packet_ids{static_cast(packets_id)...}; - for (size_t idx = 0; idx < packet_ids.size(); ++idx) - { - uint8_t pid = packet_ids[idx]; - std::vector nodes_to_merge = linked_lists[pid]; - - if (nodes_to_merge.empty()) continue; + total_data_nodes += nodes.size() - 1; + }; - nodes_to_merge.pop_back(); + count_nodes(base_packet_id); + (count_nodes(packets_id), ...); - for (auto& node : nodes_to_merge) - { - - offset += node.CBNDTR; - node.CDAR = (uint32_t)instance.data_buffer + offset; + std::vector merged_nodes; + merged_nodes.reserve(total_data_nodes + 1); + uint32_t offset = 0; + auto append_packet = [&](uint8_t packet_id) + { + auto& nodes = linked_lists[packet_id]; + const size_t data_nodes = nodes.size() - 1; + for (size_t idx = 0; idx < data_nodes; ++idx) + { + MDMA_LinkNodeTypeDef node = nodes[idx]; + node.CDAR = buffer_address + offset; + node.CLAR = 0; merged_nodes.push_back(node); + offset += node.CBNDTR; } - } + }; - if (merged_nodes.empty()) + append_packet(base_packet_id); + (append_packet(packets_id), ...); + + if (offset == 0) { ErrorHandler("Error merging linked list in MDMA"); } - MDMA_LinkNodeTypeDef aux_node = {}; - MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; - nodeConfig.Init.SourceBlockAddressOffset = 0; - nodeConfig.Init.DestBlockAddressOffset = 0; - nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; - nodeConfig.BlockDataLength = 1; - nodeConfig.SrcAddress = (uint32_t) instance.data_buffer; - nodeConfig.DstAddress = (uint32_t) instance.data_buffer + offset; - - HAL_MDMA_LinkedList_CreateNode(&aux_node, &nodeConfig); - merged_nodes.push_back(aux_node); - - for (size_t i = 0; i < merged_nodes.size() - 1; ++i) + MDMA_LinkNodeTypeDef final_node = base_nodes.back(); + final_node.CSAR = buffer_address; + final_node.CDAR = buffer_address + offset; + final_node.CBNDTR = offset; + final_node.CLAR = 0; + merged_nodes.push_back(final_node); + + const uint8_t new_packet_id = number_of_packets++; + linked_lists[new_packet_id] = std::move(merged_nodes); + packet_sizes[new_packet_id] = offset; + + auto& stored_nodes = linked_lists[new_packet_id]; + for (size_t i = 0; i + 1 < stored_nodes.size(); ++i) { - merged_nodes[i].CLAR = reinterpret_cast(&merged_nodes[i + 1]); + stored_nodes[i].CLAR = reinterpret_cast(&stored_nodes[i + 1]); } - merged_nodes.back().CLAR = 0; - - SCB_CleanDCache_by_Addr((uint32_t*)merged_nodes.data(), sizeof(MDMA_LinkNodeTypeDef) * merged_nodes.size()); + stored_nodes.back().CLAR = 0; - return new_packet_id; + return new_packet_id; } \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index aa40038b8..b6f284328 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -196,13 +196,15 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ uint8_t* final_destination = destination_address; if (final_destination == nullptr) { + if (instance.destination_address == nullptr) + { + ErrorHandler("No destination address provided for MDMA transfer"); + } + final_destination = instance.destination_address; } - if (final_destination == nullptr) - { - ErrorHandler("No destination address provided for MDMA transfer"); - } + nodes.back().CDAR = reinterpret_cast(final_destination); nodes.back().CBNDTR = packet_sizes[packet_id]; From 37175037f460bbf7e812f16bacddd6fdfbd891b7 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 17 Nov 2025 13:53:16 +0100 Subject: [PATCH 036/166] Now checking if the mdma_id exists --- Inc/HALAL/Models/MDMA/MDMA.hpp | 12 +++++++++++- Src/HALAL/Models/MDMA/MDMA.cpp | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index a326b17ab..2d518742b 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -115,6 +115,11 @@ class MDMA{ template inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple& values) { + auto it = instances.find(MDMA_id); + if (it == instances.end()) + { + ErrorHandler("MDMA instance ID not found in add_packet"); + } Instance& instance = instances[MDMA_id]; uint32_t offset{0}; HAL_StatusTypeDef status; @@ -133,7 +138,7 @@ inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id, const uint8_t base_packet_id, const PacketIds... packets_id) { + auto it = instances.find(MDMA_id); + if (it == instances.end()) + { + ErrorHandler("MDMA instance ID not found in add_packet"); + } Instance& instance = instances[MDMA_id]; auto& base_nodes = linked_lists[base_packet_id]; diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index b6f284328..95f7e9e2a 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -90,6 +90,10 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) { + if (instances.size() >= instance_to_channel.size()) + { + ErrorHandler("Maximum number of MDMA instances reached"); + } const uint8_t id = static_cast(instances.size()); MDMA_HandleTypeDef mdma_handle{}; mdma_handle.Instance = instance_to_channel[id]; @@ -175,6 +179,11 @@ void MDMA::irq_handler() void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address,Promise* promise) { + auto it = instances.find(MDMA_id); + if (it == instances.end()) + { + ErrorHandler("MDMA instance ID not found in add_packet"); + } Instance& instance = instances[MDMA_id]; if(promise == nullptr) @@ -221,6 +230,11 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) { + auto it = instances.find(MDMA_id); + if (it == instances.end()) + { + ErrorHandler("MDMA instance ID not found in add_packet"); + } Instance& instance = instances[MDMA_id]; if(promise == nullptr) From a7f140820dc3b4567a40c1379df6b6f5e398affc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cant=C3=B3=20Catal=C3=A1n?= <144663567+Cantonplas@users.noreply.github.com> Date: Mon, 17 Nov 2025 23:44:25 +0100 Subject: [PATCH 037/166] Copy paste typo, oops Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/HALAL/Models/MDMA/MDMA.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 95f7e9e2a..fe9335aa7 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -182,7 +182,7 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ auto it = instances.find(MDMA_id); if (it == instances.end()) { - ErrorHandler("MDMA instance ID not found in add_packet"); + ErrorHandler("MDMA instance ID not found in transfer_packet"); } Instance& instance = instances[MDMA_id]; From ea8d64250749a432d067f5cdf9d6e1ad2307b703 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 20 Nov 2025 10:31:25 +0100 Subject: [PATCH 038/166] Now using array for the instances instead of unordered_maps --- Inc/HALAL/Models/MDMA/MDMA.hpp | 59 ++++++++++++++++--------- Src/HALAL/Models/MDMA/MDMA.cpp | 81 +++++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 42 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 2d518742b..80469fe3d 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -19,24 +19,51 @@ class MDMA{ private: struct Instance{ - public: + public: MDMA_HandleTypeDef handle; uint8_t id; - uint8_t *data_buffer; + uint8_t* data_buffer; uint8_t* destination_address; Promise* promise; - bool using_promise{false}; - MDMA_LinkNodeTypeDef transfer_node{}; - uint32_t last_destination{0}; - uint32_t last_size{0}; - Instance() = default; - Instance(MDMA_HandleTypeDef handle, uint8_t id, uint8_t* data_buffer, uint8_t* destination_address,MDMA_LinkNodeTypeDef transfer_node): handle(handle), id(id), data_buffer(data_buffer), destination_address(destination_address), transfer_node(transfer_node) {} + bool using_promise; + MDMA_LinkNodeTypeDef transfer_node; + uint32_t last_destination; + uint32_t last_size; + + Instance() + : handle{} + , id(0U) + , data_buffer(nullptr) + , destination_address(nullptr) + , promise(nullptr) + , using_promise(false) + , transfer_node{} + , last_destination(0U) + , last_size(0U) + {} + + Instance(MDMA_HandleTypeDef handle_, + uint8_t id_, + uint8_t* data_buffer_, + uint8_t* destination_address_, + MDMA_LinkNodeTypeDef transfer_node_) + : handle(handle_) + , id(id_) + , data_buffer(data_buffer_) + , destination_address(destination_address_) + , promise(nullptr) + , using_promise(false) + , transfer_node(transfer_node_) + , last_destination(0U) + , last_size(0U) + {} }; static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); + static Instance& get_instance(uint8_t id); inline static std::unordered_map> linked_lists{}; - inline static std::unordered_map instances{}; + inline static std::array instances{}; inline static std::unordered_map packet_sizes{}; static std::unordered_map dst_size_to_flags; static std::unordered_map src_size_to_flags; @@ -115,12 +142,7 @@ class MDMA{ template inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple& values) { - auto it = instances.find(MDMA_id); - if (it == instances.end()) - { - ErrorHandler("MDMA instance ID not found in add_packet"); - } - Instance& instance = instances[MDMA_id]; + Instance& instance = get_instance(MDMA_id); uint32_t offset{0}; HAL_StatusTypeDef status; const uint8_t current_packet_id = number_of_packets++; @@ -222,12 +244,7 @@ inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id, const uint8_t base_packet_id, const PacketIds... packets_id) { - auto it = instances.find(MDMA_id); - if (it == instances.end()) - { - ErrorHandler("MDMA instance ID not found in add_packet"); - } - Instance& instance = instances[MDMA_id]; + Instance& instance = get_instance(MDMA_id); auto& base_nodes = linked_lists[base_packet_id]; if (base_nodes.size() < 2) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index fe9335aa7..322e3b92b 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -1,5 +1,6 @@ #include "HALAL/Models/MDMA/MDMA.hpp" +#include std::unordered_map MDMA::src_size_to_flags = { @@ -88,15 +89,44 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node + +MDMA::Instance& MDMA::get_instance(uint8_t id) +{ + if (id >= instances.size()) + { + ErrorHandler("MDMA instance ID not found"); + } + + Instance& instance = instances[id]; + if (instance.handle.Instance == nullptr) + { + ErrorHandler("MDMA instance not initialised"); + } + + return instance; +} + + uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) { - if (instances.size() >= instance_to_channel.size()) + const auto slot_it = std::find_if(instances.begin(), instances.end(), [](const Instance& instance) + { + return instance.handle.Instance == nullptr; + }); + + if (slot_it == instances.end()) { ErrorHandler("Maximum number of MDMA instances reached"); } - const uint8_t id = static_cast(instances.size()); + + const uint8_t id = static_cast(slot_it - instances.begin()); MDMA_HandleTypeDef mdma_handle{}; - mdma_handle.Instance = instance_to_channel[id]; + const auto channel_it = instance_to_channel.find(id); + if (channel_it == instance_to_channel.end()) + { + ErrorHandler("MDMA channel mapping not found"); + } + mdma_handle.Instance = channel_it->second; mdma_handle.Init.Request = MDMA_REQUEST_SW; mdma_handle.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; mdma_handle.Init.Priority = MDMA_PRIORITY_VERY_HIGH; @@ -151,9 +181,12 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); - for (auto& entry : instances) + for (auto& instance : instances) { - Instance& instance = entry.second; + if (instance.handle.Instance == nullptr) + { + continue; + } const HAL_StatusTypeDef status = HAL_MDMA_Init(&instance.handle); if (status != HAL_OK) { @@ -171,20 +204,19 @@ void MDMA::start() void MDMA::irq_handler() { - for (auto& entry : instances) + for (auto& instance : instances) { - HAL_MDMA_IRQHandler(&entry.second.handle); + if (instance.handle.Instance == nullptr) + { + continue; + } + HAL_MDMA_IRQHandler(&instance.handle); } } void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address,Promise* promise) { - auto it = instances.find(MDMA_id); - if (it == instances.end()) - { - ErrorHandler("MDMA instance ID not found in transfer_packet"); - } - Instance& instance = instances[MDMA_id]; + Instance& instance = get_instance(MDMA_id); if(promise == nullptr) { @@ -230,12 +262,7 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) { - auto it = instances.find(MDMA_id); - if (it == instances.end()) - { - ErrorHandler("MDMA instance ID not found in add_packet"); - } - Instance& instance = instances[MDMA_id]; + Instance& instance = get_instance(MDMA_id); if(promise == nullptr) { @@ -270,7 +297,13 @@ void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const ui void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) { - Instance& instance = instances[channel_to_instance[hmdma->Instance]]; + const auto channel_it = channel_to_instance.find(hmdma->Instance); + if (channel_it == channel_to_instance.end()) + { + ErrorHandler("MDMA channel not registered"); + } + + Instance& instance = get_instance(channel_it->second); SCB_InvalidateDCache_by_Addr((uint32_t*)instance.last_destination, instance.last_size); if(instance.using_promise) { @@ -282,7 +315,13 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) { - Instance& instance = instances[channel_to_instance[hmdma->Instance]]; + const auto channel_it = channel_to_instance.find(hmdma->Instance); + if (channel_it == channel_to_instance.end()) + { + ErrorHandler("MDMA channel not registered"); + } + + Instance& instance = get_instance(channel_it->second); if(instance.using_promise) { instance.using_promise = false; From 7009edb675b6e75d5ee5b38c6a9e6546b2fd05d3 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 10:37:33 +0100 Subject: [PATCH 039/166] feat(Promises): Improve Stack --- Inc/C++Utilities/Stack.hpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp index e6afdd3fa..1f46fbcd7 100644 --- a/Inc/C++Utilities/Stack.hpp +++ b/Inc/C++Utilities/Stack.hpp @@ -11,29 +11,44 @@ #include "CppImports.hpp" /** - * @brief A simple fixed-size stack for arena free list management. + * @brief A simple fixed-size stack. + * @tparam T The type of elements stored in the stack. * @tparam S The maximum number of elements. */ -template +template class Stack { public: Stack() : top(0) {} - void push(size_t value) { + bool push(T value) { if (top < S) { data[top++] = value; + return true; } + return false; } - size_t pop() { - return data[--top]; + bool pop() { + if (top == 0) { + return false; + } + top--; + return true; + } + + // Returns the top element without removing it. Returns default T{} if stack is empty. + T top() const { + if (top == 0) { + return T{}; + } + return data[top - 1]; } size_t size() const { return top; } bool empty() const { return top == 0; } private: - size_t data[S]; + T data[S]; size_t top; }; From dd26669a70ed5786f8d1cf68b161392220e5bfe7 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 10:39:40 +0100 Subject: [PATCH 040/166] style(Promises): rename Arena to Pool and apply some style fixes --- Inc/C++Utilities/{Arena.hpp => Pool.hpp} | 110 +++++++++++------------ 1 file changed, 53 insertions(+), 57 deletions(-) rename Inc/C++Utilities/{Arena.hpp => Pool.hpp} (54%) diff --git a/Inc/C++Utilities/Arena.hpp b/Inc/C++Utilities/Pool.hpp similarity index 54% rename from Inc/C++Utilities/Arena.hpp rename to Inc/C++Utilities/Pool.hpp index 999d833ea..ee7b82577 100644 --- a/Inc/C++Utilities/Arena.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -1,87 +1,83 @@ /* - * Arena.hpp + * Pool.hpp * * Created on: 15 nov. 2025 * Author: Boris */ -#ifndef ARENA_HPP -#define ARENA_HPP +#ifndef POOL_HPP +#define POOL_HPP #include "CppImports.hpp" #include "Stack.hpp" /** - * @brief A simple memory arena for fixed-size allocations. + * @brief A simple memory pool for fixed-size allocations. * @note It works like a heap but with a fixed maximum number of elements, and all the items are of the same type. - * @tparam S The maximum number of elements in the arena. - * @tparam T The type of elements stored in the arena. + * @tparam S The maximum number of elements in the pool. + * @tparam T The type of elements stored in the pool. */ -template -class Arena { +template +class Pool { public: /** - * @brief Acquire an element from the arena. - * @return Pointer to the acquired element, or nullptr if the arena is full. + * @brief Acquire an element from the pool. + * @return Pointer to the acquired element, or nullptr if the pool is full (unlikely). */ T* acquire() { if (freeIndexes.empty()) { return nullptr; } - size_t index = freeIndexes.pop(); + size_t index = freeIndexes.top(); + freeIndexes.pop(); usedIndexesSet[index] = true; return &elements[index]; } /** - * @brief Acquire and construct an element in-place in the arena. + * @brief Acquire and construct an element in-place in the pool. * @param args The constructor arguments. - * @return Pointer to the constructed element, or nullptr if the arena is full. + * @return Pointer to the constructed element, or nullptr if the pool is full. */ template T* construct(Args&&... args) { - T* ele = acquire(); - if (ele) { - new (ele) T(std::forward(args)...); // Placement new + T* elem = acquire(); + if (elem) { + new (elem) T(std::forward(args)...); // Placement new } - return ele; + return elem; } /** - * @brief Release an element back to the arena. - * @param ele Pointer to the element to release. - * @return True if the element was successfully released, false otherwise. + * @brief Release an element back to the pool. + * @param elem Pointer to the element to release. + * @return True if the element was successfully released, false otherwise (unlikely with proper management). + * @note Could potentially release an element different than the original one if misused (eg. double free). */ - bool release(T* ele) { - if (ele < &elements[0] || ele >= &elements[S]) { + bool release(T* elem) { + size_t index = elem - &elements[0]; + if (index < 0 || index >= S || !usedIndexesSet[index]) { return false; } - size_t index = ele - &elements[0]; - if (!usedIndexesSet[index]) { - return false; // Double free detected - } freeIndexes.push(index); usedIndexesSet[index] = false; return true; } /** - * @brief Destroy an element and release it back to the arena. - * @param ele Pointer to the element to destroy. + * @brief Destroy an element and release it back to the pool. + * @param elem Pointer to the element to destroy. * @return True if the element was successfully destroyed and released, false otherwise. */ - bool destroy(T* ele) { - if (ele < &elements[0] || ele >= &elements[S]) { + bool destroy(T* elem) { + size_t index = elem - &elements[0]; + if (index < 0 || index >= S || !usedIndexesSet[index]) { return false; } - size_t index = ele - &elements[0]; - if (!usedIndexesSet[index]) { - return false; // Double free detected - } - ele->~T(); - return release(ele); + elem->~T(); + return release(elem); } size_t capacity() const { return S; } @@ -89,25 +85,25 @@ class Arena { size_t used() const { return S - freeIndexes.size(); } /** - * @brief Iterator for traversing used elements in the arena. + * @brief Iterator for traversing used elements in the pool. */ class Iterator { public: - Iterator(Arena* arena, size_t index) : arena(arena), index(index) { + Iterator(pool* pool, size_t index) : pool(pool), index(index) { // Skip to the first used element - while (index < S && !arena->usedIndexesSet[index]) { + while (index < S && !pool->usedIndexesSet[index]) { ++index; } this->index = index; } - T& operator*() { return arena->elements[index]; } - T* operator->() { return &arena->elements[index]; } + T& operator*() { return pool->elements[index]; } + T* operator->() { return &pool->elements[index]; } Iterator& operator++() { do { ++index; - } while (index < S && !arena->usedIndexesSet[index]); + } while (index < S && !pool->usedIndexesSet[index]); return *this; } @@ -116,30 +112,30 @@ class Arena { } private: - Arena* arena; + pool* pool; size_t index; }; /** - * @brief Const iterator for traversing used elements in the arena. + * @brief Const iterator for traversing used elements in the pool. */ class ConstIterator { public: - ConstIterator(const Arena* arena, size_t index) : arena(arena), index(index) { + ConstIterator(const pool* pool, size_t index) : pool(pool), index(index) { // Skip to the first used element - while (index < S && !arena->usedIndexesSet[index]) { + while (index < S && !pool->usedIndexesSet[index]) { ++index; } this->index = index; } - const T& operator*() const { return arena->elements[index]; } - const T* operator->() const { return &arena->elements[index]; } + const T& operator*() const { return pool->elements[index]; } + const T* operator->() const { return &pool->elements[index]; } ConstIterator& operator++() { do { ++index; - } while (index < S && !arena->usedIndexesSet[index]); + } while (index < S && !pool->usedIndexesSet[index]); return *this; } @@ -148,7 +144,7 @@ class Arena { } private: - const Arena* arena; + const Pool* pool; size_t index; }; @@ -159,21 +155,21 @@ class Arena { ConstIterator cbegin() const { return ConstIterator(this, 0); } ConstIterator cend() const { return ConstIterator(this, S); } - Arena() { + Pool() { // Push indices in reverse order so index 0 is allocated first for (int i = S - 1; i >= 0; --i) { freeIndexes.push(i); } } - Arena(const Arena&) = delete; - Arena& operator=(const Arena&) = delete; - Arena(Arena&& other) noexcept = delete; - Arena& operator=(Arena&& other) = delete; + Pool(const Pool&) = delete; + Pool& operator=(const Pool&) = delete; + Pool(Pool&& other) noexcept = delete; + Pool& operator=(Pool&& other) = delete; private: T elements[S]; - Stack freeIndexes; + Stack freeIndexes; bool usedIndexesSet[S]{false}; }; -#endif // ARENA_HPP \ No newline at end of file +#endif // POOL_HPP \ No newline at end of file From 8415bb11a0871b4693345045a49fa8138bbee705 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 10:44:51 +0100 Subject: [PATCH 041/166] fix(Promises): Fix Pool pointer checing --- Inc/C++Utilities/Pool.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index ee7b82577..69ac22954 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -57,8 +57,11 @@ class Pool { * @note Could potentially release an element different than the original one if misused (eg. double free). */ bool release(T* elem) { + if (elem - &elements[0] < 0) { + return false; + } size_t index = elem - &elements[0]; - if (index < 0 || index >= S || !usedIndexesSet[index]) { + if (index >= S || !usedIndexesSet[index]) { return false; } freeIndexes.push(index); @@ -72,8 +75,11 @@ class Pool { * @return True if the element was successfully destroyed and released, false otherwise. */ bool destroy(T* elem) { + if (elem - &elements[0] < 0) { + return false; + } size_t index = elem - &elements[0]; - if (index < 0 || index >= S || !usedIndexesSet[index]) { + if (index >= S || !usedIndexesSet[index]) { return false; } elem->~T(); From bd3eb7fc9f8aa4748af366f937f6f6de454418e1 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 11:02:27 +0100 Subject: [PATCH 042/166] feat(Promises): Fix Stack naming collision --- Inc/C++Utilities/Stack.hpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp index 1f46fbcd7..c445aed94 100644 --- a/Inc/C++Utilities/Stack.hpp +++ b/Inc/C++Utilities/Stack.hpp @@ -18,38 +18,39 @@ template class Stack { public: - Stack() : top(0) {} + Stack() : top_idx(0) {} bool push(T value) { - if (top < S) { - data[top++] = value; + if (top_idx < S) { + data[top_idx++] = value; return true; } return false; } bool pop() { - if (top == 0) { + if (top_idx == 0) { return false; } - top--; + top_idx--; return true; } // Returns the top element without removing it. Returns default T{} if stack is empty. T top() const { - if (top == 0) { + if (top_idx == 0) { return T{}; } - return data[top - 1]; + return data[top_idx - 1]; } - size_t size() const { return top; } - bool empty() const { return top == 0; } + size_t size() const { return top_idx; } + size_t capacity() const { return S; } + bool empty() const { return top_idx == 0; } private: T data[S]; - size_t top; + size_t top_idx; }; #endif // STACK_HPP \ No newline at end of file From 9e3ccb714eeff77301a329b19b425c1b05a09acf Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 11:03:14 +0100 Subject: [PATCH 043/166] feat(Promises): Use bitset in Pool. Not using bitmap to allow larger pools (either way, using a stack for allocation ensures some locality, so bit manipulation is not as useful here). --- Inc/C++Utilities/Pool.hpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index 69ac22954..9125dc13e 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -15,8 +15,8 @@ /** * @brief A simple memory pool for fixed-size allocations. * @note It works like a heap but with a fixed maximum number of elements, and all the items are of the same type. - * @tparam S The maximum number of elements in the pool. * @tparam T The type of elements stored in the pool. + * @tparam S The maximum number of elements in the pool. */ template class Pool { @@ -32,7 +32,7 @@ class Pool { } size_t index = freeIndexes.top(); freeIndexes.pop(); - usedIndexesSet[index] = true; + usedBitset.set(index); return &elements[index]; } @@ -61,11 +61,11 @@ class Pool { return false; } size_t index = elem - &elements[0]; - if (index >= S || !usedIndexesSet[index]) { + if (index >= S || !usedBitset.test(index)) { return false; } freeIndexes.push(index); - usedIndexesSet[index] = false; + usedBitset.reset(index); return true; } @@ -79,7 +79,7 @@ class Pool { return false; } size_t index = elem - &elements[0]; - if (index >= S || !usedIndexesSet[index]) { + if (index >= S || !usedBitset.test(index)) { return false; } elem->~T(); @@ -95,12 +95,11 @@ class Pool { */ class Iterator { public: - Iterator(pool* pool, size_t index) : pool(pool), index(index) { + Iterator(Pool* pool, size_t index) : pool(pool), index(index) { // Skip to the first used element - while (index < S && !pool->usedIndexesSet[index]) { + while (index < S && !pool->usedBitset.test(index)) { ++index; } - this->index = index; } T& operator*() { return pool->elements[index]; } @@ -109,7 +108,7 @@ class Pool { Iterator& operator++() { do { ++index; - } while (index < S && !pool->usedIndexesSet[index]); + } while (index < S && !pool->usedBitset.test(index)); return *this; } @@ -118,7 +117,7 @@ class Pool { } private: - pool* pool; + Pool* pool; size_t index; }; @@ -127,12 +126,11 @@ class Pool { */ class ConstIterator { public: - ConstIterator(const pool* pool, size_t index) : pool(pool), index(index) { + ConstIterator(const Pool* pool, size_t index) : pool(pool), index(index) { // Skip to the first used element - while (index < S && !pool->usedIndexesSet[index]) { + while (index < S && !pool->usedBitset.test(index)) { ++index; } - this->index = index; } const T& operator*() const { return pool->elements[index]; } @@ -141,7 +139,7 @@ class Pool { ConstIterator& operator++() { do { ++index; - } while (index < S && !pool->usedIndexesSet[index]); + } while (index < S && !pool->usedBitset.test(index)); return *this; } @@ -175,7 +173,7 @@ class Pool { private: T elements[S]; Stack freeIndexes; - bool usedIndexesSet[S]{false}; + std::bitset usedBitset; }; #endif // POOL_HPP \ No newline at end of file From 5a0bf6c3e920ab1cb37302d0709c11c8452b7f80 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 11:04:40 +0100 Subject: [PATCH 044/166] chore(Promises): Add Stack and Pool to CppUtils --- Inc/C++Utilities/CppUtils.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Inc/C++Utilities/CppUtils.hpp b/Inc/C++Utilities/CppUtils.hpp index 809e5f26f..aa421ebe5 100644 --- a/Inc/C++Utilities/CppUtils.hpp +++ b/Inc/C++Utilities/CppUtils.hpp @@ -2,6 +2,8 @@ #include "CppImports.hpp" #include "RingBuffer.hpp" +#include "Stack.hpp" +#include "Pool.hpp" namespace chrono = std::chrono; From 45df68797099bb02b017d864a459379abacc88ea Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 21 Nov 2025 11:05:19 +0100 Subject: [PATCH 045/166] fix(Promises): Change naming in Promises to acutally use Pool, instead of Arena --- Inc/HALAL/Utils/Promise.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index d17b23bb1..5dd7d3d58 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -9,9 +9,9 @@ #define PROMISE_HPP #include -#include "C++Utilities/Arena.hpp" +#include "C++Utilities/CppUtils.hpp" -// Maximum number of concurrent Promises allowed in the arena. +// Maximum number of concurrent Promises allowed in the pool. // Default is 200, which should be sufficient for most use cases. Increase if you expect higher concurrency. // You can override this value by defining PROMISE_MAX_CONCURRENT before including this header. #ifndef PROMISE_MAX_CONCURRENT @@ -27,7 +27,7 @@ /** * @brief A simple Promise implementation for asynchronous programming. - * @note Promises are allocated from a fixed-size memory arena, so you don't own the memory. Use Promise::release() to release them back to the arena if needed. + * @note Promises are allocated from a fixed-size memory pool, so you don't own the memory. Use Promise::release() to release them back to the pool if needed. */ class Promise { using Callback = void(*)(void*); @@ -46,10 +46,10 @@ class Promise { * @brief Create a new Promise. * @return Pointer to the newly created Promise, or nullptr if allocation failed. * @note The returned Promise must be released using Promise::release(). - * @note The Promise lives in a memory arena with a fixed maximum number of Promises (S), so you don't own the memory. + * @note The Promise lives in a memory pool with a fixed maximum number of Promises (S), so you don't own the memory. */ static Promise* inscribe() { - Promise* p = Promise::arena.acquire(); + Promise* p = Promise::pool.acquire(); if (!p) { return nullptr; } @@ -60,13 +60,13 @@ class Promise { } /** - * @brief Release a Promise back to the arena. + * @brief Release a Promise back to the pool. * @param p Pointer to the Promise to release. * @return True if the Promise was successfully released, false otherwise. * @note After calling this function, the Promise pointer is no longer valid and must not be used. */ static bool release(Promise* p) { - return Promise::arena.release(p); + return Promise::pool.release(p); } /** @@ -127,8 +127,8 @@ class Promise { } /** - * @brief Resolve the Promise, triggering the registered callback. Works in interruptions. - * @note Calling this after the Promise has been handled can be dangerous, as the Promise may have already been released back to the arena. Just remove the reference to the Promise after resolving it. + * @brief Resolve the Promise, triggering the registered callback. Works in interrupts. + * @note Calling this after the Promise has been handled can be dangerous, as the Promise may have already been released back to the pool. Just remove the reference to the Promise after resolving it. * @note If the Promise is already resolved and the callback has not been called yet, calling this function has no effect. */ void resolve() { @@ -151,7 +151,7 @@ class Promise { Promise* toRelease[PROMISE_MAX_UPDATES_PER_CYCLE]; uint16_t releaseCount = 0; - for (Promise& p : arena) { + for (Promise& p : pool) { if (count >= PROMISE_MAX_UPDATES_PER_CYCLE) { break; } @@ -168,7 +168,7 @@ class Promise { // Release all completed Promises after iteration for (uint16_t i = 0; i < releaseCount; i++) { - Promise::arena.release(toRelease[i]); + Promise::pool.release(toRelease[i]); } } @@ -243,8 +243,8 @@ class Promise { std::atomic state{State::Pending}; std::atomic counter{0}; Promise* next = nullptr; - static Arena arena; + static Pool pool; }; -inline Arena Promise::arena; +inline Pool Promise::pool; #endif // PROMISE_HPP \ No newline at end of file From 7894b59d9199a60048278ca5024dae0c4cdcbbf9 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 23 Nov 2025 19:18:59 +0100 Subject: [PATCH 046/166] fix(Promises): Bug fixes and changes in the releasing to ensure proper memory management --- Inc/HALAL/Utils/Promise.hpp | 52 +++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 5dd7d3d58..f47d4acc5 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -37,14 +37,15 @@ class Promise { Pending, Resolved, Ready, - Completed + Completed, + ToBeReleased }; public: /** * @brief Create a new Promise. - * @return Pointer to the newly created Promise, or nullptr if allocation failed. + * @return Pointer to the newly created Promise, or nullptr if allocation failed (unlikely). * @note The returned Promise must be released using Promise::release(). * @note The Promise lives in a memory pool with a fixed maximum number of Promises (S), so you don't own the memory. */ @@ -56,14 +57,17 @@ class Promise { p->state.store(State::Pending, std::memory_order_release); p->callback = nullptr; p->context = nullptr; + p->chainedCallback = nullptr; + p->chainedContext = nullptr; + p->counter.store(1, std::memory_order_release); return p; } /** - * @brief Release a Promise back to the pool. + * @brief Release a Promise back to the pool. Shouldn't be called manually unless you are sure the Promise is no longer needed. * @param p Pointer to the Promise to release. - * @return True if the Promise was successfully released, false otherwise. - * @note After calling this function, the Promise pointer is no longer valid and must not be used. + * @return True if the Promise was successfully released, false otherwise (shouldn't happen with proper management). + * @note After calling this function, the Promise pointer is no longer valid and must not be used. Using it after release results in undefined behavior. */ static bool release(Promise* p) { return Promise::pool.release(p); @@ -72,8 +76,8 @@ class Promise { /** * @brief Register a callback to be called when the Promise is resolved. * @param cb The callback function. - * @param ctx The context to be passed to the callback, can only be a pointer, you must manage the memory yourself. You could use an Arena for that. - * @note If the Promise is already resolved, the callback is scheduled to be called in the next update cycle. You can call then whenever you want, but only one callback can be registered per Promise. + * @param ctx The context to be passed to the callback, can only be a pointer, you must manage the memory yourself. You could use a pool for that, or just pass a this pointer. + * @note You can call then whenever you want, but only one callback can be registered per Promise. */ void then(Callback cb, void* ctx = nullptr) { callback = cb; @@ -115,9 +119,8 @@ class Promise { Promise* p = static_cast(thisPtr); Promise* chained = p->chainedCallback(p->chainedContext); chained->then(p->next->callback, p->next->context); - p->next->state.store(State::Ready, std::memory_order_release); - p->next->callback = nullptr; - p->next->context = nullptr; + p->next->state.store(State::ToBeReleased, std::memory_order_release); + p->next->counter.store(0, std::memory_order_release); }; State expected = State::Resolved; @@ -161,14 +164,18 @@ class Promise { if (p.callback) { p.callback(p.context); } + p.counter.fetch_sub(1, std::memory_order_acq_rel); + p.state.store(State::ToBeReleased, std::memory_order_release); + } + if (p.state.load(std::memory_order_acquire) == State::ToBeReleased && p.counter.load(std::memory_order_acquire) == 0) { toRelease[releaseCount++] = &p; count++; } } // Release all completed Promises after iteration - for (uint16_t i = 0; i < releaseCount; i++) { - Promise::pool.release(toRelease[i]); + for (uint16_t i = releaseCount; i > 0; i--) { + Promise::pool.release(toRelease[i-1]); } } @@ -188,12 +195,12 @@ class Promise { } auto allPromise = Promise::inscribe(); - allPromise->counter.store(sizeof...(promises), std::memory_order_release); + allPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* allPromise = static_cast(ctx); - int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 1; + int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 2; if (remaining == 0) { allPromise->resolve(); } @@ -218,11 +225,18 @@ class Promise { } auto anyPromise = Promise::inscribe(); + anyPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* anyPromise = static_cast(ctx); - anyPromise->resolve(); + anyPromise->counter.fetch_sub(1, std::memory_order_acq_rel); + State expected = State::Pending; + if (anyPromise->state.compare_exchange_strong(expected, State::Resolved, std::memory_order_acq_rel)) { + if (anyPromise->callback) { + anyPromise->state.store(State::Ready, std::memory_order_release); + } + } }, anyPromise); } return anyPromise; @@ -236,10 +250,10 @@ class Promise { Promise& operator=(const Promise&) = delete; private: - Callback callback; - ChainedCallback chainedCallback; - void* context; - void* chainedContext; + Callback callback = nullptr; + ChainedCallback chainedCallback = nullptr; + void* context = nullptr; + void* chainedContext = nullptr; std::atomic state{State::Pending}; std::atomic counter{0}; Promise* next = nullptr; From bfb28ad68b73aef20f035574ad5caffdc412c9c9 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sun, 23 Nov 2025 23:37:04 +0100 Subject: [PATCH 047/166] fix(Promises): Avoid unnecessary copies of elements in Stack --- Inc/C++Utilities/Stack.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp index c445aed94..e6716a7e2 100644 --- a/Inc/C++Utilities/Stack.hpp +++ b/Inc/C++Utilities/Stack.hpp @@ -20,7 +20,7 @@ class Stack { public: Stack() : top_idx(0) {} - bool push(T value) { + bool push(const T& value) { if (top_idx < S) { data[top_idx++] = value; return true; From bf03a1e4c34fc4eb702d2ecb1ced67770d3d6c7d Mon Sep 17 00:00:00 2001 From: Foniks Date: Sun, 23 Nov 2025 23:37:39 +0100 Subject: [PATCH 048/166] fix(Promises): Fix pointer arithmetic in Pool --- Inc/C++Utilities/Pool.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index 9125dc13e..75d81fb0e 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -57,11 +57,11 @@ class Pool { * @note Could potentially release an element different than the original one if misused (eg. double free). */ bool release(T* elem) { - if (elem - &elements[0] < 0) { + if (elem < &elements[0] || elem - &elements[0] >= S) { return false; } size_t index = elem - &elements[0]; - if (index >= S || !usedBitset.test(index)) { + if (!usedBitset.test(index)) { return false; } freeIndexes.push(index); @@ -75,11 +75,11 @@ class Pool { * @return True if the element was successfully destroyed and released, false otherwise. */ bool destroy(T* elem) { - if (elem - &elements[0] < 0) { + if (elem < &elements[0] || elem - &elements[0] >= S) { return false; } size_t index = elem - &elements[0]; - if (index >= S || !usedBitset.test(index)) { + if (!usedBitset.test(index)) { return false; } elem->~T(); From 69a0970e4c1810fc1548958783aab44ea4f18e68 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sun, 23 Nov 2025 23:38:14 +0100 Subject: [PATCH 049/166] doc(Promises): Add some better comments in Promise --- Inc/HALAL/Utils/Promise.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index f47d4acc5..d483d27a2 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -100,6 +100,7 @@ class Promise { * p1->then([](void* ctx) { * std::cout << "Promise 1 resolved!" << std::endl; * auto p2 = Promise::inscribe(); // Return a new Promise + * return p2; * // Simulate some async work * })->then([](void* ctx) { * std::cout << "Chained Promise resolved!" << std::endl; @@ -200,7 +201,7 @@ class Promise { for (Promise* p : {promises...}) { p->then([](void* ctx) { Promise* allPromise = static_cast(ctx); - int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 2; + int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 2; // -2 because fetch_sub returns the previous value, and we want to check if it is 1 after decrement (normal value for normal promises) if (remaining == 0) { allPromise->resolve(); } From bede8e66b077266080c30f8b9fa09cec3d2b439f Mon Sep 17 00:00:00 2001 From: Foniks Date: Sun, 23 Nov 2025 23:38:55 +0100 Subject: [PATCH 050/166] feat(Promises): Add null checks in Promise chaining and combinators --- Inc/HALAL/Utils/Promise.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index d483d27a2..0bff2cd12 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -119,7 +119,9 @@ class Promise { callback = [](void* thisPtr) { Promise* p = static_cast(thisPtr); Promise* chained = p->chainedCallback(p->chainedContext); - chained->then(p->next->callback, p->next->context); + if (chained) { + chained->then(p->next->callback, p->next->context); + } p->next->state.store(State::ToBeReleased, std::memory_order_release); p->next->counter.store(0, std::memory_order_release); }; @@ -196,6 +198,9 @@ class Promise { } auto allPromise = Promise::inscribe(); + if (!allPromise) { + return nullptr; + } allPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); for (Promise* p : {promises...}) { @@ -226,6 +231,9 @@ class Promise { } auto anyPromise = Promise::inscribe(); + if (!anyPromise) { + return nullptr; + } anyPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); for (Promise* p : {promises...}) { From 5f89522175f9d8390aa8685518f7a3b142825308 Mon Sep 17 00:00:00 2001 From: Foniks Date: Sun, 23 Nov 2025 23:47:04 +0100 Subject: [PATCH 051/166] fix(Promises): Fix signed and unsigned comparison in Pool --- Inc/C++Utilities/Pool.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index 75d81fb0e..e4b4e4819 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -57,7 +57,7 @@ class Pool { * @note Could potentially release an element different than the original one if misused (eg. double free). */ bool release(T* elem) { - if (elem < &elements[0] || elem - &elements[0] >= S) { + if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { return false; } size_t index = elem - &elements[0]; @@ -75,7 +75,7 @@ class Pool { * @return True if the element was successfully destroyed and released, false otherwise. */ bool destroy(T* elem) { - if (elem < &elements[0] || elem - &elements[0] >= S) { + if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { return false; } size_t index = elem - &elements[0]; From 2ce52cbd70381b771b205332f1d5604bb39b82e7 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 25 Nov 2025 11:44:36 +0100 Subject: [PATCH 052/166] feat(Promises): Add optimized bitmap iteration for Pool class when S <= 32 --- Inc/C++Utilities/Pool.hpp | 171 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index e4b4e4819..8acf333fa 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -10,11 +10,13 @@ #include "CppImports.hpp" #include "Stack.hpp" +#include /** * @brief A simple memory pool for fixed-size allocations. * @note It works like a heap but with a fixed maximum number of elements, and all the items are of the same type. + * @note Will use a optimized version for pools with small sizes (S <= 32) using bitmap and CTZ for O(used) iteration. * @tparam T The type of elements stored in the pool. * @tparam S The maximum number of elements in the pool. */ @@ -176,4 +178,173 @@ class Pool { std::bitset usedBitset; }; +// ============================================================================ +// Optimized specialization for small pools (S <= 32) using bitmap +// ============================================================================ + +/** + * @brief Optimized memory pool for small sizes (S <= 32) using bitmap and CTZ. + * @note Uses bit manipulation for O(used) iteration instead of O(S). + * @note Optimized for 32-bit systems. + * @tparam T The type of elements stored in the pool. + * @tparam S The maximum number of elements in the pool (must be <= 32). + */ +template + requires (S <= 32) +class Pool { + public: + + /** + * @brief Acquire an element from the pool. + * @return Pointer to the acquired element, or nullptr if the pool is full. + */ + T* acquire() { + if (freeIndexes.empty()) { + return nullptr; + } + size_t index = freeIndexes.top(); + freeIndexes.pop(); + usedBitmap |= (1U << index); + return &elements[index]; + } + + /** + * @brief Acquire and construct an element in-place in the pool. + * @param args The constructor arguments. + * @return Pointer to the constructed element, or nullptr if the pool is full. + */ + template + T* construct(Args&&... args) { + T* elem = acquire(); + if (elem) { + new (elem) T(std::forward(args)...); + } + return elem; + } + + /** + * @brief Release an element back to the pool. + * @param elem Pointer to the element to release. + * @return True if the element was successfully released, false otherwise. + */ + bool release(T* elem) { + if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { + return false; + } + size_t index = elem - &elements[0]; + if (!(usedBitmap & (1U << index))) { + return false; + } + freeIndexes.push(index); + usedBitmap &= ~(1U << index); + return true; + } + + /** + * @brief Destroy an element and release it back to the pool. + * @param elem Pointer to the element to destroy. + * @return True if the element was successfully destroyed and released, false otherwise. + */ + bool destroy(T* elem) { + if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { + return false; + } + size_t index = elem - &elements[0]; + if (!(usedBitmap & (1U << index))) { + return false; + } + elem->~T(); + return release(elem); + } + + size_t capacity() const { return S; } + size_t available() const { return freeIndexes.size(); } + size_t used() const { return S - freeIndexes.size(); } + + /** + * @brief Fast iterator using bitmap CTZ for O(used) iteration. + */ + class Iterator { + public: + Iterator(Pool* pool, uint32_t bitmap) : pool(pool), bitmap(bitmap) {} + + T& operator*() { + size_t index = std::countr_zero(bitmap); + return pool->elements[index]; + } + + T* operator->() { + size_t index = std::countr_zero(bitmap); + return &pool->elements[index]; + } + + Iterator& operator++() { + bitmap &= (bitmap - 1); // Clear lowest set bit + return *this; + } + + bool operator!=(const Iterator& other) const { + return bitmap != other.bitmap; + } + + private: + Pool* pool; + uint32_t bitmap; + }; + + /** + * @brief Fast const iterator using bitmap CTZ for O(used) iteration. + */ + class ConstIterator { + public: + ConstIterator(const Pool* pool, uint32_t bitmap) : pool(pool), bitmap(bitmap) {} + + const T& operator*() const { + size_t index = std::countr_zero(bitmap); + return pool->elements[index]; + } + + const T* operator->() const { + size_t index = std::countr_zero(bitmap); + return &pool->elements[index]; + } + + ConstIterator& operator++() { + bitmap &= (bitmap - 1); // Clear lowest set bit + return *this; + } + + bool operator!=(const ConstIterator& other) const { + return bitmap != other.bitmap; + } + + private: + const Pool* pool; + uint32_t bitmap; + }; + + Iterator begin() { return Iterator(this, usedBitmap); } + Iterator end() { return Iterator(this, 0); } + ConstIterator begin() const { return ConstIterator(this, usedBitmap); } + ConstIterator end() const { return ConstIterator(this, 0); } + ConstIterator cbegin() const { return ConstIterator(this, usedBitmap); } + ConstIterator cend() const { return ConstIterator(this, 0); } + + Pool() : usedBitmap(0) { + // Push indices in reverse order so index 0 is allocated first + for (int i = S - 1; i >= 0; --i) { + freeIndexes.push(i); + } + } + Pool(const Pool&) = delete; + Pool& operator=(const Pool&) = delete; + Pool(Pool&& other) noexcept = delete; + Pool& operator=(Pool&& other) = delete; + + private: + T elements[S]; + Stack freeIndexes; + uint32_t usedBitmap; // Bitmap for fast iteration (1 = used, 0 = free) +}; + #endif // POOL_HPP \ No newline at end of file From 52cbec9e5a5bd75e51d5f06fccb6f379c9981525 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 25 Nov 2025 11:48:13 +0100 Subject: [PATCH 053/166] feat(Promises): Add Promise::update() call in ST-LIB main update loop --- Src/ST-LIB.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/ST-LIB.cpp b/Src/ST-LIB.cpp index 5e591b772..cb030e2ff 100644 --- a/Src/ST-LIB.cpp +++ b/Src/ST-LIB.cpp @@ -33,5 +33,6 @@ void STLIB::update() { Server::update_servers(); #endif ErrorHandlerModel::ErrorHandlerUpdate(); + Promise::update(); } From 6ebb5ab87fae39f4bed08e71c2e23ae9efc9a4f0 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 25 Nov 2025 11:54:38 +0100 Subject: [PATCH 054/166] fix(Promises): Include Promises header in HALAL.hpp whith SIM_ON --- Inc/HALAL/HALAL.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 3ea093f07..b7421a43d 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -68,6 +68,7 @@ #include "HALALMock/Models/BoardID/BoardID.hpp" #include "HALALMock/Models/Concepts/Concepts.hpp" #include "HALALMock/Services/Logger/Logger.hpp" +#include "HALAL/Utils/Promise.hpp" #endif namespace HALAL { From 866a39c2e2288dac9279041750390f59e0b84e75 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 27 Nov 2025 10:05:53 +0100 Subject: [PATCH 055/166] feat(Promises): Add wait() method for easier busy-waiting, to use only when needed --- Inc/HALAL/Utils/Promise.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp index 0bff2cd12..aa28d64e9 100644 --- a/Inc/HALAL/Utils/Promise.hpp +++ b/Inc/HALAL/Utils/Promise.hpp @@ -251,6 +251,26 @@ class Promise { return anyPromise; } + /** + * @brief Wait for the Promise to be completed. Busy-waits until the Promise is completed. + * @param func Optional function to be called repeatedly while waiting. Can be used to perform other tasks. + * @note This function blocks until the Promise is completed. Use with caution to avoid deadlocks. + * @note After the Promise is resolved, it executes it's callback (if any) and it is automatically released back to the pool/ + */ + void wait(void (*func)() = nullptr) { + while (state.load(std::memory_order_acquire) == State::Pending) { + if (func) { + func(); + } + } + + if (callback) { + callback(context); + } + + Promise::release(this); + } + Promise() = default; ~Promise() = default; Promise(Promise&&) = delete; From 4fb9c6750436307a2ff9ed80ab587fd0cc11e340 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 27 Nov 2025 11:57:13 +0100 Subject: [PATCH 056/166] refactor(MdmaPacket): Make MdmaPacket inherit directly from Packet and use promises --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 111 ++++++++++++++++-------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 5f163cd0b..e79ea76df 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -9,58 +9,95 @@ #define MDMA_PACKET_HPP #include "HALAL/Models/Packets/Packet.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Utils/Promise.hpp" -class Mdma; // Placeholder, remove later -/** - * @brief Packet that uses MDMA to transfer its data - * @note Non-atomic write operations may lead to corrupted / half-updated data if variables change during transfer - * @note Don't use with containers of variable size unless sizes are fixed +/** + * @brief A Packet class that uses MDMA for building and parsing packets. + * @tparam Types The types of the values in the packet. + * @note This class requires MDMA and MPUManager to be properly configured. + * @note It uses non-cached memory for MDMA operations. */ -template -class MdmaPacket : public StackPacket { -public: - static_assert(!has_container::value, "MdmaPacket does not support containers"); - using Base = StackPacket; - - MdmaPacket(uint16_t id, Types*... values) = delete; +template +class MdmaPacket : public Packet { + public: + uint16_t id; + uint8_t* buffer; + size_t& size = Packet::size; + std::tuple value_pointers; - MdmaPacket(uint16_t id, Mdma& mdma, Types*... values) : Base(id, values...), mdma(mdma) { + /** + * @brief Constructor for MdmaPacket + * @param id The packet ID + * @param values Pointers to the values to be included in the packet + */ + MdmaPacket(uint16_t id, Types*... values) + : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { + packets[id] = this; + // Allocate non-cached buffer for MDMA + buffer = MPUManager::allocate_non_cached_memory(size); + } - // Inscribe MDMA list and save id or handle or whatever of the list - // (TODO) + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @return Pointer to the built packet data (internal buffer or destination address) + */ + uint8_t* build(uint8_t* destination_address = nullptr) { + Promise* promise = Promise::inscribe(); + // (TODO) Call MDMA to transfer data + promise->wait(); + return destination_address ? destination_address : buffer; } - + /** - * @brief Build the packet data into internal buffer + * @brief Build the packet and transfer data into non-cached buffer using MDMA with a promise + * @param promise Promise to be fulfilled upon transfer completion + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @return Pointer to the built packet data (internal buffer or destination address) */ + uint8_t* build(Promise* promise, uint8_t* destination_address = nullptr) { + // (TODO) Call MDMA to transfer data + return destination_address ? destination_address : buffer; + } + + // Just for interface compliance uint8_t* build() override { - // Trigger MDMA transfer with the stored MDMA instance, buffer is managed by the MDMA - // (TODO) + return build(nullptr); + } - // Fallback until MDMA is implemented - return Base::build(); + void parse(uint8_t* data = nullptr) override { + Promise* promise = Promise::inscribe(); + // (TODO) Call MDMA to transfer data + promise->wait(); } - /** - * @brief Change the MDMA instance used for transfers - */ - void set_mdma(Mdma& new_mdma) { - mdma = new_mdma; + void parse(Promise* promise, uint8_t* data = nullptr) { + // (TODO) Call MDMA to transfer data } - -private: - Mdma& mdma; - // MDMA descriptor management - // (TODO) -}; + size_t get_size() override { + return size; + } -#if __cpp_deduction_guides >= 201606 -template -MdmaPacket(uint16_t, Mdma*, Types*... values) - -> MdmaPacket::value, Types...>; + uint16_t get_id() override { + return id; + } + + // Just for interface compliance, this is not efficient for MdmaPacket as it is (could be optimized by adding more functionality to MDMA) + // Could be optimized by using a map of index to pointer or similar structure created at compile time. + void set_pointer(size_t index, void* pointer) override { + size_t current_idx = 0; -#endif + std::apply([&](auto&&... args) { + ((current_idx++ == index ? + (args = reinterpret_cast>(pointer)) + : nullptr), ...); + }, value_pointers); + } + +}; #endif // MDMA_PACKET_HPP \ No newline at end of file From 38fafa45a75b39fa8ae4b295c98c0597b2e5455f Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 27 Nov 2025 11:57:57 +0100 Subject: [PATCH 057/166] chore(MdmaPacket): Add MdmaPacket to HALAL --- Inc/HALAL/HALAL.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index b884bdd6c..955d9258f 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -41,6 +41,7 @@ #include "HALAL/Models/TimerPeripheral/TimerPeripheral.hpp" #include "HALAL/Utils/Promise.hpp" #include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/Packets/MdmaPacket.hpp" #else #include "HALALMock/Services/DigitalOutputService/DigitalOutputService.hpp" #include "HALALMock/Services/DigitalInputService/DigitalInputService.hpp" @@ -70,6 +71,7 @@ #include "HALALMock/Models/Concepts/Concepts.hpp" #include "HALALMock/Services/Logger/Logger.hpp" #include "HALAL/Utils/Promise.hpp" +#include "HALALMock/Models/Packets/MdmaPacket.hpp" #endif namespace HALAL { From e2af8c5ddb515593d8ecf4d3475cf5b9b4ad7be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sun, 30 Nov 2025 13:57:40 +0100 Subject: [PATCH 058/166] Initial structure --- .vscode/settings.json | 2 +- CMakeLists.txt | 6 +- Inc/HALAL/HALAL.hpp | 3 + Inc/HALAL/Models/GPIO.hpp | 223 ++++++++++++++++++++++++++++++++++++++ Inc/HALAL/Models/Pin.hpp | 7 ++ Inc/ST-LIB.hpp | 88 ++++++++++++++- Inc/stm32h7xx_hal_conf.h | 2 +- 7 files changed, 325 insertions(+), 6 deletions(-) create mode 100644 Inc/HALAL/Models/GPIO.hpp create mode 100644 Inc/HALAL/Models/Pin.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index d161326bb..b2abce05a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "C_Cpp.default.cppStandard": "c++20", + "C_Cpp.default.cppStandard": "c++23", "C_Cpp.default.cStandard": "c17", "C_Cpp.clang_format_fallbackStyle": "{BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 80}", "C_Cpp.formatting": "clangFormat", diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba062c27..7d35f2ef8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,7 +263,7 @@ target_compile_options(${STLIB_LIBRARY} PRIVATE $<$:-specs=nosys.specs> $<$:-ffunction-sections> $<$:-fdata-sections> - $<$:-fno-exceptions> + # $<$:-fno-exceptions> -Wno-psabi @@ -272,8 +272,8 @@ target_compile_options(${STLIB_LIBRARY} PRIVATE $<$:-Wall> $<$:-Wpedantic> $<$:-Werror> - $<$:-Wno-gnu-zero-variadic-macro-arguments> - $<$:-Wno-inconsistent-missing-override> + # $<$:-Wno-gnu-zero-variadic-macro-arguments> + # $<$:-Wno-inconsistent-missing-override> $<$:-fno-use-cxa-atexit> $<$:-fno-rtti> $<$:-Wno-address-of-packed-member> diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index d1c0438ce..316c9605a 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -1,5 +1,8 @@ #pragma once +#include "HALAL/Models/GPIO.hpp" +#include "HALAL/Models/Pin.hpp" + #include "HALAL/Models/HALconfig/HALconfig.hpp" #include "HALAL/Models/DMA/DMA.hpp" diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp new file mode 100644 index 000000000..e9bfafe5a --- /dev/null +++ b/Inc/HALAL/Models/GPIO.hpp @@ -0,0 +1,223 @@ +#pragma once + +#include "stm32h7xx_hal.h" +#include +#include +#include + +using std::array; +using std::size_t; +using std::span; +using std::tuple; + +namespace ST_LIB { +struct GPIODomain { + enum class OperationMode : uint8_t { + INPUT, + OUTPUT, + ANALOG, + EXTERNAL_INTERRUPT_RISING, + EXTERNAL_INTERRUPT_FALLING, + EXTERNAL_INTERRUPT_RISING_FALLING, + TIMER_ALTERNATE_FUNCTION, + ALTERNATIVE, + }; + enum class Port : uint8_t { A, B, C, D, E, F, G, H }; + static inline GPIO_TypeDef *port_to_reg(Port p) { + switch (p) { + case Port::A: + return GPIOA; + case Port::B: + return GPIOB; + case Port::C: + return GPIOC; + case Port::D: + return GPIOD; + case Port::E: + return GPIOE; + case Port::F: + return GPIOF; + case Port::G: + return GPIOG; + case Port::H: + return GPIOH; + default: + return nullptr; + } + } + static inline void enable_gpio_clock(Port port) { + switch (port) { + case Port::A: + __HAL_RCC_GPIOA_CLK_ENABLE(); + break; + + case Port::B: + __HAL_RCC_GPIOB_CLK_ENABLE(); + break; + + case Port::C: + __HAL_RCC_GPIOC_CLK_ENABLE(); + break; + + case Port::D: + __HAL_RCC_GPIOD_CLK_ENABLE(); + break; + + case Port::E: + __HAL_RCC_GPIOE_CLK_ENABLE(); + break; + + case Port::F: + __HAL_RCC_GPIOF_CLK_ENABLE(); + break; + + case Port::G: + __HAL_RCC_GPIOG_CLK_ENABLE(); + break; + + case Port::H: + __HAL_RCC_GPIOH_CLK_ENABLE(); + break; + } + } + + struct Pin2 { + GPIODomain::Port port; + uint32_t pin; + + consteval Pin2(GPIODomain::Port port, uint32_t pin) + : port(port), pin(pin) {} + }; + + struct Entry { + size_t id; + Port port; + uint32_t pin; + OperationMode mode; + }; + + struct GPIO { + using domain = GPIODomain; + + Entry e; + + consteval GPIO(std::size_t id, Pin2 pin, OperationMode mode) + : e(id, pin.port, pin.pin, mode) {} + + template consteval void inscribe(Ctx &ctx) const { + ctx.template add(e); + } + }; + + static constexpr std::size_t max_instances{110}; + static_assert(max_instances > 0, + "The number of instances must be greater than 0"); + + struct Config { + size_t id; + std::tuple init_data{}; + }; + + template + static consteval array build(span pins) { + array cfgs{}; + for (std::size_t i = 0; i < N; ++i) { + const auto &e = pins[i]; + + for (std::size_t j = 0; j < i; ++j) { + const auto &prev = pins[j]; + if (prev.pin == e.pin) { + struct gpio_already_inscribed {}; + throw gpio_already_inscribed{}; + } + } + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.Pin = e.pin; + switch (e.mode) { + + case OperationMode::OUTPUT: + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + break; + + case OperationMode::INPUT: + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_NOPULL; + break; + + case OperationMode::ANALOG: + GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; + GPIO_InitStruct.Pull = GPIO_NOPULL; + break; + case OperationMode::EXTERNAL_INTERRUPT_RISING: + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + break; + case OperationMode::EXTERNAL_INTERRUPT_FALLING: + GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + break; + case OperationMode::EXTERNAL_INTERRUPT_RISING_FALLING: + GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + break; + // case OperationMode::TIMER_ALTERNATE_FUNCTION: + // GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + // GPIO_InitStruct.Pull = GPIO_NOPULL; + // GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + // GPIO_InitStruct.Alternate = pin.alternative_function; + // break; + + default: + break; + } + + cfgs[i].id = e.id; + cfgs[i].init_data = std::make_tuple(e.port, GPIO_InitStruct); + } + + return cfgs; + } + + // Runtime object + struct Instance { + GPIO_TypeDef *port; + uint16_t pin; + + void turn_on() { HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); } + + void turn_off() { HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); } + + void toggle() { HAL_GPIO_TogglePin(port, pin); } + }; + +private: + inline static Instance *instances_ptr = nullptr; + +public: + static Instance &instance(std::size_t id) { return instances_ptr[id]; } + + template struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs) { + static_assert(N > 0); + for (std::size_t i = 0; i < N; ++i) { + const auto &e = cfgs[i]; + auto [port, gpio_init] = e.init_data; + + enable_gpio_clock(port); + HAL_GPIO_Init(port_to_reg(port), &gpio_init); + + auto &inst = instances[e.id]; + inst.port = port_to_reg(port); + inst.pin = gpio_init.Pin; + } + + instances_ptr = instances.data(); + } + }; +}; +} // namespace ST_LIB \ No newline at end of file diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp new file mode 100644 index 000000000..987e689db --- /dev/null +++ b/Inc/HALAL/Models/Pin.hpp @@ -0,0 +1,7 @@ +#include "HALAL/Models/GPIO.hpp" + +namespace ST_LIB { +constexpr GPIODomain::Pin2 PA0{GPIODomain::Port::A, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PA1{GPIODomain::Port::A, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PB0{GPIODomain::Port::B, GPIO_PIN_0}; +} // namespace ST_LIB \ No newline at end of file diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 0736c791d..68e4efaf6 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -22,4 +22,90 @@ class STLIB { #endif static void update(); -}; \ No newline at end of file +}; + +namespace ST_LIB { +template struct BuildCtx { + template using Decl = typename D::Entry; + template + static constexpr std::size_t max_count_v = D::max_instances; + + std::tuple, max_count_v>...> storage{}; + std::array sizes{}; + + template + static consteval std::size_t domain_index() { + if constexpr (I >= sizeof...(Domains)) { + static_assert([] { return false; }(), "Domain not found"); + return 0; + } else if constexpr (std::is_same_v>>) { + return I; + } else { + return domain_index(); + } + } + + template consteval void add(typename D::Entry e) { + constexpr std::size_t I = domain_index(); + auto &arr = std::get(storage); + auto &size = sizes[I]; + arr[size++] = e; + } + + template consteval auto span() const { + constexpr std::size_t I = domain_index(); + auto const &arr = std::get(storage); + auto const size = sizes[I]; + using E = typename D::Entry; + return std::span{arr.data(), size}; + } +}; + +template consteval std::size_t domain_count() { + return (... + + (std::is_same_v ? 1u : 0u)); +} + +using DomainsCtx = BuildCtx; + +// Configure HW (compile-time) +template consteval auto build() { + DomainsCtx ctx{}; + + (devs.inscribe(ctx), ...); + + constexpr std::size_t gpioN = domain_count(); + // constexpr std::size_t adcN = domain_count(); + // constexpr std::size_t pwmN = domain_count(); + + struct ConfigBundle { + array gpio_cfgs; + // array adc_cgfs; + // array pwm_cgfs; + }; + + return ConfigBundle{ + .gpio_cfgs = + GPIODomain::template build(ctx.template span()), + // .adc_cgfs = + // ADCDomain::template build(ctx.template span()), + // .pwm_cgfs = + // PWMDomain::template build(ctx.template span()), + }; +} + +// Init real HW (runtime) +template void init() { + static constexpr auto cfg = build(); + + constexpr std::size_t gpioN = domain_count(); + // constexpr std::size_t adcN = domain_count(); + // constexpr std::size_t pwmN = domain_count(); + + GPIODomain::Init::init(cfg.gpio_cfgs); + // ADCDomain::Init::init(cfg.adc_cfgs); + // PWMDomain::Init::init(cfg.pwm_cfgs); +} + +} // namespace ST_LIB \ No newline at end of file diff --git a/Inc/stm32h7xx_hal_conf.h b/Inc/stm32h7xx_hal_conf.h index 574000009..74fdc0c32 100644 --- a/Inc/stm32h7xx_hal_conf.h +++ b/Inc/stm32h7xx_hal_conf.h @@ -233,7 +233,7 @@ * @brief Uncomment the line below to expanse the "assert_param" macro in the * HAL drivers code */ -/* #define USE_FULL_ASSERT 1 */ +#define USE_FULL_ASSERT 1 /* Includes ------------------------------------------------------------------*/ From 3eec305d09b320a092d10579cbef5926eb67c0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Sun, 30 Nov 2025 15:50:54 +0100 Subject: [PATCH 059/166] no need to have an id for instance --- Inc/HALAL/Models/GPIO.hpp | 10 +-- Inc/HALAL/Models/Pin.hpp | 148 +++++++++++++++++++++++++++++++++++++- Inc/ST-LIB.hpp | 126 ++++++++++++++++++++++---------- 3 files changed, 237 insertions(+), 47 deletions(-) diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index e9bfafe5a..09da6a27d 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -90,7 +90,6 @@ struct GPIODomain { }; struct Entry { - size_t id; Port port; uint32_t pin; OperationMode mode; @@ -101,8 +100,7 @@ struct GPIODomain { Entry e; - consteval GPIO(std::size_t id, Pin2 pin, OperationMode mode) - : e(id, pin.port, pin.pin, mode) {} + consteval GPIO(Pin2 pin, OperationMode mode) : e(pin.port, pin.pin, mode) {} template consteval void inscribe(Ctx &ctx) const { ctx.template add(e); @@ -114,7 +112,6 @@ struct GPIODomain { "The number of instances must be greater than 0"); struct Config { - size_t id; std::tuple init_data{}; }; @@ -126,7 +123,7 @@ struct GPIODomain { for (std::size_t j = 0; j < i; ++j) { const auto &prev = pins[j]; - if (prev.pin == e.pin) { + if (prev.pin == e.pin && prev.port == e.port) { struct gpio_already_inscribed {}; throw gpio_already_inscribed{}; } @@ -174,7 +171,6 @@ struct GPIODomain { break; } - cfgs[i].id = e.id; cfgs[i].init_data = std::make_tuple(e.port, GPIO_InitStruct); } @@ -211,7 +207,7 @@ struct GPIODomain { enable_gpio_clock(port); HAL_GPIO_Init(port_to_reg(port), &gpio_init); - auto &inst = instances[e.id]; + auto &inst = instances[i]; inst.port = port_to_reg(port); inst.pin = gpio_init.Pin; } diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp index 987e689db..fd598fcd0 100644 --- a/Inc/HALAL/Models/Pin.hpp +++ b/Inc/HALAL/Models/Pin.hpp @@ -1,7 +1,149 @@ #include "HALAL/Models/GPIO.hpp" namespace ST_LIB { -constexpr GPIODomain::Pin2 PA0{GPIODomain::Port::A, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PA1{GPIODomain::Port::A, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PB0{GPIODomain::Port::B, GPIO_PIN_0}; + +// Port A +constexpr GPIODomain::Pin2 PA0 {GPIODomain::Port::A, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PA1 {GPIODomain::Port::A, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PA2 {GPIODomain::Port::A, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PA3 {GPIODomain::Port::A, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PA4 {GPIODomain::Port::A, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PA5 {GPIODomain::Port::A, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PA6 {GPIODomain::Port::A, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PA7 {GPIODomain::Port::A, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PA8 {GPIODomain::Port::A, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PA9 {GPIODomain::Port::A, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PA10{GPIODomain::Port::A, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PA11{GPIODomain::Port::A, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PA12{GPIODomain::Port::A, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PA13{GPIODomain::Port::A, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PA14{GPIODomain::Port::A, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PA15{GPIODomain::Port::A, GPIO_PIN_15}; + +// Port B +constexpr GPIODomain::Pin2 PB0 {GPIODomain::Port::B, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PB1 {GPIODomain::Port::B, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PB2 {GPIODomain::Port::B, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PB3 {GPIODomain::Port::B, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PB4 {GPIODomain::Port::B, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PB5 {GPIODomain::Port::B, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PB6 {GPIODomain::Port::B, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PB7 {GPIODomain::Port::B, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PB8 {GPIODomain::Port::B, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PB9 {GPIODomain::Port::B, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PB10{GPIODomain::Port::B, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PB11{GPIODomain::Port::B, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PB12{GPIODomain::Port::B, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PB13{GPIODomain::Port::B, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PB14{GPIODomain::Port::B, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PB15{GPIODomain::Port::B, GPIO_PIN_15}; + +// Port C +constexpr GPIODomain::Pin2 PC0 {GPIODomain::Port::C, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PC1 {GPIODomain::Port::C, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PC2 {GPIODomain::Port::C, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PC3 {GPIODomain::Port::C, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PC4 {GPIODomain::Port::C, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PC5 {GPIODomain::Port::C, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PC6 {GPIODomain::Port::C, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PC7 {GPIODomain::Port::C, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PC8 {GPIODomain::Port::C, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PC9 {GPIODomain::Port::C, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PC10{GPIODomain::Port::C, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PC11{GPIODomain::Port::C, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PC12{GPIODomain::Port::C, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PC13{GPIODomain::Port::C, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PC14{GPIODomain::Port::C, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PC15{GPIODomain::Port::C, GPIO_PIN_15}; + +// Port D +constexpr GPIODomain::Pin2 PD0 {GPIODomain::Port::D, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PD1 {GPIODomain::Port::D, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PD2 {GPIODomain::Port::D, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PD3 {GPIODomain::Port::D, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PD4 {GPIODomain::Port::D, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PD5 {GPIODomain::Port::D, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PD6 {GPIODomain::Port::D, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PD7 {GPIODomain::Port::D, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PD8 {GPIODomain::Port::D, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PD9 {GPIODomain::Port::D, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PD10{GPIODomain::Port::D, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PD11{GPIODomain::Port::D, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PD12{GPIODomain::Port::D, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PD13{GPIODomain::Port::D, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PD14{GPIODomain::Port::D, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PD15{GPIODomain::Port::D, GPIO_PIN_15}; + +// Port E +constexpr GPIODomain::Pin2 PE0 {GPIODomain::Port::E, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PE1 {GPIODomain::Port::E, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PE2 {GPIODomain::Port::E, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PE3 {GPIODomain::Port::E, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PE4 {GPIODomain::Port::E, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PE5 {GPIODomain::Port::E, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PE6 {GPIODomain::Port::E, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PE7 {GPIODomain::Port::E, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PE8 {GPIODomain::Port::E, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PE9 {GPIODomain::Port::E, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PE10{GPIODomain::Port::E, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PE11{GPIODomain::Port::E, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PE12{GPIODomain::Port::E, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PE13{GPIODomain::Port::E, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PE14{GPIODomain::Port::E, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PE15{GPIODomain::Port::E, GPIO_PIN_15}; + +// Port F +constexpr GPIODomain::Pin2 PF0 {GPIODomain::Port::F, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PF1 {GPIODomain::Port::F, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PF2 {GPIODomain::Port::F, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PF3 {GPIODomain::Port::F, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PF4 {GPIODomain::Port::F, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PF5 {GPIODomain::Port::F, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PF6 {GPIODomain::Port::F, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PF7 {GPIODomain::Port::F, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PF8 {GPIODomain::Port::F, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PF9 {GPIODomain::Port::F, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PF10{GPIODomain::Port::F, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PF11{GPIODomain::Port::F, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PF12{GPIODomain::Port::F, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PF13{GPIODomain::Port::F, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PF14{GPIODomain::Port::F, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PF15{GPIODomain::Port::F, GPIO_PIN_15}; + +// Port G +constexpr GPIODomain::Pin2 PG0 {GPIODomain::Port::G, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PG1 {GPIODomain::Port::G, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PG2 {GPIODomain::Port::G, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PG3 {GPIODomain::Port::G, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PG4 {GPIODomain::Port::G, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PG5 {GPIODomain::Port::G, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PG6 {GPIODomain::Port::G, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PG7 {GPIODomain::Port::G, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PG8 {GPIODomain::Port::G, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PG9 {GPIODomain::Port::G, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PG10{GPIODomain::Port::G, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PG11{GPIODomain::Port::G, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PG12{GPIODomain::Port::G, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PG13{GPIODomain::Port::G, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PG14{GPIODomain::Port::G, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PG15{GPIODomain::Port::G, GPIO_PIN_15}; + +// Port H +constexpr GPIODomain::Pin2 PH0 {GPIODomain::Port::H, GPIO_PIN_0}; +constexpr GPIODomain::Pin2 PH1 {GPIODomain::Port::H, GPIO_PIN_1}; +constexpr GPIODomain::Pin2 PH2 {GPIODomain::Port::H, GPIO_PIN_2}; +constexpr GPIODomain::Pin2 PH3 {GPIODomain::Port::H, GPIO_PIN_3}; +constexpr GPIODomain::Pin2 PH4 {GPIODomain::Port::H, GPIO_PIN_4}; +constexpr GPIODomain::Pin2 PH5 {GPIODomain::Port::H, GPIO_PIN_5}; +constexpr GPIODomain::Pin2 PH6 {GPIODomain::Port::H, GPIO_PIN_6}; +constexpr GPIODomain::Pin2 PH7 {GPIODomain::Port::H, GPIO_PIN_7}; +constexpr GPIODomain::Pin2 PH8 {GPIODomain::Port::H, GPIO_PIN_8}; +constexpr GPIODomain::Pin2 PH9 {GPIODomain::Port::H, GPIO_PIN_9}; +constexpr GPIODomain::Pin2 PH10{GPIODomain::Port::H, GPIO_PIN_10}; +constexpr GPIODomain::Pin2 PH11{GPIODomain::Port::H, GPIO_PIN_11}; +constexpr GPIODomain::Pin2 PH12{GPIODomain::Port::H, GPIO_PIN_12}; +constexpr GPIODomain::Pin2 PH13{GPIODomain::Port::H, GPIO_PIN_13}; +constexpr GPIODomain::Pin2 PH14{GPIODomain::Port::H, GPIO_PIN_14}; +constexpr GPIODomain::Pin2 PH15{GPIODomain::Port::H, GPIO_PIN_15}; + } // namespace ST_LIB \ No newline at end of file diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 68e4efaf6..2f416eb5f 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -62,50 +62,102 @@ template struct BuildCtx { } }; -template consteval std::size_t domain_count() { +template consteval std::size_t domain_count() { return (... + - (std::is_same_v ? 1u : 0u)); + (std::is_same_v::domain, + Domain> + ? 1u + : 0u)); } using DomainsCtx = BuildCtx; -// Configure HW (compile-time) -template consteval auto build() { - DomainsCtx ctx{}; - - (devs.inscribe(ctx), ...); - - constexpr std::size_t gpioN = domain_count(); - // constexpr std::size_t adcN = domain_count(); - // constexpr std::size_t pwmN = domain_count(); - - struct ConfigBundle { - array gpio_cfgs; - // array adc_cgfs; - // array pwm_cgfs; - }; - - return ConfigBundle{ - .gpio_cfgs = - GPIODomain::template build(ctx.template span()), - // .adc_cgfs = - // ADCDomain::template build(ctx.template span()), - // .pwm_cgfs = - // PWMDomain::template build(ctx.template span()), - }; -} +template struct Board { + // ========== build compile-time ========== + static consteval auto build() { + DomainsCtx ctx{}; + + (devs.inscribe(ctx), ...); + + constexpr std::size_t gpioN = domain_count(); + // constexpr std::size_t adcN = domain_count(); + // constexpr std::size_t pwmN = domain_count(); + + struct ConfigBundle { + std::array gpio_cfgs; + // std::array adc_cgfs; + // std::array pwm_cgfs; + }; + + return ConfigBundle{ + .gpio_cfgs = + GPIODomain::template build(ctx.template span()), + // .adc_cgfs = + // ADCDomain::template build(ctx.template span()), + // .pwm_cgfs = + // PWMDomain::template build(ctx.template span()), + }; + } -// Init real HW (runtime) -template void init() { - static constexpr auto cfg = build(); + // ========== init runtime ========== + static void init() { + static constexpr auto cfg = build(); - constexpr std::size_t gpioN = domain_count(); - // constexpr std::size_t adcN = domain_count(); - // constexpr std::size_t pwmN = domain_count(); + constexpr std::size_t gpioN = domain_count(); + // constexpr std::size_t adcN = domain_count(); + // constexpr std::size_t pwmN = domain_count(); - GPIODomain::Init::init(cfg.gpio_cfgs); - // ADCDomain::Init::init(cfg.adc_cfgs); - // PWMDomain::Init::init(cfg.pwm_cfgs); -} + GPIODomain::Init::init(cfg.gpio_cfgs); + // ADCDomain::Init::init(cfg.adc_cfgs); + // PWMDomain::Init::init(cfg.pwm_cfgs); + } + + template static consteval std::size_t domain_size() { + return domain_count(); + } + + template + static consteval std::size_t domain_index_of_impl() { + std::size_t idx = 0; + bool found = false; + + ( + [&] { + using DevT = std::remove_cvref_t; + if constexpr (std::is_same_v) { + if (!found) { + if (&devs == &Target) { + found = true; + } else { + ++idx; + } + } + } + }(), + ...); + + if (!found) { + struct device_not_found_for_domain {}; + throw device_not_found_for_domain{}; + } + + return idx; + } + + template + static consteval std::size_t domain_index_of() { + return domain_index_of_impl(); + } + + template static auto &instance_of() { + using DevT = std::remove_cvref_t; + using Domain = typename DevT::domain; + + constexpr std::size_t idx = domain_index_of(); + constexpr std::size_t N = domain_size(); + + return Domain::template Init::instances[idx]; + } +}; } // namespace ST_LIB \ No newline at end of file From 644c34e00899c142557df5998ea973a3f8a69790 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 1 Dec 2025 09:30:05 +0100 Subject: [PATCH 060/166] feat(MDMA)!: Make MDMA use a pool of linked list nodes managed externally instead of managing them itself --- Inc/C++Utilities/Pool.hpp | 63 +++++-- Inc/HALAL/Models/MDMA/MDMA.hpp | 292 ++++++++------------------------- Src/HALAL/Models/MDMA/MDMA.cpp | 86 +++------- 3 files changed, 145 insertions(+), 296 deletions(-) diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp index 8acf333fa..933c5d5b6 100644 --- a/Inc/C++Utilities/Pool.hpp +++ b/Inc/C++Utilities/Pool.hpp @@ -11,6 +11,7 @@ #include "CppImports.hpp" #include "Stack.hpp" #include +#include /** @@ -19,8 +20,9 @@ * @note Will use a optimized version for pools with small sizes (S <= 32) using bitmap and CTZ for O(used) iteration. * @tparam T The type of elements stored in the pool. * @tparam S The maximum number of elements in the pool. + * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. */ -template +template class Pool { public: @@ -161,19 +163,34 @@ class Pool { ConstIterator cbegin() const { return ConstIterator(this, 0); } ConstIterator cend() const { return ConstIterator(this, S); } - Pool() { - // Push indices in reverse order so index 0 is allocated first - for (int i = S - 1; i >= 0; --i) { - freeIndexes.push(i); - } + Pool() requires (!ExternalMemory) { + if constexpr (!ExternalMemory) elements = storage.data; + init(); } + + Pool(T* buffer) requires (ExternalMemory) : elements(buffer) { + init(); + } + Pool(const Pool&) = delete; Pool& operator=(const Pool&) = delete; Pool(Pool&& other) noexcept = delete; Pool& operator=(Pool&& other) = delete; private: - T elements[S]; + void init() { + // Push indices in reverse order so index 0 is allocated first + for (int i = S - 1; i >= 0; --i) { + freeIndexes.push(i); + } + } + + struct InternalStorage { T data[S]; }; + struct ExternalStorage {}; + + [[no_unique_address]] std::conditional_t storage; + T* elements; + Stack freeIndexes; std::bitset usedBitset; }; @@ -188,10 +205,11 @@ class Pool { * @note Optimized for 32-bit systems. * @tparam T The type of elements stored in the pool. * @tparam S The maximum number of elements in the pool (must be <= 32). + * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. */ -template +template requires (S <= 32) -class Pool { +class Pool { public: /** @@ -330,19 +348,34 @@ class Pool { ConstIterator cbegin() const { return ConstIterator(this, usedBitmap); } ConstIterator cend() const { return ConstIterator(this, 0); } - Pool() : usedBitmap(0) { - // Push indices in reverse order so index 0 is allocated first - for (int i = S - 1; i >= 0; --i) { - freeIndexes.push(i); - } + Pool() requires (!ExternalMemory) : usedBitmap(0) { + if constexpr (!ExternalMemory) elements = storage.data; + init(); } + + Pool(T* buffer) requires (ExternalMemory) : elements(buffer), usedBitmap(0) { + init(); + } + Pool(const Pool&) = delete; Pool& operator=(const Pool&) = delete; Pool(Pool&& other) noexcept = delete; Pool& operator=(Pool&& other) = delete; private: - T elements[S]; + void init() { + // Push indices in reverse order so index 0 is allocated first + for (int i = S - 1; i >= 0; --i) { + freeIndexes.push(i); + } + } + + struct InternalStorage { T data[S]; }; + struct ExternalStorage {}; + + [[no_unique_address]] std::conditional_t storage; + T* elements; + Stack freeIndexes; uint32_t usedBitmap; // Bitmap for fast iteration (1 = used, 0 = free) }; diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 80469fe3d..b6abadf6f 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -4,6 +4,7 @@ #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" #include "HALAL/Utils/Promise.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" #include #include #include @@ -15,7 +16,13 @@ #undef MDMA #endif +#ifndef NODES_MAX +#define NODES_MAX 500 +#endif + class MDMA{ + public: + struct LinkedListNode; private: struct Instance{ @@ -26,9 +33,7 @@ class MDMA{ uint8_t* destination_address; Promise* promise; bool using_promise; - MDMA_LinkNodeTypeDef transfer_node; - uint32_t last_destination; - uint32_t last_size; + MDMA::LinkedListNode transfer_node; Instance() : handle{} @@ -38,8 +43,6 @@ class MDMA{ , promise(nullptr) , using_promise(false) , transfer_node{} - , last_destination(0U) - , last_size(0U) {} Instance(MDMA_HandleTypeDef handle_, @@ -54,29 +57,79 @@ class MDMA{ , promise(nullptr) , using_promise(false) , transfer_node(transfer_node_) - , last_destination(0U) - , last_size(0U) {} }; - static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); + static void prepare_transfer(Instance& instance, MDMA::LinkedListNode& first_node); static Instance& get_instance(uint8_t id); - inline static std::unordered_map> linked_lists{}; inline static std::array instances{}; - inline static std::unordered_map packet_sizes{}; - static std::unordered_map dst_size_to_flags; - static std::unordered_map src_size_to_flags; static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; - inline static uint8_t number_of_packets{0}; - static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); public: + + /** + * @brief A helper struct to create and manage MDMA linked list nodes. + */ + struct LinkedListNode { + /** + * @brief Constructor to create an MDMA linked list node. + * @tparam T The type of the data to be transferred. + * @param source_ptr Pointer to the source data. + * @param dest_ptr Pointer to the destination data. + */ + template + LinkedListNode(T* source_ptr, void* dest_ptr) { + MDMA_LinkNodeConfTypeDef nodeConfig; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + + this->node = {}; + nodeConfig.SrcAddress = reinterpret_cast(source_ptr); + nodeConfig.DstAddress = reinterpret_cast(dest_ptr); + nodeConfig.BlockDataLength = static_cast(sizeof(T)); + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + + auto status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) { + ErrorHandler("Error creating linked list in MDMA"); + } + } + + /** + * @brief Set the next node in the linked list. + */ + void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } + auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } + auto get_size() -> uint32_t { return node.CBNDTR; } + auto get_destination() -> uint32_t { return node.CDAR; } + auto get_source() -> uint32_t { return node.CSAR; } + auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } + + private: + MDMA_LinkNodeTypeDef node; + }; + + // Pool for MDMA_LinkNodeTypeDef, uses external non-cached memory + static Pool link_node_pool; + static void start(); static void irq_handler(); @@ -94,29 +147,6 @@ class MDMA{ static uint8_t inscribe(uint8_t* data_buffer, uint8_t* destination_address=nullptr); - /** - * @brief A method to create a linked list from a tuple of pointers that come from packets, this linked list are global for all instances. - - * @param MDMA_id the id that represents the MDMA channel instance - * @param values the tuple of pointers that will be added to the linked list - * - * @return the id that represents the linked list associated to the packet. - */ - template - static uint8_t add_packet(const uint8_t MDMA_id,const std::tuple& values); - - /** - * @brief A method to merge multiple packets into a single linked list. - * - * @param MDMA_id The ID of the MDMA channel instance. - * @param base_packet_id The ID of the base packet to which other packets will be merged. - * @param packets_id The IDs of the packets to be merged into the base packet. - * - * @return The ID of the merged linked list. - */ - template - static uint8_t merge_packets(const uint8_t MDMA_id,const uint8_t base_packet_id, const PacketIds... packets_id); - /** * @brief A method to start a transfer from source to destination using MDMA linked list * @@ -125,193 +155,15 @@ class MDMA{ * @param destination_address The destination address for the transfer. If nullptr, the default destination associated with the instance will be used. * @param promise An optional promise to be fulfilled upon transfer completion. */ - static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address=nullptr,Promise* promise=nullptr); + static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address=nullptr, Promise* promise=nullptr); /** - * @brief A method to transfer a packet using MDMA linked list + * @brief A method to transfer using MDMA linked * * @param MDMA_id The ID of the MDMA channel instance. - * @param packet_id The ID of the linked list packet to be transferred. - * @param destination_address The destination address for the transfer. If nullptr, the default destination associated with the instance will be used. + * @param first_node The linked list node representing the first node in the linked list. * @param promise An optional promise to be fulfilled upon transfer completion. */ - static void transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id, uint8_t* destination_address=nullptr, Promise* promise=nullptr); - -}; - -template -inline uint8_t MDMA::add_packet(const uint8_t MDMA_id, const std::tuple& values) -{ - Instance& instance = get_instance(MDMA_id); - uint32_t offset{0}; - HAL_StatusTypeDef status; - const uint8_t current_packet_id = number_of_packets++; - - std::vector& nodes = linked_lists[current_packet_id]; - nodes.clear(); - nodes.reserve(sizeof...(pointers) + 5U); - - MDMA_LinkNodeConfTypeDef nodeConfig; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; - nodeConfig.Init.SourceBlockAddressOffset = 0; - nodeConfig.Init.DestBlockAddressOffset = 0; - nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - - std::apply([ - &nodes, - &nodeConfig, - &offset, - &instance, - &status - ](auto... ptrs) - { - auto create_node = [&](auto ptr) - { - if (ptr == nullptr) - { - ErrorHandler("Nullptr given to MDMA"); - } - - using PointerType = std::decay_t; - using UnderlyingType = std::remove_pointer_t; - constexpr size_t type_size = sizeof(UnderlyingType); - - static_assert(type_size == 1 || type_size == 2 || type_size == 4, - "MDMA::add_packet: Passed a variable with type size > 4 "); - - MDMA_LinkNodeTypeDef node = {}; - nodeConfig.SrcAddress = reinterpret_cast(ptr); - nodeConfig.DstAddress = reinterpret_cast(instance.data_buffer) + offset; - nodeConfig.BlockDataLength = static_cast(type_size); - nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - - status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - - offset += type_size; - nodes.push_back(node); - }; - - (create_node(ptrs), ...); - }, values); - - if (nodes.empty()) - { - ErrorHandler("Error creating linked list in MDMA"); - } - - MDMA_LinkNodeTypeDef node = {}; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; - nodeConfig.BlockDataLength = offset; - nodeConfig.SrcAddress = reinterpret_cast(instance.data_buffer); - nodeConfig.DstAddress = reinterpret_cast(instance.data_buffer) + offset; - - status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) - { - ErrorHandler("Error creating linked list in MDMA"); - } - - nodes.push_back(node); - - packet_sizes[current_packet_id] = offset; - - for (size_t i = 0; i + 1 < nodes.size(); ++i) - { - nodes[i].CLAR = reinterpret_cast(&nodes[i + 1]); - } - nodes.back().CLAR = 0; - - return current_packet_id; -} - -template -inline uint8_t MDMA::merge_packets(const uint8_t MDMA_id, const uint8_t base_packet_id, const PacketIds... packets_id) -{ - Instance& instance = get_instance(MDMA_id); - - auto& base_nodes = linked_lists[base_packet_id]; - if (base_nodes.size() < 2) - { - ErrorHandler("MDMA packet merge requires packets with at least one data node"); - } - - const uint32_t buffer_address = reinterpret_cast(instance.data_buffer); - - size_t total_data_nodes = 0; - auto count_nodes = [&](uint8_t packet_id) - { - auto& nodes = linked_lists[packet_id]; - if (nodes.size() < 2) - { - ErrorHandler("MDMA packet merge requires packets with at least one data node"); - } - - total_data_nodes += nodes.size() - 1; - }; - - count_nodes(base_packet_id); - (count_nodes(packets_id), ...); - - std::vector merged_nodes; - merged_nodes.reserve(total_data_nodes + 1); - - uint32_t offset = 0; - auto append_packet = [&](uint8_t packet_id) - { - auto& nodes = linked_lists[packet_id]; - const size_t data_nodes = nodes.size() - 1; - for (size_t idx = 0; idx < data_nodes; ++idx) - { - MDMA_LinkNodeTypeDef node = nodes[idx]; - node.CDAR = buffer_address + offset; - node.CLAR = 0; - merged_nodes.push_back(node); - offset += node.CBNDTR; - } - }; - - append_packet(base_packet_id); - (append_packet(packets_id), ...); - - if (offset == 0) - { - ErrorHandler("Error merging linked list in MDMA"); - } - - MDMA_LinkNodeTypeDef final_node = base_nodes.back(); - final_node.CSAR = buffer_address; - final_node.CDAR = buffer_address + offset; - final_node.CBNDTR = offset; - final_node.CLAR = 0; - merged_nodes.push_back(final_node); - - const uint8_t new_packet_id = number_of_packets++; - linked_lists[new_packet_id] = std::move(merged_nodes); - packet_sizes[new_packet_id] = offset; - - auto& stored_nodes = linked_lists[new_packet_id]; - for (size_t i = 0; i + 1 < stored_nodes.size(); ++i) - { - stored_nodes[i].CLAR = reinterpret_cast(&stored_nodes[i + 1]); - } - stored_nodes.back().CLAR = 0; + static void transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode& first_node, Promise* promise=nullptr); - return new_packet_id; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 322e3b92b..c543d3854 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -2,17 +2,9 @@ #include - -std::unordered_map MDMA::src_size_to_flags = { - {1, MDMA_SRC_DATASIZE_BYTE}, - {2, MDMA_SRC_DATASIZE_HALFWORD}, - {4, MDMA_SRC_DATASIZE_WORD} -}; -std::unordered_map MDMA::dst_size_to_flags = { - {1, MDMA_DEST_DATASIZE_BYTE}, - {2, MDMA_DEST_DATASIZE_HALFWORD}, - {4, MDMA_DEST_DATASIZE_WORD} -}; +uint32_t buffer_size = sizeof(MDMA::LinkedListNode) * NODES_MAX; +MDMA::LinkedListNode* buffer = static_cast(MPUManager::allocate_non_cached_memory(buffer_size)); +inline Pool MDMA::link_node_pool{buffer}; std::unordered_map MDMA::instance_to_channel = { {0, MDMA_Channel0}, @@ -25,6 +17,7 @@ std::unordered_map MDMA::instance_to_channel = { {7, MDMA_Channel7} }; + std::unordered_map MDMA::channel_to_instance = { {MDMA_Channel0, 0}, {MDMA_Channel1, 1}, @@ -37,8 +30,9 @@ std::unordered_map MDMA::channel_to_instance = { }; -void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) +void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) { + auto node = first_node.get_node(); if (instance.handle.State == HAL_MDMA_STATE_BUSY || instance.handle.Lock == HAL_LOCKED) { ErrorHandler("MDMA transfer already in progress"); @@ -49,8 +43,7 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node instance.handle.State = HAL_MDMA_STATE_BUSY; instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; - instance.handle.FirstLinkedListNodeAddress = first_node; - + instance.handle.FirstLinkedListNodeAddress = node; __HAL_MDMA_DISABLE(&instance.handle); while ((instance.handle.Instance->CCR & MDMA_CCR_EN) != 0U) { @@ -59,15 +52,15 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node MDMA_Channel_TypeDef* channel = instance.handle.Instance; - channel->CTCR = first_node->CTCR; - channel->CBNDTR = first_node->CBNDTR; - channel->CSAR = first_node->CSAR; - channel->CDAR = first_node->CDAR; - channel->CBRUR = first_node->CBRUR; - channel->CTBR = first_node->CTBR; - channel->CMAR = first_node->CMAR; - channel->CMDR = first_node->CMDR; - channel->CLAR = first_node->CLAR; + channel->CTCR = node->CTCR; + channel->CBNDTR = node->CBNDTR; + channel->CSAR = node->CSAR; + channel->CDAR = node->CDAR; + channel->CBRUR = node->CBRUR; + channel->CTBR = node->CTBR; + channel->CMAR = node->CMAR; + channel->CMDR = node->CMDR; + channel->CLAR = node->CLAR; const uint32_t clear_flags = MDMA_FLAG_TE | MDMA_FLAG_CTC | MDMA_FLAG_BT | MDMA_FLAG_BFTC | MDMA_FLAG_BRT; __HAL_MDMA_CLEAR_FLAG(&instance.handle, clear_flags); @@ -88,8 +81,6 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node } - - MDMA::Instance& MDMA::get_instance(uint8_t id) { if (id >= instances.size()) @@ -177,6 +168,7 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) return id; } + void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); @@ -202,6 +194,7 @@ void MDMA::start() HAL_NVIC_EnableIRQ(MDMA_IRQn); } + void MDMA::irq_handler() { for (auto& instance : instances) @@ -214,7 +207,8 @@ void MDMA::irq_handler() } } -void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_t* destination_address,Promise* promise) + +void MDMA::transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode& first_node, Promise* promise) { Instance& instance = get_instance(MDMA_id); @@ -227,39 +221,11 @@ void MDMA::transfer_packet(const uint8_t MDMA_id, const uint8_t packet_id,uint8_ instance.promise = promise; instance.using_promise = true; } - - auto& nodes = linked_lists[packet_id]; - if (nodes.empty()) - { - ErrorHandler("No linked list nodes for MDMA packet"); - } - - uint8_t* final_destination = destination_address; - if (final_destination == nullptr) - { - if (instance.destination_address == nullptr) - { - ErrorHandler("No destination address provided for MDMA transfer"); - } - - final_destination = instance.destination_address; - } - - - - nodes.back().CDAR = reinterpret_cast(final_destination); - nodes.back().CBNDTR = packet_sizes[packet_id]; - nodes.back().CSAR = reinterpret_cast(instance.data_buffer); - instance.last_destination = nodes.back().CDAR; - instance.last_size = nodes.back().CBNDTR; - - - - SCB_CleanDCache_by_Addr((uint32_t*)&nodes.back(), sizeof(MDMA_LinkNodeTypeDef) * nodes.size()); - prepare_transfer(instance, &nodes[0]); + prepare_transfer(instance, first_node); } + void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) { Instance& instance = get_instance(MDMA_id); @@ -304,7 +270,6 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) } Instance& instance = get_instance(channel_it->second); - SCB_InvalidateDCache_by_Addr((uint32_t*)instance.last_destination, instance.last_size); if(instance.using_promise) { instance.promise->resolve(); @@ -313,6 +278,7 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) } + void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) { const auto channel_it = channel_to_instance.find(hmdma->Instance); @@ -332,10 +298,8 @@ void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) ErrorHandler("MDMA Transfer Error, code: " + std::to_string(error_code)); } + extern "C" void MDMA_IRQHandler(void) { MDMA::irq_handler(); -} - - - +} \ No newline at end of file From e870d55ba65901bc0850e41b66a3cf5f702056b9 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 1 Dec 2025 10:09:30 +0100 Subject: [PATCH 061/166] fix(MDMA): Fix bugs with instance --- Inc/HALAL/Models/MDMA/MDMA.hpp | 103 +++++++++++++++++---------------- Src/HALAL/Models/MDMA/MDMA.cpp | 26 +++++---- 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index b6abadf6f..b5479f640 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -22,57 +22,6 @@ class MDMA{ public: - struct LinkedListNode; - - private: - struct Instance{ - public: - MDMA_HandleTypeDef handle; - uint8_t id; - uint8_t* data_buffer; - uint8_t* destination_address; - Promise* promise; - bool using_promise; - MDMA::LinkedListNode transfer_node; - - Instance() - : handle{} - , id(0U) - , data_buffer(nullptr) - , destination_address(nullptr) - , promise(nullptr) - , using_promise(false) - , transfer_node{} - {} - - Instance(MDMA_HandleTypeDef handle_, - uint8_t id_, - uint8_t* data_buffer_, - uint8_t* destination_address_, - MDMA_LinkNodeTypeDef transfer_node_) - : handle(handle_) - , id(id_) - , data_buffer(data_buffer_) - , destination_address(destination_address_) - , promise(nullptr) - , using_promise(false) - , transfer_node(transfer_node_) - {} - - - }; - static void prepare_transfer(Instance& instance, MDMA::LinkedListNode& first_node); - static Instance& get_instance(uint8_t id); - inline static std::array instances{}; - static std::unordered_map instance_to_channel; - static std::unordered_map channel_to_instance; - - static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); - static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); - - public: - - /** * @brief A helper struct to create and manage MDMA linked list nodes. */ @@ -127,6 +76,58 @@ class MDMA{ MDMA_LinkNodeTypeDef node; }; + private: + struct Instance{ + public: + MDMA_HandleTypeDef handle; + uint8_t id; + uint8_t* data_buffer; + uint8_t* destination_address; + Promise* promise; + bool using_promise; + MDMA_LinkNodeTypeDef transfer_node; + + Instance() + : handle{} + , id(0U) + , data_buffer(nullptr) + , destination_address(nullptr) + , promise(nullptr) + , using_promise(false) + , transfer_node{} + {} + + Instance(MDMA_HandleTypeDef handle_, + uint8_t id_, + uint8_t* data_buffer_, + uint8_t* destination_address_, + MDMA_LinkNodeTypeDef transfer_node_) + : handle(handle_) + , id(id_) + , data_buffer(data_buffer_) + , destination_address(destination_address_) + , promise(nullptr) + , using_promise(false) + , transfer_node(transfer_node_) + {} + + + }; + static void prepare_transfer(Instance& instance, MDMA::LinkedListNode& first_node); + static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); + static Instance& get_instance(uint8_t id); + inline static std::array instances{}; + static std::unordered_map instance_to_channel; + static std::unordered_map channel_to_instance; + + static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); + static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); + + public: + + + + // Pool for MDMA_LinkNodeTypeDef, uses external non-cached memory static Pool link_node_pool; diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index c543d3854..1bb1a94a8 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -30,9 +30,8 @@ std::unordered_map MDMA::channel_to_instance = { }; -void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) +void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) { - auto node = first_node.get_node(); if (instance.handle.State == HAL_MDMA_STATE_BUSY || instance.handle.Lock == HAL_LOCKED) { ErrorHandler("MDMA transfer already in progress"); @@ -43,7 +42,7 @@ void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) instance.handle.State = HAL_MDMA_STATE_BUSY; instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; - instance.handle.FirstLinkedListNodeAddress = node; + instance.handle.FirstLinkedListNodeAddress = first_node; __HAL_MDMA_DISABLE(&instance.handle); while ((instance.handle.Instance->CCR & MDMA_CCR_EN) != 0U) { @@ -52,15 +51,15 @@ void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) MDMA_Channel_TypeDef* channel = instance.handle.Instance; - channel->CTCR = node->CTCR; - channel->CBNDTR = node->CBNDTR; - channel->CSAR = node->CSAR; - channel->CDAR = node->CDAR; - channel->CBRUR = node->CBRUR; - channel->CTBR = node->CTBR; - channel->CMAR = node->CMAR; - channel->CMDR = node->CMDR; - channel->CLAR = node->CLAR; + channel->CTCR = first_node->CTCR; + channel->CBNDTR = first_node->CBNDTR; + channel->CSAR = first_node->CSAR; + channel->CDAR = first_node->CDAR; + channel->CBRUR = first_node->CBRUR; + channel->CTBR = first_node->CTBR; + channel->CMAR = first_node->CMAR; + channel->CMDR = first_node->CMDR; + channel->CLAR = first_node->CLAR; const uint32_t clear_flags = MDMA_FLAG_TE | MDMA_FLAG_CTC | MDMA_FLAG_BT | MDMA_FLAG_BFTC | MDMA_FLAG_BRT; __HAL_MDMA_CLEAR_FLAG(&instance.handle, clear_flags); @@ -80,6 +79,9 @@ void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) instance.handle.Lock = HAL_UNLOCKED; } +void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) { MDMA::prepare_transfer(instance, first_node.get_node()); } + + MDMA::Instance& MDMA::get_instance(uint8_t id) { From ef95c66a5091341e30471e3c6cb69b6649951b24 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 1 Dec 2025 13:08:32 +0100 Subject: [PATCH 062/166] fix(MDMA): Too many linked list nodes, not enough memory --- Inc/HALAL/Models/MDMA/MDMA.hpp | 6 +++--- Src/HALAL/Models/MDMA/MDMA.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index b5479f640..caa18c4f7 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -17,7 +17,7 @@ #endif #ifndef NODES_MAX -#define NODES_MAX 500 +#define NODES_MAX 20 #endif class MDMA{ @@ -113,7 +113,7 @@ class MDMA{ }; - static void prepare_transfer(Instance& instance, MDMA::LinkedListNode& first_node); + static void prepare_transfer(Instance& instance, MDMA::LinkedListNode* first_node); static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); static Instance& get_instance(uint8_t id); inline static std::array instances{}; @@ -165,6 +165,6 @@ class MDMA{ * @param first_node The linked list node representing the first node in the linked list. * @param promise An optional promise to be fulfilled upon transfer completion. */ - static void transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode& first_node, Promise* promise=nullptr); + static void transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode* first_node, Promise* promise=nullptr); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 1bb1a94a8..578872ca9 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -79,7 +79,7 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node instance.handle.Lock = HAL_UNLOCKED; } -void MDMA::prepare_transfer(Instance& instance, LinkedListNode& first_node) { MDMA::prepare_transfer(instance, first_node.get_node()); } +void MDMA::prepare_transfer(Instance& instance, LinkedListNode* first_node) { MDMA::prepare_transfer(instance, first_node->get_node()); } @@ -210,7 +210,7 @@ void MDMA::irq_handler() } -void MDMA::transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode& first_node, Promise* promise) +void MDMA::transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode* first_node, Promise* promise) { Instance& instance = get_instance(MDMA_id); From fa3498bc702818fbbd6ab0b3d486bdb13b7c2960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 1 Dec 2025 23:47:30 +0100 Subject: [PATCH 063/166] erased no longer needed code --- Inc/HALAL/Models/GPIO.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index 09da6a27d..5423cd22d 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -189,12 +189,6 @@ struct GPIODomain { void toggle() { HAL_GPIO_TogglePin(port, pin); } }; -private: - inline static Instance *instances_ptr = nullptr; - -public: - static Instance &instance(std::size_t id) { return instances_ptr[id]; } - template struct Init { static inline std::array instances{}; @@ -211,8 +205,6 @@ struct GPIODomain { inst.port = port_to_reg(port); inst.pin = gpio_init.Pin; } - - instances_ptr = instances.data(); } }; }; From c92a7386f0218300f3d5241caf705f0ccba86f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 3 Dec 2025 00:23:23 +0100 Subject: [PATCH 064/166] Added DigitalInput and DigitalOutput Services, and added support for Alternate functions --- CMakeLists.txt | 1 - Inc/HALAL/Models/GPIO.hpp | 167 ++++++++++++------- Inc/HALAL/Models/Pin.hpp | 258 +++++++++++++++--------------- Inc/ST-LIB.hpp | 80 +++++---- Inc/ST-LIB_LOW/DigitalInput2.hpp | 63 ++++++++ Inc/ST-LIB_LOW/DigitalOutput2.hpp | 75 +++++++++ Inc/ST-LIB_LOW/ST-LIB_LOW.hpp | 3 + 7 files changed, 426 insertions(+), 221 deletions(-) create mode 100644 Inc/ST-LIB_LOW/DigitalInput2.hpp create mode 100644 Inc/ST-LIB_LOW/DigitalOutput2.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d35f2ef8..9ff603b45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,7 +263,6 @@ target_compile_options(${STLIB_LIBRARY} PRIVATE $<$:-specs=nosys.specs> $<$:-ffunction-sections> $<$:-fdata-sections> - # $<$:-fno-exceptions> -Wno-psabi diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index 5423cd22d..335ac3d26 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -13,14 +13,80 @@ using std::tuple; namespace ST_LIB { struct GPIODomain { enum class OperationMode : uint8_t { - INPUT, - OUTPUT, - ANALOG, - EXTERNAL_INTERRUPT_RISING, - EXTERNAL_INTERRUPT_FALLING, - EXTERNAL_INTERRUPT_RISING_FALLING, - TIMER_ALTERNATE_FUNCTION, - ALTERNATIVE, + INPUT, // GPIO_MODE_INPUT + OUTPUT_PUSHPULL, // GPIO_MODE_OUTPUT_PP + OUTPUT_OPENDRAIN, // GPIO_MODE_OUTPUT_OD + ANALOG, // GPIO_MODE_ANALOG + ALT_PP, // GPIO_MODE_AF_PP + ALT_OD, // GPIO_MODE_AF_OD + EXTI_RISING, // GPIO_MODE_IT_RISING + EXTI_FALLING, // GPIO_MODE_IT_FALLING + EXTI_RISING_FALLING, // GPIO_MODE_IT_RISING_FALLING + }; + static constexpr uint32_t to_hal_mode(OperationMode m) { + switch (m) { + case OperationMode::INPUT: + return GPIO_MODE_INPUT; + case OperationMode::OUTPUT_PUSHPULL: + return GPIO_MODE_OUTPUT_PP; + case OperationMode::OUTPUT_OPENDRAIN: + return GPIO_MODE_OUTPUT_OD; + case OperationMode::ANALOG: + return GPIO_MODE_ANALOG; + case OperationMode::ALT_PP: + return GPIO_MODE_AF_PP; + case OperationMode::ALT_OD: + return GPIO_MODE_AF_OD; + case OperationMode::EXTI_RISING: + return GPIO_MODE_IT_RISING; + case OperationMode::EXTI_FALLING: + return GPIO_MODE_IT_FALLING; + case OperationMode::EXTI_RISING_FALLING: + return GPIO_MODE_IT_RISING_FALLING; + } + } + enum class Pull : uint8_t { None, Up, Down }; + static constexpr uint32_t to_hal_pull(Pull p) { + switch (p) { + case Pull::None: + return GPIO_NOPULL; + case Pull::Up: + return GPIO_PULLUP; + case Pull::Down: + return GPIO_PULLDOWN; + } + } + enum class Speed : uint8_t { Low, Medium, High, VeryHigh }; + static constexpr uint32_t to_hal_speed(Speed s) { + switch (s) { + case Speed::Low: + return GPIO_SPEED_FREQ_LOW; + case Speed::Medium: + return GPIO_SPEED_FREQ_MEDIUM; + case Speed::High: + return GPIO_SPEED_FREQ_HIGH; + case Speed::VeryHigh: + return GPIO_SPEED_FREQ_VERY_HIGH; + } + } + enum class AlternateFunction : uint8_t { + NO_AF = 20, + AF0 = 15, + AF1 = 14, + AF2 = 13, + AF3 = 12, + AF4 = 11, + AF5 = 10, + AF6 = 9, + AF7 = 8, + AF8 = 7, + AF9 = 6, + AF10 = 5, + AF11 = 4, + AF12 = 3, + AF13 = 2, + AF14 = 1, + AF15 = 0 }; enum class Port : uint8_t { A, B, C, D, E, F, G, H }; static inline GPIO_TypeDef *port_to_reg(Port p) { @@ -81,18 +147,25 @@ struct GPIODomain { } } - struct Pin2 { + struct Pin { GPIODomain::Port port; uint32_t pin; + uint16_t afs; - consteval Pin2(GPIODomain::Port port, uint32_t pin) - : port(port), pin(pin) {} + inline constexpr bool valid_af(const AlternateFunction af) const { + if (af == AlternateFunction::NO_AF) + return true; + return ((1 << static_cast(af)) & afs) != 0; + } }; struct Entry { Port port; uint32_t pin; OperationMode mode; + Pull pull; + Speed speed; + AlternateFunction af; }; struct GPIO { @@ -100,7 +173,13 @@ struct GPIODomain { Entry e; - consteval GPIO(Pin2 pin, OperationMode mode) : e(pin.port, pin.pin, mode) {} + consteval GPIO(const Pin &pin, OperationMode mode, Pull pull, Speed speed, + AlternateFunction af = AlternateFunction::NO_AF) + : e{pin.port, pin.pin, mode, pull, speed, af} { + if (!pin.valid_af(af)) { + throw "Alternate function not valid for this pin"; + } + } template consteval void inscribe(Ctx &ctx) const { ctx.template add(e); @@ -108,8 +187,6 @@ struct GPIODomain { }; static constexpr std::size_t max_instances{110}; - static_assert(max_instances > 0, - "The number of instances must be greater than 0"); struct Config { std::tuple init_data{}; @@ -124,51 +201,17 @@ struct GPIODomain { for (std::size_t j = 0; j < i; ++j) { const auto &prev = pins[j]; if (prev.pin == e.pin && prev.port == e.port) { - struct gpio_already_inscribed {}; - throw gpio_already_inscribed{}; + throw "GPIO already inscribed"; } } - GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitTypeDef GPIO_InitStruct{}; GPIO_InitStruct.Pin = e.pin; - switch (e.mode) { - - case OperationMode::OUTPUT: - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; - GPIO_InitStruct.Pull = GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - break; - - case OperationMode::INPUT: - GPIO_InitStruct.Mode = GPIO_MODE_INPUT; - GPIO_InitStruct.Pull = GPIO_NOPULL; - break; - - case OperationMode::ANALOG: - GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; - GPIO_InitStruct.Pull = GPIO_NOPULL; - break; - case OperationMode::EXTERNAL_INTERRUPT_RISING: - GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - break; - case OperationMode::EXTERNAL_INTERRUPT_FALLING: - GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - break; - case OperationMode::EXTERNAL_INTERRUPT_RISING_FALLING: - GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; - GPIO_InitStruct.Pull = GPIO_PULLDOWN; - break; - // case OperationMode::TIMER_ALTERNATE_FUNCTION: - // GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - // GPIO_InitStruct.Pull = GPIO_NOPULL; - // GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; - // GPIO_InitStruct.Alternate = pin.alternative_function; - // break; - - default: - break; + GPIO_InitStruct.Mode = to_hal_mode(e.mode); + GPIO_InitStruct.Pull = to_hal_pull(e.pull); + GPIO_InitStruct.Speed = to_hal_speed(e.speed); + if (e.mode == OperationMode::ALT_PP || e.mode == OperationMode::ALT_OD) { + GPIO_InitStruct.Alternate = static_cast(e.af); } cfgs[i].init_data = std::make_tuple(e.port, GPIO_InitStruct); @@ -179,14 +222,22 @@ struct GPIODomain { // Runtime object struct Instance { + private: GPIO_TypeDef *port; - uint16_t pin; + uint32_t pin; + + public: + constexpr Instance() : port{nullptr}, pin{0} {} + Instance(GPIO_TypeDef *p, uint32_t pin) + : port{p}, pin{static_cast(pin)} {} void turn_on() { HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET); } void turn_off() { HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET); } void toggle() { HAL_GPIO_TogglePin(port, pin); } + + GPIO_PinState read() { return HAL_GPIO_ReadPin(port, pin); } }; template struct Init { @@ -201,9 +252,7 @@ struct GPIODomain { enable_gpio_clock(port); HAL_GPIO_Init(port_to_reg(port), &gpio_init); - auto &inst = instances[i]; - inst.port = port_to_reg(port); - inst.pin = gpio_init.Pin; + instances[i] = Instance{port_to_reg(port), gpio_init.Pin}; } } }; diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp index fd598fcd0..bc3a5331e 100644 --- a/Inc/HALAL/Models/Pin.hpp +++ b/Inc/HALAL/Models/Pin.hpp @@ -1,149 +1,151 @@ #include "HALAL/Models/GPIO.hpp" +using enum ST_LIB::GPIODomain::Port; + namespace ST_LIB { // Port A -constexpr GPIODomain::Pin2 PA0 {GPIODomain::Port::A, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PA1 {GPIODomain::Port::A, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PA2 {GPIODomain::Port::A, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PA3 {GPIODomain::Port::A, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PA4 {GPIODomain::Port::A, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PA5 {GPIODomain::Port::A, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PA6 {GPIODomain::Port::A, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PA7 {GPIODomain::Port::A, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PA8 {GPIODomain::Port::A, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PA9 {GPIODomain::Port::A, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PA10{GPIODomain::Port::A, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PA11{GPIODomain::Port::A, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PA12{GPIODomain::Port::A, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PA13{GPIODomain::Port::A, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PA14{GPIODomain::Port::A, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PA15{GPIODomain::Port::A, GPIO_PIN_15}; +constexpr GPIODomain::Pin PA0{A, GPIO_PIN_0, 0b0111110111111001}; +constexpr GPIODomain::Pin PA1{A, GPIO_PIN_1}; +constexpr GPIODomain::Pin PA2{A, GPIO_PIN_2}; +constexpr GPIODomain::Pin PA3{A, GPIO_PIN_3}; +constexpr GPIODomain::Pin PA4{A, GPIO_PIN_4}; +constexpr GPIODomain::Pin PA5{A, GPIO_PIN_5}; +constexpr GPIODomain::Pin PA6{A, GPIO_PIN_6}; +constexpr GPIODomain::Pin PA7{A, GPIO_PIN_7}; +constexpr GPIODomain::Pin PA8{A, GPIO_PIN_8}; +constexpr GPIODomain::Pin PA9{A, GPIO_PIN_9}; +constexpr GPIODomain::Pin PA10{A, GPIO_PIN_10}; +constexpr GPIODomain::Pin PA11{A, GPIO_PIN_11}; +constexpr GPIODomain::Pin PA12{A, GPIO_PIN_12}; +constexpr GPIODomain::Pin PA13{A, GPIO_PIN_13}; +constexpr GPIODomain::Pin PA14{A, GPIO_PIN_14}; +constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15}; // Port B -constexpr GPIODomain::Pin2 PB0 {GPIODomain::Port::B, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PB1 {GPIODomain::Port::B, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PB2 {GPIODomain::Port::B, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PB3 {GPIODomain::Port::B, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PB4 {GPIODomain::Port::B, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PB5 {GPIODomain::Port::B, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PB6 {GPIODomain::Port::B, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PB7 {GPIODomain::Port::B, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PB8 {GPIODomain::Port::B, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PB9 {GPIODomain::Port::B, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PB10{GPIODomain::Port::B, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PB11{GPIODomain::Port::B, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PB12{GPIODomain::Port::B, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PB13{GPIODomain::Port::B, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PB14{GPIODomain::Port::B, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PB15{GPIODomain::Port::B, GPIO_PIN_15}; +constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0}; +constexpr GPIODomain::Pin PB1{B, GPIO_PIN_1}; +constexpr GPIODomain::Pin PB2{B, GPIO_PIN_2}; +constexpr GPIODomain::Pin PB3{B, GPIO_PIN_3}; +constexpr GPIODomain::Pin PB4{B, GPIO_PIN_4}; +constexpr GPIODomain::Pin PB5{B, GPIO_PIN_5}; +constexpr GPIODomain::Pin PB6{B, GPIO_PIN_6}; +constexpr GPIODomain::Pin PB7{B, GPIO_PIN_7}; +constexpr GPIODomain::Pin PB8{B, GPIO_PIN_8}; +constexpr GPIODomain::Pin PB9{B, GPIO_PIN_9}; +constexpr GPIODomain::Pin PB10{B, GPIO_PIN_10}; +constexpr GPIODomain::Pin PB11{B, GPIO_PIN_11}; +constexpr GPIODomain::Pin PB12{B, GPIO_PIN_12}; +constexpr GPIODomain::Pin PB13{B, GPIO_PIN_13}; +constexpr GPIODomain::Pin PB14{B, GPIO_PIN_14}; +constexpr GPIODomain::Pin PB15{B, GPIO_PIN_15}; // Port C -constexpr GPIODomain::Pin2 PC0 {GPIODomain::Port::C, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PC1 {GPIODomain::Port::C, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PC2 {GPIODomain::Port::C, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PC3 {GPIODomain::Port::C, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PC4 {GPIODomain::Port::C, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PC5 {GPIODomain::Port::C, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PC6 {GPIODomain::Port::C, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PC7 {GPIODomain::Port::C, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PC8 {GPIODomain::Port::C, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PC9 {GPIODomain::Port::C, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PC10{GPIODomain::Port::C, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PC11{GPIODomain::Port::C, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PC12{GPIODomain::Port::C, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PC13{GPIODomain::Port::C, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PC14{GPIODomain::Port::C, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PC15{GPIODomain::Port::C, GPIO_PIN_15}; +constexpr GPIODomain::Pin PC0{C, GPIO_PIN_0}; +constexpr GPIODomain::Pin PC1{C, GPIO_PIN_1}; +constexpr GPIODomain::Pin PC2{C, GPIO_PIN_2}; +constexpr GPIODomain::Pin PC3{C, GPIO_PIN_3}; +constexpr GPIODomain::Pin PC4{C, GPIO_PIN_4}; +constexpr GPIODomain::Pin PC5{C, GPIO_PIN_5}; +constexpr GPIODomain::Pin PC6{C, GPIO_PIN_6}; +constexpr GPIODomain::Pin PC7{C, GPIO_PIN_7}; +constexpr GPIODomain::Pin PC8{C, GPIO_PIN_8}; +constexpr GPIODomain::Pin PC9{C, GPIO_PIN_9}; +constexpr GPIODomain::Pin PC10{C, GPIO_PIN_10}; +constexpr GPIODomain::Pin PC11{C, GPIO_PIN_11}; +constexpr GPIODomain::Pin PC12{C, GPIO_PIN_12}; +constexpr GPIODomain::Pin PC13{C, GPIO_PIN_13}; +constexpr GPIODomain::Pin PC14{C, GPIO_PIN_14}; +constexpr GPIODomain::Pin PC15{C, GPIO_PIN_15}; // Port D -constexpr GPIODomain::Pin2 PD0 {GPIODomain::Port::D, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PD1 {GPIODomain::Port::D, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PD2 {GPIODomain::Port::D, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PD3 {GPIODomain::Port::D, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PD4 {GPIODomain::Port::D, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PD5 {GPIODomain::Port::D, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PD6 {GPIODomain::Port::D, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PD7 {GPIODomain::Port::D, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PD8 {GPIODomain::Port::D, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PD9 {GPIODomain::Port::D, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PD10{GPIODomain::Port::D, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PD11{GPIODomain::Port::D, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PD12{GPIODomain::Port::D, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PD13{GPIODomain::Port::D, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PD14{GPIODomain::Port::D, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PD15{GPIODomain::Port::D, GPIO_PIN_15}; +constexpr GPIODomain::Pin PD0{D, GPIO_PIN_0}; +constexpr GPIODomain::Pin PD1{D, GPIO_PIN_1}; +constexpr GPIODomain::Pin PD2{D, GPIO_PIN_2}; +constexpr GPIODomain::Pin PD3{D, GPIO_PIN_3}; +constexpr GPIODomain::Pin PD4{D, GPIO_PIN_4}; +constexpr GPIODomain::Pin PD5{D, GPIO_PIN_5}; +constexpr GPIODomain::Pin PD6{D, GPIO_PIN_6}; +constexpr GPIODomain::Pin PD7{D, GPIO_PIN_7}; +constexpr GPIODomain::Pin PD8{D, GPIO_PIN_8}; +constexpr GPIODomain::Pin PD9{D, GPIO_PIN_9}; +constexpr GPIODomain::Pin PD10{D, GPIO_PIN_10}; +constexpr GPIODomain::Pin PD11{D, GPIO_PIN_11}; +constexpr GPIODomain::Pin PD12{D, GPIO_PIN_12}; +constexpr GPIODomain::Pin PD13{D, GPIO_PIN_13}; +constexpr GPIODomain::Pin PD14{D, GPIO_PIN_14}; +constexpr GPIODomain::Pin PD15{D, GPIO_PIN_15}; // Port E -constexpr GPIODomain::Pin2 PE0 {GPIODomain::Port::E, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PE1 {GPIODomain::Port::E, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PE2 {GPIODomain::Port::E, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PE3 {GPIODomain::Port::E, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PE4 {GPIODomain::Port::E, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PE5 {GPIODomain::Port::E, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PE6 {GPIODomain::Port::E, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PE7 {GPIODomain::Port::E, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PE8 {GPIODomain::Port::E, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PE9 {GPIODomain::Port::E, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PE10{GPIODomain::Port::E, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PE11{GPIODomain::Port::E, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PE12{GPIODomain::Port::E, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PE13{GPIODomain::Port::E, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PE14{GPIODomain::Port::E, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PE15{GPIODomain::Port::E, GPIO_PIN_15}; +constexpr GPIODomain::Pin PE0{E, GPIO_PIN_0}; +constexpr GPIODomain::Pin PE1{E, GPIO_PIN_1}; +constexpr GPIODomain::Pin PE2{E, GPIO_PIN_2}; +constexpr GPIODomain::Pin PE3{E, GPIO_PIN_3}; +constexpr GPIODomain::Pin PE4{E, GPIO_PIN_4}; +constexpr GPIODomain::Pin PE5{E, GPIO_PIN_5}; +constexpr GPIODomain::Pin PE6{E, GPIO_PIN_6}; +constexpr GPIODomain::Pin PE7{E, GPIO_PIN_7}; +constexpr GPIODomain::Pin PE8{E, GPIO_PIN_8}; +constexpr GPIODomain::Pin PE9{E, GPIO_PIN_9}; +constexpr GPIODomain::Pin PE10{E, GPIO_PIN_10}; +constexpr GPIODomain::Pin PE11{E, GPIO_PIN_11}; +constexpr GPIODomain::Pin PE12{E, GPIO_PIN_12}; +constexpr GPIODomain::Pin PE13{E, GPIO_PIN_13}; +constexpr GPIODomain::Pin PE14{E, GPIO_PIN_14}; +constexpr GPIODomain::Pin PE15{E, GPIO_PIN_15}; // Port F -constexpr GPIODomain::Pin2 PF0 {GPIODomain::Port::F, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PF1 {GPIODomain::Port::F, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PF2 {GPIODomain::Port::F, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PF3 {GPIODomain::Port::F, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PF4 {GPIODomain::Port::F, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PF5 {GPIODomain::Port::F, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PF6 {GPIODomain::Port::F, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PF7 {GPIODomain::Port::F, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PF8 {GPIODomain::Port::F, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PF9 {GPIODomain::Port::F, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PF10{GPIODomain::Port::F, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PF11{GPIODomain::Port::F, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PF12{GPIODomain::Port::F, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PF13{GPIODomain::Port::F, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PF14{GPIODomain::Port::F, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PF15{GPIODomain::Port::F, GPIO_PIN_15}; +constexpr GPIODomain::Pin PF0{F, GPIO_PIN_0}; +constexpr GPIODomain::Pin PF1{F, GPIO_PIN_1}; +constexpr GPIODomain::Pin PF2{F, GPIO_PIN_2}; +constexpr GPIODomain::Pin PF3{F, GPIO_PIN_3}; +constexpr GPIODomain::Pin PF4{F, GPIO_PIN_4}; +constexpr GPIODomain::Pin PF5{F, GPIO_PIN_5}; +constexpr GPIODomain::Pin PF6{F, GPIO_PIN_6}; +constexpr GPIODomain::Pin PF7{F, GPIO_PIN_7}; +constexpr GPIODomain::Pin PF8{F, GPIO_PIN_8}; +constexpr GPIODomain::Pin PF9{F, GPIO_PIN_9}; +constexpr GPIODomain::Pin PF10{F, GPIO_PIN_10}; +constexpr GPIODomain::Pin PF11{F, GPIO_PIN_11}; +constexpr GPIODomain::Pin PF12{F, GPIO_PIN_12}; +constexpr GPIODomain::Pin PF13{F, GPIO_PIN_13}; +constexpr GPIODomain::Pin PF14{F, GPIO_PIN_14}; +constexpr GPIODomain::Pin PF15{F, GPIO_PIN_15}; // Port G -constexpr GPIODomain::Pin2 PG0 {GPIODomain::Port::G, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PG1 {GPIODomain::Port::G, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PG2 {GPIODomain::Port::G, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PG3 {GPIODomain::Port::G, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PG4 {GPIODomain::Port::G, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PG5 {GPIODomain::Port::G, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PG6 {GPIODomain::Port::G, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PG7 {GPIODomain::Port::G, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PG8 {GPIODomain::Port::G, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PG9 {GPIODomain::Port::G, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PG10{GPIODomain::Port::G, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PG11{GPIODomain::Port::G, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PG12{GPIODomain::Port::G, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PG13{GPIODomain::Port::G, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PG14{GPIODomain::Port::G, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PG15{GPIODomain::Port::G, GPIO_PIN_15}; +constexpr GPIODomain::Pin PG0{G, GPIO_PIN_0}; +constexpr GPIODomain::Pin PG1{G, GPIO_PIN_1}; +constexpr GPIODomain::Pin PG2{G, GPIO_PIN_2}; +constexpr GPIODomain::Pin PG3{G, GPIO_PIN_3}; +constexpr GPIODomain::Pin PG4{G, GPIO_PIN_4}; +constexpr GPIODomain::Pin PG5{G, GPIO_PIN_5}; +constexpr GPIODomain::Pin PG6{G, GPIO_PIN_6}; +constexpr GPIODomain::Pin PG7{G, GPIO_PIN_7}; +constexpr GPIODomain::Pin PG8{G, GPIO_PIN_8}; +constexpr GPIODomain::Pin PG9{G, GPIO_PIN_9}; +constexpr GPIODomain::Pin PG10{G, GPIO_PIN_10}; +constexpr GPIODomain::Pin PG11{G, GPIO_PIN_11}; +constexpr GPIODomain::Pin PG12{G, GPIO_PIN_12}; +constexpr GPIODomain::Pin PG13{G, GPIO_PIN_13}; +constexpr GPIODomain::Pin PG14{G, GPIO_PIN_14}; +constexpr GPIODomain::Pin PG15{G, GPIO_PIN_15}; // Port H -constexpr GPIODomain::Pin2 PH0 {GPIODomain::Port::H, GPIO_PIN_0}; -constexpr GPIODomain::Pin2 PH1 {GPIODomain::Port::H, GPIO_PIN_1}; -constexpr GPIODomain::Pin2 PH2 {GPIODomain::Port::H, GPIO_PIN_2}; -constexpr GPIODomain::Pin2 PH3 {GPIODomain::Port::H, GPIO_PIN_3}; -constexpr GPIODomain::Pin2 PH4 {GPIODomain::Port::H, GPIO_PIN_4}; -constexpr GPIODomain::Pin2 PH5 {GPIODomain::Port::H, GPIO_PIN_5}; -constexpr GPIODomain::Pin2 PH6 {GPIODomain::Port::H, GPIO_PIN_6}; -constexpr GPIODomain::Pin2 PH7 {GPIODomain::Port::H, GPIO_PIN_7}; -constexpr GPIODomain::Pin2 PH8 {GPIODomain::Port::H, GPIO_PIN_8}; -constexpr GPIODomain::Pin2 PH9 {GPIODomain::Port::H, GPIO_PIN_9}; -constexpr GPIODomain::Pin2 PH10{GPIODomain::Port::H, GPIO_PIN_10}; -constexpr GPIODomain::Pin2 PH11{GPIODomain::Port::H, GPIO_PIN_11}; -constexpr GPIODomain::Pin2 PH12{GPIODomain::Port::H, GPIO_PIN_12}; -constexpr GPIODomain::Pin2 PH13{GPIODomain::Port::H, GPIO_PIN_13}; -constexpr GPIODomain::Pin2 PH14{GPIODomain::Port::H, GPIO_PIN_14}; -constexpr GPIODomain::Pin2 PH15{GPIODomain::Port::H, GPIO_PIN_15}; +constexpr GPIODomain::Pin PH0{H, GPIO_PIN_0}; +constexpr GPIODomain::Pin PH1{H, GPIO_PIN_1}; +constexpr GPIODomain::Pin PH2{H, GPIO_PIN_2}; +constexpr GPIODomain::Pin PH3{H, GPIO_PIN_3}; +constexpr GPIODomain::Pin PH4{H, GPIO_PIN_4}; +constexpr GPIODomain::Pin PH5{H, GPIO_PIN_5}; +constexpr GPIODomain::Pin PH6{H, GPIO_PIN_6}; +constexpr GPIODomain::Pin PH7{H, GPIO_PIN_7}; +constexpr GPIODomain::Pin PH8{H, GPIO_PIN_8}; +constexpr GPIODomain::Pin PH9{H, GPIO_PIN_9}; +constexpr GPIODomain::Pin PH10{H, GPIO_PIN_10}; +constexpr GPIODomain::Pin PH11{H, GPIO_PIN_11}; +constexpr GPIODomain::Pin PH12{H, GPIO_PIN_12}; +constexpr GPIODomain::Pin PH13{H, GPIO_PIN_13}; +constexpr GPIODomain::Pin PH14{H, GPIO_PIN_14}; +constexpr GPIODomain::Pin PH15{H, GPIO_PIN_15}; } // namespace ST_LIB \ No newline at end of file diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 2f416eb5f..dff92504d 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -46,11 +46,13 @@ template struct BuildCtx { } } - template consteval void add(typename D::Entry e) { + template consteval std::size_t add(typename D::Entry e) { constexpr std::size_t I = domain_index(); auto &arr = std::get(storage); auto &size = sizes[I]; + const auto idx = size; arr[size++] = e; + return idx; } template consteval auto span() const { @@ -60,60 +62,72 @@ template struct BuildCtx { using E = typename D::Entry; return std::span{arr.data(), size}; } -}; -template consteval std::size_t domain_count() { - return (... + - (std::is_same_v::domain, - Domain> - ? 1u - : 0u)); -} + template consteval std::size_t size() const { + constexpr std::size_t I = domain_index(); + return sizes[I]; + } +}; -using DomainsCtx = BuildCtx; +using DomainsCtx = BuildCtx; template struct Board { - // ========== build compile-time ========== - static consteval auto build() { + static consteval auto build_ctx() { DomainsCtx ctx{}; - (devs.inscribe(ctx), ...); + return ctx; + } + + static constexpr auto ctx = build_ctx(); - constexpr std::size_t gpioN = domain_count(); - // constexpr std::size_t adcN = domain_count(); - // constexpr std::size_t pwmN = domain_count(); + template static consteval std::size_t domain_size() { + return ctx.template span().size(); + } + + static consteval auto build() { + constexpr std::size_t gpioN = domain_size(); + constexpr std::size_t doutN = domain_size(); + constexpr std::size_t dinN = domain_size(); + // ... struct ConfigBundle { std::array gpio_cfgs; - // std::array adc_cgfs; - // std::array pwm_cgfs; + std::array dout_cfgs; + std::array din_cfgs; + // ... }; return ConfigBundle{ .gpio_cfgs = GPIODomain::template build(ctx.template span()), - // .adc_cgfs = - // ADCDomain::template build(ctx.template span()), - // .pwm_cgfs = - // PWMDomain::template build(ctx.template span()), + .dout_cfgs = DigitalOutputDomain::template build( + ctx.template span()), + .din_cfgs = DigitalInputDomain::template build( + ctx.template span()), + // ... }; } - // ========== init runtime ========== - static void init() { - static constexpr auto cfg = build(); + static constexpr auto cfg = build(); - constexpr std::size_t gpioN = domain_count(); - // constexpr std::size_t adcN = domain_count(); - // constexpr std::size_t pwmN = domain_count(); + static void init() { + constexpr std::size_t gpioN = domain_size(); + constexpr std::size_t doutN = domain_size(); + constexpr std::size_t dinN = domain_size(); + // ... GPIODomain::Init::init(cfg.gpio_cfgs); - // ADCDomain::Init::init(cfg.adc_cfgs); - // PWMDomain::Init::init(cfg.pwm_cfgs); + DigitalOutputDomain::Init::init(cfg.dout_cfgs, + GPIODomain::Init::instances); + DigitalInputDomain::Init::init(cfg.din_cfgs, + GPIODomain::Init::instances); + // ... } - template static consteval std::size_t domain_size() { - return domain_count(); + template + static consteval std::size_t domain_size_for_instance() { + return domain_size(); } template @@ -154,7 +168,7 @@ template struct Board { using Domain = typename DevT::domain; constexpr std::size_t idx = domain_index_of(); - constexpr std::size_t N = domain_size(); + constexpr std::size_t N = domain_size_for_instance(); return Domain::template Init::instances[idx]; } diff --git a/Inc/ST-LIB_LOW/DigitalInput2.hpp b/Inc/ST-LIB_LOW/DigitalInput2.hpp new file mode 100644 index 000000000..e161e39a5 --- /dev/null +++ b/Inc/ST-LIB_LOW/DigitalInput2.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "HALAL/Models/GPIO.hpp" + +using ST_LIB::GPIODomain; + +namespace ST_LIB { +struct DigitalInputDomain { + struct Entry { + size_t gpio_idx; + }; + struct DigitalInput { + GPIODomain::GPIO gpio; + using domain = DigitalInputDomain; + + consteval DigitalInput(const GPIODomain::Pin &pin, + GPIODomain::Pull pull = GPIODomain::Pull::None, + GPIODomain::Speed speed = GPIODomain::Speed::Low) + : gpio{pin, GPIODomain::OperationMode::INPUT, pull, speed} {} + + template consteval void inscribe(Ctx &ctx) const { + const auto gpio_idx = ctx.template add(gpio.e); + Entry e{.gpio_idx = gpio_idx}; + ctx.template add(e); + } + }; + + static constexpr std::size_t max_instances{110}; + + struct Config { + size_t gpio_idx; + }; + + template + static consteval array build(span outputs) { + array cfgs{}; + + for (std::size_t i = 0; i < N; ++i) { + cfgs[i].gpio_idx = outputs[i].gpio_idx; + } + return cfgs; + } + + struct Instance { + GPIODomain::Instance *gpio_instance; + + GPIO_PinState read() { return gpio_instance->read(); } + }; + + template struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span gpio_instances) { + for (std::size_t i = 0; i < N; ++i) { + const auto &e = cfgs[i]; + + instances[i].gpio_instance = &gpio_instances[e.gpio_idx]; + } + } + }; +}; +} // namespace ST_LIB diff --git a/Inc/ST-LIB_LOW/DigitalOutput2.hpp b/Inc/ST-LIB_LOW/DigitalOutput2.hpp new file mode 100644 index 000000000..7dcad5b65 --- /dev/null +++ b/Inc/ST-LIB_LOW/DigitalOutput2.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "HALAL/Models/GPIO.hpp" + +using ST_LIB::GPIODomain; + +namespace ST_LIB { +struct DigitalOutputDomain { + enum class OutputMode : uint8_t { + PUSH_PULL = + static_cast(GPIODomain::OperationMode::OUTPUT_PUSHPULL), + OPEN_DRAIN = + static_cast(GPIODomain::OperationMode::OUTPUT_OPENDRAIN), + }; + struct Entry { + size_t gpio_idx; + }; + struct DigitalOutput { + GPIODomain::GPIO gpio; + using domain = DigitalOutputDomain; + + consteval DigitalOutput(const GPIODomain::Pin &pin, + OutputMode mode = OutputMode::PUSH_PULL, + GPIODomain::Pull pull = GPIODomain::Pull::None, + GPIODomain::Speed speed = GPIODomain::Speed::Low) + : gpio{pin, static_cast(mode), pull, speed} { + } + + template consteval void inscribe(Ctx &ctx) const { + const auto gpio_idx = ctx.template add(gpio.e); + Entry e{.gpio_idx = gpio_idx}; + ctx.template add(e); + } + }; + + static constexpr std::size_t max_instances{110}; + + struct Config { + size_t gpio_idx; + }; + + template + static consteval array build(span outputs) { + array cfgs{}; + + for (std::size_t i = 0; i < N; ++i) { + cfgs[i].gpio_idx = outputs[i].gpio_idx; + } + return cfgs; + } + + struct Instance { + GPIODomain::Instance *gpio_instance; + + void turn_on() { gpio_instance->turn_on(); } + + void turn_off() { gpio_instance->turn_off(); } + + void toggle() { gpio_instance->toggle(); } + }; + + template struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span gpio_instances) { + for (std::size_t i = 0; i < N; ++i) { + const auto &e = cfgs[i]; + + instances[i].gpio_instance = &gpio_instances[e.gpio_idx]; + } + } + }; +}; +} // namespace ST_LIB diff --git a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp index 5473a9d1e..b73165c24 100644 --- a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp +++ b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp @@ -1,5 +1,8 @@ #pragma once +#include "ST-LIB_LOW/DigitalOutput2.hpp" +#include "ST-LIB_LOW/DigitalInput2.hpp" + #include "Clocks/Counter.hpp" #include "Clocks/Stopwatch.hpp" #include "Sensors/LinearSensor/LinearSensor.hpp" From 42df50f3534a3648e0038071c1442340cd6f538e Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 3 Dec 2025 15:46:39 +0100 Subject: [PATCH 065/166] feat(MdmaPacket): Make MdmaPacket compatible with MDMA linked list transfers, still lacks decoupling from instance logic --- Inc/HALAL/Models/MDMA/MDMA.hpp | 2 + Inc/HALAL/Models/Packets/MdmaPacket.hpp | 66 +++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index caa18c4f7..f4a27eb9d 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -66,6 +66,8 @@ class MDMA{ * @brief Set the next node in the linked list. */ void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } + void set_destination(void* destination) { node.CDAR = reinterpret_cast(destination); } + void set_source(void* source) { node.CSAR = reinterpret_cast(source); } auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } auto get_size() -> uint32_t { return node.CBNDTR; } auto get_destination() -> uint32_t { return node.CDAR; } diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index e79ea76df..a3e025b47 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -38,6 +38,39 @@ class MdmaPacket : public Packet { packets[id] = this; // Allocate non-cached buffer for MDMA buffer = MPUManager::allocate_non_cached_memory(size); + + MDMA::LinkedListNode* prev_node = nullptr; + uint32_t offset = 0; + uint32_t idx = 0; + std::apply([&](auto&&... args) { + (([&]() { + using T = std::remove_pointer_t; + MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(args, buffer + offset); + build_nodes[idx++] = node; + offset += sizeof(T); + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); + }, value_pointers); + + prev_node = nullptr; + offset = 0; + idx = 0; + std::apply([&](auto&&... args) { + (([&]() { + MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(buffer + offset, args); + parse_nodes[idx++] = node; + offset += sizeof(args); + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); + }, value_pointers); + + transfer_node = MDMA::link_node_pool.construct(buffer, nullptr); // Used when needed for dynamic destination / origin } /** @@ -46,8 +79,9 @@ class MdmaPacket : public Packet { * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(uint8_t* destination_address = nullptr) { + set_external_buffer(destination_address, build_nodes[sizeof...(Types)+1]); Promise* promise = Promise::inscribe(); - // (TODO) Call MDMA to transfer data + MDMA::transfer_list(0, build_nodes[0], promise); promise->wait(); return destination_address ? destination_address : buffer; } @@ -59,7 +93,8 @@ class MdmaPacket : public Packet { * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(Promise* promise, uint8_t* destination_address = nullptr) { - // (TODO) Call MDMA to transfer data + set_external_buffer(destination_address, build_nodes[sizeof...(Types)+1]); + MDMA::transfer_list(0, build_nodes[0], promise); return destination_address ? destination_address : buffer; } @@ -70,12 +105,20 @@ class MdmaPacket : public Packet { void parse(uint8_t* data = nullptr) override { Promise* promise = Promise::inscribe(); - // (TODO) Call MDMA to transfer data + if (set_external_buffer(data, parse_nodes[0])) { + MDMA::transfer_list(0, transfer_node, promise); + } else { + MDMA::transfer_list(0, parse_nodes[0], promise); + } promise->wait(); } void parse(Promise* promise, uint8_t* data = nullptr) { - // (TODO) Call MDMA to transfer data + if (set_external_buffer(data, parse_nodes[0])) { + MDMA::transfer_list(0, transfer_node, promise); + } else { + MDMA::transfer_list(0, parse_nodes[0], promise); + } } size_t get_size() override { @@ -98,6 +141,21 @@ class MdmaPacket : public Packet { }, value_pointers); } + private: + MDMA::LinkedListNode* transfer_node; // Node used for destination address + MDMA::LinkedListNode* build_nodes[sizeof...(Types) + 1]; + MDMA::LinkedListNode* parse_nodes[sizeof...(Types) + 1]; + + bool set_external_buffer(uint8_t* external_buffer, MDMA::LinkedListNode* node) { + if (external_buffer != nullptr) { + transfer_node->set_destination(external_buffer); + node->set_next(transfer_node->get_node()); + return true; + } else { + node->set_next(nullptr); + return false; + } + } }; #endif // MDMA_PACKET_HPP \ No newline at end of file From db7596ce8048a73110a05aef4fc1d29ef537b7b1 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 3 Dec 2025 17:59:32 +0100 Subject: [PATCH 066/166] fix(MDMA): Fixes --- CMakeLists.txt | 3 ++ Inc/HALAL/Models/Packets/MdmaPacket.hpp | 52 ++++++++++++++----------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba062c27..c5b8e0808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,9 @@ if(CMAKE_CROSSCOMPILING) ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_gpio.c ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_exti.c + # MDMA + ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_mdma.c + # ADC ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_adc.c ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_adc_ex.c diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index a3e025b47..aacec86c7 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -25,7 +25,7 @@ class MdmaPacket : public Packet { public: uint16_t id; uint8_t* buffer; - size_t& size = Packet::size; + size_t size; std::tuple value_pointers; /** @@ -37,7 +37,7 @@ class MdmaPacket : public Packet { : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { packets[id] = this; // Allocate non-cached buffer for MDMA - buffer = MPUManager::allocate_non_cached_memory(size); + buffer = reinterpret_cast(MPUManager::allocate_non_cached_memory(size)); MDMA::LinkedListNode* prev_node = nullptr; uint32_t offset = 0; @@ -70,7 +70,8 @@ class MdmaPacket : public Packet { }()), ...); }, value_pointers); - transfer_node = MDMA::link_node_pool.construct(buffer, nullptr); // Used when needed for dynamic destination / origin + build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr); // Used when needed for dynamic destination + parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer); // Used when needed for dynamic origin } /** @@ -79,7 +80,7 @@ class MdmaPacket : public Packet { * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(uint8_t* destination_address = nullptr) { - set_external_buffer(destination_address, build_nodes[sizeof...(Types)+1]); + set_build_destination(destination_address); Promise* promise = Promise::inscribe(); MDMA::transfer_list(0, build_nodes[0], promise); promise->wait(); @@ -93,32 +94,27 @@ class MdmaPacket : public Packet { * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(Promise* promise, uint8_t* destination_address = nullptr) { - set_external_buffer(destination_address, build_nodes[sizeof...(Types)+1]); + set_build_destination(destination_address); MDMA::transfer_list(0, build_nodes[0], promise); return destination_address ? destination_address : buffer; } // Just for interface compliance uint8_t* build() override { - return build(nullptr); + uint8_t* destination_address = nullptr; + return build(destination_address); } void parse(uint8_t* data = nullptr) override { Promise* promise = Promise::inscribe(); - if (set_external_buffer(data, parse_nodes[0])) { - MDMA::transfer_list(0, transfer_node, promise); - } else { - MDMA::transfer_list(0, parse_nodes[0], promise); - } + auto source_node = set_parse_source(data); + MDMA::transfer_list(0, source_node, promise); promise->wait(); } void parse(Promise* promise, uint8_t* data = nullptr) { - if (set_external_buffer(data, parse_nodes[0])) { - MDMA::transfer_list(0, transfer_node, promise); - } else { - MDMA::transfer_list(0, parse_nodes[0], promise); - } + auto source_node = set_parse_source(data); + MDMA::transfer_list(0, source_node, promise); } size_t get_size() override { @@ -142,18 +138,28 @@ class MdmaPacket : public Packet { } private: - MDMA::LinkedListNode* transfer_node; // Node used for destination address + MDMA::LinkedListNode* build_transfer_node; // Node used for destination address + MDMA::LinkedListNode* parse_transfer_node; // Node used for source address MDMA::LinkedListNode* build_nodes[sizeof...(Types) + 1]; MDMA::LinkedListNode* parse_nodes[sizeof...(Types) + 1]; - bool set_external_buffer(uint8_t* external_buffer, MDMA::LinkedListNode* node) { + void set_build_destination(uint8_t* external_buffer) { + if (external_buffer != nullptr) { + build_transfer_node->set_destination(external_buffer); + build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); + } else { + build_nodes[sizeof...(Types)]->set_next(nullptr); + } + } + + MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { if (external_buffer != nullptr) { - transfer_node->set_destination(external_buffer); - node->set_next(transfer_node->get_node()); - return true; + parse_transfer_node->set_source(external_buffer); + parse_transfer_node->set_next(parse_nodes[0]->get_node()); + return parse_transfer_node; } else { - node->set_next(nullptr); - return false; + parse_nodes[0]->set_next(nullptr); + return parse_nodes[0]; } } }; From 9dc516bd6b48a3f23821be33bce8721d605c08fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 8 Dec 2025 12:11:04 +0100 Subject: [PATCH 067/166] Replaced throw by undefined function --- CMakeLists.txt | 5 +++-- Inc/HALAL/Models/GPIO.hpp | 6 ++++-- Inc/ST-LIB.hpp | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ff603b45..9ba062c27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -263,6 +263,7 @@ target_compile_options(${STLIB_LIBRARY} PRIVATE $<$:-specs=nosys.specs> $<$:-ffunction-sections> $<$:-fdata-sections> + $<$:-fno-exceptions> -Wno-psabi @@ -271,8 +272,8 @@ target_compile_options(${STLIB_LIBRARY} PRIVATE $<$:-Wall> $<$:-Wpedantic> $<$:-Werror> - # $<$:-Wno-gnu-zero-variadic-macro-arguments> - # $<$:-Wno-inconsistent-missing-override> + $<$:-Wno-gnu-zero-variadic-macro-arguments> + $<$:-Wno-inconsistent-missing-override> $<$:-fno-use-cxa-atexit> $<$:-fno-rtti> $<$:-Wno-address-of-packed-member> diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index 335ac3d26..c19131e37 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -11,6 +11,8 @@ using std::span; using std::tuple; namespace ST_LIB { +extern void compile_error(const char *msg); + struct GPIODomain { enum class OperationMode : uint8_t { INPUT, // GPIO_MODE_INPUT @@ -177,7 +179,7 @@ struct GPIODomain { AlternateFunction af = AlternateFunction::NO_AF) : e{pin.port, pin.pin, mode, pull, speed, af} { if (!pin.valid_af(af)) { - throw "Alternate function not valid for this pin"; + compile_error("Alternate function not valid for this pin"); } } @@ -201,7 +203,7 @@ struct GPIODomain { for (std::size_t j = 0; j < i; ++j) { const auto &prev = pins[j]; if (prev.pin == e.pin && prev.port == e.port) { - throw "GPIO already inscribed"; + compile_error("GPIO already inscribed"); } } diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index dff92504d..32fb54814 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -25,6 +25,7 @@ class STLIB { }; namespace ST_LIB { +extern void compile_error(const char *msg); template struct BuildCtx { template using Decl = typename D::Entry; template @@ -151,8 +152,7 @@ template struct Board { ...); if (!found) { - struct device_not_found_for_domain {}; - throw device_not_found_for_domain{}; + compile_error("Device not found for domain"); } return idx; From d9263feeff56bd698ecf2f54b4c35b4f6903e4af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 8 Dec 2025 12:11:14 +0100 Subject: [PATCH 068/166] Implemented alternate functions --- Inc/HALAL/Models/Pin.hpp | 242 ++++++++++++++++++--------------------- 1 file changed, 114 insertions(+), 128 deletions(-) diff --git a/Inc/HALAL/Models/Pin.hpp b/Inc/HALAL/Models/Pin.hpp index bc3a5331e..ad1bc45df 100644 --- a/Inc/HALAL/Models/Pin.hpp +++ b/Inc/HALAL/Models/Pin.hpp @@ -6,146 +6,132 @@ namespace ST_LIB { // Port A constexpr GPIODomain::Pin PA0{A, GPIO_PIN_0, 0b0111110111111001}; -constexpr GPIODomain::Pin PA1{A, GPIO_PIN_1}; -constexpr GPIODomain::Pin PA2{A, GPIO_PIN_2}; -constexpr GPIODomain::Pin PA3{A, GPIO_PIN_3}; -constexpr GPIODomain::Pin PA4{A, GPIO_PIN_4}; -constexpr GPIODomain::Pin PA5{A, GPIO_PIN_5}; -constexpr GPIODomain::Pin PA6{A, GPIO_PIN_6}; -constexpr GPIODomain::Pin PA7{A, GPIO_PIN_7}; -constexpr GPIODomain::Pin PA8{A, GPIO_PIN_8}; -constexpr GPIODomain::Pin PA9{A, GPIO_PIN_9}; -constexpr GPIODomain::Pin PA10{A, GPIO_PIN_10}; -constexpr GPIODomain::Pin PA11{A, GPIO_PIN_11}; -constexpr GPIODomain::Pin PA12{A, GPIO_PIN_12}; -constexpr GPIODomain::Pin PA13{A, GPIO_PIN_13}; -constexpr GPIODomain::Pin PA14{A, GPIO_PIN_14}; -constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15}; +constexpr GPIODomain::Pin PA1{A, GPIO_PIN_1, 0b0111100111111011}; +constexpr GPIODomain::Pin PA2{A, GPIO_PIN_2, 0b0111101110011011}; +constexpr GPIODomain::Pin PA3{A, GPIO_PIN_3, 0b0111111101111011}; +constexpr GPIODomain::Pin PA4{A, GPIO_PIN_4, 0b1010011110001111}; +constexpr GPIODomain::Pin PA5{A, GPIO_PIN_5, 0b1101010010101111}; +constexpr GPIODomain::Pin PA6{A, GPIO_PIN_6, 0b0111011011111111}; +constexpr GPIODomain::Pin PA7{A, GPIO_PIN_7, 0b0111010011111011}; +constexpr GPIODomain::Pin PA8{A, GPIO_PIN_8, 0b1101101100111111}; +constexpr GPIODomain::Pin PA9{A, GPIO_PIN_9, 0b0101111100010111}; +constexpr GPIODomain::Pin PA10{A, GPIO_PIN_10, 0b0101110100010111}; +constexpr GPIODomain::Pin PA11{A, GPIO_PIN_11, 0b0101111110010011}; +constexpr GPIODomain::Pin PA12{A, GPIO_PIN_12, 0b0011111110010111}; +constexpr GPIODomain::Pin PA13{A, GPIO_PIN_13, 0b1000000000000001}; +constexpr GPIODomain::Pin PA14{A, GPIO_PIN_14, 0b1000000000000001}; +constexpr GPIODomain::Pin PA15{A, GPIO_PIN_15, 0b0111010111111011}; // Port B -constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0}; -constexpr GPIODomain::Pin PB1{B, GPIO_PIN_1}; -constexpr GPIODomain::Pin PB2{B, GPIO_PIN_2}; -constexpr GPIODomain::Pin PB3{B, GPIO_PIN_3}; -constexpr GPIODomain::Pin PB4{B, GPIO_PIN_4}; -constexpr GPIODomain::Pin PB5{B, GPIO_PIN_5}; -constexpr GPIODomain::Pin PB6{B, GPIO_PIN_6}; -constexpr GPIODomain::Pin PB7{B, GPIO_PIN_7}; -constexpr GPIODomain::Pin PB8{B, GPIO_PIN_8}; -constexpr GPIODomain::Pin PB9{B, GPIO_PIN_9}; -constexpr GPIODomain::Pin PB10{B, GPIO_PIN_10}; -constexpr GPIODomain::Pin PB11{B, GPIO_PIN_11}; -constexpr GPIODomain::Pin PB12{B, GPIO_PIN_12}; -constexpr GPIODomain::Pin PB13{B, GPIO_PIN_13}; -constexpr GPIODomain::Pin PB14{B, GPIO_PIN_14}; -constexpr GPIODomain::Pin PB15{B, GPIO_PIN_15}; +constexpr GPIODomain::Pin PB0{B, GPIO_PIN_0, 0b0111110011111011}; +constexpr GPIODomain::Pin PB1{B, GPIO_PIN_1, 0b0111110011111011}; +constexpr GPIODomain::Pin PB2{B, GPIO_PIN_2, 0b0110111110111011}; +constexpr GPIODomain::Pin PB3{B, GPIO_PIN_3, 0b0111110110111011}; +constexpr GPIODomain::Pin PB4{B, GPIO_PIN_4, 0b0111111110110011}; +constexpr GPIODomain::Pin PB5{B, GPIO_PIN_5, 0b0111110111111111}; +constexpr GPIODomain::Pin PB6{B, GPIO_PIN_6, 0b0111110111111111}; +constexpr GPIODomain::Pin PB7{B, GPIO_PIN_7, 0b0111110111111011}; +constexpr GPIODomain::Pin PB8{B, GPIO_PIN_8, 0b0110111111111111}; +constexpr GPIODomain::Pin PB9{B, GPIO_PIN_9, 0b0110111111111111}; +constexpr GPIODomain::Pin PB10{B, GPIO_PIN_10, 0b0111110111111111}; +constexpr GPIODomain::Pin PB11{B, GPIO_PIN_11, 0b0111110111111011}; +constexpr GPIODomain::Pin PB12{B, GPIO_PIN_12, 0b0111110111111011}; +constexpr GPIODomain::Pin PB13{B, GPIO_PIN_13, 0b0111110111111011}; +constexpr GPIODomain::Pin PB14{B, GPIO_PIN_14, 0b0111110011111011}; +constexpr GPIODomain::Pin PB15{B, GPIO_PIN_15, 0b0111110011111011}; // Port C -constexpr GPIODomain::Pin PC0{C, GPIO_PIN_0}; -constexpr GPIODomain::Pin PC1{C, GPIO_PIN_1}; -constexpr GPIODomain::Pin PC2{C, GPIO_PIN_2}; -constexpr GPIODomain::Pin PC3{C, GPIO_PIN_3}; -constexpr GPIODomain::Pin PC4{C, GPIO_PIN_4}; -constexpr GPIODomain::Pin PC5{C, GPIO_PIN_5}; -constexpr GPIODomain::Pin PC6{C, GPIO_PIN_6}; -constexpr GPIODomain::Pin PC7{C, GPIO_PIN_7}; -constexpr GPIODomain::Pin PC8{C, GPIO_PIN_8}; -constexpr GPIODomain::Pin PC9{C, GPIO_PIN_9}; -constexpr GPIODomain::Pin PC10{C, GPIO_PIN_10}; -constexpr GPIODomain::Pin PC11{C, GPIO_PIN_11}; -constexpr GPIODomain::Pin PC12{C, GPIO_PIN_12}; -constexpr GPIODomain::Pin PC13{C, GPIO_PIN_13}; -constexpr GPIODomain::Pin PC14{C, GPIO_PIN_14}; -constexpr GPIODomain::Pin PC15{C, GPIO_PIN_15}; +constexpr GPIODomain::Pin PC0{C, GPIO_PIN_0, 0b0110110110110011}; +constexpr GPIODomain::Pin PC1{C, GPIO_PIN_1, 0b0110111111111111}; +constexpr GPIODomain::Pin PC2{C, GPIO_PIN_2, 0b0110111110110011}; +constexpr GPIODomain::Pin PC3{C, GPIO_PIN_3, 0b0110111110110011}; +constexpr GPIODomain::Pin PC4{C, GPIO_PIN_4, 0b0110110110111011}; +constexpr GPIODomain::Pin PC5{C, GPIO_PIN_5, 0b0110110110111111}; +constexpr GPIODomain::Pin PC6{C, GPIO_PIN_6, 0b0111110011111111}; +constexpr GPIODomain::Pin PC7{C, GPIO_PIN_7, 0b0111110011111111}; +constexpr GPIODomain::Pin PC8{C, GPIO_PIN_8, 0b0111110011011111}; +constexpr GPIODomain::Pin PC9{C, GPIO_PIN_9, 0b0111110110111111}; +constexpr GPIODomain::Pin PC10{C, GPIO_PIN_10, 0b0110111111111111}; +constexpr GPIODomain::Pin PC11{C, GPIO_PIN_11, 0b0110111111111111}; +constexpr GPIODomain::Pin PC12{C, GPIO_PIN_12, 0b0110111111011111}; +constexpr GPIODomain::Pin PC13{C, GPIO_PIN_13, 0b1000000000000001}; +constexpr GPIODomain::Pin PC14{C, GPIO_PIN_14, 0b1000000000000001}; +constexpr GPIODomain::Pin PC15{C, GPIO_PIN_15, 0b1000000000000001}; // Port D -constexpr GPIODomain::Pin PD0{D, GPIO_PIN_0}; -constexpr GPIODomain::Pin PD1{D, GPIO_PIN_1}; -constexpr GPIODomain::Pin PD2{D, GPIO_PIN_2}; -constexpr GPIODomain::Pin PD3{D, GPIO_PIN_3}; -constexpr GPIODomain::Pin PD4{D, GPIO_PIN_4}; -constexpr GPIODomain::Pin PD5{D, GPIO_PIN_5}; -constexpr GPIODomain::Pin PD6{D, GPIO_PIN_6}; -constexpr GPIODomain::Pin PD7{D, GPIO_PIN_7}; -constexpr GPIODomain::Pin PD8{D, GPIO_PIN_8}; -constexpr GPIODomain::Pin PD9{D, GPIO_PIN_9}; -constexpr GPIODomain::Pin PD10{D, GPIO_PIN_10}; -constexpr GPIODomain::Pin PD11{D, GPIO_PIN_11}; -constexpr GPIODomain::Pin PD12{D, GPIO_PIN_12}; -constexpr GPIODomain::Pin PD13{D, GPIO_PIN_13}; -constexpr GPIODomain::Pin PD14{D, GPIO_PIN_14}; -constexpr GPIODomain::Pin PD15{D, GPIO_PIN_15}; +constexpr GPIODomain::Pin PD0{D, GPIO_PIN_0, 0b0111110110011011}; +constexpr GPIODomain::Pin PD1{D, GPIO_PIN_1, 0b0111110110011011}; +constexpr GPIODomain::Pin PD2{D, GPIO_PIN_2, 0b1110110110011011}; +constexpr GPIODomain::Pin PD3{D, GPIO_PIN_3, 0b0110110111110011}; +constexpr GPIODomain::Pin PD4{D, GPIO_PIN_4, 0b0010110010010011}; +constexpr GPIODomain::Pin PD5{D, GPIO_PIN_5, 0b0010110010010011}; +constexpr GPIODomain::Pin PD6{D, GPIO_PIN_6, 0b0111110110111011}; +constexpr GPIODomain::Pin PD7{D, GPIO_PIN_7, 0b0110110111111011}; +constexpr GPIODomain::Pin PD8{D, GPIO_PIN_8, 0b0110110110010011}; +constexpr GPIODomain::Pin PD9{D, GPIO_PIN_9, 0b0110110110010011}; +constexpr GPIODomain::Pin PD10{D, GPIO_PIN_10, 0b0110110110010011}; +constexpr GPIODomain::Pin PD11{D, GPIO_PIN_11, 0b0111100110110011}; +constexpr GPIODomain::Pin PD12{D, GPIO_PIN_12, 0b0111110110110011}; +constexpr GPIODomain::Pin PD13{D, GPIO_PIN_13, 0b0111110110110011}; +constexpr GPIODomain::Pin PD14{D, GPIO_PIN_14, 0b0111010010010011}; +constexpr GPIODomain::Pin PD15{D, GPIO_PIN_15, 0b0111010010010011}; // Port E -constexpr GPIODomain::Pin PE0{E, GPIO_PIN_0}; -constexpr GPIODomain::Pin PE1{E, GPIO_PIN_1}; -constexpr GPIODomain::Pin PE2{E, GPIO_PIN_2}; -constexpr GPIODomain::Pin PE3{E, GPIO_PIN_3}; -constexpr GPIODomain::Pin PE4{E, GPIO_PIN_4}; -constexpr GPIODomain::Pin PE5{E, GPIO_PIN_5}; -constexpr GPIODomain::Pin PE6{E, GPIO_PIN_6}; -constexpr GPIODomain::Pin PE7{E, GPIO_PIN_7}; -constexpr GPIODomain::Pin PE8{E, GPIO_PIN_8}; -constexpr GPIODomain::Pin PE9{E, GPIO_PIN_9}; -constexpr GPIODomain::Pin PE10{E, GPIO_PIN_10}; -constexpr GPIODomain::Pin PE11{E, GPIO_PIN_11}; -constexpr GPIODomain::Pin PE12{E, GPIO_PIN_12}; -constexpr GPIODomain::Pin PE13{E, GPIO_PIN_13}; -constexpr GPIODomain::Pin PE14{E, GPIO_PIN_14}; -constexpr GPIODomain::Pin PE15{E, GPIO_PIN_15}; +constexpr GPIODomain::Pin PE0{E, GPIO_PIN_0, 0b0111110010110011}; +constexpr GPIODomain::Pin PE1{E, GPIO_PIN_1, 0b0111110010011011}; +constexpr GPIODomain::Pin PE2{E, GPIO_PIN_2, 0b0110110111110011}; +constexpr GPIODomain::Pin PE3{E, GPIO_PIN_3, 0b0110110111110011}; +constexpr GPIODomain::Pin PE4{E, GPIO_PIN_4, 0b0110110111111111}; +constexpr GPIODomain::Pin PE5{E, GPIO_PIN_5, 0b0110110111111011}; +constexpr GPIODomain::Pin PE6{E, GPIO_PIN_6, 0b0110110111111111}; +constexpr GPIODomain::Pin PE7{E, GPIO_PIN_7, 0b0111110010010011}; +constexpr GPIODomain::Pin PE8{E, GPIO_PIN_8, 0b0111110010010011}; +constexpr GPIODomain::Pin PE9{E, GPIO_PIN_9, 0b0111110010010011}; +constexpr GPIODomain::Pin PE10{E, GPIO_PIN_10, 0b0111110010010011}; +constexpr GPIODomain::Pin PE11{E, GPIO_PIN_11, 0b0111110010011011}; +constexpr GPIODomain::Pin PE12{E, GPIO_PIN_12, 0b0111110010011011}; +constexpr GPIODomain::Pin PE13{E, GPIO_PIN_13, 0b0111110010011011}; +constexpr GPIODomain::Pin PE14{E, GPIO_PIN_14, 0b0011110010010011}; +constexpr GPIODomain::Pin PE15{E, GPIO_PIN_15, 0b0110110010010011}; // Port F -constexpr GPIODomain::Pin PF0{F, GPIO_PIN_0}; -constexpr GPIODomain::Pin PF1{F, GPIO_PIN_1}; -constexpr GPIODomain::Pin PF2{F, GPIO_PIN_2}; -constexpr GPIODomain::Pin PF3{F, GPIO_PIN_3}; -constexpr GPIODomain::Pin PF4{F, GPIO_PIN_4}; -constexpr GPIODomain::Pin PF5{F, GPIO_PIN_5}; -constexpr GPIODomain::Pin PF6{F, GPIO_PIN_6}; -constexpr GPIODomain::Pin PF7{F, GPIO_PIN_7}; -constexpr GPIODomain::Pin PF8{F, GPIO_PIN_8}; -constexpr GPIODomain::Pin PF9{F, GPIO_PIN_9}; -constexpr GPIODomain::Pin PF10{F, GPIO_PIN_10}; -constexpr GPIODomain::Pin PF11{F, GPIO_PIN_11}; -constexpr GPIODomain::Pin PF12{F, GPIO_PIN_12}; -constexpr GPIODomain::Pin PF13{F, GPIO_PIN_13}; -constexpr GPIODomain::Pin PF14{F, GPIO_PIN_14}; -constexpr GPIODomain::Pin PF15{F, GPIO_PIN_15}; +constexpr GPIODomain::Pin PF0{F, GPIO_PIN_0, 0b0010110010010011}; +constexpr GPIODomain::Pin PF1{F, GPIO_PIN_1, 0b0010110010010011}; +constexpr GPIODomain::Pin PF2{F, GPIO_PIN_2, 0b0010110010010011}; +constexpr GPIODomain::Pin PF3{F, GPIO_PIN_3, 0b0010010010010011}; +constexpr GPIODomain::Pin PF4{F, GPIO_PIN_4, 0b0010010010010011}; +constexpr GPIODomain::Pin PF5{F, GPIO_PIN_5, 0b0010010010010011}; +constexpr GPIODomain::Pin PF6{F, GPIO_PIN_6, 0b0111110010110011}; +constexpr GPIODomain::Pin PF7{F, GPIO_PIN_7, 0b0111110010110011}; +constexpr GPIODomain::Pin PF8{F, GPIO_PIN_8, 0b0111110010111011}; +constexpr GPIODomain::Pin PF9{F, GPIO_PIN_9, 0b0111110010111011}; +constexpr GPIODomain::Pin PF10{F, GPIO_PIN_10, 0b0110110010110011}; +constexpr GPIODomain::Pin PF11{F, GPIO_PIN_11, 0b0010110010010011}; +constexpr GPIODomain::Pin PF12{F, GPIO_PIN_12, 0b0010010010010011}; +constexpr GPIODomain::Pin PF13{F, GPIO_PIN_13, 0b0110110010110011}; +constexpr GPIODomain::Pin PF14{F, GPIO_PIN_14, 0b0110110010110011}; +constexpr GPIODomain::Pin PF15{F, GPIO_PIN_15, 0b0010110010010011}; // Port G -constexpr GPIODomain::Pin PG0{G, GPIO_PIN_0}; -constexpr GPIODomain::Pin PG1{G, GPIO_PIN_1}; -constexpr GPIODomain::Pin PG2{G, GPIO_PIN_2}; -constexpr GPIODomain::Pin PG3{G, GPIO_PIN_3}; -constexpr GPIODomain::Pin PG4{G, GPIO_PIN_4}; -constexpr GPIODomain::Pin PG5{G, GPIO_PIN_5}; -constexpr GPIODomain::Pin PG6{G, GPIO_PIN_6}; -constexpr GPIODomain::Pin PG7{G, GPIO_PIN_7}; -constexpr GPIODomain::Pin PG8{G, GPIO_PIN_8}; -constexpr GPIODomain::Pin PG9{G, GPIO_PIN_9}; -constexpr GPIODomain::Pin PG10{G, GPIO_PIN_10}; -constexpr GPIODomain::Pin PG11{G, GPIO_PIN_11}; -constexpr GPIODomain::Pin PG12{G, GPIO_PIN_12}; -constexpr GPIODomain::Pin PG13{G, GPIO_PIN_13}; -constexpr GPIODomain::Pin PG14{G, GPIO_PIN_14}; -constexpr GPIODomain::Pin PG15{G, GPIO_PIN_15}; +constexpr GPIODomain::Pin PG0{G, GPIO_PIN_0, 0b0010010010010011}; +constexpr GPIODomain::Pin PG1{G, GPIO_PIN_1, 0b0010010010010011}; +constexpr GPIODomain::Pin PG2{G, GPIO_PIN_2, 0b0110010010010011}; +constexpr GPIODomain::Pin PG3{G, GPIO_PIN_3, 0b0110010010010011}; +constexpr GPIODomain::Pin PG4{G, GPIO_PIN_4, 0b0110010010010011}; +constexpr GPIODomain::Pin PG5{G, GPIO_PIN_5, 0b0010010010010011}; +constexpr GPIODomain::Pin PG6{G, GPIO_PIN_6, 0b0111110010110011}; +constexpr GPIODomain::Pin PG7{G, GPIO_PIN_7, 0b0110110010110011}; +constexpr GPIODomain::Pin PG8{G, GPIO_PIN_8, 0b0111110010110011}; +constexpr GPIODomain::Pin PG9{G, GPIO_PIN_9, 0b0110110110110011}; +constexpr GPIODomain::Pin PG10{G, GPIO_PIN_10, 0b0110110110111011}; +constexpr GPIODomain::Pin PG11{G, GPIO_PIN_11, 0b0110110110111011}; +constexpr GPIODomain::Pin PG12{G, GPIO_PIN_12, 0b0111110110111111}; +constexpr GPIODomain::Pin PG13{G, GPIO_PIN_13, 0b0111110110111111}; +constexpr GPIODomain::Pin PG14{G, GPIO_PIN_14, 0b0111110110011111}; +constexpr GPIODomain::Pin PG15{G, GPIO_PIN_15, 0b0110110010010011}; // Port H -constexpr GPIODomain::Pin PH0{H, GPIO_PIN_0}; -constexpr GPIODomain::Pin PH1{H, GPIO_PIN_1}; -constexpr GPIODomain::Pin PH2{H, GPIO_PIN_2}; -constexpr GPIODomain::Pin PH3{H, GPIO_PIN_3}; -constexpr GPIODomain::Pin PH4{H, GPIO_PIN_4}; -constexpr GPIODomain::Pin PH5{H, GPIO_PIN_5}; -constexpr GPIODomain::Pin PH6{H, GPIO_PIN_6}; -constexpr GPIODomain::Pin PH7{H, GPIO_PIN_7}; -constexpr GPIODomain::Pin PH8{H, GPIO_PIN_8}; -constexpr GPIODomain::Pin PH9{H, GPIO_PIN_9}; -constexpr GPIODomain::Pin PH10{H, GPIO_PIN_10}; -constexpr GPIODomain::Pin PH11{H, GPIO_PIN_11}; -constexpr GPIODomain::Pin PH12{H, GPIO_PIN_12}; -constexpr GPIODomain::Pin PH13{H, GPIO_PIN_13}; -constexpr GPIODomain::Pin PH14{H, GPIO_PIN_14}; -constexpr GPIODomain::Pin PH15{H, GPIO_PIN_15}; +constexpr GPIODomain::Pin PH0{H, GPIO_PIN_0, 0b1000000000000001}; +constexpr GPIODomain::Pin PH1{H, GPIO_PIN_1, 0b1000000000000001}; -} // namespace ST_LIB \ No newline at end of file +} // namespace ST_LIB From 4cd2796f8c2ba0a448769da28d331dba45b6ca22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Mon, 8 Dec 2025 12:41:27 +0100 Subject: [PATCH 069/166] Added linker script and startup code, and fixed using hal template conf --- .cproject | 1012 ----------------- .mxproject | 12 - .project | 32 - CMakeLists.txt | 16 + ...l_conf.h => stm32h7xx_hal_conf_template.h} | 2 +- STM32H723ZGTX_FLASH.ld | 209 ++++ STM32H723ZGTX_RAM.ld | 173 +++ setup.env | 1 - startup_stm32h723zgtx.s | 756 ++++++++++++ 9 files changed, 1155 insertions(+), 1058 deletions(-) delete mode 100644 .cproject delete mode 100644 .mxproject delete mode 100644 .project rename Inc/{stm32h7xx_hal_conf.h => stm32h7xx_hal_conf_template.h} (99%) create mode 100644 STM32H723ZGTX_FLASH.ld create mode 100644 STM32H723ZGTX_RAM.ld delete mode 100644 setup.env create mode 100644 startup_stm32h723zgtx.s diff --git a/.cproject b/.cproject deleted file mode 100644 index ff4c420e3..000000000 --- a/.cproject +++ /dev/null @@ -1,1012 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.mxproject b/.mxproject deleted file mode 100644 index d59c167e7..000000000 --- a/.mxproject +++ /dev/null @@ -1,12 +0,0 @@ -[PreviousGenFiles] -SourcePath=..\Src -SourceFiles=ST-LIB.c; - -[PreviousLibFiles] -LibFiles=Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h723xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Include\system_stm32h7xx.h;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Drivers\CMSIS\Include\cmsis_armcc.h;Drivers\CMSIS\Include\cmsis_armclang.h;Drivers\CMSIS\Include\cmsis_armclang_ltm.h;Drivers\CMSIS\Include\cmsis_compiler.h;Drivers\CMSIS\Include\cmsis_gcc.h;Drivers\CMSIS\Include\cmsis_iccarm.h;Drivers\CMSIS\Include\cmsis_version.h;Drivers\CMSIS\Include\core_armv81mml.h;Drivers\CMSIS\Include\core_armv8mbl.h;Drivers\CMSIS\Include\core_armv8mml.h;Drivers\CMSIS\Include\core_cm0.h;Drivers\CMSIS\Include\core_cm0plus.h;Drivers\CMSIS\Include\core_cm1.h;Drivers\CMSIS\Include\core_cm23.h;Drivers\CMSIS\Include\core_cm3.h;Drivers\CMSIS\Include\core_cm33.h;Drivers\CMSIS\Include\core_cm35p.h;Drivers\CMSIS\Include\core_cm4.h;Drivers\CMSIS\Include\core_cm7.h;Drivers\CMSIS\Include\core_sc000.h;Drivers\CMSIS\Include\core_sc300.h;Drivers\CMSIS\Include\mpu_armv7.h;Drivers\CMSIS\Include\mpu_armv8.h;Drivers\CMSIS\Include\tz_context.h; - -[PreviousUsedCubeIDEFiles] -SourceFiles=Src\ST-LIB.c;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Src\stm32h7xx_hal_conf_template.h;Drivers\CMSIS\Device\ST\STM32H7xx\Source\Templates\system_stm32h7xx.c;Src\stm32h7xx_hal_conf_template.h;;; -HeaderPath=Drivers\CMSIS\Device\ST\STM32H7xx\Include;Drivers\CMSIS\Include;Inc;Drivers\STM32H7xx_HAL_Driver\Inc;Drivers\STM32H7xx_HAL_Driver\Inc\Legacy; -CDefines=USE_HAL_DRIVER;STM32H723xx;USE_HAL_DRIVER;USE_HAL_DRIVER; - diff --git a/.project b/.project deleted file mode 100644 index 32ef24281..000000000 --- a/.project +++ /dev/null @@ -1,32 +0,0 @@ - - - ST-LIB - - - - - - org.eclipse.cdt.managedbuilder.core.genmakebuilder - clean,full,incremental, - - - - - org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder - full,incremental, - - - - - - com.st.stm32cube.ide.mcu.MCUProjectNature - org.eclipse.cdt.core.cnature - org.eclipse.cdt.core.ccnature - com.st.stm32cube.ide.mcu.MCUCubeIdeServicesRevAev2ProjectNature - com.st.stm32cube.ide.mcu.MCUCubeProjectNature - com.st.stm32cube.ide.mcu.MCUSingleCpuProjectNature - com.st.stm32cube.ide.mcu.MCURootProjectNature - org.eclipse.cdt.managedbuilder.core.managedBuildNature - org.eclipse.cdt.managedbuilder.core.ScannerConfigNature - - diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba062c27..f956d7aba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -313,6 +313,22 @@ target_include_directories(${STLIB_LIBRARY} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/Inc/ST-LIB_HIGH ) +if(PROJECT_IS_TOP_LEVEL) + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/Inc/stm32h7xx_hal_conf_template.h + ${CMAKE_CURRENT_BINARY_DIR}/stm32h7xx_hal_conf.h + COPYONLY + ) + + target_include_directories(${STLIB_LIBRARY} PUBLIC + ${CMAKE_CURRENT_BINARY_DIR} + ) +else() + target_include_directories(${STLIB_LIBRARY} PUBLIC + ${CMAKE_SOURCE_DIR}/Core/Inc + ) +endif() + if(PROJECT_IS_TOP_LEVEL) execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink diff --git a/Inc/stm32h7xx_hal_conf.h b/Inc/stm32h7xx_hal_conf_template.h similarity index 99% rename from Inc/stm32h7xx_hal_conf.h rename to Inc/stm32h7xx_hal_conf_template.h index 74fdc0c32..4f41c7d25 100644 --- a/Inc/stm32h7xx_hal_conf.h +++ b/Inc/stm32h7xx_hal_conf_template.h @@ -233,7 +233,7 @@ * @brief Uncomment the line below to expanse the "assert_param" macro in the * HAL drivers code */ -#define USE_FULL_ASSERT 1 +/* #define USE_FULL_ASSERT 1U */ /* Includes ------------------------------------------------------------------*/ diff --git a/STM32H723ZGTX_FLASH.ld b/STM32H723ZGTX_FLASH.ld new file mode 100644 index 000000000..12b8d7793 --- /dev/null +++ b/STM32H723ZGTX_FLASH.ld @@ -0,0 +1,209 @@ +/* +****************************************************************************** +** +** File : LinkerScript.ld +** +** Author : STM32CubeIDE +** +** Abstract : Linker script for STM32H7 series +** 1024Kbytes FLASH and 560Kbytes RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +***************************************************************************** +** @attention +** +** Copyright (c) 2022 STMicroelectronics. +** All rights reserved. +** +** This software is licensed under terms that can be found in the LICENSE file +** in the root directory of this software component. +** If no LICENSE file comes with this software, it is provided AS-IS. +** +**************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1); /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200; /* required amount of heap */ +_Min_Stack_Size = 0x400; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K + DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K-128K + RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 320K + RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K + RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into FLASH */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >FLASH + + /* The program code and other data goes into FLASH */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >FLASH + + /* Constant data goes into FLASH */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >FLASH + + .ARM.extab (READONLY): { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH + .ARM (READONLY): { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >FLASH + + .preinit_array (READONLY): + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >FLASH + + .init_array (READONLY): + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >FLASH + + .fini_array (READONLY): + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >FLASH + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >RAM_D1 AT> FLASH + /* + this needs to be the last thing in FLASH + because the preceeding sections are appended after the one preceeding them + this is, if this were the first thing in FLASH + the sections below it would try to be placed afterwards + thus overflowing the FLASH + */ + .metadata_pool : + { + . = ABSOLUTE(0x080DFD00); + . = ALIGN(4); + metadata = .; + KEEP(*(.metadata_pool)) + . += 0x100; + } >FLASH + /* Uninitialized data section */ + . = ALIGN(4); + .bss (NOLOAD) : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + /* ETH_CODE: add placement of RX buffer. STM32H72x/H73x has small D2 RAM, so we need to put it there. + * (NOLOAD) attribute used for .bss section to avoid linker warning (.bss initialized by startup code) + */ + . = ALIGN(32); + *(.Rx_PoolSection) + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM_D1 + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >RAM_D1 + + /* ETH_CODE: add placement of DMA descriptors, rest is used by RX_POOL */ + .lwip_sec (NOLOAD) : + { + . = ABSOLUTE(0x30000000); + *(.RxDecripSection) + + . = ABSOLUTE(0x30000100); + *(.TxDecripSection) + + } >RAM_D2 + .stlib_no_cache_ram_pool : + { + . = ABSOLUTE(0x38000000); + _no_cached_ram_start = .; + + } >RAM_D3 + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/STM32H723ZGTX_RAM.ld b/STM32H723ZGTX_RAM.ld new file mode 100644 index 000000000..4d2e09c43 --- /dev/null +++ b/STM32H723ZGTX_RAM.ld @@ -0,0 +1,173 @@ +/* +****************************************************************************** +** +** File : LinkerScript.ld (debug in RAM dedicated) +** +** Author : STM32CubeIDE +** +** Abstract : Linker script for STM32H7 series +** 320Kbytes RAM_EXEC and 240Kbytes RAM +** +** Set heap size, stack size and stack location according +** to application requirements. +** +** Set memory bank area and size if external memory is used. +** +** Target : STMicroelectronics STM32 +** +** Distribution: The file is distributed as is, without any warranty +** of any kind. +** +***************************************************************************** +** @attention +** +** Copyright (c) 2022 STMicroelectronics. +** All rights reserved. +** +** This software is licensed under terms that can be found in the LICENSE file +** in the root directory of this software component. +** If no LICENSE file comes with this software, it is provided AS-IS. +** +**************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM); /* end of RAM */ +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0x200 ; /* required amount of heap */ +_Min_Stack_Size = 0x400 ; /* required amount of stack */ + +/* Specify the memory areas */ +MEMORY +{ + RAM_EXEC (xrw) : ORIGIN = 0x24000000, LENGTH = 320K + DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K + ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K + RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 32K + RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 16K +} + +/* Define output sections */ +SECTIONS +{ + /* The startup code goes first into RAM_EXEC */ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + } >RAM_EXEC + + /* The program code and other data goes into RAM_EXEC */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + *(.RamFunc) /* .RamFunc sections */ + *(.RamFunc*) /* .RamFunc* sections */ + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >RAM_EXEC + + /* Constant data goes into RAM_EXEC */ + .rodata : + { + . = ALIGN(4); + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + . = ALIGN(4); + } >RAM_EXEC + + .ARM.extab (READONLY): { *(.ARM.extab* .gnu.linkonce.armextab.*) } >RAM_EXEC + .ARM (READONLY): { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >RAM_EXEC + + .preinit_array (READONLY): + { + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP (*(.preinit_array*)) + PROVIDE_HIDDEN (__preinit_array_end = .); + } >RAM_EXEC + + .init_array (READONLY): + { + PROVIDE_HIDDEN (__init_array_start = .); + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array*)) + PROVIDE_HIDDEN (__init_array_end = .); + } >RAM_EXEC + + .fini_array (READONLY): + { + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP (*(SORT(.fini_array.*))) + KEEP (*(.fini_array*)) + PROVIDE_HIDDEN (__fini_array_end = .); + } >RAM_EXEC + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >DTCMRAM AT> RAM_EXEC + + /* Uninitialized data section */ + . = ALIGN(4); + .bss : + { + /* This is used by the startup in order to initialize the .bss section */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >DTCMRAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } >DTCMRAM + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/setup.env b/setup.env deleted file mode 100644 index 6d1022f4f..000000000 --- a/setup.env +++ /dev/null @@ -1 +0,0 @@ -export STLIB_PATH=$PWD \ No newline at end of file diff --git a/startup_stm32h723zgtx.s b/startup_stm32h723zgtx.s new file mode 100644 index 000000000..73d8bd295 --- /dev/null +++ b/startup_stm32h723zgtx.s @@ -0,0 +1,756 @@ +/** + ****************************************************************************** + * @file startup_stm32h723xx.s + * @author MCD Application Team + * @brief STM32H723xx Devices vector table for GCC based toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2019 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m7 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + +/* Call the clock system initialization function.*/ + bl SystemInit + +/* Copy the data segment initializers from flash to SRAM */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit +/* Zero fill the bss segment. */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_AVD_IRQHandler /* PVD/AVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word FDCAN1_IT0_IRQHandler /* FDCAN1 interrupt line 0 */ + .word FDCAN2_IT0_IRQHandler /* FDCAN2 interrupt line 0 */ + .word FDCAN1_IT1_IRQHandler /* FDCAN1 interrupt line 1 */ + .word FDCAN2_IT1_IRQHandler /* FDCAN2 interrupt line 1 */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_IRQHandler /* TIM1 Break interrupt */ + .word TIM1_UP_IRQHandler /* TIM1 Update interrupt */ + .word TIM1_TRG_COM_IRQHandler /* TIM1 Trigger and Commutation interrupt */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word 0 /* Reserved */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FMC_IRQHandler /* FMC */ + .word SDMMC1_IRQHandler /* SDMMC1 */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word FDCAN_CAL_IRQHandler /* FDCAN calibration unit interrupt*/ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_PSSI_IRQHandler /* DCMI, PSSI */ + .word 0 /* Reserved */ + .word RNG_IRQHandler /* Rng */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word LTDC_IRQHandler /* LTDC */ + .word LTDC_ER_IRQHandler /* LTDC error */ + .word DMA2D_IRQHandler /* DMA2D */ + .word 0 /* Reserved */ + .word OCTOSPI1_IRQHandler /* OCTOSPI1 */ + .word LPTIM1_IRQHandler /* LPTIM1 */ + .word CEC_IRQHandler /* HDMI_CEC */ + .word I2C4_EV_IRQHandler /* I2C4 Event */ + .word I2C4_ER_IRQHandler /* I2C4 Error */ + .word SPDIF_RX_IRQHandler /* SPDIF_RX */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word DMAMUX1_OVR_IRQHandler /* DMAMUX1 Overrun interrupt */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word DFSDM1_FLT0_IRQHandler /* DFSDM Filter0 Interrupt */ + .word DFSDM1_FLT1_IRQHandler /* DFSDM Filter1 Interrupt */ + .word DFSDM1_FLT2_IRQHandler /* DFSDM Filter2 Interrupt */ + .word DFSDM1_FLT3_IRQHandler /* DFSDM Filter3 Interrupt */ + .word 0 /* Reserved */ + .word SWPMI1_IRQHandler /* Serial Wire Interface 1 global interrupt */ + .word TIM15_IRQHandler /* TIM15 global Interrupt */ + .word TIM16_IRQHandler /* TIM16 global Interrupt */ + .word TIM17_IRQHandler /* TIM17 global Interrupt */ + .word MDIOS_WKUP_IRQHandler /* MDIOS Wakeup Interrupt */ + .word MDIOS_IRQHandler /* MDIOS global Interrupt */ + .word 0 /* Reserved */ + .word MDMA_IRQHandler /* MDMA global Interrupt */ + .word 0 /* Reserved */ + .word SDMMC2_IRQHandler /* SDMMC2 global Interrupt */ + .word HSEM1_IRQHandler /* HSEM1 global Interrupt */ + .word 0 /* Reserved */ + .word ADC3_IRQHandler /* ADC3 global Interrupt */ + .word DMAMUX2_OVR_IRQHandler /* DMAMUX Overrun interrupt */ + .word BDMA_Channel0_IRQHandler /* BDMA Channel 0 global Interrupt */ + .word BDMA_Channel1_IRQHandler /* BDMA Channel 1 global Interrupt */ + .word BDMA_Channel2_IRQHandler /* BDMA Channel 2 global Interrupt */ + .word BDMA_Channel3_IRQHandler /* BDMA Channel 3 global Interrupt */ + .word BDMA_Channel4_IRQHandler /* BDMA Channel 4 global Interrupt */ + .word BDMA_Channel5_IRQHandler /* BDMA Channel 5 global Interrupt */ + .word BDMA_Channel6_IRQHandler /* BDMA Channel 6 global Interrupt */ + .word BDMA_Channel7_IRQHandler /* BDMA Channel 7 global Interrupt */ + .word COMP1_IRQHandler /* COMP1 global Interrupt */ + .word LPTIM2_IRQHandler /* LP TIM2 global interrupt */ + .word LPTIM3_IRQHandler /* LP TIM3 global interrupt */ + .word LPTIM4_IRQHandler /* LP TIM4 global interrupt */ + .word LPTIM5_IRQHandler /* LP TIM5 global interrupt */ + .word LPUART1_IRQHandler /* LP UART1 interrupt */ + .word 0 /* Reserved */ + .word CRS_IRQHandler /* Clock Recovery Global Interrupt */ + .word ECC_IRQHandler /* ECC diagnostic Global Interrupt */ + .word SAI4_IRQHandler /* SAI4 global interrupt */ + .word DTS_IRQHandler /* Digital Temperature Sensor interrupt */ + .word 0 /* Reserved */ + .word WAKEUP_PIN_IRQHandler /* Interrupt for all 6 wake-up pins */ + .word OCTOSPI2_IRQHandler /* OCTOSPI2 Interrupt */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word FMAC_IRQHandler /* FMAC Interrupt */ + .word CORDIC_IRQHandler /* CORDIC Interrupt */ + .word UART9_IRQHandler /* UART9 Interrupt */ + .word USART10_IRQHandler /* UART10 Interrupt */ + .word I2C5_EV_IRQHandler /* I2C5 Event Interrupt */ + .word I2C5_ER_IRQHandler /* I2C5 Error Interrupt */ + .word FDCAN3_IT0_IRQHandler /* FDCAN3 interrupt line 0 */ + .word FDCAN3_IT1_IRQHandler /* FDCAN3 interrupt line 1 */ + .word TIM23_IRQHandler /* TIM23 global interrupt */ + .word TIM24_IRQHandler /* TIM24 global interrupt */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_AVD_IRQHandler + .thumb_set PVD_AVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak FDCAN1_IT0_IRQHandler + .thumb_set FDCAN1_IT0_IRQHandler,Default_Handler + + .weak FDCAN2_IT0_IRQHandler + .thumb_set FDCAN2_IT0_IRQHandler,Default_Handler + + .weak FDCAN1_IT1_IRQHandler + .thumb_set FDCAN1_IT1_IRQHandler,Default_Handler + + .weak FDCAN2_IT1_IRQHandler + .thumb_set FDCAN2_IT1_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_IRQHandler + .thumb_set TIM1_BRK_IRQHandler,Default_Handler + + .weak TIM1_UP_IRQHandler + .thumb_set TIM1_UP_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_IRQHandler + .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FMC_IRQHandler + .thumb_set FMC_IRQHandler,Default_Handler + + .weak SDMMC1_IRQHandler + .thumb_set SDMMC1_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak FDCAN_CAL_IRQHandler + .thumb_set FDCAN_CAL_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_PSSI_IRQHandler + .thumb_set DCMI_PSSI_IRQHandler,Default_Handler + + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak LTDC_IRQHandler + .thumb_set LTDC_IRQHandler,Default_Handler + + .weak LTDC_ER_IRQHandler + .thumb_set LTDC_ER_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler + + .weak OCTOSPI1_IRQHandler + .thumb_set OCTOSPI1_IRQHandler,Default_Handler + + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak I2C4_EV_IRQHandler + .thumb_set I2C4_EV_IRQHandler,Default_Handler + + .weak I2C4_ER_IRQHandler + .thumb_set I2C4_ER_IRQHandler,Default_Handler + + .weak SPDIF_RX_IRQHandler + .thumb_set SPDIF_RX_IRQHandler,Default_Handler + + .weak DMAMUX1_OVR_IRQHandler + .thumb_set DMAMUX1_OVR_IRQHandler,Default_Handler + + .weak DFSDM1_FLT0_IRQHandler + .thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler + + .weak DFSDM1_FLT1_IRQHandler + .thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler + + .weak DFSDM1_FLT2_IRQHandler + .thumb_set DFSDM1_FLT2_IRQHandler,Default_Handler + + .weak DFSDM1_FLT3_IRQHandler + .thumb_set DFSDM1_FLT3_IRQHandler,Default_Handler + + .weak SWPMI1_IRQHandler + .thumb_set SWPMI1_IRQHandler,Default_Handler + + .weak TIM15_IRQHandler + .thumb_set TIM15_IRQHandler,Default_Handler + + .weak TIM16_IRQHandler + .thumb_set TIM16_IRQHandler,Default_Handler + + .weak TIM17_IRQHandler + .thumb_set TIM17_IRQHandler,Default_Handler + + .weak MDIOS_WKUP_IRQHandler + .thumb_set MDIOS_WKUP_IRQHandler,Default_Handler + + .weak MDIOS_IRQHandler + .thumb_set MDIOS_IRQHandler,Default_Handler + + .weak MDMA_IRQHandler + .thumb_set MDMA_IRQHandler,Default_Handler + + .weak SDMMC2_IRQHandler + .thumb_set SDMMC2_IRQHandler,Default_Handler + + .weak HSEM1_IRQHandler + .thumb_set HSEM1_IRQHandler,Default_Handler + + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + + .weak DMAMUX2_OVR_IRQHandler + .thumb_set DMAMUX2_OVR_IRQHandler,Default_Handler + + .weak BDMA_Channel0_IRQHandler + .thumb_set BDMA_Channel0_IRQHandler,Default_Handler + + .weak BDMA_Channel1_IRQHandler + .thumb_set BDMA_Channel1_IRQHandler,Default_Handler + + .weak BDMA_Channel2_IRQHandler + .thumb_set BDMA_Channel2_IRQHandler,Default_Handler + + .weak BDMA_Channel3_IRQHandler + .thumb_set BDMA_Channel3_IRQHandler,Default_Handler + + .weak BDMA_Channel4_IRQHandler + .thumb_set BDMA_Channel4_IRQHandler,Default_Handler + + .weak BDMA_Channel5_IRQHandler + .thumb_set BDMA_Channel5_IRQHandler,Default_Handler + + .weak BDMA_Channel6_IRQHandler + .thumb_set BDMA_Channel6_IRQHandler,Default_Handler + + .weak BDMA_Channel7_IRQHandler + .thumb_set BDMA_Channel7_IRQHandler,Default_Handler + + .weak COMP1_IRQHandler + .thumb_set COMP1_IRQHandler,Default_Handler + + .weak LPTIM2_IRQHandler + .thumb_set LPTIM2_IRQHandler,Default_Handler + + .weak LPTIM3_IRQHandler + .thumb_set LPTIM3_IRQHandler,Default_Handler + + .weak LPTIM4_IRQHandler + .thumb_set LPTIM4_IRQHandler,Default_Handler + + .weak LPTIM5_IRQHandler + .thumb_set LPTIM5_IRQHandler,Default_Handler + + .weak LPUART1_IRQHandler + .thumb_set LPUART1_IRQHandler,Default_Handler + + .weak CRS_IRQHandler + .thumb_set CRS_IRQHandler,Default_Handler + + .weak ECC_IRQHandler + .thumb_set ECC_IRQHandler,Default_Handler + + .weak SAI4_IRQHandler + .thumb_set SAI4_IRQHandler,Default_Handler + + .weak DTS_IRQHandler + .thumb_set DTS_IRQHandler,Default_Handler + + .weak WAKEUP_PIN_IRQHandler + .thumb_set WAKEUP_PIN_IRQHandler,Default_Handler + + .weak OCTOSPI2_IRQHandler + .thumb_set OCTOSPI2_IRQHandler,Default_Handler + + .weak FMAC_IRQHandler + .thumb_set FMAC_IRQHandler,Default_Handler + + .weak CORDIC_IRQHandler + .thumb_set CORDIC_IRQHandler,Default_Handler + + .weak UART9_IRQHandler + .thumb_set UART9_IRQHandler,Default_Handler + + .weak USART10_IRQHandler + .thumb_set USART10_IRQHandler,Default_Handler + + .weak I2C5_EV_IRQHandler + .thumb_set I2C5_EV_IRQHandler,Default_Handler + + .weak I2C5_ER_IRQHandler + .thumb_set I2C5_ER_IRQHandler,Default_Handler + + .weak FDCAN3_IT0_IRQHandler + .thumb_set FDCAN3_IT0_IRQHandler,Default_Handler + + .weak FDCAN3_IT1_IRQHandler + .thumb_set FDCAN3_IT1_IRQHandler,Default_Handler + + .weak TIM23_IRQHandler + .thumb_set TIM23_IRQHandler,Default_Handler + + .weak TIM24_IRQHandler + .thumb_set TIM24_IRQHandler,Default_Handler + + From 25edffc1ea569bd02ca7f5f6cd2f2c3140e9d11c Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 13:27:49 +0100 Subject: [PATCH 070/166] Working on making the mdma work --- Inc/HALAL/HALAL.hpp | 3 +-- Inc/HALAL/Models/MDMA/MDMA.hpp | 31 ++++++++++++++++++++++++- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 8 +++++-- Src/HALAL/Models/MDMA/MDMA.cpp | 7 ++---- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 01ca7c222..84d8cf2a5 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -57,8 +57,7 @@ #include "HALAL/Services/Communication/Ethernet/TCP/Socket.hpp" #include "HALAL/Services/Communication/Ethernet/Ethernet.hpp" #include "HALAL/Services/Communication/SNTP/SNTP.hpp" -#include "HALAL/Utils/Promise.hpp" -#include "HALALMock/Models/Packets/MdmaPacket.hpp" +// #include "HALAL/Utils/Promise.hpp" #endif namespace HALAL { diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index f4a27eb9d..49aad6cec 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -34,7 +34,7 @@ class MDMA{ */ template LinkedListNode(T* source_ptr, void* dest_ptr) { - MDMA_LinkNodeConfTypeDef nodeConfig; + MDMA_LinkNodeConfTypeDef nodeConfig{}; nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; @@ -61,6 +61,35 @@ class MDMA{ ErrorHandler("Error creating linked list in MDMA"); } } + template + LinkedListNode(T* source_ptr, void* dest_ptr,size_t size) { + MDMA_LinkNodeConfTypeDef nodeConfig{}; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + + this->node = {}; + nodeConfig.SrcAddress = reinterpret_cast(source_ptr); + nodeConfig.DstAddress = reinterpret_cast(dest_ptr); + nodeConfig.BlockDataLength = static_cast(size); + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + + auto status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); + if (status != HAL_OK) { + ErrorHandler("Error creating linked list in MDMA"); + } + } /** * @brief Set the next node in the linked list. diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index aacec86c7..26ce0d59a 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -38,6 +38,10 @@ class MdmaPacket : public Packet { packets[id] = this; // Allocate non-cached buffer for MDMA buffer = reinterpret_cast(MPUManager::allocate_non_cached_memory(size)); + // buffer = new uint8_t[size]; + if (buffer == nullptr) { + ErrorHandler("Failed to allocate MDMA buffer for packet"); + } MDMA::LinkedListNode* prev_node = nullptr; uint32_t offset = 0; @@ -70,8 +74,8 @@ class MdmaPacket : public Packet { }()), ...); }, value_pointers); - build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr); // Used when needed for dynamic destination - parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer); // Used when needed for dynamic origin + build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr,offset); // Used when needed for dynamic destination + parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer,offset); // Used when needed for dynamic origin } /** diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 578872ca9..91b1e1b2d 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -32,14 +32,12 @@ std::unordered_map MDMA::channel_to_instance = { void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) { - if (instance.handle.State == HAL_MDMA_STATE_BUSY || instance.handle.Lock == HAL_LOCKED) + if (instance.handle.State == HAL_MDMA_STATE_BUSY ) { ErrorHandler("MDMA transfer already in progress"); return; } - instance.handle.Lock = HAL_LOCKED; - instance.handle.State = HAL_MDMA_STATE_BUSY; instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; instance.handle.FirstLinkedListNodeAddress = first_node; @@ -71,12 +69,10 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node if (HAL_MDMA_GenerateSWRequest(&instance.handle) != HAL_OK) { instance.handle.State = HAL_MDMA_STATE_READY; - instance.handle.Lock = HAL_UNLOCKED; ErrorHandler("Error generating MDMA SW request"); return; } - instance.handle.Lock = HAL_UNLOCKED; } void MDMA::prepare_transfer(Instance& instance, LinkedListNode* first_node) { MDMA::prepare_transfer(instance, first_node->get_node()); } @@ -272,6 +268,7 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) } Instance& instance = get_instance(channel_it->second); + instance.handle.State = HAL_MDMA_STATE_READY; if(instance.using_promise) { instance.promise->resolve(); From 79bbcd53a8b4a1057c1587aaaa9df417419b15a5 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 14:44:14 +0100 Subject: [PATCH 071/166] Still not working the mdma packets --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 26ce0d59a..46c513037 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -74,8 +74,8 @@ class MdmaPacket : public Packet { }()), ...); }, value_pointers); - build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr,offset); // Used when needed for dynamic destination - parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer,offset); // Used when needed for dynamic origin + build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr,size); // Used when needed for dynamic destination + parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer,size); // Used when needed for dynamic origin } /** From ee27a82627337dde3afb1a1f84a02a56c8d84f7c Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 15:45:02 +0100 Subject: [PATCH 072/166] Esto es peor que el sida de testear --- Inc/HALAL/Models/MDMA/MDMA.hpp | 128 +++++++++--------------- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 24 +++-- 2 files changed, 64 insertions(+), 88 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 49aad6cec..e2ff7447f 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -26,86 +26,56 @@ class MDMA{ * @brief A helper struct to create and manage MDMA linked list nodes. */ struct LinkedListNode { - /** - * @brief Constructor to create an MDMA linked list node. - * @tparam T The type of the data to be transferred. - * @param source_ptr Pointer to the source data. - * @param dest_ptr Pointer to the destination data. - */ - template - LinkedListNode(T* source_ptr, void* dest_ptr) { - MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; - nodeConfig.Init.SourceBlockAddressOffset = 0; - nodeConfig.Init.DestBlockAddressOffset = 0; - nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - - this->node = {}; - nodeConfig.SrcAddress = reinterpret_cast(source_ptr); - nodeConfig.DstAddress = reinterpret_cast(dest_ptr); - nodeConfig.BlockDataLength = static_cast(sizeof(T)); - nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - - auto status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) { - ErrorHandler("Error creating linked list in MDMA"); - } + template + LinkedListNode(T* source_ptr, void* dest_ptr) { + init_node(source_ptr, dest_ptr, sizeof(T)); + } + + template + LinkedListNode(T* source_ptr, void* dest_ptr, size_t size) { + init_node(source_ptr, dest_ptr, size); + } + + void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } + void set_destination(void* destination) { node.CDAR = reinterpret_cast(destination); } + void set_source(void* source) { node.CSAR = reinterpret_cast(source); } + auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } + auto get_size() -> uint32_t { return node.CBNDTR; } + auto get_destination() -> uint32_t { return node.CDAR; } + auto get_source() -> uint32_t { return node.CSAR; } + auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } + +private: + MDMA_LinkNodeTypeDef node; + + void init_node(void* src, void* dst, size_t size) { + MDMA_LinkNodeConfTypeDef nodeConfig{}; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_RIGHT; + nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; + nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; + nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; + nodeConfig.Init.SourceBlockAddressOffset = 0; + nodeConfig.Init.DestBlockAddressOffset = 0; + nodeConfig.BlockCount = 1; + nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; + nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; + nodeConfig.Init.Request = MDMA_REQUEST_SW; + + this->node = {}; + nodeConfig.SrcAddress = reinterpret_cast(src); + nodeConfig.DstAddress = reinterpret_cast(dst); + nodeConfig.BlockDataLength = static_cast(size); + nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; + nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; + nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; + nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; + + if (HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig) != HAL_OK) { + ErrorHandler("Error creating linked list in MDMA"); } - template - LinkedListNode(T* source_ptr, void* dest_ptr,size_t size) { - MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; - nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; - nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; - nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; - nodeConfig.Init.SourceBlockAddressOffset = 0; - nodeConfig.Init.DestBlockAddressOffset = 0; - nodeConfig.BlockCount = 1; - nodeConfig.Init.Priority = MDMA_PRIORITY_VERY_HIGH; - nodeConfig.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE; - nodeConfig.Init.Request = MDMA_REQUEST_SW; - - this->node = {}; - nodeConfig.SrcAddress = reinterpret_cast(source_ptr); - nodeConfig.DstAddress = reinterpret_cast(dest_ptr); - nodeConfig.BlockDataLength = static_cast(size); - nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; - nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; - nodeConfig.Init.SourceInc = MDMA_SRC_INC_BYTE; - nodeConfig.Init.DestinationInc = MDMA_DEST_INC_BYTE; - - auto status = HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig); - if (status != HAL_OK) { - ErrorHandler("Error creating linked list in MDMA"); - } - } - - /** - * @brief Set the next node in the linked list. - */ - void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } - void set_destination(void* destination) { node.CDAR = reinterpret_cast(destination); } - void set_source(void* source) { node.CSAR = reinterpret_cast(source); } - auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } - auto get_size() -> uint32_t { return node.CBNDTR; } - auto get_destination() -> uint32_t { return node.CDAR; } - auto get_source() -> uint32_t { return node.CSAR; } - auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } - - private: - MDMA_LinkNodeTypeDef node; - }; + } +}; private: struct Instance{ diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 46c513037..f0c3146de 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -36,9 +36,8 @@ class MdmaPacket : public Packet { MdmaPacket(uint16_t id, Types*... values) : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { packets[id] = this; - // Allocate non-cached buffer for MDMA buffer = reinterpret_cast(MPUManager::allocate_non_cached_memory(size)); - // buffer = new uint8_t[size]; + if (buffer == nullptr) { ErrorHandler("Failed to allocate MDMA buffer for packet"); } @@ -46,12 +45,15 @@ class MdmaPacket : public Packet { MDMA::LinkedListNode* prev_node = nullptr; uint32_t offset = 0; uint32_t idx = 0; + std::apply([&](auto&&... args) { (([&]() { - using T = std::remove_pointer_t; - MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(args, buffer + offset); + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(args, buffer + offset,type_size); build_nodes[idx++] = node; - offset += sizeof(T); + offset += type_size; if (prev_node != nullptr) { prev_node->set_next(node->get_node()); } @@ -62,11 +64,15 @@ class MdmaPacket : public Packet { prev_node = nullptr; offset = 0; idx = 0; + std::apply([&](auto&&... args) { (([&]() { - MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(buffer + offset, args); + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(buffer + offset, args, type_size); parse_nodes[idx++] = node; - offset += sizeof(args); + offset += type_size; if (prev_node != nullptr) { prev_node->set_next(node->get_node()); } @@ -74,8 +80,8 @@ class MdmaPacket : public Packet { }()), ...); }, value_pointers); - build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr,size); // Used when needed for dynamic destination - parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer,size); // Used when needed for dynamic origin + build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr, size); + parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer, size); } /** From 61d4f342d02ac0efeb5a58c9803d255fd4ac8ca0 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 16:08:34 +0100 Subject: [PATCH 073/166] Work to be done, the auxilary buffer recieves data but crashes when it sends it to the destination buffer --- Inc/HALAL/Models/MDMA/MDMA.hpp | 2 +- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index e2ff7447f..012ae3225 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -50,7 +50,7 @@ class MDMA{ void init_node(void* src, void* dst, size_t size) { MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_RIGHT; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; nodeConfig.Init.BufferTransferLength = 1; diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index f0c3146de..be6eff954 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -41,7 +41,7 @@ class MdmaPacket : public Packet { if (buffer == nullptr) { ErrorHandler("Failed to allocate MDMA buffer for packet"); } - + SCB_CleanDCache_by_Addr(reinterpret_cast(&this->id), sizeof(id)); MDMA::LinkedListNode* prev_node = nullptr; uint32_t offset = 0; uint32_t idx = 0; @@ -160,6 +160,7 @@ class MdmaPacket : public Packet { } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } + SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { From 6571b3b5b179a1e208c9ee74f3f94878138aa267 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 16:09:35 +0100 Subject: [PATCH 074/166] Oops forgot to comment a thing :p --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index be6eff954..ebe8b17b7 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -160,7 +160,7 @@ class MdmaPacket : public Packet { } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } - SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); + // SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { From 5ab710e2dc4ec8ec73ac7a682b471f0a373790a1 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 16:16:07 +0100 Subject: [PATCH 075/166] Now working yipeee --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index ebe8b17b7..ee93cbb40 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -160,7 +160,8 @@ class MdmaPacket : public Packet { } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } - // SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); + SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) ); + SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)+1], sizeof(MDMA::LinkedListNode) ); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { From c4748dd114a1f66a9492c3389e80cd6d66c1953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Tue, 9 Dec 2025 21:07:28 +0100 Subject: [PATCH 076/166] Added initial GPIO mock --- CMakeLists.txt | 56 ++-- Inc/HALAL/Models/GPIO.hpp | 4 + Inc/MockedDrivers/hal_gpio_interface.h | 388 +++++++++++++++++++++++++ Tests/CMakeLists.txt | 42 +-- 4 files changed, 439 insertions(+), 51 deletions(-) create mode 100644 Inc/MockedDrivers/hal_gpio_interface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f956d7aba..e015bfde2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,9 @@ if(NOT PROJECT_IS_TOP_LEVEL) set(STLIB_LIBRARY ${STLIB_LIBRARY} PARENT_SCOPE) endif() +include(CTest) +enable_testing() + option(USE_ETHERNET "Enable ethernet peripheral" OFF) option(TARGET_NUCLEO "Targets the STM32H723 Nucleo development board" OFF) @@ -18,22 +21,6 @@ message(STATUS "${PROJECT_NAME} Ethernet: ${USE_ETHERNET}") message(STATUS "${PROJECT_NAME} Nucleo: ${TARGET_NUCLEO}") message(STATUS "${PROJECT_NAME} Crosscompiling: ${CMAKE_CROSSCOMPILING}") -# ============================ -# Tests + GTest - Simulator -# ============================ -if((PROJECT_IS_TOP_LEVEL AND (NOT CMAKE_CROSSCOMPILING))) - include(FetchContent) - option(BUILD_GMOCK OFF) - option(INSTALL_GTEST OFF) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.15.2 - ) - FetchContent_MakeAvailable(googletest) - add_library(GTest::GTest INTERFACE IMPORTED) - target_link_libraries(GTest::GTest INTERFACE gtest_main) -endif() if(NOT CMAKE_CROSSCOMPILING) message(STATUS "Compiling for simulator") @@ -216,25 +203,29 @@ add_library(${STLIB_LIBRARY} STATIC $<$,$>:${LWIP_SOURCES}> $<$,$>:${USER_LWIP_SOURCES}> - ${HALAL_C_NO_ETH} - ${HALAL_CPP_NO_ETH} - $<$:${HALAL_C_ETH}> - $<$:${HALAL_CPP_ETH}> + $<$:${HALAL_C_NO_ETH}> + $<$:${HALAL_CPP_NO_ETH}> + $<$,$>:${HALAL_C_ETH}> + $<$,$>:${HALAL_CPP_ETH}> + + $<$:${CPP_UTILITIES_C}> + $<$:${CPP_UTILITIES_CPP}> - ${CPP_UTILITIES_C} - ${CPP_UTILITIES_CPP} + $<$:${STLIB_LOW_C_NO_ETH}> + $<$:${STLIB_LOW_CPP_NO_ETH}> + $<$,$>:${STLIB_LOW_C_ETH}> + $<$,$>:${STLIB_LOW_CPP_ETH}> - ${STLIB_LOW_C_NO_ETH} - ${STLIB_LOW_CPP_NO_ETH} - $<$:${STLIB_LOW_C_ETH}> - $<$:${STLIB_LOW_CPP_ETH}> + $<$:${STLIB_HIGH_C_NO_ETH}> + $<$:${STLIB_HIGH_CPP_NO_ETH}> + $<$,$>:${STLIB_HIGH_C_ETH}> + $<$,$>:${STLIB_HIGH_CPP_ETH}> - ${STLIB_HIGH_C_NO_ETH} - ${STLIB_HIGH_CPP_NO_ETH} - $<$:${STLIB_HIGH_C_ETH}> - $<$:${STLIB_HIGH_CPP_ETH}> + $<$:${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB.cpp> - ${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB.cpp + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Time/Scheduler.cpp> + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/mocked_ll_tim.cpp> + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/NVIC.cpp> ) set_target_properties(${STLIB_LIBRARY} PROPERTIES @@ -248,7 +239,7 @@ target_compile_definitions(${STLIB_LIBRARY} PUBLIC $<$:USE_HAL_DRIVER> $<$:STM32H723xx> - $<$>:SIM_ON> + $<$>:TESTING_ENV> $<$:STLIB_ETH> $,NUCLEO,BOARD> @@ -313,6 +304,7 @@ target_include_directories(${STLIB_LIBRARY} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/Inc/ST-LIB_HIGH ) + if(PROJECT_IS_TOP_LEVEL) configure_file( ${CMAKE_CURRENT_LIST_DIR}/Inc/stm32h7xx_hal_conf_template.h diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index c19131e37..1988c9785 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -1,6 +1,10 @@ #pragma once +#ifndef TESTING_ENV #include "stm32h7xx_hal.h" +#else +#include "MockedDrivers/hal_gpio_interface.h" +#endif #include #include #include diff --git a/Inc/MockedDrivers/hal_gpio_interface.h b/Inc/MockedDrivers/hal_gpio_interface.h new file mode 100644 index 000000000..579b6ad45 --- /dev/null +++ b/Inc/MockedDrivers/hal_gpio_interface.h @@ -0,0 +1,388 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_gpio.h + * @author MCD Application Team + * @brief Header file of GPIO HAL module. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_GPIO_H +#define STM32H7xx_HAL_GPIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal_def.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @addtogroup GPIO + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup GPIO_Exported_Types GPIO Exported Types + * @{ + */ + +/** + * @brief GPIO Init structure definition + */ +typedef struct { + uint32_t Pin; /*!< Specifies the GPIO pins to be configured. + This parameter can be any value of @ref GPIO_pins_define */ + + uint32_t Mode; /*!< Specifies the operating mode for the selected pins. + This parameter can be a value of @ref GPIO_mode_define */ + + uint32_t + Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected + pins. This parameter can be a value of @ref GPIO_pull_define */ + + uint32_t + Speed; /*!< Specifies the speed for the selected pins. + This parameter can be a value of @ref GPIO_speed_define */ + + uint32_t Alternate; /*!< Peripheral to be connected to the selected pins. + This parameter can be a value of @ref + GPIO_Alternate_function_selection */ +} GPIO_InitTypeDef; + +/** + * @brief GPIO Bit SET and Bit RESET enumeration + */ +typedef enum { GPIO_PIN_RESET = 0U, GPIO_PIN_SET } GPIO_PinState; +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ + +/** @defgroup GPIO_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_pins_define GPIO pins define + * @{ + */ +#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */ +#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */ +#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */ +#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */ +#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */ +#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */ +#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */ +#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */ +#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */ +#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */ +#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */ +#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */ +#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */ +#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */ +#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */ +#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */ +#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */ + +#define GPIO_PIN_MASK (0x0000FFFFU) /* PIN mask for assert test */ +/** + * @} + */ + +/** @defgroup GPIO_mode_define GPIO mode define + * @brief GPIO Configuration Mode + * Elements values convention: 0x00WX00YZ + * - W : EXTI trigger detection on 3 bits + * - X : EXTI mode (IT or Event) on 2 bits + * - Y : Output type (Push Pull or Open Drain) on 1 bit + * - Z : GPIO mode (Input, Output, Alternate or Analog) on 2 bits + * @{ + */ +#define GPIO_MODE_INPUT \ + MODE_INPUT /*!< Input Floating Mode */ +#define GPIO_MODE_OUTPUT_PP \ + (MODE_OUTPUT | OUTPUT_PP) /*!< Output Push Pull Mode */ +#define GPIO_MODE_OUTPUT_OD \ + (MODE_OUTPUT | OUTPUT_OD) /*!< Output Open Drain Mode */ +#define GPIO_MODE_AF_PP \ + (MODE_AF | OUTPUT_PP) /*!< Alternate Function Push Pull Mode */ +#define GPIO_MODE_AF_OD \ + (MODE_AF | OUTPUT_OD) /*!< Alternate Function Open Drain Mode */ +#define GPIO_MODE_ANALOG \ + MODE_ANALOG /*!< Analog Mode */ +#define GPIO_MODE_IT_RISING \ + (MODE_INPUT | EXTI_IT | TRIGGER_RISING) /*!< External Interrupt Mode with \ + Rising edge trigger detection */ +#define GPIO_MODE_IT_FALLING \ + (MODE_INPUT | EXTI_IT | \ + TRIGGER_FALLING) /*!< External Interrupt Mode with Falling edge trigger \ + detection */ +#define GPIO_MODE_IT_RISING_FALLING \ + (MODE_INPUT | EXTI_IT | TRIGGER_RISING | \ + TRIGGER_FALLING) /*!< External Interrupt Mode with Rising/Falling edge \ + trigger detection */ + +#define GPIO_MODE_EVT_RISING \ + (MODE_INPUT | EXTI_EVT | TRIGGER_RISING) /*!< External Event Mode with \ + Rising edge trigger detection */ +#define GPIO_MODE_EVT_FALLING \ + (MODE_INPUT | EXTI_EVT | \ + TRIGGER_FALLING) /*!< External Event Mode with Falling edge trigger \ + detection */ +#define GPIO_MODE_EVT_RISING_FALLING \ + (MODE_INPUT | EXTI_EVT | TRIGGER_RISING | \ + TRIGGER_FALLING) /*!< External Event Mode with Rising/Falling edge trigger \ + detection */ +/** + * @} + */ + +/** @defgroup GPIO_speed_define GPIO speed define + * @brief GPIO Output Maximum frequency + * @{ + */ +#define GPIO_SPEED_FREQ_LOW (0x00000000U) /*!< Low speed */ +#define GPIO_SPEED_FREQ_MEDIUM (0x00000001U) /*!< Medium speed */ +#define GPIO_SPEED_FREQ_HIGH (0x00000002U) /*!< Fast speed */ +#define GPIO_SPEED_FREQ_VERY_HIGH (0x00000003U) /*!< High speed */ +/** + * @} + */ + +/** @defgroup GPIO_pull_define GPIO pull define + * @brief GPIO Pull-Up or Pull-Down Activation + * @{ + */ +#define GPIO_NOPULL (0x00000000U) /*!< No Pull-up or Pull-down activation */ +#define GPIO_PULLUP (0x00000001U) /*!< Pull-up activation */ +#define GPIO_PULLDOWN (0x00000002U) /*!< Pull-down activation */ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIO_Exported_Macros GPIO Exported Macros + * @{ + */ + +/** + * @brief Checks whether the specified EXTI line flag is set or not. + * @param __EXTI_LINE__: specifies the EXTI line flag to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTI_GET_FLAG(__EXTI_LINE__) (EXTI->PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending flags. + * @param __EXTI_LINE__: specifies the EXTI lines flags to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_CLEAR_FLAG(__EXTI_LINE__) (EXTI->PR1 = (__EXTI_LINE__)) + +/** + * @brief Checks whether the specified EXTI line is asserted or not. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending bits. + * @param __EXTI_LINE__: specifies the EXTI lines to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR1 = (__EXTI_LINE__)) + +#if defined(DUAL_CORE) +/** + * @brief Checks whether the specified EXTI line flag is set or not. + * @param __EXTI_LINE__: specifies the EXTI line flag to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTID2_GET_FLAG(__EXTI_LINE__) \ + (EXTI->C2PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending flags. + * @param __EXTI_LINE__: specifies the EXTI lines flags to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTID2_CLEAR_FLAG(__EXTI_LINE__) \ + (EXTI->C2PR1 = (__EXTI_LINE__)) + +/** + * @brief Checks whether the specified EXTI line is asserted or not. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTID2_GET_IT(__EXTI_LINE__) (EXTI->C2PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending bits. + * @param __EXTI_LINE__: specifies the EXTI lines to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTID2_CLEAR_IT(__EXTI_LINE__) \ + (EXTI->C2PR1 = (__EXTI_LINE__)) +#endif + +/** + * @brief Generates a Software interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_GENERATE_SWIT(__EXTI_LINE__) \ + (EXTI->SWIER1 |= (__EXTI_LINE__)) +/** + * @} + */ + +/* Include GPIO HAL Extension module */ +#include "stm32h7xx_hal_gpio_ex.h" + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup GPIO_Exported_Functions + * @{ + */ + +/** @addtogroup GPIO_Exported_Functions_Group1 + * @{ + */ +/* Initialization and de-initialization functions *****************************/ +void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, const GPIO_InitTypeDef *GPIO_Init); +void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin); +/** + * @} + */ + +/** @addtogroup GPIO_Exported_Functions_Group2 + * @{ + */ +/* IO operation functions *****************************************************/ +GPIO_PinState HAL_GPIO_ReadPin(const GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, + GPIO_PinState PinState); +void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin); +void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin); + +/** + * @} + */ + +/** + * @} + */ +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIO_Private_Constants GPIO Private Constants + * @{ + */ +#define GPIO_MODE_Pos 0u +#define GPIO_MODE (0x3uL << GPIO_MODE_Pos) +#define MODE_INPUT (0x0uL << GPIO_MODE_Pos) +#define MODE_OUTPUT (0x1uL << GPIO_MODE_Pos) +#define MODE_AF (0x2uL << GPIO_MODE_Pos) +#define MODE_ANALOG (0x3uL << GPIO_MODE_Pos) +#define OUTPUT_TYPE_Pos 4u +#define OUTPUT_TYPE (0x1uL << OUTPUT_TYPE_Pos) +#define OUTPUT_PP (0x0uL << OUTPUT_TYPE_Pos) +#define OUTPUT_OD (0x1uL << OUTPUT_TYPE_Pos) +#define EXTI_MODE_Pos 16u +#define EXTI_MODE (0x3uL << EXTI_MODE_Pos) +#define EXTI_IT (0x1uL << EXTI_MODE_Pos) +#define EXTI_EVT (0x2uL << EXTI_MODE_Pos) +#define TRIGGER_MODE_Pos 20u +#define TRIGGER_MODE (0x7uL << TRIGGER_MODE_Pos) +#define TRIGGER_RISING (0x1uL << TRIGGER_MODE_Pos) +#define TRIGGER_FALLING (0x2uL << TRIGGER_MODE_Pos) +#define TRIGGER_LEVEL (0x4uL << TRIGGER_MODE_Pos) +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIO_Private_Macros GPIO Private Macros + * @{ + */ +#define IS_GPIO_PIN_ACTION(ACTION) \ + (((ACTION) == GPIO_PIN_RESET) || ((ACTION) == GPIO_PIN_SET)) +#define IS_GPIO_PIN(__PIN__) \ + ((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U) && \ + (((uint32_t)(__PIN__) & ~GPIO_PIN_MASK) == 0x00U)) +#define IS_GPIO_MODE(MODE) \ + (((MODE) == GPIO_MODE_INPUT) || ((MODE) == GPIO_MODE_OUTPUT_PP) || \ + ((MODE) == GPIO_MODE_OUTPUT_OD) || ((MODE) == GPIO_MODE_AF_PP) || \ + ((MODE) == GPIO_MODE_AF_OD) || ((MODE) == GPIO_MODE_IT_RISING) || \ + ((MODE) == GPIO_MODE_IT_FALLING) || \ + ((MODE) == GPIO_MODE_IT_RISING_FALLING) || \ + ((MODE) == GPIO_MODE_EVT_RISING) || ((MODE) == GPIO_MODE_EVT_FALLING) || \ + ((MODE) == GPIO_MODE_EVT_RISING_FALLING) || ((MODE) == GPIO_MODE_ANALOG)) +#define IS_GPIO_SPEED(SPEED) \ + (((SPEED) == GPIO_SPEED_FREQ_LOW) || ((SPEED) == GPIO_SPEED_FREQ_MEDIUM) || \ + ((SPEED) == GPIO_SPEED_FREQ_HIGH) || \ + ((SPEED) == GPIO_SPEED_FREQ_VERY_HIGH)) + +#define IS_GPIO_PULL(PULL) \ + (((PULL) == GPIO_NOPULL) || ((PULL) == GPIO_PULLUP) || \ + ((PULL) == GPIO_PULLDOWN)) + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIO_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_GPIO_H */ diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index e677d6bee..c06522432 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -4,33 +4,38 @@ else() set(STLIB_TEST_EXECUTABLE st-lib-test) endif() +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip +) +FetchContent_MakeAvailable(googletest) + message(STATUS "Generating test executable for ST-LIB") -include(CTest) -enable_testing() + add_executable(${STLIB_TEST_EXECUTABLE} - ${CMAKE_CURRENT_LIST_DIR}/Runes/Pins.cpp - ${CMAKE_CURRENT_LIST_DIR}/Runes/SimRunes.cpp - ${CMAKE_CURRENT_LIST_DIR}/DigitalOutTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/DigitalInTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/Time/test_time.cpp - ${CMAKE_CURRENT_LIST_DIR}/EncoderTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/InputCaptureTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/ADCTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/PWMTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/DualPWMTest.cpp - ${CMAKE_CURRENT_LIST_DIR}/EXTITest.cpp + ${CMAKE_CURRENT_LIST_DIR}/Time/scheduler_test.cpp ) -set_target_properties(${STLIB_TEST_EXECUTABLE} PROPERTIES CXX_STANDARD 20 CXX_STANDARD_REQUIRED C_STANDARD 11 C_STANDARD_REQUIRED) +set_target_properties(${STLIB_TEST_EXECUTABLE} PROPERTIES + CXX_STANDARD 23 + CXX_STANDARD_REQUIRED YES + C_STANDARD 11 + C_STANDARD_REQUIRED YES +) target_link_libraries( ${STLIB_TEST_EXECUTABLE} - ${STLIB_LIBRARY} GTest::gtest_main + ${STLIB_LIBRARY} ) +if(MINGW OR CYGWIN) + target_link_options(${STLIB_TEST_EXECUTABLE} PRIVATE -static) +endif() + target_compile_definitions(${STLIB_TEST_EXECUTABLE} PRIVATE - SIM_ON + TESTING_ENV ) target_include_directories(${STLIB_TEST_EXECUTABLE} PRIVATE @@ -42,11 +47,10 @@ target_include_directories(${STLIB_TEST_EXECUTABLE} PRIVATE include(GoogleTest) gtest_discover_tests(${STLIB_TEST_EXECUTABLE}) -# Create symlink to test executable for CI/CD to pick it up add_custom_command( TARGET ${STLIB_TEST_EXECUTABLE} POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_CURRENT_BINARY_DIR}/${STLIB_TEST_EXECUTABLE} - ${CMAKE_SOURCE_DIR}/out/build/test.elf + ${CMAKE_CURRENT_BINARY_DIR}/${STLIB_TEST_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/out/build/test.elf ) \ No newline at end of file From 922379c1ff55e541ac50a285358020f0744493bb Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 11 Dec 2025 15:43:57 +0100 Subject: [PATCH 077/166] mdma V2.0 --- Inc/HALAL/Models/MDMA/MDMA.hpp | 43 +++++----- Src/HALAL/Models/MDMA/MDMA.cpp | 144 +++++++++++++++++---------------- Src/ST-LIB.cpp | 1 + 3 files changed, 99 insertions(+), 89 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 012ae3225..5f64aaf9c 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -17,9 +17,12 @@ #endif #ifndef NODES_MAX -#define NODES_MAX 20 +#define NODES_MAX 100 #endif +#ifndef TRANSFER_QUEUE_MAX_SIZE +#define TRANSFER_QUEUE_MAX_SIZE 50 +#endif class MDMA{ public: /** @@ -120,10 +123,22 @@ class MDMA{ inline static std::array instances{}; static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; + static std::bitset<8> instance_free_map; + static Stack,50> transfer_queue; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); + /** + * @brief A method to add MDMA channels in linked list mode. + + * This method will be called internally for each channel during the MDMA::start() process. + * + * @param instance The reference to the MDMA instance to be initialised + */ + + static void inscribe(Instance& instance,uint8_t id); + public: @@ -134,38 +149,28 @@ class MDMA{ static void start(); static void irq_handler(); + static void update(); - /** - * @brief A method to add MDMA channels in linked list mode. - - * This method has to be invoked before the ST-LIB::start() - * - * @param data_buffer the buffer where the MDMA will write the data, very important to be a non-cached buffer - * @param destination_address the address where the MDMA will read the data from, if nullptr it will make it so that the destination varies dinamically - * - * @return the id that represents the MDMA channel with its designated buffer inside this utility class, used in all its functions. - */ - - static uint8_t inscribe(uint8_t* data_buffer, uint8_t* destination_address=nullptr); + /** * @brief A method to start a transfer from source to destination using MDMA linked list * - * @param MDMA_id The ID of the MDMA channel instance. - * @param packet_id The ID of the linked list packet to be transferred. - * @param destination_address The destination address for the transfer. If nullptr, the default destination associated with the instance will be used. + * @param source_address The source address for the transfer. + * @param data_length The length of data to be transferred. + * @param destination_address The destination address for the transfer. * @param promise An optional promise to be fulfilled upon transfer completion. + * @return True if the transfer was successfully started, false otherwise. */ - static void transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address=nullptr, Promise* promise=nullptr); + static bool transfer_data(uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address, Promise* promise=nullptr); /** * @brief A method to transfer using MDMA linked * - * @param MDMA_id The ID of the MDMA channel instance. * @param first_node The linked list node representing the first node in the linked list. * @param promise An optional promise to be fulfilled upon transfer completion. */ - static void transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode* first_node, Promise* promise=nullptr); + static void transfer_list(MDMA::LinkedListNode* first_node, Promise* promise=nullptr); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 91b1e1b2d..f71f53c48 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -5,6 +5,7 @@ uint32_t buffer_size = sizeof(MDMA::LinkedListNode) * NODES_MAX; MDMA::LinkedListNode* buffer = static_cast(MPUManager::allocate_non_cached_memory(buffer_size)); inline Pool MDMA::link_node_pool{buffer}; +std::bitset<8> MDMA::instance_free_map{}; std::unordered_map MDMA::instance_to_channel = { {0, MDMA_Channel0}, @@ -37,15 +38,12 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node ErrorHandler("MDMA transfer already in progress"); return; } + instance_free_map[instance.id] = false; instance.handle.State = HAL_MDMA_STATE_BUSY; instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; instance.handle.FirstLinkedListNodeAddress = first_node; __HAL_MDMA_DISABLE(&instance.handle); - while ((instance.handle.Instance->CCR & MDMA_CCR_EN) != 0U) - { - __NOP(); - } MDMA_Channel_TypeDef* channel = instance.handle.Instance; @@ -81,34 +79,15 @@ void MDMA::prepare_transfer(Instance& instance, LinkedListNode* first_node) { MD MDMA::Instance& MDMA::get_instance(uint8_t id) { - if (id >= instances.size()) - { - ErrorHandler("MDMA instance ID not found"); - } Instance& instance = instances[id]; - if (instance.handle.Instance == nullptr) - { - ErrorHandler("MDMA instance not initialised"); - } return instance; } -uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) +void MDMA::inscribe(Instance& instance,uint8_t id) { - const auto slot_it = std::find_if(instances.begin(), instances.end(), [](const Instance& instance) - { - return instance.handle.Instance == nullptr; - }); - - if (slot_it == instances.end()) - { - ErrorHandler("Maximum number of MDMA instances reached"); - } - - const uint8_t id = static_cast(slot_it - instances.begin()); MDMA_HandleTypeDef mdma_handle{}; const auto channel_it = instance_to_channel.find(id); if (channel_it == instance_to_channel.end()) @@ -150,20 +129,18 @@ uint8_t MDMA::inscribe(uint8_t* data_buffer, uint8_t* destination_address) nodeConfig.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE; nodeConfig.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE; nodeConfig.BlockDataLength = 1; - nodeConfig.SrcAddress = reinterpret_cast(data_buffer); - uint8_t* dst_ptr = (destination_address == nullptr) ? data_buffer : destination_address; - nodeConfig.DstAddress = reinterpret_cast(dst_ptr); + nodeConfig.SrcAddress = reinterpret_cast(nullptr); + nodeConfig.DstAddress = reinterpret_cast(nullptr); const HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); if (status != HAL_OK) { ErrorHandler("Error creating linked list in MDMA"); } + instance_free_map[id] = false; - Instance instance(mdma_handle, id, data_buffer, destination_address, transfer_node); - instances[id] = instance; + instance = Instance(mdma_handle, id, nullptr, nullptr, transfer_node); - return id; } @@ -171,11 +148,15 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); + uint8_t id = 0; for (auto& instance : instances) { + inscribe(instance,id); + id++; + if (instance.handle.Instance == nullptr) { - continue; + ErrorHandler("MDMA instance not initialised"); } const HAL_StatusTypeDef status = HAL_MDMA_Init(&instance.handle); if (status != HAL_OK) @@ -192,6 +173,38 @@ void MDMA::start() HAL_NVIC_EnableIRQ(MDMA_IRQn); } +void MDMA::update() +{ + if(transfer_queue.empty()) + { + return; + } + + for(size_t i = 0; i < instances.size(); i++) + { + if(transfer_queue.empty()) + { + return; + } + if(instance_free_map[i]) + { + Instance& instance = get_instance(i); + auto transfer = transfer_queue.top(); + if(transfer.second == nullptr) + { + instance.using_promise = false; + } + else + { + instance.promise = transfer.second; + instance.using_promise = true; + } + prepare_transfer(instance, transfer.first); + transfer_queue.pop(); + } + } +} + void MDMA::irq_handler() { @@ -206,56 +219,46 @@ void MDMA::irq_handler() } -void MDMA::transfer_list(const uint8_t MDMA_id, MDMA::LinkedListNode* first_node, Promise* promise) +void MDMA::transfer_list(MDMA::LinkedListNode* first_node, Promise* promise) { - Instance& instance = get_instance(MDMA_id); - - if(promise == nullptr) + if(transfer_queue.size() >= TRANSFER_QUEUE_MAX_SIZE) { - instance.using_promise = false; - } - else - { - instance.promise = promise; - instance.using_promise = true; + ErrorHandler("MDMA transfer queue full"); + return; } - - prepare_transfer(instance, first_node); + transfer_queue.push({first_node, promise}); } -void MDMA::transfer_data(const uint8_t MDMA_id,uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) -{ - Instance& instance = get_instance(MDMA_id); - if(promise == nullptr) - { - instance.using_promise = false; - } - else - { - instance.promise = promise; - instance.using_promise = true; - } - - instance.transfer_node.CSAR = reinterpret_cast(source_address); - instance.transfer_node.CBNDTR = data_length; - if(destination_address == nullptr) +bool MDMA::transfer_data(uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) +{ + for(size_t i = 0; i < instances.size(); i++) { - if(instance.destination_address ==nullptr) + if(instance_free_map[i]) { - ErrorHandler("No destination address provided for MDMA transfer"); + Instance& instance = get_instance(i); + if(promise == nullptr) + { + instance.using_promise = false; + } + else + { + instance.promise = promise; + instance.using_promise = true; + } + + instance.transfer_node.CSAR = reinterpret_cast(source_address); + instance.transfer_node.CBNDTR = data_length; + instance.transfer_node.CDAR = reinterpret_cast(destination_address); + + SCB_CleanDCache_by_Addr((uint32_t*)&instance.transfer_node, sizeof(MDMA_LinkNodeTypeDef)); //To be removed when MPU fixed + + prepare_transfer(instance, &instance.transfer_node); + return true; } - instance.transfer_node.CDAR = reinterpret_cast(instance.destination_address); - } - else - { - instance.transfer_node.CDAR = reinterpret_cast(destination_address); } - - SCB_CleanDCache_by_Addr((uint32_t*)&instance.transfer_node, sizeof(MDMA_LinkNodeTypeDef)); - - prepare_transfer(instance, &instance.transfer_node); + return false; } @@ -269,6 +272,7 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) Instance& instance = get_instance(channel_it->second); instance.handle.State = HAL_MDMA_STATE_READY; + instance_free_map[instance.id] = true; if(instance.using_promise) { instance.promise->resolve(); diff --git a/Src/ST-LIB.cpp b/Src/ST-LIB.cpp index 019c8e7b9..2b114f011 100644 --- a/Src/ST-LIB.cpp +++ b/Src/ST-LIB.cpp @@ -40,6 +40,7 @@ void STLIB::update() { Server::update_servers(); #endif ErrorHandlerModel::ErrorHandlerUpdate(); + MDMA::update(); Promise::update(); } From be515a27e6c190e47ec996f7505ce8de07554cbd Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 11 Dec 2025 16:02:32 +0100 Subject: [PATCH 078/166] Some errors fixed --- Inc/HALAL/Models/MDMA/MDMA.hpp | 2 +- Src/HALAL/Models/MDMA/MDMA.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 5f64aaf9c..742b40914 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -124,7 +124,7 @@ class MDMA{ static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; static std::bitset<8> instance_free_map; - static Stack,50> transfer_queue; + inline static Stack,50> transfer_queue{}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index f71f53c48..785643b2b 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -66,7 +66,7 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node if (HAL_MDMA_GenerateSWRequest(&instance.handle) != HAL_OK) { - instance.handle.State = HAL_MDMA_STATE_READY; + instance.handle.State = HAL_MDMA_STATE_BUSY; ErrorHandler("Error generating MDMA SW request"); return; } @@ -137,7 +137,7 @@ void MDMA::inscribe(Instance& instance,uint8_t id) { ErrorHandler("Error creating linked list in MDMA"); } - instance_free_map[id] = false; + instance_free_map[id] = true; instance = Instance(mdma_handle, id, nullptr, nullptr, transfer_node); From 815ba89d22bd9d6e66ef5ed6f6dd6aff03bdf5f4 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 11 Dec 2025 19:16:38 +0100 Subject: [PATCH 079/166] Cosas --- Src/HALAL/Models/MDMA/MDMA.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 785643b2b..cc9f80690 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -43,7 +43,6 @@ void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node instance.handle.State = HAL_MDMA_STATE_BUSY; instance.handle.ErrorCode = HAL_MDMA_ERROR_NONE; instance.handle.FirstLinkedListNodeAddress = first_node; - __HAL_MDMA_DISABLE(&instance.handle); MDMA_Channel_TypeDef* channel = instance.handle.Instance; From c4405406d45c1dd1f2c9f1b6caab17e036aae0cb Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 10 Dec 2025 16:59:10 +0100 Subject: [PATCH 080/166] feat(MPU): Initial structure && design of the API --- Inc/HALAL/Models/MPU.hpp | 220 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 Inc/HALAL/Models/MPU.hpp diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp new file mode 100644 index 000000000..f87d2698b --- /dev/null +++ b/Inc/HALAL/Models/MPU.hpp @@ -0,0 +1,220 @@ +/* + * MPU.hpp + * + * Created on: 10 dic. 2025 + * Author: Boris + */ + +#ifndef MPU_HPP +#define MPU_HPP + +#ifndef HALAL_MPUBUFFERS_MAX_INSTANCES +#define HALAL_MPUBUFFERS_MAX_INSTANCES 100 +#endif + +#include "C++Utilities/CppUtils.hpp" +#include "C++Utilities/CppImports.hpp" +#include "ErrorHandler/ErrorHandler.hpp" +#include "stm32h7xx_hal.h" + + +struct MPUDomain { + + enum class MemoryDomain : uint8_t { + D1 = 1, // AXI SRAM (0x24000000) + D2 = 2, // SRAM 1/2/3 (0x30000000) + D3 = 3 // SRAM 4 (0x38000000) + }; + + enum class MemoryType : bool { + Cached = true, + NonCached = false + }; + + // Buffer Request + struct Entry { + MemoryDomain memory_domain; + MemoryType memory_type; + std::size_t alignment; + std::size_t size_in_bytes; + }; + + // Buffer Request Wrapper + struct Buffer { + using domain = MPUDomain; + Entry e; + + /** + * @brief Constructs a Buffer entry for a type T. + * @tparam T The type for which the buffer is requested. Must be a POD type. + * @param type The memory type (Cached or NonCached). + * @param domain The memory domain where the buffer should be allocated. + * @param force_cache_alignment If true, forces the buffer to be cache line aligned (32 bytes, takes the rest as padding). + */ + template + consteval Buffer(MemoryType type = MemoryType::NonCached, MemoryDomain domain = MemoryDomain::D2, bool force_cache_alignment = false) + requires(std::is_standard_layout_v && std::is_trivial_v) + : e{ + domain, + type, + force_cache_alignment ? std::max(std::size_t(32), alignof(T)) : alignof(T), + sizeof(T)} + { + static_assert(alignof(T) <= 32, "Requested type has alignment greater than cache line size (32 bytes)."); + } + + template + consteval void inscribe(Ctx &ctx) const { + ctx.template add(e); + } + }; + + static constexpr std::size_t max_instances = HALAL_MPUBUFFERS_MAX_INSTANCES; + + struct Config { + uint32_t base_address; + + std::size_t size; + + // MPU Config Data + bool is_mpu_leader; + MPU_Region_InitTypeDef mpu_init; + }; + + // 5. Build compile-time: Entry[] → Config[] + // + // IMPORTANTE: + // - Esta función es consteval: se ejecuta en tiempo de compilación. + // - Aquí es donde se hacen TODAS las validaciones estáticas sobre los Entry. + // + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + + for (std::size_t i = 0; i < N; i++) { + const Entry &e = entries[i]; + + // Aquí se pueden hacer checks globales y locales: + // - Comprobar que no se repite el mismo recurso. + // - Verificar que el recurso es válido para este dominio. + // - Validar que la combinación de parámetros tiene sentido. + + // Ejemplo de patrón (pseudo-código): + // for (std::size_t j = 0; j < i; ++j) { + // if (entries[j].pin == e.pin && entries[j].port == e.port) { + // struct duplicate_resource {}; + // throw duplicate_resource{}; + // } + // } + + // A partir de 'e' se construye cfgs[i] con los datos necesarios + // para configurar el hardware en tiempo de ejecución. + // cfgs[i] = ...; + } + + return cfgs; + } + + struct Instance { + + /** + * @brief Constructs the object of type T in the allocated MPU memory. + * @param args Arguments to forward to T's constructor. + * @return Pointer to the constructed object of type T. + */ + template + T* construct(Args&&... args) { + is_valid_type(); + return new (ptr) T(std::forward(args)...); // Placement new + } + + /** + * @brief Casts the stored pointer to the desired type T. + * @tparam T The type to cast the pointer to. + * @return Pointer of type T. + */ + template + T* as() { + is_valid_type(); + return static_cast(ptr); + } + + private: + template friend struct Init; + + template + void is_valid_type() { + static_assert(std::is_standard_layout_v && std::is_trivial_v, + "MPU Buffer can only store POD types (standard layout and trivial)."); + if (alignof(T) > alignment) { + ErrorHandler("Type alignment (%d) exceeds MPU buffer alignment (%d).", alignof(T), alignment); + } + } + + void* ptr; + std::size_t size; + Instance() : ptr{nullptr}, size{0} {} + }; + + // 7. Inicialización runtime: aplica Config → crea Instance[] + template + struct Init { + static inline std::array instances{}; + + // Esta función se llama desde Board::init() en TIEMPO DE EJECUCIÓN. + // Aquí ya no se hacen checks de diseño: solo se aplica la configuración + // generada en compile-time y se llama a HAL/LL. + static void init(std::span cfgs) { + for (std::size_t i = 0; i < N; i++) { + const auto &cfg = cfgs[i]; + auto &inst = instances[i]; + + // Inicializar 'inst' a partir de 'cfg': + // - llamadas a HAL/LL + // - set de registros, punteros a periféricos, etc. + } + } + }; + + private: + static consteval uint32_t get_address(MemoryDomain domain, std::size_t offset) { + switch (domain) { + case MemoryDomain::D1: + return 0x24000000 + static_cast(offset); + case MemoryDomain::D2: + return 0x30000000 + static_cast(offset); + case MemoryDomain::D3: + return 0x38000000 + static_cast(offset); + default: + ErrorHandler("Invalid Memory Domain"); + return 0; + } + } + + static consteval uint32_t align_up(uint32_t address, std::size_t alignment) { + uint32_t mask = static_cast(alignment - 1); + return (address + mask) & ~mask; + } + + static consteval std::pair get_size_needed(std::size_t size) { + // Get next power of two + if (popcount(size) == 1) { + return {size, 0}; + } else { + size_t power = 1 + countl_zero(size); + + if (power < 5) { + power = 5; // Minimum MPU region size is 32 bytes + } + + // MPU can divide to 8 subregions, so try to get a closer size + size_t subregion_size = 1U << (power - 3); + size_t num_subregions = (size + subregion_size - 1) / subregion_size; // Round up division + uint8_t subregion_disable = static_cast(~(0xFFU << num_subregions)); + + return {subregion_size * num_subregions, subregion_disable}; + } + } +}; + +#endif // MPU_HPP \ No newline at end of file From 3286140968520f955e2a0e2bef7b5bbe5268a2b0 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 10 Dec 2025 17:41:30 +0100 Subject: [PATCH 081/166] fix(MPU): Bug fixing (MPU subregion calculation, throws and alignment checks) --- Inc/HALAL/Models/MPU.hpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index f87d2698b..d6f933d76 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -146,8 +146,11 @@ struct MPUDomain { void is_valid_type() { static_assert(std::is_standard_layout_v && std::is_trivial_v, "MPU Buffer can only store POD types (standard layout and trivial)."); - if (alignof(T) > alignment) { - ErrorHandler("Type alignment (%d) exceeds MPU buffer alignment (%d).", alignof(T), alignment); + if (sizeof(T) > size) { + ErrorHandler("Requested type size exceeds allocated MPU buffer size."); + } + if (reinterpret_cast(ptr) % alignof(T) != 0) { + ErrorHandler("Requested type alignment is not satisfied by allocated MPU buffer."); } } @@ -186,8 +189,7 @@ struct MPUDomain { case MemoryDomain::D3: return 0x38000000 + static_cast(offset); default: - ErrorHandler("Invalid Memory Domain"); - return 0; + throw "Invalid Memory Domain"; } } @@ -210,7 +212,8 @@ struct MPUDomain { // MPU can divide to 8 subregions, so try to get a closer size size_t subregion_size = 1U << (power - 3); size_t num_subregions = (size + subregion_size - 1) / subregion_size; // Round up division - uint8_t subregion_disable = static_cast(~(0xFFU << num_subregions)); + uint8_t subregion_disable = static_cast(~((0xFFU << num_subregions) - 1)); + static_assert(subregion_disable != 0xFF, "Something isn't working on the MPU region size calculation."); return {subregion_size * num_subregions, subregion_disable}; } From 755fd4ba077904e49db68d0380cd3562db51728c Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 10 Dec 2025 17:53:11 +0100 Subject: [PATCH 082/166] fix(MPU): Minor bug fixes (missing std::, incorrect subregion mask) --- Inc/HALAL/Models/MPU.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index d6f933d76..351165820 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -200,10 +200,10 @@ struct MPUDomain { static consteval std::pair get_size_needed(std::size_t size) { // Get next power of two - if (popcount(size) == 1) { + if (std::popcount(size) == 1) { return {size, 0}; } else { - size_t power = 1 + countl_zero(size); + size_t power = 1 + std::countl_zero(size); if (power < 5) { power = 5; // Minimum MPU region size is 32 bytes @@ -212,7 +212,7 @@ struct MPUDomain { // MPU can divide to 8 subregions, so try to get a closer size size_t subregion_size = 1U << (power - 3); size_t num_subregions = (size + subregion_size - 1) / subregion_size; // Round up division - uint8_t subregion_disable = static_cast(~((0xFFU << num_subregions) - 1)); + uint8_t subregion_disable = static_cast(~((1U << num_subregions) - 1)); static_assert(subregion_disable != 0xFF, "Something isn't working on the MPU region size calculation."); return {subregion_size * num_subregions, subregion_disable}; From 1b5c8fe06dc18efede3875e814fdb6f439c94711 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 10:17:55 +0100 Subject: [PATCH 083/166] feat(MPU): Initial implementation of MPUManager refactor --- Inc/HALAL/Models/MPU.hpp | 407 +++++++++++++++++++++++++++++---------- 1 file changed, 309 insertions(+), 98 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 351165820..2b43ff998 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -40,8 +40,10 @@ struct MPUDomain { }; // Buffer Request Wrapper + template struct Buffer { using domain = MPUDomain; + using buffer_type = T; Entry e; /** @@ -51,13 +53,12 @@ struct MPUDomain { * @param domain The memory domain where the buffer should be allocated. * @param force_cache_alignment If true, forces the buffer to be cache line aligned (32 bytes, takes the rest as padding). */ - template consteval Buffer(MemoryType type = MemoryType::NonCached, MemoryDomain domain = MemoryDomain::D2, bool force_cache_alignment = false) requires(std::is_standard_layout_v && std::is_trivial_v) : e{ domain, type, - force_cache_alignment ? std::max(std::size_t(32), alignof(T)) : alignof(T), + force_cache_alignment ? 32 : alignof(T), sizeof(T)} { static_assert(alignof(T) <= 32, "Requested type has alignment greater than cache line size (32 bytes)."); @@ -69,155 +70,365 @@ struct MPUDomain { } }; + static constexpr std::size_t max_instances = HALAL_MPUBUFFERS_MAX_INSTANCES; struct Config { - uint32_t base_address; - + uint32_t offset; // Offset relative to the start of the domain buffer std::size_t size; + MemoryDomain domain; + + // MPU Configuration Data + bool is_mpu_leader; // If true, this entry triggers an MPU region config with the below params + uint8_t mpu_number; // MPU Region Number + uint8_t mpu_size; // Encoded Size (e.g. MPU_REGION_SIZE_256B) + uint8_t mpu_subregion; // Subregion Disable Mask + }; + + // Helper to calculate sizes needed for static arrays + struct DomainSizes { + size_t d1_total = 0; + size_t d2_total = 0; + size_t d3_total = 0; - // MPU Config Data - bool is_mpu_leader; - MPU_Region_InitTypeDef mpu_init; + size_t d1_nc_size = 0; + size_t d2_nc_size = 0; + size_t d3_nc_size = 0; }; - // 5. Build compile-time: Entry[] → Config[] - // - // IMPORTANTE: - // - Esta función es consteval: se ejecuta en tiempo de compilación. - // - Aquí es donde se hacen TODAS las validaciones estáticas sobre los Entry. - // + static consteval uint32_t align_up(uint32_t val, size_t align) { + return (val + align - 1) & ~(align - 1); // Align up to 'align' leaving the minimum padding + } + + static consteval DomainSizes calculate_total_sizes(std::span entries) { + DomainSizes sizes; + size_t counters[3] = {}; + + size_t alignments[] = {32, 16, 8, 4, 2, 1}; + + /* Non-Cached Pass */ + for (size_t align : alignments) { + for (const auto& entry : entries) { + if (entry.memory_type == MemoryType::NonCached && entry.alignment == align) { + size_t idx = static_cast(entry.memory_domain) - 1; + counters[idx].val = align_up(counters[idx].val, align); + counters[idx].val += entry.size_in_bytes; + } + } + } + + // Align Non-Cached section end to the actual MPU Region boundary + // This ensures the Cached section starts exactly where the Non-Cached MPU region ends (or effectively ends via subregions) + for(int i=0; i<3; i++) { + if (counters[i] > 0) { + auto [r_size, r_sub] = get_size_needed(counters[i]); + + // Store the raw MPU region size for alignment purposes + if(i==0) sizes.d1_nc_size = r_size; + if(i==1) sizes.d2_nc_size = r_size; + if(i==2) sizes.d3_nc_size = r_size; + + counters[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled + counters[i] = align_up(counters[i], 32); // Align to 32 bytes just in case + } + } + + /* Cached Pass */ + for (size_t align : alignments) { + for (const auto& entry : entries) { // Inneficient but consteval is fine, easier to read + if (entry.memory_type == MemoryType::Cached && entry.alignment == align) { + size_t idx = static_cast(entry.memory_domain) - 1; + counters[idx].val = align_up(counters[idx].val, align); + counters[idx].val += entry.size_in_bytes; + } + } + } + + // Align final total to 32 bytes just in case + sizes.d1_total = align_up(counters[0].val, 32); + sizes.d2_total = align_up(counters[1].val, 32); + sizes.d3_total = align_up(counters[2].val, 32); + return sizes; + } + template static consteval std::array build(std::span entries) { std::array cfgs{}; + + uint32_t offsets[3] = {}; // D1, D2, D3 + uint32_t assigned_offsets[N]; + + size_t alignments[] = {32, 16, 8, 4, 2, 1}; + + /* Non-Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::NonCached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } + } + } + + // Capture Non-Cached Sizes for MPU and adjust offsets for Cached data + size_t nc_sizes[3]; + for(int i=0; i<3; i++) { + if (offsets[i] > 0) { + auto [r_size, r_sub] = get_size_needed(offsets[i]); + nc_sizes[i] = offsets[i]; + + // Move the offset pointer to the end of the MPU region block to have Cached data start after it + offsets[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled + offsets[i] = align_up(offsets[i], 32); // Align to 32 bytes just in case + } else { + nc_sizes[i] = 0; + } + } + + /* Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::Cached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } + } + } + + /* Build Configs */ + bool domain_configured[3] = {false, false, false}; for (std::size_t i = 0; i < N; i++) { - const Entry &e = entries[i]; - - // Aquí se pueden hacer checks globales y locales: - // - Comprobar que no se repite el mismo recurso. - // - Verificar que el recurso es válido para este dominio. - // - Validar que la combinación de parámetros tiene sentido. - - // Ejemplo de patrón (pseudo-código): - // for (std::size_t j = 0; j < i; ++j) { - // if (entries[j].pin == e.pin && entries[j].port == e.port) { - // struct duplicate_resource {}; - // throw duplicate_resource{}; - // } - // } - - // A partir de 'e' se construye cfgs[i] con los datos necesarios - // para configurar el hardware en tiempo de ejecución. - // cfgs[i] = ...; + cfgs[i].size = entries[i].size_in_bytes; + cfgs[i].domain = entries[i].memory_domain; + cfgs[i].offset = assigned_offsets[i]; + cfgs[i].is_mpu_leader = false; + + if (entries[i].memory_type == MemoryType::NonCached) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + if (!domain_configured[d_idx]) { + // This entry is the "Leader" responsible for configuring the MPU region for the whole domain + cfgs[i].is_mpu_leader = true; + domain_configured[d_idx] = true; + + auto [r_size, r_sub] = get_size_needed(nc_sizes[d_idx]); + cfgs[i].mpu_size = get_region_size_encoding(r_size); + cfgs[i].mpu_subregion = r_sub; + cfgs[i].mpu_number = (d_idx == 0) ? MPU_REGION_NUMBER3 : + (d_idx == 1) ? MPU_REGION_NUMBER5 : MPU_REGION_NUMBER7; + } + } } return cfgs; } struct Instance { + void* ptr; + std::size_t size; - /** - * @brief Constructs the object of type T in the allocated MPU memory. - * @param args Arguments to forward to T's constructor. - * @return Pointer to the constructed object of type T. - */ template T* construct(Args&&... args) { - is_valid_type(); - return new (ptr) T(std::forward(args)...); // Placement new + validate(); + return new (ptr) T(std::forward(args)...); } - /** - * @brief Casts the stored pointer to the desired type T. - * @tparam T The type to cast the pointer to. - * @return Pointer of type T. - */ template T* as() { - is_valid_type(); + validate(); return static_cast(ptr); } private: - template friend struct Init; - template - void is_valid_type() { - static_assert(std::is_standard_layout_v && std::is_trivial_v, - "MPU Buffer can only store POD types (standard layout and trivial)."); - if (sizeof(T) > size) { - ErrorHandler("Requested type size exceeds allocated MPU buffer size."); - } - if (reinterpret_cast(ptr) % alignof(T) != 0) { - ErrorHandler("Requested type alignment is not satisfied by allocated MPU buffer."); - } + void validate() { + if (sizeof(T) != size) ErrorHandler("MPU: Buffer size mismatch."); + if (reinterpret_cast(ptr) % alignof(T) != 0) ErrorHandler("MPU: Buffer alignment mismatch."); } - - void* ptr; - std::size_t size; - Instance() : ptr{nullptr}, size{0} {} }; - // 7. Inicialización runtime: aplica Config → crea Instance[] + template struct Init { static inline std::array instances{}; + static constexpr auto Sizes = calculate_total_sizes(Entries); + + // --- Actual Storage (Placed by Linker) --- + // These sections must be defined in the Linker Script. + // They will be placed automatically, avoiding conflicts with other data. + // Alignment must match the MPU region size for Non-Cached areas. + static constexpr size_t d1_align = Sizes.d1_nc_size > 0 ? Sizes.d1_nc_size : 32; + static constexpr size_t d2_align = Sizes.d2_nc_size > 0 ? Sizes.d2_nc_size : 32; + static constexpr size_t d3_align = Sizes.d3_nc_size > 0 ? Sizes.d3_nc_size : 32; + + static inline alignas(d1_align) uint8_t d1_nc_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1] __attribute__((section(".mpu_ram_d1_nc"))); + static inline alignas(d2_align) uint8_t d2_nc_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1] __attribute__((section(".mpu_ram_d2_nc"))); + static inline alignas(d3_align) uint8_t d3_nc_buffer[Sizes.d3_total > 0 ? Sizes.d3_total : 1] __attribute__((section(".mpu_ram_d3_nc"))); - // Esta función se llama desde Board::init() en TIEMPO DE EJECUCIÓN. - // Aquí ya no se hacen checks de diseño: solo se aplica la configuración - // generada en compile-time y se llama a HAL/LL. static void init(std::span cfgs) { + HAL_MPU_Disable(); + configure_static_regions(); + + uint8_t* bases[3] = { &d1_nc_buffer[0], &d2_nc_buffer[0], &d3_nc_buffer[0] }; + for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; auto &inst = instances[i]; - // Inicializar 'inst' a partir de 'cfg': - // - llamadas a HAL/LL - // - set de registros, punteros a periféricos, etc. + if (cfg.domain == MemoryDomain::D1 || cfg.domain == MemoryDomain::D2 || cfg.domain == MemoryDomain::D3) { + size_t d_idx = static_cast(cfg.domain) - 1; + // Calculate absolute address: Base + Offset + inst.ptr = bases[d_idx] + cfg.offset; + inst.size = cfg.size; + + if (cfg.is_mpu_leader) { + MPU_Region_InitTypeDef init = {0}; + init.Enable = MPU_REGION_ENABLE; + init.Number = cfg.mpu_number; + init.BaseAddress = (uint32_t)bases[d_idx]; // Base of the whole buffer + init.Size = cfg.mpu_size; + init.SubRegionDisable = cfg.mpu_subregion; + init.TypeExtField = MPU_TEX_LEVEL1; // Normal, Non-Cached + init.AccessPermission = MPU_REGION_FULL_ACCESS; + init.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + init.IsShareable = MPU_ACCESS_SHAREABLE; + init.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + init.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&init); + } + } } + + HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); + SCB_EnableICache(); + SCB_EnableDCache(); } }; private: - static consteval uint32_t get_address(MemoryDomain domain, std::size_t offset) { - switch (domain) { - case MemoryDomain::D1: - return 0x24000000 + static_cast(offset); - case MemoryDomain::D2: - return 0x30000000 + static_cast(offset); - case MemoryDomain::D3: - return 0x38000000 + static_cast(offset); - default: - throw "Invalid Memory Domain"; - } + static consteval std::pair get_size_needed(std::size_t size) { + if (size == 0) return {32, 0xFF}; + size_t power = std::bit_width(size); + if (power < 5) power = 5; // Min 32B + size_t subregion_size = 1U << (power - 3); + size_t num_subregions = (size + subregion_size - 1) / subregion_size; + uint8_t subregion_disable = static_cast(~((1U << num_subregions) - 1)); + return {(1U << power), subregion_disable}; } - static consteval uint32_t align_up(uint32_t address, std::size_t alignment) { - uint32_t mask = static_cast(alignment - 1); - return (address + mask) & ~mask; + static consteval uint8_t get_region_size_encoding(std::size_t size) { + if (size <= 32) return MPU_REGION_SIZE_32B; + if (size <= 64) return MPU_REGION_SIZE_64B; + if (size <= 128) return MPU_REGION_SIZE_128B; + if (size <= 256) return MPU_REGION_SIZE_256B; + if (size <= 512) return MPU_REGION_SIZE_512B; + if (size <= 1024) return MPU_REGION_SIZE_1KB; + if (size <= 2048) return MPU_REGION_SIZE_2KB; + if (size <= 4096) return MPU_REGION_SIZE_4KB; + if (size <= 8192) return MPU_REGION_SIZE_8KB; + if (size <= 16384) return MPU_REGION_SIZE_16KB; + if (size <= 32768) return MPU_REGION_SIZE_32KB; + if (size <= 65536) return MPU_REGION_SIZE_64KB; + if (size <= 131072) return MPU_REGION_SIZE_128KB; + if (size <= 262144) return MPU_REGION_SIZE_256KB; + if (size <= 524288) return MPU_REGION_SIZE_512KB; + if (size <= 1048576) return MPU_REGION_SIZE_1MB; + if (size <= 2097152) return MPU_REGION_SIZE_2MB; + if (size <= 4194304) return MPU_REGION_SIZE_4MB; + return MPU_REGION_SIZE_4GB; } - static consteval std::pair get_size_needed(std::size_t size) { - // Get next power of two - if (std::popcount(size) == 1) { - return {size, 0}; - } else { - size_t power = 1 + std::countl_zero(size); - - if (power < 5) { - power = 5; // Minimum MPU region size is 32 bytes - } + static void configure_static_regions() { + MPU_Region_InitTypeDef MPU_InitStruct = {0}; + + // Background (No Access) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER0; + MPU_InitStruct.BaseAddress = 0x0; + MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; + MPU_InitStruct.SubRegionDisable = 0x87; + MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // Flash (Non-cached, Executable) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER1; + MPU_InitStruct.BaseAddress = 0x08000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_1MB; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // This should be scrutinized to see why it was non-cacheable before changing to cacheable + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D1 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER2; + MPU_InitStruct.BaseAddress = 0x24000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; + MPU_InitStruct.SubRegionDisable = 0xE0; // Only 320KB available + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D2 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER4; + MPU_InitStruct.BaseAddress = 0x30000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // Ethernet Descriptors (D2 Base) - Legacy, should change Ethernet driver to use MPU buffers + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER8; + MPU_InitStruct.BaseAddress = 0x30000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_512B; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; // Device + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D3 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER6; // FIX: Changed from 5 to 6 to avoid collision with D2 NC + MPU_InitStruct.BaseAddress = 0x38000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); - // MPU can divide to 8 subregions, so try to get a closer size - size_t subregion_size = 1U << (power - 3); - size_t num_subregions = (size + subregion_size - 1) / subregion_size; // Round up division - uint8_t subregion_disable = static_cast(~((1U << num_subregions) - 1)); - static_assert(subregion_disable != 0xFF, "Something isn't working on the MPU region size calculation."); + /** + * Other regions are: + * 3. D1 RAM (Non-Cached) - Configured dynamically + * 5. D2 RAM (Non-Cached) - Configured dynamically + * 7. D3 RAM (Non-Cached) - Configured dynamically + */ - return {subregion_size * num_subregions, subregion_disable}; - } + /** + * Other memory areas (not configured explicitly): + * - Peripheral space (0x40000000 - 0x5FFFFFFF): Defaults to device memory + */ } }; -#endif // MPU_HPP \ No newline at end of file +#endif // MPU_HPP From ed0a65aa8e607eae9302b744b2ea414102cf076b Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 11:07:40 +0100 Subject: [PATCH 084/166] feat(MPU)!: Bug fixing and changes on the infrastructure to allow proper MPU configuration --- Inc/HALAL/HALAL.hpp | 1 + Inc/HALAL/Models/MPU.hpp | 83 +++++++++++++++------------------------- Inc/ST-LIB.hpp | 8 +++- 3 files changed, 39 insertions(+), 53 deletions(-) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 316c9605a..1cab3c0b1 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -36,6 +36,7 @@ #include "HALAL/Services/FMAC/FMAC.hpp" #include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" #include "HALAL/Services/InfoWarning/InfoWarning.hpp" #include "HALAL/Services/Watchdog/Watchdog.hpp" diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 2b43ff998..dcaea81dc 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -82,6 +82,7 @@ struct MPUDomain { bool is_mpu_leader; // If true, this entry triggers an MPU region config with the below params uint8_t mpu_number; // MPU Region Number uint8_t mpu_size; // Encoded Size (e.g. MPU_REGION_SIZE_256B) + std::size_t mpu_region_size; // Actual size in bytes of the MPU region uint8_t mpu_subregion; // Subregion Disable Mask }; @@ -100,54 +101,25 @@ struct MPUDomain { return (val + align - 1) & ~(align - 1); // Align up to 'align' leaving the minimum padding } - static consteval DomainSizes calculate_total_sizes(std::span entries) { - DomainSizes sizes; - size_t counters[3] = {}; - - size_t alignments[] = {32, 16, 8, 4, 2, 1}; - - /* Non-Cached Pass */ - for (size_t align : alignments) { - for (const auto& entry : entries) { - if (entry.memory_type == MemoryType::NonCached && entry.alignment == align) { - size_t idx = static_cast(entry.memory_domain) - 1; - counters[idx].val = align_up(counters[idx].val, align); - counters[idx].val += entry.size_in_bytes; - } - } - } - - // Align Non-Cached section end to the actual MPU Region boundary - // This ensures the Cached section starts exactly where the Non-Cached MPU region ends (or effectively ends via subregions) - for(int i=0; i<3; i++) { - if (counters[i] > 0) { - auto [r_size, r_sub] = get_size_needed(counters[i]); - - // Store the raw MPU region size for alignment purposes - if(i==0) sizes.d1_nc_size = r_size; - if(i==1) sizes.d2_nc_size = r_size; - if(i==2) sizes.d3_nc_size = r_size; - counters[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled - counters[i] = align_up(counters[i], 32); // Align to 32 bytes just in case - } - } - - /* Cached Pass */ - for (size_t align : alignments) { - for (const auto& entry : entries) { // Inneficient but consteval is fine, easier to read - if (entry.memory_type == MemoryType::Cached && entry.alignment == align) { - size_t idx = static_cast(entry.memory_domain) - 1; - counters[idx].val = align_up(counters[idx].val, align); - counters[idx].val += entry.size_in_bytes; - } + static consteval DomainSizes calculate_total_sizes(std::span configs) { + DomainSizes sizes; + for (const auto& cfg : configs) { + size_t end = cfg.offset + cfg.size; + if (cfg.domain == MemoryDomain::D1) sizes.d1_total = std::max(sizes.d1_total, end); + else if (cfg.domain == MemoryDomain::D2) sizes.d2_total = std::max(sizes.d2_total, end); + else if (cfg.domain == MemoryDomain::D3) sizes.d3_total = std::max(sizes.d3_total, end); + + if (cfg.is_mpu_leader) { + if (cfg.domain == MemoryDomain::D1) sizes.d1_nc_size = cfg.mpu_region_size; + else if (cfg.domain == MemoryDomain::D2) sizes.d2_nc_size = cfg.mpu_region_size; + else if (cfg.domain == MemoryDomain::D3) sizes.d3_nc_size = cfg.mpu_region_size; } } - - // Align final total to 32 bytes just in case - sizes.d1_total = align_up(counters[0].val, 32); - sizes.d2_total = align_up(counters[1].val, 32); - sizes.d3_total = align_up(counters[2].val, 32); + // Align totals to 32 bytes + sizes.d1_total = align_up(sizes.d1_total, 32); + sizes.d2_total = align_up(sizes.d2_total, 32); + sizes.d3_total = align_up(sizes.d3_total, 32); return sizes; } @@ -207,6 +179,7 @@ struct MPUDomain { cfgs[i].domain = entries[i].memory_domain; cfgs[i].offset = assigned_offsets[i]; cfgs[i].is_mpu_leader = false; + cfgs[i].mpu_region_size = 0; if (entries[i].memory_type == MemoryType::NonCached) { size_t d_idx = static_cast(entries[i].memory_domain) - 1; @@ -218,6 +191,7 @@ struct MPUDomain { auto [r_size, r_sub] = get_size_needed(nc_sizes[d_idx]); cfgs[i].mpu_size = get_region_size_encoding(r_size); cfgs[i].mpu_subregion = r_sub; + cfgs[i].mpu_region_size = r_size; // Store for Init alignment cfgs[i].mpu_number = (d_idx == 0) ? MPU_REGION_NUMBER3 : (d_idx == 1) ? MPU_REGION_NUMBER5 : MPU_REGION_NUMBER7; } @@ -251,11 +225,13 @@ struct MPUDomain { } }; - - template + + template cfgs> struct Init { static inline std::array instances{}; - static constexpr auto Sizes = calculate_total_sizes(Entries); + + // Calculate sizes at compile time from the template parameter + static constexpr auto Sizes = calculate_total_sizes(cfgs); // --- Actual Storage (Placed by Linker) --- // These sections must be defined in the Linker Script. @@ -265,11 +241,14 @@ struct MPUDomain { static constexpr size_t d2_align = Sizes.d2_nc_size > 0 ? Sizes.d2_nc_size : 32; static constexpr size_t d3_align = Sizes.d3_nc_size > 0 ? Sizes.d3_nc_size : 32; - static inline alignas(d1_align) uint8_t d1_nc_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1] __attribute__((section(".mpu_ram_d1_nc"))); - static inline alignas(d2_align) uint8_t d2_nc_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1] __attribute__((section(".mpu_ram_d2_nc"))); - static inline alignas(d3_align) uint8_t d3_nc_buffer[Sizes.d3_total > 0 ? Sizes.d3_total : 1] __attribute__((section(".mpu_ram_d3_nc"))); + __attribute__((section(".mpu_ram_d1_nc"))) alignas(d1_align) + static inline uint8_t d1_nc_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1]; + __attribute__((section(".mpu_ram_d2_nc"))) alignas(d2_align) + static inline uint8_t d2_nc_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1]; + __attribute__((section(".mpu_ram_d3_nc"))) alignas(d3_align) + static inline uint8_t d3_nc_buffer[Sizes.d3_total > 0 ? Sizes.d3_total : 1]; - static void init(std::span cfgs) { + static void init() { HAL_MPU_Disable(); configure_static_regions(); diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 32fb54814..0c1017653 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -71,7 +71,7 @@ template struct BuildCtx { }; using DomainsCtx = BuildCtx; + DigitalInputDomain, MPUDomain /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -90,12 +90,14 @@ template struct Board { constexpr std::size_t gpioN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); + constexpr std::size_t mpuN = domain_size(); // ... struct ConfigBundle { std::array gpio_cfgs; std::array dout_cfgs; std::array din_cfgs; + std::array mpu_cfgs; // ... }; @@ -106,6 +108,8 @@ template struct Board { ctx.template span()), .din_cfgs = DigitalInputDomain::template build( ctx.template span()), + .mpu_cfgs = MPUDomain::template build( + ctx.template span()) // ... }; } @@ -116,6 +120,7 @@ template struct Board { constexpr std::size_t gpioN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); + constexpr std::size_t mpuN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); @@ -123,6 +128,7 @@ template struct Board { GPIODomain::Init::instances); DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); + MPUDomain::Init::init(); // ... } From aa7dddc3f4c4d6b890b9096767a0d85965a34b45 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 12:52:40 +0100 Subject: [PATCH 085/166] reafactor(MPU): Support legacy MPUManager without conflicts --- Inc/HALAL/Models/MPU.hpp | 28 ++++++++- Inc/HALAL/Models/MPUManager/MPUManager.hpp | 68 +--------------------- 2 files changed, 26 insertions(+), 70 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index dcaea81dc..0898b7d39 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -16,6 +16,7 @@ #include "C++Utilities/CppImports.hpp" #include "ErrorHandler/ErrorHandler.hpp" #include "stm32h7xx_hal.h" +#include "HALAL/Models/MPUManager/MPUManager.hpp" struct MPUDomain { @@ -240,19 +241,40 @@ struct MPUDomain { static constexpr size_t d1_align = Sizes.d1_nc_size > 0 ? Sizes.d1_nc_size : 32; static constexpr size_t d2_align = Sizes.d2_nc_size > 0 ? Sizes.d2_nc_size : 32; static constexpr size_t d3_align = Sizes.d3_nc_size > 0 ? Sizes.d3_nc_size : 32; + static constexpr size_t legacy_size = NO_CACHED_RAM_MAXIMUM_SPACE; __attribute__((section(".mpu_ram_d1_nc"))) alignas(d1_align) static inline uint8_t d1_nc_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1]; __attribute__((section(".mpu_ram_d2_nc"))) alignas(d2_align) static inline uint8_t d2_nc_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1]; - __attribute__((section(".mpu_ram_d3_nc"))) alignas(d3_align) - static inline uint8_t d3_nc_buffer[Sizes.d3_total > 0 ? Sizes.d3_total : 1]; + __attribute__((section(".mpu_ram_d3_nc"))) alignas(std::max(d3_align, legacy_size)) + static inline uint8_t d3_nc_buffer[(Sizes.d3_total > 0 ? Sizes.d3_total : 0) + legacy_size]; static void init() { HAL_MPU_Disable(); configure_static_regions(); - uint8_t* bases[3] = { &d1_nc_buffer[0], &d2_nc_buffer[0], &d3_nc_buffer[0] }; + // Configure Legacy MPUManager Region (D3 Non-Cached) + // We place it at the beginning of d3_nc_buffer + MPU_Region_InitTypeDef Legacy_InitStruct = {0}; + Legacy_InitStruct.Enable = MPU_REGION_ENABLE; + Legacy_InitStruct.Number = MPU_REGION_NUMBER9; // Use a high region number to override D3 Cached + Legacy_InitStruct.BaseAddress = (uint32_t)&d3_nc_buffer[0]; + Legacy_InitStruct.Size = get_region_size_encoding(legacy_size); + Legacy_InitStruct.SubRegionDisable = 0x0; + Legacy_InitStruct.TypeExtField = MPU_TEX_LEVEL1; + Legacy_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + Legacy_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + Legacy_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + Legacy_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + Legacy_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&Legacy_InitStruct); + + // Set the legacy pointer + MPUManager::no_cached_ram_start = &d3_nc_buffer[0]; + + // Adjust bases for new buffers (they start after legacy_size) + uint8_t* bases[3] = { &d1_nc_buffer[0], &d2_nc_buffer[0], &d3_nc_buffer[legacy_size] }; for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; diff --git a/Inc/HALAL/Models/MPUManager/MPUManager.hpp b/Inc/HALAL/Models/MPUManager/MPUManager.hpp index 9b63f46e2..73493fa1b 100644 --- a/Inc/HALAL/Models/MPUManager/MPUManager.hpp +++ b/Inc/HALAL/Models/MPUManager/MPUManager.hpp @@ -5,75 +5,9 @@ #define NO_CACHED_RAM_MAXIMUM_SPACE 2048 -extern unsigned long _no_cached_ram_start; - - class MPUManager{ + friend struct MPUDomain; public: - static struct config{ - bool using_cache = true; - }MPUConfig;/**< MPU configuration defined un Runes.hpp*/ - - static void start(){ - MPU_Region_InitTypeDef MPU_InitStruct = {0}; - HAL_MPU_Disable(); - - MPU_InitStruct.Enable = MPU_REGION_ENABLE; - MPU_InitStruct.Number = MPU_REGION_NUMBER0; - MPU_InitStruct.BaseAddress = 0x0; - MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; - MPU_InitStruct.SubRegionDisable = 0x87; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; - MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER1; - MPU_InitStruct.BaseAddress = 0x30000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; - MPU_InitStruct.SubRegionDisable = 0x0; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER2; - MPU_InitStruct.Size = MPU_REGION_SIZE_512B; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER3; - MPU_InitStruct.BaseAddress = 0x08000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_1MB; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - - MPU_InitStruct.Number = MPU_REGION_NUMBER4; - MPU_InitStruct.BaseAddress = 0x38000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; - MPU_InitStruct.SubRegionDisable = 0x0; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /* Enables the MPU */ - HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); - - if(MPUConfig.using_cache){ - SCB_EnableICache(); - SCB_EnableDCache(); - } - } - static void* allocate_non_cached_memory(uint32_t size){ void* buffer = (void*)((uint8_t*)no_cached_ram_start + no_cached_ram_occupied_bytes); no_cached_ram_occupied_bytes = no_cached_ram_occupied_bytes + size; From 3f826bd3a7c50c8d53112b4f26467c9b444bc5e3 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 12:53:47 +0100 Subject: [PATCH 086/166] fix(MPU): Remove MPUManager start call from HALAL init --- Src/HALAL/HALAL.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 2ee470ed9..2a98eb062 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -20,7 +20,6 @@ static void common_start(UART::Peripheral &printf_peripheral) { Watchdog::check_reset_flag(); #endif - MPUManager::start(); HAL_Init(); HALconfig::system_clock(); HALconfig::peripheral_clock(); From 6c2829483ac6938aea52996123271d47cc79e197 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 21:54:00 +0100 Subject: [PATCH 087/166] feat(MPU): Modify linker scripts for MPU configuration --- STM32H723ZGTX_FLASH.ld | 26 +++++++++++++++++++++----- STM32H723ZGTX_RAM.ld | 23 ++++++++++++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/STM32H723ZGTX_FLASH.ld b/STM32H723ZGTX_FLASH.ld index 12b8d7793..a5bf2e7e0 100644 --- a/STM32H723ZGTX_FLASH.ld +++ b/STM32H723ZGTX_FLASH.ld @@ -170,6 +170,13 @@ SECTIONS __bss_end__ = _ebss; } >RAM_D1 + .mpu_ram_d1_nc : + { + . = ALIGN(32); + *(.mpu_ram_d1_nc) + . = ALIGN(32); + } >RAM_D1 + /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { @@ -191,12 +198,21 @@ SECTIONS *(.TxDecripSection) } >RAM_D2 - .stlib_no_cache_ram_pool : + + .mpu_ram_d2_nc : { - . = ABSOLUTE(0x38000000); - _no_cached_ram_start = .; - + . = ALIGN(32); + *(.mpu_ram_d2_nc) + . = ALIGN(32); + } >RAM_D2 + + .mpu_ram_d3_nc : + { + . = ALIGN(32); + *(.mpu_ram_d3_nc) + . = ALIGN(32); } >RAM_D3 + /* Remove information from the standard libraries */ /DISCARD/ : { @@ -206,4 +222,4 @@ SECTIONS } .ARM.attributes 0 : { *(.ARM.attributes) } -} +} \ No newline at end of file diff --git a/STM32H723ZGTX_RAM.ld b/STM32H723ZGTX_RAM.ld index 4d2e09c43..da990d738 100644 --- a/STM32H723ZGTX_RAM.ld +++ b/STM32H723ZGTX_RAM.ld @@ -119,6 +119,13 @@ SECTIONS PROVIDE_HIDDEN (__fini_array_end = .); } >RAM_EXEC + .mpu_ram_d1_nc : + { + . = ALIGN(32); + *(.mpu_ram_d1_nc) + . = ALIGN(32); + } >RAM_EXEC + /* used by the startup to initialize data */ _sidata = LOADADDR(.data); @@ -161,6 +168,20 @@ SECTIONS . = ALIGN(8); } >DTCMRAM + .mpu_ram_d2_nc : + { + . = ALIGN(32); + *(.mpu_ram_d2_nc) + . = ALIGN(32); + } >RAM_D2 + + .mpu_ram_d3_nc : + { + . = ALIGN(32); + *(.mpu_ram_d3_nc) + . = ALIGN(32); + } >RAM_D3 + /* Remove information from the standard libraries */ /DISCARD/ : { @@ -170,4 +191,4 @@ SECTIONS } .ARM.attributes 0 : { *(.ARM.attributes) } -} +} \ No newline at end of file From 4352de3229e8e61ea08dce8ce61d9ff975a2164a Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 21:54:19 +0100 Subject: [PATCH 088/166] fix(MPU): Remove legacy things --- Src/HALAL/Models/MPUManager/MPUManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Src/HALAL/Models/MPUManager/MPUManager.cpp b/Src/HALAL/Models/MPUManager/MPUManager.cpp index c9ef34ca5..b45d8d4b2 100644 --- a/Src/HALAL/Models/MPUManager/MPUManager.cpp +++ b/Src/HALAL/Models/MPUManager/MPUManager.cpp @@ -1,4 +1,3 @@ #include "HALAL/Models/MPUManager/MPUManager.hpp" -void* MPUManager::no_cached_ram_start = (void*)&_no_cached_ram_start; uint32_t MPUManager::no_cached_ram_occupied_bytes = 0; From 5d0dbd02b5ea2fe601444fc3f0fc4ec7a065a942 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 22:20:05 +0100 Subject: [PATCH 089/166] fix(MPU): Fix legacy MPUManager --- Src/HALAL/Models/MPUManager/MPUManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/HALAL/Models/MPUManager/MPUManager.cpp b/Src/HALAL/Models/MPUManager/MPUManager.cpp index b45d8d4b2..99bf036d0 100644 --- a/Src/HALAL/Models/MPUManager/MPUManager.cpp +++ b/Src/HALAL/Models/MPUManager/MPUManager.cpp @@ -1,3 +1,4 @@ #include "HALAL/Models/MPUManager/MPUManager.hpp" +void* MPUManager::no_cached_ram_start = nullptr; uint32_t MPUManager::no_cached_ram_occupied_bytes = 0; From a365558a9f7184909c3b3b4938609764c1cbde36 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 22:20:21 +0100 Subject: [PATCH 090/166] fix(MPU): Fix no buffer array of size zero issue --- Inc/HALAL/Models/MPU.hpp | 130 ++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 0898b7d39..a0ea766f0 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -126,80 +126,84 @@ struct MPUDomain { template static consteval std::array build(std::span entries) { - std::array cfgs{}; - - uint32_t offsets[3] = {}; // D1, D2, D3 - uint32_t assigned_offsets[N]; - - size_t alignments[] = {32, 16, 8, 4, 2, 1}; - - /* Non-Cached Offsets */ - for (size_t align : alignments) { - for (size_t i = 0; i < N; i++) { - if (entries[i].memory_type == MemoryType::NonCached && entries[i].alignment == align) { - size_t d_idx = static_cast(entries[i].memory_domain) - 1; - offsets[d_idx] = align_up(offsets[d_idx], align); - assigned_offsets[i] = offsets[d_idx]; - offsets[d_idx] += entries[i].size_in_bytes; + if constexpr (N == 0) { + return {}; + } else { + std::array cfgs{}; + + uint32_t offsets[3] = {}; // D1, D2, D3 + uint32_t assigned_offsets[N]; + + size_t alignments[] = {32, 16, 8, 4, 2, 1}; + + /* Non-Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::NonCached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } } } - } - // Capture Non-Cached Sizes for MPU and adjust offsets for Cached data - size_t nc_sizes[3]; - for(int i=0; i<3; i++) { - if (offsets[i] > 0) { - auto [r_size, r_sub] = get_size_needed(offsets[i]); - nc_sizes[i] = offsets[i]; - - // Move the offset pointer to the end of the MPU region block to have Cached data start after it - offsets[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled - offsets[i] = align_up(offsets[i], 32); // Align to 32 bytes just in case - } else { - nc_sizes[i] = 0; + // Capture Non-Cached Sizes for MPU and adjust offsets for Cached data + size_t nc_sizes[3]; + for(int i=0; i<3; i++) { + if (offsets[i] > 0) { + auto [r_size, r_sub] = get_size_needed(offsets[i]); + nc_sizes[i] = offsets[i]; + + // Move the offset pointer to the end of the MPU region block to have Cached data start after it + offsets[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled + offsets[i] = align_up(offsets[i], 32); // Align to 32 bytes just in case + } else { + nc_sizes[i] = 0; + } } - } - /* Cached Offsets */ - for (size_t align : alignments) { - for (size_t i = 0; i < N; i++) { - if (entries[i].memory_type == MemoryType::Cached && entries[i].alignment == align) { - size_t d_idx = static_cast(entries[i].memory_domain) - 1; - offsets[d_idx] = align_up(offsets[d_idx], align); - assigned_offsets[i] = offsets[d_idx]; - offsets[d_idx] += entries[i].size_in_bytes; + /* Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::Cached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } } } - } - /* Build Configs */ - bool domain_configured[3] = {false, false, false}; - - for (std::size_t i = 0; i < N; i++) { - cfgs[i].size = entries[i].size_in_bytes; - cfgs[i].domain = entries[i].memory_domain; - cfgs[i].offset = assigned_offsets[i]; - cfgs[i].is_mpu_leader = false; - cfgs[i].mpu_region_size = 0; - - if (entries[i].memory_type == MemoryType::NonCached) { - size_t d_idx = static_cast(entries[i].memory_domain) - 1; - if (!domain_configured[d_idx]) { - // This entry is the "Leader" responsible for configuring the MPU region for the whole domain - cfgs[i].is_mpu_leader = true; - domain_configured[d_idx] = true; - - auto [r_size, r_sub] = get_size_needed(nc_sizes[d_idx]); - cfgs[i].mpu_size = get_region_size_encoding(r_size); - cfgs[i].mpu_subregion = r_sub; - cfgs[i].mpu_region_size = r_size; // Store for Init alignment - cfgs[i].mpu_number = (d_idx == 0) ? MPU_REGION_NUMBER3 : - (d_idx == 1) ? MPU_REGION_NUMBER5 : MPU_REGION_NUMBER7; + /* Build Configs */ + bool domain_configured[3] = {false, false, false}; + + for (std::size_t i = 0; i < N; i++) { + cfgs[i].size = entries[i].size_in_bytes; + cfgs[i].domain = entries[i].memory_domain; + cfgs[i].offset = assigned_offsets[i]; + cfgs[i].is_mpu_leader = false; + cfgs[i].mpu_region_size = 0; + + if (entries[i].memory_type == MemoryType::NonCached) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + if (!domain_configured[d_idx]) { + // This entry is the "Leader" responsible for configuring the MPU region for the whole domain + cfgs[i].is_mpu_leader = true; + domain_configured[d_idx] = true; + + auto [r_size, r_sub] = get_size_needed(nc_sizes[d_idx]); + cfgs[i].mpu_size = get_region_size_encoding(r_size); + cfgs[i].mpu_subregion = r_sub; + cfgs[i].mpu_region_size = r_size; // Store for Init alignment + cfgs[i].mpu_number = (d_idx == 0) ? MPU_REGION_NUMBER3 : + (d_idx == 1) ? MPU_REGION_NUMBER5 : MPU_REGION_NUMBER7; + } } } - } - return cfgs; + return cfgs; + } } struct Instance { From d40a2133ff1145e2de180596b0c80ee1f1578424 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 12 Dec 2025 23:33:36 +0100 Subject: [PATCH 091/166] fix(MPU): Fix ST-LIB so that it accepts templated objects for MPU buffers --- Inc/ST-LIB.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 0c1017653..ebb70246c 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -147,7 +147,9 @@ template struct Board { using DevT = std::remove_cvref_t; if constexpr (std::is_same_v) { if (!found) { - if (&devs == &Target) { + auto dev_ptr = reinterpret_cast(&devs); + auto target_ptr = reinterpret_cast(&Target); + if (dev_ptr == target_ptr) { found = true; } else { ++idx; @@ -176,7 +178,11 @@ template struct Board { constexpr std::size_t idx = domain_index_of(); constexpr std::size_t N = domain_size_for_instance(); - return Domain::template Init::instances[idx]; + if constexpr (std::is_same_v) { + return Domain::template Init::instances[idx]; + } else { + return Domain::template Init::instances[idx]; + } } }; From 6e8eac895dada6ea9ebd146d82c8042da7179116 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 13 Dec 2025 11:49:22 +0100 Subject: [PATCH 092/166] feat(MPU): Add concepts and safer interface --- Inc/HALAL/Models/MPU.hpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index a0ea766f0..84d7ed59f 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -19,6 +19,17 @@ #include "HALAL/Models/MPUManager/MPUManager.hpp" +template +concept mpu_buffer_request = requires(typename T::domain d) { + typename T::buffer_type; + { T{ } } -> std::same_as; +}; + +// POD types only +template +concept mpu_buffer_payload = + std::is_standard_layout_v && std::is_trivial_v; + struct MPUDomain { enum class MemoryDomain : uint8_t { @@ -41,7 +52,7 @@ struct MPUDomain { }; // Buffer Request Wrapper - template + template struct Buffer { using domain = MPUDomain; using buffer_type = T; @@ -55,7 +66,6 @@ struct MPUDomain { * @param force_cache_alignment If true, forces the buffer to be cache line aligned (32 bytes, takes the rest as padding). */ consteval Buffer(MemoryType type = MemoryType::NonCached, MemoryDomain domain = MemoryDomain::D2, bool force_cache_alignment = false) - requires(std::is_standard_layout_v && std::is_trivial_v) : e{ domain, type, @@ -210,24 +220,17 @@ struct MPUDomain { void* ptr; std::size_t size; - template - T* construct(Args&&... args) { - validate(); + template + auto* construct(Args&&... args) { + using T = typename std::remove_cvref_t::buffer_type; return new (ptr) T(std::forward(args)...); } - template - T* as() { - validate(); + template + auto* as() { + using T = typename std::remove_cvref_t::buffer_type; return static_cast(ptr); } - - private: - template - void validate() { - if (sizeof(T) != size) ErrorHandler("MPU: Buffer size mismatch."); - if (reinterpret_cast(ptr) % alignof(T) != 0) ErrorHandler("MPU: Buffer alignment mismatch."); - } }; From f0f99ec122df6fefcb482cfda43c8e8611c7791a Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 13 Dec 2025 11:52:57 +0100 Subject: [PATCH 093/166] feat(MPU): Make construct method return a reference instead of pointer --- Inc/HALAL/Models/MPU.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 84d7ed59f..db0f824d8 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -221,9 +221,9 @@ struct MPUDomain { std::size_t size; template - auto* construct(Args&&... args) { + auto& construct(Args&&... args) { using T = typename std::remove_cvref_t::buffer_type; - return new (ptr) T(std::forward(args)...); + return *new (ptr) T(std::forward(args)...); } template From 6410c72dc5468ffccf5394cc2aa727d8f9ba9f30 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 13 Dec 2025 17:45:56 +0100 Subject: [PATCH 094/166] fix(MPU): Relax constraints on MPU buffers to allow trivially destructible types --- Inc/HALAL/Models/MPU.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index db0f824d8..a1bee8064 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -25,10 +25,10 @@ concept mpu_buffer_request = requires(typename T::domain d) { { T{ } } -> std::same_as; }; -// POD types only +// POD-like buffers that can safely live in static storage without custom destruction. template concept mpu_buffer_payload = - std::is_standard_layout_v && std::is_trivial_v; + std::is_standard_layout_v && std::is_trivially_destructible_v; struct MPUDomain { From 2a291dc9adf783aeb1ff4c3fda07844702174a7d Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 13 Dec 2025 17:46:13 +0100 Subject: [PATCH 095/166] style(MPU): Better wording and documentation --- Inc/HALAL/Models/MPU.hpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index a1bee8064..45b834d28 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -250,23 +250,24 @@ struct MPUDomain { static constexpr size_t d3_align = Sizes.d3_nc_size > 0 ? Sizes.d3_nc_size : 32; static constexpr size_t legacy_size = NO_CACHED_RAM_MAXIMUM_SPACE; + // Always reserves at least 1 byte to avoid zero-length arrays, could possibly be optimized further, but this is only 3B overhead... __attribute__((section(".mpu_ram_d1_nc"))) alignas(d1_align) - static inline uint8_t d1_nc_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1]; + static inline uint8_t d1_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1]; __attribute__((section(".mpu_ram_d2_nc"))) alignas(d2_align) - static inline uint8_t d2_nc_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1]; + static inline uint8_t d2_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1]; __attribute__((section(".mpu_ram_d3_nc"))) alignas(std::max(d3_align, legacy_size)) - static inline uint8_t d3_nc_buffer[(Sizes.d3_total > 0 ? Sizes.d3_total : 0) + legacy_size]; + static inline uint8_t d3_buffer[(Sizes.d3_total > 0 ? Sizes.d3_total : 0) + legacy_size]; static void init() { HAL_MPU_Disable(); configure_static_regions(); // Configure Legacy MPUManager Region (D3 Non-Cached) - // We place it at the beginning of d3_nc_buffer + // We place it at the beginning of d3_buffer MPU_Region_InitTypeDef Legacy_InitStruct = {0}; Legacy_InitStruct.Enable = MPU_REGION_ENABLE; Legacy_InitStruct.Number = MPU_REGION_NUMBER9; // Use a high region number to override D3 Cached - Legacy_InitStruct.BaseAddress = (uint32_t)&d3_nc_buffer[0]; + Legacy_InitStruct.BaseAddress = (uint32_t)&d3_buffer[0]; Legacy_InitStruct.Size = get_region_size_encoding(legacy_size); Legacy_InitStruct.SubRegionDisable = 0x0; Legacy_InitStruct.TypeExtField = MPU_TEX_LEVEL1; @@ -278,10 +279,10 @@ struct MPUDomain { HAL_MPU_ConfigRegion(&Legacy_InitStruct); // Set the legacy pointer - MPUManager::no_cached_ram_start = &d3_nc_buffer[0]; + MPUManager::no_cached_ram_start = &d3_buffer[0]; // Adjust bases for new buffers (they start after legacy_size) - uint8_t* bases[3] = { &d1_nc_buffer[0], &d2_nc_buffer[0], &d3_nc_buffer[legacy_size] }; + uint8_t* bases[3] = { &d1_buffer[0], &d2_buffer[0], &d3_buffer[legacy_size] }; for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; From 846d52deb08e8fba1ffcbdb3320f5077866dee9b Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sat, 13 Dec 2025 18:24:16 +0100 Subject: [PATCH 096/166] feat(MPU): Make MPU ultra restrictive, should check that there's no region outside defined ones --- Inc/HALAL/Models/MPU.hpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 45b834d28..fc3649f0b 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -354,14 +354,16 @@ struct MPUDomain { static void configure_static_regions() { MPU_Region_InitTypeDef MPU_InitStruct = {0}; - // Background (No Access) + // Background (No Access) - Covers all memory not explicitly defined by a further region MPU_InitStruct.Enable = MPU_REGION_ENABLE; MPU_InitStruct.Number = MPU_REGION_NUMBER0; - MPU_InitStruct.BaseAddress = 0x0; + MPU_InitStruct.BaseAddress = 0x00; MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; - MPU_InitStruct.SubRegionDisable = 0x87; + MPU_InitStruct.SubRegionDisable = 0x04; // Disable 512MB at 0x40000000 (Peripheral space) + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); @@ -371,11 +373,13 @@ struct MPUDomain { MPU_InitStruct.Number = MPU_REGION_NUMBER1; MPU_InitStruct.BaseAddress = 0x08000000; MPU_InitStruct.Size = MPU_REGION_SIZE_1MB; - MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.SubRegionDisable = 0x00; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // This should be scrutinized to see why it was non-cacheable before changing to cacheable - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // This should be scrutinized to see why it was non-bufferable before changing to bufferable HAL_MPU_ConfigRegion(&MPU_InitStruct); // D1 RAM (Cached) @@ -384,8 +388,10 @@ struct MPUDomain { MPU_InitStruct.BaseAddress = 0x24000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; MPU_InitStruct.SubRegionDisable = 0xE0; // Only 320KB available + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); @@ -396,8 +402,10 @@ struct MPUDomain { MPU_InitStruct.BaseAddress = 0x30000000; MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); @@ -408,8 +416,10 @@ struct MPUDomain { MPU_InitStruct.BaseAddress = 0x30000000; MPU_InitStruct.Size = MPU_REGION_SIZE_512B; MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; // Device HAL_MPU_ConfigRegion(&MPU_InitStruct); @@ -420,8 +430,10 @@ struct MPUDomain { MPU_InitStruct.BaseAddress = 0x38000000; MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; HAL_MPU_ConfigRegion(&MPU_InitStruct); From ae017490679ddef74b0e70fab4f3f96b3b4922d5 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 13:27:55 +0100 Subject: [PATCH 097/166] feat(Sd): Initial implementation of the infrastructure for Sd --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 302 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 Inc/ST-LIB_LOW/Sd/Sd.hpp diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp new file mode 100644 index 000000000..f6950a177 --- /dev/null +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -0,0 +1,302 @@ +/* + * Sd.hpp + * + * Created on: 13 dec. 2025 + * Author: Boris + */ + +#ifndef SD_HPP +#define SD_HPP + +#include "HALAL/HALAL.hpp" +#include "stm32h7xx_hal.h" +#include "ErrorHandler/ErrorHandler.hpp" + +using ST-LIB::DigitalInputDomain; +using ST-LIB::GPIODomain; + +static SD_HandleTypeDef* g_sdmmc1_handle = nullptr; +static SD_HandleTypeDef* g_sdmmc2_handle = nullptr; + +struct SdDomain { + + enum class Peripheral : uint32_t { + SDMMC1 = SDMMC1_BASE, + SDMMC2 = SDMMC2_BASE, + }; + + + struct Entry { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional cd_pin_idx; // Card Detect pin index in GPIO domain, if any + std::optional wp_pin_idx; // Write Protect pin index in GPIO domain, if any + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; // Hardcoded unless SDMMC1 + std::size_t d1_pin_idx; // Hardcoded unless SDMMC1 + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + struct SdCard { + using domain = SdDomain; + Entry e; + + Peripheral peripheral; + + MPUDomain::Buffer> buffer0; + MPUDomain::Buffer> buffer1; + + std::optional cd; // Card Detect, if any + std::optional wp; // Write Protect, if any + + GPIODomain::GPIO cmd; + GPIODomain::GPIO ck; + GPIODomain::GPIO d0; + GPIODomain::GPIO d1; + GPIODomain::GPIO d2; + GPIODomain::GPIO d3; + + /** + * @brief Construct a new SdCard + * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer + * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::SDMMC1 or Peripheral::SDMMC2) + * @param card_detect Optional Card Detect pin (DigitalInputDomain::DigitalInput), or null for none + * @param write_protect Optional Write Protect pin (DigitalInputDomain::DigitalInput), or null for none + * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8) + * @param d1_pin_for_sdmmc1 D1 pin to use if using SDMMC1 (default PC9) + * @note The other pins (CMD, CK, D2, D3) are fixed for each peripheral. + */ + consteval SdCard(Peripheral sdmmc_peripheral, + DigitalInputDomain::DigitalInput card_detect = null, DigitalInputDomain::DigitalInput write_protect = null, + GPIODomain::Pin d0_pin_for_sdmmc1 = PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = PC9) { + + e.peripheral = sdmmc_peripheral; + + buffer0 = MPUDomain::Buffer>; + buffer1 = MPUDomain::Buffer>; + + cd = card_detect; + wp = write_protect; + + if (sdmmc_peripheral == Peripheral::SDMMC1) { + cmd = GPIODomain::GPIO(PC6, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + ck = GPIODomain::GPIO(PC12, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d0 = GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d1 = GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d2 = GPIODomain::GPIO(PC10, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d3 = GPIODomain::GPIO(PC11, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + } else if (sdmmc_peripheral == Peripheral::SDMMC2) { + cmd = GPIODomain::GPIO(PD7, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + ck = GPIODomain::GPIO(PD6, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d0 = GPIODomain::GPIO(PB14, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d1 = GPIODomain::GPIO(PB15, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d2 = GPIODomain::GPIO(PG11, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + d3 = GPIODomain::GPIO(PG12, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + } else { + throw "Invalid SDMMC peripheral"; + } + } + + + template + consteval void inscribe(Ctx &ctx) const { + e.mpu_buffer0_idx = ctx.template add(e.mpu_buffer0); + e.mpu_buffer1_idx = ctx.template add(e.mpu_buffer1); + + if (e.cd != null) { + e.cd_idx = ctx.template add(cd); + } + if (e.wp != null) { + e.wp_idx = ctx.template add(wp); + } + + e.cmd_pin_idx = ctx.template add(cmd); + e.ck_pin_idx = ctx.template add(ck); + e.d0_pin_idx = ctx.template add(d0); + e.d1_pin_idx = ctx.template add(d1); + e.d2_pin_idx = ctx.template add(d2); + e.d3_pin_idx = ctx.template add(d3); + + ctx.template add(e); + } + }; + + + static constexpr std::size_t max_instances = 2; + + + struct Config { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional cd_pin_idx; + std::optional wp_pin_idx; + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; + std::size_t d1_pin_idx; + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + bool peripheral_used[2] = {false, false}; // SDMMC1, SDMMC2 + + for (std::size_t i = 0; i < N; i++) { + const Entry &e = entries[i]; + + // Verify uniqueness of peripheral usage + std::size_t peripheral_index = (e.peripheral == Peripheral::SDMMC1) ? 0 : 1; + if (peripheral_used[peripheral_index]) throw "SDMMC peripheral already used"; + peripheral_used[peripheral_index] = true; + + // Fill configuration + cfgs[i].peripheral = e.peripheral; + cfgs[i].mpu_buffer0_idx = e.mpu_buffer0_idx; + cfgs[i].mpu_buffer1_idx = e.mpu_buffer1_idx; + cfgs[i].cd_pin_idx = e.cd_pin_idx; + cfgs[i].wp_pin_idx = e.wp_pin_idx; + cfgs[i].cmd_pin_idx = e.cmd_pin_idx; + cfgs[i].ck_pin_idx = e.ck_pin_idx; + cfgs[i].d0_pin_idx = e.d0_pin_idx; + cfgs[i].d1_pin_idx = e.d1_pin_idx; + cfgs[i].d2_pin_idx = e.d2_pin_idx; + cfgs[i].d3_pin_idx = e.d3_pin_idx; + } + + return cfgs; + } + + + // State holder, logic is in SdCardWrapper + struct Instance { + friend class SdCardWrapper; + friend struct Init; + + private: + SD_HandleTypeDef hsd; + + MPUDomain::Instance* mpu_buffer0_instance; + MPUDomain::Instance* mpu_buffer1_instance; + + std::optional cd_instance; + std::optional wp_instance; + + bool card_initialized; + }; + + struct SdCardWrapperI { + + }; + + template + struct SdCardWrapper : public SdCardWrapperI { + using peripheral = decltype(card_request)::peripheral; + using has_cd = decltype(card_request)::cd != null; + using has_wp = decltype(card_request)::wp != null; + + SdCardWrapper(Instance& instance) : instance(instance) {}; + + // Methods to operate the SD card + + private: + Instance& instance; // Actual State + }; + + + template + struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span mpu_buffer_instances, + std::span digital_input_instances) { + + for (std::size_t i = 0; i < N; i++) { + const auto &cfg = cfgs[i]; + auto &inst = instances[i]; + inst.mpu_buffer0_instance = mpu_buffer_instances[cfg.mpu_buffer0_idx]; + inst.mpu_buffer1_instance = mpu_buffer_instances[cfg.mpu_buffer1_idx]; + if (cfg.cd_pin_idx.has_value()) { + inst.cd_instance = &digital_input_instances[cfg.cd_pin_idx.value()]; + } + if (cfg.wp_pin_idx.has_value()) { + inst.wp_instance = &digital_input_instances[cfg.wp_pin_idx.value()]; + } + + inst.hsd.Instance = static_cast(static_cast(cfg.peripheral)); + inst.card_initialized = false; + + // Initialize HAL SD + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; + RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { + ErrorHandler("SDMMC1 clock configuration failed"); + } + + if (cfg.peripheral == Peripheral::SDMMC1) { + g_sdmmc1_handle = &inst.hsd; + __HAL_RCC_SDMMC1_CLK_ENABLE(); + HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + } else if (cfg.peripheral == Peripheral::SDMMC2) { + g_sdmmc2_handle = &inst.hsd; + __HAL_RCC_SDMMC2_CLK_ENABLE(); + HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC2_IRQn); + } + } + } + }; +}; + + +extern "C" void SDMMC1_IRQHandler(void) { + if (g_sdmmc1_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc1_handle); + } +} + +extern "C" void SDMMC2_IRQHandler(void) { + if (g_sdmmc2_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc2_handle); + } +} + +void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); + //SDMMC_CmdStopTransfer(hsd->Instance); +} +void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); + //SDMMC_CmdStopTransfer(hsd->Instance); +} + +void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); + //SDMMC_CmdStopTransfer(hsd->Instance); +} +void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); + //SDMMC_CmdStopTransfer(hsd->Instance); +} + +void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); +} + +void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { + auto sd_instance = reinterpret_cast(hsd->Context); + ErrorHandler("SD Card error occurred"); +} + +#endif // SD_HPP \ No newline at end of file From 965d7045b4bd97e7895684559620af7241a2de27 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 13:52:51 +0100 Subject: [PATCH 098/166] fix(Sd): Fix syntax errors --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 85 ++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index f6950a177..6e6ee0403 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -9,11 +9,12 @@ #define SD_HPP #include "HALAL/HALAL.hpp" +#include "ST-LIB_LOW/ST-LIB_LOW.hpp" #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" -using ST-LIB::DigitalInputDomain; -using ST-LIB::GPIODomain; +using ST_LIB::DigitalInputDomain; +using ST_LIB::GPIODomain; static SD_HandleTypeDef* g_sdmmc1_handle = nullptr; static SD_HandleTypeDef* g_sdmmc2_handle = nullptr; @@ -21,8 +22,8 @@ static SD_HandleTypeDef* g_sdmmc2_handle = nullptr; struct SdDomain { enum class Peripheral : uint32_t { - SDMMC1 = SDMMC1_BASE, - SDMMC2 = SDMMC2_BASE, + sdmmc1 = SDMMC1_BASE, + sdmmc2 = SDMMC2_BASE, }; @@ -48,8 +49,8 @@ struct SdDomain { Peripheral peripheral; - MPUDomain::Buffer> buffer0; - MPUDomain::Buffer> buffer1; + MPUDomain::Buffer> buffer0; // Alignment of 32-bit for SDMMC DMA + MPUDomain::Buffer> buffer1; // Alignment of 32-bit for SDMMC DMA std::optional cd; // Card Detect, if any std::optional wp; // Write Protect, if any @@ -64,7 +65,7 @@ struct SdDomain { /** * @brief Construct a new SdCard * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer - * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::SDMMC1 or Peripheral::SDMMC2) + * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) * @param card_detect Optional Card Detect pin (DigitalInputDomain::DigitalInput), or null for none * @param write_protect Optional Write Protect pin (DigitalInputDomain::DigitalInput), or null for none * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8) @@ -72,31 +73,31 @@ struct SdDomain { * @note The other pins (CMD, CK, D2, D3) are fixed for each peripheral. */ consteval SdCard(Peripheral sdmmc_peripheral, - DigitalInputDomain::DigitalInput card_detect = null, DigitalInputDomain::DigitalInput write_protect = null, + std::optional card_detect, std::optional write_protect, GPIODomain::Pin d0_pin_for_sdmmc1 = PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = PC9) { e.peripheral = sdmmc_peripheral; - buffer0 = MPUDomain::Buffer>; - buffer1 = MPUDomain::Buffer>; + buffer0 = MPUDomain::Buffer>(); + buffer1 = MPUDomain::Buffer>(); cd = card_detect; wp = write_protect; - if (sdmmc_peripheral == Peripheral::SDMMC1) { - cmd = GPIODomain::GPIO(PC6, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - ck = GPIODomain::GPIO(PC12, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d0 = GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d1 = GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d2 = GPIODomain::GPIO(PC10, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d3 = GPIODomain::GPIO(PC11, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - } else if (sdmmc_peripheral == Peripheral::SDMMC2) { - cmd = GPIODomain::GPIO(PD7, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - ck = GPIODomain::GPIO(PD6, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d0 = GPIODomain::GPIO(PB14, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d1 = GPIODomain::GPIO(PB15, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d2 = GPIODomain::GPIO(PG11, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); - d3 = GPIODomain::GPIO(PG12, GPIODomain::OperationMode::AF, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AF::AF12); + if (sdmmc_peripheral == Peripheral::sdmmc1) { + cmd = GPIODomain::GPIO(PC6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + ck = GPIODomain::GPIO(PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d0 = GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d1 = GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d2 = GPIODomain::GPIO(PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d3 = GPIODomain::GPIO(PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + } else if (sdmmc_peripheral == Peripheral::sdmmc2) { + cmd = GPIODomain::GPIO(PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + ck = GPIODomain::GPIO(PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d0 = GPIODomain::GPIO(PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d1 = GPIODomain::GPIO(PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d2 = GPIODomain::GPIO(PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); + d3 = GPIODomain::GPIO(PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); } else { throw "Invalid SDMMC peripheral"; } @@ -105,14 +106,14 @@ struct SdDomain { template consteval void inscribe(Ctx &ctx) const { - e.mpu_buffer0_idx = ctx.template add(e.mpu_buffer0); - e.mpu_buffer1_idx = ctx.template add(e.mpu_buffer1); + e.mpu_buffer0_idx = ctx.template add(buffer0); + e.mpu_buffer1_idx = ctx.template add(buffer1); - if (e.cd != null) { - e.cd_idx = ctx.template add(cd); + if (cd.has_value()) { + e.cd_pin_idx = ctx.template add(cd.value()); } - if (e.wp != null) { - e.wp_idx = ctx.template add(wp); + if (wp.has_value()) { + e.wp_pin_idx = ctx.template add(wp.value()); } e.cmd_pin_idx = ctx.template add(cmd); @@ -154,7 +155,7 @@ struct SdDomain { const Entry &e = entries[i]; // Verify uniqueness of peripheral usage - std::size_t peripheral_index = (e.peripheral == Peripheral::SDMMC1) ? 0 : 1; + std::size_t peripheral_index = (e.peripheral == Peripheral::sdmmc1) ? 0 : 1; if (peripheral_used[peripheral_index]) throw "SDMMC peripheral already used"; peripheral_used[peripheral_index] = true; @@ -200,8 +201,8 @@ struct SdDomain { template struct SdCardWrapper : public SdCardWrapperI { using peripheral = decltype(card_request)::peripheral; - using has_cd = decltype(card_request)::cd != null; - using has_wp = decltype(card_request)::wp != null; + static constexpr bool has_cd = decltype(card_request)::cd.has_value(); + static constexpr bool has_wp = decltype(card_request)::wp.has_value(); SdCardWrapper(Instance& instance) : instance(instance) {}; @@ -232,7 +233,7 @@ struct SdDomain { inst.wp_instance = &digital_input_instances[cfg.wp_pin_idx.value()]; } - inst.hsd.Instance = static_cast(static_cast(cfg.peripheral)); + inst.hsd.Instance = reinterpret_cast(static_cast(cfg.peripheral)); inst.card_initialized = false; // Initialize HAL SD @@ -243,12 +244,12 @@ struct SdDomain { ErrorHandler("SDMMC1 clock configuration failed"); } - if (cfg.peripheral == Peripheral::SDMMC1) { + if (cfg.peripheral == Peripheral::sdmmc1) { g_sdmmc1_handle = &inst.hsd; __HAL_RCC_SDMMC1_CLK_ENABLE(); HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); HAL_NVIC_EnableIRQ(SDMMC1_IRQn); - } else if (cfg.peripheral == Peripheral::SDMMC2) { + } else if (cfg.peripheral == Peripheral::sdmmc2) { g_sdmmc2_handle = &inst.hsd; __HAL_RCC_SDMMC2_CLK_ENABLE(); HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); @@ -273,29 +274,29 @@ extern "C" void SDMMC2_IRQHandler(void) { } void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); //SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); //SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); //SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); //SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); } void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = reinterpret_cast(hsd->Context); ErrorHandler("SD Card error occurred"); } From a0b09beda0310cdd5058a2d5bee1c5a0dacc430d Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 13:53:18 +0100 Subject: [PATCH 099/166] feat(Sd): Add Sd to ST-LIB --- Inc/ST-LIB.hpp | 13 +++++++++++-- Inc/ST-LIB_LOW/ST-LIB_LOW.hpp | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index ebb70246c..4a941a52d 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -71,7 +71,8 @@ template struct BuildCtx { }; using DomainsCtx = BuildCtx; + DigitalInputDomain, MPUDomain, SdDomain + /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -91,6 +92,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... struct ConfigBundle { @@ -98,6 +100,7 @@ template struct Board { std::array dout_cfgs; std::array din_cfgs; std::array mpu_cfgs; + std::array sd_cfgs; // ... }; @@ -109,7 +112,9 @@ template struct Board { .din_cfgs = DigitalInputDomain::template build( ctx.template span()), .mpu_cfgs = MPUDomain::template build( - ctx.template span()) + ctx.template span()), + .sd_cfgs = SdDomain::template build( + ctx.template span()), // ... }; } @@ -121,6 +126,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); @@ -129,6 +135,9 @@ template struct Board { DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); MPUDomain::Init::init(); + SdDomain::Init::init(cfg.sd_cfgs, + MPUDomain::Init::instances, + DigitalInputDomain::Init::instances); // ... } diff --git a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp index b73165c24..bc25ac7ec 100644 --- a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp +++ b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp @@ -3,6 +3,8 @@ #include "ST-LIB_LOW/DigitalOutput2.hpp" #include "ST-LIB_LOW/DigitalInput2.hpp" +#include "ST-LIB_LOW/Sd/Sd.hpp" + #include "Clocks/Counter.hpp" #include "Clocks/Stopwatch.hpp" #include "Sensors/LinearSensor/LinearSensor.hpp" From efb806dbceae54b7d078da2ba8173c08f7b1ba71 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 15:17:21 +0100 Subject: [PATCH 100/166] feat(Sd): Receive CD and WP pin active states --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 6e6ee0403..d67a9ad24 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -31,8 +31,8 @@ struct SdDomain { Peripheral peripheral; std::size_t mpu_buffer0_idx; std::size_t mpu_buffer1_idx; - std::optional cd_pin_idx; // Card Detect pin index in GPIO domain, if any - std::optional wp_pin_idx; // Write Protect pin index in GPIO domain, if any + std::optional> cd_pin_idx; // Card Detect pin index in GPIO domain, if any + std::optional> wp_pin_idx; // Write Protect pin index in GPIO domain, if any std::size_t cmd_pin_idx; std::size_t ck_pin_idx; std::size_t d0_pin_idx; // Hardcoded unless SDMMC1 @@ -52,8 +52,8 @@ struct SdDomain { MPUDomain::Buffer> buffer0; // Alignment of 32-bit for SDMMC DMA MPUDomain::Buffer> buffer1; // Alignment of 32-bit for SDMMC DMA - std::optional cd; // Card Detect, if any - std::optional wp; // Write Protect, if any + std::optional> cd; // Card Detect, if any, and its active state + std::optional> wp; // Write Protect, if any, and its active state GPIODomain::GPIO cmd; GPIODomain::GPIO ck; @@ -66,14 +66,14 @@ struct SdDomain { * @brief Construct a new SdCard * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) - * @param card_detect Optional Card Detect pin (DigitalInputDomain::DigitalInput), or null for none - * @param write_protect Optional Write Protect pin (DigitalInputDomain::DigitalInput), or null for none + * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or null for none + * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or null for none * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8) * @param d1_pin_for_sdmmc1 D1 pin to use if using SDMMC1 (default PC9) * @note The other pins (CMD, CK, D2, D3) are fixed for each peripheral. */ consteval SdCard(Peripheral sdmmc_peripheral, - std::optional card_detect, std::optional write_protect, + std::optional> card_detect_config, std::optional> write_protect_config, GPIODomain::Pin d0_pin_for_sdmmc1 = PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = PC9) { e.peripheral = sdmmc_peripheral; @@ -81,8 +81,8 @@ struct SdDomain { buffer0 = MPUDomain::Buffer>(); buffer1 = MPUDomain::Buffer>(); - cd = card_detect; - wp = write_protect; + cd = card_detect_config; + wp = write_protect_config; if (sdmmc_peripheral == Peripheral::sdmmc1) { cmd = GPIODomain::GPIO(PC6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); @@ -110,10 +110,10 @@ struct SdDomain { e.mpu_buffer1_idx = ctx.template add(buffer1); if (cd.has_value()) { - e.cd_pin_idx = ctx.template add(cd.value()); + e.cd_pin_idx = {ctx.template add(cd.value().first), cd.value().second}; } if (wp.has_value()) { - e.wp_pin_idx = ctx.template add(wp.value()); + e.wp_pin_idx = {ctx.template add(wp.value().first), wp.value().second}; } e.cmd_pin_idx = ctx.template add(cmd); @@ -176,10 +176,14 @@ struct SdDomain { return cfgs; } + enum class BufferSelect : bool { + Buffer0 = false, + Buffer1 = true + }; // State holder, logic is in SdCardWrapper struct Instance { - friend class SdCardWrapper; + friend struct SdCardWrapper; friend struct Init; private: @@ -188,10 +192,11 @@ struct SdDomain { MPUDomain::Instance* mpu_buffer0_instance; MPUDomain::Instance* mpu_buffer1_instance; - std::optional cd_instance; - std::optional wp_instance; + std::optional> cd_instance; + std::optional> wp_instance; bool card_initialized; + BufferSelect current_buffer; // The one that is currently available for CPU access and not used by IDMA }; struct SdCardWrapperI { @@ -201,6 +206,7 @@ struct SdDomain { template struct SdCardWrapper : public SdCardWrapperI { using peripheral = decltype(card_request)::peripheral; + using buffer_type = typename decltype(card_request.buffer0)::buffer_type; static constexpr bool has_cd = decltype(card_request)::cd.has_value(); static constexpr bool has_wp = decltype(card_request)::wp.has_value(); @@ -227,14 +233,16 @@ struct SdDomain { inst.mpu_buffer0_instance = mpu_buffer_instances[cfg.mpu_buffer0_idx]; inst.mpu_buffer1_instance = mpu_buffer_instances[cfg.mpu_buffer1_idx]; if (cfg.cd_pin_idx.has_value()) { - inst.cd_instance = &digital_input_instances[cfg.cd_pin_idx.value()]; + inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; } if (cfg.wp_pin_idx.has_value()) { - inst.wp_instance = &digital_input_instances[cfg.wp_pin_idx.value()]; + inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; } inst.hsd.Instance = reinterpret_cast(static_cast(cfg.peripheral)); + inst.card_initialized = false; + inst.current_buffer = BufferSelect::Buffer0; // Initialize HAL SD RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; From f2845c17d790debaa0c6f454392d6c7be25a8736 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 16:34:58 +0100 Subject: [PATCH 101/166] feat(Sd): Full implementation of the class --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 369 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 350 insertions(+), 19 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index d67a9ad24..1e4f48617 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -19,6 +19,9 @@ using ST_LIB::GPIODomain; static SD_HandleTypeDef* g_sdmmc1_handle = nullptr; static SD_HandleTypeDef* g_sdmmc2_handle = nullptr; +static void* g_sdmmc1_instance_ptr = nullptr; +static void* g_sdmmc2_instance_ptr = nullptr; + struct SdDomain { enum class Peripheral : uint32_t { @@ -186,6 +189,8 @@ struct SdDomain { friend struct SdCardWrapper; friend struct Init; + bool* operation_flag = nullptr; // External flag to indicate that an operation has finished + private: SD_HandleTypeDef hsd; @@ -197,25 +202,313 @@ struct SdDomain { bool card_initialized; BufferSelect current_buffer; // The one that is currently available for CPU access and not used by IDMA - }; - struct SdCardWrapperI { + // Functions + bool is_card_present() { return cd_instance->first->read() == cd_instance->second; } + bool is_write_protected() { return wp_instance->first->read() == wp_instance->second; } + + bool is_busy() { + return (hsd.State != HAL_SD_STATE_TRANSFER); + } + + bool initialize_card() { + if (card_initialized) { return true; } // Already initialized + + HAL_StatusTypeDef status = HAL_SD_Init(&hsd); + + if (status != HAL_OK) { return false; } + + card_initialized = true; + return true; + } + + bool deinitialize_card() { + if (!card_initialized) { return true; } // Already deinitialized + + HAL_StatusTypeDef status = HAL_SD_DeInit(&hsd); + if (status != HAL_OK) { return false; } + + card_initialized = false; + return true; // Placeholder + } + void switch_buffer() { + current_buffer = (current_buffer == BufferSelect::Buffer0) ? BufferSelect::Buffer1 : BufferSelect::Buffer0; + } + + bool configure_idma() { + HAL_StatusTypeDef status = HAL_SDEx_ConfigDMAMultiBuffer(&hsd, + reinterpret_cast(mpu_buffer0_instance->ptr), + reinterpret_cast(mpu_buffer1_instance->ptr), + mpu_buffer0_instance->size / 512); // Number of 512B-blocks + + if (status != HAL_OK) { return false; } + return true; + } }; template - struct SdCardWrapper : public SdCardWrapperI { - using peripheral = decltype(card_request)::peripheral; - using buffer_type = typename decltype(card_request.buffer0)::buffer_type; + struct SdCardWrapper{ static constexpr bool has_cd = decltype(card_request)::cd.has_value(); static constexpr bool has_wp = decltype(card_request)::wp.has_value(); - SdCardWrapper(Instance& instance) : instance(instance) {}; + SdCardWrapper(Instance& instance) : instance(instance) { + check_cd_wp(); + }; + + void init_card() { + check_cd_wp(); + bool success = instance.initialize_card(); + if (!success) { + ErrorHandler("SD Card initialization failed"); + } + } + + void deinit_card() { + check_cd_wp(); + bool success = instance.deinitialize_card(); + if (!success) { + ErrorHandler("SD Card deinitialization failed"); + } + } + + bool is_card_initialized() { + return instance.card_initialized; + } + + bool read_blocks(uint32_t start_block, uint32_t num_blocks, bool& operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (instance.is_busy()) { + return false; // Busy + } + + instance.operation_flag = &operation_complete_flag; + operation_complete_flag = false; + + auto& buffer = get_current_buffer(); + + // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card read operation failed"); + } + + return true; + } + + bool write_blocks(uint32_t start_block, uint32_t num_blocks, bool& operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (instance.is_busy()) { + return false; // Busy + } + + instance.operation_flag = &operation_complete_flag; + operation_complete_flag = false; + + auto& buffer = get_current_buffer(); + + // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card write operation failed"); + } + + return true; + } - // Methods to operate the SD card private: Instance& instance; // Actual State + + + void check_cd_wp() { + if constexpr (has_cd) { + if (!instance.is_card_present()) { ErrorHandler("SD Card not present"); } + } + if constexpr (has_wp) { + if (instance.is_write_protected()) { ErrorHandler("SD Card is write-protected"); } + } + } + + auto& get_current_buffer() { + if (instance.current_buffer == BufferSelect::Buffer0) { + return instance.mpu_buffer0_instance->template as(); + } else { + return instance.mpu_buffer1_instance->template as(); + } + } + + // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs + HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + auto* hsd = instance.hsd; + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd->State == HAL_SD_STATE_READY) + { + if ((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + { + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd->Instance->IDMABASE0; + DmaBase1_reg = hsd->Instance->IDMABASE1; + + if ((hsd->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) + { + hsd->ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd->Instance->DCTRL = 0; + /* Clear old Flags*/ + __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); + + hsd->ErrorCode = HAL_SD_ERROR_NONE; + hsd->State = HAL_SD_STATE_BUSY; + + if (hsd->SdCard.CardType != CARD_SDHC_SDXC) + { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd->Instance, &config); + + hsd->Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd->Instance); + + if (instance.current_buffer == BufferSelect::Buffer1) { + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + instance.switch_buffer(); + + /* Read Blocks in DMA mode */ + hsd->Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Read Multi Block command */ + errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) + { + hsd->State = HAL_SD_STATE_READY; + hsd->ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else + { + return HAL_BUSY; + } + + } + + // Variation of HAL_SDEx_WriteBlocksDMAMultiBuffer to fit our needs + HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + auto* hsd = instance.hsd; + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd->State == HAL_SD_STATE_READY) + { + if ((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) + { + hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd->Instance->IDMABASE0; + DmaBase1_reg = hsd->Instance->IDMABASE1; + if ((hsd->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) + { + hsd->ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd->Instance->DCTRL = 0; + + hsd->ErrorCode = HAL_SD_ERROR_NONE; + + hsd->State = HAL_SD_STATE_BUSY; + + if (hsd->SdCard.CardType != CARD_SDHC_SDXC) + { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd->Instance, &config); + + //hsd->Instance->DCTRL |= SDMMC_DCTRL_FIFORST; // I am manually flushing the FIFO here, hal did not do it + + __SDMMC_CMDTRANS_ENABLE(hsd->Instance); + + //hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + if (instance.current_buffer == BufferSelect::Buffer1) { + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + instance.switch_buffer(); + + /* Write Blocks in DMA mode */ + hsd->Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Write Multi Block command */ + errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) + { + hsd->State = HAL_SD_STATE_READY; + hsd->ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else + { + return HAL_BUSY; + } + } }; @@ -230,8 +523,12 @@ struct SdDomain { for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; auto &inst = instances[i]; - inst.mpu_buffer0_instance = mpu_buffer_instances[cfg.mpu_buffer0_idx]; - inst.mpu_buffer1_instance = mpu_buffer_instances[cfg.mpu_buffer1_idx]; + inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; + inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; + if (!inst.configure_idma()) { + ErrorHandler("SD Card IDMA configuration failed"); + } + if (cfg.cd_pin_idx.has_value()) { inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; } @@ -240,6 +537,12 @@ struct SdDomain { } inst.hsd.Instance = reinterpret_cast(static_cast(cfg.peripheral)); + inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; + inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; + inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + inst.hsd.Init.ClockDiv = 0; + inst.card_initialized = false; inst.current_buffer = BufferSelect::Buffer0; @@ -254,11 +557,13 @@ struct SdDomain { if (cfg.peripheral == Peripheral::sdmmc1) { g_sdmmc1_handle = &inst.hsd; + g_sdmmc1_instance_ptr = &inst; __HAL_RCC_SDMMC1_CLK_ENABLE(); HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); HAL_NVIC_EnableIRQ(SDMMC1_IRQn); } else if (cfg.peripheral == Peripheral::sdmmc2) { g_sdmmc2_handle = &inst.hsd; + g_sdmmc2_instance_ptr = &inst; __HAL_RCC_SDMMC2_CLK_ENABLE(); HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); HAL_NVIC_EnableIRQ(SDMMC2_IRQn); @@ -268,6 +573,11 @@ struct SdDomain { }; }; +static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { + if (hsd == g_sdmmc1_handle) return static_cast(g_sdmmc1_instance_ptr); + if (hsd == g_sdmmc2_handle) return static_cast(g_sdmmc2_instance_ptr); + return nullptr; +} extern "C" void SDMMC1_IRQHandler(void) { if (g_sdmmc1_handle != nullptr) { @@ -281,31 +591,52 @@ extern "C" void SDMMC2_IRQHandler(void) { } } +extern "C" { + void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); - //SDMMC_CmdStopTransfer(hsd->Instance); + auto sd_instance = get_sd_instance(hsd); + if (sd_instance && sd_instance->operation_flag) { + *sd_instance->operation_flag = true; + sd_instance->operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); - //SDMMC_CmdStopTransfer(hsd->Instance); + auto sd_instance = get_sd_instance(hsd); + if (sd_instance && sd_instance->operation_flag) { + *sd_instance->operation_flag = true; + sd_instance->operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); - //SDMMC_CmdStopTransfer(hsd->Instance); + auto sd_instance = get_sd_instance(hsd); + if (sd_instance && sd_instance->operation_flag) { + *sd_instance->operation_flag = true; + sd_instance->operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); - //SDMMC_CmdStopTransfer(hsd->Instance); + auto sd_instance = get_sd_instance(hsd); + if (sd_instance && sd_instance->operation_flag) { + *sd_instance->operation_flag = true; + sd_instance->operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); + // auto sd_instance = get_sd_instance(hsd); + ErrorHandler("SD Card operation aborted"); } void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = reinterpret_cast(hsd->Context); + //auto sd_instance = get_sd_instance(hsd); ErrorHandler("SD Card error occurred"); } +} // extern "C" + #endif // SD_HPP \ No newline at end of file From a66a8864303eb71db8cbb06424044442043b8fa2 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 18:33:14 +0100 Subject: [PATCH 102/166] fix(Sd): Sd buffers use D1 memory --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 1e4f48617..cb5c80888 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -81,8 +81,8 @@ struct SdDomain { e.peripheral = sdmmc_peripheral; - buffer0 = MPUDomain::Buffer>(); - buffer1 = MPUDomain::Buffer>(); + buffer0 = MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1); + buffer1 = MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1); cd = card_detect_config; wp = write_protect_config; From 76f1df8b08d0f2eef8473aae7ae7e30d264fdcc4 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 18:36:01 +0100 Subject: [PATCH 103/166] Changed Promise to bool pointer, and now working properly --- Inc/HALAL/Models/MDMA/MDMA.hpp | 32 ++++++++------------- Src/HALAL/Models/MDMA/MDMA.cpp | 51 +++++++++++++--------------------- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 742b40914..4bfaafee8 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -85,33 +85,23 @@ class MDMA{ public: MDMA_HandleTypeDef handle; uint8_t id; - uint8_t* data_buffer; - uint8_t* destination_address; - Promise* promise; - bool using_promise; + bool* done; MDMA_LinkNodeTypeDef transfer_node; Instance() : handle{} , id(0U) - , data_buffer(nullptr) - , destination_address(nullptr) - , promise(nullptr) - , using_promise(false) + , done(nullptr) , transfer_node{} {} Instance(MDMA_HandleTypeDef handle_, uint8_t id_, - uint8_t* data_buffer_, - uint8_t* destination_address_, + bool* done_, MDMA_LinkNodeTypeDef transfer_node_) : handle(handle_) , id(id_) - , data_buffer(data_buffer_) - , destination_address(destination_address_) - , promise(nullptr) - , using_promise(false) + , done(done_) , transfer_node(transfer_node_) {} @@ -124,7 +114,7 @@ class MDMA{ static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; static std::bitset<8> instance_free_map; - inline static Stack,50> transfer_queue{}; + inline static Stack,50> transfer_queue{}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); @@ -146,6 +136,7 @@ class MDMA{ // Pool for MDMA_LinkNodeTypeDef, uses external non-cached memory static Pool link_node_pool; + //To be reviewed when we make mdma in compile time static void start(); static void irq_handler(); @@ -158,19 +149,18 @@ class MDMA{ * @brief A method to start a transfer from source to destination using MDMA linked list * * @param source_address The source address for the transfer. - * @param data_length The length of data to be transferred. * @param destination_address The destination address for the transfer. - * @param promise An optional promise to be fulfilled upon transfer completion. - * @return True if the transfer was successfully started, false otherwise. + * @param data_length The length of data to be transferred. + * @param check A reference boolean that will be set to true if the transfer was successfully started, false otherwise. */ - static bool transfer_data(uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address, Promise* promise=nullptr); + static void transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, bool* done=nullptr); /** * @brief A method to transfer using MDMA linked * * @param first_node The linked list node representing the first node in the linked list. - * @param promise An optional promise to be fulfilled upon transfer completion. + * @param check A reference boolean that will be set to true if the transfer was successfully queued, false otherwise. */ - static void transfer_list(MDMA::LinkedListNode* first_node, Promise* promise=nullptr); + static void transfer_list(MDMA::LinkedListNode* first_node,bool* check=nullptr); }; \ No newline at end of file diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index cc9f80690..69b6d996c 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -138,7 +138,7 @@ void MDMA::inscribe(Instance& instance,uint8_t id) } instance_free_map[id] = true; - instance = Instance(mdma_handle, id, nullptr, nullptr, transfer_node); + instance = Instance(mdma_handle, id, nullptr, transfer_node); } @@ -189,15 +189,7 @@ void MDMA::update() { Instance& instance = get_instance(i); auto transfer = transfer_queue.top(); - if(transfer.second == nullptr) - { - instance.using_promise = false; - } - else - { - instance.promise = transfer.second; - instance.using_promise = true; - } + instance.done = transfer.second; prepare_transfer(instance, transfer.first); transfer_queue.pop(); } @@ -218,46 +210,36 @@ void MDMA::irq_handler() } -void MDMA::transfer_list(MDMA::LinkedListNode* first_node, Promise* promise) +void MDMA::transfer_list(MDMA::LinkedListNode* first_node, bool* done) { if(transfer_queue.size() >= TRANSFER_QUEUE_MAX_SIZE) { ErrorHandler("MDMA transfer queue full"); return; } - transfer_queue.push({first_node, promise}); + transfer_queue.push({first_node, done}); } -bool MDMA::transfer_data(uint8_t* source_address, const uint32_t data_length,uint8_t* destination_address,Promise* promise) +void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, bool* done) { for(size_t i = 0; i < instances.size(); i++) { if(instance_free_map[i]) { Instance& instance = get_instance(i); - if(promise == nullptr) - { - instance.using_promise = false; - } - else - { - instance.promise = promise; - instance.using_promise = true; - } + instance.done = done; instance.transfer_node.CSAR = reinterpret_cast(source_address); instance.transfer_node.CBNDTR = data_length; instance.transfer_node.CDAR = reinterpret_cast(destination_address); - SCB_CleanDCache_by_Addr((uint32_t*)&instance.transfer_node, sizeof(MDMA_LinkNodeTypeDef)); //To be removed when MPU fixed - prepare_transfer(instance, &instance.transfer_node); - return true; + return; } } - return false; + return; } @@ -272,10 +254,13 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) Instance& instance = get_instance(channel_it->second); instance.handle.State = HAL_MDMA_STATE_READY; instance_free_map[instance.id] = true; - if(instance.using_promise) + if(instance.done == nullptr) + { + return; + } + else { - instance.promise->resolve(); - instance.using_promise = false; + *(instance.done) = true; } } @@ -290,9 +275,13 @@ void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) } Instance& instance = get_instance(channel_it->second); - if(instance.using_promise) + if(instance.done == nullptr) + { + return; + } + else { - instance.using_promise = false; + *(instance.done) = false; } From 7ccf5dc66d45f731c8da9b7b286d1d531a5fc3ea Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 18:49:53 +0100 Subject: [PATCH 104/166] Deleted promises and mdma packets from this branch --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 179 --------------- Inc/HALAL/Utils/Promise.hpp | 293 ------------------------ 2 files changed, 472 deletions(-) delete mode 100644 Inc/HALAL/Models/Packets/MdmaPacket.hpp delete mode 100644 Inc/HALAL/Utils/Promise.hpp diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp deleted file mode 100644 index ee93cbb40..000000000 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * MdmaPacket.hpp - * - * Created on: 06 nov. 2025 - * Author: Boris - */ - -#ifndef MDMA_PACKET_HPP -#define MDMA_PACKET_HPP - -#include "HALAL/Models/Packets/Packet.hpp" -#include "HALAL/Models/MDMA/MDMA.hpp" -#include "HALAL/Models/MPUManager/MPUManager.hpp" -#include "HALAL/Utils/Promise.hpp" - - -/** - * @brief A Packet class that uses MDMA for building and parsing packets. - * @tparam Types The types of the values in the packet. - * @note This class requires MDMA and MPUManager to be properly configured. - * @note It uses non-cached memory for MDMA operations. - */ -template -class MdmaPacket : public Packet { - public: - uint16_t id; - uint8_t* buffer; - size_t size; - std::tuple value_pointers; - - /** - * @brief Constructor for MdmaPacket - * @param id The packet ID - * @param values Pointers to the values to be included in the packet - */ - MdmaPacket(uint16_t id, Types*... values) - : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { - packets[id] = this; - buffer = reinterpret_cast(MPUManager::allocate_non_cached_memory(size)); - - if (buffer == nullptr) { - ErrorHandler("Failed to allocate MDMA buffer for packet"); - } - SCB_CleanDCache_by_Addr(reinterpret_cast(&this->id), sizeof(id)); - MDMA::LinkedListNode* prev_node = nullptr; - uint32_t offset = 0; - uint32_t idx = 0; - - std::apply([&](auto&&... args) { - (([&]() { - using PointerType = std::decay_t; - using UnderlyingType = std::remove_pointer_t; - constexpr size_t type_size = sizeof(UnderlyingType); - MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(args, buffer + offset,type_size); - build_nodes[idx++] = node; - offset += type_size; - if (prev_node != nullptr) { - prev_node->set_next(node->get_node()); - } - prev_node = node; - }()), ...); - }, value_pointers); - - prev_node = nullptr; - offset = 0; - idx = 0; - - std::apply([&](auto&&... args) { - (([&]() { - using PointerType = std::decay_t; - using UnderlyingType = std::remove_pointer_t; - constexpr size_t type_size = sizeof(UnderlyingType); - MDMA::LinkedListNode* node = MDMA::link_node_pool.construct(buffer + offset, args, type_size); - parse_nodes[idx++] = node; - offset += type_size; - if (prev_node != nullptr) { - prev_node->set_next(node->get_node()); - } - prev_node = node; - }()), ...); - }, value_pointers); - - build_transfer_node = MDMA::link_node_pool.construct(buffer, nullptr, size); - parse_transfer_node = MDMA::link_node_pool.construct(buffer, buffer, size); - } - - /** - * @brief Build the packet and transfer data into non-cached buffer using MDMA - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) - * @return Pointer to the built packet data (internal buffer or destination address) - */ - uint8_t* build(uint8_t* destination_address = nullptr) { - set_build_destination(destination_address); - Promise* promise = Promise::inscribe(); - MDMA::transfer_list(0, build_nodes[0], promise); - promise->wait(); - return destination_address ? destination_address : buffer; - } - - /** - * @brief Build the packet and transfer data into non-cached buffer using MDMA with a promise - * @param promise Promise to be fulfilled upon transfer completion - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) - * @return Pointer to the built packet data (internal buffer or destination address) - */ - uint8_t* build(Promise* promise, uint8_t* destination_address = nullptr) { - set_build_destination(destination_address); - MDMA::transfer_list(0, build_nodes[0], promise); - return destination_address ? destination_address : buffer; - } - - // Just for interface compliance - uint8_t* build() override { - uint8_t* destination_address = nullptr; - return build(destination_address); - } - - void parse(uint8_t* data = nullptr) override { - Promise* promise = Promise::inscribe(); - auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, promise); - promise->wait(); - } - - void parse(Promise* promise, uint8_t* data = nullptr) { - auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, promise); - } - - size_t get_size() override { - return size; - } - - uint16_t get_id() override { - return id; - } - - // Just for interface compliance, this is not efficient for MdmaPacket as it is (could be optimized by adding more functionality to MDMA) - // Could be optimized by using a map of index to pointer or similar structure created at compile time. - void set_pointer(size_t index, void* pointer) override { - size_t current_idx = 0; - - std::apply([&](auto&&... args) { - ((current_idx++ == index ? - (args = reinterpret_cast>(pointer)) - : nullptr), ...); - }, value_pointers); - } - - private: - MDMA::LinkedListNode* build_transfer_node; // Node used for destination address - MDMA::LinkedListNode* parse_transfer_node; // Node used for source address - MDMA::LinkedListNode* build_nodes[sizeof...(Types) + 1]; - MDMA::LinkedListNode* parse_nodes[sizeof...(Types) + 1]; - - void set_build_destination(uint8_t* external_buffer) { - if (external_buffer != nullptr) { - build_transfer_node->set_destination(external_buffer); - build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); - } else { - build_nodes[sizeof...(Types)]->set_next(nullptr); - } - SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) ); - SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)+1], sizeof(MDMA::LinkedListNode) ); - } - - MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { - if (external_buffer != nullptr) { - parse_transfer_node->set_source(external_buffer); - parse_transfer_node->set_next(parse_nodes[0]->get_node()); - return parse_transfer_node; - } else { - parse_nodes[0]->set_next(nullptr); - return parse_nodes[0]; - } - } -}; - -#endif // MDMA_PACKET_HPP \ No newline at end of file diff --git a/Inc/HALAL/Utils/Promise.hpp b/Inc/HALAL/Utils/Promise.hpp deleted file mode 100644 index aa28d64e9..000000000 --- a/Inc/HALAL/Utils/Promise.hpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Promise.hpp - * - * Created on: 15 nov. 2025 - * Author: Boris - */ - -#ifndef PROMISE_HPP -#define PROMISE_HPP - -#include -#include "C++Utilities/CppUtils.hpp" - -// Maximum number of concurrent Promises allowed in the pool. -// Default is 200, which should be sufficient for most use cases. Increase if you expect higher concurrency. -// You can override this value by defining PROMISE_MAX_CONCURRENT before including this header. -#ifndef PROMISE_MAX_CONCURRENT -#define PROMISE_MAX_CONCURRENT 200 -#endif - -// Maximum number of Promise updates processed per cycle. -// Default is 50, which balances throughput and responsiveness. Tune this for your workload. -// You can override this value by defining PROMISE_MAX_UPDATES_PER_CYCLE before including this header. -#ifndef PROMISE_MAX_UPDATES_PER_CYCLE -#define PROMISE_MAX_UPDATES_PER_CYCLE 50 -#endif - -/** - * @brief A simple Promise implementation for asynchronous programming. - * @note Promises are allocated from a fixed-size memory pool, so you don't own the memory. Use Promise::release() to release them back to the pool if needed. - */ -class Promise { - using Callback = void(*)(void*); - using ChainedCallback = Promise*(*)(void*); - - enum class State : uint8_t { - Pending, - Resolved, - Ready, - Completed, - ToBeReleased - }; - - public: - - /** - * @brief Create a new Promise. - * @return Pointer to the newly created Promise, or nullptr if allocation failed (unlikely). - * @note The returned Promise must be released using Promise::release(). - * @note The Promise lives in a memory pool with a fixed maximum number of Promises (S), so you don't own the memory. - */ - static Promise* inscribe() { - Promise* p = Promise::pool.acquire(); - if (!p) { - return nullptr; - } - p->state.store(State::Pending, std::memory_order_release); - p->callback = nullptr; - p->context = nullptr; - p->chainedCallback = nullptr; - p->chainedContext = nullptr; - p->counter.store(1, std::memory_order_release); - return p; - } - - /** - * @brief Release a Promise back to the pool. Shouldn't be called manually unless you are sure the Promise is no longer needed. - * @param p Pointer to the Promise to release. - * @return True if the Promise was successfully released, false otherwise (shouldn't happen with proper management). - * @note After calling this function, the Promise pointer is no longer valid and must not be used. Using it after release results in undefined behavior. - */ - static bool release(Promise* p) { - return Promise::pool.release(p); - } - - /** - * @brief Register a callback to be called when the Promise is resolved. - * @param cb The callback function. - * @param ctx The context to be passed to the callback, can only be a pointer, you must manage the memory yourself. You could use a pool for that, or just pass a this pointer. - * @note You can call then whenever you want, but only one callback can be registered per Promise. - */ - void then(Callback cb, void* ctx = nullptr) { - callback = cb; - context = ctx; - - State expected = State::Resolved; - state.compare_exchange_strong(expected, State::Ready, std::memory_order_acq_rel); // If already resolved, mark as ready - } - - /** - * @brief Register a Promise-returning chained callback to be called when the Promise is resolved. You can chain multiple Promises together using this method. Be extremely careful with memory management when using chained Promises. - * @param cb The chained callback function that returns a new Promise. - * @param ctx The context to be passed to the chained callback, can only be a pointer, you must manage the memory yourself. - * @return Pointer to the newly created chained Promise. - * @note If the Promise is already resolved, the chained callback is scheduled to be called in the next update cycle. You can call then whenever you want, but only one chained callback can be registered per Promise. - * @note You should not store the returned Promise pointer for long-term use, as it is managed by the Promise system. Call then on the returned Promise to register further callbacks. - * @example - * ```cpp - * Promise* p1 = Promise::inscribe(); - * p1->then([](void* ctx) { - * std::cout << "Promise 1 resolved!" << std::endl; - * auto p2 = Promise::inscribe(); // Return a new Promise - * return p2; - * // Simulate some async work - * })->then([](void* ctx) { - * std::cout << "Chained Promise resolved!" << std::endl; - * }); - * p1->resolve(); // This will trigger the first callback, and when the second Promise resolves, the chained callback will be called. - * ``` - */ - Promise* then(ChainedCallback cb, void* ctx = nullptr) { - next = Promise::inscribe(); - if (!next) { - return nullptr; - } - chainedCallback = cb; - chainedContext = ctx; - context = this; - callback = [](void* thisPtr) { - Promise* p = static_cast(thisPtr); - Promise* chained = p->chainedCallback(p->chainedContext); - if (chained) { - chained->then(p->next->callback, p->next->context); - } - p->next->state.store(State::ToBeReleased, std::memory_order_release); - p->next->counter.store(0, std::memory_order_release); - }; - - State expected = State::Resolved; - state.compare_exchange_strong(expected, State::Ready, std::memory_order_acq_rel); - - return next; - } - - /** - * @brief Resolve the Promise, triggering the registered callback. Works in interrupts. - * @note Calling this after the Promise has been handled can be dangerous, as the Promise may have already been released back to the pool. Just remove the reference to the Promise after resolving it. - * @note If the Promise is already resolved and the callback has not been called yet, calling this function has no effect. - */ - void resolve() { - State expected = State::Pending; - if (!state.compare_exchange_strong(expected, State::Resolved, std::memory_order_acq_rel)) { - return; - } - - if (callback) { - state.store(State::Ready, std::memory_order_release); - } - } - - /** - * @brief Update the Promise system, processing all resolved Promises and calling their callbacks. - * @note This function should be called regularly in the main loop of your application. - */ - static void update() { - uint16_t count = 0; - Promise* toRelease[PROMISE_MAX_UPDATES_PER_CYCLE]; - uint16_t releaseCount = 0; - - for (Promise& p : pool) { - if (count >= PROMISE_MAX_UPDATES_PER_CYCLE) { - break; - } - - State expected = State::Ready; - if (p.state.compare_exchange_strong(expected, State::Completed, std::memory_order_acq_rel)) { - if (p.callback) { - p.callback(p.context); - } - p.counter.fetch_sub(1, std::memory_order_acq_rel); - p.state.store(State::ToBeReleased, std::memory_order_release); - } - if (p.state.load(std::memory_order_acquire) == State::ToBeReleased && p.counter.load(std::memory_order_acquire) == 0) { - toRelease[releaseCount++] = &p; - count++; - } - } - - // Release all completed Promises after iteration - for (uint16_t i = releaseCount; i > 0; i--) { - Promise::pool.release(toRelease[i-1]); - } - } - - /** - * @brief Create a new Promise that resolves when all the given Promises are resolved. - * @param promises The Promises to wait for. - * @return Pointer to the newly created Promise that resolves when all given Promises are resolved. - * @note The promises will fail to create the all Promise and return nullptr if any of them already have a callback registered. - */ - template - static Promise* all(Promises*... promises) { - // Check if any promise already has a callback registered - for (Promise* p : {promises...}) { - if (p->callback != nullptr || p->chainedCallback != nullptr) { - return nullptr; - } - } - - auto allPromise = Promise::inscribe(); - if (!allPromise) { - return nullptr; - } - allPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); - - for (Promise* p : {promises...}) { - p->then([](void* ctx) { - Promise* allPromise = static_cast(ctx); - int remaining = allPromise->counter.fetch_sub(1, std::memory_order_acq_rel) - 2; // -2 because fetch_sub returns the previous value, and we want to check if it is 1 after decrement (normal value for normal promises) - if (remaining == 0) { - allPromise->resolve(); - } - }, allPromise); - } - return allPromise; - } - - /** - * @brief Create a new Promise that resolves when any of the given Promises is resolved. - * @param promises The Promises to wait for. - * @return Pointer to the newly created Promise that resolves when any given Promise is resolved. - * @note The promises will fail to create the any Promise and return nullptr if any of them already have a callback registered. - */ - template - static Promise* any(Args*... promises) { - // Check if any promise already has a callback registered - for (Promise* p : {promises...}) { - if (p->callback != nullptr || p->chainedCallback != nullptr) { - return nullptr; - } - } - - auto anyPromise = Promise::inscribe(); - if (!anyPromise) { - return nullptr; - } - anyPromise->counter.store(sizeof...(promises) + 1, std::memory_order_release); - - for (Promise* p : {promises...}) { - p->then([](void* ctx) { - Promise* anyPromise = static_cast(ctx); - anyPromise->counter.fetch_sub(1, std::memory_order_acq_rel); - State expected = State::Pending; - if (anyPromise->state.compare_exchange_strong(expected, State::Resolved, std::memory_order_acq_rel)) { - if (anyPromise->callback) { - anyPromise->state.store(State::Ready, std::memory_order_release); - } - } - }, anyPromise); - } - return anyPromise; - } - - /** - * @brief Wait for the Promise to be completed. Busy-waits until the Promise is completed. - * @param func Optional function to be called repeatedly while waiting. Can be used to perform other tasks. - * @note This function blocks until the Promise is completed. Use with caution to avoid deadlocks. - * @note After the Promise is resolved, it executes it's callback (if any) and it is automatically released back to the pool/ - */ - void wait(void (*func)() = nullptr) { - while (state.load(std::memory_order_acquire) == State::Pending) { - if (func) { - func(); - } - } - - if (callback) { - callback(context); - } - - Promise::release(this); - } - - Promise() = default; - ~Promise() = default; - Promise(Promise&&) = delete; - Promise(const Promise&) = delete; - Promise& operator=(Promise&&) = delete; - Promise& operator=(const Promise&) = delete; - - private: - Callback callback = nullptr; - ChainedCallback chainedCallback = nullptr; - void* context = nullptr; - void* chainedContext = nullptr; - std::atomic state{State::Pending}; - std::atomic counter{0}; - Promise* next = nullptr; - static Pool pool; -}; -inline Pool Promise::pool; - -#endif // PROMISE_HPP \ No newline at end of file From daf47b9b13072bd684df8e5bead72901258d41ab Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 18:53:11 +0100 Subject: [PATCH 105/166] Now it compiles oops :p --- Inc/HALAL/HALAL.hpp | 2 -- Inc/HALAL/Models/MDMA/MDMA.hpp | 10 ++++++---- Src/ST-LIB.cpp | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 84d8cf2a5..244ad89a8 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -40,7 +40,6 @@ #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" -#include "HALAL/Utils/Promise.hpp" #include "HALAL/Models/MDMA/MDMA.hpp" #ifdef STLIB_ETH @@ -57,7 +56,6 @@ #include "HALAL/Services/Communication/Ethernet/TCP/Socket.hpp" #include "HALAL/Services/Communication/Ethernet/Ethernet.hpp" #include "HALAL/Services/Communication/SNTP/SNTP.hpp" -// #include "HALAL/Utils/Promise.hpp" #endif namespace HALAL { diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 4bfaafee8..e1bb09a66 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -3,7 +3,6 @@ #include "C++Utilities/CppUtils.hpp" #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" -#include "HALAL/Utils/Promise.hpp" #include "HALAL/Models/MPUManager/MPUManager.hpp" #include #include @@ -30,12 +29,14 @@ class MDMA{ */ struct LinkedListNode { template - LinkedListNode(T* source_ptr, void* dest_ptr) { + LinkedListNode(T* source_ptr, void* dest_ptr) + { init_node(source_ptr, dest_ptr, sizeof(T)); } template - LinkedListNode(T* source_ptr, void* dest_ptr, size_t size) { + LinkedListNode(T* source_ptr, void* dest_ptr, size_t size) + { init_node(source_ptr, dest_ptr, size); } @@ -51,7 +52,8 @@ class MDMA{ private: MDMA_LinkNodeTypeDef node; - void init_node(void* src, void* dst, size_t size) { + void init_node(void* src, void* dst, size_t size) + { MDMA_LinkNodeConfTypeDef nodeConfig{}; nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; diff --git a/Src/ST-LIB.cpp b/Src/ST-LIB.cpp index 2b114f011..293a925af 100644 --- a/Src/ST-LIB.cpp +++ b/Src/ST-LIB.cpp @@ -41,6 +41,5 @@ void STLIB::update() { #endif ErrorHandlerModel::ErrorHandlerUpdate(); MDMA::update(); - Promise::update(); } From 3bae59fb7fb0a487287c2e91cab628b61e2769e3 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 19:01:10 +0100 Subject: [PATCH 106/166] Some suggested changed changes donde, ty clanker --- Inc/HALAL/Models/MDMA/MDMA.hpp | 4 ++-- Src/HALAL/Models/MDMA/MDMA.cpp | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index e1bb09a66..1cd7a7eaa 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -153,7 +153,7 @@ class MDMA{ * @param source_address The source address for the transfer. * @param destination_address The destination address for the transfer. * @param data_length The length of data to be transferred. - * @param check A reference boolean that will be set to true if the transfer was successfully started, false otherwise. + * @param check A reference boolean that will be set to true if the transfer was successfully done, false otherwise. */ static void transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, bool* done=nullptr); @@ -161,7 +161,7 @@ class MDMA{ * @brief A method to transfer using MDMA linked * * @param first_node The linked list node representing the first node in the linked list. - * @param check A reference boolean that will be set to true if the transfer was successfully queued, false otherwise. + * @param check A reference boolean that will be set to true if the transfer was successfully done, false otherwise. */ static void transfer_list(MDMA::LinkedListNode* first_node,bool* check=nullptr); diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 69b6d996c..cfc18b080 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -147,6 +147,11 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); + if (buffer==nullptr) + { + ErrorHandler("Failed to allocate MDMA link node pool buffer"); + } + uint8_t id = 0; for (auto& instance : instances) { @@ -258,10 +263,7 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) { return; } - else - { - *(instance.done) = true; - } + *(instance.done) = true; } @@ -279,11 +281,7 @@ void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) { return; } - else - { - *(instance.done) = false; - } - + *(instance.done) = false; const unsigned long error_code = static_cast(hmdma->ErrorCode); ErrorHandler("MDMA Transfer Error, code: " + std::to_string(error_code)); From 80d49aa76eafc017be570dd4b8ae8fba59b53def Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cant=C3=B3=20Catal=C3=A1n?= <144663567+Cantonplas@users.noreply.github.com> Date: Sun, 14 Dec 2025 19:03:53 +0100 Subject: [PATCH 107/166] Update Src/HALAL/Models/MDMA/MDMA.cpp Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Src/HALAL/Models/MDMA/MDMA.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index cfc18b080..d0fc20722 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -92,6 +92,7 @@ void MDMA::inscribe(Instance& instance,uint8_t id) if (channel_it == instance_to_channel.end()) { ErrorHandler("MDMA channel mapping not found"); + return; } mdma_handle.Instance = channel_it->second; mdma_handle.Init.Request = MDMA_REQUEST_SW; From cd37953e9e0ac681283f1fbd2b6a403e597869cf Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 19:06:59 +0100 Subject: [PATCH 108/166] Forgot to add the false condition to the data transfer --- Src/HALAL/Models/MDMA/MDMA.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index d0fc20722..b57c2cbdc 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -245,6 +245,10 @@ void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, return; } } + if(done) + { + *done = false; + } return; } From 0913ba2da998025896c9724354455c21f6b1e236 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 19:07:47 +0100 Subject: [PATCH 109/166] Damn --- Src/HALAL/Models/MDMA/MDMA.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index b57c2cbdc..d0fc20722 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -245,10 +245,6 @@ void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, return; } } - if(done) - { - *done = false; - } return; } From 67f5322ccf9e5a2c99302dbbee112430c03eb2f8 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 19:08:22 +0100 Subject: [PATCH 110/166] =?UTF-8?q?Co=C3=B1o=20con=20los=20conflictos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/HALAL/Models/MDMA/MDMA.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index d0fc20722..b1a93ed1f 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -244,6 +244,10 @@ void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, prepare_transfer(instance, &instance.transfer_node); return; } + if(done) + { + *done = false; + } } return; } From a78d46af8bd1a060acaa47726434be3578ac514f Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 19:35:48 +0100 Subject: [PATCH 111/166] fix(Sd): Configure card speed --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index cb5c80888..4a2210885 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -218,6 +218,10 @@ struct SdDomain { if (status != HAL_OK) { return false; } + if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { + ErrorHandler("SD Card speed/bus configuration failed"); + } + card_initialized = true; return true; } From 44e9a23fddbd4aaaf040f501852b78906de78bbc Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 19:36:11 +0100 Subject: [PATCH 112/166] feat(Sd): Add debug mode --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 4a2210885..8f1430c16 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -548,6 +548,18 @@ struct SdDomain { inst.hsd.Init.ClockDiv = 0; + #ifdef SD_DEBUG_ENABLE + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; + // Get a 400 kHz clock for debugging + uint32_t pll1_freq = HAL_RCCEx_GetPLL1ClockFreq(); // SDMMC clock source is PLL1 + uint32_t sdmmc_clk = pll1_freq / 2; // SDMMC clock before divider + uint32_t target_div = sdmmc_clk / 400000; // Target divider + if (target_div < 2) target_div = 2; // Minimum divider is 2 + if (target_div > 256) target_div = 256; // Maximum divider is 256 + inst.hsd.Init.ClockDiv = target_div - 2; // ClockDiv is (divider - 2) + #endif // SD_DEBUG_ENABLE + + inst.card_initialized = false; inst.current_buffer = BufferSelect::Buffer0; From e6d9715884764ea1e1bc04d663698b02b18d85b3 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 20:18:09 +0100 Subject: [PATCH 113/166] fix(Sd): Configure cd and wp pins in init --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 8f1430c16..912df5c93 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -138,8 +138,8 @@ struct SdDomain { Peripheral peripheral; std::size_t mpu_buffer0_idx; std::size_t mpu_buffer1_idx; - std::optional cd_pin_idx; - std::optional wp_pin_idx; + std::optional> cd_pin_idx; + std::optional> wp_pin_idx; std::size_t cmd_pin_idx; std::size_t ck_pin_idx; std::size_t d0_pin_idx; @@ -527,6 +527,10 @@ struct SdDomain { for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; auto &inst = instances[i]; + + inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; + inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; + inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; if (!inst.configure_idma()) { From 448649c90fd26e640e29dacc0b1115f1390c07d9 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 20:21:48 +0100 Subject: [PATCH 114/166] fix(Sd): Ensure --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 912df5c93..3b68062f0 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -192,7 +192,7 @@ struct SdDomain { bool* operation_flag = nullptr; // External flag to indicate that an operation has finished private: - SD_HandleTypeDef hsd; + SD_HandleTypeDef* hsd; MPUDomain::Instance* mpu_buffer0_instance; MPUDomain::Instance* mpu_buffer1_instance; From fddd13cc0d1a0df4fba98c037114eb55c60b7b7a Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 20:24:22 +0100 Subject: [PATCH 115/166] fix(Sd): Mismatched pointer and handle --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 3b68062f0..2fc647e6a 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -192,7 +192,7 @@ struct SdDomain { bool* operation_flag = nullptr; // External flag to indicate that an operation has finished private: - SD_HandleTypeDef* hsd; + SD_HandleTypeDef hsd; MPUDomain::Instance* mpu_buffer0_instance; MPUDomain::Instance* mpu_buffer1_instance; @@ -352,7 +352,7 @@ struct SdDomain { // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { - auto* hsd = instance.hsd; + auto* hsd = &instance.hsd; SDMMC_DataInitTypeDef config; uint32_t errorstate; uint32_t DmaBase0_reg; From aaedb2419a8b6a997717b61a19727e5ba48005ce Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 20:27:53 +0100 Subject: [PATCH 116/166] fix(Sd): General small fixes --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 2fc647e6a..fc712f4b9 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -233,7 +233,7 @@ struct SdDomain { if (status != HAL_OK) { return false; } card_initialized = false; - return true; // Placeholder + return true; } void switch_buffer() { @@ -292,8 +292,6 @@ struct SdDomain { instance.operation_flag = &operation_complete_flag; operation_complete_flag = false; - auto& buffer = get_current_buffer(); - // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); @@ -316,8 +314,6 @@ struct SdDomain { instance.operation_flag = &operation_complete_flag; operation_complete_flag = false; - auto& buffer = get_current_buffer(); - // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); From 628f99881723c45520b152aec7cffec3f4899aa5 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 20:38:26 +0100 Subject: [PATCH 117/166] fix(Sd): General bugfixing --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index fc712f4b9..61ad1a44f 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -69,8 +69,8 @@ struct SdDomain { * @brief Construct a new SdCard * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) - * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or null for none - * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or null for none + * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none + * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8) * @param d1_pin_for_sdmmc1 D1 pin to use if using SDMMC1 (default PC9) * @note The other pins (CMD, CK, D2, D3) are fixed for each peripheral. @@ -186,7 +186,7 @@ struct SdDomain { // State holder, logic is in SdCardWrapper struct Instance { - friend struct SdCardWrapper; + template friend struct SdCardWrapper; friend struct Init; bool* operation_flag = nullptr; // External flag to indicate that an operation has finished @@ -251,7 +251,7 @@ struct SdDomain { } }; - template + template struct SdCardWrapper{ static constexpr bool has_cd = decltype(card_request)::cd.has_value(); static constexpr bool has_wp = decltype(card_request)::wp.has_value(); @@ -431,7 +431,7 @@ struct SdDomain { // Variation of HAL_SDEx_WriteBlocksDMAMultiBuffer to fit our needs HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { - auto* hsd = instance.hsd; + auto* hsd = &instance.hsd; SDMMC_DataInitTypeDef config; uint32_t errorstate; uint32_t DmaBase0_reg; @@ -475,11 +475,8 @@ struct SdDomain { config.DPSM = SDMMC_DPSM_DISABLE; (void)SDMMC_ConfigData(hsd->Instance, &config); - //hsd->Instance->DCTRL |= SDMMC_DCTRL_FIFORST; // I am manually flushing the FIFO here, hal did not do it - __SDMMC_CMDTRANS_ENABLE(hsd->Instance); - //hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; if (instance.current_buffer == BufferSelect::Buffer1) { hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; } else { @@ -529,9 +526,6 @@ struct SdDomain { inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; - if (!inst.configure_idma()) { - ErrorHandler("SD Card IDMA configuration failed"); - } if (cfg.cd_pin_idx.has_value()) { inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; @@ -547,6 +541,10 @@ struct SdDomain { inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; inst.hsd.Init.ClockDiv = 0; + if (!inst.configure_idma()) { + ErrorHandler("SD Card IDMA configuration failed"); + } + #ifdef SD_DEBUG_ENABLE inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; @@ -568,7 +566,7 @@ struct SdDomain { RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { - ErrorHandler("SDMMC1 clock configuration failed"); + ErrorHandler("SDMMC clock configuration failed"); } if (cfg.peripheral == Peripheral::sdmmc1) { From 4879967216eca1f26f243a8e77ada9af7fb30d2e Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Sun, 14 Dec 2025 21:34:04 +0100 Subject: [PATCH 118/166] fix(Sd): Minor fixes --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 61ad1a44f..817d63667 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -289,9 +289,6 @@ struct SdDomain { return false; // Busy } - instance.operation_flag = &operation_complete_flag; - operation_complete_flag = false; - // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); @@ -299,6 +296,9 @@ struct SdDomain { ErrorHandler("SD Card read operation failed"); } + instance.operation_flag = &operation_complete_flag; + operation_complete_flag = false; + return true; } @@ -311,9 +311,6 @@ struct SdDomain { return false; // Busy } - instance.operation_flag = &operation_complete_flag; - operation_complete_flag = false; - // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); @@ -321,6 +318,9 @@ struct SdDomain { ErrorHandler("SD Card write operation failed"); } + instance.operation_flag = &operation_complete_flag; + operation_complete_flag = false; + return true; } @@ -516,14 +516,19 @@ struct SdDomain { static void init(std::span cfgs, std::span mpu_buffer_instances, std::span digital_input_instances) { + + // Initialize HAL SD + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; + RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { + ErrorHandler("SDMMC clock configuration failed"); + } for (std::size_t i = 0; i < N; i++) { const auto &cfg = cfgs[i]; auto &inst = instances[i]; - inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; - inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; - inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; @@ -561,14 +566,6 @@ struct SdDomain { inst.card_initialized = false; inst.current_buffer = BufferSelect::Buffer0; - // Initialize HAL SD - RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; - RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; - RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; - if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { - ErrorHandler("SDMMC clock configuration failed"); - } - if (cfg.peripheral == Peripheral::sdmmc1) { g_sdmmc1_handle = &inst.hsd; g_sdmmc1_instance_ptr = &inst; From 904b8151869ee2bdafb1bce750d23e6c99013e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cant=C3=B3=20Catal=C3=A1n?= <144663567+Cantonplas@users.noreply.github.com> Date: Mon, 15 Dec 2025 12:18:01 +0100 Subject: [PATCH 119/166] Fix condition to check if instance.done is not null, now it generates an errorhandler --- Src/HALAL/Models/MDMA/MDMA.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index b1a93ed1f..ce056400b 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -282,11 +282,10 @@ void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) } Instance& instance = get_instance(channel_it->second); - if(instance.done == nullptr) + if(instance.done != nullptr) { - return; + *(instance.done) = false; } - *(instance.done) = false; const unsigned long error_code = static_cast(hmdma->ErrorCode); ErrorHandler("MDMA Transfer Error, code: " + std::to_string(error_code)); @@ -296,4 +295,4 @@ void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) extern "C" void MDMA_IRQHandler(void) { MDMA::irq_handler(); -} \ No newline at end of file +} From d07302da20cdb1b1f6d8c4638157520fccd395a2 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 15 Dec 2025 13:43:14 +0100 Subject: [PATCH 120/166] feat(MdmaPacket): Implement MdmaPacket --- Inc/HALAL/HALAL.hpp | 1 + Inc/HALAL/Models/Packets/MdmaPacket.hpp | 249 ++++++++++++++++++++++++ Inc/ST-LIB.hpp | 11 +- 3 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 Inc/HALAL/Models/Packets/MdmaPacket.hpp diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 1cbd8a1dc..78ccf6c8e 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -45,6 +45,7 @@ #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" #include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/Packets/MdmaPacket.hpp" #ifdef STLIB_ETH #include "HALAL/Models/Packets/Packet.hpp" diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp new file mode 100644 index 000000000..60ecd76e1 --- /dev/null +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -0,0 +1,249 @@ +/* +* MdmaPacket.hpp +* +* Created on: 06 nov. 2025 +* Author: Boris +*/ + +#ifndef MDMA_PACKET_HPP +#define MDMA_PACKET_HPP + +#include "HALAL/Models/Packets/Packet.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" +#include "C++Utilities/CppImports.hpp" + +#ifndef MDMA_PACKET_MAX_INSTANCES +#define MDMA_PACKET_MAX_INSTANCES 50 +#endif + +struct MdmaPacketDomain { + struct Entry { + size_t packet_mpu_index; + size_t nodes_mpu_index; + }; + + struct Config { + size_t packet_mpu_index; + size_t nodes_mpu_index; + }; + + struct Instance { + uint8_t* packet_buffer; + MDMA::LinkedListNode* nodes; + }; + + static constexpr size_t max_instances = MDMA_PACKET_MAX_INSTANCES; + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + for (std::size_t i = 0; i < N; i++) { + cfgs[i].packet_mpu_index = entries[i].packet_mpu_index; + cfgs[i].nodes_mpu_index = entries[i].nodes_mpu_index; + } + return cfgs; + } + + template + struct Init { + static inline std::array instances{}; + + template + static void init(const std::array& cfgs, std::array& mpu_instances) { + for (std::size_t i = 0; i < N; i++) { + instances[i].packet_buffer = static_cast(mpu_instances[cfgs[i].packet_mpu_index].ptr); + instances[i].nodes = static_cast(mpu_instances[cfgs[i].nodes_mpu_index].ptr); + } + } + }; + + /** + * @brief A Packet class that uses MDMA for building and parsing packets. + * @tparam Types The types of the values in the packet. + * @note It uses non-cached memory for MDMA operations. + */ + template + class MdmaPacket : public Packet { + public: + uint16_t id; + uint8_t* buffer; + size_t size; + std::tuple value_pointers; + + MdmaPacket(const MdmaPacket&) = delete; + MdmaPacket& operator=(const MdmaPacket&) = delete; + + struct Request { + using domain = MdmaPacketDomain; + + static constexpr size_t packet_size_bytes = (sizeof(Types) + ...) + sizeof(uint16_t); + static constexpr size_t nodes_size_bytes = (2 * (sizeof...(Types) + 1) + 2) * sizeof(MDMA::LinkedListNode); + + using PacketMem = std::array; + using NodesMem = std::array; + + MPUDomain::Buffer packet_req; + MPUDomain::Buffer nodes_req; + + consteval Request() : + packet_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1), + nodes_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1) + {} + + template + consteval void inscribe(Ctx &ctx) const { + size_t p_idx = ctx.template add(packet_req.e); + size_t n_idx = ctx.template add(nodes_req.e); + ctx.template add({p_idx, n_idx}); + } + }; + + MdmaPacket(Instance& instance, uint16_t id, Types*... values) + : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { + + packets[id] = this; + this->buffer = instance.packet_buffer; + MDMA::LinkedListNode* nodes = instance.nodes; + + MDMA::LinkedListNode* prev_node = nullptr; + uint32_t offset = 0; + uint32_t idx = 0; + size_t node_idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(args, buffer + offset,type_size); // Placement new + build_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + prev_node = nullptr; + offset = 0; + idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer + offset, args, type_size); // Placement new + parse_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + build_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, nullptr, size); + parse_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, buffer, size); + } + + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @return Pointer to the built packet data (internal buffer or destination address) + */ + uint8_t* build(uint8_t* destination_address = nullptr) { + set_build_destination(destination_address); + bool done = false; + MDMA::transfer_list(0, build_nodes[0], &done); + while (!done) { + // Busy wait + } + return destination_address ? destination_address : buffer; + } + + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA with a promise + * @param promise Promise to be fulfilled upon transfer completion + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @return Pointer to the built packet data (internal buffer or destination address) + */ + uint8_t* build(bool* done, uint8_t* destination_address = nullptr) { + set_build_destination(destination_address); + MDMA::transfer_list(0, build_nodes[0], done); + return destination_address ? destination_address : buffer; + } + + // Just for interface compliance + uint8_t* build() override { + uint8_t* destination_address = nullptr; + return build(destination_address); + } + + void parse(uint8_t* data = nullptr) override { + bool done = false; + auto source_node = set_parse_source(data); + MDMA::transfer_list(0, source_node, &done); + while (!done) { + // Busy wait + } + } + + void parse(bool* done, uint8_t* data = nullptr) { + auto source_node = set_parse_source(data); + MDMA::transfer_list(0, source_node, done); + } + + size_t get_size() override { + return size; + } + + uint16_t get_id() override { + return id; + } + + // Just for interface compliance, this is not efficient for MdmaPacket as it is. + // Could be optimized by using a map of index to pointer or similar structure created at compile time, but doesn't seem worthy now. + void set_pointer(size_t index, void* pointer) override { + size_t current_idx = 0; + + std::apply([&](auto&&... args) { + ((current_idx++ == index ? + (args = reinterpret_cast>(pointer)) + : nullptr), ...); + }, value_pointers); + } + + private: + MDMA::LinkedListNode* build_transfer_node; // Node used for destination address + MDMA::LinkedListNode* parse_transfer_node; // Node used for source address + MDMA::LinkedListNode* build_nodes[sizeof...(Types) + 1]; + MDMA::LinkedListNode* parse_nodes[sizeof...(Types) + 1]; + + void set_build_destination(uint8_t* external_buffer) { + if (external_buffer != nullptr) { + build_transfer_node->set_destination(external_buffer); + build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); + } else { + build_nodes[sizeof...(Types)]->set_next(nullptr); + } + SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); + } + + MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { + if (external_buffer != nullptr) { + parse_transfer_node->set_source(external_buffer); + parse_transfer_node->set_next(parse_nodes[0]->get_node()); + return parse_transfer_node; + } else { + parse_nodes[0]->set_next(nullptr); + return parse_nodes[0]; + } + } + }; +}; + +#endif // MDMA_PACKET_HPP diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index ebb70246c..9bbaa95e4 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -71,7 +71,7 @@ template struct BuildCtx { }; using DomainsCtx = BuildCtx; + DigitalInputDomain, MPUDomain, MdmaPacketDomain /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -91,6 +91,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); // ... struct ConfigBundle { @@ -98,6 +99,7 @@ template struct Board { std::array dout_cfgs; std::array din_cfgs; std::array mpu_cfgs; + std::array mdma_packet_cfgs; // ... }; @@ -109,7 +111,9 @@ template struct Board { .din_cfgs = DigitalInputDomain::template build( ctx.template span()), .mpu_cfgs = MPUDomain::template build( - ctx.template span()) + ctx.template span()), + .mdma_packet_cfgs = MdmaPacketDomain::template build( + ctx.template span()) // ... }; } @@ -121,6 +125,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); @@ -129,6 +134,8 @@ template struct Board { DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); MPUDomain::Init::init(); + MdmaPacketDomain::Init::init(cfg.mdma_packet_cfgs, + MPUDomain::Init::instances); // ... } From 6760c3b8f8b48c857f4a811e50e4f76129d30a0b Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 15 Dec 2025 17:12:06 +0100 Subject: [PATCH 121/166] fix(MdmaPacket): Small fixes --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 60ecd76e1..d3b964ea8 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -158,7 +158,7 @@ struct MdmaPacketDomain { uint8_t* build(uint8_t* destination_address = nullptr) { set_build_destination(destination_address); bool done = false; - MDMA::transfer_list(0, build_nodes[0], &done); + MDMA::transfer_list(build_nodes[0], &done); while (!done) { // Busy wait } @@ -173,7 +173,7 @@ struct MdmaPacketDomain { */ uint8_t* build(bool* done, uint8_t* destination_address = nullptr) { set_build_destination(destination_address); - MDMA::transfer_list(0, build_nodes[0], done); + MDMA::transfer_list(build_nodes[0], done); return destination_address ? destination_address : buffer; } @@ -186,7 +186,7 @@ struct MdmaPacketDomain { void parse(uint8_t* data = nullptr) override { bool done = false; auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, &done); + MDMA::transfer_list(source_node, &done); while (!done) { // Busy wait } @@ -194,7 +194,7 @@ struct MdmaPacketDomain { void parse(bool* done, uint8_t* data = nullptr) { auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, done); + MDMA::transfer_list(source_node, done); } size_t get_size() override { @@ -226,11 +226,11 @@ struct MdmaPacketDomain { void set_build_destination(uint8_t* external_buffer) { if (external_buffer != nullptr) { build_transfer_node->set_destination(external_buffer); + // build_transfer_node->node.CTBR = 0; Hardcoded works, should fix build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } - SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { From 66435613b8c5821dc460903913b0b95280c6326b Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 15 Dec 2025 17:35:15 +0100 Subject: [PATCH 122/166] MDMA packet bug fixed, now if you change the destination the bus will be selected correctly --- Inc/HALAL/Models/MDMA/MDMA.hpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 1cd7a7eaa..58787f1fb 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -41,8 +41,30 @@ class MDMA{ } void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } - void set_destination(void* destination) { node.CDAR = reinterpret_cast(destination); } - void set_source(void* source) { node.CSAR = reinterpret_cast(source); } + void set_destination(void* destination) + { + uint32_t destination_adress = reinterpret_cast(destination); + node.CDAR = destination_adress; + node.CTBR &= ~MDMA_CTBR_DBUS; + if (destination_adress < 0x00010000) + { + node.CTBR |= MDMA_CTBR_DBUS; + } + else if (destination_adress >= 0x20000000 && destination_adress < 0x20020000) + { + node.CTBR |= MDMA_CTBR_DBUS; + } + } + void set_source(void* source) { + uint32_t source_adress = reinterpret_cast(source); + node.CSAR = source_adress; + + node.CTBR &= ~MDMA_CTBR_SBUS; + + if ((source_adress < 0x00010000) || (source_adress >= 0x20000000 && source_adress < 0x20020000)) { + node.CTBR |= MDMA_CTBR_SBUS; + } + } auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } auto get_size() -> uint32_t { return node.CBNDTR; } auto get_destination() -> uint32_t { return node.CDAR; } From 4b44df669da735f074a425db2b83cc53ff840c83 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 15 Dec 2025 22:25:32 +0100 Subject: [PATCH 123/166] fix(MdmaPacket): Small fixes --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 95ab48671..78e13b711 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -152,15 +152,15 @@ struct MdmaPacketDomain { /** * @brief Build the packet and transfer data into non-cached buffer using MDMA - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. * @return Pointer to the built packet data (internal buffer or destination address) */ - uint8_t* build(uint8_t* destination_address = nullptr) { + uint8_t* build(uint8_t* destination_address) { set_build_destination(destination_address); bool done = false; MDMA::transfer_list(build_nodes[0], &done); while (!done) { - // Busy wait + MDMA::update(); } return destination_address ? destination_address : buffer; } @@ -183,15 +183,25 @@ struct MdmaPacketDomain { return build(destination_address); } - void parse(uint8_t* data = nullptr) override { + /** + * @brief Parse the packet data from non-cached buffer using MDMA + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. + * @param done Optional pointer to a boolean that will be set to true when parsing is done. + */ + void parse(uint8_t* data) override { bool done = false; auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, &done); while (!done) { - // Busy wait + MDMA::update(); } } + /** + * @brief Parse the packet data from non-cached buffer using MDMA with a promise + * @param done Pointer to a boolean that will be set to true when parsing is done. + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). + */ void parse(bool* done, uint8_t* data = nullptr) { auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, done); @@ -226,7 +236,6 @@ struct MdmaPacketDomain { void set_build_destination(uint8_t* external_buffer) { if (external_buffer != nullptr) { build_transfer_node->set_destination(external_buffer); - // build_transfer_node->node.CTBR = 0; Hardcoded works, should fix build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); } else { build_nodes[sizeof...(Types)]->set_next(nullptr); From 9bb3aa86f2f26ce53349c4acb2dd827fdba2dcc7 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 15 Dec 2025 22:25:55 +0100 Subject: [PATCH 124/166] fix(MDMA): Small fixes to MDMA --- Inc/HALAL/Models/MDMA/MDMA.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 58787f1fb..a4f55a544 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -16,7 +16,7 @@ #endif #ifndef NODES_MAX -#define NODES_MAX 100 +#define NODES_MAX 5 #endif #ifndef TRANSFER_QUEUE_MAX_SIZE @@ -72,15 +72,15 @@ class MDMA{ auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } private: - MDMA_LinkNodeTypeDef node; + alignas(8) MDMA_LinkNodeTypeDef node; void init_node(void* src, void* dst, size_t size) { MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_RIGHT; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.BufferTransferLength = 128; nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; nodeConfig.Init.SourceBlockAddressOffset = 0; nodeConfig.Init.DestBlockAddressOffset = 0; From 291fca4d78481950a2626d1b61cf4a907fa46b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cant=C3=B3=20Catal=C3=A1n?= <144663567+Cantonplas@users.noreply.github.com> Date: Mon, 15 Dec 2025 23:14:42 +0100 Subject: [PATCH 125/166] Change MDMA node alignment and transfer settings --- Inc/HALAL/Models/MDMA/MDMA.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 58787f1fb..7a1a4679f 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -72,15 +72,15 @@ class MDMA{ auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } private: - MDMA_LinkNodeTypeDef node; + alignas(8) MDMA_LinkNodeTypeDef node; void init_node(void* src, void* dst, size_t size) { MDMA_LinkNodeConfTypeDef nodeConfig{}; - nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; + nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_RIGHT; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; nodeConfig.Init.DestBurst = MDMA_DEST_BURST_SINGLE; - nodeConfig.Init.BufferTransferLength = 1; + nodeConfig.Init.BufferTransferLength = 128; nodeConfig.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; nodeConfig.Init.SourceBlockAddressOffset = 0; nodeConfig.Init.DestBlockAddressOffset = 0; @@ -187,4 +187,4 @@ class MDMA{ */ static void transfer_list(MDMA::LinkedListNode* first_node,bool* check=nullptr); -}; \ No newline at end of file +}; From 72f4ff08d8b3e50eed078707baf79d108761133f Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 09:59:50 +0100 Subject: [PATCH 126/166] fix(Sd): Change throw to compile_error for better compile-time checks --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 817d63667..4aa49f33e 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -102,7 +102,7 @@ struct SdDomain { d2 = GPIODomain::GPIO(PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); d3 = GPIODomain::GPIO(PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); } else { - throw "Invalid SDMMC peripheral"; + ST_LIB::compile_error("Invalid SDMMC peripheral"); } } @@ -152,6 +152,9 @@ struct SdDomain { template static consteval std::array build(std::span entries) { std::array cfgs{}; + if (N == 0 ) { + return cfgs; + } bool peripheral_used[2] = {false, false}; // SDMMC1, SDMMC2 for (std::size_t i = 0; i < N; i++) { @@ -159,7 +162,7 @@ struct SdDomain { // Verify uniqueness of peripheral usage std::size_t peripheral_index = (e.peripheral == Peripheral::sdmmc1) ? 0 : 1; - if (peripheral_used[peripheral_index]) throw "SDMMC peripheral already used"; + if (peripheral_used[peripheral_index]) ST_LIB::compile_error("SDMMC peripheral used multiple times in SdDomain"); peripheral_used[peripheral_index] = true; // Fill configuration From a80582eb2535250d1b5028569cc9b0173e485ea8 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 10:12:28 +0100 Subject: [PATCH 127/166] fix(Sd): Private accesibility things --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 73 ++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 4aa49f33e..68c02a184 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -187,13 +187,41 @@ struct SdDomain { Buffer1 = true }; + template struct SdCardWrapper; + template struct Init; + // State holder, logic is in SdCardWrapper struct Instance { template friend struct SdCardWrapper; - friend struct Init; + template friend struct Init; bool* operation_flag = nullptr; // External flag to indicate that an operation has finished + // Public handlers called from C HAL callbacks (keeps private members encapsulated) + void on_dma_read_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); + } + + void on_dma_write_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); + } + + void on_abort() { + ErrorHandler("SD Card operation aborted"); + } + + void on_error() { + ErrorHandler("SD Card error occurred"); + } + private: SD_HandleTypeDef hsd; @@ -256,6 +284,7 @@ struct SdDomain { template struct SdCardWrapper{ + template friend struct Init; static constexpr bool has_cd = decltype(card_request)::cd.has_value(); static constexpr bool has_wp = decltype(card_request)::wp.has_value(); @@ -608,47 +637,41 @@ extern "C" void SDMMC2_IRQHandler(void) { extern "C" { void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = get_sd_instance(hsd); - if (sd_instance && sd_instance->operation_flag) { - *sd_instance->operation_flag = true; - sd_instance->operation_flag = nullptr; + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); } - SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = get_sd_instance(hsd); - if (sd_instance && sd_instance->operation_flag) { - *sd_instance->operation_flag = true; - sd_instance->operation_flag = nullptr; + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); } - SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = get_sd_instance(hsd); - if (sd_instance && sd_instance->operation_flag) { - *sd_instance->operation_flag = true; - sd_instance->operation_flag = nullptr; + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); } - SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - auto sd_instance = get_sd_instance(hsd); - if (sd_instance && sd_instance->operation_flag) { - *sd_instance->operation_flag = true; - sd_instance->operation_flag = nullptr; + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); } - SDMMC_CmdStopTransfer(hsd->Instance); } void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { - // auto sd_instance = get_sd_instance(hsd); - ErrorHandler("SD Card operation aborted"); + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_abort(); + } else { + ErrorHandler("SD Card operation aborted"); + } } void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { - //auto sd_instance = get_sd_instance(hsd); - ErrorHandler("SD Card error occurred"); + if (auto sd_instance = get_sd_instance(hsd)) { + sd_instance->on_error(); + } else { + ErrorHandler("SD Card error occurred"); + } } } // extern "C" From ea5344d957176e1d63f75364f6aa45c238855ab0 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 10:13:54 +0100 Subject: [PATCH 128/166] fix(Sd): Fix inline issue and add dependencies to CMakeLists.txt --- CMakeLists.txt | 1 + Inc/ST-LIB_LOW/Sd/Sd.hpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e015bfde2..af432285b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ if(CMAKE_CROSSCOMPILING) # SD ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd.c ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd_ex.c + ${HAL_DRIVER_SRC_DIR}/stm32h7xx_ll_sdmmc.c # CAN / FDCAN ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_fdcan.c diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 68c02a184..10b63d952 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -622,13 +622,13 @@ static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { return nullptr; } -extern "C" void SDMMC1_IRQHandler(void) { +extern "C" inline void SDMMC1_IRQHandler(void) { if (g_sdmmc1_handle != nullptr) { HAL_SD_IRQHandler(g_sdmmc1_handle); } } -extern "C" void SDMMC2_IRQHandler(void) { +extern "C" inline void SDMMC2_IRQHandler(void) { if (g_sdmmc2_handle != nullptr) { HAL_SD_IRQHandler(g_sdmmc2_handle); } @@ -636,29 +636,29 @@ extern "C" void SDMMC2_IRQHandler(void) { extern "C" { -void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_dma_read_complete(); } } -void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_dma_read_complete(); } } -void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_dma_write_complete(); } } -void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_dma_write_complete(); } } -void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_abort(); } else { @@ -666,7 +666,7 @@ void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { } } -void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { +inline void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = get_sd_instance(hsd)) { sd_instance->on_error(); } else { From 19ae0ff7a3f8269d5db620eda4192804fb742e84 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 10:30:58 +0100 Subject: [PATCH 129/166] fix(Sd): Fix GPIO configuration for SD card --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 10b63d952..667996291 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -77,31 +77,32 @@ struct SdDomain { */ consteval SdCard(Peripheral sdmmc_peripheral, std::optional> card_detect_config, std::optional> write_protect_config, - GPIODomain::Pin d0_pin_for_sdmmc1 = PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = PC9) { - - e.peripheral = sdmmc_peripheral; - - buffer0 = MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1); - buffer1 = MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1); - - cd = card_detect_config; - wp = write_protect_config; - - if (sdmmc_peripheral == Peripheral::sdmmc1) { - cmd = GPIODomain::GPIO(PC6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - ck = GPIODomain::GPIO(PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d0 = GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d1 = GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d2 = GPIODomain::GPIO(PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d3 = GPIODomain::GPIO(PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - } else if (sdmmc_peripheral == Peripheral::sdmmc2) { - cmd = GPIODomain::GPIO(PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - ck = GPIODomain::GPIO(PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d0 = GPIODomain::GPIO(PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d1 = GPIODomain::GPIO(PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d2 = GPIODomain::GPIO(PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - d3 = GPIODomain::GPIO(PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12); - } else { + GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = ST_LIB::PC9) : + e{.peripheral = sdmmc_peripheral}, + buffer0(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + buffer1(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + cd(card_detect_config), + wp(write_protect_config), + cmd((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + ck((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + d0((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + d1((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + d2((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + d3((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)) + { + if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { ST_LIB::compile_error("Invalid SDMMC peripheral"); } } From 55cb1b4d66dd732b3d49d924592ab5bfa43e7d68 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 12:27:38 +0100 Subject: [PATCH 130/166] fix(Sd): General bug fixing --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 120 +++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 43 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 667996291..26b043421 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -79,28 +79,29 @@ struct SdDomain { std::optional> card_detect_config, std::optional> write_protect_config, GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = ST_LIB::PC9) : e{.peripheral = sdmmc_peripheral}, + peripheral(sdmmc_peripheral), buffer0(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), buffer1(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), cd(card_detect_config), wp(write_protect_config), cmd((sdmmc_peripheral == Peripheral::sdmmc1) ? - GPIODomain::GPIO(ST_LIB::PC6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + GPIODomain::GPIO(ST_LIB::PD2, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), ck((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(ST_LIB::PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + GPIODomain::GPIO(ST_LIB::PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), d0((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), d1((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), d2((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(ST_LIB::PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)), + GPIODomain::GPIO(ST_LIB::PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)), d3((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(ST_LIB::PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : - GPIODomain::GPIO(ST_LIB::PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12)) + GPIODomain::GPIO(ST_LIB::PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)) { if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { ST_LIB::compile_error("Invalid SDMMC peripheral"); @@ -110,24 +111,32 @@ struct SdDomain { template consteval void inscribe(Ctx &ctx) const { - e.mpu_buffer0_idx = ctx.template add(buffer0); - e.mpu_buffer1_idx = ctx.template add(buffer1); + Entry local_e = e; + + local_e.mpu_buffer0_idx = ctx.template add(buffer0.e); + local_e.mpu_buffer1_idx = ctx.template add(buffer1.e); if (cd.has_value()) { - e.cd_pin_idx = {ctx.template add(cd.value().first), cd.value().second}; + auto& di = cd.value().first; + auto gpio_idx = ctx.template add(di.gpio.e); + DigitalInputDomain::Entry di_entry{gpio_idx}; + local_e.cd_pin_idx = {ctx.template add(di_entry), cd.value().second}; } if (wp.has_value()) { - e.wp_pin_idx = {ctx.template add(wp.value().first), wp.value().second}; + auto& di = wp.value().first; + auto gpio_idx = ctx.template add(di.gpio.e); + DigitalInputDomain::Entry di_entry{gpio_idx}; + local_e.wp_pin_idx = {ctx.template add(di_entry), wp.value().second}; } - e.cmd_pin_idx = ctx.template add(cmd); - e.ck_pin_idx = ctx.template add(ck); - e.d0_pin_idx = ctx.template add(d0); - e.d1_pin_idx = ctx.template add(d1); - e.d2_pin_idx = ctx.template add(d2); - e.d3_pin_idx = ctx.template add(d3); + local_e.cmd_pin_idx = ctx.template add(cmd.e); + local_e.ck_pin_idx = ctx.template add(ck.e); + local_e.d0_pin_idx = ctx.template add(d0.e); + local_e.d1_pin_idx = ctx.template add(d1.e); + local_e.d2_pin_idx = ctx.template add(d2.e); + local_e.d3_pin_idx = ctx.template add(d3.e); - ctx.template add(e); + ctx.template add(local_e); } }; @@ -250,6 +259,10 @@ struct SdDomain { if (status != HAL_OK) { return false; } + if (!configure_idma()) { + ErrorHandler("SD Card IDMA configuration failed"); + } + if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { ErrorHandler("SD Card speed/bus configuration failed"); } @@ -286,8 +299,8 @@ struct SdDomain { template struct SdCardWrapper{ template friend struct Init; - static constexpr bool has_cd = decltype(card_request)::cd.has_value(); - static constexpr bool has_wp = decltype(card_request)::wp.has_value(); + static constexpr bool has_cd = card_request.cd.has_value(); + static constexpr bool has_wp = card_request.wp.has_value(); SdCardWrapper(Instance& instance) : instance(instance) { check_cd_wp(); @@ -373,9 +386,9 @@ struct SdDomain { auto& get_current_buffer() { if (instance.current_buffer == BufferSelect::Buffer0) { - return instance.mpu_buffer0_instance->template as(); + return *instance.mpu_buffer0_instance->template as(); } else { - return instance.mpu_buffer1_instance->template as(); + return *instance.mpu_buffer1_instance->template as(); } } @@ -550,12 +563,8 @@ struct SdDomain { std::span mpu_buffer_instances, std::span digital_input_instances) { - // Initialize HAL SD - RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; - RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; - RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; - if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { - ErrorHandler("SDMMC clock configuration failed"); + if (N == 0) { + return; } for (std::size_t i = 0; i < N; i++) { @@ -572,17 +581,17 @@ struct SdDomain { inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; } - inst.hsd.Instance = reinterpret_cast(static_cast(cfg.peripheral)); + if (cfg.peripheral == Peripheral::sdmmc1) { + inst.hsd.Instance = SDMMC1; + } else if (cfg.peripheral == Peripheral::sdmmc2) { + inst.hsd.Instance = SDMMC2; + } inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; inst.hsd.Init.ClockDiv = 0; - if (!inst.configure_idma()) { - ErrorHandler("SD Card IDMA configuration failed"); - } - #ifdef SD_DEBUG_ENABLE inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; @@ -595,23 +604,31 @@ struct SdDomain { inst.hsd.Init.ClockDiv = target_div - 2; // ClockDiv is (divider - 2) #endif // SD_DEBUG_ENABLE - - inst.card_initialized = false; - inst.current_buffer = BufferSelect::Buffer0; - if (cfg.peripheral == Peripheral::sdmmc1) { g_sdmmc1_handle = &inst.hsd; g_sdmmc1_instance_ptr = &inst; - __HAL_RCC_SDMMC1_CLK_ENABLE(); - HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SDMMC1_IRQn); } else if (cfg.peripheral == Peripheral::sdmmc2) { g_sdmmc2_handle = &inst.hsd; g_sdmmc2_instance_ptr = &inst; - __HAL_RCC_SDMMC2_CLK_ENABLE(); - HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SDMMC2_IRQn); } + + inst.card_initialized = false; + inst.current_buffer = BufferSelect::Buffer0; + } + + // Initialize HAL SD + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; + RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { + ErrorHandler("SDMMC clock configuration failed"); + } + + // Ensure PLL1Q output is enabled (it might be disabled by default if not used by other peripherals) + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ); + + if (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) == 0) { + ErrorHandler("SDMMC clock frequency is 0"); } } }; @@ -623,6 +640,23 @@ static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { return nullptr; } +extern "C" inline void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { + if (hsd->Instance == SDMMC1) { + __HAL_RCC_SDMMC1_CLK_ENABLE(); + __HAL_RCC_SDMMC1_FORCE_RESET(); + __HAL_RCC_SDMMC1_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + + } else { + __HAL_RCC_SDMMC2_CLK_ENABLE(); + __HAL_RCC_SDMMC2_FORCE_RESET(); + __HAL_RCC_SDMMC2_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC2_IRQn); + } +} + extern "C" inline void SDMMC1_IRQHandler(void) { if (g_sdmmc1_handle != nullptr) { HAL_SD_IRQHandler(g_sdmmc1_handle); From 40fc2166a518adbd19a01a5d55c6e359aab5f1b2 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 13:13:26 +0100 Subject: [PATCH 131/166] fix(Sd): Move things to cpp and circular dependency fix --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 83 ++++------------------------------------ Src/ST-LIB_LOW/Sd/Sd.cpp | 80 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 76 deletions(-) create mode 100644 Src/ST-LIB_LOW/Sd/Sd.cpp diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 26b043421..e2f04e090 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -9,19 +9,19 @@ #define SD_HPP #include "HALAL/HALAL.hpp" -#include "ST-LIB_LOW/ST-LIB_LOW.hpp" +#include "ST-LIB_LOW/DigitalInput2.hpp" #include "stm32h7xx_hal.h" #include "ErrorHandler/ErrorHandler.hpp" using ST_LIB::DigitalInputDomain; using ST_LIB::GPIODomain; -static SD_HandleTypeDef* g_sdmmc1_handle = nullptr; -static SD_HandleTypeDef* g_sdmmc2_handle = nullptr; - -static void* g_sdmmc1_instance_ptr = nullptr; -static void* g_sdmmc2_instance_ptr = nullptr; +extern SD_HandleTypeDef* g_sdmmc1_handle; +extern SD_HandleTypeDef* g_sdmmc2_handle; +extern void* g_sdmmc1_instance_ptr; +extern void* g_sdmmc2_instance_ptr; +namespace ST_LIB { struct SdDomain { enum class Peripheral : uint32_t { @@ -640,75 +640,6 @@ static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { return nullptr; } -extern "C" inline void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { - if (hsd->Instance == SDMMC1) { - __HAL_RCC_SDMMC1_CLK_ENABLE(); - __HAL_RCC_SDMMC1_FORCE_RESET(); - __HAL_RCC_SDMMC1_RELEASE_RESET(); - HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SDMMC1_IRQn); - - } else { - __HAL_RCC_SDMMC2_CLK_ENABLE(); - __HAL_RCC_SDMMC2_FORCE_RESET(); - __HAL_RCC_SDMMC2_RELEASE_RESET(); - HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); - HAL_NVIC_EnableIRQ(SDMMC2_IRQn); - } -} - -extern "C" inline void SDMMC1_IRQHandler(void) { - if (g_sdmmc1_handle != nullptr) { - HAL_SD_IRQHandler(g_sdmmc1_handle); - } -} - -extern "C" inline void SDMMC2_IRQHandler(void) { - if (g_sdmmc2_handle != nullptr) { - HAL_SD_IRQHandler(g_sdmmc2_handle); - } -} - -extern "C" { - -inline void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_dma_read_complete(); - } -} -inline void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_dma_read_complete(); - } -} - -inline void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_dma_write_complete(); - } -} -inline void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_dma_write_complete(); - } -} - -inline void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_abort(); - } else { - ErrorHandler("SD Card operation aborted"); - } -} - -inline void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = get_sd_instance(hsd)) { - sd_instance->on_error(); - } else { - ErrorHandler("SD Card error occurred"); - } -} - -} // extern "C" +} // namespace ST_LIB #endif // SD_HPP \ No newline at end of file diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp new file mode 100644 index 000000000..88ec5c058 --- /dev/null +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -0,0 +1,80 @@ +#include "ST-LIB_LOW/Sd/Sd.hpp" + +// 1. Define the globals here (Strong definitions) +SD_HandleTypeDef* g_sdmmc1_handle = nullptr; +SD_HandleTypeDef* g_sdmmc2_handle = nullptr; + +void* g_sdmmc1_instance_ptr = nullptr; +void* g_sdmmc2_instance_ptr = nullptr; + +extern "C" { + +void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { + if (hsd->Instance == SDMMC1) { + __HAL_RCC_SDMMC1_CLK_ENABLE(); + __HAL_RCC_SDMMC1_FORCE_RESET(); + __HAL_RCC_SDMMC1_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + } else { + __HAL_RCC_SDMMC2_CLK_ENABLE(); + __HAL_RCC_SDMMC2_FORCE_RESET(); + __HAL_RCC_SDMMC2_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC2_IRQn); + } +} + +void SDMMC1_IRQHandler(void) { + if (g_sdmmc1_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc1_handle); + } +} + +void SDMMC2_IRQHandler(void) { + if (g_sdmmc2_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc2_handle); + } +} + +void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); + } +} + +void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); + } +} + +void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); + } +} + +void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); + } +} + +void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_abort(); + } else { + ErrorHandler("SD Card operation aborted"); + } +} + +void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_error(); + } else { + ErrorHandler("SD Card error occurred"); + } +} + +} // extern "C" \ No newline at end of file From c1be258a7f81712e639fb5151f59f272528eead3 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 16:50:26 +0100 Subject: [PATCH 132/166] fix(Sd): General bugfixing, now works --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 59 ++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index e2f04e090..9485b80b6 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -244,12 +244,16 @@ struct SdDomain { bool card_initialized; BufferSelect current_buffer; // The one that is currently available for CPU access and not used by IDMA + HAL_SD_CardInfoTypeDef card_info; + // Functions bool is_card_present() { return cd_instance->first->read() == cd_instance->second; } bool is_write_protected() { return wp_instance->first->read() == wp_instance->second; } bool is_busy() { - return (hsd.State != HAL_SD_STATE_TRANSFER); + if (!card_initialized) return false; + HAL_SD_StateTypeDef state = HAL_SD_GetState(&hsd); + return (state != HAL_SD_STATE_READY && state != HAL_SD_STATE_TRANSFER); } bool initialize_card() { @@ -259,6 +263,10 @@ struct SdDomain { if (status != HAL_OK) { return false; } + if (HAL_SD_GetCardInfo(&hsd, &card_info) != HAL_OK) { + ErrorHandler("Failed to get SD card info"); + } + if (!configure_idma()) { ErrorHandler("SD Card IDMA configuration failed"); } @@ -326,7 +334,7 @@ struct SdDomain { return instance.card_initialized; } - bool read_blocks(uint32_t start_block, uint32_t num_blocks, bool& operation_complete_flag) { + bool read_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { check_cd_wp(); if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); @@ -342,13 +350,13 @@ struct SdDomain { ErrorHandler("SD Card read operation failed"); } - instance.operation_flag = &operation_complete_flag; - operation_complete_flag = false; + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; return true; } - bool write_blocks(uint32_t start_block, uint32_t num_blocks, bool& operation_complete_flag) { + bool write_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { check_cd_wp(); if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); @@ -364,17 +372,27 @@ struct SdDomain { ErrorHandler("SD Card write operation failed"); } - instance.operation_flag = &operation_complete_flag; - operation_complete_flag = false; + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; return true; } + auto* get_current_buffer() { + if (instance.current_buffer == BufferSelect::Buffer0) { + return instance.mpu_buffer0_instance->template as(); + } else { + return instance.mpu_buffer1_instance->template as(); + } + } + + bool is_busy() { + return instance.is_busy(); + } private: Instance& instance; // Actual State - void check_cd_wp() { if constexpr (has_cd) { if (!instance.is_card_present()) { ErrorHandler("SD Card not present"); } @@ -384,14 +402,6 @@ struct SdDomain { } } - auto& get_current_buffer() { - if (instance.current_buffer == BufferSelect::Buffer0) { - return *instance.mpu_buffer0_instance->template as(); - } else { - return *instance.mpu_buffer1_instance->template as(); - } - } - // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { auto* hsd = &instance.hsd; @@ -445,9 +455,9 @@ struct SdDomain { __SDMMC_CMDTRANS_ENABLE(hsd->Instance); if (instance.current_buffer == BufferSelect::Buffer1) { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; } else { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; } instance.switch_buffer(); @@ -524,9 +534,9 @@ struct SdDomain { __SDMMC_CMDTRANS_ENABLE(hsd->Instance); if (instance.current_buffer == BufferSelect::Buffer1) { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; } else { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; } instance.switch_buffer(); @@ -586,17 +596,18 @@ struct SdDomain { } else if (cfg.peripheral == Peripheral::sdmmc2) { inst.hsd.Instance = SDMMC2; } - inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; + inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; - inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; - inst.hsd.Init.ClockDiv = 0; + inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE; + inst.hsd.Init.ClockDiv = 2; #ifdef SD_DEBUG_ENABLE inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; // Get a 400 kHz clock for debugging - uint32_t pll1_freq = HAL_RCCEx_GetPLL1ClockFreq(); // SDMMC clock source is PLL1 + //uint32_t pll1_freq = HAL_RCCEx_GetPLL1ClockFreq(); // SDMMC clock source is PLL1 + uint32_t pll1_freq = 480000000; // Assume PLL1 is at 480 MHz uint32_t sdmmc_clk = pll1_freq / 2; // SDMMC clock before divider uint32_t target_div = sdmmc_clk / 400000; // Target divider if (target_div < 2) target_div = 2; // Minimum divider is 2 From 17a92ba0ee2628f805e7affe135f45def183d078 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 17:41:01 +0100 Subject: [PATCH 133/166] fix(Sd): The Sd is too finicky, it needs it's freaking time --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 9485b80b6..231e822a6 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -339,11 +339,26 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } + + // 1. Wait for the CARD (hardware) to be ready, not just the handle + uint32_t timeout = HAL_GetTick() + 1000; + while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + if (HAL_GetTick() > timeout) { + // 2. OPTIONAL: If stuck, try forcing a STOP command to reset the state + SDMMC_CmdStopTransfer(instance.hsd.Instance); + HAL_Delay(10); // Give it a moment + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + ErrorHandler("Timeout waiting for SD Card to enter TRANSFER state"); + return false; + } + } + } + if (instance.is_busy()) { - return false; // Busy + return false; // Driver busy } - // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + // Now it is safe to send CMD18 HAL_StatusTypeDef status = Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); if (status != HAL_OK) { @@ -361,10 +376,24 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } + + // 1. Wait for the CARD (hardware) to be ready, not just the handle + uint32_t timeout = HAL_GetTick() + 1000; + while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + if (HAL_GetTick() > timeout) { + // 2. OPTIONAL: If stuck, try forcing a STOP command to reset the state + SDMMC_CmdStopTransfer(instance.hsd.Instance); + HAL_Delay(10); // Give it a moment + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + ErrorHandler("Timeout waiting for SD Card to enter TRANSFER state"); + return false; + } + } + } + if (instance.is_busy()) { return false; // Busy } - // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); @@ -596,10 +625,10 @@ struct SdDomain { } else if (cfg.peripheral == Peripheral::sdmmc2) { inst.hsd.Instance = SDMMC2; } - inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; - inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_ENABLE; + inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; inst.hsd.Init.ClockDiv = 2; From 7c5fe10bc4902febc5b2357c3760ab2527b9a5c9 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Tue, 16 Dec 2025 19:56:44 +0100 Subject: [PATCH 134/166] feat(SdLogger): First implementation of SdLogger --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 16 ++- Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp | 1 + Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp | 138 ++++++++++++++++++++++++ 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 60ecd76e1..22d107e5f 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -64,8 +64,14 @@ struct MdmaPacketDomain { * @tparam Types The types of the values in the packet. * @note It uses non-cached memory for MDMA operations. */ + class MdmaPacketBase : public Packet { + public: + virtual uint8_t* build(bool* done, uint8_t* destination_address = nullptr) = 0; + using Packet::build; + }; + template - class MdmaPacket : public Packet { + class MdmaPacket : public MdmaPacketBase { public: uint16_t id; uint8_t* buffer; @@ -158,7 +164,7 @@ struct MdmaPacketDomain { uint8_t* build(uint8_t* destination_address = nullptr) { set_build_destination(destination_address); bool done = false; - MDMA::transfer_list(0, build_nodes[0], &done); + MDMA::transfer_list(build_nodes[0], &done); while (!done) { // Busy wait } @@ -173,7 +179,7 @@ struct MdmaPacketDomain { */ uint8_t* build(bool* done, uint8_t* destination_address = nullptr) { set_build_destination(destination_address); - MDMA::transfer_list(0, build_nodes[0], done); + MDMA::transfer_list(build_nodes[0], done); return destination_address ? destination_address : buffer; } @@ -186,7 +192,7 @@ struct MdmaPacketDomain { void parse(uint8_t* data = nullptr) override { bool done = false; auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, &done); + MDMA::transfer_list(source_node, &done); while (!done) { // Busy wait } @@ -194,7 +200,7 @@ struct MdmaPacketDomain { void parse(bool* done, uint8_t* data = nullptr) { auto source_node = set_parse_source(data); - MDMA::transfer_list(0, source_node, done); + MDMA::transfer_list(source_node, done); } size_t get_size() override { diff --git a/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp b/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp index 392bbee9c..894f395d3 100644 --- a/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp +++ b/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp @@ -22,6 +22,7 @@ #include "Control/Blocks/MatrixMultiplier.hpp" #include "Control/Blocks/MeanCalculator.hpp" #include "Control/ControlSystem.hpp" +#include "SdLogger/SdLogger.hpp" #ifdef SIM_ON #else #include "FlashStorer/FlashStorer.hpp" diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp new file mode 100644 index 000000000..928473b40 --- /dev/null +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -0,0 +1,138 @@ +/* + * SdLogger.hpp + * + * Created on: 16 dec. 2025 + * Author: Boris + */ + +#pragma once + +#include "ST-LIB_LOW/ST-LIB_LOW.hpp" +#include "HALAL/HALAL.hpp" +#include "Sd/Sd.hpp" +#include "HALAL/Models/Packets/MdmaPacket.hpp" +#include + +template +class SdLogger { +public: + static constexpr size_t MAX_PACKETS = 32; + + SdLogger(SdWrapper& sd_card) : sd(sd_card) { + sd.init_card(); + current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); + current_buffer_size = sd.get_current_buffer()->size() * 4; + } + + void add_packet(MdmaPacketDomain::MdmaPacketBase* packet) { + if (packet_count >= MAX_PACKETS) { + ErrorHandler("Max packets reached in SdLogger"); + return; + } + packets[packet_count] = packet; + packet_count++; + } + + void log(uint16_t packet_index) { + request_mask |= (1UL << packet_index); + } + + void update() { + if (sd_write_ongoing) { + if (sd_write_complete_flag) { + sd_write_ongoing = false; + sd_write_complete_flag = false; + } + } + + if (building_mask) { + uint32_t temp_mask = building_mask; + while (temp_mask) { + uint32_t i = __builtin_ctz(temp_mask); + if (build_complete_flags[i]) { + building_mask &= ~(1UL << i); + build_complete_flags[i] = false; + } + temp_mask &= ~(1UL << i); + } + } + + if (!buffer_flush_pending) { + uint32_t temp_mask = request_mask; + while (temp_mask) { + uint32_t i = __builtin_ctz(temp_mask); + temp_mask &= ~(1UL << i); + + size_t packet_size = packets[i]->get_size(); + if (current_buffer_offset + packet_size > current_buffer_size) { + buffer_flush_pending = true; + break; + } + + if (building_mask & (1UL << i)) { + continue; + } + + request_mask &= ~(1UL << i); + building_mask |= (1UL << i); + build_complete_flags[i] = false; + + packets[i]->build(&build_complete_flags[i], current_buffer_ptr + current_buffer_offset); + current_buffer_offset += packet_size; + } + } + + if (buffer_flush_pending && building_mask == 0) { + if (flush_buffer()) { + buffer_flush_pending = false; + } + } + } + +private: + bool flush_buffer() { + if (sd_write_ongoing) { + return false; + } + + uint32_t bytes_to_write = current_buffer_offset; + if (bytes_to_write == 0) { + current_buffer_offset = 0; + return true; + } + + uint32_t blocks = (bytes_to_write + 511) / 512; + + sd_write_ongoing = true; + sd_write_complete_flag = false; + + if (!sd.write_blocks(sd_block_addr, blocks, &sd_write_complete_flag)) { + ErrorHandler("Failed to start SD write"); + sd_write_ongoing = false; + return false; + } else { + sd_block_addr += blocks; + current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); + current_buffer_offset = 0; + return true; + } + } + + SdWrapper& sd; + std::array packets; + size_t packet_count = 0; + uint32_t request_mask = 0; + + bool sd_write_ongoing = false; + bool sd_write_complete_flag = false; + + uint32_t building_mask = 0; + std::array build_complete_flags; + + uint8_t* current_buffer_ptr = nullptr; + uint32_t current_buffer_size = 0; + uint32_t current_buffer_offset = 0; + + bool buffer_flush_pending = false; + uint32_t sd_block_addr = 0; +}; From ad4bda06247719f5fc715a3360d8abccfd881503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 17 Dec 2025 00:32:57 +0100 Subject: [PATCH 135/166] fixed getting wrong instance --- Inc/HALAL/Models/GPIO.hpp | 3 ++- Inc/ST-LIB.hpp | 43 ++----------------------------- Inc/ST-LIB_LOW/DigitalInput2.hpp | 3 ++- Inc/ST-LIB_LOW/DigitalOutput2.hpp | 3 ++- 4 files changed, 8 insertions(+), 44 deletions(-) diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index c19131e37..370f46162 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -172,6 +172,7 @@ struct GPIODomain { struct GPIO { using domain = GPIODomain; + size_t index; Entry e; @@ -184,7 +185,7 @@ struct GPIODomain { } template consteval void inscribe(Ctx &ctx) const { - ctx.template add(e); + index = ctx.template add(e); } }; diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 32fb54814..c17d73f5b 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -126,51 +126,12 @@ template struct Board { // ... } - template - static consteval std::size_t domain_size_for_instance() { - return domain_size(); - } - - template - static consteval std::size_t domain_index_of_impl() { - std::size_t idx = 0; - bool found = false; - - ( - [&] { - using DevT = std::remove_cvref_t; - if constexpr (std::is_same_v) { - if (!found) { - if (&devs == &Target) { - found = true; - } else { - ++idx; - } - } - } - }(), - ...); - - if (!found) { - compile_error("Device not found for domain"); - } - - return idx; - } - - template - static consteval std::size_t domain_index_of() { - return domain_index_of_impl(); - } - template static auto &instance_of() { using DevT = std::remove_cvref_t; using Domain = typename DevT::domain; - constexpr std::size_t idx = domain_index_of(); - constexpr std::size_t N = domain_size_for_instance(); - - return Domain::template Init::instances[idx]; + constexpr std::size_t N = domain_size(); + return Domain::template Init::instances[Target.index]; } }; diff --git a/Inc/ST-LIB_LOW/DigitalInput2.hpp b/Inc/ST-LIB_LOW/DigitalInput2.hpp index e161e39a5..66d95ce0e 100644 --- a/Inc/ST-LIB_LOW/DigitalInput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalInput2.hpp @@ -12,6 +12,7 @@ struct DigitalInputDomain { struct DigitalInput { GPIODomain::GPIO gpio; using domain = DigitalInputDomain; + size_t index; consteval DigitalInput(const GPIODomain::Pin &pin, GPIODomain::Pull pull = GPIODomain::Pull::None, @@ -21,7 +22,7 @@ struct DigitalInputDomain { template consteval void inscribe(Ctx &ctx) const { const auto gpio_idx = ctx.template add(gpio.e); Entry e{.gpio_idx = gpio_idx}; - ctx.template add(e); + index = ctx.template add(e); } }; diff --git a/Inc/ST-LIB_LOW/DigitalOutput2.hpp b/Inc/ST-LIB_LOW/DigitalOutput2.hpp index 7dcad5b65..9d817be05 100644 --- a/Inc/ST-LIB_LOW/DigitalOutput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalOutput2.hpp @@ -18,6 +18,7 @@ struct DigitalOutputDomain { struct DigitalOutput { GPIODomain::GPIO gpio; using domain = DigitalOutputDomain; + size_t index; consteval DigitalOutput(const GPIODomain::Pin &pin, OutputMode mode = OutputMode::PUSH_PULL, @@ -29,7 +30,7 @@ struct DigitalOutputDomain { template consteval void inscribe(Ctx &ctx) const { const auto gpio_idx = ctx.template add(gpio.e); Entry e{.gpio_idx = gpio_idx}; - ctx.template add(e); + index = ctx.template add(e); } }; From 328cca0369c020bd0808e6f66276cf449847e757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Wed, 17 Dec 2025 00:39:39 +0100 Subject: [PATCH 136/166] marked indexs as mutable --- Inc/HALAL/Models/GPIO.hpp | 2 +- Inc/ST-LIB_LOW/DigitalInput2.hpp | 2 +- Inc/ST-LIB_LOW/DigitalOutput2.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index 370f46162..953d8b19f 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -172,7 +172,7 @@ struct GPIODomain { struct GPIO { using domain = GPIODomain; - size_t index; + mutable size_t index; Entry e; diff --git a/Inc/ST-LIB_LOW/DigitalInput2.hpp b/Inc/ST-LIB_LOW/DigitalInput2.hpp index 66d95ce0e..a900b924c 100644 --- a/Inc/ST-LIB_LOW/DigitalInput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalInput2.hpp @@ -12,7 +12,7 @@ struct DigitalInputDomain { struct DigitalInput { GPIODomain::GPIO gpio; using domain = DigitalInputDomain; - size_t index; + mutable size_t index; consteval DigitalInput(const GPIODomain::Pin &pin, GPIODomain::Pull pull = GPIODomain::Pull::None, diff --git a/Inc/ST-LIB_LOW/DigitalOutput2.hpp b/Inc/ST-LIB_LOW/DigitalOutput2.hpp index 9d817be05..8889691b4 100644 --- a/Inc/ST-LIB_LOW/DigitalOutput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalOutput2.hpp @@ -18,7 +18,7 @@ struct DigitalOutputDomain { struct DigitalOutput { GPIODomain::GPIO gpio; using domain = DigitalOutputDomain; - size_t index; + mutable size_t index; consteval DigitalOutput(const GPIODomain::Pin &pin, OutputMode mode = OutputMode::PUSH_PULL, From 804e7b9870d96e14f789f00d15daaddad88de58b Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 07:58:19 +0100 Subject: [PATCH 137/166] fix(MPU): Implemente st-lib fix --- Inc/HALAL/Models/MPU.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index fc3649f0b..f447d2d1b 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -57,6 +57,7 @@ struct MPUDomain { using domain = MPUDomain; using buffer_type = T; Entry e; + mutable size_t index; /** * @brief Constructs a Buffer entry for a type T. @@ -77,7 +78,7 @@ struct MPUDomain { template consteval void inscribe(Ctx &ctx) const { - ctx.template add(e); + index = ctx.template add(e); } }; From b7248926ccb0b149f00a6f8fcae4d2750cd7b25d Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 08:08:03 +0100 Subject: [PATCH 138/166] fix(MPU): Fix merge --- Inc/ST-LIB.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 9ebab8e09..c2cddeb77 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -143,13 +143,13 @@ template struct Board { using DevT = std::remove_cvref_t; using Domain = typename DevT::domain; - constexpr std::size_t idx = domain_index_of(); - constexpr std::size_t N = domain_size_for_instance(); + constexpr std::size_t N = domain_size(); + return Domain::template Init::instances[Target.index]; if constexpr (std::is_same_v) { - return Domain::template Init::instances[idx]; + return Domain::template Init::instances[Target.index]; } else { - return Domain::template Init::instances[idx]; + return Domain::template Init::instances[Target.index]; } } }; From a58ffdb9ec45cdd09e9a0c4f48f393a45066279d Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 08:10:04 +0100 Subject: [PATCH 139/166] fix(Sd): Implemenent st-lib fix --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 231e822a6..8940320dc 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -49,6 +49,7 @@ struct SdDomain { struct SdCard { using domain = SdDomain; Entry e; + mutable size_t index; Peripheral peripheral; @@ -136,7 +137,7 @@ struct SdDomain { local_e.d2_pin_idx = ctx.template add(d2.e); local_e.d3_pin_idx = ctx.template add(d3.e); - ctx.template add(local_e); + index = ctx.template add(local_e); } }; From e74ed3903eb2b9e6209188cc80800637d1ccd832 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 08:19:15 +0100 Subject: [PATCH 140/166] fix(Sd): Validate pin selection for SDMMC1 --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 8940320dc..68b4b034a 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -72,13 +72,12 @@ struct SdDomain { * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none - * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8) - * @param d1_pin_for_sdmmc1 D1 pin to use if using SDMMC1 (default PC9) - * @note The other pins (CMD, CK, D2, D3) are fixed for each peripheral. + * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8, can also be PB13). + * @note The other pins (CMD, CK, D1, D2, D3) are fixed for each peripheral. */ consteval SdCard(Peripheral sdmmc_peripheral, std::optional> card_detect_config, std::optional> write_protect_config, - GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8, GPIODomain::Pin d1_pin_for_sdmmc1 = ST_LIB::PC9) : + GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8) : e{.peripheral = sdmmc_peripheral}, peripheral(sdmmc_peripheral), buffer0(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), @@ -107,6 +106,9 @@ struct SdDomain { if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { ST_LIB::compile_error("Invalid SDMMC peripheral"); } + if (d0_pin_for_sdmmc1 != ST_LIB::PC8 && d0_pin_for_sdmmc1 != ST_LIB::PB13) { + ST_LIB::compile_error("D0 pin can only be PC8 or PB13 for SDMMC1"); + } } From 3edf7d50068172dc9cca9075e860788c95c9e107 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 08:54:47 +0100 Subject: [PATCH 141/166] style(Sd): Move things to .cpp and small style fixes --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 296 +++++---------------------------------- Src/ST-LIB_LOW/Sd/Sd.cpp | 225 ++++++++++++++++++++++++++++- 2 files changed, 256 insertions(+), 265 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 68b4b034a..60841d101 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -94,7 +94,7 @@ struct SdDomain { GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), d1((sdmmc_peripheral == Peripheral::sdmmc1) ? - GPIODomain::GPIO(d1_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PC9, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), d2((sdmmc_peripheral == Peripheral::sdmmc1) ? GPIODomain::GPIO(ST_LIB::PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : @@ -106,7 +106,9 @@ struct SdDomain { if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { ST_LIB::compile_error("Invalid SDMMC peripheral"); } - if (d0_pin_for_sdmmc1 != ST_LIB::PC8 && d0_pin_for_sdmmc1 != ST_LIB::PB13) { + if ((d0_pin_for_sdmmc1.pin != ST_LIB::PC8.pin) && (d0_pin_for_sdmmc1.port != ST_LIB::PC8.port) + && + (d0_pin_for_sdmmc1.pin != ST_LIB::PB13.pin) && (d0_pin_for_sdmmc1.port != ST_LIB::PB13.port)) { ST_LIB::compile_error("D0 pin can only be PC8 or PB13 for SDMMC1"); } } @@ -178,7 +180,6 @@ struct SdDomain { if (peripheral_used[peripheral_index]) ST_LIB::compile_error("SDMMC peripheral used multiple times in SdDomain"); peripheral_used[peripheral_index] = true; - // Fill configuration cfgs[i].peripheral = e.peripheral; cfgs[i].mpu_buffer0_idx = e.mpu_buffer0_idx; cfgs[i].mpu_buffer1_idx = e.mpu_buffer1_idx; @@ -208,32 +209,13 @@ struct SdDomain { template friend struct SdCardWrapper; template friend struct Init; - bool* operation_flag = nullptr; // External flag to indicate that an operation has finished - - // Public handlers called from C HAL callbacks (keeps private members encapsulated) - void on_dma_read_complete() { - if (operation_flag) { - *operation_flag = true; - operation_flag = nullptr; - } - SDMMC_CmdStopTransfer(hsd.Instance); - } - - void on_dma_write_complete() { - if (operation_flag) { - *operation_flag = true; - operation_flag = nullptr; - } - SDMMC_CmdStopTransfer(hsd.Instance); - } - - void on_abort() { - ErrorHandler("SD Card operation aborted"); - } - - void on_error() { - ErrorHandler("SD Card error occurred"); - } + bool* operation_flag = nullptr; // External flag to indicate that an operation has finished. Only public so that it can be set in the public handlers below. + + // Public handlers called from C HAL callbacks. Don't use them, don't even think about using them. + void on_dma_read_complete(); + void on_dma_write_complete(); + void on_abort(); + void on_error(); private: SD_HandleTypeDef hsd; @@ -245,71 +227,25 @@ struct SdDomain { std::optional> wp_instance; bool card_initialized; - BufferSelect current_buffer; // The one that is currently available for CPU access and not used by IDMA + BufferSelect current_buffer; // The one that is currently available for CPU access and not being used by IDMA HAL_SD_CardInfoTypeDef card_info; // Functions - bool is_card_present() { return cd_instance->first->read() == cd_instance->second; } - bool is_write_protected() { return wp_instance->first->read() == wp_instance->second; } - - bool is_busy() { - if (!card_initialized) return false; - HAL_SD_StateTypeDef state = HAL_SD_GetState(&hsd); - return (state != HAL_SD_STATE_READY && state != HAL_SD_STATE_TRANSFER); - } - - bool initialize_card() { - if (card_initialized) { return true; } // Already initialized - - HAL_StatusTypeDef status = HAL_SD_Init(&hsd); - - if (status != HAL_OK) { return false; } - - if (HAL_SD_GetCardInfo(&hsd, &card_info) != HAL_OK) { - ErrorHandler("Failed to get SD card info"); - } - - if (!configure_idma()) { - ErrorHandler("SD Card IDMA configuration failed"); - } - - if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { - ErrorHandler("SD Card speed/bus configuration failed"); - } - - card_initialized = true; - return true; - } - - bool deinitialize_card() { - if (!card_initialized) { return true; } // Already deinitialized - - HAL_StatusTypeDef status = HAL_SD_DeInit(&hsd); - if (status != HAL_OK) { return false; } - - card_initialized = false; - return true; - } - - void switch_buffer() { - current_buffer = (current_buffer == BufferSelect::Buffer0) ? BufferSelect::Buffer1 : BufferSelect::Buffer0; - } - - bool configure_idma() { - HAL_StatusTypeDef status = HAL_SDEx_ConfigDMAMultiBuffer(&hsd, - reinterpret_cast(mpu_buffer0_instance->ptr), - reinterpret_cast(mpu_buffer1_instance->ptr), - mpu_buffer0_instance->size / 512); // Number of 512B-blocks - - if (status != HAL_OK) { return false; } - return true; - } + bool is_card_present(); + bool is_write_protected(); + bool is_busy(); + bool initialize_card(); + bool deinitialize_card(); + void switch_buffer(); + bool configure_idma(); + // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs + HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); + HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); }; template struct SdCardWrapper{ - template friend struct Init; static constexpr bool has_cd = card_request.cd.has_value(); static constexpr bool has_wp = card_request.wp.has_value(); @@ -343,11 +279,11 @@ struct SdDomain { ErrorHandler("SD Card not initialized"); } - // 1. Wait for the CARD (hardware) to be ready, not just the handle + // Wait for the CARD (hardware) to be ready, not just the handle uint32_t timeout = HAL_GetTick() + 1000; while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { if (HAL_GetTick() > timeout) { - // 2. OPTIONAL: If stuck, try forcing a STOP command to reset the state + // Try to force a STOP command to reset the state, just in case SDMMC_CmdStopTransfer(instance.hsd.Instance); HAL_Delay(10); // Give it a moment if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { @@ -357,12 +293,8 @@ struct SdDomain { } } - if (instance.is_busy()) { - return false; // Driver busy - } - - // Now it is safe to send CMD18 - HAL_StatusTypeDef status = Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); + // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); if (status != HAL_OK) { ErrorHandler("SD Card read operation failed"); @@ -380,11 +312,11 @@ struct SdDomain { ErrorHandler("SD Card not initialized"); } - // 1. Wait for the CARD (hardware) to be ready, not just the handle + // Wait for the CARD (hardware) to be ready, not just the handle uint32_t timeout = HAL_GetTick() + 1000; while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { if (HAL_GetTick() > timeout) { - // 2. OPTIONAL: If stuck, try forcing a STOP command to reset the state + // Try to force a STOP command to reset the state, just in case SDMMC_CmdStopTransfer(instance.hsd.Instance); HAL_Delay(10); // Give it a moment if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { @@ -393,12 +325,8 @@ struct SdDomain { } } } - - if (instance.is_busy()) { - return false; // Busy - } // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want - HAL_StatusTypeDef status = Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); if (status != HAL_OK) { ErrorHandler("SD Card write operation failed"); @@ -433,167 +361,6 @@ struct SdDomain { if (instance.is_write_protected()) { ErrorHandler("SD Card is write-protected"); } } } - - // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs - HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { - auto* hsd = &instance.hsd; - SDMMC_DataInitTypeDef config; - uint32_t errorstate; - uint32_t DmaBase0_reg; - uint32_t DmaBase1_reg; - uint32_t add = BlockAdd; - - if (hsd->State == HAL_SD_STATE_READY) - { - if ((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) - { - hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; - return HAL_ERROR; - } - - DmaBase0_reg = hsd->Instance->IDMABASE0; - DmaBase1_reg = hsd->Instance->IDMABASE1; - - if ((hsd->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) - { - hsd->ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; - return HAL_ERROR; - } - - /* Initialize data control register */ - hsd->Instance->DCTRL = 0; - /* Clear old Flags*/ - __HAL_SD_CLEAR_FLAG(hsd, SDMMC_STATIC_DATA_FLAGS); - - hsd->ErrorCode = HAL_SD_ERROR_NONE; - hsd->State = HAL_SD_STATE_BUSY; - - if (hsd->SdCard.CardType != CARD_SDHC_SDXC) - { - add *= 512U; - } - - /* Configure the SD DPSM (Data Path State Machine) */ - config.DataTimeOut = SDMMC_DATATIMEOUT; - config.DataLength = BLOCKSIZE * NumberOfBlocks; - config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; - config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; - config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; - config.DPSM = SDMMC_DPSM_DISABLE; - (void)SDMMC_ConfigData(hsd->Instance, &config); - - hsd->Instance->DCTRL |= SDMMC_DCTRL_FIFORST; - - __SDMMC_CMDTRANS_ENABLE(hsd->Instance); - - if (instance.current_buffer == BufferSelect::Buffer1) { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; - } else { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; - } - instance.switch_buffer(); - - /* Read Blocks in DMA mode */ - hsd->Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA); - - /* Read Multi Block command */ - errorstate = SDMMC_CmdReadMultiBlock(hsd->Instance, add); - if (errorstate != HAL_SD_ERROR_NONE) - { - hsd->State = HAL_SD_STATE_READY; - hsd->ErrorCode |= errorstate; - return HAL_ERROR; - } - - __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND | - SDMMC_IT_IDMABTC)); - - return HAL_OK; - } - else - { - return HAL_BUSY; - } - - } - - // Variation of HAL_SDEx_WriteBlocksDMAMultiBuffer to fit our needs - HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { - auto* hsd = &instance.hsd; - SDMMC_DataInitTypeDef config; - uint32_t errorstate; - uint32_t DmaBase0_reg; - uint32_t DmaBase1_reg; - uint32_t add = BlockAdd; - - if (hsd->State == HAL_SD_STATE_READY) - { - if ((add + NumberOfBlocks) > (hsd->SdCard.LogBlockNbr)) - { - hsd->ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; - return HAL_ERROR; - } - - DmaBase0_reg = hsd->Instance->IDMABASE0; - DmaBase1_reg = hsd->Instance->IDMABASE1; - if ((hsd->Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) - { - hsd->ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; - return HAL_ERROR; - } - - /* Initialize data control register */ - hsd->Instance->DCTRL = 0; - - hsd->ErrorCode = HAL_SD_ERROR_NONE; - - hsd->State = HAL_SD_STATE_BUSY; - - if (hsd->SdCard.CardType != CARD_SDHC_SDXC) - { - add *= 512U; - } - - /* Configure the SD DPSM (Data Path State Machine) */ - config.DataTimeOut = SDMMC_DATATIMEOUT; - config.DataLength = BLOCKSIZE * NumberOfBlocks; - config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; - config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; - config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; - config.DPSM = SDMMC_DPSM_DISABLE; - (void)SDMMC_ConfigData(hsd->Instance, &config); - - __SDMMC_CMDTRANS_ENABLE(hsd->Instance); - - if (instance.current_buffer == BufferSelect::Buffer1) { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; - } else { - hsd->Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; - } - instance.switch_buffer(); - - /* Write Blocks in DMA mode */ - hsd->Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA); - - /* Write Multi Block command */ - errorstate = SDMMC_CmdWriteMultiBlock(hsd->Instance, add); - if (errorstate != HAL_SD_ERROR_NONE) - { - hsd->State = HAL_SD_STATE_READY; - hsd->ErrorCode |= errorstate; - return HAL_ERROR; - } - - __HAL_SD_ENABLE_IT(hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND | - SDMMC_IT_IDMABTC)); - - return HAL_OK; - } - else - { - return HAL_BUSY; - } - } }; @@ -636,6 +403,7 @@ struct SdDomain { #ifdef SD_DEBUG_ENABLE + // Doesn't really work in this moment, need to actually get the PLL1 frequency somehow inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; // Get a 400 kHz clock for debugging //uint32_t pll1_freq = HAL_RCCEx_GetPLL1ClockFreq(); // SDMMC clock source is PLL1 @@ -664,10 +432,10 @@ struct SdDomain { RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { - ErrorHandler("SDMMC clock configuration failed"); + ErrorHandler("SDMMC clock configuration failed, maybe try with a slower clock or higher divider?"); } - // Ensure PLL1Q output is enabled (it might be disabled by default if not used by other peripherals) + // Ensure PLL1Q output is enabled __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ); if (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) == 0) { diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp index 88ec5c058..806416b58 100644 --- a/Src/ST-LIB_LOW/Sd/Sd.cpp +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -1,6 +1,229 @@ #include "ST-LIB_LOW/Sd/Sd.hpp" -// 1. Define the globals here (Strong definitions) + +using namespace ST_LIB; + +void SdDomain::Instance::on_dma_read_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_dma_write_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_abort() { ErrorHandler("SD Card operation aborted"); } + +void SdDomain::Instance::on_error() { ErrorHandler("SD Card error occurred"); } + +bool SdDomain::Instance::is_card_present() { return cd_instance->first->read() == cd_instance->second; } +bool SdDomain::Instance::is_write_protected() { return wp_instance->first->read() == wp_instance->second; } + +bool SdDomain::Instance::is_busy() { + if (!card_initialized) return false; + return (hsd.State == HAL_SD_STATE_BUSY) || (hsd.State == HAL_SD_STATE_PROGRAMMING) || (hsd.State == HAL_SD_STATE_RECEIVING); +} + +bool SdDomain::Instance::initialize_card() { + if (card_initialized) { return true; } // Already initialized + + HAL_StatusTypeDef status = HAL_SD_Init(&hsd); + if (status != HAL_OK) { + return false; + } + + if (HAL_SD_GetCardInfo(&hsd, &card_info) != HAL_OK) { + return false; + } + + if (!configure_idma()) { + return false; + } + + if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { + return false; + } + + card_initialized = true; + return true; +} + +bool SdDomain::Instance::deinitialize_card() { + if (!card_initialized) { return true; } // Already deinitialized + + HAL_StatusTypeDef status = HAL_SD_DeInit(&hsd); + if (status != HAL_OK) { + return false; + } + + card_initialized = false; + return true; +} + +void SdDomain::Instance::switch_buffer() { + current_buffer = (current_buffer == BufferSelect::Buffer0) ? BufferSelect::Buffer1 : BufferSelect::Buffer0; +} + +bool SdDomain::Instance::configure_idma() { + HAL_StatusTypeDef status = HAL_SDEx_ConfigDMAMultiBuffer(&hsd, + reinterpret_cast(mpu_buffer0_instance->ptr), + reinterpret_cast(mpu_buffer1_instance->ptr), + mpu_buffer0_instance->size / 512); // Number of 512B-blocks + + if (status != HAL_OK) { return false; } + return true; +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + /* Clear old Flags*/ + __HAL_SD_CLEAR_FLAG(&hsd, SDMMC_STATIC_DATA_FLAGS); + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + hsd.State = HAL_SD_STATE_BUSY; + + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Read Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Read Multi Block command */ + errorstate = SDMMC_CmdReadMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + + hsd.State = HAL_SD_STATE_BUSY; + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Write Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Write Multi Block command */ + errorstate = SDMMC_CmdWriteMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + + SD_HandleTypeDef* g_sdmmc1_handle = nullptr; SD_HandleTypeDef* g_sdmmc2_handle = nullptr; From f47c0ba739d4c1f20834dd1f2e562b41ef7f6160 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 08:58:58 +0100 Subject: [PATCH 142/166] feat(Sd): Don't use a timeout but just return false if sd not ready --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 60841d101..bd94a7664 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -279,18 +279,8 @@ struct SdDomain { ErrorHandler("SD Card not initialized"); } - // Wait for the CARD (hardware) to be ready, not just the handle - uint32_t timeout = HAL_GetTick() + 1000; - while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { - if (HAL_GetTick() > timeout) { - // Try to force a STOP command to reset the state, just in case - SDMMC_CmdStopTransfer(instance.hsd.Instance); - HAL_Delay(10); // Give it a moment - if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { - ErrorHandler("Timeout waiting for SD Card to enter TRANSFER state"); - return false; - } - } + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer } // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want @@ -312,18 +302,8 @@ struct SdDomain { ErrorHandler("SD Card not initialized"); } - // Wait for the CARD (hardware) to be ready, not just the handle - uint32_t timeout = HAL_GetTick() + 1000; - while (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { - if (HAL_GetTick() > timeout) { - // Try to force a STOP command to reset the state, just in case - SDMMC_CmdStopTransfer(instance.hsd.Instance); - HAL_Delay(10); // Give it a moment - if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { - ErrorHandler("Timeout waiting for SD Card to enter TRANSFER state"); - return false; - } - } + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer } // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want HAL_StatusTypeDef status = instance.Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); From 13f048f5ee3617126e6a225eab9576a47a3b4b60 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 09:05:19 +0100 Subject: [PATCH 143/166] feat(MdmaPacket): Add a MdmaPacketBase --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 33 +++++++++++-------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 78e13b711..22d107e5f 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -64,8 +64,14 @@ struct MdmaPacketDomain { * @tparam Types The types of the values in the packet. * @note It uses non-cached memory for MDMA operations. */ + class MdmaPacketBase : public Packet { + public: + virtual uint8_t* build(bool* done, uint8_t* destination_address = nullptr) = 0; + using Packet::build; + }; + template - class MdmaPacket : public Packet { + class MdmaPacket : public MdmaPacketBase { public: uint16_t id; uint8_t* buffer; @@ -79,10 +85,10 @@ struct MdmaPacketDomain { using domain = MdmaPacketDomain; static constexpr size_t packet_size_bytes = (sizeof(Types) + ...) + sizeof(uint16_t); - static constexpr size_t nodes_size = (2 * (sizeof...(Types) + 1) + 2); + static constexpr size_t nodes_size_bytes = (2 * (sizeof...(Types) + 1) + 2) * sizeof(MDMA::LinkedListNode); using PacketMem = std::array; - using NodesMem = std::array; + using NodesMem = std::array; MPUDomain::Buffer packet_req; MPUDomain::Buffer nodes_req; @@ -152,15 +158,15 @@ struct MdmaPacketDomain { /** * @brief Build the packet and transfer data into non-cached buffer using MDMA - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) * @return Pointer to the built packet data (internal buffer or destination address) */ - uint8_t* build(uint8_t* destination_address) { + uint8_t* build(uint8_t* destination_address = nullptr) { set_build_destination(destination_address); bool done = false; MDMA::transfer_list(build_nodes[0], &done); while (!done) { - MDMA::update(); + // Busy wait } return destination_address ? destination_address : buffer; } @@ -183,25 +189,15 @@ struct MdmaPacketDomain { return build(destination_address); } - /** - * @brief Parse the packet data from non-cached buffer using MDMA - * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. - * @param done Optional pointer to a boolean that will be set to true when parsing is done. - */ - void parse(uint8_t* data) override { + void parse(uint8_t* data = nullptr) override { bool done = false; auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, &done); while (!done) { - MDMA::update(); + // Busy wait } } - /** - * @brief Parse the packet data from non-cached buffer using MDMA with a promise - * @param done Pointer to a boolean that will be set to true when parsing is done. - * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). - */ void parse(bool* done, uint8_t* data = nullptr) { auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, done); @@ -240,6 +236,7 @@ struct MdmaPacketDomain { } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } + SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { From 616d579cf0bf656c4fcebef6e1ef7323cfd5d41c Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 09:20:00 +0100 Subject: [PATCH 144/166] fix(Sd): Check that not too many blocks are being requested for write/read --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index bd94a7664..9b90d66d7 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -278,6 +278,9 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } + if (num_blocs > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to write in SD"); + } if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { return false; // Card not ready for data transfer @@ -301,6 +304,9 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } + if (num_blocs > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to write in SD"); + } if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { return false; // Card not ready for data transfer From a84f6336d3c4efc7b2afbc5fc524570025aad260 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 10:21:25 +0100 Subject: [PATCH 145/166] feat(SdLogger): Second iteration of the logger, optimizing update --- Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp | 75 ++++++++++++++------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp index 928473b40..573b8d618 100644 --- a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -5,32 +5,33 @@ * Author: Boris */ -#pragma once +#ifndef SDLOGGER_HPP +#define SDLOGGER_HPP #include "ST-LIB_LOW/ST-LIB_LOW.hpp" #include "HALAL/HALAL.hpp" -#include "Sd/Sd.hpp" -#include "HALAL/Models/Packets/MdmaPacket.hpp" -#include template class SdLogger { -public: + public: static constexpr size_t MAX_PACKETS = 32; SdLogger(SdWrapper& sd_card) : sd(sd_card) { sd.init_card(); current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); - current_buffer_size = sd.get_current_buffer()->size() * 4; + current_buffer_size = sd.get_current_buffer()->size() * 4; // uint32_t is 4B + current_buffer_blocks = current_buffer_size / 512; } - void add_packet(MdmaPacketDomain::MdmaPacketBase* packet) { + uint8_t add_packet(MdmaPacketDomain::MdmaPacketBase* packet) { if (packet_count >= MAX_PACKETS) { ErrorHandler("Max packets reached in SdLogger"); - return; + return 0; } packets[packet_count] = packet; + packet_sizes[packet_count] = packet->get_size(); // Cache size to avoid virtual call in update packet_count++; + return packet_count - 1; } void log(uint16_t packet_index) { @@ -38,6 +39,7 @@ class SdLogger { } void update() { + /* Sd update logic */ if (sd_write_ongoing) { if (sd_write_complete_flag) { sd_write_ongoing = false; @@ -45,9 +47,10 @@ class SdLogger { } } + /* Packets building update logic */ if (building_mask) { uint32_t temp_mask = building_mask; - while (temp_mask) { + while (temp_mask) { // Iterate over the packets being build and check if they've been built already uint32_t i = __builtin_ctz(temp_mask); if (build_complete_flags[i]) { building_mask &= ~(1UL << i); @@ -57,61 +60,57 @@ class SdLogger { } } + /* Buffer management update logic */ + + // Normal buffer operation if (!buffer_flush_pending) { uint32_t temp_mask = request_mask; - while (temp_mask) { + while (temp_mask) { // Iterate over all requested to be built packets uint32_t i = __builtin_ctz(temp_mask); temp_mask &= ~(1UL << i); - size_t packet_size = packets[i]->get_size(); + // If it is already being built, don't add it once more (your program is kinda slow then though, should reconsider what you're doing) + if (building_mask & (1UL << i)) { + continue; + } + + size_t packet_size = packet_sizes[i]; + // Don't add more packets if they can't fit (this could be optimized to test if there are other packets smaller that can fit, but that's more cycles, this works) if (current_buffer_offset + packet_size > current_buffer_size) { buffer_flush_pending = true; break; } - if (building_mask & (1UL << i)) { - continue; - } - request_mask &= ~(1UL << i); building_mask |= (1UL << i); build_complete_flags[i] = false; - packets[i]->build(&build_complete_flags[i], current_buffer_ptr + current_buffer_offset); + packets[i]->build(const_cast(&build_complete_flags[i]), current_buffer_ptr + current_buffer_offset); current_buffer_offset += packet_size; } } + // Have to flush and all packets have been built so we can flush if (buffer_flush_pending && building_mask == 0) { - if (flush_buffer()) { - buffer_flush_pending = false; - } + if (flush_buffer()) { + buffer_flush_pending = false; + } } } -private: + private: bool flush_buffer() { if (sd_write_ongoing) { return false; } - - uint32_t bytes_to_write = current_buffer_offset; - if (bytes_to_write == 0) { - current_buffer_offset = 0; - return true; - } - - uint32_t blocks = (bytes_to_write + 511) / 512; - sd_write_ongoing = true; sd_write_complete_flag = false; - - if (!sd.write_blocks(sd_block_addr, blocks, &sd_write_complete_flag)) { - ErrorHandler("Failed to start SD write"); - sd_write_ongoing = false; + + if (!sd.write_blocks(sd_block_addr, current_buffer_blocks, const_cast(&sd_write_complete_flag))) { return false; } else { - sd_block_addr += blocks; + sd_write_ongoing = true; + sd_block_addr += current_buffer_blocks; current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); current_buffer_offset = 0; return true; @@ -120,19 +119,23 @@ class SdLogger { SdWrapper& sd; std::array packets; - size_t packet_count = 0; + size_t packet_sizes[MAX_PACKETS]; + uint8_t packet_count = 0; uint32_t request_mask = 0; bool sd_write_ongoing = false; bool sd_write_complete_flag = false; uint32_t building_mask = 0; - std::array build_complete_flags; + volatile bool build_complete_flags[MAX_PACKETS]; uint8_t* current_buffer_ptr = nullptr; uint32_t current_buffer_size = 0; + uint32_t current_buffer_blocks = 0; uint32_t current_buffer_offset = 0; bool buffer_flush_pending = false; uint32_t sd_block_addr = 0; }; + +#endif // SDLOGGER_HPP \ No newline at end of file From 13589f149425b7389ccea8c77c179fc5c5188699 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 10:27:05 +0100 Subject: [PATCH 146/166] fix(Sd): Typo --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index 9b90d66d7..f34f761b4 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -278,7 +278,7 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } - if (num_blocs > instance.mpu_buffer0_instance->size / 512) { + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { ErrorHandler("Too many blocks requested to write in SD"); } @@ -304,7 +304,7 @@ struct SdDomain { if (!instance.card_initialized) { ErrorHandler("SD Card not initialized"); } - if (num_blocs > instance.mpu_buffer0_instance->size / 512) { + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { ErrorHandler("Too many blocks requested to write in SD"); } From cb80ec6e926e134a84d2d4f9c9e7565127e6e650 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 10:27:56 +0100 Subject: [PATCH 147/166] fix(SdLogger): Fix typo introduced by merge --- Inc/ST-LIB.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 2a7868ee7..56dda096f 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -116,7 +116,7 @@ template struct Board { .mpu_cfgs = MPUDomain::template build( ctx.template span()), .sd_cfgs = SdDomain::template build( - ctx.template span()),, + ctx.template span()), .mdma_packet_cfgs = MdmaPacketDomain::template build( ctx.template span()) // ... From 2188ccb144d8fd97bc6552c81b1259710c0d219c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Cant=C3=B3=20Catal=C3=A1n?= <144663567+Cantonplas@users.noreply.github.com> Date: Wed, 17 Dec 2025 15:07:46 +0100 Subject: [PATCH 148/166] Remove NODES_MAX definition from MDMA.hpp Removed the NODES_MAX definition and related comments. --- Inc/HALAL/Models/MDMA/MDMA.hpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 7a1a4679f..0754c4c72 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -15,10 +15,6 @@ #undef MDMA #endif -#ifndef NODES_MAX -#define NODES_MAX 100 -#endif - #ifndef TRANSFER_QUEUE_MAX_SIZE #define TRANSFER_QUEUE_MAX_SIZE 50 #endif @@ -134,6 +130,7 @@ class MDMA{ static void prepare_transfer(Instance& instance, MDMA::LinkedListNode* first_node); static void prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node); static Instance& get_instance(uint8_t id); + inline static std::array instances{}; static std::unordered_map instance_to_channel; static std::unordered_map channel_to_instance; @@ -155,13 +152,6 @@ class MDMA{ public: - - - - // Pool for MDMA_LinkNodeTypeDef, uses external non-cached memory - static Pool link_node_pool; - //To be reviewed when we make mdma in compile time - static void start(); static void irq_handler(); static void update(); From c768655ab33a32990c0850b6f81db402468065d6 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 16:18:08 +0100 Subject: [PATCH 149/166] fix(Sd): Check limit of blocks --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index f34f761b4..ea226a80d 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -111,6 +111,11 @@ struct SdDomain { (d0_pin_for_sdmmc1.pin != ST_LIB::PB13.pin) && (d0_pin_for_sdmmc1.port != ST_LIB::PB13.port)) { ST_LIB::compile_error("D0 pin can only be PC8 or PB13 for SDMMC1"); } + if (buffer_blocks == 0) { + ST_LIB::compile_error("Buffer blocks must be greater than 0"); + } else if (buffer_blocks > 15) { + ST_LIB::compile_error("Buffer blocks must be less than or equal to 15"); + } } From 25f1ba839c6bb37639fa74948006962228dd0677 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 17 Dec 2025 16:18:41 +0100 Subject: [PATCH 150/166] fix(Sd): Unnecesary operation --- Src/ST-LIB_LOW/Sd/Sd.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp index 806416b58..178c9021a 100644 --- a/Src/ST-LIB_LOW/Sd/Sd.cpp +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -123,7 +123,7 @@ HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint config.DPSM = SDMMC_DPSM_DISABLE; (void)SDMMC_ConfigData(hsd.Instance, &config); - hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; __SDMMC_CMDTRANS_ENABLE(hsd.Instance); if (current_buffer == BufferSelect::Buffer1) { @@ -193,6 +193,8 @@ HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uin config.DPSM = SDMMC_DPSM_DISABLE; (void)SDMMC_ConfigData(hsd.Instance, &config); + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); if (current_buffer == BufferSelect::Buffer1) { From f27035cc0d9cbef462507fcbc7595dd81a1646e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20S=C3=A1ez?= Date: Thu, 18 Dec 2025 00:04:37 +0100 Subject: [PATCH 151/166] Fixed instance_of method. Now add interface needs the Device --- Inc/HALAL/Models/GPIO.hpp | 3 +-- Inc/ST-LIB.hpp | 33 ++++++++++++++++++++++++++++--- Inc/ST-LIB_LOW/DigitalInput2.hpp | 5 ++--- Inc/ST-LIB_LOW/DigitalOutput2.hpp | 5 ++--- 4 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Inc/HALAL/Models/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index 953d8b19f..e69c94c16 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -172,7 +172,6 @@ struct GPIODomain { struct GPIO { using domain = GPIODomain; - mutable size_t index; Entry e; @@ -185,7 +184,7 @@ struct GPIODomain { } template consteval void inscribe(Ctx &ctx) const { - index = ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index c17d73f5b..221035b08 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -32,6 +32,7 @@ template struct BuildCtx { static constexpr std::size_t max_count_v = D::max_instances; std::tuple, max_count_v>...> storage{}; + std::tuple>...> owners{}; std::array sizes{}; template @@ -47,12 +48,17 @@ template struct BuildCtx { } } - template consteval std::size_t add(typename D::Entry e) { + template + consteval std::size_t add(typename D::Entry e, const Owner *owner) { constexpr std::size_t I = domain_index(); auto &arr = std::get(storage); + auto &own = std::get(owners); auto &size = sizes[I]; + const auto idx = size; - arr[size++] = e; + arr[size] = e; + own[size] = owner; + ++size; return idx; } @@ -64,6 +70,13 @@ template struct BuildCtx { return std::span{arr.data(), size}; } + template consteval auto owners_span() const { + constexpr std::size_t I = domain_index(); + auto const &arr = std::get(owners); + auto const size = sizes[I]; + return std::span{arr.data(), size}; + } + template consteval std::size_t size() const { constexpr std::size_t I = domain_index(); return sizes[I]; @@ -126,12 +139,26 @@ template struct Board { // ... } + template + static consteval std::size_t owner_index_of() { + constexpr auto owners = ctx.template owners_span(); + + if constexpr (I >= owners.size()) { + compile_error("Device not registered in domain"); + return 0; + } else { + return owners[I] == &Target ? I : owner_index_of(); + } + } + template static auto &instance_of() { using DevT = std::remove_cvref_t; using Domain = typename DevT::domain; + constexpr std::size_t idx = owner_index_of(); + constexpr std::size_t N = domain_size(); - return Domain::template Init::instances[Target.index]; + return Domain::template Init::instances[idx]; } }; diff --git a/Inc/ST-LIB_LOW/DigitalInput2.hpp b/Inc/ST-LIB_LOW/DigitalInput2.hpp index a900b924c..4dec87413 100644 --- a/Inc/ST-LIB_LOW/DigitalInput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalInput2.hpp @@ -12,7 +12,6 @@ struct DigitalInputDomain { struct DigitalInput { GPIODomain::GPIO gpio; using domain = DigitalInputDomain; - mutable size_t index; consteval DigitalInput(const GPIODomain::Pin &pin, GPIODomain::Pull pull = GPIODomain::Pull::None, @@ -20,9 +19,9 @@ struct DigitalInputDomain { : gpio{pin, GPIODomain::OperationMode::INPUT, pull, speed} {} template consteval void inscribe(Ctx &ctx) const { - const auto gpio_idx = ctx.template add(gpio.e); + const auto gpio_idx = ctx.template add(gpio.e, &gpio); Entry e{.gpio_idx = gpio_idx}; - index = ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/ST-LIB_LOW/DigitalOutput2.hpp b/Inc/ST-LIB_LOW/DigitalOutput2.hpp index 8889691b4..8c99b9f24 100644 --- a/Inc/ST-LIB_LOW/DigitalOutput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalOutput2.hpp @@ -18,7 +18,6 @@ struct DigitalOutputDomain { struct DigitalOutput { GPIODomain::GPIO gpio; using domain = DigitalOutputDomain; - mutable size_t index; consteval DigitalOutput(const GPIODomain::Pin &pin, OutputMode mode = OutputMode::PUSH_PULL, @@ -28,9 +27,9 @@ struct DigitalOutputDomain { } template consteval void inscribe(Ctx &ctx) const { - const auto gpio_idx = ctx.template add(gpio.e); + const auto gpio_idx = ctx.template add(gpio.e, &gpio); Entry e{.gpio_idx = gpio_idx}; - index = ctx.template add(e); + ctx.template add(e, this); } }; From ae95584dd99842282e94d1ddb5eb8839aa52f198 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 10:25:19 +0100 Subject: [PATCH 152/166] fix(MPU): Apply the fix of the fix --- Inc/HALAL/Models/MPU.hpp | 3 +-- Inc/ST-LIB.hpp | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index f447d2d1b..3ed5fbbcc 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -57,7 +57,6 @@ struct MPUDomain { using domain = MPUDomain; using buffer_type = T; Entry e; - mutable size_t index; /** * @brief Constructs a Buffer entry for a type T. @@ -78,7 +77,7 @@ struct MPUDomain { template consteval void inscribe(Ctx &ctx) const { - index = ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index d179cdbd9..5fbf706e2 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -164,7 +164,12 @@ template struct Board { constexpr std::size_t idx = owner_index_of(); constexpr std::size_t N = domain_size(); - return Domain::template Init::instances[idx]; + + if constexpr (std::is_same_v) { + return Domain::template Init::instances[idx]; + } else { + return Domain::template Init::instances[idx]; + } } }; From 7d032812b9695f9d29e9231f5dc2d9d3d5c40875 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 10:29:17 +0100 Subject: [PATCH 153/166] fix(MdmaPacket): Fix something went wrong somewhere in a merge --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 22d107e5f..4371ea8cb 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -85,10 +85,10 @@ struct MdmaPacketDomain { using domain = MdmaPacketDomain; static constexpr size_t packet_size_bytes = (sizeof(Types) + ...) + sizeof(uint16_t); - static constexpr size_t nodes_size_bytes = (2 * (sizeof...(Types) + 1) + 2) * sizeof(MDMA::LinkedListNode); + static constexpr size_t nodes_size = (2 * (sizeof...(Types) + 1) + 2); using PacketMem = std::array; - using NodesMem = std::array; + using NodesMem = std::array; MPUDomain::Buffer packet_req; MPUDomain::Buffer nodes_req; @@ -236,7 +236,6 @@ struct MdmaPacketDomain { } else { build_nodes[sizeof...(Types)]->set_next(nullptr); } - SCB_CleanDCache_by_Addr((uint32_t*)build_nodes[sizeof...(Types)], sizeof(MDMA::LinkedListNode) * 2); } MDMA::LinkedListNode* set_parse_source(uint8_t* external_buffer) { @@ -252,4 +251,4 @@ struct MdmaPacketDomain { }; }; -#endif // MDMA_PACKET_HPP +#endif // MDMA_PACKET_HPP \ No newline at end of file From 7ee058fda27dce72c3956d589c995b89fe4def55 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 10:55:16 +0100 Subject: [PATCH 154/166] fix(MdmaPacket): Apply the fix of the fix --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 4371ea8cb..ffd03086e 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -100,9 +100,9 @@ struct MdmaPacketDomain { template consteval void inscribe(Ctx &ctx) const { - size_t p_idx = ctx.template add(packet_req.e); - size_t n_idx = ctx.template add(nodes_req.e); - ctx.template add({p_idx, n_idx}); + size_t p_idx = ctx.template add(packet_req.e, this); + size_t n_idx = ctx.template add(nodes_req.e, this); + ctx.template add({p_idx, n_idx}, this); } }; @@ -161,7 +161,7 @@ struct MdmaPacketDomain { * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) * @return Pointer to the built packet data (internal buffer or destination address) */ - uint8_t* build(uint8_t* destination_address = nullptr) { + uint8_t* build(uint8_t* destination_address) { set_build_destination(destination_address); bool done = false; MDMA::transfer_list(build_nodes[0], &done); @@ -189,7 +189,7 @@ struct MdmaPacketDomain { return build(destination_address); } - void parse(uint8_t* data = nullptr) override { + void parse(uint8_t* data) override { bool done = false; auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, &done); From fd1e5795f44657738d99e4e8f8fde90827e41c29 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 10:59:23 +0100 Subject: [PATCH 155/166] fix(MdmaPacket): Fix merge things that went wrong --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index ffd03086e..222354a93 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -158,7 +158,7 @@ struct MdmaPacketDomain { /** * @brief Build the packet and transfer data into non-cached buffer using MDMA - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(uint8_t* destination_address) { @@ -166,7 +166,7 @@ struct MdmaPacketDomain { bool done = false; MDMA::transfer_list(build_nodes[0], &done); while (!done) { - // Busy wait + MDMA::update(); } return destination_address ? destination_address : buffer; } @@ -189,15 +189,25 @@ struct MdmaPacketDomain { return build(destination_address); } + /** + * @brief Parse the packet data from non-cached buffer using MDMA + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. + * @param done Optional pointer to a boolean that will be set to true when parsing is done. + */ void parse(uint8_t* data) override { bool done = false; auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, &done); while (!done) { - // Busy wait + MDMA::update(); } } + /** + * @brief Parse the packet data from non-cached buffer using MDMA with a promise + * @param done Pointer to a boolean that will be set to true when parsing is done. + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). + */ void parse(bool* done, uint8_t* data = nullptr) { auto source_node = set_parse_source(data); MDMA::transfer_list(source_node, done); From 7cdea707792b93cf910055fd4a398a4027fa80bc Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 11:06:11 +0100 Subject: [PATCH 156/166] fix(Sd): Use correct callbacks --- Src/ST-LIB_LOW/Sd/Sd.cpp | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp index 178c9021a..9d1425b9b 100644 --- a/Src/ST-LIB_LOW/Sd/Sd.cpp +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -262,27 +262,15 @@ void SDMMC2_IRQHandler(void) { } } -void HAL_SDEx_Read_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { - sd_instance->on_dma_read_complete(); - } -} - -void HAL_SDEx_Read_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { - if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { - sd_instance->on_dma_read_complete(); - } -} - -void HAL_SDEx_Write_DMADoubleBuf0CpltCallback(SD_HandleTypeDef* hsd) { +void HAL_SD_TxCpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { sd_instance->on_dma_write_complete(); } } -void HAL_SDEx_Write_DMADoubleBuf1CpltCallback(SD_HandleTypeDef* hsd) { +void HAL_SD_RxCpltCallback(SD_HandleTypeDef* hsd) { if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { - sd_instance->on_dma_write_complete(); + sd_instance->on_dma_read_complete(); } } From 91ca16f38b0aa33fd7c5420d09e1c34d55128908 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 16:24:55 +0100 Subject: [PATCH 157/166] fix(Sd): Fix the fix of the st-lib --- Inc/ST-LIB_LOW/Sd/Sd.hpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp index ea226a80d..dfb0803f0 100644 --- a/Inc/ST-LIB_LOW/Sd/Sd.hpp +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -49,7 +49,6 @@ struct SdDomain { struct SdCard { using domain = SdDomain; Entry e; - mutable size_t index; Peripheral peripheral; @@ -123,8 +122,8 @@ struct SdDomain { consteval void inscribe(Ctx &ctx) const { Entry local_e = e; - local_e.mpu_buffer0_idx = ctx.template add(buffer0.e); - local_e.mpu_buffer1_idx = ctx.template add(buffer1.e); + local_e.mpu_buffer0_idx = ctx.template add(buffer0.e, this); + local_e.mpu_buffer1_idx = ctx.template add(buffer1.e, this); if (cd.has_value()) { auto& di = cd.value().first; @@ -139,14 +138,14 @@ struct SdDomain { local_e.wp_pin_idx = {ctx.template add(di_entry), wp.value().second}; } - local_e.cmd_pin_idx = ctx.template add(cmd.e); - local_e.ck_pin_idx = ctx.template add(ck.e); - local_e.d0_pin_idx = ctx.template add(d0.e); - local_e.d1_pin_idx = ctx.template add(d1.e); - local_e.d2_pin_idx = ctx.template add(d2.e); - local_e.d3_pin_idx = ctx.template add(d3.e); + local_e.cmd_pin_idx = ctx.template add(cmd.e, this); + local_e.ck_pin_idx = ctx.template add(ck.e, this); + local_e.d0_pin_idx = ctx.template add(d0.e, this); + local_e.d1_pin_idx = ctx.template add(d1.e, this); + local_e.d2_pin_idx = ctx.template add(d2.e, this); + local_e.d3_pin_idx = ctx.template add(d3.e, this); - index = ctx.template add(local_e); + ctx.template add(local_e, this); } }; From d5cafe2d08ee1c8240ae9118f06bd66d9955ce65 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 16:41:22 +0100 Subject: [PATCH 158/166] fix(SdLogger): Fix order in st-lib --- Inc/ST-LIB.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index a90832fbe..e5bf4fc5e 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -128,10 +128,10 @@ template struct Board { ctx.template span()), .mpu_cfgs = MPUDomain::template build( ctx.template span()), + .mdma_packet_cfgs = MdmaPacketDomain::template build( + ctx.template span()), .sd_cfgs = SdDomain::template build( ctx.template span()), - .mdma_packet_cfgs = MdmaPacketDomain::template build( - ctx.template span()) // ... }; } From ee9d0c4d5a5b40f959b257db89a35ece232bb891 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 18 Dec 2025 17:20:09 +0100 Subject: [PATCH 159/166] feat(SdLogger): Add fucntion to get current block address --- Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp index 573b8d618..4808a1e59 100644 --- a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -98,6 +98,10 @@ class SdLogger { } } + uint32_t get_current_block() { + return sd_block_addr; + } + private: bool flush_buffer() { if (sd_write_ongoing) { From d0dd96c486bd7c3abed8da8d2a0c72022d6265bf Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Fri, 19 Dec 2025 16:35:17 +0100 Subject: [PATCH 160/166] feat(SdLogger): Add end-of-log marker when flushing buffer --- Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp index 4808a1e59..2039a4d54 100644 --- a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -76,8 +76,11 @@ class SdLogger { size_t packet_size = packet_sizes[i]; // Don't add more packets if they can't fit (this could be optimized to test if there are other packets smaller that can fit, but that's more cycles, this works) - if (current_buffer_offset + packet_size > current_buffer_size) { + if (current_buffer_offset + packet_size > current_buffer_size - 2) { buffer_flush_pending = true; + + // Put "0xFFFF" end of log marker (as if it were the index, but it obviously won't match any real packet) + *(uint16_t*)(current_buffer_ptr + current_buffer_offset) = 0xFFFF; break; } From 59cac34911b6a445fbb5fb38a6135566918f8c0e Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Tue, 23 Dec 2025 15:04:52 +0100 Subject: [PATCH 161/166] Now it compiles jeje --- Src/HALAL/Models/MDMA/MDMA.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index ce056400b..44b737421 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -2,9 +2,6 @@ #include -uint32_t buffer_size = sizeof(MDMA::LinkedListNode) * NODES_MAX; -MDMA::LinkedListNode* buffer = static_cast(MPUManager::allocate_non_cached_memory(buffer_size)); -inline Pool MDMA::link_node_pool{buffer}; std::bitset<8> MDMA::instance_free_map{}; std::unordered_map MDMA::instance_to_channel = { @@ -148,11 +145,6 @@ void MDMA::start() { __HAL_RCC_MDMA_CLK_ENABLE(); - if (buffer==nullptr) - { - ErrorHandler("Failed to allocate MDMA link node pool buffer"); - } - uint8_t id = 0; for (auto& instance : instances) { From 33bb954f4bd52291f6776ad5e0cb9f79782edf89 Mon Sep 17 00:00:00 2001 From: Foniks Date: Thu, 25 Dec 2025 20:28:50 +0100 Subject: [PATCH 162/166] fix(MPU): Make the MPUManager take it's buffer pointer from a linker symbol, to ensure initialization order doesn't matter --- Inc/HALAL/Models/MPU.hpp | 3 --- Inc/HALAL/Models/MPUManager/MPUManager.hpp | 1 - STM32H723ZGTX_FLASH.ld | 1 + Src/HALAL/Models/MPUManager/MPUManager.cpp | 4 +++- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index 3ed5fbbcc..dfeab1cd4 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -278,9 +278,6 @@ struct MPUDomain { Legacy_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; HAL_MPU_ConfigRegion(&Legacy_InitStruct); - // Set the legacy pointer - MPUManager::no_cached_ram_start = &d3_buffer[0]; - // Adjust bases for new buffers (they start after legacy_size) uint8_t* bases[3] = { &d1_buffer[0], &d2_buffer[0], &d3_buffer[legacy_size] }; diff --git a/Inc/HALAL/Models/MPUManager/MPUManager.hpp b/Inc/HALAL/Models/MPUManager/MPUManager.hpp index 73493fa1b..fd3ba8b49 100644 --- a/Inc/HALAL/Models/MPUManager/MPUManager.hpp +++ b/Inc/HALAL/Models/MPUManager/MPUManager.hpp @@ -6,7 +6,6 @@ #define NO_CACHED_RAM_MAXIMUM_SPACE 2048 class MPUManager{ - friend struct MPUDomain; public: static void* allocate_non_cached_memory(uint32_t size){ void* buffer = (void*)((uint8_t*)no_cached_ram_start + no_cached_ram_occupied_bytes); diff --git a/STM32H723ZGTX_FLASH.ld b/STM32H723ZGTX_FLASH.ld index be8018351..ec60f62a6 100644 --- a/STM32H723ZGTX_FLASH.ld +++ b/STM32H723ZGTX_FLASH.ld @@ -209,6 +209,7 @@ SECTIONS .mpu_ram_d3_nc : { . = ALIGN(32); + __mpu_ram_d3_nc_start = .; /* For the legacy MPUManager */ *(.mpu_ram_d3_nc) . = ALIGN(32); } >RAM_D3 diff --git a/Src/HALAL/Models/MPUManager/MPUManager.cpp b/Src/HALAL/Models/MPUManager/MPUManager.cpp index 99bf036d0..759a853c0 100644 --- a/Src/HALAL/Models/MPUManager/MPUManager.cpp +++ b/Src/HALAL/Models/MPUManager/MPUManager.cpp @@ -1,4 +1,6 @@ #include "HALAL/Models/MPUManager/MPUManager.hpp" -void* MPUManager::no_cached_ram_start = nullptr; +extern "C" uint8_t __mpu_ram_d3_nc_start[]; + +void* MPUManager::no_cached_ram_start = __mpu_ram_d3_nc_start; uint32_t MPUManager::no_cached_ram_occupied_bytes = 0; From c434028125ee714e92f22c286cfa010119605dee Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 25 Dec 2025 21:15:28 +0100 Subject: [PATCH 163/166] style(MdmaPacket): Forgot to rename a @param Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 222354a93..15c6e79b6 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -173,7 +173,7 @@ struct MdmaPacketDomain { /** * @brief Build the packet and transfer data into non-cached buffer using MDMA with a promise - * @param promise Promise to be fulfilled upon transfer completion + * @param done Promise to be fulfilled upon transfer completion * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) * @return Pointer to the built packet data (internal buffer or destination address) */ From 7d67f07bc8b204ae07112035866f0368f9208a57 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 25 Dec 2025 21:16:25 +0100 Subject: [PATCH 164/166] style(MdmaPacket): Fix typo Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Inc/HALAL/Models/Packets/MdmaPacket.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp index 15c6e79b6..7d925dd1b 100644 --- a/Inc/HALAL/Models/Packets/MdmaPacket.hpp +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -158,7 +158,7 @@ struct MdmaPacketDomain { /** * @brief Build the packet and transfer data into non-cached buffer using MDMA - * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here because there's a specific overload without parameters for compliance with Packet interface. * @return Pointer to the built packet data (internal buffer or destination address) */ uint8_t* build(uint8_t* destination_address) { From 228f1655ffe081d8904ca782f0d1f597a7525237 Mon Sep 17 00:00:00 2001 From: Foniks Date: Thu, 25 Dec 2025 22:22:32 +0100 Subject: [PATCH 165/166] fix(SdLogger): Remove unnecessary const_casts --- Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp index 2039a4d54..ab2e95f47 100644 --- a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -88,7 +88,7 @@ class SdLogger { building_mask |= (1UL << i); build_complete_flags[i] = false; - packets[i]->build(const_cast(&build_complete_flags[i]), current_buffer_ptr + current_buffer_offset); + packets[i]->build(&build_complete_flags[i], current_buffer_ptr + current_buffer_offset); current_buffer_offset += packet_size; } } @@ -113,7 +113,7 @@ class SdLogger { sd_write_complete_flag = false; - if (!sd.write_blocks(sd_block_addr, current_buffer_blocks, const_cast(&sd_write_complete_flag))) { + if (!sd.write_blocks(sd_block_addr, current_buffer_blocks, &sd_write_complete_flag)) { return false; } else { sd_write_ongoing = true; From 42a3b997aa06f9323849ec89a700417863b8e219 Mon Sep 17 00:00:00 2001 From: Foniks Date: Thu, 25 Dec 2025 22:24:28 +0100 Subject: [PATCH 166/166] style(MPU): Remove comment --- Inc/HALAL/Models/MPU.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp index dfeab1cd4..8e482bc78 100644 --- a/Inc/HALAL/Models/MPU.hpp +++ b/Inc/HALAL/Models/MPU.hpp @@ -423,7 +423,7 @@ struct MPUDomain { // D3 RAM (Cached) MPU_InitStruct.Enable = MPU_REGION_ENABLE; - MPU_InitStruct.Number = MPU_REGION_NUMBER6; // FIX: Changed from 5 to 6 to avoid collision with D2 NC + MPU_InitStruct.Number = MPU_REGION_NUMBER6; MPU_InitStruct.BaseAddress = 0x38000000; MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; MPU_InitStruct.SubRegionDisable = 0x0;