Move buffers between DDR, OCM and BRAM, maintain cache coherence and place ELF sections in the correct memory.
DMA moves data while the processor can do other work. Evaluate setup, cache maintenance, and completion overhead rather than bus throughput alone.
Compare several paths
The proposed benchmark copies 4096-word blocks through these paths:
Source
Destination
Boundary crossed
DDR
DDR
PS memory
BRAM
BRAM
PL memory
DDR
BRAM
PS to PL
BRAM
DDR
PL to PS
TTC measures ticks. Convert them using its clock and prescaler and handle counter wraparound; they are not automatically CPU cycles. Compare every destination word after each copy.
Prepare noncoherent buffers
for (u32 i = 0; i < word_count; i++) { source[i] = 0x10000000U + i; destination[i] = 0U;}Xil_DCacheFlushRange((UINTPTR)source, word_count * sizeof(u32));Xil_DCacheFlushRange((UINTPTR)destination, word_count * sizeof(u32));start_dma(source, destination, word_count);wait_for_dma_done();Xil_DCacheInvalidateRange((UINTPTR)destination, word_count * sizeof(u32));
The CPU wrote both buffers. Cleaning them removes dirty cache state before DMA. After completion, destination invalidation makes later reads fetch fresh data.
Implement start_dma and wait_for_dma_done for the controller: PL330/XDmaPs on Zynq-7000, or ZDMA/XZDma in an appropriate MPSoC design. Include error handling and bounded waiting.
DMA operation
CPU action
DMA reads CPU-produced data
Clean or flush the source
DMA writes CPU-consumed data
Deal with dirty lines before DMA, then invalidate after completion
Coherent DMA path
Use correct memory attributes and coherent port
Buffers need aligned, dedicated cache lines. The CPU must not modify them during transfer. Maintenance must cover all touched lines; sharing endpoint lines with neighboring data risks corruption.
Disabling caches can isolate a fault but changes performance substantially. Establish a correct cache policy afterward rather than retaining the workaround.
Distinguish addresses
Respect controller and bus alignment; unaligned access may need a specific mode. Distinguish C address, physical DMA address, and AXI-master-visible address. They often coincide in simple standalone code but not necessarily under Linux with an MMU.
Place program sections
Section
Contents
.text
Instructions
.rodata
Read-only constants
.data
Initialized variables
.bss
Zero-initialized storage
heap
Dynamic allocation
stack
Local variables and call context
A linker script maps sections to memory regions. This excerpt illustrates main code in DDR and a fast handler in OCM:
It is not a complete script. Retain required startup, data, heap, and stack sections. Proposed addresses must be accessible and initialized on the target.
The attribute places this function in .fast_isr. It records completion; a real handler must also acknowledge the DMA source as required.
Measure total cost
Include configuration, maintenance, and waiting. For a few words, DMA overhead can exceed CPU copying; larger repeated blocks may benefit. Repeat measurements, check timer frequency, and keep UART output outside the timed region.
UG585 describes Zynq-7000 DMA and memory; the AXI DMA driver covers a DMA in PL.