diff --git a/.vscode/settings.json b/.vscode/settings.json index b2abce05a..ce7c39b71 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,9 +7,94 @@ "C_Cpp.clang_format_sortIncludes": true, "C_Cpp.intelliSenseCacheSize": 0, "files.associations": { - "mem.h": "c", - "tinydir.h": "c", - "dirent.h": "c" + "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/CMakeLists.txt b/CMakeLists.txt index 037e4f115..c5d15bbcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,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 @@ -75,6 +78,7 @@ if(CMAKE_CROSSCOMPILING) # SD ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd.c ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_sd_ex.c + ${HAL_DRIVER_SRC_DIR}/stm32h7xx_ll_sdmmc.c # CAN / FDCAN ${HAL_DRIVER_SRC_DIR}/stm32h7xx_hal_fdcan.c @@ -222,6 +226,10 @@ add_library(${STLIB_LIBRARY} STATIC $<$,$>:${STLIB_HIGH_CPP_ETH}> $<$:${CMAKE_CURRENT_LIST_DIR}/Src/ST-LIB.cpp> + + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/HALAL/Services/Time/Scheduler.cpp> + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/mocked_ll_tim.cpp> + $<$>:${CMAKE_CURRENT_LIST_DIR}/Src/MockedDrivers/NVIC.cpp> ) set_target_properties(${STLIB_LIBRARY} PROPERTIES 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; diff --git a/Inc/C++Utilities/Pool.hpp b/Inc/C++Utilities/Pool.hpp new file mode 100644 index 000000000..933c5d5b6 --- /dev/null +++ b/Inc/C++Utilities/Pool.hpp @@ -0,0 +1,383 @@ +/* + * 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 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/HALAL.hpp b/Inc/HALAL/HALAL.hpp index 316c9605a..78ccf6c8e 100644 --- a/Inc/HALAL/HALAL.hpp +++ b/Inc/HALAL/HALAL.hpp @@ -36,6 +36,7 @@ #include "HALAL/Services/FMAC/FMAC.hpp" #include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" #include "HALAL/Services/InfoWarning/InfoWarning.hpp" #include "HALAL/Services/Watchdog/Watchdog.hpp" @@ -43,6 +44,8 @@ #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/GPIO.hpp b/Inc/HALAL/Models/GPIO.hpp index c19131e37..e69c94c16 100644 --- a/Inc/HALAL/Models/GPIO.hpp +++ b/Inc/HALAL/Models/GPIO.hpp @@ -184,7 +184,7 @@ struct GPIODomain { } template consteval void inscribe(Ctx &ctx) const { - ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/HALAL/Models/MDMA/MDMA.hpp b/Inc/HALAL/Models/MDMA/MDMA.hpp new file mode 100644 index 000000000..0754c4c72 --- /dev/null +++ b/Inc/HALAL/Models/MDMA/MDMA.hpp @@ -0,0 +1,180 @@ +#pragma once + +#include "C++Utilities/CppUtils.hpp" +#include "stm32h7xx_hal.h" +#include "ErrorHandler/ErrorHandler.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include +#include +#include +#include +#include +#include + +#ifdef MDMA +#undef MDMA +#endif + +#ifndef TRANSFER_QUEUE_MAX_SIZE +#define TRANSFER_QUEUE_MAX_SIZE 50 +#endif +class MDMA{ + public: + /** + * @brief A helper struct to create and manage MDMA linked list nodes. + */ + struct LinkedListNode { + 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) + { + 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; } + auto get_source() -> uint32_t { return node.CSAR; } + auto get_next() -> MDMA_LinkNodeTypeDef* { return reinterpret_cast(node.CLAR); } + +private: + alignas(8) 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 = 128; + 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"); + } + } +}; + + private: + struct Instance{ + public: + MDMA_HandleTypeDef handle; + uint8_t id; + bool* done; + MDMA_LinkNodeTypeDef transfer_node; + + Instance() + : handle{} + , id(0U) + , done(nullptr) + , transfer_node{} + {} + + Instance(MDMA_HandleTypeDef handle_, + uint8_t id_, + bool* done_, + MDMA_LinkNodeTypeDef transfer_node_) + : handle(handle_) + , id(id_) + , done(done_) + , 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 std::bitset<8> instance_free_map; + inline 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: + + static void start(); + static void irq_handler(); + static void update(); + + + + + /** + * @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 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 done, false otherwise. + */ + 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 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/Inc/HALAL/Models/MPU.hpp b/Inc/HALAL/Models/MPU.hpp new file mode 100644 index 000000000..8e482bc78 --- /dev/null +++ b/Inc/HALAL/Models/MPU.hpp @@ -0,0 +1,452 @@ +/* + * MPU.hpp + * + * Created on: 10 dic. 2025 + * Author: Boris + */ + +#ifndef MPU_HPP +#define MPU_HPP + +#ifndef HALAL_MPUBUFFERS_MAX_INSTANCES +#define HALAL_MPUBUFFERS_MAX_INSTANCES 100 +#endif + +#include "C++Utilities/CppUtils.hpp" +#include "C++Utilities/CppImports.hpp" +#include "ErrorHandler/ErrorHandler.hpp" +#include "stm32h7xx_hal.h" +#include "HALAL/Models/MPUManager/MPUManager.hpp" + + +template +concept mpu_buffer_request = requires(typename T::domain d) { + typename T::buffer_type; + { T{ } } -> std::same_as; +}; + +// POD-like buffers that can safely live in static storage without custom destruction. +template +concept mpu_buffer_payload = + std::is_standard_layout_v && std::is_trivially_destructible_v; + +struct MPUDomain { + + enum class MemoryDomain : uint8_t { + D1 = 1, // AXI SRAM (0x24000000) + D2 = 2, // SRAM 1/2/3 (0x30000000) + D3 = 3 // SRAM 4 (0x38000000) + }; + + enum class MemoryType : bool { + Cached = true, + NonCached = false + }; + + // Buffer Request + struct Entry { + MemoryDomain memory_domain; + MemoryType memory_type; + std::size_t alignment; + std::size_t size_in_bytes; + }; + + // Buffer Request Wrapper + template + struct Buffer { + using domain = MPUDomain; + using buffer_type = T; + Entry e; + + /** + * @brief Constructs a Buffer entry for a type T. + * @tparam T The type for which the buffer is requested. Must be a POD type. + * @param type The memory type (Cached or NonCached). + * @param domain The memory domain where the buffer should be allocated. + * @param force_cache_alignment If true, forces the buffer to be cache line aligned (32 bytes, takes the rest as padding). + */ + consteval Buffer(MemoryType type = MemoryType::NonCached, MemoryDomain domain = MemoryDomain::D2, bool force_cache_alignment = false) + : e{ + domain, + type, + force_cache_alignment ? 32 : alignof(T), + sizeof(T)} + { + static_assert(alignof(T) <= 32, "Requested type has alignment greater than cache line size (32 bytes)."); + } + + template + consteval void inscribe(Ctx &ctx) const { + ctx.template add(e, this); + } + }; + + + static constexpr std::size_t max_instances = HALAL_MPUBUFFERS_MAX_INSTANCES; + + struct Config { + uint32_t offset; // Offset relative to the start of the domain buffer + std::size_t size; + MemoryDomain domain; + + // MPU Configuration Data + bool is_mpu_leader; // If true, this entry triggers an MPU region config with the below params + uint8_t mpu_number; // MPU Region Number + uint8_t mpu_size; // Encoded Size (e.g. MPU_REGION_SIZE_256B) + std::size_t mpu_region_size; // Actual size in bytes of the MPU region + uint8_t mpu_subregion; // Subregion Disable Mask + }; + + // Helper to calculate sizes needed for static arrays + struct DomainSizes { + size_t d1_total = 0; + size_t d2_total = 0; + size_t d3_total = 0; + + size_t d1_nc_size = 0; + size_t d2_nc_size = 0; + size_t d3_nc_size = 0; + }; + + static consteval uint32_t align_up(uint32_t val, size_t align) { + return (val + align - 1) & ~(align - 1); // Align up to 'align' leaving the minimum padding + } + + + static consteval DomainSizes calculate_total_sizes(std::span configs) { + DomainSizes sizes; + for (const auto& cfg : configs) { + size_t end = cfg.offset + cfg.size; + if (cfg.domain == MemoryDomain::D1) sizes.d1_total = std::max(sizes.d1_total, end); + else if (cfg.domain == MemoryDomain::D2) sizes.d2_total = std::max(sizes.d2_total, end); + else if (cfg.domain == MemoryDomain::D3) sizes.d3_total = std::max(sizes.d3_total, end); + + if (cfg.is_mpu_leader) { + if (cfg.domain == MemoryDomain::D1) sizes.d1_nc_size = cfg.mpu_region_size; + else if (cfg.domain == MemoryDomain::D2) sizes.d2_nc_size = cfg.mpu_region_size; + else if (cfg.domain == MemoryDomain::D3) sizes.d3_nc_size = cfg.mpu_region_size; + } + } + // Align totals to 32 bytes + sizes.d1_total = align_up(sizes.d1_total, 32); + sizes.d2_total = align_up(sizes.d2_total, 32); + sizes.d3_total = align_up(sizes.d3_total, 32); + return sizes; + } + + template + static consteval std::array build(std::span entries) { + if constexpr (N == 0) { + return {}; + } else { + std::array cfgs{}; + + uint32_t offsets[3] = {}; // D1, D2, D3 + uint32_t assigned_offsets[N]; + + size_t alignments[] = {32, 16, 8, 4, 2, 1}; + + /* Non-Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::NonCached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } + } + } + + // Capture Non-Cached Sizes for MPU and adjust offsets for Cached data + size_t nc_sizes[3]; + for(int i=0; i<3; i++) { + if (offsets[i] > 0) { + auto [r_size, r_sub] = get_size_needed(offsets[i]); + nc_sizes[i] = offsets[i]; + + // Move the offset pointer to the end of the MPU region block to have Cached data start after it + offsets[i] = r_size / 8 * (8 - std::popcount(r_sub)); // Effective used size considering subregions disabled + offsets[i] = align_up(offsets[i], 32); // Align to 32 bytes just in case + } else { + nc_sizes[i] = 0; + } + } + + /* Cached Offsets */ + for (size_t align : alignments) { + for (size_t i = 0; i < N; i++) { + if (entries[i].memory_type == MemoryType::Cached && entries[i].alignment == align) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + offsets[d_idx] = align_up(offsets[d_idx], align); + assigned_offsets[i] = offsets[d_idx]; + offsets[d_idx] += entries[i].size_in_bytes; + } + } + } + + /* Build Configs */ + bool domain_configured[3] = {false, false, false}; + + for (std::size_t i = 0; i < N; i++) { + cfgs[i].size = entries[i].size_in_bytes; + cfgs[i].domain = entries[i].memory_domain; + cfgs[i].offset = assigned_offsets[i]; + cfgs[i].is_mpu_leader = false; + cfgs[i].mpu_region_size = 0; + + if (entries[i].memory_type == MemoryType::NonCached) { + size_t d_idx = static_cast(entries[i].memory_domain) - 1; + if (!domain_configured[d_idx]) { + // This entry is the "Leader" responsible for configuring the MPU region for the whole domain + cfgs[i].is_mpu_leader = true; + domain_configured[d_idx] = true; + + auto [r_size, r_sub] = get_size_needed(nc_sizes[d_idx]); + cfgs[i].mpu_size = get_region_size_encoding(r_size); + cfgs[i].mpu_subregion = r_sub; + cfgs[i].mpu_region_size = r_size; // Store for Init alignment + cfgs[i].mpu_number = (d_idx == 0) ? MPU_REGION_NUMBER3 : + (d_idx == 1) ? MPU_REGION_NUMBER5 : MPU_REGION_NUMBER7; + } + } + } + + return cfgs; + } + } + + struct Instance { + void* ptr; + std::size_t size; + + template + auto& construct(Args&&... args) { + using T = typename std::remove_cvref_t::buffer_type; + return *new (ptr) T(std::forward(args)...); + } + + template + auto* as() { + using T = typename std::remove_cvref_t::buffer_type; + return static_cast(ptr); + } + }; + + + template cfgs> + struct Init { + static inline std::array instances{}; + + // Calculate sizes at compile time from the template parameter + static constexpr auto Sizes = calculate_total_sizes(cfgs); + + // --- Actual Storage (Placed by Linker) --- + // These sections must be defined in the Linker Script. + // They will be placed automatically, avoiding conflicts with other data. + // Alignment must match the MPU region size for Non-Cached areas. + static constexpr size_t d1_align = Sizes.d1_nc_size > 0 ? Sizes.d1_nc_size : 32; + static constexpr size_t d2_align = Sizes.d2_nc_size > 0 ? Sizes.d2_nc_size : 32; + static constexpr size_t d3_align = Sizes.d3_nc_size > 0 ? Sizes.d3_nc_size : 32; + static constexpr size_t legacy_size = NO_CACHED_RAM_MAXIMUM_SPACE; + + // Always reserves at least 1 byte to avoid zero-length arrays, could possibly be optimized further, but this is only 3B overhead... + __attribute__((section(".mpu_ram_d1_nc"))) alignas(d1_align) + static inline uint8_t d1_buffer[Sizes.d1_total > 0 ? Sizes.d1_total : 1]; + __attribute__((section(".mpu_ram_d2_nc"))) alignas(d2_align) + static inline uint8_t d2_buffer[Sizes.d2_total > 0 ? Sizes.d2_total : 1]; + __attribute__((section(".mpu_ram_d3_nc"))) alignas(std::max(d3_align, legacy_size)) + static inline uint8_t d3_buffer[(Sizes.d3_total > 0 ? Sizes.d3_total : 0) + legacy_size]; + + static void init() { + HAL_MPU_Disable(); + configure_static_regions(); + + // Configure Legacy MPUManager Region (D3 Non-Cached) + // We place it at the beginning of d3_buffer + MPU_Region_InitTypeDef Legacy_InitStruct = {0}; + Legacy_InitStruct.Enable = MPU_REGION_ENABLE; + Legacy_InitStruct.Number = MPU_REGION_NUMBER9; // Use a high region number to override D3 Cached + Legacy_InitStruct.BaseAddress = (uint32_t)&d3_buffer[0]; + Legacy_InitStruct.Size = get_region_size_encoding(legacy_size); + Legacy_InitStruct.SubRegionDisable = 0x0; + Legacy_InitStruct.TypeExtField = MPU_TEX_LEVEL1; + Legacy_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + Legacy_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + Legacy_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + Legacy_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + Legacy_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&Legacy_InitStruct); + + // Adjust bases for new buffers (they start after legacy_size) + uint8_t* bases[3] = { &d1_buffer[0], &d2_buffer[0], &d3_buffer[legacy_size] }; + + for (std::size_t i = 0; i < N; i++) { + const auto &cfg = cfgs[i]; + auto &inst = instances[i]; + + if (cfg.domain == MemoryDomain::D1 || cfg.domain == MemoryDomain::D2 || cfg.domain == MemoryDomain::D3) { + size_t d_idx = static_cast(cfg.domain) - 1; + // Calculate absolute address: Base + Offset + inst.ptr = bases[d_idx] + cfg.offset; + inst.size = cfg.size; + + if (cfg.is_mpu_leader) { + MPU_Region_InitTypeDef init = {0}; + init.Enable = MPU_REGION_ENABLE; + init.Number = cfg.mpu_number; + init.BaseAddress = (uint32_t)bases[d_idx]; // Base of the whole buffer + init.Size = cfg.mpu_size; + init.SubRegionDisable = cfg.mpu_subregion; + init.TypeExtField = MPU_TEX_LEVEL1; // Normal, Non-Cached + init.AccessPermission = MPU_REGION_FULL_ACCESS; + init.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + init.IsShareable = MPU_ACCESS_SHAREABLE; + init.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + init.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&init); + } + } + } + + HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); + SCB_EnableICache(); + SCB_EnableDCache(); + } + }; + + private: + static consteval std::pair get_size_needed(std::size_t size) { + if (size == 0) return {32, 0xFF}; + size_t power = std::bit_width(size); + if (power < 5) power = 5; // Min 32B + size_t subregion_size = 1U << (power - 3); + size_t num_subregions = (size + subregion_size - 1) / subregion_size; + uint8_t subregion_disable = static_cast(~((1U << num_subregions) - 1)); + return {(1U << power), subregion_disable}; + } + + static consteval uint8_t get_region_size_encoding(std::size_t size) { + if (size <= 32) return MPU_REGION_SIZE_32B; + if (size <= 64) return MPU_REGION_SIZE_64B; + if (size <= 128) return MPU_REGION_SIZE_128B; + if (size <= 256) return MPU_REGION_SIZE_256B; + if (size <= 512) return MPU_REGION_SIZE_512B; + if (size <= 1024) return MPU_REGION_SIZE_1KB; + if (size <= 2048) return MPU_REGION_SIZE_2KB; + if (size <= 4096) return MPU_REGION_SIZE_4KB; + if (size <= 8192) return MPU_REGION_SIZE_8KB; + if (size <= 16384) return MPU_REGION_SIZE_16KB; + if (size <= 32768) return MPU_REGION_SIZE_32KB; + if (size <= 65536) return MPU_REGION_SIZE_64KB; + if (size <= 131072) return MPU_REGION_SIZE_128KB; + if (size <= 262144) return MPU_REGION_SIZE_256KB; + if (size <= 524288) return MPU_REGION_SIZE_512KB; + if (size <= 1048576) return MPU_REGION_SIZE_1MB; + if (size <= 2097152) return MPU_REGION_SIZE_2MB; + if (size <= 4194304) return MPU_REGION_SIZE_4MB; + return MPU_REGION_SIZE_4GB; + } + + static void configure_static_regions() { + MPU_Region_InitTypeDef MPU_InitStruct = {0}; + + // Background (No Access) - Covers all memory not explicitly defined by a further region + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER0; + MPU_InitStruct.BaseAddress = 0x00; + MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; + MPU_InitStruct.SubRegionDisable = 0x04; // Disable 512MB at 0x40000000 (Peripheral space) + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; + MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // Flash (Non-cached, Executable) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER1; + MPU_InitStruct.BaseAddress = 0x08000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_1MB; + MPU_InitStruct.SubRegionDisable = 0x00; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; // This should be scrutinized to see why it was non-cacheable before changing to cacheable + MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; // This should be scrutinized to see why it was non-bufferable before changing to bufferable + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D1 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER2; + MPU_InitStruct.BaseAddress = 0x24000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_512KB; + MPU_InitStruct.SubRegionDisable = 0xE0; // Only 320KB available + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D2 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER4; + MPU_InitStruct.BaseAddress = 0x30000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // Ethernet Descriptors (D2 Base) - Legacy, should change Ethernet driver to use MPU buffers + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER8; + MPU_InitStruct.BaseAddress = 0x30000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_512B; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; // Device + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + // D3 RAM (Cached) + MPU_InitStruct.Enable = MPU_REGION_ENABLE; + MPU_InitStruct.Number = MPU_REGION_NUMBER6; + MPU_InitStruct.BaseAddress = 0x38000000; + MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; + MPU_InitStruct.SubRegionDisable = 0x0; + MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; + MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; + MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; + MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; + MPU_InitStruct.IsCacheable = MPU_ACCESS_CACHEABLE; + MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; + HAL_MPU_ConfigRegion(&MPU_InitStruct); + + /** + * Other regions are: + * 3. D1 RAM (Non-Cached) - Configured dynamically + * 5. D2 RAM (Non-Cached) - Configured dynamically + * 7. D3 RAM (Non-Cached) - Configured dynamically + */ + + /** + * Other memory areas (not configured explicitly): + * - Peripheral space (0x40000000 - 0x5FFFFFFF): Defaults to device memory + */ + } +}; + +#endif // MPU_HPP diff --git a/Inc/HALAL/Models/MPUManager/MPUManager.hpp b/Inc/HALAL/Models/MPUManager/MPUManager.hpp index 9b63f46e2..fd3ba8b49 100644 --- a/Inc/HALAL/Models/MPUManager/MPUManager.hpp +++ b/Inc/HALAL/Models/MPUManager/MPUManager.hpp @@ -5,75 +5,8 @@ #define NO_CACHED_RAM_MAXIMUM_SPACE 2048 -extern unsigned long _no_cached_ram_start; - - class MPUManager{ public: - static struct config{ - bool using_cache = true; - }MPUConfig;/**< MPU configuration defined un Runes.hpp*/ - - static void start(){ - MPU_Region_InitTypeDef MPU_InitStruct = {0}; - HAL_MPU_Disable(); - - MPU_InitStruct.Enable = MPU_REGION_ENABLE; - MPU_InitStruct.Number = MPU_REGION_NUMBER0; - MPU_InitStruct.BaseAddress = 0x0; - MPU_InitStruct.Size = MPU_REGION_SIZE_4GB; - MPU_InitStruct.SubRegionDisable = 0x87; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.AccessPermission = MPU_REGION_NO_ACCESS; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE; - MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; - MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER1; - MPU_InitStruct.BaseAddress = 0x30000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_32KB; - MPU_InitStruct.SubRegionDisable = 0x0; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER2; - MPU_InitStruct.Size = MPU_REGION_SIZE_512B; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0; - MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /** Initializes and configures the Region and the memory to be protected - */ - MPU_InitStruct.Number = MPU_REGION_NUMBER3; - MPU_InitStruct.BaseAddress = 0x08000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_1MB; - MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE; - MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - - MPU_InitStruct.Number = MPU_REGION_NUMBER4; - MPU_InitStruct.BaseAddress = 0x38000000; - MPU_InitStruct.Size = MPU_REGION_SIZE_16KB; - MPU_InitStruct.SubRegionDisable = 0x0; - MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1; - MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS; - MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE; - HAL_MPU_ConfigRegion(&MPU_InitStruct); - /* Enables the MPU */ - HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); - - if(MPUConfig.using_cache){ - SCB_EnableICache(); - SCB_EnableDCache(); - } - } - static void* allocate_non_cached_memory(uint32_t size){ void* buffer = (void*)((uint8_t*)no_cached_ram_start + no_cached_ram_occupied_bytes); no_cached_ram_occupied_bytes = no_cached_ram_occupied_bytes + size; diff --git a/Inc/HALAL/Models/Packets/MdmaPacket.hpp b/Inc/HALAL/Models/Packets/MdmaPacket.hpp new file mode 100644 index 000000000..7d925dd1b --- /dev/null +++ b/Inc/HALAL/Models/Packets/MdmaPacket.hpp @@ -0,0 +1,264 @@ +/* +* MdmaPacket.hpp +* +* Created on: 06 nov. 2025 +* Author: Boris +*/ + +#ifndef MDMA_PACKET_HPP +#define MDMA_PACKET_HPP + +#include "HALAL/Models/Packets/Packet.hpp" +#include "HALAL/Models/MDMA/MDMA.hpp" +#include "HALAL/Models/MPUManager/MPUManager.hpp" +#include "HALAL/Models/MPU.hpp" +#include "C++Utilities/CppImports.hpp" + +#ifndef MDMA_PACKET_MAX_INSTANCES +#define MDMA_PACKET_MAX_INSTANCES 50 +#endif + +struct MdmaPacketDomain { + struct Entry { + size_t packet_mpu_index; + size_t nodes_mpu_index; + }; + + struct Config { + size_t packet_mpu_index; + size_t nodes_mpu_index; + }; + + struct Instance { + uint8_t* packet_buffer; + MDMA::LinkedListNode* nodes; + }; + + static constexpr size_t max_instances = MDMA_PACKET_MAX_INSTANCES; + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + for (std::size_t i = 0; i < N; i++) { + cfgs[i].packet_mpu_index = entries[i].packet_mpu_index; + cfgs[i].nodes_mpu_index = entries[i].nodes_mpu_index; + } + return cfgs; + } + + template + struct Init { + static inline std::array instances{}; + + template + static void init(const std::array& cfgs, std::array& mpu_instances) { + for (std::size_t i = 0; i < N; i++) { + instances[i].packet_buffer = static_cast(mpu_instances[cfgs[i].packet_mpu_index].ptr); + instances[i].nodes = static_cast(mpu_instances[cfgs[i].nodes_mpu_index].ptr); + } + } + }; + + /** + * @brief A Packet class that uses MDMA for building and parsing packets. + * @tparam Types The types of the values in the packet. + * @note It uses non-cached memory for MDMA operations. + */ + 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; + + MPUDomain::Buffer packet_req; + MPUDomain::Buffer nodes_req; + + consteval Request() : + packet_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1), + nodes_req(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1) + {} + + template + consteval void inscribe(Ctx &ctx) const { + size_t p_idx = ctx.template add(packet_req.e, this); + size_t n_idx = ctx.template add(nodes_req.e, this); + ctx.template add({p_idx, n_idx}, this); + } + }; + + MdmaPacket(Instance& instance, uint16_t id, Types*... values) + : id(id), size((sizeof(Types) + ...) + sizeof(uint16_t)) , value_pointers(&this->id, values...) { + + packets[id] = this; + this->buffer = instance.packet_buffer; + MDMA::LinkedListNode* nodes = instance.nodes; + + MDMA::LinkedListNode* prev_node = nullptr; + uint32_t offset = 0; + uint32_t idx = 0; + size_t node_idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(args, buffer + offset,type_size); // Placement new + build_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + prev_node = nullptr; + offset = 0; + idx = 0; + + std::apply([&](auto&&... args) { (([&]() { + using PointerType = std::decay_t; + using UnderlyingType = std::remove_pointer_t; + + constexpr size_t type_size = sizeof(UnderlyingType); + MDMA::LinkedListNode* node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer + offset, args, type_size); // Placement new + parse_nodes[idx++] = node; + offset += type_size; + + if (prev_node != nullptr) { + prev_node->set_next(node->get_node()); + } + prev_node = node; + }()), ...); }, value_pointers); + + build_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, nullptr, size); + parse_transfer_node = new (&nodes[node_idx++]) MDMA::LinkedListNode(buffer, buffer, size); + } + + /** + * @brief Build the packet and transfer data into non-cached buffer using MDMA + * @param destination_address Optional destination address for the built packet (should be non-cached, else you will need to manage cache coherency). 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/MockedDrivers/hal_gpio_interface.h b/Inc/MockedDrivers/hal_gpio_interface.h new file mode 100644 index 000000000..579b6ad45 --- /dev/null +++ b/Inc/MockedDrivers/hal_gpio_interface.h @@ -0,0 +1,388 @@ +/** + ****************************************************************************** + * @file stm32h7xx_hal_gpio.h + * @author MCD Application Team + * @brief Header file of GPIO HAL module. + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef STM32H7xx_HAL_GPIO_H +#define STM32H7xx_HAL_GPIO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32h7xx_hal_def.h" + +/** @addtogroup STM32H7xx_HAL_Driver + * @{ + */ + +/** @addtogroup GPIO + * @{ + */ + +/* Exported types ------------------------------------------------------------*/ +/** @defgroup GPIO_Exported_Types GPIO Exported Types + * @{ + */ + +/** + * @brief GPIO Init structure definition + */ +typedef struct { + uint32_t Pin; /*!< Specifies the GPIO pins to be configured. + This parameter can be any value of @ref GPIO_pins_define */ + + uint32_t Mode; /*!< Specifies the operating mode for the selected pins. + This parameter can be a value of @ref GPIO_mode_define */ + + uint32_t + Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected + pins. This parameter can be a value of @ref GPIO_pull_define */ + + uint32_t + Speed; /*!< Specifies the speed for the selected pins. + This parameter can be a value of @ref GPIO_speed_define */ + + uint32_t Alternate; /*!< Peripheral to be connected to the selected pins. + This parameter can be a value of @ref + GPIO_Alternate_function_selection */ +} GPIO_InitTypeDef; + +/** + * @brief GPIO Bit SET and Bit RESET enumeration + */ +typedef enum { GPIO_PIN_RESET = 0U, GPIO_PIN_SET } GPIO_PinState; +/** + * @} + */ + +/* Exported constants --------------------------------------------------------*/ + +/** @defgroup GPIO_Exported_Constants GPIO Exported Constants + * @{ + */ + +/** @defgroup GPIO_pins_define GPIO pins define + * @{ + */ +#define GPIO_PIN_0 ((uint16_t)0x0001) /* Pin 0 selected */ +#define GPIO_PIN_1 ((uint16_t)0x0002) /* Pin 1 selected */ +#define GPIO_PIN_2 ((uint16_t)0x0004) /* Pin 2 selected */ +#define GPIO_PIN_3 ((uint16_t)0x0008) /* Pin 3 selected */ +#define GPIO_PIN_4 ((uint16_t)0x0010) /* Pin 4 selected */ +#define GPIO_PIN_5 ((uint16_t)0x0020) /* Pin 5 selected */ +#define GPIO_PIN_6 ((uint16_t)0x0040) /* Pin 6 selected */ +#define GPIO_PIN_7 ((uint16_t)0x0080) /* Pin 7 selected */ +#define GPIO_PIN_8 ((uint16_t)0x0100) /* Pin 8 selected */ +#define GPIO_PIN_9 ((uint16_t)0x0200) /* Pin 9 selected */ +#define GPIO_PIN_10 ((uint16_t)0x0400) /* Pin 10 selected */ +#define GPIO_PIN_11 ((uint16_t)0x0800) /* Pin 11 selected */ +#define GPIO_PIN_12 ((uint16_t)0x1000) /* Pin 12 selected */ +#define GPIO_PIN_13 ((uint16_t)0x2000) /* Pin 13 selected */ +#define GPIO_PIN_14 ((uint16_t)0x4000) /* Pin 14 selected */ +#define GPIO_PIN_15 ((uint16_t)0x8000) /* Pin 15 selected */ +#define GPIO_PIN_All ((uint16_t)0xFFFF) /* All pins selected */ + +#define GPIO_PIN_MASK (0x0000FFFFU) /* PIN mask for assert test */ +/** + * @} + */ + +/** @defgroup GPIO_mode_define GPIO mode define + * @brief GPIO Configuration Mode + * Elements values convention: 0x00WX00YZ + * - W : EXTI trigger detection on 3 bits + * - X : EXTI mode (IT or Event) on 2 bits + * - Y : Output type (Push Pull or Open Drain) on 1 bit + * - Z : GPIO mode (Input, Output, Alternate or Analog) on 2 bits + * @{ + */ +#define GPIO_MODE_INPUT \ + MODE_INPUT /*!< Input Floating Mode */ +#define GPIO_MODE_OUTPUT_PP \ + (MODE_OUTPUT | OUTPUT_PP) /*!< Output Push Pull Mode */ +#define GPIO_MODE_OUTPUT_OD \ + (MODE_OUTPUT | OUTPUT_OD) /*!< Output Open Drain Mode */ +#define GPIO_MODE_AF_PP \ + (MODE_AF | OUTPUT_PP) /*!< Alternate Function Push Pull Mode */ +#define GPIO_MODE_AF_OD \ + (MODE_AF | OUTPUT_OD) /*!< Alternate Function Open Drain Mode */ +#define GPIO_MODE_ANALOG \ + MODE_ANALOG /*!< Analog Mode */ +#define GPIO_MODE_IT_RISING \ + (MODE_INPUT | EXTI_IT | TRIGGER_RISING) /*!< External Interrupt Mode with \ + Rising edge trigger detection */ +#define GPIO_MODE_IT_FALLING \ + (MODE_INPUT | EXTI_IT | \ + TRIGGER_FALLING) /*!< External Interrupt Mode with Falling edge trigger \ + detection */ +#define GPIO_MODE_IT_RISING_FALLING \ + (MODE_INPUT | EXTI_IT | TRIGGER_RISING | \ + TRIGGER_FALLING) /*!< External Interrupt Mode with Rising/Falling edge \ + trigger detection */ + +#define GPIO_MODE_EVT_RISING \ + (MODE_INPUT | EXTI_EVT | TRIGGER_RISING) /*!< External Event Mode with \ + Rising edge trigger detection */ +#define GPIO_MODE_EVT_FALLING \ + (MODE_INPUT | EXTI_EVT | \ + TRIGGER_FALLING) /*!< External Event Mode with Falling edge trigger \ + detection */ +#define GPIO_MODE_EVT_RISING_FALLING \ + (MODE_INPUT | EXTI_EVT | TRIGGER_RISING | \ + TRIGGER_FALLING) /*!< External Event Mode with Rising/Falling edge trigger \ + detection */ +/** + * @} + */ + +/** @defgroup GPIO_speed_define GPIO speed define + * @brief GPIO Output Maximum frequency + * @{ + */ +#define GPIO_SPEED_FREQ_LOW (0x00000000U) /*!< Low speed */ +#define GPIO_SPEED_FREQ_MEDIUM (0x00000001U) /*!< Medium speed */ +#define GPIO_SPEED_FREQ_HIGH (0x00000002U) /*!< Fast speed */ +#define GPIO_SPEED_FREQ_VERY_HIGH (0x00000003U) /*!< High speed */ +/** + * @} + */ + +/** @defgroup GPIO_pull_define GPIO pull define + * @brief GPIO Pull-Up or Pull-Down Activation + * @{ + */ +#define GPIO_NOPULL (0x00000000U) /*!< No Pull-up or Pull-down activation */ +#define GPIO_PULLUP (0x00000001U) /*!< Pull-up activation */ +#define GPIO_PULLDOWN (0x00000002U) /*!< Pull-down activation */ +/** + * @} + */ + +/** + * @} + */ + +/* Exported macro ------------------------------------------------------------*/ +/** @defgroup GPIO_Exported_Macros GPIO Exported Macros + * @{ + */ + +/** + * @brief Checks whether the specified EXTI line flag is set or not. + * @param __EXTI_LINE__: specifies the EXTI line flag to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTI_GET_FLAG(__EXTI_LINE__) (EXTI->PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending flags. + * @param __EXTI_LINE__: specifies the EXTI lines flags to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_CLEAR_FLAG(__EXTI_LINE__) (EXTI->PR1 = (__EXTI_LINE__)) + +/** + * @brief Checks whether the specified EXTI line is asserted or not. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTI_GET_IT(__EXTI_LINE__) (EXTI->PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending bits. + * @param __EXTI_LINE__: specifies the EXTI lines to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_CLEAR_IT(__EXTI_LINE__) (EXTI->PR1 = (__EXTI_LINE__)) + +#if defined(DUAL_CORE) +/** + * @brief Checks whether the specified EXTI line flag is set or not. + * @param __EXTI_LINE__: specifies the EXTI line flag to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTID2_GET_FLAG(__EXTI_LINE__) \ + (EXTI->C2PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending flags. + * @param __EXTI_LINE__: specifies the EXTI lines flags to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTID2_CLEAR_FLAG(__EXTI_LINE__) \ + (EXTI->C2PR1 = (__EXTI_LINE__)) + +/** + * @brief Checks whether the specified EXTI line is asserted or not. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval The new state of __EXTI_LINE__ (SET or RESET). + */ +#define __HAL_GPIO_EXTID2_GET_IT(__EXTI_LINE__) (EXTI->C2PR1 & (__EXTI_LINE__)) + +/** + * @brief Clears the EXTI's line pending bits. + * @param __EXTI_LINE__: specifies the EXTI lines to clear. + * This parameter can be any combination of GPIO_PIN_x where x can be + * (0..15) + * @retval None + */ +#define __HAL_GPIO_EXTID2_CLEAR_IT(__EXTI_LINE__) \ + (EXTI->C2PR1 = (__EXTI_LINE__)) +#endif + +/** + * @brief Generates a Software interrupt on selected EXTI line. + * @param __EXTI_LINE__: specifies the EXTI line to check. + * This parameter can be GPIO_PIN_x where x can be(0..15) + * @retval None + */ +#define __HAL_GPIO_EXTI_GENERATE_SWIT(__EXTI_LINE__) \ + (EXTI->SWIER1 |= (__EXTI_LINE__)) +/** + * @} + */ + +/* Include GPIO HAL Extension module */ +#include "stm32h7xx_hal_gpio_ex.h" + +/* Exported functions --------------------------------------------------------*/ +/** @addtogroup GPIO_Exported_Functions + * @{ + */ + +/** @addtogroup GPIO_Exported_Functions_Group1 + * @{ + */ +/* Initialization and de-initialization functions *****************************/ +void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, const GPIO_InitTypeDef *GPIO_Init); +void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin); +/** + * @} + */ + +/** @addtogroup GPIO_Exported_Functions_Group2 + * @{ + */ +/* IO operation functions *****************************************************/ +GPIO_PinState HAL_GPIO_ReadPin(const GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, + GPIO_PinState PinState); +void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin); +void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin); +void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin); + +/** + * @} + */ + +/** + * @} + */ +/* Private types -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +/* Private constants ---------------------------------------------------------*/ +/** @defgroup GPIO_Private_Constants GPIO Private Constants + * @{ + */ +#define GPIO_MODE_Pos 0u +#define GPIO_MODE (0x3uL << GPIO_MODE_Pos) +#define MODE_INPUT (0x0uL << GPIO_MODE_Pos) +#define MODE_OUTPUT (0x1uL << GPIO_MODE_Pos) +#define MODE_AF (0x2uL << GPIO_MODE_Pos) +#define MODE_ANALOG (0x3uL << GPIO_MODE_Pos) +#define OUTPUT_TYPE_Pos 4u +#define OUTPUT_TYPE (0x1uL << OUTPUT_TYPE_Pos) +#define OUTPUT_PP (0x0uL << OUTPUT_TYPE_Pos) +#define OUTPUT_OD (0x1uL << OUTPUT_TYPE_Pos) +#define EXTI_MODE_Pos 16u +#define EXTI_MODE (0x3uL << EXTI_MODE_Pos) +#define EXTI_IT (0x1uL << EXTI_MODE_Pos) +#define EXTI_EVT (0x2uL << EXTI_MODE_Pos) +#define TRIGGER_MODE_Pos 20u +#define TRIGGER_MODE (0x7uL << TRIGGER_MODE_Pos) +#define TRIGGER_RISING (0x1uL << TRIGGER_MODE_Pos) +#define TRIGGER_FALLING (0x2uL << TRIGGER_MODE_Pos) +#define TRIGGER_LEVEL (0x4uL << TRIGGER_MODE_Pos) +/** + * @} + */ + +/* Private macros ------------------------------------------------------------*/ +/** @defgroup GPIO_Private_Macros GPIO Private Macros + * @{ + */ +#define IS_GPIO_PIN_ACTION(ACTION) \ + (((ACTION) == GPIO_PIN_RESET) || ((ACTION) == GPIO_PIN_SET)) +#define IS_GPIO_PIN(__PIN__) \ + ((((uint32_t)(__PIN__) & GPIO_PIN_MASK) != 0x00U) && \ + (((uint32_t)(__PIN__) & ~GPIO_PIN_MASK) == 0x00U)) +#define IS_GPIO_MODE(MODE) \ + (((MODE) == GPIO_MODE_INPUT) || ((MODE) == GPIO_MODE_OUTPUT_PP) || \ + ((MODE) == GPIO_MODE_OUTPUT_OD) || ((MODE) == GPIO_MODE_AF_PP) || \ + ((MODE) == GPIO_MODE_AF_OD) || ((MODE) == GPIO_MODE_IT_RISING) || \ + ((MODE) == GPIO_MODE_IT_FALLING) || \ + ((MODE) == GPIO_MODE_IT_RISING_FALLING) || \ + ((MODE) == GPIO_MODE_EVT_RISING) || ((MODE) == GPIO_MODE_EVT_FALLING) || \ + ((MODE) == GPIO_MODE_EVT_RISING_FALLING) || ((MODE) == GPIO_MODE_ANALOG)) +#define IS_GPIO_SPEED(SPEED) \ + (((SPEED) == GPIO_SPEED_FREQ_LOW) || ((SPEED) == GPIO_SPEED_FREQ_MEDIUM) || \ + ((SPEED) == GPIO_SPEED_FREQ_HIGH) || \ + ((SPEED) == GPIO_SPEED_FREQ_VERY_HIGH)) + +#define IS_GPIO_PULL(PULL) \ + (((PULL) == GPIO_NOPULL) || ((PULL) == GPIO_PULLUP) || \ + ((PULL) == GPIO_PULLDOWN)) + +/** + * @} + */ + +/* Private functions ---------------------------------------------------------*/ +/** @defgroup GPIO_Private_Functions GPIO Private Functions + * @{ + */ + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* STM32H7xx_HAL_GPIO_H */ diff --git a/Inc/ST-LIB.hpp b/Inc/ST-LIB.hpp index 32fb54814..e5bf4fc5e 100644 --- a/Inc/ST-LIB.hpp +++ b/Inc/ST-LIB.hpp @@ -32,6 +32,7 @@ template struct BuildCtx { static constexpr std::size_t max_count_v = D::max_instances; std::tuple, max_count_v>...> storage{}; + std::tuple>...> owners{}; std::array sizes{}; template @@ -47,12 +48,17 @@ template struct BuildCtx { } } - template consteval std::size_t add(typename D::Entry e) { + template + consteval std::size_t add(typename D::Entry e, const Owner *owner) { constexpr std::size_t I = domain_index(); auto &arr = std::get(storage); + auto &own = std::get(owners); auto &size = sizes[I]; + const auto idx = size; - arr[size++] = e; + arr[size] = e; + own[size] = owner; + ++size; return idx; } @@ -64,6 +70,13 @@ template struct BuildCtx { return std::span{arr.data(), size}; } + template consteval auto owners_span() const { + constexpr std::size_t I = domain_index(); + auto const &arr = std::get(owners); + auto const size = sizes[I]; + return std::span{arr.data(), size}; + } + template consteval std::size_t size() const { constexpr std::size_t I = domain_index(); return sizes[I]; @@ -71,7 +84,8 @@ template struct BuildCtx { }; using DomainsCtx = BuildCtx; + DigitalInputDomain, MPUDomain, MdmaPacketDomain, SdDomain + /*, ADCDomain, PWMDomain, ...*/>; template struct Board { static consteval auto build_ctx() { @@ -90,12 +104,18 @@ template struct Board { constexpr std::size_t gpioN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); + constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... struct ConfigBundle { std::array gpio_cfgs; std::array dout_cfgs; std::array din_cfgs; + std::array mpu_cfgs; + std::array mdma_packet_cfgs; + std::array sd_cfgs; // ... }; @@ -106,6 +126,12 @@ template struct Board { ctx.template span()), .din_cfgs = DigitalInputDomain::template build( ctx.template span()), + .mpu_cfgs = MPUDomain::template build( + ctx.template span()), + .mdma_packet_cfgs = MdmaPacketDomain::template build( + ctx.template span()), + .sd_cfgs = SdDomain::template build( + ctx.template span()), // ... }; } @@ -116,6 +142,9 @@ template struct Board { constexpr std::size_t gpioN = domain_size(); constexpr std::size_t doutN = domain_size(); constexpr std::size_t dinN = domain_size(); + constexpr std::size_t mpuN = domain_size(); + constexpr std::size_t mdmaPacketN = domain_size(); + constexpr std::size_t sdN = domain_size(); // ... GPIODomain::Init::init(cfg.gpio_cfgs); @@ -123,54 +152,40 @@ template struct Board { GPIODomain::Init::instances); DigitalInputDomain::Init::init(cfg.din_cfgs, GPIODomain::Init::instances); + MPUDomain::Init::init(); + MdmaPacketDomain::Init::init(cfg.mdma_packet_cfgs, + MPUDomain::Init::instances); + SdDomain::Init::init(cfg.sd_cfgs, + MPUDomain::Init::instances, + DigitalInputDomain::Init::instances); // ... } - template - static consteval std::size_t domain_size_for_instance() { - return domain_size(); - } - template - static consteval std::size_t domain_index_of_impl() { - std::size_t idx = 0; - bool found = false; - - ( - [&] { - using DevT = std::remove_cvref_t; - if constexpr (std::is_same_v) { - if (!found) { - if (&devs == &Target) { - found = true; - } else { - ++idx; - } - } - } - }(), - ...); - - if (!found) { - compile_error("Device not found for domain"); - } + static consteval std::size_t owner_index_of() { + constexpr auto owners = ctx.template owners_span(); - return idx; - } - - template - static consteval std::size_t domain_index_of() { - return domain_index_of_impl(); + if constexpr (I >= owners.size()) { + compile_error("Device not registered in domain"); + return 0; + } else { + return owners[I] == &Target ? I : owner_index_of(); + } } template static auto &instance_of() { using DevT = std::remove_cvref_t; using Domain = typename DevT::domain; - constexpr std::size_t idx = domain_index_of(); - constexpr std::size_t N = domain_size_for_instance(); + constexpr std::size_t idx = owner_index_of(); - return Domain::template Init::instances[idx]; + constexpr std::size_t N = domain_size(); + + if constexpr (std::is_same_v) { + return Domain::template Init::instances[idx]; + } else { + return Domain::template Init::instances[idx]; + } } }; diff --git a/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp b/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp index 392bbee9c..894f395d3 100644 --- a/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp +++ b/Inc/ST-LIB_HIGH/ST-LIB_HIGH.hpp @@ -22,6 +22,7 @@ #include "Control/Blocks/MatrixMultiplier.hpp" #include "Control/Blocks/MeanCalculator.hpp" #include "Control/ControlSystem.hpp" +#include "SdLogger/SdLogger.hpp" #ifdef SIM_ON #else #include "FlashStorer/FlashStorer.hpp" diff --git a/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp new file mode 100644 index 000000000..ab2e95f47 --- /dev/null +++ b/Inc/ST-LIB_HIGH/SdLogger/SdLogger.hpp @@ -0,0 +1,148 @@ +/* + * SdLogger.hpp + * + * Created on: 16 dec. 2025 + * Author: Boris + */ + +#ifndef SDLOGGER_HPP +#define SDLOGGER_HPP + +#include "ST-LIB_LOW/ST-LIB_LOW.hpp" +#include "HALAL/HALAL.hpp" + +template +class SdLogger { + public: + static constexpr size_t MAX_PACKETS = 32; + + SdLogger(SdWrapper& sd_card) : sd(sd_card) { + sd.init_card(); + current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); + current_buffer_size = sd.get_current_buffer()->size() * 4; // uint32_t is 4B + current_buffer_blocks = current_buffer_size / 512; + } + + uint8_t add_packet(MdmaPacketDomain::MdmaPacketBase* packet) { + if (packet_count >= MAX_PACKETS) { + ErrorHandler("Max packets reached in SdLogger"); + return 0; + } + packets[packet_count] = packet; + packet_sizes[packet_count] = packet->get_size(); // Cache size to avoid virtual call in update + packet_count++; + return packet_count - 1; + } + + void log(uint16_t packet_index) { + request_mask |= (1UL << packet_index); + } + + void update() { + /* Sd update logic */ + if (sd_write_ongoing) { + if (sd_write_complete_flag) { + sd_write_ongoing = false; + sd_write_complete_flag = false; + } + } + + /* Packets building update logic */ + if (building_mask) { + uint32_t temp_mask = building_mask; + while (temp_mask) { // Iterate over the packets being build and check if they've been built already + uint32_t i = __builtin_ctz(temp_mask); + if (build_complete_flags[i]) { + building_mask &= ~(1UL << i); + build_complete_flags[i] = false; + } + temp_mask &= ~(1UL << i); + } + } + + /* Buffer management update logic */ + + // Normal buffer operation + if (!buffer_flush_pending) { + uint32_t temp_mask = request_mask; + while (temp_mask) { // Iterate over all requested to be built packets + uint32_t i = __builtin_ctz(temp_mask); + temp_mask &= ~(1UL << i); + + // If it is already being built, don't add it once more (your program is kinda slow then though, should reconsider what you're doing) + if (building_mask & (1UL << i)) { + continue; + } + + size_t packet_size = packet_sizes[i]; + // Don't add more packets if they can't fit (this could be optimized to test if there are other packets smaller that can fit, but that's more cycles, this works) + if (current_buffer_offset + packet_size > current_buffer_size - 2) { + buffer_flush_pending = true; + + // Put "0xFFFF" end of log marker (as if it were the index, but it obviously won't match any real packet) + *(uint16_t*)(current_buffer_ptr + current_buffer_offset) = 0xFFFF; + break; + } + + request_mask &= ~(1UL << i); + building_mask |= (1UL << i); + build_complete_flags[i] = false; + + packets[i]->build(&build_complete_flags[i], current_buffer_ptr + current_buffer_offset); + current_buffer_offset += packet_size; + } + } + + // Have to flush and all packets have been built so we can flush + if (buffer_flush_pending && building_mask == 0) { + if (flush_buffer()) { + buffer_flush_pending = false; + } + } + } + + uint32_t get_current_block() { + return sd_block_addr; + } + + private: + bool flush_buffer() { + if (sd_write_ongoing) { + return false; + } + + sd_write_complete_flag = false; + + if (!sd.write_blocks(sd_block_addr, current_buffer_blocks, &sd_write_complete_flag)) { + return false; + } else { + sd_write_ongoing = true; + sd_block_addr += current_buffer_blocks; + current_buffer_ptr = reinterpret_cast(sd.get_current_buffer()->data()); + current_buffer_offset = 0; + return true; + } + } + + SdWrapper& sd; + std::array packets; + size_t packet_sizes[MAX_PACKETS]; + uint8_t packet_count = 0; + uint32_t request_mask = 0; + + bool sd_write_ongoing = false; + bool sd_write_complete_flag = false; + + uint32_t building_mask = 0; + volatile bool build_complete_flags[MAX_PACKETS]; + + uint8_t* current_buffer_ptr = nullptr; + uint32_t current_buffer_size = 0; + uint32_t current_buffer_blocks = 0; + uint32_t current_buffer_offset = 0; + + bool buffer_flush_pending = false; + uint32_t sd_block_addr = 0; +}; + +#endif // SDLOGGER_HPP \ No newline at end of file diff --git a/Inc/ST-LIB_LOW/DigitalInput2.hpp b/Inc/ST-LIB_LOW/DigitalInput2.hpp index e161e39a5..4dec87413 100644 --- a/Inc/ST-LIB_LOW/DigitalInput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalInput2.hpp @@ -19,9 +19,9 @@ struct DigitalInputDomain { : gpio{pin, GPIODomain::OperationMode::INPUT, pull, speed} {} template consteval void inscribe(Ctx &ctx) const { - const auto gpio_idx = ctx.template add(gpio.e); + const auto gpio_idx = ctx.template add(gpio.e, &gpio); Entry e{.gpio_idx = gpio_idx}; - ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/ST-LIB_LOW/DigitalOutput2.hpp b/Inc/ST-LIB_LOW/DigitalOutput2.hpp index 7dcad5b65..8c99b9f24 100644 --- a/Inc/ST-LIB_LOW/DigitalOutput2.hpp +++ b/Inc/ST-LIB_LOW/DigitalOutput2.hpp @@ -27,9 +27,9 @@ struct DigitalOutputDomain { } template consteval void inscribe(Ctx &ctx) const { - const auto gpio_idx = ctx.template add(gpio.e); + const auto gpio_idx = ctx.template add(gpio.e, &gpio); Entry e{.gpio_idx = gpio_idx}; - ctx.template add(e); + ctx.template add(e, this); } }; diff --git a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp index b73165c24..bc25ac7ec 100644 --- a/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp +++ b/Inc/ST-LIB_LOW/ST-LIB_LOW.hpp @@ -3,6 +3,8 @@ #include "ST-LIB_LOW/DigitalOutput2.hpp" #include "ST-LIB_LOW/DigitalInput2.hpp" +#include "ST-LIB_LOW/Sd/Sd.hpp" + #include "Clocks/Counter.hpp" #include "Clocks/Stopwatch.hpp" #include "Sensors/LinearSensor/LinearSensor.hpp" diff --git a/Inc/ST-LIB_LOW/Sd/Sd.hpp b/Inc/ST-LIB_LOW/Sd/Sd.hpp new file mode 100644 index 000000000..dfb0803f0 --- /dev/null +++ b/Inc/ST-LIB_LOW/Sd/Sd.hpp @@ -0,0 +1,446 @@ +/* + * Sd.hpp + * + * Created on: 13 dec. 2025 + * Author: Boris + */ + +#ifndef SD_HPP +#define SD_HPP + +#include "HALAL/HALAL.hpp" +#include "ST-LIB_LOW/DigitalInput2.hpp" +#include "stm32h7xx_hal.h" +#include "ErrorHandler/ErrorHandler.hpp" + +using ST_LIB::DigitalInputDomain; +using ST_LIB::GPIODomain; + +extern SD_HandleTypeDef* g_sdmmc1_handle; +extern SD_HandleTypeDef* g_sdmmc2_handle; +extern void* g_sdmmc1_instance_ptr; +extern void* g_sdmmc2_instance_ptr; + +namespace ST_LIB { +struct SdDomain { + + enum class Peripheral : uint32_t { + sdmmc1 = SDMMC1_BASE, + sdmmc2 = SDMMC2_BASE, + }; + + + struct Entry { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional> cd_pin_idx; // Card Detect pin index in GPIO domain, if any + std::optional> wp_pin_idx; // Write Protect pin index in GPIO domain, if any + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; // Hardcoded unless SDMMC1 + std::size_t d1_pin_idx; // Hardcoded unless SDMMC1 + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + struct SdCard { + using domain = SdDomain; + Entry e; + + Peripheral peripheral; + + MPUDomain::Buffer> buffer0; // Alignment of 32-bit for SDMMC DMA + MPUDomain::Buffer> buffer1; // Alignment of 32-bit for SDMMC DMA + + std::optional> cd; // Card Detect, if any, and its active state + std::optional> wp; // Write Protect, if any, and its active state + + GPIODomain::GPIO cmd; + GPIODomain::GPIO ck; + GPIODomain::GPIO d0; + GPIODomain::GPIO d1; + GPIODomain::GPIO d2; + GPIODomain::GPIO d3; + + /** + * @brief Construct a new SdCard + * @tparam buffer_blocks Number of 512-byte blocks for the MPU buffer + * @param sdmmc_peripheral The SDMMC peripheral to use (Peripheral::sdmmc1 or Peripheral::sdmmc2) + * @param card_detect_config Optional Card Detect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none + * @param write_protect_config Optional Write Protect pin (DigitalInputDomain::DigitalInput) and its active state, or nullopt for none + * @param d0_pin_for_sdmmc1 D0 pin to use if using SDMMC1 (default PC8, can also be PB13). + * @note The other pins (CMD, CK, D1, D2, D3) are fixed for each peripheral. + */ + consteval SdCard(Peripheral sdmmc_peripheral, + std::optional> card_detect_config, std::optional> write_protect_config, + GPIODomain::Pin d0_pin_for_sdmmc1 = ST_LIB::PC8) : + e{.peripheral = sdmmc_peripheral}, + peripheral(sdmmc_peripheral), + buffer0(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + buffer1(MPUDomain::Buffer>(MPUDomain::MemoryType::NonCached, MPUDomain::MemoryDomain::D1)), + cd(card_detect_config), + wp(write_protect_config), + cmd((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PD2, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD7, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), + ck((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PD6, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF11)), + d0((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(d0_pin_for_sdmmc1, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB14, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), + d1((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC9, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PB15, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF9)), + d2((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC10, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)), + d3((sdmmc_peripheral == Peripheral::sdmmc1) ? + GPIODomain::GPIO(ST_LIB::PC11, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF12) : + GPIODomain::GPIO(ST_LIB::PG12, GPIODomain::OperationMode::ALT_PP, GPIODomain::Pull::None, GPIODomain::Speed::VeryHigh, GPIODomain::AlternateFunction::AF10)) + { + if (sdmmc_peripheral != Peripheral::sdmmc1 && sdmmc_peripheral != Peripheral::sdmmc2) { + ST_LIB::compile_error("Invalid SDMMC peripheral"); + } + if ((d0_pin_for_sdmmc1.pin != ST_LIB::PC8.pin) && (d0_pin_for_sdmmc1.port != ST_LIB::PC8.port) + && + (d0_pin_for_sdmmc1.pin != ST_LIB::PB13.pin) && (d0_pin_for_sdmmc1.port != ST_LIB::PB13.port)) { + ST_LIB::compile_error("D0 pin can only be PC8 or PB13 for SDMMC1"); + } + if (buffer_blocks == 0) { + ST_LIB::compile_error("Buffer blocks must be greater than 0"); + } else if (buffer_blocks > 15) { + ST_LIB::compile_error("Buffer blocks must be less than or equal to 15"); + } + } + + + template + consteval void inscribe(Ctx &ctx) const { + Entry local_e = e; + + local_e.mpu_buffer0_idx = ctx.template add(buffer0.e, this); + local_e.mpu_buffer1_idx = ctx.template add(buffer1.e, this); + + if (cd.has_value()) { + auto& di = cd.value().first; + auto gpio_idx = ctx.template add(di.gpio.e); + DigitalInputDomain::Entry di_entry{gpio_idx}; + local_e.cd_pin_idx = {ctx.template add(di_entry), cd.value().second}; + } + if (wp.has_value()) { + auto& di = wp.value().first; + auto gpio_idx = ctx.template add(di.gpio.e); + DigitalInputDomain::Entry di_entry{gpio_idx}; + local_e.wp_pin_idx = {ctx.template add(di_entry), wp.value().second}; + } + + local_e.cmd_pin_idx = ctx.template add(cmd.e, this); + local_e.ck_pin_idx = ctx.template add(ck.e, this); + local_e.d0_pin_idx = ctx.template add(d0.e, this); + local_e.d1_pin_idx = ctx.template add(d1.e, this); + local_e.d2_pin_idx = ctx.template add(d2.e, this); + local_e.d3_pin_idx = ctx.template add(d3.e, this); + + ctx.template add(local_e, this); + } + }; + + + static constexpr std::size_t max_instances = 2; + + + struct Config { + Peripheral peripheral; + std::size_t mpu_buffer0_idx; + std::size_t mpu_buffer1_idx; + std::optional> cd_pin_idx; + std::optional> wp_pin_idx; + std::size_t cmd_pin_idx; + std::size_t ck_pin_idx; + std::size_t d0_pin_idx; + std::size_t d1_pin_idx; + std::size_t d2_pin_idx; + std::size_t d3_pin_idx; + }; + + + template + static consteval std::array build(std::span entries) { + std::array cfgs{}; + if (N == 0 ) { + return cfgs; + } + bool peripheral_used[2] = {false, false}; // SDMMC1, SDMMC2 + + for (std::size_t i = 0; i < N; i++) { + const Entry &e = entries[i]; + + // Verify uniqueness of peripheral usage + std::size_t peripheral_index = (e.peripheral == Peripheral::sdmmc1) ? 0 : 1; + if (peripheral_used[peripheral_index]) ST_LIB::compile_error("SDMMC peripheral used multiple times in SdDomain"); + peripheral_used[peripheral_index] = true; + + cfgs[i].peripheral = e.peripheral; + cfgs[i].mpu_buffer0_idx = e.mpu_buffer0_idx; + cfgs[i].mpu_buffer1_idx = e.mpu_buffer1_idx; + cfgs[i].cd_pin_idx = e.cd_pin_idx; + cfgs[i].wp_pin_idx = e.wp_pin_idx; + cfgs[i].cmd_pin_idx = e.cmd_pin_idx; + cfgs[i].ck_pin_idx = e.ck_pin_idx; + cfgs[i].d0_pin_idx = e.d0_pin_idx; + cfgs[i].d1_pin_idx = e.d1_pin_idx; + cfgs[i].d2_pin_idx = e.d2_pin_idx; + cfgs[i].d3_pin_idx = e.d3_pin_idx; + } + + return cfgs; + } + + enum class BufferSelect : bool { + Buffer0 = false, + Buffer1 = true + }; + + template struct SdCardWrapper; + template struct Init; + + // State holder, logic is in SdCardWrapper + struct Instance { + template friend struct SdCardWrapper; + template friend struct Init; + + bool* operation_flag = nullptr; // External flag to indicate that an operation has finished. Only public so that it can be set in the public handlers below. + + // Public handlers called from C HAL callbacks. Don't use them, don't even think about using them. + void on_dma_read_complete(); + void on_dma_write_complete(); + void on_abort(); + void on_error(); + + private: + SD_HandleTypeDef hsd; + + MPUDomain::Instance* mpu_buffer0_instance; + MPUDomain::Instance* mpu_buffer1_instance; + + std::optional> cd_instance; + std::optional> wp_instance; + + bool card_initialized; + BufferSelect current_buffer; // The one that is currently available for CPU access and not being used by IDMA + + HAL_SD_CardInfoTypeDef card_info; + + // Functions + bool is_card_present(); + bool is_write_protected(); + bool is_busy(); + bool initialize_card(); + bool deinitialize_card(); + void switch_buffer(); + bool configure_idma(); + // Variation of HAL_SDEx_ReadBlocksDMAMultiBuffer to fit our needs + HAL_StatusTypeDef Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); + HAL_StatusTypeDef Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks); + }; + + template + struct SdCardWrapper{ + static constexpr bool has_cd = card_request.cd.has_value(); + static constexpr bool has_wp = card_request.wp.has_value(); + + SdCardWrapper(Instance& instance) : instance(instance) { + check_cd_wp(); + }; + + void init_card() { + check_cd_wp(); + bool success = instance.initialize_card(); + if (!success) { + ErrorHandler("SD Card initialization failed"); + } + } + + void deinit_card() { + check_cd_wp(); + bool success = instance.deinitialize_card(); + if (!success) { + ErrorHandler("SD Card deinitialization failed"); + } + } + + bool is_card_initialized() { + return instance.card_initialized; + } + + bool read_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to write in SD"); + } + + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer + } + + // Won't use HAL_SDEx_ReadBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card read operation failed"); + } + + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; + + return true; + } + + bool write_blocks(uint32_t start_block, uint32_t num_blocks, bool* operation_complete_flag) { + check_cd_wp(); + if (!instance.card_initialized) { + ErrorHandler("SD Card not initialized"); + } + if (num_blocks > instance.mpu_buffer0_instance->size / 512) { + ErrorHandler("Too many blocks requested to write in SD"); + } + + if (HAL_SD_GetCardState(&instance.hsd) != HAL_SD_CARD_TRANSFER) { + return false; // Card not ready for data transfer + } + // Won't use HAL_SDEx_WriteBlocksDMAMultiBuffer because it doesn't support double buffering the way we want + HAL_StatusTypeDef status = instance.Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(start_block, num_blocks); + + if (status != HAL_OK) { + ErrorHandler("SD Card write operation failed"); + } + + instance.operation_flag = operation_complete_flag; + *operation_complete_flag = false; + + return true; + } + + auto* get_current_buffer() { + if (instance.current_buffer == BufferSelect::Buffer0) { + return instance.mpu_buffer0_instance->template as(); + } else { + return instance.mpu_buffer1_instance->template as(); + } + } + + bool is_busy() { + return instance.is_busy(); + } + + private: + Instance& instance; // Actual State + + void check_cd_wp() { + if constexpr (has_cd) { + if (!instance.is_card_present()) { ErrorHandler("SD Card not present"); } + } + if constexpr (has_wp) { + if (instance.is_write_protected()) { ErrorHandler("SD Card is write-protected"); } + } + } + }; + + + template + struct Init { + static inline std::array instances{}; + + static void init(std::span cfgs, + std::span mpu_buffer_instances, + std::span digital_input_instances) { + + if (N == 0) { + return; + } + + for (std::size_t i = 0; i < N; i++) { + const auto &cfg = cfgs[i]; + auto &inst = instances[i]; + + inst.mpu_buffer0_instance = &mpu_buffer_instances[cfg.mpu_buffer0_idx]; + inst.mpu_buffer1_instance = &mpu_buffer_instances[cfg.mpu_buffer1_idx]; + + if (cfg.cd_pin_idx.has_value()) { + inst.cd_instance = {&digital_input_instances[cfg.cd_pin_idx.value().first], cfg.cd_pin_idx.value().second}; + } + if (cfg.wp_pin_idx.has_value()) { + inst.wp_instance = {&digital_input_instances[cfg.wp_pin_idx.value().first], cfg.wp_pin_idx.value().second}; + } + + if (cfg.peripheral == Peripheral::sdmmc1) { + inst.hsd.Instance = SDMMC1; + } else if (cfg.peripheral == Peripheral::sdmmc2) { + inst.hsd.Instance = SDMMC2; + } + inst.hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_FALLING; + inst.hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_4B; + inst.hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + inst.hsd.Init.ClockDiv = 2; + + + #ifdef SD_DEBUG_ENABLE + // Doesn't really work in this moment, need to actually get the PLL1 frequency somehow + inst.hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; + // Get a 400 kHz clock for debugging + //uint32_t pll1_freq = HAL_RCCEx_GetPLL1ClockFreq(); // SDMMC clock source is PLL1 + uint32_t pll1_freq = 480000000; // Assume PLL1 is at 480 MHz + uint32_t sdmmc_clk = pll1_freq / 2; // SDMMC clock before divider + uint32_t target_div = sdmmc_clk / 400000; // Target divider + if (target_div < 2) target_div = 2; // Minimum divider is 2 + if (target_div > 256) target_div = 256; // Maximum divider is 256 + inst.hsd.Init.ClockDiv = target_div - 2; // ClockDiv is (divider - 2) + #endif // SD_DEBUG_ENABLE + + if (cfg.peripheral == Peripheral::sdmmc1) { + g_sdmmc1_handle = &inst.hsd; + g_sdmmc1_instance_ptr = &inst; + } else if (cfg.peripheral == Peripheral::sdmmc2) { + g_sdmmc2_handle = &inst.hsd; + g_sdmmc2_instance_ptr = &inst; + } + + inst.card_initialized = false; + inst.current_buffer = BufferSelect::Buffer0; + } + + // Initialize HAL SD + RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct; + RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC; + RCC_PeriphCLKInitStruct.SdmmcClockSelection = RCC_SDMMCCLKSOURCE_PLL; + if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) { + ErrorHandler("SDMMC clock configuration failed, maybe try with a slower clock or higher divider?"); + } + + // Ensure PLL1Q output is enabled + __HAL_RCC_PLLCLKOUT_ENABLE(RCC_PLL1_DIVQ); + + if (HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC) == 0) { + ErrorHandler("SDMMC clock frequency is 0"); + } + } + }; +}; + +static inline SdDomain::Instance* get_sd_instance(SD_HandleTypeDef* hsd) { + if (hsd == g_sdmmc1_handle) return static_cast(g_sdmmc1_instance_ptr); + if (hsd == g_sdmmc2_handle) return static_cast(g_sdmmc2_instance_ptr); + return nullptr; +} + +} // namespace ST_LIB + +#endif // SD_HPP \ No newline at end of file diff --git a/STM32H723ZGTX_FLASH.ld b/STM32H723ZGTX_FLASH.ld index 12b8d7793..ec60f62a6 100644 --- a/STM32H723ZGTX_FLASH.ld +++ b/STM32H723ZGTX_FLASH.ld @@ -170,6 +170,13 @@ SECTIONS __bss_end__ = _ebss; } >RAM_D1 + .mpu_ram_d1_nc : + { + . = ALIGN(32); + *(.mpu_ram_d1_nc) + . = ALIGN(32); + } >RAM_D1 + /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { @@ -191,12 +198,22 @@ SECTIONS *(.TxDecripSection) } >RAM_D2 - .stlib_no_cache_ram_pool : + + .mpu_ram_d2_nc : + { + . = ALIGN(32); + *(.mpu_ram_d2_nc) + . = ALIGN(32); + } >RAM_D2 + + .mpu_ram_d3_nc : { - . = ABSOLUTE(0x38000000); - _no_cached_ram_start = .; - + . = ALIGN(32); + __mpu_ram_d3_nc_start = .; /* For the legacy MPUManager */ + *(.mpu_ram_d3_nc) + . = ALIGN(32); } >RAM_D3 + /* Remove information from the standard libraries */ /DISCARD/ : { diff --git a/STM32H723ZGTX_RAM.ld b/STM32H723ZGTX_RAM.ld index 4d2e09c43..fb9f8fae9 100644 --- a/STM32H723ZGTX_RAM.ld +++ b/STM32H723ZGTX_RAM.ld @@ -119,6 +119,13 @@ SECTIONS PROVIDE_HIDDEN (__fini_array_end = .); } >RAM_EXEC + .mpu_ram_d1_nc : + { + . = ALIGN(32); + *(.mpu_ram_d1_nc) + . = ALIGN(32); + } >RAM_EXEC + /* used by the startup to initialize data */ _sidata = LOADADDR(.data); @@ -161,6 +168,20 @@ SECTIONS . = ALIGN(8); } >DTCMRAM + .mpu_ram_d2_nc : + { + . = ALIGN(32); + *(.mpu_ram_d2_nc) + . = ALIGN(32); + } >RAM_D2 + + .mpu_ram_d3_nc : + { + . = ALIGN(32); + *(.mpu_ram_d3_nc) + . = ALIGN(32); + } >RAM_D3 + /* Remove information from the standard libraries */ /DISCARD/ : { diff --git a/Src/HALAL/HALAL.cpp b/Src/HALAL/HALAL.cpp index 2ee470ed9..beb072dcb 100644 --- a/Src/HALAL/HALAL.cpp +++ b/Src/HALAL/HALAL.cpp @@ -20,7 +20,6 @@ static void common_start(UART::Peripheral &printf_peripheral) { Watchdog::check_reset_flag(); #endif - MPUManager::start(); HAL_Init(); HALconfig::system_clock(); HALconfig::peripheral_clock(); @@ -37,6 +36,10 @@ static void common_start(UART::Peripheral &printf_peripheral) { 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 new file mode 100644 index 000000000..44b737421 --- /dev/null +++ b/Src/HALAL/Models/MDMA/MDMA.cpp @@ -0,0 +1,290 @@ +#include "HALAL/Models/MDMA/MDMA.hpp" + +#include + +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} +}; + + +void MDMA::prepare_transfer(Instance& instance, MDMA_LinkNodeTypeDef* first_node) +{ + if (instance.handle.State == HAL_MDMA_STATE_BUSY ) + { + 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; + + 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); + + __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_BUSY; + ErrorHandler("Error generating MDMA SW request"); + return; + } + +} + +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) +{ + + Instance& instance = instances[id]; + + return instance; +} + + +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()) + { + ErrorHandler("MDMA channel mapping not found"); + return; + } + 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; + 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; + + MDMA_LinkNodeConfTypeDef nodeConfig{}; + MDMA_LinkNodeTypeDef transfer_node{}; + + 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 = 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] = true; + + instance = Instance(mdma_handle, id, nullptr, transfer_node); + +} + + +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) + { + ErrorHandler("MDMA instance not initialised"); + } + 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); + 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); +} + +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(); + instance.done = transfer.second; + prepare_transfer(instance, transfer.first); + transfer_queue.pop(); + } + } +} + + +void MDMA::irq_handler() +{ + for (auto& instance : instances) + { + if (instance.handle.Instance == nullptr) + { + continue; + } + HAL_MDMA_IRQHandler(&instance.handle); + } +} + + +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, done}); +} + + + +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); + 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); + + prepare_transfer(instance, &instance.transfer_node); + return; + } + if(done) + { + *done = false; + } + } + return; +} + + +void MDMA::TransferCompleteCallback(MDMA_HandleTypeDef *hmdma) +{ + 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); + instance.handle.State = HAL_MDMA_STATE_READY; + instance_free_map[instance.id] = true; + if(instance.done == nullptr) + { + return; + } + *(instance.done) = true; + +} + + +void MDMA::TransferErrorCallback(MDMA_HandleTypeDef *hmdma) +{ + 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.done != nullptr) + { + *(instance.done) = 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(); +} diff --git a/Src/HALAL/Models/MPUManager/MPUManager.cpp b/Src/HALAL/Models/MPUManager/MPUManager.cpp index c9ef34ca5..759a853c0 100644 --- a/Src/HALAL/Models/MPUManager/MPUManager.cpp +++ b/Src/HALAL/Models/MPUManager/MPUManager.cpp @@ -1,4 +1,6 @@ #include "HALAL/Models/MPUManager/MPUManager.hpp" -void* MPUManager::no_cached_ram_start = (void*)&_no_cached_ram_start; +extern "C" uint8_t __mpu_ram_d3_nc_start[]; + +void* MPUManager::no_cached_ram_start = __mpu_ram_d3_nc_start; uint32_t MPUManager::no_cached_ram_occupied_bytes = 0; diff --git a/Src/HALAL/Services/Communication/UART/UART.cpp b/Src/HALAL/Services/Communication/UART/UART.cpp index 7a354e166..3662beb00 100644 --- a/Src/HALAL/Services/Communication/UART/UART.cpp +++ b/Src/HALAL/Services/Communication/UART/UART.cpp @@ -221,7 +221,17 @@ UART_HandleTypeDef* UART::get_handle(uint8_t id) { extern "C" { #endif +int _write(int file, char* str, int len) { + UART::print_by_uart(str , len); + if (*str == '\n') { + char retorno[1] = {'\r'}; + UART::print_by_uart(retorno, 1); + } + + + return len; +} #ifdef __cplusplus } diff --git a/Src/ST-LIB.cpp b/Src/ST-LIB.cpp index 1e560e1d5..293a925af 100644 --- a/Src/ST-LIB.cpp +++ b/Src/ST-LIB.cpp @@ -39,6 +39,7 @@ void STLIB::update() { Ethernet::update(); Server::update_servers(); #endif - - ErrorHandlerModel::ErrorHandlerUpdate(); -} \ No newline at end of file + ErrorHandlerModel::ErrorHandlerUpdate(); + MDMA::update(); + +} diff --git a/Src/ST-LIB_LOW/Sd/Sd.cpp b/Src/ST-LIB_LOW/Sd/Sd.cpp new file mode 100644 index 000000000..9d1425b9b --- /dev/null +++ b/Src/ST-LIB_LOW/Sd/Sd.cpp @@ -0,0 +1,293 @@ +#include "ST-LIB_LOW/Sd/Sd.hpp" + + +using namespace ST_LIB; + +void SdDomain::Instance::on_dma_read_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_dma_write_complete() { + if (operation_flag) { + *operation_flag = true; + operation_flag = nullptr; + } + SDMMC_CmdStopTransfer(hsd.Instance); +} + +void SdDomain::Instance::on_abort() { ErrorHandler("SD Card operation aborted"); } + +void SdDomain::Instance::on_error() { ErrorHandler("SD Card error occurred"); } + +bool SdDomain::Instance::is_card_present() { return cd_instance->first->read() == cd_instance->second; } +bool SdDomain::Instance::is_write_protected() { return wp_instance->first->read() == wp_instance->second; } + +bool SdDomain::Instance::is_busy() { + if (!card_initialized) return false; + return (hsd.State == HAL_SD_STATE_BUSY) || (hsd.State == HAL_SD_STATE_PROGRAMMING) || (hsd.State == HAL_SD_STATE_RECEIVING); +} + +bool SdDomain::Instance::initialize_card() { + if (card_initialized) { return true; } // Already initialized + + HAL_StatusTypeDef status = HAL_SD_Init(&hsd); + if (status != HAL_OK) { + return false; + } + + if (HAL_SD_GetCardInfo(&hsd, &card_info) != HAL_OK) { + return false; + } + + if (!configure_idma()) { + return false; + } + + if (HAL_SD_ConfigSpeedBusOperation(&hsd, SDMMC_SPEED_MODE_AUTO) != HAL_OK) { + return false; + } + + card_initialized = true; + return true; +} + +bool SdDomain::Instance::deinitialize_card() { + if (!card_initialized) { return true; } // Already deinitialized + + HAL_StatusTypeDef status = HAL_SD_DeInit(&hsd); + if (status != HAL_OK) { + return false; + } + + card_initialized = false; + return true; +} + +void SdDomain::Instance::switch_buffer() { + current_buffer = (current_buffer == BufferSelect::Buffer0) ? BufferSelect::Buffer1 : BufferSelect::Buffer0; +} + +bool SdDomain::Instance::configure_idma() { + HAL_StatusTypeDef status = HAL_SDEx_ConfigDMAMultiBuffer(&hsd, + reinterpret_cast(mpu_buffer0_instance->ptr), + reinterpret_cast(mpu_buffer1_instance->ptr), + mpu_buffer0_instance->size / 512); // Number of 512B-blocks + + if (status != HAL_OK) { return false; } + return true; +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_ReadBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + /* Clear old Flags*/ + __HAL_SD_CLEAR_FLAG(&hsd, SDMMC_STATIC_DATA_FLAGS); + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + hsd.State = HAL_SD_STATE_BUSY; + + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_SDMMC; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Read Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_READ_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Read Multi Block command */ + errorstate = SDMMC_CmdReadMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_RXOVERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + +HAL_StatusTypeDef SdDomain::Instance::Not_HAL_SDEx_WriteBlocksDMAMultiBuffer(uint32_t BlockAdd, uint32_t NumberOfBlocks) { + SDMMC_DataInitTypeDef config; + uint32_t errorstate; + uint32_t DmaBase0_reg; + uint32_t DmaBase1_reg; + uint32_t add = BlockAdd; + + if (hsd.State == HAL_SD_STATE_READY) { + if ((add + NumberOfBlocks) > (hsd.SdCard.LogBlockNbr)) { + hsd.ErrorCode |= HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + DmaBase0_reg = hsd.Instance->IDMABASE0; + DmaBase1_reg = hsd.Instance->IDMABASE1; + if ((hsd.Instance->IDMABSIZE == 0U) || (DmaBase0_reg == 0U) || (DmaBase1_reg == 0U)) { + hsd.ErrorCode = HAL_SD_ERROR_ADDR_OUT_OF_RANGE; + return HAL_ERROR; + } + + /* Initialize data control register */ + hsd.Instance->DCTRL = 0; + + hsd.ErrorCode = HAL_SD_ERROR_NONE; + + hsd.State = HAL_SD_STATE_BUSY; + if (hsd.SdCard.CardType != CARD_SDHC_SDXC) { + add *= 512U; + } + + /* Configure the SD DPSM (Data Path State Machine) */ + config.DataTimeOut = SDMMC_DATATIMEOUT; + config.DataLength = BLOCKSIZE * NumberOfBlocks; + config.DataBlockSize = SDMMC_DATABLOCK_SIZE_512B; + config.TransferDir = SDMMC_TRANSFER_DIR_TO_CARD; + config.TransferMode = SDMMC_TRANSFER_MODE_BLOCK; + config.DPSM = SDMMC_DPSM_DISABLE; + (void)SDMMC_ConfigData(hsd.Instance, &config); + + // hsd.Instance->DCTRL |= SDMMC_DCTRL_FIFORST; + + __SDMMC_CMDTRANS_ENABLE(hsd.Instance); + + if (current_buffer == BufferSelect::Buffer1) { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF1; + } else { + hsd.Instance->IDMACTRL = SDMMC_ENABLE_IDMA_DOUBLE_BUFF0; + } + switch_buffer(); + + /* Write Blocks in DMA mode */ + hsd.Context = (SD_CONTEXT_WRITE_MULTIPLE_BLOCK | SD_CONTEXT_DMA); + + /* Write Multi Block command */ + errorstate = SDMMC_CmdWriteMultiBlock(hsd.Instance, add); + if (errorstate != HAL_SD_ERROR_NONE) { + hsd.State = HAL_SD_STATE_READY; + hsd.ErrorCode |= errorstate; + return HAL_ERROR; + } + + __HAL_SD_ENABLE_IT(&hsd, (SDMMC_IT_DCRCFAIL | SDMMC_IT_DTIMEOUT | SDMMC_IT_TXUNDERR | SDMMC_IT_DATAEND | + SDMMC_IT_IDMABTC)); + + return HAL_OK; + } + else { + return HAL_BUSY; + } +} + + +SD_HandleTypeDef* g_sdmmc1_handle = nullptr; +SD_HandleTypeDef* g_sdmmc2_handle = nullptr; + +void* g_sdmmc1_instance_ptr = nullptr; +void* g_sdmmc2_instance_ptr = nullptr; + +extern "C" { + +void HAL_SD_MspInit(SD_HandleTypeDef* hsd) { + if (hsd->Instance == SDMMC1) { + __HAL_RCC_SDMMC1_CLK_ENABLE(); + __HAL_RCC_SDMMC1_FORCE_RESET(); + __HAL_RCC_SDMMC1_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC1_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + } else { + __HAL_RCC_SDMMC2_CLK_ENABLE(); + __HAL_RCC_SDMMC2_FORCE_RESET(); + __HAL_RCC_SDMMC2_RELEASE_RESET(); + HAL_NVIC_SetPriority(SDMMC2_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(SDMMC2_IRQn); + } +} + +void SDMMC1_IRQHandler(void) { + if (g_sdmmc1_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc1_handle); + } +} + +void SDMMC2_IRQHandler(void) { + if (g_sdmmc2_handle != nullptr) { + HAL_SD_IRQHandler(g_sdmmc2_handle); + } +} + +void HAL_SD_TxCpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_write_complete(); + } +} + +void HAL_SD_RxCpltCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_dma_read_complete(); + } +} + +void HAL_SD_AbortCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_abort(); + } else { + ErrorHandler("SD Card operation aborted"); + } +} + +void HAL_SD_ErrorCallback(SD_HandleTypeDef* hsd) { + if (auto sd_instance = ST_LIB::get_sd_instance(hsd)) { + sd_instance->on_error(); + } else { + ErrorHandler("SD Card error occurred"); + } +} + +} // extern "C" \ No newline at end of file