Intel® FPGA Software Installation & Licensing
Installation and Licensing that’s includes Intel Quartus® Prime software, ModelSim* - Intel FPGA Edition software, Nios® II Embedded Design Suite on Windows or Linux operating systems.

Problem with mSGDMA transfer


Dear readers,

I am an electronics engineer and an experienced FPGA programmer. However, I am new to the Cyclone V SoC.


Currently, I am designing a vision system, in which the FPGA controls a scanning camera, and the HPS needs to store the captured images, and preforms some processing on them. For the camera control, use the building blocks from Platform Designer (QSYS), and all seems to work properly. For capturing the images, I want to let the FPGA write the video stream directly into the HPS SDRAM memory.

To gain some knowledge on the subject, I downloaded the example project from Critical Link:

This uses a mSGDMA Dispatcher IP, a DMA Write Master IP, and a user defined Avalon Streaming Source. I adapted this design to our own custom board based on a 5CSEBA2U23C7S. When I synthesize this example, all seem to work. Since, I am not familiar with programming in Linux, I use U-Boot memory commands to test the design. I reserved memory space 0x07000000 through 0x07FFFFFF for the DMA transfer, the msgDMA descriptor is connected to the lightweight bridge on location 0xFF200300, and the CSR is at 0xFF200200.

I edited the streaming component a bit. It does not use the package mode anymore, and is resettable by bit 2 of a general purpose output on location 0xFF200010. Bits 1 and 0 are connector to LEDs, so I can create also some extra visual information. If bit 2 is zero the component outputs the counter values. When bit 2 is one, the counter is being reset, and while remaining one, the component only outputs zeroes. The VHDL of the streaming component is now:

entity st_cnt_src_otto is

 port (

   clk           : in std_logic                    := '0'; -- clock.clk

   reset         : in std_logic                    := '0'; -- reset.reset

   aso_out0_data : out std_logic_vector(63 downto 0);       --

   aso_out0_ready : in std_logic                    := '0'; --     .ready

   aso_out0_valid : out std_logic;                           --     .valid

   conduit_end_new_signal  : in std_logic                    -- preset.set


end entity st_cnt_src_otto;

architecture rtl of st_cnt_src_otto is

signal slice : unsigned(13 downto 0);


 process(clk, reset)


   if reset = '1' then

     slice <= (others=>'0');

     aso_out0_valid <= '0';

   elsif rising_edge(clk) then

     if aso_out0_ready = '1' then

       aso_out0_valid <= '1';

       slice         <= slice + 1;

     end if;

     if conduit_end_new_signal = '1' then

       slice <= (others=>'0');

     end if;

   end if;

 end process;

 aso_out0_data <= std_logic_vector(Slice) & "00" & std_logic_vector(Slice) & "01" &

                   std_logic_vector(Slice) & "10" & std_logic_vector(Slice) & "11" when conduit_end_new_signal = '0'

                   else (others=>'0');

end architecture rtl; -- of st_cnt_src_otto

First I initialize the memory with a recognizable pattern:


mw 07000000 21646142 200 // write string “BAD!” to SDRAM

md.b 07000000 110 // readback the memory

Second, I start the DMA and read back the memory:

mw ff200010 7          // reset the counter, and LEDs on

mw ff200010 0          // release the counter, and LEDs off

mw ff200304 07000000 1 // write destination address in descriptor

mw ff200308 00000100 1 // set length of transport to 256 bytes

mw ff20030C 80000000 1 // start the transport

md.b 07000000 110      // read back the memory


This looks OK, but now I switch to outputting zeroes:

// write xx zeroes

mw ff200010 7          // reset the counter, and keep output to all zeroes

mw ff200304 07000000 1

mw ff200308 00000100 1

mw ff20030C 80000000 1

md.b 07000000 110

It appears the counter was first still running, and from address 0x80 the zeroes appear. Running the DMA again fills the memory correctly

As far as I can see, it seems the DMA action reads more data (0x80 bytes) than needed, and keeps that in a FIFO. When starting again those bytes are output first, before the new data come. In QSYS I have set the FIFO length at 16 at a data with of 64, which matches the 128 (0x80) bytes I see in the output. I played a lot with the parameters in QSYS, but I couldn’t find a solution. Does anyone recognized this behavior? If so, is there a solution, for instance on how to empty the FIFO?


Additional info: I looked into the generated code for the interface between the streaming source and the write master:


snk_data  : in std_logic_vector(63 downto 0) := (others => 'X'); -- data

snk_valid : in std_logic                     := 'X';            -- valid

snk_ready : out std_logic;                                        -- ready

snk_sop   : in std_logic                     := 'X';            -- startofpacket

snk_eop   : in std_logic                     := 'X';            -- endofpacket

snk_empty : in std_logic_vector(2 downto 0)  := (others => 'X'); -- empty


In the demo, the empty signal is not used, the sop and eop are also not used in my application. This leaves only the data and the valid and ready signals. I placed these signals on an oscilloscope to monitor them. As soon as I start the DMA transfer, the signal ready becomes high. In response, I make signal valid high and output the sample counter data. As soon as signal valid becomes low again, I  stop the transfer by pulling valid low. I guess that signal ready becomes low as soon as the programmed number of bytes are transferred to the SDRAM. In my opinion, I think that the FIFO is still full of data. Apparently, valid and ready cannot be used as flow control. I can probably fix this by instructing the streaming unit to output exactly the same number of bytes as the write unit expects. But what about if an error occurs? Then there could be data in the FIFO again, and I still need to flush the FIFO.


Otto Tuil (the Netherlands)


0 Kudos
1 Reply

In the meantime I had some progress.

The f2h_sdram0_clock of the bridge is connected to a 100 MHz clock source. The same clock runs the h2f lw bridge, and also the peripherals (including the mSGDMA IP) connected to this bridge. The st_sink (Avalon Streaming Sink) contains three signals: input sink_data, input sink_valid, and output sink_ready. It appears that sink_valid may not be active permanently while sink_ready is active, but may only be active at minimum once every two clock cycles. Otherwise the DMA transfer may miss a word when the FIFO gets full.

In addition, when I reset the mSGDMA (writing bit 1 of the control register) prior to starting the DMA by writing the descriptor (including the “go” bit), the transfer is correct. If I do not reset the DMA, I receive the contents of the FIFO of the previous transfer prior to the correct data. I still appears that sink_ready is active during the descriptor‘s length plus the size of the FIFO. So, I have to clear the FIFO with the reset command, before starting a new transfer. I still don’t know why this is needed, but with this work around, I can capture the images from the camera.



0 Kudos