Several times in this series we have used direct memory access (DMA) to transfer data from the programmable logic (PL) to the processing system (PS) in a Zynq MPSoC.

One example of such a transfer is when we implement image processing systems and use VDMA to transfer the image to the PS DDR.

However, there are times we want to transfer data entirely within the PS domain (e.g. DRAM to DRAM) or from the PS the PL. This is where the DMAs within the PS come into play.

Within the PS, there are two DMA controllers — one located in the low power domain (LPD) and another located within the full power domain (FPD).

Both DMA controllers offer eight channels and can implement both simple and scatter gather transfers, owever there are a few differences. For example:

FPD DMA transfers are 128-bit and not coherent with the cache coherent interconnect (CCI).

LPD DMA transfers are 64 bits and I/O coherent with the cache coherent interconnect (CCI)

In the Zynq MPSoC memory space, the eight channels for the LPD start at address 0xFFA80000, while the eight channels for the FPD DMA start at address 0xFD500000.

Let’s take a look at what we need to get up and running with a simple example. As always we start with Vivado. The board I am targeting for this blog is the Ultra96.

In this example, we are going to use the FPD AXI master port on the PS to transfer data to a BRAM in the PL. Of course, we can also transfer data internally with the PS DMA, e.g DDR to DDR.

For this example I set the AXI BRAM controller as below:

AXI BRAM controller settings

When it comes to using both LPD and FPD DMA within SDK, the parameters are defined as always within Xparameters.h

Now, here is where it can get a little confusing. Both FPD and LPD are referred to as general purpose DMA. As such they are covered by the General Purpose DMA API defined within the file Xzdma.h.

However, within Xparameters.h you may notice the definitions of the LPD DMA is referred to as ADMA while the FPD DMA is defined as GDMA.

In our software application, we are going to do the following:

Initialize the DMA, BRAM controller and interrupt controller.

Set up the interrupt controller to trigger of the FPD DMA channel 0 interrupt.

Generate the test data, in the source destination memory.

Configure the FPD DMA to perform a simple transfer from the source memory to destination.

Once the transfer is complete, check for any DMA errors and check the integrity of the data transferred.

The example can be found on my GitHub; however, the main body of the DMA config and transfer can be seen below.

DMA configuration

When I ran the code on my Ultra96, I observed the following output on the terminal showing no errors and that the DMA transfer was working as expected.

Start of example showing first 10 elements of source and destination buffers

Post transfer first 10 elements of source and destination buffer

Of course, using the PS DMA to transfer large amounts of data from the PS the PL is more efficient than using one of the APU cores to transfer the data.

Now we now how to transfer data efficiently within the PS and in both directions between the PS and PL.

See My FPGA / SoC Projects: Adam Taylor on Hackster.io

Get the Code: ATaylorCEngFIET (Adam Taylor)

Access the MicroZed Chronicles Archives with over 270 articles on the Zynq / Zynq MpSoC updated weekly at MicroZed Chronicles.