diff --git a/Inc/HALAL/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 396f99eb4..8d867efb7 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -44,6 +44,7 @@ #include "HALAL/Models/BoardID/BoardID.hpp" #include "HALAL/Models/Concepts/Concepts.hpp" #include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/Packets/MdmaPacket.hpp" #ifdef STLIB_ETH #include "HALAL/Models/Packets/Packet.hpp" diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp index 0f5224c59..d1e48bf4a 100644 --- a/Inc/HALAL/Models/MDMA/MDMA.hpp +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -50,10 +50,10 @@ class MDMA{ return; } - if (destination_address >= 0x24000000 && destination_address < 0x24050000) { - node.CTBR &= ~MDMA_CTBR_DBUS; - } else { + if (destination_address >= 0x40000000 && destination_address < 0x60000000) { node.CTBR |= MDMA_CTBR_DBUS; + } else { + node.CTBR &= ~MDMA_CTBR_DBUS; } } void set_source(void* source) { @@ -65,10 +65,10 @@ class MDMA{ return; } - if (source_address >= 0x24000000 && source_address < 0x24050000) { - node.CTBR &= ~MDMA_CTBR_SBUS; + if (source_address >= 0x40000000 && source_address < 0x60000000) { + node.CTBR |= MDMA_CTBR_SBUS; } else { - node.CTBR |= MDMA_CTBR_SBUS; + node.CTBR &= ~MDMA_CTBR_SBUS; } } auto get_node() -> MDMA_LinkNodeTypeDef* { return &node; } @@ -105,7 +105,12 @@ class MDMA{ uint32_t source_inc; uint32_t dest_inc; - switch(static_cast(size)) { + size_t effective_size = size; + if (effective_size == 2 && ((reinterpret_cast(src) | reinterpret_cast(dst)) & 1)) effective_size = 1; // Odd address, so fallback to byte-wise + else if (effective_size == 4 && ((reinterpret_cast(src) | reinterpret_cast(dst)) & 3)) effective_size = 1; // Not word-aligned, so fallback to byte-wise + else if (effective_size == 8 && ((reinterpret_cast(src) | reinterpret_cast(dst)) & 7)) effective_size = 1; // Not doubleword-aligned, so fallback to byte-wise + + switch(static_cast(effective_size)) { case 2: source_data_size = MDMA_SRC_DATASIZE_HALFWORD; dest_data_size = MDMA_DEST_DATASIZE_HALFWORD; diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp new file mode 100644 index 000000000..6de61d7e7 --- /dev/null +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -0,0 +1,274 @@ +/* +* MdmaPacket.hpp +* +* Created on: 06 nov. 2025 +* Author: Boris +*/ + +#ifndef MDMA_PACKET_HPP +#define MDMA_PACKET_HPP + +#include "HALAL/Models/Packets/Packet.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" +#include "C++Utilities/CppImports.hpp" + +#ifndef MDMA_PACKET_MAX_INSTANCES +#define MDMA_PACKET_MAX_INSTANCES 50 +#endif + +struct MdmaPacketDomain { + struct Entry { + size_t packet_mpu_index; + size_t nodes_mpu_index; + size_t id_mpu_index; + }; + + struct Config { + size_t packet_mpu_index; + size_t nodes_mpu_index; + size_t id_mpu_index; + }; + + struct Instance { + uint8_t* packet_buffer; + MDMA::LinkedListNode* nodes; + uint16_t* id; + }; + + static constexpr size_t max_instances = MDMA_PACKET_MAX_INSTANCES; + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + for (std::size_t i = 0; i < N; i++) { + cfgs[i].packet_mpu_index = entries[i].packet_mpu_index; + cfgs[i].nodes_mpu_index = entries[i].nodes_mpu_index; + cfgs[i].id_mpu_index = entries[i].id_mpu_index; + } + return cfgs; + } + + template + struct Init { + static inline std::array instances{}; + + template + static void init(const std::array& cfgs, std::array& mpu_instances) { + for (std::size_t i = 0; i < N; i++) { + instances[i].packet_buffer = static_cast(mpu_instances[cfgs[i].packet_mpu_index].ptr); + instances[i].nodes = static_cast(mpu_instances[cfgs[i].nodes_mpu_index].ptr); + instances[i].id = static_cast(mpu_instances[cfgs[i].id_mpu_index].ptr); + } + } + }; + + /** + * @brief A Packet class that uses MDMA for building and parsing packets. + * @tparam Types The types of the values in the packet. + * @note It uses non-cached memory for MDMA operations. + */ + class MdmaPacketBase : public Packet { + public: + virtual uint8_t* build(bool* done, uint8_t* destination_address = nullptr) = 0; + using Packet::build; + }; + + template + class MdmaPacket : public MdmaPacketBase { + public: + uint16_t* id; + uint8_t* buffer; + size_t size; + std::tuple value_pointers; + + MdmaPacket(const MdmaPacket&) = delete; + MdmaPacket& operator=(const MdmaPacket&) = delete; + + struct Request { + using domain = MdmaPacketDomain; + + static constexpr size_t packet_size_bytes = (sizeof(Types) + ...) + sizeof(uint16_t); + static constexpr size_t nodes_size = (2 * (sizeof...(Types) + 1) + 2); + + using PacketMem = std::array; + using NodesMem = std::array; + using IdMem = uint16_t; + + MPUDomain::Buffer packet_req; + MPUDomain::Buffer nodes_req; + MPUDomain::Buffer id_req; + + consteval Request() : + packet_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1), + nodes_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1), + id_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1) + {} + + template + consteval void inscribe(Ctx &ctx) const { + size_t p_idx = packet_req.inscribe(ctx); + size_t n_idx = nodes_req.inscribe(ctx); + size_t i_idx = id_req.inscribe(ctx); + ctx.template add({p_idx, n_idx, i_idx}, this); + } + }; + + MdmaPacket(Instance& instance, uint16_t id, Types*... values) + : id(instance.id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(this->id, values...) { + + *this->id = id; + packets[*this->id] = this; + this->buffer = instance.packet_buffer; + MDMA::LinkedListNode* nodes = instance.nodes; + + MDMA::LinkedListNode* prev_node = nullptr; + uint32_t offset = 0; + uint32_t idx = 0; + size_t node_idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(args, buffer + offset,type_size); // Placement new + build_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + prev_node = nullptr; + offset = 0; + idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer + offset, args, type_size); // Placement new + parse_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + build_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, nullptr, size); + parse_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, buffer, size); + } + + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). It isn't optional here because there's a specific overload without parameters for compliance with Packet interface. + * @return Pointer to the built packet data (internal buffer or destination address) + */ + uint8_t* build(uint8_t* destination_address) { + set_build_destination(destination_address); + bool done = false; + MDMA::transfer_list(build_nodes[0], &done); + while (!done) { + MDMA::update(); + } + return destination_address ? destination_address : buffer; + } + + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA with a promise + * @param done Promise to be fulfilled upon transfer completion + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency) + * @return Pointer to the built packet data (internal buffer or destination address) + */ + uint8_t* build(bool* done, uint8_t* destination_address = nullptr) { + set_build_destination(destination_address); + MDMA::transfer_list(build_nodes[0], done); + return destination_address ? destination_address : buffer; + } + + // Just for interface compliance + uint8_t* build() override { + uint8_t* destination_address = nullptr; + return build(destination_address); + } + + /** + * @brief Parse the packet data from non-cached buffer using MDMA + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). It isn't optional here becasue there's a specific overload without parameters for compliance with Packet interface. + * @param done Optional pointer to a boolean that will be set to true when parsing is done. + */ + void parse(uint8_t* data) override { + bool done = false; + auto source_node = set_parse_source(data); + MDMA::transfer_list(source_node, &done); + while (!done) { + MDMA::update(); + } + } + + /** + * @brief Parse the packet data from non-cached buffer using MDMA with a promise + * @param done Pointer to a boolean that will be set to true when parsing is done. + * @param data Optional source data address to parse from (should be non-cached, else you will need to manage cache coherency). + */ + void parse(bool* done, uint8_t* data = nullptr) { + auto source_node = set_parse_source(data); + MDMA::transfer_list(source_node, done); + } + + size_t get_size() override { + return size; + } + + uint16_t get_id() override { + return *id; + } + + // Just for interface compliance, this is not efficient for MdmaPacket as it is. + // Could be optimized by using a map of index to pointer or similar structure created at compile time, but doesn't seem worthy now. + void set_pointer(size_t index, void* pointer) override { + size_t current_idx = 0; + + std::apply([&](auto&&... args) { + ((current_idx++ == index ? + (args = reinterpret_cast>(pointer)) + : nullptr), ...); + }, value_pointers); + } + + private: + MDMA::LinkedListNode* build_transfer_node; // Node used for destination address + MDMA::LinkedListNode* parse_transfer_node; // Node used for source address + MDMA::LinkedListNode* build_nodes[sizeof...(Types) + 1]; + MDMA::LinkedListNode* parse_nodes[sizeof...(Types) + 1]; + + void set_build_destination(uint8_t* external_buffer) { + if (external_buffer != nullptr) { + build_transfer_node->set_destination(external_buffer); + build_nodes[sizeof...(Types)]->set_next(build_transfer_node->get_node()); + } else { + build_nodes[sizeof...(Types)]->set_next(nullptr); + } + } + + 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/ST-LIB.hpp b/Inc/ST-LIB.hpp index a9acb61f2..3560c5dbf 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -85,7 +85,7 @@ template struct BuildCtx { using DomainsCtx = BuildCtx; + DigitalInputDomain, MdmaPacketDomain /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -106,6 +106,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); // ... struct ConfigBundle { @@ -114,6 +115,7 @@ template struct Board { std::array dout_cfgs; std::array din_cfgs; std::array mpu_cfgs; + std::array mdma_packet_cfgs; // ... }; @@ -127,7 +129,9 @@ template struct Board { .din_cfgs = DigitalInputDomain::template build( ctx.template span()), .mpu_cfgs = MPUDomain::template build( - ctx.template span()) + ctx.template span()), + .mdma_packet_cfgs = MdmaPacketDomain::template build( + ctx.template span()) // ... }; } @@ -140,6 +144,7 @@ template struct Board { constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); @@ -149,6 +154,8 @@ template struct Board { DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); MPUDomain::Init::init(); + MdmaPacketDomain::Init::init(cfg.mdma_packet_cfgs, + MPUDomain::Init::instances); // ... }