DMA Cache Management
Direct Memory Access (DMA) cache coherency functions for Daisy hardware.
This module provides functions to maintain cache coherency when using DMA (Direct Memory Access) peripherals on the STM32H750 microcontroller.
Cache Coherency Problem
The STM32H750 has data caches (D-cache) that sit between the CPU and SRAM. When using DMA:
- CPU accesses cache, but DMA accesses SRAM directly
- Cache and SRAM can become out of sync
- This causes data corruption in DMA transfers
Example Problem:
var buffer = [1'u8, 2, 3, 4] # CPU writes to cache, but not yet to SRAM # DMA reads from SRAM → gets old/garbage data!
Solutions
Option 1: Disable cache for DMA buffers (Recommended)
Place DMA buffers in D2 memory domain with cache disabled:
var buffer {.emit: "DMA_BUFFER_MEM_SECTION".}: array[256, uint8]
This is the cleanest solution - no manual cache management needed.
Option 2: Manual cache management (When Option 1 is not possible)
Use the functions in this module:
var buffer: array[256, uint8] # Before DMA transmit (write) dmaClearCache(buffer) # Flush cache to SRAM startDmaTransmit(buffer) # After DMA receive (read) waitForDmaComplete() dmaInvalidateCache(buffer) # Reload cache from SRAM # Now buffer contains DMA-received data
When to Use Each Function
Use `dmaClearCache()` before DMA transmit (TX):
- CPU wrote data to buffer
- About to transmit via DMA (SPI, I2C, UART, etc.)
- Need to flush CPU cache changes to SRAM
Use `dmaInvalidateCache()` after DMA receive (RX):
- DMA wrote data to buffer
- Need to read the data with CPU
- Need to discard stale cache and reload from SRAM
Complete Example
SPI transmit with DMA:
var txBuffer: array[128, uint8] # CPU prepares data for i in 0..<128: txBuffer[i] = uint8(i) # Flush cache before DMA reads from SRAM dmaClearCache(txBuffer) # Start DMA transfer spi.dmaTransmit(txBuffer.addr, 128) # Wait for completion while spi.isBusy(): discard
SPI receive with DMA:
var rxBuffer: array[128, uint8] # Start DMA transfer spi.dmaReceive(rxBuffer.addr, 128) # Wait for completion while spi.isBusy(): discard # Invalidate cache before CPU reads from buffer dmaInvalidateCache(rxBuffer) # Now safe to read echo "First byte: ", rxBuffer[0]
Performance Notes
- Cache operations are relatively expensive (microseconds)
- Prefer Option 1 (DMA_BUFFER_MEM_SECTION) when possible
- Only use manual cache management when necessary
- Call cache functions as close as possible to DMA operations
See Also
- sys/system - System initialization and cache config
- per/spi - SPI with DMA support
- per/i2c - I2C with DMA support
- examples/system_control.nim - DMA usage examples
Procs
proc dmaClearCacheFor(p: pointer; size: int) {....raises: [], tags: [], forbids: [].}
-
Clear (flush) cache for arbitrary memory region.
Use when you need fine-grained control over cache operations.
Parameters:
- p - Pointer to memory region
- size - Size in bytes
Example:
type MyStruct = object field1: uint32 field2: array[16, uint8] var data: MyStruct dmaClearCacheFor(data.addr, sizeof(MyStruct))
Source Edit proc dmaInvalidateCacheFor(p: pointer; size: int) {....raises: [], tags: [], forbids: [].}
-
Invalidate cache for arbitrary memory region.
Use when you need fine-grained control over cache operations.
Parameters:
- p - Pointer to memory region
- size - Size in bytes
Example:
type MyStruct = object field1: uint32 field2: array[16, uint8] var data: MyStruct # After DMA receive dmaInvalidateCacheFor(data.addr, sizeof(MyStruct))
Source Edit proc dsy_dma_clear_cache_for_buffer(buffer: ptr uint8; size: csize_t) {. importc: "dsy_dma_clear_cache_for_buffer", header: "sys/dma.h", ...raises: [], tags: [], forbids: [].}
-
Clear (flush) CPU cache to SRAM for a buffer (low-level).
Use before DMA transmit to ensure SRAM has latest data.
Parameters:
- buffer - Pointer to buffer start
- size - Buffer size in bytes
Note: Prefer the type-safe dmaClearCache[T]() template.
Example:
var data: array[64, uint8] dsy_dma_clear_cache_for_buffer(cast[ptr uint8](data[0].addr), 64)
Source Edit proc dsy_dma_deinit() {.importc: "dsy_dma_deinit", header: "sys/dma.h", ...raises: [], tags: [], forbids: [].}
-
Deinitialize DMA peripheral (low-level).
This is called automatically by System.deInit(). You rarely need to call this directly.
Source Edit proc dsy_dma_init() {.importc: "dsy_dma_init", header: "sys/dma.h", ...raises: [], tags: [], forbids: [].}
-
Initialize DMA peripheral (low-level).
This is called automatically by System.init(). You rarely need to call this directly.
Initializes DMA controllers used by libDaisy peripherals (SPI, I2C, UART, SAI, etc.).
Source Edit proc dsy_dma_invalidate_cache_for_buffer(buffer: ptr uint8; size: csize_t) {. importc: "dsy_dma_invalidate_cache_for_buffer", header: "sys/dma.h", ...raises: [], tags: [], forbids: [].}
-
Invalidate CPU cache for a buffer, reload from SRAM (low-level).
Use after DMA receive to ensure CPU reads latest data.
Parameters:
- buffer - Pointer to buffer start
- size - Buffer size in bytes
Note: Prefer the type-safe dmaInvalidateCache[T]() template.
Example:
var data: array[64, uint8] dsy_dma_invalidate_cache_for_buffer(cast[ptr uint8](data[0].addr), 64)
Source Edit
Templates
template dmaClearCache[T](buffer: var openArray[T])
-
Clear (flush) CPU cache to SRAM before DMA transmit.
Type-safe template that automatically calculates buffer size.
When to use:
- CPU wrote data to buffer
- About to transmit via DMA
- Need cache changes written to SRAM
Parameters:
- buffer - Array or openArray to flush
Example:
var txData: array[256, uint8] for i in 0..<256: txData[i] = uint8(i) dmaClearCache(txData) # Flush cache spi.dmaTransmit(txData.addr, 256)
Generic types supported:
var byteArray: array[128, uint8] var wordArray: array[64, uint16] var floatArray: array[32, float32] dmaClearCache(byteArray) # Works dmaClearCache(wordArray) # Works dmaClearCache(floatArray) # Works
Source Edit template dmaInvalidateCache[T](buffer: var openArray[T])
-
Invalidate CPU cache after DMA receive, reload from SRAM.
Type-safe template that automatically calculates buffer size.
When to use:
- DMA wrote data to buffer
- Need to read data with CPU
- Need to discard stale cache
Parameters:
- buffer - Array or openArray to invalidate
Example:
var rxData: array[256, uint8] spi.dmaReceive(rxData.addr, 256) while spi.isBusy(): discard dmaInvalidateCache(rxData) # Reload from SRAM echo "Received: ", rxData[0]
Generic types supported:
var byteArray: array[128, uint8] var wordArray: array[64, uint16] var floatArray: array[32, float32] dmaInvalidateCache(byteArray) # Works dmaInvalidateCache(wordArray) # Works dmaInvalidateCache(floatArray) # Works
Source Edit