From 384e0795a26190c5597c98abcaa71d7c102a8eea Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Thu, 6 Nov 2025 12:44:52 +0100 Subject: [PATCH 01/90] 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 02/90] 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 03/90] 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 04/90] 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 05/90] 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 06/90] 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 07/90] 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 08/90] 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 09/90] 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 10/90] 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 11/90] 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 12/90] 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 13/90] 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 14/90] 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 15/90] 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 16/90] 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 17/90] 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 18/90] 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 19/90] 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 20/90] 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 21/90] 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 22/90] 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 23/90] 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 24/90] 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 25/90] 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 26/90] 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 27/90] 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 28/90] 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 29/90] 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 30/90] 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 31/90] 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 32/90] 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 33/90] 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 34/90] 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 35/90] 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 36/90] 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 37/90] 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 38/90] 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 39/90] 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 40/90] 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 41/90] 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 42/90] 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 43/90] 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 44/90] 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 45/90] 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 46/90] 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 47/90] 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 48/90] 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 49/90] 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 50/90] 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 51/90] 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 52/90] 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 53/90] 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 54/90] 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 55/90] 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 56/90] 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 57/90] 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 644c34e00899c142557df5998ea973a3f8a69790 Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Mon, 1 Dec 2025 09:30:05 +0100 Subject: [PATCH 58/90] 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 59/90] 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 60/90] 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 42df50f3534a3648e0038071c1442340cd6f538e Mon Sep 17 00:00:00 2001 From: Boris Mladenov Beslimov Date: Wed, 3 Dec 2025 15:46:39 +0100 Subject: [PATCH 61/90] 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 62/90] 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 25edffc1ea569bd02ca7f5f6cd2f2c3140e9d11c Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 8 Dec 2025 13:27:49 +0100 Subject: [PATCH 63/90] 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 64/90] 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 65/90] 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 66/90] 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 67/90] 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 68/90] 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 922379c1ff55e541ac50a285358020f0744493bb Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 11 Dec 2025 15:43:57 +0100 Subject: [PATCH 69/90] 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 70/90] 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 71/90] 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 76f1df8b08d0f2eef8473aae7ae7e30d264fdcc4 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Sun, 14 Dec 2025 18:36:01 +0100 Subject: [PATCH 72/90] 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 73/90] 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 74/90] 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 75/90] 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 76/90] 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 77/90] 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 78/90] 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 79/90] =?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 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 80/90] 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 66435613b8c5821dc460903913b0b95280c6326b Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Mon, 15 Dec 2025 17:35:15 +0100 Subject: [PATCH 81/90] 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 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 82/90] 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 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 83/90] 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 59cac34911b6a445fbb5fb38a6135566918f8c0e Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Tue, 23 Dec 2025 15:04:52 +0100 Subject: [PATCH 84/90] 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 c219b3b00482a17fded89e1348c42ec93aa7e720 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 16 Jan 2026 19:20:44 +0100 Subject: [PATCH 85/90] Fixed pr --- Inc/C++Utilities/CppUtils.hpp | 2 - Inc/C++Utilities/Pool.hpp | 383 ---------------------------------- Inc/C++Utilities/Stack.hpp | 56 ----- 3 files changed, 441 deletions(-) delete mode 100644 Inc/C++Utilities/Pool.hpp delete mode 100644 Inc/C++Utilities/Stack.hpp diff --git a/Inc/C++Utilities/CppUtils.hpp b/Inc/C++Utilities/CppUtils.hpp index aa421ebe5..809e5f26f 100644 --- a/Inc/C++Utilities/CppUtils.hpp +++ b/Inc/C++Utilities/CppUtils.hpp @@ -2,8 +2,6 @@ #include "CppImports.hpp" #include "RingBuffer.hpp" -#include "Stack.hpp" -#include "Pool.hpp" namespace chrono = std::chrono; diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp deleted file mode 100644 index 933c5d5b6..000000000 --- a/Inc/C++Utilities/Pool.hpp +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Pool.hpp - * - * Created on: 15 nov. 2025 - * Author: Boris - */ - -#ifndef POOL_HPP -#define POOL_HPP - -#include "CppImports.hpp" -#include "Stack.hpp" -#include -#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. - * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. - */ -template -class Pool { - public: - - /** - * @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.top(); - freeIndexes.pop(); - usedBitset.set(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)...); // Placement new - } - 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 (unlikely with proper management). - * @note Could potentially release an element different than the original one if misused (eg. double free). - */ - bool release(T* elem) { - if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { - return false; - } - size_t index = elem - &elements[0]; - if (!usedBitset.test(index)) { - return false; - } - freeIndexes.push(index); - usedBitset.reset(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 (!usedBitset.test(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 Iterator for traversing used elements in the pool. - */ - class Iterator { - public: - Iterator(Pool* pool, size_t index) : pool(pool), index(index) { - // Skip to the first used element - while (index < S && !pool->usedBitset.test(index)) { - ++index; - } - } - - T& operator*() { return pool->elements[index]; } - T* operator->() { return &pool->elements[index]; } - - Iterator& operator++() { - do { - ++index; - } while (index < S && !pool->usedBitset.test(index)); - return *this; - } - - bool operator!=(const Iterator& other) const { - return index != other.index; - } - - private: - Pool* pool; - size_t index; - }; - - /** - * @brief Const iterator for traversing used elements in the pool. - */ - class ConstIterator { - public: - ConstIterator(const Pool* pool, size_t index) : pool(pool), index(index) { - // Skip to the first used element - while (index < S && !pool->usedBitset.test(index)) { - ++index; - } - } - - const T& operator*() const { return pool->elements[index]; } - const T* operator->() const { return &pool->elements[index]; } - - ConstIterator& operator++() { - do { - ++index; - } while (index < S && !pool->usedBitset.test(index)); - return *this; - } - - bool operator!=(const ConstIterator& other) const { - return index != other.index; - } - - private: - const Pool* pool; - 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); } - - 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: - 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; -}; - -// ============================================================================ -// 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). - * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. - */ -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() 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: - 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) -}; - -#endif // POOL_HPP \ No newline at end of file diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp deleted file mode 100644 index e6716a7e2..000000000 --- a/Inc/C++Utilities/Stack.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Stack.hpp - * - * Created on: 15 nov. 2025 - * Author: Boris - */ - -#ifndef STACK_HPP -#define STACK_HPP - -#include "CppImports.hpp" - -/** - * @brief A simple fixed-size stack. - * @tparam T The type of elements stored in the stack. - * @tparam S The maximum number of elements. - */ -template -class Stack { -public: - Stack() : top_idx(0) {} - - bool push(const T& value) { - if (top_idx < S) { - data[top_idx++] = value; - return true; - } - return false; - } - - bool pop() { - if (top_idx == 0) { - return false; - } - top_idx--; - return true; - } - - // Returns the top element without removing it. Returns default T{} if stack is empty. - T top() const { - if (top_idx == 0) { - return T{}; - } - return data[top_idx - 1]; - } - - 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_idx; -}; - -#endif // STACK_HPP \ No newline at end of file From 581867001f3f4406fe6f34dda7f87d0eaba2a6cd Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 16 Jan 2026 20:33:50 +0100 Subject: [PATCH 86/90] AL final stack lo necesito --- Inc/C++Utilities/CppUtils.hpp | 2 - Inc/C++Utilities/Pool.hpp | 383 ---------------------------------- Inc/C++Utilities/Stack.hpp | 56 ----- 3 files changed, 441 deletions(-) delete mode 100644 Inc/C++Utilities/Pool.hpp delete mode 100644 Inc/C++Utilities/Stack.hpp diff --git a/Inc/C++Utilities/CppUtils.hpp b/Inc/C++Utilities/CppUtils.hpp index aa421ebe5..809e5f26f 100644 --- a/Inc/C++Utilities/CppUtils.hpp +++ b/Inc/C++Utilities/CppUtils.hpp @@ -2,8 +2,6 @@ #include "CppImports.hpp" #include "RingBuffer.hpp" -#include "Stack.hpp" -#include "Pool.hpp" namespace chrono = std::chrono; diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp deleted file mode 100644 index 933c5d5b6..000000000 --- a/Inc/C++Utilities/Pool.hpp +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Pool.hpp - * - * Created on: 15 nov. 2025 - * Author: Boris - */ - -#ifndef POOL_HPP -#define POOL_HPP - -#include "CppImports.hpp" -#include "Stack.hpp" -#include -#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. - * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. - */ -template -class Pool { - public: - - /** - * @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.top(); - freeIndexes.pop(); - usedBitset.set(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)...); // Placement new - } - 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 (unlikely with proper management). - * @note Could potentially release an element different than the original one if misused (eg. double free). - */ - bool release(T* elem) { - if (elem < &elements[0] || static_cast(elem - &elements[0]) >= S) { - return false; - } - size_t index = elem - &elements[0]; - if (!usedBitset.test(index)) { - return false; - } - freeIndexes.push(index); - usedBitset.reset(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 (!usedBitset.test(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 Iterator for traversing used elements in the pool. - */ - class Iterator { - public: - Iterator(Pool* pool, size_t index) : pool(pool), index(index) { - // Skip to the first used element - while (index < S && !pool->usedBitset.test(index)) { - ++index; - } - } - - T& operator*() { return pool->elements[index]; } - T* operator->() { return &pool->elements[index]; } - - Iterator& operator++() { - do { - ++index; - } while (index < S && !pool->usedBitset.test(index)); - return *this; - } - - bool operator!=(const Iterator& other) const { - return index != other.index; - } - - private: - Pool* pool; - size_t index; - }; - - /** - * @brief Const iterator for traversing used elements in the pool. - */ - class ConstIterator { - public: - ConstIterator(const Pool* pool, size_t index) : pool(pool), index(index) { - // Skip to the first used element - while (index < S && !pool->usedBitset.test(index)) { - ++index; - } - } - - const T& operator*() const { return pool->elements[index]; } - const T* operator->() const { return &pool->elements[index]; } - - ConstIterator& operator++() { - do { - ++index; - } while (index < S && !pool->usedBitset.test(index)); - return *this; - } - - bool operator!=(const ConstIterator& other) const { - return index != other.index; - } - - private: - const Pool* pool; - 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); } - - 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: - 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; -}; - -// ============================================================================ -// 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). - * @tparam ExternalMemory If true, the pool uses an external buffer provided in the constructor. - */ -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() 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: - 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) -}; - -#endif // POOL_HPP \ No newline at end of file diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp deleted file mode 100644 index e6716a7e2..000000000 --- a/Inc/C++Utilities/Stack.hpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Stack.hpp - * - * Created on: 15 nov. 2025 - * Author: Boris - */ - -#ifndef STACK_HPP -#define STACK_HPP - -#include "CppImports.hpp" - -/** - * @brief A simple fixed-size stack. - * @tparam T The type of elements stored in the stack. - * @tparam S The maximum number of elements. - */ -template -class Stack { -public: - Stack() : top_idx(0) {} - - bool push(const T& value) { - if (top_idx < S) { - data[top_idx++] = value; - return true; - } - return false; - } - - bool pop() { - if (top_idx == 0) { - return false; - } - top_idx--; - return true; - } - - // Returns the top element without removing it. Returns default T{} if stack is empty. - T top() const { - if (top_idx == 0) { - return T{}; - } - return data[top_idx - 1]; - } - - 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_idx; -}; - -#endif // STACK_HPP \ No newline at end of file From 3bb10cb61ceaffc994061411f514930bb5800098 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 16 Jan 2026 20:34:02 +0100 Subject: [PATCH 87/90] Al final stack lo necesito --- Inc/C++Utilities/Stack.hpp | 56 ++++++++++++++++++++++++++++++++++ Inc/HALAL/Models/MDMA/MDMA.hpp | 3 +- 2 files changed, 58 insertions(+), 1 deletion(-) 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..e6716a7e2 --- /dev/null +++ b/Inc/C++Utilities/Stack.hpp @@ -0,0 +1,56 @@ +/* + * Stack.hpp + * + * Created on: 15 nov. 2025 + * Author: Boris + */ + +#ifndef STACK_HPP +#define STACK_HPP + +#include "CppImports.hpp" + +/** + * @brief A simple fixed-size stack. + * @tparam T The type of elements stored in the stack. + * @tparam S The maximum number of elements. + */ +template +class Stack { +public: + Stack() : top_idx(0) {} + + bool push(const T& value) { + if (top_idx < S) { + data[top_idx++] = value; + return true; + } + return false; + } + + bool pop() { + if (top_idx == 0) { + return false; + } + top_idx--; + return true; + } + + // Returns the top element without removing it. Returns default T{} if stack is empty. + T top() const { + if (top_idx == 0) { + return T{}; + } + return data[top_idx - 1]; + } + + 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_idx; +}; + +#endif // STACK_HPP \ No newline at end of file diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 0754c4c72..02c896f54 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef MDMA #undef MDMA @@ -135,7 +136,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 std::stack,50> transfer_queue{}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); From d42756311959b0fb3b10867b4aa39606066d6c91 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Fri, 16 Jan 2026 20:36:02 +0100 Subject: [PATCH 88/90] Que lio de commits, pero solucionado --- Inc/C++Utilities/CppUtils.hpp | 1 + Inc/C++Utilities/Stack.hpp | 56 ++++++++++++++++++++++++++++++++++ Inc/HALAL/Models/MDMA/MDMA.hpp | 3 +- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 Inc/C++Utilities/Stack.hpp diff --git a/Inc/C++Utilities/CppUtils.hpp b/Inc/C++Utilities/CppUtils.hpp index 809e5f26f..23c843600 100644 --- a/Inc/C++Utilities/CppUtils.hpp +++ b/Inc/C++Utilities/CppUtils.hpp @@ -2,6 +2,7 @@ #include "CppImports.hpp" #include "RingBuffer.hpp" +#include "Stack.hpp" namespace chrono = std::chrono; diff --git a/Inc/C++Utilities/Stack.hpp b/Inc/C++Utilities/Stack.hpp new file mode 100644 index 000000000..e6716a7e2 --- /dev/null +++ b/Inc/C++Utilities/Stack.hpp @@ -0,0 +1,56 @@ +/* + * Stack.hpp + * + * Created on: 15 nov. 2025 + * Author: Boris + */ + +#ifndef STACK_HPP +#define STACK_HPP + +#include "CppImports.hpp" + +/** + * @brief A simple fixed-size stack. + * @tparam T The type of elements stored in the stack. + * @tparam S The maximum number of elements. + */ +template +class Stack { +public: + Stack() : top_idx(0) {} + + bool push(const T& value) { + if (top_idx < S) { + data[top_idx++] = value; + return true; + } + return false; + } + + bool pop() { + if (top_idx == 0) { + return false; + } + top_idx--; + return true; + } + + // Returns the top element without removing it. Returns default T{} if stack is empty. + T top() const { + if (top_idx == 0) { + return T{}; + } + return data[top_idx - 1]; + } + + 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_idx; +}; + +#endif // STACK_HPP \ No newline at end of file diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 02c896f54..0754c4c72 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #ifdef MDMA #undef MDMA @@ -136,7 +135,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 std::stack,50> transfer_queue{}; + inline static Stack,50> transfer_queue{}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); From 5b75ec3ffbde8641703c26d1a4881f69d44d7273 Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Wed, 21 Jan 2026 19:06:48 +0100 Subject: [PATCH 89/90] Eliminated useless hashmaps and added type adaptative nodes --- Inc/HALAL/Models/MDMA/MDMA.hpp | 90 ++++++++++++++++++++++++---------- Src/HALAL/Models/MDMA/MDMA.cpp | 57 +++++++++------------ 2 files changed, 89 insertions(+), 58 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 0754c4c72..3aaf1f41a 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -39,26 +39,33 @@ class MDMA{ void set_next(MDMA_LinkNodeTypeDef* next_node) { node.CLAR = reinterpret_cast(next_node); } 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; + uint32_t destination_address = reinterpret_cast(destination); + node.CDAR = destination_address; + + if ((destination_address < 0x00010000) || (destination_address >= 0x20000000 && destination_address < 0x20020000)) { + ErrorHandler("Error: MDMA destination address is inside ITCM or DTCM, which are not accessible."); + return; } - else if (destination_adress >= 0x20000000 && destination_adress < 0x20020000) - { + + if (destination_address >= 0x24000000 && destination_address < 0x24050000) { + node.CTBR &= ~MDMA_CTBR_DBUS; + } else { node.CTBR |= MDMA_CTBR_DBUS; - } + } } void set_source(void* source) { - uint32_t source_adress = reinterpret_cast(source); - node.CSAR = source_adress; + uint32_t source_address = reinterpret_cast(source); + node.CSAR = source_address; - node.CTBR &= ~MDMA_CTBR_SBUS; + if ((source_address < 0x00010000) || (source_address >= 0x20000000 && source_address < 0x20020000)) { + ErrorHandler("Error: MDMA source address is inside ITCM or DTCM, which are not accessible."); + return; + } - if ((source_adress < 0x00010000) || (source_adress >= 0x20000000 && source_adress < 0x20020000)) { - node.CTBR |= MDMA_CTBR_SBUS; + if (source_address >= 0x24000000 && source_address < 0x24050000) { + node.CTBR &= ~MDMA_CTBR_SBUS; + } else { + node.CTBR |= MDMA_CTBR_SBUS; } } auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } @@ -89,10 +96,43 @@ class MDMA{ 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; + + uint32_t source_data_size; + uint32_t dest_data_size; + uint32_t source_inc; + uint32_t dest_inc; + + switch(static_cast(size)) { + case 2: + source_data_size = MDMA_SRC_DATASIZE_HALFWORD; + dest_data_size = MDMA_DEST_DATASIZE_HALFWORD; + source_inc = MDMA_SRC_INC_HALFWORD; + dest_inc = MDMA_DEST_INC_HALFWORD; + break; + case 4: + source_data_size = MDMA_SRC_DATASIZE_WORD; + dest_data_size = MDMA_DEST_DATASIZE_WORD; + source_inc = MDMA_SRC_INC_WORD; + dest_inc = MDMA_DEST_INC_WORD; + break; + case 8: + source_data_size = MDMA_SRC_DATASIZE_DOUBLEWORD; + dest_data_size = MDMA_DEST_DATASIZE_DOUBLEWORD; + source_inc = MDMA_SRC_INC_DOUBLEWORD; + dest_inc = MDMA_DEST_INC_DOUBLEWORD; + break; + default: + source_data_size = MDMA_SRC_DATASIZE_BYTE; + dest_data_size = MDMA_DEST_DATASIZE_BYTE; + source_inc = MDMA_SRC_INC_BYTE; + dest_inc = MDMA_DEST_INC_BYTE; + break; + } + + nodeConfig.Init.SourceDataSize = source_data_size; + nodeConfig.Init.DestDataSize = dest_data_size; + nodeConfig.Init.SourceInc = source_inc; + nodeConfig.Init.DestinationInc = dest_inc; if (HAL_MDMA_LinkedList_CreateNode(&node, &nodeConfig) != HAL_OK) { ErrorHandler("Error creating linked list in MDMA"); @@ -105,7 +145,7 @@ class MDMA{ public: MDMA_HandleTypeDef handle; uint8_t id; - bool* done; + volatile bool* done; MDMA_LinkNodeTypeDef transfer_node; Instance() @@ -117,7 +157,7 @@ class MDMA{ Instance(MDMA_HandleTypeDef handle_, uint8_t id_, - bool* done_, + volatile bool* done_, MDMA_LinkNodeTypeDef transfer_node_) : handle(handle_) , id(id_) @@ -130,12 +170,12 @@ 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); + static MDMA_Channel_TypeDef* get_channel(uint8_t id); + static uint8_t get_instance_id(MDMA_Channel_TypeDef* channel); 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; - inline static Stack,50> transfer_queue{}; + inline static Stack,50> transfer_queue{}; static void TransferCompleteCallback(MDMA_HandleTypeDef *hmdma); static void TransferErrorCallback(MDMA_HandleTypeDef *hmdma); @@ -167,7 +207,7 @@ class MDMA{ * @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 done, false otherwise. */ - static void transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, bool* done=nullptr); + static void transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length,volatile bool* done=nullptr); /** * @brief A method to transfer using MDMA linked @@ -175,6 +215,6 @@ class MDMA{ * @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 done, false otherwise. */ - static void transfer_list(MDMA::LinkedListNode* first_node,bool* check=nullptr); + static void transfer_list(MDMA::LinkedListNode* first_node,volatile bool* check=nullptr); }; diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 44b737421..2cb79e5c2 100644 --- a/Src/HALAL/Models/MDMA/MDMA.cpp +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -4,28 +4,17 @@ std::bitset<8> MDMA::instance_free_map{}; -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} -}; +MDMA_Channel_TypeDef* MDMA::get_channel(uint8_t id) { + if (id > 15) { + return nullptr; + } + return reinterpret_cast(MDMA_BASE + 0x40UL + (id * 0x40UL)); +} + +uint8_t MDMA::get_instance_id(MDMA_Channel_TypeDef* channel) { + uint32_t address = reinterpret_cast(channel); + return static_cast((address - (MDMA_BASE + 0x40UL)) / 0x40UL); +} void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) @@ -85,13 +74,13 @@ MDMA::Instance& MDMA::get_instance(uint8_t id) void MDMA::inscribe(Instance& instance,uint8_t id) { MDMA_HandleTypeDef mdma_handle{}; - const auto channel_it = instance_to_channel.find(id); - if (channel_it == instance_to_channel.end()) + MDMA_Channel_TypeDef* channel = get_channel(id); + if (channel == nullptr) { ErrorHandler("MDMA channel mapping not found"); return; } - mdma_handle.Instance = channel_it->second; + mdma_handle.Instance = channel; mdma_handle.Init.Request = MDMA_REQUEST_SW; mdma_handle.Init.TransferTriggerMode = MDMA_FULL_TRANSFER; mdma_handle.Init.Priority = MDMA_PRIORITY_VERY_HIGH; @@ -208,7 +197,7 @@ void MDMA::irq_handler() } -void MDMA::transfer_list(MDMA::LinkedListNode* first_node, bool* done) +void MDMA::transfer_list(MDMA::LinkedListNode* first_node, volatile bool* done) { if(transfer_queue.size() >= TRANSFER_QUEUE_MAX_SIZE) { @@ -220,7 +209,7 @@ void MDMA::transfer_list(MDMA::LinkedListNode* first_node, bool* done) -void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, bool* done) +void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, const uint32_t data_length, volatile bool* done) { for(size_t i = 0; i < instances.size(); i++) { @@ -247,13 +236,14 @@ void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) { - const auto channel_it = channel_to_instance.find(hmdma->Instance); - if (channel_it == channel_to_instance.end()) + uint8_t id = get_instance_id(hmdma->Instance); + if (id >= instances.size()) { ErrorHandler("MDMA channel not registered"); + return; } - Instance& instance = get_instance(channel_it->second); + Instance& instance = get_instance(id); instance.handle.State = HAL_MDMA_STATE_READY; instance_free_map[instance.id] = true; if(instance.done == nullptr) @@ -267,13 +257,14 @@ void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) { - const auto channel_it = channel_to_instance.find(hmdma->Instance); - if (channel_it == channel_to_instance.end()) + uint8_t id = get_instance_id(hmdma->Instance); + if (id >= instances.size()) { ErrorHandler("MDMA channel not registered"); + return; } - Instance& instance = get_instance(channel_it->second); + Instance& instance = get_instance(id); if(instance.done != nullptr) { *(instance.done) = false; From cda2f9f066bb31a0dac56720e82a2d31a01622af Mon Sep 17 00:00:00 2001 From: Cantonplas Date: Thu, 22 Jan 2026 23:43:58 +0100 Subject: [PATCH 90/90] Made transfer nodes D1_NC --- Inc/HALAL/Models/MDMA/MDMA.hpp | 11 ++++++++--- Src/HALAL/Models/MDMA/MDMA.cpp | 14 ++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 3aaf1f41a..0f5224c59 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/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" #include #include #include @@ -11,6 +12,8 @@ #include #include + + #ifdef MDMA #undef MDMA #endif @@ -141,24 +144,26 @@ class MDMA{ }; private: + static D1_NC MDMA_LinkNodeTypeDef internal_nodes[8]; + struct Instance{ public: MDMA_HandleTypeDef handle; uint8_t id; volatile bool* done; - MDMA_LinkNodeTypeDef transfer_node; + MDMA_LinkNodeTypeDef* transfer_node; Instance() : handle{} , id(0U) , done(nullptr) - , transfer_node{} + , transfer_node(nullptr) {} Instance(MDMA_HandleTypeDef handle_, uint8_t id_, volatile bool* done_, - MDMA_LinkNodeTypeDef transfer_node_) + MDMA_LinkNodeTypeDef* transfer_node_) : handle(handle_) , id(id_) , done(done_) diff --git a/Src/HALAL/Models/MDMA/MDMA.cpp b/Src/HALAL/Models/MDMA/MDMA.cpp index 2cb79e5c2..328a28ca2 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" +D1_NC MDMA_LinkNodeTypeDef MDMA::internal_nodes[8]; + #include std::bitset<8> MDMA::instance_free_map{}; @@ -97,7 +99,7 @@ void MDMA::inscribe(Instance& instance,uint8_t id) mdma_handle.Init.DestBlockAddressOffset = 0; MDMA_LinkNodeConfTypeDef nodeConfig{}; - MDMA_LinkNodeTypeDef transfer_node{}; + MDMA_LinkNodeTypeDef* transfer_node = &internal_nodes[id]; nodeConfig.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE; nodeConfig.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE; @@ -118,7 +120,7 @@ void MDMA::inscribe(Instance& instance,uint8_t id) nodeConfig.SrcAddress = reinterpret_cast(nullptr); nodeConfig.DstAddress = reinterpret_cast(nullptr); - const HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(&transfer_node, &nodeConfig); + const HAL_StatusTypeDef status = HAL_MDMA_LinkedList_CreateNode(transfer_node, &nodeConfig); if (status != HAL_OK) { ErrorHandler("Error creating linked list in MDMA"); @@ -218,11 +220,11 @@ void MDMA::transfer_data(uint8_t* source_address, uint8_t* destination_address, Instance& instance = get_instance(i); 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); + instance.transfer_node->CSAR = reinterpret_cast(source_address); + instance.transfer_node->CBNDTR = data_length; + instance.transfer_node->CDAR = reinterpret_cast(destination_address); - prepare_transfer(instance, &instance.transfer_node); + prepare_transfer(instance, instance.transfer_node); return; } if(done)