Reliable transfer from AXI4-Stream Data FIFO using AXI DMA

Hello, I implemented the design from the following tutorial:
https://www.youtube.com/watch?v=K4OkNH17hiA

The issue I’m having is that whenever I attempt to read less than the fill level of FIFO, it results in error, here’s one example.

The same thing happens when I first call transfer on the receiving channel (with nbytes=8 just like on example above), and then send more data through the send channel.

I collected input and output of the FIFO using logic analyser. The waveform below shows that despite requesting a single item (using nbytes=8, and in my case each item has 8 bytes), the DMA receives all 16 items that were sent (128 bytes instead of 8).

There are few tutorials about using AXI4-Stream Data FIFO with DMA but none of them is really showing how to work with continuous data stream, in each tutorial I’ve seen the FIFO is first supplied with specific number of items, is there some standard practice when the FIFO supplier is continuously trying to fill it? What could be the reason why the DMA reads more data than requested using “nbytes” (which results in an error)?

Here’s some rather messy code I used for testing, in case if anyone finds it useful to reproduce the issue:
dma_test.ipynb (20.2 KB)

I’m using pynq 2.7, Ultra96-V2 board (rev 1.1), and Vivado 2020.1.

My main goal is to read (in python) and process the data from FIFO while it’s being filled continuously.

Edit:
I noticed that following the error, the DMAIntErr register is set to 1

How wide are your data interfaces for your DMA and through your data path?

Note that you don’t need to wait() after you call the first (send) transfer. You usually only need to call wait() after the ‘receive’ to make sure you don’t try to access results before they are ready.

You can start a send a large transfer that may not be able to transfer or buffer more than a few samples due to limited FIFO/data path buffer. You can then call the ‘receive’ which will start to read the output, freeing up space in the data path, allowing the ‘send’ to continue sending more data.

Cathal

They’re 64 bits if I’m not mistaken

image

I did some more tests and it looks like the DMA ignores things like buffer length or “nbytes” argument supplied to dma.recvchannel.transfer and determines how many bytes to transfer by the presence of “tlast” set to 1. If “tlast” is 0, it will attempt to read any number of available data from S_AXIS_S2MM (which seems to be described in documentation)

Then, AXI DMA writes start at the Buffer Address and continues until tlast is
received from the streaming side.

In transfer method of _SDMAChannel class (from dma.py), the nbytes doc string states:

    nbytes : int
         Number of bytes to transfer. Default is 0.

The transfer function writes the nbytes to S2MM_LENGTH register (at 0x28 offset):

self._mmio.write(self._offset + 0x28, nbytes)

Then the S2MM_LENGTH register value gets automatically updated to the number of received bytes (controlled by “tlast”) if I’m not mistaken.

It seems to me that the purpose of nbytes is to let the hardware know the buffer end (e.g. to flag potential errors by setting things like “DMAIntErr”), not really because it’s some feature for users who would like to receive arbitrary number of bytes.

Is “tlast” correct setting the only way to determine the size of received data through DMA? Is it plausible to use GPIO that will let the AXI stream producer (e.g. FIFO) know when to set the “tlast” to 1? Or would it be better to set “tlast” every N number of items and receive fixed length chunks?

Are my considerations correct? I feel like there must be a simple way to do what I want but I dwell on it because I’m not familiar with AXI DMA.