diff --git a/CMakeLists.txt b/CMakeLists.txt index bab43939a686d7586dd75b0a22557adbb5dde95f..05d709a1b3e759e450cd7f50b3a0fb7552d0901b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -285,7 +285,7 @@ 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 stm32f407vg_stm32f4discovery) +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/src/shared/drivers/dma/DMA.cpp b/src/shared/drivers/dma/DMA.cpp index d6b79b46cd1f28c4f07bf719b10f15a920861284..844c1fdba04a1577fbe5992b6a5f1929599a2461 100644 --- a/src/shared/drivers/dma/DMA.cpp +++ b/src/shared/drivers/dma/DMA.cpp @@ -579,6 +579,10 @@ void DMAStream::waitForTransferComplete() 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) @@ -598,7 +602,52 @@ bool DMAStream::timedWaitForTransferComplete( std::bind(&DMAStream::getTransferCompleteFlagStatus, this), std::bind(&DMAStream::clearTransferCompleteFlag, this), transferCompleteFlag, timeout_ns.count()); + +#ifdef STM32F767xx + invalidateCache(); +#endif // STM32F767xx +} + +#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. + */ + + // 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) { diff --git a/src/shared/drivers/dma/DMA.h b/src/shared/drivers/dma/DMA.h index cabb4de6f3994c6267b23aec010241a08a26562e..e23bec7120509a0ca12e9e07992f73279cff204f 100644 --- a/src/shared/drivers/dma/DMA.h +++ b/src/shared/drivers/dma/DMA.h @@ -161,6 +161,8 @@ public: * @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. It is up to the user. */ void waitForHalfTransfer(); @@ -168,6 +170,8 @@ public: * @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(); @@ -178,6 +182,8 @@ public: * @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. It is up to the user. */ bool timedWaitForHalfTransfer(std::chrono::nanoseconds timeout_ns); @@ -185,6 +191,8 @@ public: * @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. @@ -433,6 +441,16 @@ private: 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;