From 6d42a48fa36d9ec9e8075a0a39ea6b5ee65886f6 Mon Sep 17 00:00:00 2001 From: Fabrizio Monti <fabrizio.monti@skywarder.eu> Date: Thu, 5 Jun 2025 19:48:10 +0000 Subject: [PATCH] [DMA] DMA driver --- CMakeLists.txt | 3 + cmake/boardcore.cmake | 2 + src/shared/drivers/dma/DMA.cpp | 763 ++++++++++++++++++ src/shared/drivers/dma/DMA.h | 573 +++++++++++++ src/shared/drivers/dma/DMADefs.cpp | 69 ++ src/shared/drivers/dma/DMADefs.h | 257 ++++++ src/shared/drivers/dma/DMAStream.h | 510 ------------ .../board_mappings/stm32f407xx_mappings.cpp | 232 ++++++ .../board_mappings/stm32f429xx_mappings.cpp | 262 ++++++ .../board_mappings/stm32f767xx_mappings.cpp | 307 +++++++ src/tests/drivers/test-dma-mem-to-mem.cpp | 87 ++ 11 files changed, 2555 insertions(+), 510 deletions(-) create mode 100644 src/shared/drivers/dma/DMA.cpp create mode 100644 src/shared/drivers/dma/DMA.h create mode 100644 src/shared/drivers/dma/DMADefs.cpp create mode 100644 src/shared/drivers/dma/DMADefs.h delete mode 100644 src/shared/drivers/dma/DMAStream.h create mode 100644 src/shared/drivers/dma/board_mappings/stm32f407xx_mappings.cpp create mode 100644 src/shared/drivers/dma/board_mappings/stm32f429xx_mappings.cpp create mode 100644 src/shared/drivers/dma/board_mappings/stm32f767xx_mappings.cpp create mode 100644 src/tests/drivers/test-dma-mem-to-mem.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f1ed2c694..05d709a1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,6 +284,9 @@ sbs_target(test-usart-f4 stm32f429zi_stm32f4discovery) add_executable(test-usart-f7 src/tests/drivers/usart/test-usart.cpp) sbs_target(test-usart-f7 stm32f767zi_nucleo) +add_executable(test-dma-mem-to-mem src/tests/drivers/test-dma-mem-to-mem.cpp) +sbs_target(test-dma-mem-to-mem stm32f767zi_compute_unit) + add_executable(test-i2c-driver-f4 src/tests/drivers/i2c/test-i2c-driver.cpp) sbs_target(test-i2c-driver-f4 stm32f429zi_stm32f4discovery) diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 503803036..1e306418a 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -58,6 +58,8 @@ set(BOARDCORE_SRC ${BOARDCORE_PATH}/src/shared/drivers/canbus/CanDriver/CanDriver.cpp ${BOARDCORE_PATH}/src/shared/drivers/canbus/CanDriver/CanInterrupt.cpp ${BOARDCORE_PATH}/src/shared/drivers/canbus/CanProtocol/CanProtocol.cpp + ${BOARDCORE_PATH}/src/shared/drivers/dma/DMA.cpp + ${BOARDCORE_PATH}/src/shared/drivers/dma/DMADefs.cpp ${BOARDCORE_PATH}/src/shared/drivers/interrupt/external_interrupts.cpp ${BOARDCORE_PATH}/src/shared/drivers/timer/PWM.cpp ${BOARDCORE_PATH}/src/shared/drivers/timer/CountedPWM.cpp diff --git a/src/shared/drivers/dma/DMA.cpp b/src/shared/drivers/dma/DMA.cpp new file mode 100644 index 000000000..fd87b8cc8 --- /dev/null +++ b/src/shared/drivers/dma/DMA.cpp @@ -0,0 +1,763 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Alberto Nidasio, Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "DMA.h" + +#include <kernel/logging.h> +#include <utils/ClockUtils.h> +#include <utils/Debug.h> + +#include <map> + +using namespace miosix; + +/** + * Here are defined the IRQHandlers for the various streams. + * + * The problem is that some of these stream are used + * by miosix. The corresponding IRQHandlers are already defined + * in there, causing conflicts. + * Moreover, the used streams might differ from different boards. + * That's why some streams are available only for a particular board. + */ + +void __attribute__((naked)) DMA1_Stream0_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream0_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream0_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str0); +} + +#ifndef STM32F407xx +// This stream is used by miosix for STM32F407xx boards +void __attribute__((naked)) DMA1_Stream1_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream1_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream1_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str1); +} +#endif // STM32F407xx + +void __attribute__((naked)) DMA1_Stream2_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream2_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream2_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str2); +} + +#ifndef STM32F407xx +// This stream is used by miosix for STM32F407xx boards +void __attribute__((naked)) DMA1_Stream3_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream3_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream3_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str3); +} +#endif // STM32F407xx + +void __attribute__((naked)) DMA1_Stream4_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream4_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream4_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str4); +} + +void __attribute__((naked)) DMA1_Stream5_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream5_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream5_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str5); +} + +void __attribute__((naked)) DMA1_Stream6_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream6_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream6_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str6); +} + +void __attribute__((naked)) DMA1_Stream7_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA1_Stream7_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA1_Stream7_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA1_Str7); +} + +void __attribute__((naked)) DMA2_Stream0_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream0_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream0_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str0); +} + +void __attribute__((naked)) DMA2_Stream1_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream1_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream1_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str1); +} + +void __attribute__((naked)) DMA2_Stream2_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream2_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream2_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str2); +} + +// This stream is used by miosix for all currently supported +// boards, so it is simply commented out +// void __attribute__((naked)) DMA2_Stream3_IRQHandler() +// { +// saveContext(); +// asm volatile("bl _Z20DMA2_Stream3_IRQImplv"); +// restoreContext(); +// } + +// void __attribute__((used)) DMA2_Stream3_IRQImpl() +// { +// Boardcore::DMADriver::instance().IRQhandleInterrupt( +// Boardcore::DMADefs::DMAStreamId::DMA2_Str3); +// } + +void __attribute__((naked)) DMA2_Stream4_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream4_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream4_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str4); +} + +#if !defined(STM32F767xx) && !defined(STM32F429xx) +// This stream is used by miosix for STM32F767xx +// and STM32F429xx boards +void __attribute__((naked)) DMA2_Stream5_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream5_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream5_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str5); +} +#endif // STM32F767xx & STM32F429xx + +void __attribute__((naked)) DMA2_Stream6_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream6_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream6_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str6); +} + +#if !defined(STM32F767xx) && !defined(STM32F429xx) +// This stream is used by miosix for STM32F767xx +// and STM32F429xx boards +void __attribute__((naked)) DMA2_Stream7_IRQHandler() +{ + saveContext(); + asm volatile("bl _Z20DMA2_Stream7_IRQImplv"); + restoreContext(); +} + +void __attribute__((used)) DMA2_Stream7_IRQImpl() +{ + Boardcore::DMADriver::instance().IRQhandleInterrupt( + Boardcore::DMADefs::DMAStreamId::DMA2_Str7); +} +#endif // STM32F767xx & STM32F429xx + +namespace Boardcore +{ + +void DMADriver::IRQhandleInterrupt(DMADefs::DMAStreamId id) +{ + DMAStream& stream = streams.at(id); + + stream.readFlags(); + stream.clearAllFlags(); + + // Run the callbacks if necessary + if (stream.halfTransferCallback && stream.halfTransferFlag) + stream.halfTransferCallback(); + + if (stream.transferCompleteCallback && stream.transferCompleteFlag) + stream.transferCompleteCallback(); + + if (stream.errorCallback && + (stream.transferErrorFlag || stream.fifoErrorFlag || + stream.directModeErrorFlag)) + { + stream.errorCallback(); + } + + // Wakeup the thread if the user is waiting + if (stream.waitingThread) + IRQwakeupThread(stream); +} + +void DMADriver::IRQwakeupThread(DMAStream& stream) +{ + // Wakeup the waiting thread + stream.waitingThread->wakeup(); + + // If the waiting thread has a higher priority than the current + // thread then reschedule + if (stream.waitingThread->IRQgetPriority() > + miosix::Thread::IRQgetCurrentThread()->IRQgetPriority()) + { + miosix::Scheduler::IRQfindNextThread(); + } + + // Clear the thread pointer, this way the thread will be sure it is + // not a spurious wakeup + stream.waitingThread = nullptr; +} + +DMADriver& DMADriver::instance() +{ + static DMADriver instance; + return instance; +} + +bool DMADriver::tryStream(DMADefs::DMAStreamId id) +{ + Lock<FastMutex> l(mutex); + + // Return true, meaning that the channel is free, only if it is not yet + // allocated + return streams.count(id) == 0; +} + +DMAStreamGuard DMADriver::acquireStream(DMADefs::DMAStreamId id, + DMADefs::Channel channel, + std::chrono::nanoseconds timeout) +{ + Lock<FastMutex> l(mutex); + + // Wait until the stream is free or the timeout expires + while (streams.count(id) != 0) + { + if (timeout == std::chrono::nanoseconds::zero()) + { + cv.wait(l); + } + else + { + auto res = cv.timedWait(l, timeout.count()); + + if (res == TimedWaitResult::Timeout) + { + // The timeout expired + return DMAStreamGuard(nullptr); + } + } + } + + streams.insert( + std::pair<DMADefs::DMAStreamId, DMAStream>(id, DMAStream(id, channel))); + return DMAStreamGuard(&(streams.at(id))); +} + +DMAStreamGuard DMADriver::acquireStreamForPeripheral( + DMADefs::Peripherals peripheral, std::chrono::nanoseconds timeout) +{ + const auto availableStreams = + DMADefs::mapPeripherals.equal_range(peripheral); + + Lock<FastMutex> l(mutex); + while (true) + { + // Iterate through the streams for that peripheral, + // return the first available + for (auto it = availableStreams.first; it != availableStreams.second; + ++it) + { + DMADefs::DMAStreamId id = it->second.first; + DMADefs::Channel channel = it->second.second; + + if (streams.count(id) == 0) + { + // Stream is free + streams.insert(std::pair<DMADefs::DMAStreamId, DMAStream>( + id, DMAStream(id, channel))); + return DMAStreamGuard(&(streams.at(id))); + } + } + + if (timeout == std::chrono::nanoseconds::zero()) + { + cv.wait(l); + } + else + { + auto res = cv.timedWait(l, timeout.count()); + + if (res == TimedWaitResult::Timeout) + { + // The timeout expired + return DMAStreamGuard(nullptr); + } + } + } +} + +void DMADriver::releaseStream(DMADefs::DMAStreamId id) +{ + Lock<FastMutex> l(mutex); + + if (streams.count(id) != 0) + { + streams.erase(id); + cv.broadcast(); + } +} + +DMADriver::DMADriver() +{ + // For now the clocks are always enabled + ClockUtils::enablePeripheralClock(DMA1); + ClockUtils::enablePeripheralClock(DMA2); + + // Reset interrupts flags by setting the clear bits to 1 + constexpr int resetValue = 0x0f7d0f7d; + DMA1->HIFCR = resetValue; + DMA1->LIFCR = resetValue; + DMA2->HIFCR = resetValue; + DMA2->LIFCR = resetValue; +} + +void DMAStream::setup(DMATransaction& transaction) +{ + currentSetup = transaction; + + // Reset the configuration + registers->CR = 0; + + // Wait for the stream to actually be disabled + while (registers->CR & DMA_SxCR_EN) + ; + + setChannel(currentChannel); + registers->CR |= static_cast<uint32_t>(transaction.direction); + registers->CR |= static_cast<uint32_t>(transaction.priority); + if (transaction.circularMode) + registers->CR |= DMA_SxCR_CIRC; + + setNumberOfDataItems(transaction.numberOfDataItems); + + if (transaction.direction == DMATransaction::Direction::MEM_TO_PER) + { + // In memory to peripheral mode, the source address is the memory + // address + + registers->CR |= static_cast<uint32_t>(transaction.srcSize) + << DMA_SxCR_MSIZE_Pos; + registers->CR |= static_cast<uint32_t>(transaction.dstSize) + << DMA_SxCR_PSIZE_Pos; + + if (transaction.srcIncrement) + registers->CR |= DMA_SxCR_MINC; + if (transaction.dstIncrement) + registers->CR |= DMA_SxCR_PINC; + + registers->M0AR = reinterpret_cast<uint32_t>(transaction.srcAddress); + registers->PAR = reinterpret_cast<uint32_t>(transaction.dstAddress); + } + else + { + // In peripheral to memory or memory to memory mode, the source address + // goes into the peripheral address register + + registers->CR |= static_cast<uint32_t>(transaction.srcSize) + << DMA_SxCR_PSIZE_Pos; + registers->CR |= static_cast<uint32_t>(transaction.dstSize) + << DMA_SxCR_MSIZE_Pos; + + if (transaction.srcIncrement) + registers->CR |= DMA_SxCR_PINC; + if (transaction.dstIncrement) + registers->CR |= DMA_SxCR_MINC; + + registers->PAR = reinterpret_cast<uint32_t>(transaction.srcAddress); + registers->M0AR = reinterpret_cast<uint32_t>(transaction.dstAddress); + } + + if (transaction.doubleBufferMode) + { + registers->CR |= DMA_SxCR_DBM; + registers->M1AR = + reinterpret_cast<uint32_t>(transaction.secondMemoryAddress); + } + + bool enableInterrupt = false; + if (transaction.enableHalfTransferInterrupt) + { + clearHalfTransferFlag(); + registers->CR |= DMA_SxCR_HTIE; + enableInterrupt = true; + } + if (transaction.enableTransferCompleteInterrupt) + { + clearTransferCompleteFlag(); + registers->CR |= DMA_SxCR_TCIE; + enableInterrupt = true; + } + if (transaction.enableTransferErrorInterrupt) + { + clearTransferErrorFlag(); + registers->CR |= DMA_SxCR_TEIE; + enableInterrupt = true; + } + if (transaction.enableFifoErrorInterrupt) + { + clearFifoErrorFlag(); + registers->FCR |= DMA_SxFCR_FEIE; + enableInterrupt = true; + } + if (transaction.enableDirectModeErrorInterrupt) + { + clearDirectModeErrorFlag(); + registers->CR |= DMA_SxCR_DMEIE; + enableInterrupt = true; + } + + // Select the interrupt number + IRQn_Type irqNumber = DMADefs::irqNumberMapping[static_cast<uint8_t>(id)]; + if (enableInterrupt) + { + NVIC_SetPriority(irqNumber, 8); + NVIC_ClearPendingIRQ(irqNumber); + NVIC_EnableIRQ(irqNumber); + } + else + { + NVIC_DisableIRQ(irqNumber); + } +} + +void DMAStream::enable() +{ + // Reset all saved flags + halfTransferFlag = false; + transferCompleteFlag = false; + transferErrorFlag = false; + fifoErrorFlag = false; + directModeErrorFlag = false; + + // Before setting EN bit to '1' to start a new transfer, the event + // flags corresponding to the stream in DMA_LISR or DMA_HISR + // register must be cleared. + clearAllFlags(); + + // Enable the peripheral + registers->CR |= DMA_SxCR_EN; +} + +void DMAStream::disable() { registers->CR &= ~DMA_SxCR_EN; } + +void DMAStream::waitForHalfTransfer() +{ + waitForInterruptEventImpl( + currentSetup.enableHalfTransferInterrupt, + std::bind(&DMAStream::getHalfTransferFlagStatus, this), + std::bind(&DMAStream::clearHalfTransferFlag, this), halfTransferFlag, + -1); +} + +void DMAStream::waitForTransferComplete() +{ + waitForInterruptEventImpl( + currentSetup.enableTransferCompleteInterrupt, + std::bind(&DMAStream::getTransferCompleteFlagStatus, this), + std::bind(&DMAStream::clearTransferCompleteFlag, this), + transferCompleteFlag, -1); + +#ifdef STM32F767xx + invalidateCache(); +#endif // STM32F767xx +} + +bool DMAStream::timedWaitForHalfTransfer(std::chrono::nanoseconds timeout_ns) +{ + return waitForInterruptEventImpl( + currentSetup.enableHalfTransferInterrupt, + std::bind(&DMAStream::getHalfTransferFlagStatus, this), + std::bind(&DMAStream::clearHalfTransferFlag, this), halfTransferFlag, + timeout_ns.count()); +} + +bool DMAStream::timedWaitForTransferComplete( + std::chrono::nanoseconds timeout_ns) +{ + bool ret = waitForInterruptEventImpl( + currentSetup.enableTransferCompleteInterrupt, + std::bind(&DMAStream::getTransferCompleteFlagStatus, this), + std::bind(&DMAStream::clearTransferCompleteFlag, this), + transferCompleteFlag, timeout_ns.count()); + +#ifdef STM32F767xx + invalidateCache(); +#endif // STM32F767xx + + return ret; +} + +#ifdef STM32F767xx +void DMAStream::invalidateCache() +{ + /** + * STM32F7 boards use data cache. Unluckily the dma doesn't + * trigger the cache refresh. + * This means that when copying data to ram, the user won't + * see the result. + * This method check if cache invalidation is needed, and + * forces it if necessary. + * + * The memory being invalidated must be 32 bytes aligned. + * + * As of today, the cache is set as write-through. This + * means that values written in cache are immediately + * written in ram, and we don't need to worry about memory + * to peripheral operations, + */ + + // If the data was copied from memory to a peripheral there's + // no need to worry about cache + if (currentSetup.direction == DMATransaction::Direction::MEM_TO_PER) + return; + + constexpr uint8_t CACHE_LINE_SIZE = 32; + + // Aligned ptr: round down to the nearest address that is + // 32 bytes aligned + uintptr_t alignedPtr = + (uintptr_t)currentSetup.dstAddress & ~(CACHE_LINE_SIZE - 1); + + // Evaluate how many bytes were added, due to the round down + uintptr_t diff = (uintptr_t)currentSetup.dstAddress - alignedPtr; + + // Aligned size: compute the amount of bytes being invalidated + int32_t alignedSize = currentSetup.numberOfDataItems; + if (currentSetup.dstSize == DMATransaction::DataSize::BITS_16) + alignedSize *= 2; + else if (currentSetup.dstSize == DMATransaction::DataSize::BITS_32) + alignedSize *= 4; + alignedSize += diff; + + SCB_InvalidateDCache_by_Addr((uint32_t*)alignedPtr, alignedSize); +} +#endif // STM32F767xx + +void DMAStream::setHalfTransferCallback(std::function<void()> callback) +{ + halfTransferCallback = callback; +} + +void DMAStream::resetHalfTransferCallback() { halfTransferCallback = nullptr; } + +void DMAStream::setTransferCompleteCallback(std::function<void()> callback) +{ + transferCompleteCallback = callback; +} + +void DMAStream::resetTransferCompleteCallback() +{ + transferCompleteCallback = nullptr; +} + +void DMAStream::setErrorCallback(std::function<void()> callback) +{ + errorCallback = callback; +} + +void DMAStream::resetErrorCallback() { errorCallback = nullptr; } + +void DMAStream::readFlags() +{ + uint8_t flags = *ISR >> IFindex; + + halfTransferFlag = flags & DMA_LISR_HTIF0; + transferCompleteFlag = flags & DMA_LISR_TCIF0; + transferErrorFlag = flags & DMA_LISR_TEIF0; + fifoErrorFlag = flags & DMA_LISR_DMEIF0; + directModeErrorFlag = flags & DMA_LISR_DMEIF0; +} + +bool DMAStream::setNumberOfDataItems(const uint16_t nBytes) +{ + // Verify that the stream is disabled while doing it + if ((registers->CR & DMA_SxCR_EN) != 0) + { + // Cannot proceed + return false; + } + + currentSetup.numberOfDataItems = nBytes; + registers->NDTR = nBytes; + return true; +} + +void DMAStream::setChannel(const DMADefs::Channel channel) +{ + registers->CR |= static_cast<uint32_t>(channel); +} + +int DMAStream::getCurrentBufferNumber() +{ + return (registers->CR & DMA_SxCR_CT) != 0 ? 2 : 1; +} + +DMAStream::DMAStream(DMADefs::DMAStreamId id, DMADefs::Channel channel) + : id(id), currentChannel(channel) +{ + // Get the channel registers base address and the interrupt flags clear + // register address + if (id < DMADefs::DMAStreamId::DMA2_Str0) + { + registers = reinterpret_cast<DMA_Stream_TypeDef*>( + DMA1_BASE + 0x10 + 0x18 * static_cast<uint8_t>(id)); + + if (id < DMADefs::DMAStreamId::DMA1_Str4) + { + // Streams from 0 to 3 use low registers (LIFCR and LISR) + IFCR = &DMA1->LIFCR; + ISR = &DMA1->LISR; + } + else + { + // Streams from 4 to 7 use high registers (HIFCR and HISR) + IFCR = &DMA1->HIFCR; + ISR = &DMA1->HISR; + } + } + else + { + registers = reinterpret_cast<DMA_Stream_TypeDef*>( + DMA2_BASE + 0x10 + 0x18 * (static_cast<uint8_t>(id) - 8)); + + if (id < DMADefs::DMAStreamId::DMA2_Str4) + { + // Streams from 0 to 3 use low registers (LIFCR and LISR) + IFCR = &DMA2->LIFCR; + ISR = &DMA2->LISR; + } + else + { + // Streams from 4 to 7 use high registers (HIFCR and HISR) + IFCR = &DMA2->HIFCR; + ISR = &DMA2->HISR; + } + } + + // Compute the index for the interrupt flags clear register + // Refer to reference manual for the register bits structure + int offset = static_cast<uint8_t>(id) % 4; + IFindex = (offset % 2) * 6 + (offset / 2) * 16; +} + +DMAStream* DMAStreamGuard::operator->() +{ + D(assert((pStream != nullptr) && "DMAStreamGuard: pointer is null")); + + return pStream; +} + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/DMA.h b/src/shared/drivers/dma/DMA.h new file mode 100644 index 000000000..fef7afc4d --- /dev/null +++ b/src/shared/drivers/dma/DMA.h @@ -0,0 +1,573 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Alberto Nidasio, Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <kernel/scheduler/scheduler.h> +#include <kernel/sync.h> + +#include <chrono> +#include <functional> +#include <map> + +#include "DMADefs.h" + +namespace Boardcore +{ + +/** + * @brief This is the configuration struct for + * a DMA transaction. + */ +struct DMATransaction +{ + enum class Direction : uint16_t + { + MEM_TO_MEM = DMA_SxCR_DIR_1, + MEM_TO_PER = DMA_SxCR_DIR_0, + PER_TO_MEM = 0, + }; + + /** + * Priority of the DMA transaction. When multiple + * streams from the same controller (DMA1 or DMA2) + * are requested, they are served following the + * priority order. + * If two requests have the same software priority level, + * the stream with the lower number takes priority over + * the stream with the higher number. For example, Stream + * 2 takes priority over Stream 4. + */ + enum class Priority : uint32_t + { + VERY_HIGH = DMA_SxCR_PL, + HIGH = DMA_SxCR_PL_1, + MEDIUM = DMA_SxCR_PL_0, + LOW = 0, + }; + + enum class DataSize : uint8_t + { + BITS_8 = 0, + BITS_16, + BITS_32, + }; + + Direction direction = Direction::MEM_TO_MEM; + Priority priority = Priority::LOW; + DataSize srcSize = DataSize::BITS_32; + DataSize dstSize = DataSize::BITS_32; + volatile void* srcAddress = nullptr; + volatile void* dstAddress = nullptr; + volatile void* secondMemoryAddress = nullptr; + uint16_t numberOfDataItems = 0; + bool srcIncrement = false; + bool dstIncrement = false; + + /** + * @brief Enables circular buffer mode. + * @warning Not available with memory to memory transfers. + */ + bool circularMode = false; + + /** + * @brief Enables double buffer mode. + * @warning Automatically enables circular mode. Not + * available with memory to memory transfers. + */ + bool doubleBufferMode = false; + bool enableTransferCompleteInterrupt = false; + + /** + * @warning After a full DMA transaction, cache invalidation is required + * to maintain data coherence. However, the half transfer + * mechanism does not allow the driver to handle cache + * invalidation internally, leaving the job to the user. + */ + bool enableHalfTransferInterrupt = false; + + /** + * The transfer error interrupt flag is set when: + * - A bus error occurs during a DMA read or a write access. + * - A write access is requested by software on a memory address register + * in Double buffer mode whereas the stream is enabled and the current + * target memory is the one impacted by the write into the memory address + * register. + */ + bool enableTransferErrorInterrupt = false; + + /** + * Fifo overrun/underrun condition. + * + * In direct mode, the FIFO error flag can also be set under + * the following conditions: + * - In the peripheral-to-memory mode, the FIFO can be saturated + * (overrun) if the memory bus is not granted for several + * peripheral requests. + * - In the memory-to-peripheral mode, an underrun condition may + * occur if the memory bus has not been granted before a + * peripheral request occurs. + */ + bool enableFifoErrorInterrupt = false; + + /** + * Direct mode is the default fifo operating mode. + * + * Direct mode error can only be set in the peripheral-to-memory + * mode while operating in direct mode. This flag is set when a DMA request + * occurs while the previous data have not yet been fully transferred into + * the memory (because the memory bus was not granted). In this case, the + * flag indicates that 2 data items were be transferred successively to the + * same destination address, which could be an issue if the destination is + * not able to manage this situation. + */ + bool enableDirectModeErrorInterrupt = false; +}; + +// Forward declaration +class DMAStream; +class DMAStreamGuard; + +/** + * @brief This class is responsible for streams acquisition, + * streams release and interrupt handling. + */ +class DMADriver +{ +public: + // cppcheck-suppress noExplicitConstructor + + void IRQhandleInterrupt(DMADefs::DMAStreamId id); + + static DMADriver& instance(); + + /** + * @return True if the stream is not already in use. + */ + bool tryStream(DMADefs::DMAStreamId id); + + /** + * @brief Try to acquire the specified stream and initialize it with the + * correct channel. + * @param id The id of the stream to be acquired. + * @param channel The channel used to initialize the stream. + * @param timeout The maximum time that will be waited, defaults to waiting + * forever. + * @return A stream guard that might be valid or not, depending on the + * outcome of the request. + */ + DMAStreamGuard acquireStream( + DMADefs::DMAStreamId id, DMADefs::Channel channel, + std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero()); + + /** + * @brief Try to acquire a stream that is connected to the specified + * peripheral. + * @param peripheral The wanted peripheral. + * @param timeout The maximum time that will be waited, defaults to waiting + * forever. + * @return A stream guard that might be valid or not, depending on the + * outcome of the request. + */ + DMAStreamGuard acquireStreamForPeripheral( + DMADefs::Peripherals peripheral, + std::chrono::nanoseconds timeout = std::chrono::nanoseconds::zero()); + + void releaseStream(DMADefs::DMAStreamId id); + +private: + DMADriver(); + + /** + * @brief Wakeup the sleeping thread associated to the stream. + */ + void IRQwakeupThread(DMAStream& stream); + + miosix::FastMutex mutex; + miosix::ConditionVariable cv; + std::map<DMADefs::DMAStreamId, DMAStream> streams; + +public: + DMADriver(const DMADriver&) = delete; + DMADriver& operator=(const DMADriver&) = delete; +}; + +/** + * @brief This class represents the actual DMA stream. + * It can be used to setup, start and stop DMA transactions. + */ +class DMAStream +{ + friend DMADriver; + +public: + // cppcheck-suppress noExplicitConstructor + + /** + * @brief Setup the stream with the given configuration. + */ + void setup(DMATransaction& transaction); + + /** + * @brief Activate the stream. As soon as the stream is enabled, it + * serves any DMA request from/to the peripheral connected to the stream. + */ + void enable(); + + /** + * @brief Stop the DMA transaction (if running). + * This is equivalent to killing the transaction: DO NOT expect to be able + * to restart the transaction from where it was interrupted. The work + * completed up to the call will still be valid. + * @warning If set, the transfer complete interrupt will be fired. + */ + void disable(); + + /** + * @brief Wait for the half transfer complete signal. + * The caller waits for the corresponding interrupt, if enabled. + * Otherwise it goes to polling mode on the flag. + * @warning In case cache is used, this method DOES NOT invalidate + * the cache lines. Cache invalidation must be handled by the + * user. + */ + void waitForHalfTransfer(); + + /** + * @brief Wait for the transfer complete signal. + * The caller waits for the corresponding interrupt, if enabled. + * Otherwise it goes to polling mode on the flag. + * In case cache is used, this method invalidates the + * cache lines, so that the user can see the memory as is in ram. + */ + void waitForTransferComplete(); + + /** + * @brief Wait for the half transfer complete signal. + * The caller waits for the corresponding interrupt, if enabled. + * Otherwise it goes to polling mode on the flag. + * @param timeout_ns The maximum time that will be waited. + * @return True if the event is reached, false if the + * timeout expired. + * @warning In case cache is used, this method DOES NOT invalidate + * the cache lines. Cache invalidation must be handled by the + * user. + */ + bool timedWaitForHalfTransfer(std::chrono::nanoseconds timeout_ns); + + /** + * @brief Wait for the transfer complete signal. + * The caller waits for the corresponding interrupt, if enabled. + * Otherwise it goes to polling mode on the flag. + * In case cache is used, this method invalidates the + * cache lines, so that the user can see the memory as is in ram. + * @param timeout_ns The maximum time that will be waited. + * @return True if the event is reached, false if the + * timeout expired. + */ + bool timedWaitForTransferComplete(std::chrono::nanoseconds timeout_ns); + + void setHalfTransferCallback(std::function<void()> callback); + + void resetHalfTransferCallback(); + + void setTransferCompleteCallback(std::function<void()> callback); + + void resetTransferCompleteCallback(); + + void setErrorCallback(std::function<void()> callback); + + void resetErrorCallback(); + + /** + * @brief Reads the current flags status. + * + * The values can be read with the get***FlagStatus functions. + */ + void readFlags(); + + /** + * @brief Set the number of bytes to be exchanged during a + * dma transaction. Useful in case you don't want to change + * the entire configuration. Use while the stream is not + * enabled. + * @return True if the operation succeeded, false otherwise. + */ + bool setNumberOfDataItems(const uint16_t nBytes); + + /** + * @brief Select the channel to be used by the stream during + * the transactions. + */ + void setChannel(const DMADefs::Channel channel); + + /** + * @brief Returns the last read status of the half transfer flag. + */ + inline bool getHalfTransferFlagStatus() { return halfTransferFlag; } + + /** + * @brief Returns the last read status of the transfer complete flag. + */ + inline bool getTransferCompleteFlagStatus() { return transferCompleteFlag; } + + /** + * @brief Returns the last read status of the transfer error flag. + */ + inline bool getTransferErrorFlagStatus() { return transferErrorFlag; } + + /** + * @brief Returns the last read status of the fifo error flag. + */ + inline bool getFifoErrorFlagStatus() { return fifoErrorFlag; } + + /** + * @brief Returns the last read status of the direct mode (default + * mode) error flag. + */ + inline bool getDirectModeErrorFlagStatus() { return directModeErrorFlag; } + + /** + * @brief Returns the number of the buffer currently in use when + * in double buffer mode. + * @return 1 or 2 depending on the buffer currently in use. + */ + int getCurrentBufferNumber(); + + inline DMADefs::DMAStreamId getStreamId() { return id; } + + inline DMADefs::Channel getCurrentChannel() { return currentChannel; } + + inline void clearHalfTransferFlag() + { + *IFCR |= DMA_LIFCR_CHTIF0 << IFindex; + } + + inline void clearTransferCompleteFlag() + { + *IFCR |= DMA_LIFCR_CTCIF0 << IFindex; + } + + inline void clearTransferErrorFlag() + { + *IFCR |= DMA_LIFCR_CTEIF0 << IFindex; + } + + inline void clearFifoErrorFlag() { *IFCR |= DMA_LIFCR_CFEIF0 << IFindex; } + + inline void clearDirectModeErrorFlag() + { + *IFCR |= DMA_LIFCR_CDMEIF0 << IFindex; + } + + /** + * @brief Clear all the flags for the selected stream in the DMA ISR + * register (LISR or HISR depending on the selected stream id). + */ + inline void clearAllFlags() + { + *IFCR |= (DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTCIF0 | DMA_LIFCR_CTEIF0 | + DMA_LIFCR_CFEIF0 | DMA_LIFCR_CDMEIF0) + << IFindex; + } + +private: + DMAStream(DMADefs::DMAStreamId id, DMADefs::Channel channel); + + DMATransaction currentSetup; + + /** + * @brief Used to determine if the user thread is + * waiting to be awakened by an interrupt. + */ + miosix::Thread* waitingThread = nullptr; + + // These flags are set by the interrupt routine and tells the user + // which event were triggered + bool halfTransferFlag = false; + bool transferCompleteFlag = false; + bool transferErrorFlag = false; + bool fifoErrorFlag = false; + bool directModeErrorFlag = false; + + std::function<void()> halfTransferCallback; + std::function<void()> transferCompleteCallback; + std::function<void()> errorCallback; + + const DMADefs::DMAStreamId id; + DMADefs::Channel currentChannel; + DMA_Stream_TypeDef* registers; + + volatile uint32_t* ISR; ///< Interrupt status register + volatile uint32_t* IFCR; ///< Interrupt flags clear register + int IFindex; ///< Interrupt flags index + + inline bool waitForInterruptEventImpl( + bool isInterruptEnabled, std::function<bool()> getEventStatus, + std::function<void()> clearEventStatus, bool& eventTriggered, + long long timeout_ns) + { + // Return value: true if the event was triggered, false if the timeout + // expired + bool result = false; + + // If the interrupt is enabled we can just pause and wait for it. + // Otherwise we need to pool the flag. + if (isInterruptEnabled) + { + // Here we have 2 cases: + // - This function has been called after the interrupt fired. In + // this case the interrupt saves the flags status and we check them + // - The interrupt has not yet fired. We pause the thread and the + // interrupt will wake us up + + if (eventTriggered) + { + result = true; + } + else + { + // Save the current thread pointer + waitingThread = miosix::Thread::getCurrentThread(); + + // Wait until the thread is woken up and the pointer is cleared + miosix::FastInterruptDisableLock dLock; + if (timeout_ns >= 0) + { + do + { + if (miosix::Thread::IRQenableIrqAndTimedWait( + dLock, timeout_ns + miosix::getTime()) == + miosix::TimedWaitResult::Timeout) + { + result = false; + + // If the timeout expired we clear the thread + // pointer so that the interrupt, if it will occur, + // will not wake up the thread (and we can exit the + // while loop) + waitingThread = nullptr; + } + else + { + result = true; + } + } while (waitingThread); + } + else + { + do + { + miosix::Thread::IRQenableIrqAndWait(dLock); + } while (waitingThread); + result = true; + } + } + + // Before returning we need to clear the flags otherwise we could + // get misfires + eventTriggered = false; + } + else + { + // Pool the flag if the user did not enable the interrupt + if (timeout_ns >= 0) + { + const long long start = miosix::getTime(); + + do + { + readFlags(); + } while (!getEventStatus() && + miosix::getTime() - start < timeout_ns); + + result = getEventStatus(); + } + else + { + while (!getEventStatus()) + readFlags(); + result = true; + } + + if (result) + { + // Clear the flag + clearEventStatus(); + } + } + + return result; + } + +#ifdef STM32F767xx + /** + * @brief In case cache is used and data is written to ram, + * we have to invalidate cache lines in order to see the + * updated data. This function verifies if this operation + * is needed and performs it. + */ + void invalidateCache(); +#endif // STM32F767xx + +public: + DMAStream(const DMAStream&) = delete; + DMAStream& operator=(const DMAStream&) = delete; + + DMAStream(DMAStream&&) noexcept = default; + DMAStream& operator=(DMAStream&&) noexcept = default; +}; + +/** + * @brief Simple RAII class to handle DMA streams. + */ +class DMAStreamGuard +{ +public: + // cppcheck-suppress noExplicitConstructor + DMAStreamGuard(DMAStream* ptr) : pStream(ptr) {} + + ~DMAStreamGuard() + { + if (pStream != nullptr) + DMADriver::instance().releaseStream(pStream->getStreamId()); + } + + DMAStreamGuard(const DMAStreamGuard&) = delete; + DMAStreamGuard& operator=(const DMAStreamGuard&) = delete; + + DMAStreamGuard(DMAStreamGuard&&) noexcept = default; + DMAStreamGuard& operator=(DMAStreamGuard&&) noexcept = default; + + DMAStream* operator->(); + + /** + * @return True if the stream was correctly allocated and + * is ready to use. False otherwise. + */ + inline bool isValid() { return pStream != nullptr; } + +private: + DMAStream* const pStream; +}; + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/DMADefs.cpp b/src/shared/drivers/dma/DMADefs.cpp new file mode 100644 index 000000000..6289a7a1a --- /dev/null +++ b/src/shared/drivers/dma/DMADefs.cpp @@ -0,0 +1,69 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "DMADefs.h" + +// Include board mappings +#if defined(STM32F407xx) +#include "board_mappings/stm32f407xx_mappings.cpp" + +#elif defined(STM32F429xx) +#include "board_mappings/stm32f429xx_mappings.cpp" + +#elif defined(STM32F767xx) +#include "board_mappings/stm32f767xx_mappings.cpp" + +#else +#warning \ + "DMA: mapping not supported for this board! An empty mapping will be used, acquireStreamForPeripheral() will not work!" +namespace Boardcore +{ +namespace DMADefs +{ +const std::multimap<Peripherals, std::pair<DMAStreamId, Channel> > + mapPeripherals = {}; +} // namespace DMADefs +} // namespace Boardcore + +#endif + +// Check that at most 1 board is defined +#if defined(STM32F407xx) + defined(STM32F429xx) + defined(STM32F767xx) > 1 +#error "DMA: Multiple boards defined. Only one must be defined" +#endif + +namespace Boardcore +{ + +namespace DMADefs +{ + +const IRQn_Type irqNumberMapping[] = { + DMA1_Stream0_IRQn, DMA1_Stream1_IRQn, DMA1_Stream2_IRQn, DMA1_Stream3_IRQn, + DMA1_Stream4_IRQn, DMA1_Stream5_IRQn, DMA1_Stream6_IRQn, DMA1_Stream7_IRQn, + DMA2_Stream0_IRQn, DMA2_Stream1_IRQn, DMA2_Stream2_IRQn, DMA2_Stream3_IRQn, + DMA2_Stream4_IRQn, DMA2_Stream5_IRQn, DMA2_Stream6_IRQn, DMA2_Stream7_IRQn, +}; + +} // namespace DMADefs + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/DMADefs.h b/src/shared/drivers/dma/DMADefs.h new file mode 100644 index 000000000..2b2d9fe3e --- /dev/null +++ b/src/shared/drivers/dma/DMADefs.h @@ -0,0 +1,257 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include <interfaces/arch_registers.h> +#include <stdint.h> + +#include <map> + +namespace Boardcore +{ + +// For some reason these defines are missing +// in the CMSIS STM32F4xx file +#if defined(STM32F407xx) || defined(STM32F429xx) + +#ifndef DMA_SxCR_MSIZE_Pos +#define DMA_SxCR_MSIZE_Pos (13U) +#endif + +#ifndef DMA_SxCR_PSIZE_Pos +#define DMA_SxCR_PSIZE_Pos (11U) +#endif + +#endif // STM32F407xx + +namespace DMADefs +{ + +enum class DMAStreamId : uint8_t +{ + /** + * Here are defined the selectable streams. + * + * The problem is that some of these stream are used + * by miosix. The corresponding IRQHandlers are already defined + * in there, causing conflicts. + * Moreover, the used streams might differ from different boards. + * That's why some streams are available only for a particular + * board. + */ + + DMA1_Str0 = 0, + +#ifndef STM32F407xx + // This stream is used by miosix for STM32F407xx boards + DMA1_Str1 = 1, +#endif // STM32F407xx + + DMA1_Str2 = 2, + +#ifndef STM32F407xx + // This stream is used by miosix for STM32F407xx boards + DMA1_Str3 = 3, +#endif // STM32F407xx + + DMA1_Str4 = 4, + DMA1_Str5 = 5, + DMA1_Str6 = 6, + DMA1_Str7 = 7, + DMA2_Str0 = 8, + DMA2_Str1 = 9, + DMA2_Str2 = 10, + // DMA2_Str3 = 11, // Always used by miosix on currently supported boards + DMA2_Str4 = 12, + +#if !defined(STM32F767xx) && !defined(STM32F429xx) + // This stream is used by miosix for STM32F767xx + // and STM32F429xx boards + DMA2_Str5 = 13, +#endif // STM32F767xx & STM32F429xx + + DMA2_Str6 = 14, + +#if !defined(STM32F767xx) && !defined(STM32F429xx) + // This stream is used by miosix for STM32F767xx + // and STM32F429xx boards + DMA2_Str7 = 15, +#endif // STM32F767xx & STM32F429xx +}; + +/** + * @brief Channels selectable for each dma stream. + */ +enum class Channel : uint32_t +{ + // The first 8 channels are valid for all supported architectures + CHANNEL0 = 0, + CHANNEL1 = DMA_SxCR_CHSEL_0, + CHANNEL2 = DMA_SxCR_CHSEL_1, + CHANNEL3 = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, + CHANNEL4 = DMA_SxCR_CHSEL_2, + CHANNEL5 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_0, + CHANNEL6 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1, + CHANNEL7 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, + +// stm32f767 also has channels from 8 to 11 +#ifdef STM32F767xx + CHANNEL8 = DMA_SxCR_CHSEL_3, + CHANNEL9 = DMA_SxCR_CHSEL_3 | DMA_SxCR_CHSEL_0, + CHANNEL10 = DMA_SxCR_CHSEL_3 | DMA_SxCR_CHSEL_1, + CHANNEL11 = DMA_SxCR_CHSEL_3 | DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, +#endif // STM32F767xx +}; + +/** + * @brief All the peripherals connected to dma. + */ +enum class Peripherals : uint8_t +{ + PE_MEM_ONLY, + + PE_SPI1_TX, + PE_SPI1_RX, + PE_SPI2_TX, + PE_SPI2_RX, + PE_SPI3_TX, + PE_SPI3_RX, + PE_SPI4_TX, + PE_SPI4_RX, + PE_SPI5_TX, + PE_SPI5_RX, + PE_SPI6_TX, + PE_SPI6_RX, + PE_QUADSPI, + PE_USART1_TX, + PE_USART1_RX, + PE_USART2_TX, + PE_USART2_RX, + PE_USART3_TX, + PE_USART3_RX, + PE_UART4_TX, + PE_UART4_RX, + PE_UART5_TX, + PE_UART5_RX, + PE_USART6_TX, + PE_USART6_RX, + PE_UART7_TX, + PE_UART7_RX, + PE_UART8_TX, + PE_UART8_RX, + PE_I2C1_TX, + PE_I2C1_RX, + PE_I2C2_TX, + PE_I2C2_RX, + PE_I2C3_TX, + PE_I2C3_RX, + PE_I2C4_TX, + PE_I2C4_RX, + PE_I2S2_EXT_TX, + PE_I2S2_EXT_RX, + PE_I2S3_EXT_TX, + PE_I2S3_EXT_RX, + PE_TIM1_UP, + PE_TIM1_TRIG, + PE_TIM1_COM, + PE_TIM1_CH1, + PE_TIM1_CH2, + PE_TIM1_CH3, + PE_TIM1_CH4, + PE_TIM2_UP, + PE_TIM2_CH1, + PE_TIM2_CH2, + PE_TIM2_CH3, + PE_TIM2_CH4, + PE_TIM3_UP, + PE_TIM3_TRIG, + PE_TIM3_CH1, + PE_TIM3_CH2, + PE_TIM3_CH3, + PE_TIM3_CH4, + PE_TIM4_UP, + PE_TIM4_CH1, + PE_TIM4_CH2, + PE_TIM4_CH3, + PE_TIM5_UP, + PE_TIM5_TRIG, + PE_TIM5_CH1, + PE_TIM5_CH2, + PE_TIM5_CH3, + PE_TIM5_CH4, + PE_TIM6_UP, + PE_TIM7_UP, + PE_TIM8_UP, + PE_TIM8_TRIG, + PE_TIM8_COM, + PE_TIM8_CH1, + PE_TIM8_CH2, + PE_TIM8_CH3, + PE_TIM8_CH4, + PE_DAC1, + PE_DAC2, + PE_ADC1, + PE_ADC2, + PE_ADC3, + PE_SAI1_A, + PE_SAI2_A, + PE_SAI1_B, + PE_SAI2_B, + PE_DCMI, + PE_SDIO, + PE_SDMMC1, + PE_SDMMC2, + PE_CRYP_OUT, + PE_CRYP_IN, + PE_HASH_IN, + PE_SPDIFRX_DT, + PE_SPDIFRX_CS, + PE_DFSDM1_FLT0, + PE_DFSDM1_FLT1, + PE_DFSDM1_FLT2, + PE_DFSDM1_FLT3, + PE_JPEG_IN, + PE_JPEG_OUT, +}; + +/** + * @brief Mapping between `DMAStreamId` and the corresponding irq number. + * This is needed because irq number values are not contiguous and they are + * architecture dependent. + */ +extern const IRQn_Type irqNumberMapping[]; + +/** + * @brief Maps the peripherals to the dma streams (and + * the corresponding channel) that are connected with. + * + * The actual initialization of mapPeripherals is board + * specific, and can be found inside the "board_mappings" + * folder. + */ +extern const std::multimap<Peripherals, std::pair<DMAStreamId, Channel>> + mapPeripherals; + +} // namespace DMADefs + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/DMAStream.h b/src/shared/drivers/dma/DMAStream.h deleted file mode 100644 index ad3fd03b8..000000000 --- a/src/shared/drivers/dma/DMAStream.h +++ /dev/null @@ -1,510 +0,0 @@ -/* Copyright (c) 2021 Skyward Experimental Rocketry - * Author: Alberto Nidasio - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#pragma once - -#include <diagnostic/PrintLogger.h> -#include <interfaces/arch_registers.h> - -namespace Boardcore -{ - -/** - * @brief Driver for STM32 DMA streams. - * - * Direct Memory Access is used in order to provide high-speed data transfer - * between peripherals and memory and betweeen memory and memory. Data can be - * quickly moved by DMA without any CPU interacion. - * - * The STM32F4 family features two DMA controller, each with 8 streams. - * - * DMA features are: - * - Four-word depth 32 FIFO memory buffers per stream, that can be used in FIFO - * mode or direct mode - * - Double buffering - * - Software programmable piorities between DMA stream requests (4 levels) - * - The number of data items to be transferred can be managed either by the DMA - * controller or by the peripheral: - * - DMA flow controller: the number of data items to be transferred is - * software-programmable form 1 to 65535 - * - Peripheral flow controller: the number of data items to be transferred is - * unknown and controlled by the source or the destination peripheral that - * signals the end of the transfer by hardware - * - Incrementing or non incrementing adressing for source and destination - * - Circular buffer management - * - * Note that only DMA2 controller can perform memory-to-memory transactions. - * - * Each DMA transfer consists of three operations: - * - A loading from the peripheral data register or a location in memory - * - A storage of the data loaded to the peripheral data register or a location - * in memory - * - A post-decrement of the counter of transeffer data items (NDTR) - * - * After an event, the peripheral sends a request signal to the DMA controller. - * The DMA controller serves the request depending on the channel priorities. As - * soon as the DMA controller accesses the peripheral, an acknowledge signal is - * sent to the peripheral by the DMA controller. The peripheral releases its - * request as soon as it gets the acknowledge signal. - * - * Each stream is associated with a DMA request that can be selected out of 8 - * possible channel requests. - * - * Note that configuration functions have effect only if the stream is disabled. - */ -class DMAStream -{ -public: - enum class Channel : uint32_t - { - CHANNEL0 = 0, - CHANNEL1 = DMA_SxCR_CHSEL_0, - CHANNEL2 = DMA_SxCR_CHSEL_1, - CHANNEL3 = DMA_SxCR_CHSEL_1 | DMA_SxCR_CHSEL_0, - CHANNEL4 = DMA_SxCR_CHSEL_2, - CHANNEL5 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_0, - CHANNEL6 = DMA_SxCR_CHSEL_2 | DMA_SxCR_CHSEL_1, - CHANNEL7 = DMA_SxCR_CHSEL, - }; - - enum class MemoryBurstConfiguration : uint32_t - { - SINGLE_TRANSFER = 0, - INCR4 = DMA_SxCR_MBURST_0, - INCR8 = DMA_SxCR_MBURST_1, - INCR16 = DMA_SxCR_MBURST, - }; - - enum class PeripheralBurstConfiguration : uint32_t - { - SINGLE_TRANSFER = 0, - INCR4 = DMA_SxCR_PBURST_0, - INCR8 = DMA_SxCR_PBURST_1, - INCR16 = DMA_SxCR_PBURST, - }; - - enum class PriorityLevel : uint32_t - { - LOW = 0, - MEDIUM = DMA_SxCR_PL_0, - HIGH = DMA_SxCR_PL_1, - VERY_HIGH = DMA_SxCR_PL, - }; - - enum class MemoryDataSize : uint32_t - { - BYTE = 0, - HALF_WORD = DMA_SxCR_MSIZE_0, - WORD = DMA_SxCR_MSIZE_1 - }; - - enum class PeripheralDataSize : uint32_t - { - BYTE = 0, - HALF_WORD = DMA_SxCR_PSIZE_0, - WORD = DMA_SxCR_PSIZE_1 - }; - - enum class DataTransferDirection : uint32_t - { - PERIPH_TO_MEM = 0, - MEM_TO_PERIPH = DMA_SxCR_DIR_0, - MEM_TO_MEM = DMA_SxCR_DIR_1, - }; - - DMAStream(DMA_Stream_TypeDef* dmaStream); - - DMA_TypeDef* getController(); - - DMA_Stream_TypeDef* getStream(); - - void reset(); - - void enable(); - - void disable(); - - void setStreamChannel(Channel channel); - - void setStreamMemoryBurstConfiguration(MemoryBurstConfiguration config); - - void setStreamPeripheralBurstConfiguration( - PeripheralBurstConfiguration config); - - void enableDoubleBufferMode(); - - void disableDoubleBufferMode(); - - void setStreamPriorityLevel(PriorityLevel priorityLevel); - - void setMemoryDataSize(MemoryDataSize size); - - void setPeripheralDataSize(PeripheralDataSize size); - - void enableMemoryIncrement(); - - void disableMemoryIncrement(); - - void enablePeripheralIncrement(); - - void disablePeripheralIncrement(); - - void enableCircularMode(); - - void disableCircularMode(); - - void setDataTransferDirection(DataTransferDirection direction); - - void setPeripheralFlowController(); - - void setDMAFlowController(); - - void disableTransferCompleteInterrupt(); - - void enableTransferCompleteInterrupt(); - - void enableHalfTransferCompleteInterrupt(); - - void disableHalfTransferCompleteInterrupt(); - - void enableTransferErrorInterrupt(); - - void disableTransferErrorInterrupt(); - - void enableDirectModeErrorInterrupt(); - - void disableDirectModeErrorInterrupt(); - - /** - * @brief Sets the number of data items to transfer. - * - * The NDTR register decrements after each DMA transfer. Once the transfer - * has completed, this register can either stay at zero (when the stream is - * in normal mode) or be reloaded automatically with the previusly - * programmed value in the following cases: - * - When the stream is configure in circular mode - * - When the stream is enabled again (by setting EN bit to 1) - * If the value of the NDTR register is zero, no transaction can be served - * even if the stream is enabled. - */ - void setNumberOfDataItems(uint16_t numberOfDataItems); - - uint16_t readNumberOfDataItems(); - - void setPeripheralAddress(uint32_t* address); - - void setMemory0Address(uint32_t* address); - - /** - * @brief Sets the memory address used only in double buffer mode. - */ - void setMemory1Address(uint32_t* address); - - void clearStatusRegister(); - -private: - DMA_TypeDef* dmaController; - DMA_Stream_TypeDef* dmaStream; - - // Interrupt status flags - volatile uint32_t* IFCR; ///< Interrupt flags clear register - uint32_t IFCR_MASK; ///< Clear mask for all interrupt flags - - PrintLogger logger = Logging::getLogger("DMAStream"); -}; - -inline DMAStream::DMAStream(DMA_Stream_TypeDef* dmaStream) - : dmaStream(dmaStream) -{ - // Find the correct DMA controller - if (reinterpret_cast<uint32_t*>(dmaStream) < &(DMA2->LISR)) - dmaController = DMA1; - else - dmaController = DMA2; - - // Find the corret interrupt flags clear register - if (dmaController == DMA1) - if (dmaStream <= DMA1_Stream3) - IFCR = &(DMA1->LIFCR); - else - IFCR = &(DMA1->HIFCR); - else if (dmaStream <= DMA2_Stream3) - IFCR = &(DMA2->LIFCR); - else - IFCR = &(DMA2->HIFCR); - - // Find the correct clear mask - if (dmaStream == DMA1_Stream0 || dmaStream == DMA2_Stream0) - { - IFCR_MASK = DMA_LIFCR_CFEIF0 | DMA_LIFCR_CHTIF0 | DMA_LIFCR_CTCIF0 | - DMA_LIFCR_CTEIF0 | DMA_LIFCR_CDMEIF0; - } - else if (dmaStream == DMA1_Stream1 || dmaStream == DMA2_Stream1) - { - IFCR_MASK = DMA_LIFCR_CFEIF1 | DMA_LIFCR_CHTIF1 | DMA_LIFCR_CTCIF1 | - DMA_LIFCR_CTEIF1 | DMA_LIFCR_CDMEIF1; - } - else if (dmaStream == DMA1_Stream2 || dmaStream == DMA2_Stream2) - { - IFCR_MASK = DMA_LIFCR_CFEIF2 | DMA_LIFCR_CHTIF2 | DMA_LIFCR_CTCIF2 | - DMA_LIFCR_CTEIF2 | DMA_LIFCR_CDMEIF2; - } - else if (dmaStream == DMA1_Stream3 || dmaStream == DMA2_Stream3) - { - IFCR_MASK = DMA_LIFCR_CFEIF3 | DMA_LIFCR_CHTIF3 | DMA_LIFCR_CTCIF3 | - DMA_LIFCR_CTEIF3 | DMA_LIFCR_CDMEIF3; - } - else if (dmaStream == DMA1_Stream4 || dmaStream == DMA2_Stream4) - { - IFCR_MASK = DMA_HIFCR_CFEIF4 | DMA_HIFCR_CHTIF4 | DMA_HIFCR_CTCIF4 | - DMA_HIFCR_CTEIF4 | DMA_HIFCR_CDMEIF4; - } - else if (dmaStream == DMA1_Stream5 || dmaStream == DMA2_Stream5) - { - IFCR_MASK = DMA_HIFCR_CFEIF5 | DMA_HIFCR_CHTIF5 | DMA_HIFCR_CTCIF5 | - DMA_HIFCR_CTEIF5 | DMA_HIFCR_CDMEIF5; - } - else if (dmaStream == DMA1_Stream6 || dmaStream == DMA2_Stream6) - { - IFCR_MASK = DMA_HIFCR_CFEIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTCIF6 | - DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6; - } - else if (dmaStream == DMA1_Stream7 || dmaStream == DMA2_Stream7) - { - IFCR_MASK = DMA_HIFCR_CFEIF7 | DMA_HIFCR_CHTIF7 | DMA_HIFCR_CTCIF7 | - DMA_HIFCR_CTEIF7 | DMA_HIFCR_CDMEIF7; - } - else - { - IFCR_MASK = 0; - LOG_CRIT(logger, "Could not recognize DMA stream"); - } -} - -inline DMA_TypeDef* DMAStream::getController() { return dmaController; } - -inline DMA_Stream_TypeDef* DMAStream::getStream() { return dmaStream; } - -inline void DMAStream::reset() -{ - // Disable stream - dmaStream->CR &= ~DMA_SxCR_EN; - - // Wait for the stream to be disabled - while (dmaStream->CR & DMA_SxCR_EN) - ; - - // Clear the registers - dmaStream->CR = 0; - dmaStream->FCR = 0; - clearStatusRegister(); - - // Wait for the stream to be disabled - while (dmaStream->CR & DMA_SxCR_EN) - ; -} - -inline void DMAStream::enable() -{ - // Before enabling the stream ensures all status flags are cleared - clearStatusRegister(); - - // Enable the stream - dmaStream->CR |= DMA_SxCR_EN; -} - -inline void DMAStream::disable() { dmaStream->CR &= ~DMA_SxCR_EN; } - -inline void DMAStream::setStreamChannel(Channel channel) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_CHSEL; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(channel); -} - -inline void DMAStream::setStreamMemoryBurstConfiguration( - MemoryBurstConfiguration config) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_MBURST; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(config); -} - -inline void DMAStream::setStreamPeripheralBurstConfiguration( - PeripheralBurstConfiguration config) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_PBURST; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(config); -} - -inline void DMAStream::enableDoubleBufferMode() -{ - dmaStream->CR |= DMA_SxCR_DBM; -} - -inline void DMAStream::disableDoubleBufferMode() -{ - dmaStream->CR &= ~DMA_SxCR_DBM; -} - -inline void DMAStream::setStreamPriorityLevel(PriorityLevel priorityLevel) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_PL; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(priorityLevel); -} - -inline void DMAStream::setMemoryDataSize(MemoryDataSize size) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_MSIZE; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(size); -} - -inline void DMAStream::setPeripheralDataSize(PeripheralDataSize size) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_PSIZE; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(size); -} - -inline void DMAStream::enableMemoryIncrement() -{ - dmaStream->CR |= DMA_SxCR_MINC; -} - -inline void DMAStream::disableMemoryIncrement() -{ - dmaStream->CR &= ~DMA_SxCR_MINC; -} - -inline void DMAStream::enablePeripheralIncrement() -{ - dmaStream->CR |= DMA_SxCR_PINC; -} - -inline void DMAStream::disablePeripheralIncrement() -{ - dmaStream->CR &= ~DMA_SxCR_PINC; -} - -inline void DMAStream::enableCircularMode() { dmaStream->CR |= DMA_SxCR_CIRC; } - -inline void DMAStream::disableCircularMode() { dmaStream->CR |= DMA_SxCR_CIRC; } - -inline void DMAStream::setDataTransferDirection(DataTransferDirection direction) -{ - // First clear the configuration - dmaStream->CR &= ~DMA_SxCR_DIR; - - // Set the new value - dmaStream->CR |= static_cast<uint32_t>(direction); -} - -inline void DMAStream::setPeripheralFlowController() -{ - dmaStream->CR |= DMA_SxCR_PFCTRL; -} - -inline void DMAStream::setDMAFlowController() -{ - dmaStream->CR &= ~DMA_SxCR_PFCTRL; -} - -inline void DMAStream::enableTransferCompleteInterrupt() -{ - dmaStream->CR |= DMA_SxCR_TCIE; -} - -inline void DMAStream::disableTransferCompleteInterrupt() -{ - dmaStream->CR &= ~DMA_SxCR_TCIE; -} - -inline void DMAStream::enableHalfTransferCompleteInterrupt() -{ - dmaStream->CR |= DMA_SxCR_HTIE; -} - -inline void DMAStream::disableHalfTransferCompleteInterrupt() -{ - dmaStream->CR &= ~DMA_SxCR_HTIE; -} - -inline void DMAStream::enableTransferErrorInterrupt() -{ - dmaStream->CR |= DMA_SxCR_TEIE; -} - -inline void DMAStream::disableTransferErrorInterrupt() -{ - dmaStream->CR &= ~DMA_SxCR_TEIE; -} - -inline void DMAStream::enableDirectModeErrorInterrupt() -{ - dmaStream->CR |= DMA_SxCR_DMEIE; -} - -inline void DMAStream::disableDirectModeErrorInterrupt() -{ - dmaStream->CR &= ~DMA_SxCR_DMEIE; -} - -inline void DMAStream::setNumberOfDataItems(uint16_t numberOfDataItems) -{ - dmaStream->NDTR = numberOfDataItems; -} - -inline uint16_t DMAStream::readNumberOfDataItems() { return dmaStream->NDTR; } - -inline void DMAStream::setPeripheralAddress(uint32_t* address) -{ - dmaStream->PAR = reinterpret_cast<uint32_t>(address); -} - -inline void DMAStream::setMemory0Address(uint32_t* address) -{ - dmaStream->M0AR = reinterpret_cast<uint32_t>(address); -} - -inline void DMAStream::setMemory1Address(uint32_t* address) -{ - dmaStream->M1AR = reinterpret_cast<uint32_t>(address); -} - -inline void DMAStream::clearStatusRegister() { *IFCR |= IFCR_MASK; } - -} // namespace Boardcore diff --git a/src/shared/drivers/dma/board_mappings/stm32f407xx_mappings.cpp b/src/shared/drivers/dma/board_mappings/stm32f407xx_mappings.cpp new file mode 100644 index 000000000..613a53d21 --- /dev/null +++ b/src/shared/drivers/dma/board_mappings/stm32f407xx_mappings.cpp @@ -0,0 +1,232 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../DMADefs.h" + +namespace Boardcore +{ + +namespace DMADefs +{ +const std::multimap<Peripherals, std::pair<DMAStreamId, Channel>> + mapPeripherals = { + + /** + * Here are defined the mappings between peripherals and + * streams. + * + * The problem is that some of these stream are used + * by miosix. The corresponding IRQHandlers are already defined + * in there, causing conflicts. + * For this reason the unavailable mappings are commented out. + */ + + // MEM-TO-MEM (only dma2 can perform mem-to-mem copy) + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str1, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str5, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str7, Channel::CHANNEL0}}, + + // SPI + {Peripherals::PE_SPI1_TX, {DMAStreamId::DMA2_Str5, Channel::CHANNEL3}}, + // {Peripherals::PE_SPI1_TX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str2, Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str0, Channel::CHANNEL3}}, + + {Peripherals::PE_SPI2_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL0}}, + // {Peripherals::PE_SPI2_RX, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL0}}, + + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL0}}, + + // UART & USART + {Peripherals::PE_USART1_TX, + {DMAStreamId::DMA2_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_USART1_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL4}}, + {Peripherals::PE_USART1_RX, + {DMAStreamId::DMA2_Str5, Channel::CHANNEL4}}, + + {Peripherals::PE_USART2_TX, + {DMAStreamId::DMA1_Str6, Channel::CHANNEL4}}, + {Peripherals::PE_USART2_RX, + {DMAStreamId::DMA1_Str5, Channel::CHANNEL4}}, + + // {Peripherals::PE_USART3_TX, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL4}}, + {Peripherals::PE_USART3_TX, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL7}}, + // {Peripherals::PE_USART3_RX, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL4}}, + + {Peripherals::PE_UART4_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL4}}, + {Peripherals::PE_UART4_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL4}}, + + {Peripherals::PE_UART5_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_UART5_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL4}}, + + {Peripherals::PE_USART6_TX, + {DMAStreamId::DMA2_Str6, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_TX, + {DMAStreamId::DMA2_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str1, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL5}}, + + // I2C + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL1}}, + + {Peripherals::PE_I2C2_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL7}}, + {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL7}}, + // {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL7}}, + + {Peripherals::PE_I2C3_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL3}}, + {Peripherals::PE_I2C3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL3}}, + + {Peripherals::PE_I2S2_EXT_TX, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL2}}, + // {Peripherals::PE_I2S2_EXT_RX, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL3}}, + + {Peripherals::PE_I2S3_EXT_TX, + {DMAStreamId::DMA1_Str5, Channel::CHANNEL2}}, + {Peripherals::PE_I2S3_EXT_RX, + {DMAStreamId::DMA1_Str2, Channel::CHANNEL2}}, + {Peripherals::PE_I2S3_EXT_RX, + {DMAStreamId::DMA1_Str0, Channel::CHANNEL3}}, + + // TIMERS + {Peripherals::PE_TIM1_UP, {DMAStreamId::DMA2_Str5, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_COM, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str1, Channel::CHANNEL6}}, + // {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH4, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + + // {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + // {Peripherals::PE_TIM2_CH3, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + + {Peripherals::PE_TIM3_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_TRIG, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH1, {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH2, {DMAStreamId::DMA1_Str5, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH4, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + + {Peripherals::PE_TIM4_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH1, {DMAStreamId::DMA1_Str0, Channel::CHANNEL2}}, + // {Peripherals::PE_TIM4_CH2, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL2}}, + + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL6}}, + // {Peripherals::PE_TIM5_TRIG, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL6}}, + // {Peripherals::PE_TIM5_TRIG, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH1, {DMAStreamId::DMA1_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH2, {DMAStreamId::DMA1_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH3, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + // {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL6}}, + // {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str3, + // Channel::CHANNEL6}}, + + // {Peripherals::PE_TIM6_UP, {DMAStreamId::DMA1_Str1, + // Channel::CHANNEL7}}, + + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL1}}, + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str4, Channel::CHANNEL1}}, + + {Peripherals::PE_TIM8_UP, {DMAStreamId::DMA2_Str1, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_TRIG, + {DMAStreamId::DMA2_Str7, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_COM, {DMAStreamId::DMA2_Str7, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str4, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH4, {DMAStreamId::DMA2_Str7, Channel::CHANNEL7}}, + + // Others + {Peripherals::PE_DAC1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL7}}, + {Peripherals::PE_DAC2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL7}}, + + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + + {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL1}}, + // {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str3, Channel::CHANNEL1}}, + + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str0, Channel::CHANNEL2}}, + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str1, Channel::CHANNEL2}}, + + {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str1, Channel::CHANNEL1}}, + {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str7, Channel::CHANNEL1}}, + + // {Peripherals::PE_SDIO, {DMAStreamId::DMA2_Str3, Channel::CHANNEL4}}, + {Peripherals::PE_SDIO, {DMAStreamId::DMA2_Str6, Channel::CHANNEL4}}, + + {Peripherals::PE_CRYP_OUT, {DMAStreamId::DMA2_Str5, Channel::CHANNEL2}}, + {Peripherals::PE_CRYP_IN, {DMAStreamId::DMA2_Str6, Channel::CHANNEL2}}, + + {Peripherals::PE_HASH_IN, {DMAStreamId::DMA2_Str7, Channel::CHANNEL2}}, +}; + +} // namespace DMADefs + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/board_mappings/stm32f429xx_mappings.cpp b/src/shared/drivers/dma/board_mappings/stm32f429xx_mappings.cpp new file mode 100644 index 000000000..9f3a667a9 --- /dev/null +++ b/src/shared/drivers/dma/board_mappings/stm32f429xx_mappings.cpp @@ -0,0 +1,262 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../DMADefs.h" + +namespace Boardcore +{ + +namespace DMADefs +{ +const std::multimap<Peripherals, std::pair<DMAStreamId, Channel>> + mapPeripherals = { + + /** + * Here are defined the mappings between peripherals and + * streams. + * + * The problem is that some of these stream are used + * by miosix. The corresponding IRQHandlers are already defined + * in there, causing conflicts. + * For this reason the unavailable mappings are commented out. + */ + + // MEM-TO-MEM (only dma2 can perform mem-to-mem copy) + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str1, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL0}}, + + // SPI + // {Peripherals::PE_SPI1_TX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL3}}, {Peripherals::PE_SPI1_TX, + // {DMAStreamId::DMA2_Str3, Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str2, Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str0, Channel::CHANNEL3}}, + + {Peripherals::PE_SPI2_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL0}}, + {Peripherals::PE_SPI2_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL0}}, + + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL0}}, + + {Peripherals::PE_SPI4_TX, {DMAStreamId::DMA2_Str1, Channel::CHANNEL4}}, + {Peripherals::PE_SPI4_TX, {DMAStreamId::DMA2_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_SPI4_RX, {DMAStreamId::DMA2_Str0, Channel::CHANNEL4}}, + // {Peripherals::PE_SPI4_RX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL5}}, + + {Peripherals::PE_SPI5_TX, {DMAStreamId::DMA2_Str4, Channel::CHANNEL2}}, + {Peripherals::PE_SPI5_TX, {DMAStreamId::DMA2_Str6, Channel::CHANNEL7}}, + // {Peripherals::PE_SPI5_RX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL2}}, + // {Peripherals::PE_SPI5_RX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL7}}, + + // {Peripherals::PE_SPI6_TX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL1}}, + {Peripherals::PE_SPI6_RX, {DMAStreamId::DMA2_Str6, Channel::CHANNEL1}}, + + // UART & USART + // {Peripherals::PE_USART1_TX, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_USART1_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL4}}, + // {Peripherals::PE_USART1_RX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL4}}, + + {Peripherals::PE_USART2_TX, + {DMAStreamId::DMA1_Str6, Channel::CHANNEL4}}, + {Peripherals::PE_USART2_RX, + {DMAStreamId::DMA1_Str5, Channel::CHANNEL4}}, + + {Peripherals::PE_USART3_TX, + {DMAStreamId::DMA1_Str3, Channel::CHANNEL4}}, + {Peripherals::PE_USART3_TX, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL7}}, + {Peripherals::PE_USART3_RX, + {DMAStreamId::DMA1_Str1, Channel::CHANNEL4}}, + + {Peripherals::PE_UART4_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL4}}, + {Peripherals::PE_UART4_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL4}}, + + {Peripherals::PE_UART5_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_UART5_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL4}}, + + {Peripherals::PE_UART7_TX, {DMAStreamId::DMA1_Str1, Channel::CHANNEL5}}, + {Peripherals::PE_UART7_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL5}}, + + {Peripherals::PE_UART8_TX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL5}}, + {Peripherals::PE_UART8_RX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL5}}, + + {Peripherals::PE_USART6_TX, + {DMAStreamId::DMA2_Str6, Channel::CHANNEL5}}, + // {Peripherals::PE_USART6_TX, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str1, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL5}}, + + // I2C + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL1}}, + + {Peripherals::PE_I2C2_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL7}}, + {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL7}}, + {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL7}}, + + {Peripherals::PE_I2C3_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL3}}, + {Peripherals::PE_I2C3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL3}}, + + {Peripherals::PE_I2S2_EXT_TX, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL2}}, + {Peripherals::PE_I2S2_EXT_RX, + {DMAStreamId::DMA1_Str3, Channel::CHANNEL3}}, + + {Peripherals::PE_I2S3_EXT_TX, + {DMAStreamId::DMA1_Str5, Channel::CHANNEL2}}, + {Peripherals::PE_I2S3_EXT_RX, + {DMAStreamId::DMA1_Str2, Channel::CHANNEL2}}, + {Peripherals::PE_I2S3_EXT_RX, + {DMAStreamId::DMA1_Str0, Channel::CHANNEL3}}, + + // TIMERS + // {Peripherals::PE_TIM1_UP, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_COM, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str1, Channel::CHANNEL6}}, + // {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH4, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + + {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str1, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH3, {DMAStreamId::DMA1_Str1, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + + {Peripherals::PE_TIM3_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_TRIG, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH1, {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH2, {DMAStreamId::DMA1_Str5, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH4, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + + {Peripherals::PE_TIM4_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH1, {DMAStreamId::DMA1_Str0, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH2, {DMAStreamId::DMA1_Str3, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL2}}, + + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_TRIG, + {DMAStreamId::DMA1_Str1, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_TRIG, + {DMAStreamId::DMA1_Str3, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH1, {DMAStreamId::DMA1_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH2, {DMAStreamId::DMA1_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH3, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str1, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str3, Channel::CHANNEL6}}, + + {Peripherals::PE_TIM6_UP, {DMAStreamId::DMA1_Str1, Channel::CHANNEL7}}, + + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL1}}, + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str4, Channel::CHANNEL1}}, + + {Peripherals::PE_TIM8_UP, {DMAStreamId::DMA2_Str1, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_TRIG, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_COM, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str4, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_CH4, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL7}}, + + // Others + {Peripherals::PE_DAC1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL7}}, + {Peripherals::PE_DAC2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL7}}, + + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + + {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL1}}, + // {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str3, Channel::CHANNEL1}}, + + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str0, Channel::CHANNEL2}}, + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str1, Channel::CHANNEL2}}, + + {Peripherals::PE_SAI1_A, {DMAStreamId::DMA2_Str1, Channel::CHANNEL0}}, + // {Peripherals::PE_SAI1_A, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL0}}, + + // {Peripherals::PE_SAI1_B, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL0}}, + {Peripherals::PE_SAI1_B, {DMAStreamId::DMA2_Str4, Channel::CHANNEL1}}, + + {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str1, Channel::CHANNEL1}}, + // {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str7, Channel::CHANNEL1}}, + + // {Peripherals::PE_SDIO, {DMAStreamId::DMA2_Str3, Channel::CHANNEL4}}, + {Peripherals::PE_SDIO, {DMAStreamId::DMA2_Str6, Channel::CHANNEL4}}, + + // {Peripherals::PE_CRYP_OUT, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL2}}, + {Peripherals::PE_CRYP_IN, {DMAStreamId::DMA2_Str6, Channel::CHANNEL2}}, + + // {Peripherals::PE_HASH_IN, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL2}}, +}; + +} // namespace DMADefs + +} // namespace Boardcore diff --git a/src/shared/drivers/dma/board_mappings/stm32f767xx_mappings.cpp b/src/shared/drivers/dma/board_mappings/stm32f767xx_mappings.cpp new file mode 100644 index 000000000..e50f3d7ca --- /dev/null +++ b/src/shared/drivers/dma/board_mappings/stm32f767xx_mappings.cpp @@ -0,0 +1,307 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "../DMADefs.h" + +namespace Boardcore +{ + +namespace DMADefs +{ +const std::multimap<Peripherals, std::pair<DMAStreamId, Channel>> + mapPeripherals = { + + /** + * Here are defined the mappings between peripherals and + * streams. + * + * The problem is that some of these stream are used + * by miosix. The corresponding IRQHandlers are already defined + * in there, causing conflicts. + * For this reason the unavailable mappings are commented out. + */ + + // MEM-TO-MEM (only dma2 can perform mem-to-mem copy) + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str1, Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL0}}, + {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + // {Peripherals::PE_MEM_ONLY, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL0}}, + + // SPI + // {Peripherals::PE_SPI1_TX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL3}}, + // {Peripherals::PE_SPI1_TX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str2, Channel::CHANNEL3}}, + {Peripherals::PE_SPI1_RX, {DMAStreamId::DMA2_Str0, Channel::CHANNEL3}}, + + {Peripherals::PE_SPI2_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL0}}, + {Peripherals::PE_SPI2_TX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL9}}, + {Peripherals::PE_SPI2_RX, {DMAStreamId::DMA1_Str1, Channel::CHANNEL9}}, + {Peripherals::PE_SPI2_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL0}}, + + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_SPI3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL0}}, + + {Peripherals::PE_SPI4_TX, {DMAStreamId::DMA2_Str1, Channel::CHANNEL4}}, + {Peripherals::PE_SPI4_TX, {DMAStreamId::DMA2_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_SPI4_TX, {DMAStreamId::DMA2_Str2, Channel::CHANNEL9}}, + {Peripherals::PE_SPI4_RX, {DMAStreamId::DMA2_Str0, Channel::CHANNEL4}}, + // {Peripherals::PE_SPI4_RX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL5}}, + + {Peripherals::PE_SPI5_TX, {DMAStreamId::DMA2_Str4, Channel::CHANNEL2}}, + {Peripherals::PE_SPI5_TX, {DMAStreamId::DMA2_Str6, Channel::CHANNEL7}}, + // {Peripherals::PE_SPI5_RX, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL2}}, + // {Peripherals::PE_SPI5_RX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL7}}, + // {Peripherals::PE_SPI5_RX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL9}}, + + // {Peripherals::PE_SPI6_TX, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL1}}, + {Peripherals::PE_SPI6_RX, {DMAStreamId::DMA2_Str6, Channel::CHANNEL1}}, + + // {Peripherals::PE_QUADSPI, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL3}}, + {Peripherals::PE_QUADSPI, {DMAStreamId::DMA2_Str2, Channel::CHANNEL11}}, + + // UART & USART + // {Peripherals::PE_USART1_TX, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_USART1_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL4}}, + // {Peripherals::PE_USART1_RX, + // {DMAStreamId::DMA2_Str5, Channel::CHANNEL4}}, + + {Peripherals::PE_USART2_TX, + {DMAStreamId::DMA1_Str6, Channel::CHANNEL4}}, + {Peripherals::PE_USART2_RX, + {DMAStreamId::DMA1_Str5, Channel::CHANNEL4}}, + + {Peripherals::PE_USART3_TX, + {DMAStreamId::DMA1_Str3, Channel::CHANNEL4}}, + {Peripherals::PE_USART3_TX, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL7}}, + {Peripherals::PE_USART3_RX, + {DMAStreamId::DMA1_Str1, Channel::CHANNEL4}}, + + {Peripherals::PE_UART4_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL4}}, + {Peripherals::PE_UART4_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL4}}, + + {Peripherals::PE_UART5_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL4}}, + {Peripherals::PE_UART5_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL4}}, + + {Peripherals::PE_UART7_TX, {DMAStreamId::DMA1_Str1, Channel::CHANNEL5}}, + {Peripherals::PE_UART7_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL5}}, + + {Peripherals::PE_UART8_TX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL5}}, + {Peripherals::PE_UART8_RX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL5}}, + + {Peripherals::PE_USART6_TX, + {DMAStreamId::DMA2_Str6, Channel::CHANNEL5}}, + // {Peripherals::PE_USART6_TX, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str1, Channel::CHANNEL5}}, + {Peripherals::PE_USART6_RX, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL5}}, + + // I2C + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL1}}, + {Peripherals::PE_I2C1_RX, {DMAStreamId::DMA1_Str5, Channel::CHANNEL1}}, + + {Peripherals::PE_I2C2_TX, {DMAStreamId::DMA1_Str7, Channel::CHANNEL7}}, + {Peripherals::PE_I2C2_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL8}}, + {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL7}}, + {Peripherals::PE_I2C2_RX, {DMAStreamId::DMA1_Str3, Channel::CHANNEL7}}, + + {Peripherals::PE_I2C3_TX, {DMAStreamId::DMA1_Str4, Channel::CHANNEL3}}, + {Peripherals::PE_I2C3_TX, {DMAStreamId::DMA1_Str0, Channel::CHANNEL8}}, + {Peripherals::PE_I2C3_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL3}}, + {Peripherals::PE_I2C3_RX, {DMAStreamId::DMA1_Str1, Channel::CHANNEL1}}, + + {Peripherals::PE_I2C4_TX, {DMAStreamId::DMA1_Str6, Channel::CHANNEL8}}, + {Peripherals::PE_I2C4_RX, {DMAStreamId::DMA1_Str2, Channel::CHANNEL2}}, + {Peripherals::PE_I2C4_RX, {DMAStreamId::DMA1_Str1, Channel::CHANNEL8}}, + + // TIMERS + // {Peripherals::PE_TIM1_UP, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_TRIG, + {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_COM, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str1, Channel::CHANNEL6}}, + // {Peripherals::PE_TIM1_CH1, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL0}}, + {Peripherals::PE_TIM1_CH3, {DMAStreamId::DMA2_Str6, Channel::CHANNEL6}}, + {Peripherals::PE_TIM1_CH4, {DMAStreamId::DMA2_Str4, Channel::CHANNEL6}}, + + {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str1, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_UP, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH3, {DMAStreamId::DMA1_Str1, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_TIM2_CH4, {DMAStreamId::DMA1_Str7, Channel::CHANNEL3}}, + + {Peripherals::PE_TIM3_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_TRIG, + {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH1, {DMAStreamId::DMA1_Str4, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH2, {DMAStreamId::DMA1_Str5, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL5}}, + {Peripherals::PE_TIM3_CH4, {DMAStreamId::DMA1_Str2, Channel::CHANNEL5}}, + + {Peripherals::PE_TIM4_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH1, {DMAStreamId::DMA1_Str0, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH2, {DMAStreamId::DMA1_Str3, Channel::CHANNEL2}}, + {Peripherals::PE_TIM4_CH3, {DMAStreamId::DMA1_Str7, Channel::CHANNEL2}}, + + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_UP, {DMAStreamId::DMA1_Str6, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_TRIG, + {DMAStreamId::DMA1_Str1, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_TRIG, + {DMAStreamId::DMA1_Str3, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH1, {DMAStreamId::DMA1_Str2, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH2, {DMAStreamId::DMA1_Str4, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH3, {DMAStreamId::DMA1_Str0, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str1, Channel::CHANNEL6}}, + {Peripherals::PE_TIM5_CH4, {DMAStreamId::DMA1_Str3, Channel::CHANNEL6}}, + + {Peripherals::PE_TIM6_UP, {DMAStreamId::DMA1_Str1, Channel::CHANNEL7}}, + + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str2, Channel::CHANNEL1}}, + {Peripherals::PE_TIM7_UP, {DMAStreamId::DMA1_Str4, Channel::CHANNEL1}}, + + {Peripherals::PE_TIM8_UP, {DMAStreamId::DMA2_Str1, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_TRIG, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_COM, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH1, {DMAStreamId::DMA2_Str2, Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + // {Peripherals::PE_TIM8_CH2, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL7}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str2, Channel::CHANNEL0}}, + {Peripherals::PE_TIM8_CH3, {DMAStreamId::DMA2_Str4, Channel::CHANNEL7}}, + // {Peripherals::PE_TIM8_CH4, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL7}}, + + // Others + {Peripherals::PE_DAC1, {DMAStreamId::DMA1_Str5, Channel::CHANNEL7}}, + {Peripherals::PE_DAC2, {DMAStreamId::DMA1_Str6, Channel::CHANNEL7}}, + + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str0, Channel::CHANNEL0}}, + {Peripherals::PE_ADC1, {DMAStreamId::DMA2_Str4, Channel::CHANNEL0}}, + + {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str2, Channel::CHANNEL1}}, + // {Peripherals::PE_ADC2, {DMAStreamId::DMA2_Str3, Channel::CHANNEL1}}, + + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str0, Channel::CHANNEL2}}, + {Peripherals::PE_ADC3, {DMAStreamId::DMA2_Str1, Channel::CHANNEL2}}, + + {Peripherals::PE_SAI1_A, {DMAStreamId::DMA2_Str1, Channel::CHANNEL0}}, + {Peripherals::PE_SAI1_A, {DMAStreamId::DMA2_Str6, Channel::CHANNEL10}}, + // {Peripherals::PE_SAI1_A, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL0}}, + {Peripherals::PE_SAI2_A, {DMAStreamId::DMA2_Str4, Channel::CHANNEL3}}, + {Peripherals::PE_SAI2_A, {DMAStreamId::DMA2_Str2, Channel::CHANNEL10}}, + + // {Peripherals::PE_SAI1_B, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL0}}, + {Peripherals::PE_SAI1_B, {DMAStreamId::DMA2_Str4, Channel::CHANNEL1}}, + {Peripherals::PE_SAI1_B, {DMAStreamId::DMA2_Str0, Channel::CHANNEL10}}, + // {Peripherals::PE_SAI2_B, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL0}}, + {Peripherals::PE_SAI2_B, {DMAStreamId::DMA2_Str6, Channel::CHANNEL3}}, + {Peripherals::PE_SAI2_B, {DMAStreamId::DMA2_Str1, Channel::CHANNEL10}}, + + {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str1, Channel::CHANNEL1}}, + // {Peripherals::PE_DCMI, {DMAStreamId::DMA2_Str7, Channel::CHANNEL1}}, + + // {Peripherals::PE_SDMMC1, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL4}}, + {Peripherals::PE_SDMMC1, {DMAStreamId::DMA2_Str6, Channel::CHANNEL4}}, + {Peripherals::PE_SDMMC2, {DMAStreamId::DMA2_Str0, Channel::CHANNEL11}}, + // {Peripherals::PE_SDMMC2, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL11}}, + + // {Peripherals::PE_CRYP_OUT, {DMAStreamId::DMA2_Str5, + // Channel::CHANNEL2}}, + {Peripherals::PE_CRYP_IN, {DMAStreamId::DMA2_Str6, Channel::CHANNEL2}}, + + // {Peripherals::PE_HASH_IN, {DMAStreamId::DMA2_Str7, + // Channel::CHANNEL2}}, + + {Peripherals::PE_SPDIFRX_DT, + {DMAStreamId::DMA1_Str1, Channel::CHANNEL0}}, + {Peripherals::PE_SPDIFRX_CS, + {DMAStreamId::DMA1_Str6, Channel::CHANNEL0}}, + + {Peripherals::PE_DFSDM1_FLT0, + {DMAStreamId::DMA2_Str0, Channel::CHANNEL8}}, + {Peripherals::PE_DFSDM1_FLT0, + {DMAStreamId::DMA2_Str4, Channel::CHANNEL8}}, + {Peripherals::PE_DFSDM1_FLT1, + {DMAStreamId::DMA2_Str1, Channel::CHANNEL8}}, + // {Peripherals::PE_DFSDM1_FLT1, + // {DMAStreamId::DMA2_Str5, Channel::CHANNEL8}}, + {Peripherals::PE_DFSDM1_FLT2, + {DMAStreamId::DMA2_Str2, Channel::CHANNEL8}}, + {Peripherals::PE_DFSDM1_FLT2, + {DMAStreamId::DMA2_Str6, Channel::CHANNEL8}}, + // {Peripherals::PE_DFSDM1_FLT3, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL8}}, + // {Peripherals::PE_DFSDM1_FLT3, + // {DMAStreamId::DMA2_Str7, Channel::CHANNEL8}}, + + {Peripherals::PE_JPEG_IN, {DMAStreamId::DMA2_Str0, Channel::CHANNEL9}}, + // {Peripherals::PE_JPEG_IN, {DMAStreamId::DMA2_Str3, + // Channel::CHANNEL9}} + {Peripherals::PE_JPEG_OUT, {DMAStreamId::DMA2_Str1, Channel::CHANNEL9}}, + {Peripherals::PE_JPEG_OUT, {DMAStreamId::DMA2_Str4, Channel::CHANNEL9}}, +}; + +} // namespace DMADefs + +} // namespace Boardcore diff --git a/src/tests/drivers/test-dma-mem-to-mem.cpp b/src/tests/drivers/test-dma-mem-to-mem.cpp new file mode 100644 index 000000000..6eef0b9f2 --- /dev/null +++ b/src/tests/drivers/test-dma-mem-to-mem.cpp @@ -0,0 +1,87 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Authors: Alberto Nidasio, Fabrizio Monti + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include <drivers/dma/DMA.h> +#include <miosix.h> +#include <util/util.h> + +using namespace miosix; +using namespace Boardcore; + +void printBuffer(uint8_t* buffer, size_t size); + +int main() +{ + DMAStreamGuard stream = DMADriver::instance().acquireStreamForPeripheral( + DMADefs::Peripherals::PE_MEM_ONLY); + + if (!stream.isValid()) + { + printf("Error, cannot allocate dma stream\n"); + return 0; + } + + /** + * In this test we want to copy a buffer1 into buffer2 with the DMA. + */ + + uint8_t buffer1[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + uint8_t buffer2[8] = {0}; + + printf("Before:\n"); + printf("Buffer 1:\n"); + printBuffer(buffer1, sizeof(buffer1)); + printf("Buffer 2:\n"); + printBuffer(buffer2, sizeof(buffer2)); + + DMATransaction trn{ + .direction = DMATransaction::Direction::MEM_TO_MEM, + .srcSize = DMATransaction::DataSize::BITS_8, + .dstSize = DMATransaction::DataSize::BITS_8, + .srcAddress = buffer1, + .dstAddress = buffer2, + .numberOfDataItems = sizeof(buffer1), + .srcIncrement = true, + .dstIncrement = true, + .enableTransferCompleteInterrupt = true, + }; + stream->setup(trn); + stream->enable(); + + stream->waitForTransferComplete(); + + printf("After:\n"); + printf("Buffer 1:\n"); + printBuffer(buffer1, sizeof(buffer1)); + printf("Buffer 2:\n"); + printBuffer(buffer2, sizeof(buffer2)); + + return 0; +} + +void printBuffer(uint8_t* buffer, size_t size) +{ + for (size_t i = 0; i < size - 1; i++) + printf("%x,", buffer[i]); + + printf("%x\n", buffer[size - 1]); +} -- GitLab