UPGRADE YOUR BROWSER

We have detected your current browser version is not the latest one. Xilinx.com uses the latest web technologies to bring you the best online experience possible. Please upgrade to a Xilinx.com supported browser:Chrome, Firefox, Internet Explorer 11, Safari. Thank you!

cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Explorer
Explorer
2,667 Views
Registered: ‎05-14-2017

AXI DMA S2MM issue

Jump to solution

Hi all,

I am testing AXI DMA S2MM communication with a simple AXI4-Stream Counter:

Screenshot_2019-02-21_19-42-00.pngScreenshot_2019-02-21_19-42-52.pngScreenshot_2019-02-21_19-43-53.pngThe AxiStreamCounter_0 IP  is a 32 bit counter that increments TDATA on each positive clock edge with no upper limit and it asserts TLAST every 256 bits. I have no problem to show the code if necessary.

Both AxiStreamCounter_0 and axis_data_fifo_0 S_AXIS subsystem run at lower frequency then the AXI DMA.

To test the design, I wrote this simple code based on PG021. It just turns on the DMA in Direct Register mode, configures the lower address, the interrupts and runs the while loop:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"

/*
   A DMA operation for the S2MM channel is set up and started by the following sequence:

   1. 	Start the S2MM channel running by setting the run/stop bit to 1 (S2MM_DMACR.RS = 1).
   The halted bit (DMASR.Halted) should deassert indicating the S2MM channel is
   running.

   2. 	If desired, enable interrupts by writing a 1 to S2MM_DMACR.IOC_IrqEn and S2MM_DMACR.Err_IrqEn.
   The delay interrupt, delay count, and threshold count are not used when
   the AXI DMA is configured for Direct Register Mode.

   3. 	Write a valid destination address to the S2MM_DA register. If AXI DMA is configured
   for an address space greater than 32, program the S2MM_DA MSB register.

   4. 	If the AXI DMA is not configured for Data Re-Alignment then a valid address must be
   aligned or undefined results occur. What is considered aligned or unaligned is based on
   the stream data width.

   For example, if Memory Map Data Width= 32, data is aligned if it is located at word
   offsets (32-bit offset), that is, 0x0, 0x4, 0x8, 0xC, and so forth. If DRE is enabled and
   Streaming Data Width < 128 then the Destination Addresses can be of any byte offset.
   */

#define MEM_DDR_ADDR 0x01000000

#define C_BASE_ADDR 0x40400000

#define S2MM_DMACR C_BASE_ADDR + 0x30
// S2MM_DMASR S2MM DMA Status register
#define S2MM_DMASR C_BASE_ADDR + 0x34
// S2MM Destination Address. Lower 32 bit address.
#define S2MM_DA C_BASE_ADDR +0x48
// S2MM Destination Address. Upper 32 bit address.
#define S2MM_DA_MSB C_BASE_ADDR + 0x4c
// S2MM Buffer Length (Bytes)
#define S2MM_LENGTH C_BASE_ADDR + 0x58

#define INTR_ID XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

void resetDMA(void)
{
    Xil_Out32(S2MM_DMACR, 0x00000004);
}

uint32_t getIOCIRQU(void)
{
    return Xil_In32(S2MM_DMASR) & 0x5000;
}

void clearIOCIRQU(void)
{
    uint32_t value = Xil_In32(S2MM_DMASR);
    value |= 0x5000;
    Xil_Out32(S2MM_DMASR, value);
}

void setIOCIRQU(void)
{
    uint32_t value = Xil_In32(S2MM_DMACR);
    value |= 0x00007000;
    Xil_Out32(S2MM_DMACR, value);
}

void startDMA(void)
{
    uint32_t reg = Xil_In32(S2MM_DMACR);
    reg |= 0x00000001;
    Xil_Out32(S2MM_DMACR, reg);
}

void stopDMA(void)
{
    uint32_t reg = Xil_In32(S2MM_DMACR);
    reg &= 0xfffffffe;
    Xil_Out32(S2MM_DMACR, reg);
}

uint32_t hasSG(void)
{
    return Xil_In32(S2MM_DMASR) & 0x00000008;
}

uint32_t getS2MMLAddr(void)
{
    return Xil_In32(S2MM_DA);
}

uint32_t getS2MMHAddr(void)
{
    return Xil_In32(S2MM_DA_MSB);
}

void setS2MMLAddr(uint32_t value)
{
    Xil_Out32(S2MM_DA, value & 0xffffffff);
}

void setS2MMHAddr(uint32_t value)
{
    return Xil_Out32(S2MM_DA_MSB, value & 0xffffffff);
}

uint32_t getS2MMLenght(void)
{
    return Xil_In32(S2MM_LENGTH);
}

void setS2MMLenght(uint32_t value)
{
    Xil_Out32(S2MM_LENGTH, value & 0x1ffffff);
}

int main()
{
    uint32_t s2mmLAddr = 0;
    uint32_t s2mmHAddr = 0;
    uint32_t s2mmLenght = 0;
    uint8_t sgIncluded = 0;

    init_platform();

    for(uint32_t i = 0; i < 100; i++)
        printf("\r\n");

    sgIncluded = hasSG();
    printf("********* In [ %s ] *********\r\n", __FUNCTION__);
    printf("SG %s included\r\n", (sgIncluded) ? "is":"is not");

    s2mmLAddr = getS2MMLAddr();
    s2mmHAddr = getS2MMHAddr();
    s2mmLenght = getS2MMLenght();

    startDMA();
    setIOCIRQU();

    printf("DMA Control Register = 0x%.8lx\r\n", Xil_In32(S2MM_DMACR));

    setS2MMLAddr(MEM_DDR_ADDR);
    setS2MMHAddr(0x3fffffff);

    s2mmLAddr = getS2MMLAddr();
    s2mmHAddr = getS2MMHAddr();
    setS2MMLenght(0x000001ff);
    s2mmLenght = getS2MMLenght();

    printf("Register length = 0x%.8lx\r\n", s2mmLenght);
    printf("Low Address = 0x%.8lx\r\n", s2mmLAddr);
    printf("High Address = 0x%.8lx\r\n", s2mmHAddr);

    while(1)
    {
        setS2MMLenght(0x000001ff);
        uint32_t *datap = (uint32_t *) s2mmLAddr;
        while (!getIOCIRQU())
            ;
        clearIOCIRQU();

        for(uint8_t i = 0; i < 8; i++)
        {
            printf("@%.8lx => data[%02d] = 0x%.8lx\r\n", datap, i, *datap);
            datap++;
        }
    }

    printf("********* Out [ %s ] *********\r\n", __FUNCTION__);
    cleanup_platform();
    return 0;
}

With this code I would expect to read the counter's whole range of values (0 to 0xFFFFFFFF), however I repeatedly read the same values:

********* In [ main ] *********
SG is not included
DMA Control Register = 0x00017003
Register length = 0x000001ff
Low Address = 0x01000000
High Address = 0x00000000
@01000000 => data[00] = 0x00000007
@01000004 => data[01] = 0x00000008
@01000008 => data[02] = 0x00000009
@0100000c => data[03] = 0x0000000a
@01000010 => data[04] = 0x0000000b
@01000014 => data[05] = 0x0000000c
@01000018 => data[06] = 0x0000000d
@0100001c => data[07] = 0x0000000e
@01000000 => data[00] = 0x00000007
@01000004 => data[01] = 0x00000008
@01000008 => data[02] = 0x00000009
@0100000c => data[03] = 0x0000000a
@01000010 => data[04] = 0x0000000b
@01000014 => data[05] = 0x0000000c
@01000018 => data[06] = 0x0000000d
@0100001c => data[07] = 0x0000000e
@01000000 => data[00] = 0x00000007
@01000004 => data[01] = 0x00000008
@01000008 => data[02] = 0x00000009
@0100000c => data[03] = 0x0000000a
@01000010 => data[04] = 0x0000000b
@01000014 => data[05] = 0x0000000c
@01000018 => data[06] = 0x0000000d
@0100001c => data[07] = 0x0000000e
@01000000 => data[00] = 0x00000007
@01000004 => data[01] = 0x00000008
@01000008 => data[02] = 0x00000009
@0100000c => data[03] = 0x0000000a
@01000010 => data[04] = 0x0000000b
@01000014 => data[05] = 0x0000000c
@01000018 => data[06] = 0x0000000d
@0100001c => data[07] = 0x0000000e
@01000000 => data[00] = 0x00000007
@01000004 => data[01] = 0x00000008
@01000008 => data[02] = 0x00000009
@0100000c => data[03] = 0x0000000a
@01000010 => data[04] = 0x0000000b
@01000014 => data[05] = 0x0000000c
@01000018 => data[06] = 0x0000000d
@0100001c => data[07] = 0x0000000e
@01000000 => data[00] = 0x00000007

Could someone suggest what I am doing wrong ?

Thanks

simozz

Tags (2)
0 Kudos
1 Solution

Accepted Solutions
Explorer
Explorer
2,304 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

It seems I finally solved this issue.

The main problem seems to be the missing call to Xil_DCacheFlushRange function in my code. That's why the address content was not being updated.

Then I applied two small changes to the design:

1) I included an init clock counter after reset before asserting TVALID. This is not mandatory but I think it's not bad as well.

2) I used the memory address that Xilinx's examples use:

 

#define MEM_BASE_ADDR 0x01000000

#define TX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x004FFFFF)

so the pseudo-code for S2MM only (with no interrupts) is:

1  - reset DMA

2  - start DMA

3 - set Low address to RX_BUFFER_BASE

4 - init while(1) loop

4.1 - set S2MM Length

4.2 - while DMA is not Idle, wait

4.3 - read data according to the burst size (or the content updated S2MM Length)

4.4 - call Xil_DCacheFlushRange

4.5 - returns to 4.1

Regards,

simozz

 

View solution in original post

16 Replies
Scholar jg_bds
Scholar
2,620 Views
Registered: ‎02-01-2013

Re: AXI DMA S2MM issue

Jump to solution

 

Perhaps a crudely distilled interpretation of your code might help:

------------------------------------------------------------------------------

...

While (1) {

(Re)set starting pointer to the same value EVERY TIME this outer loop runs

loop 8 times {

Read an address & print a value

Incr pointer

}

}

...

------------------------------------------------------------------------------

-Joe G.

 

0 Kudos
Explorer
Explorer
2,605 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

Hello @jg_bds ,

It's not clear to me if you want to make me think more about my implementation.

I thought that the code was easy to understand. Perhaps it's not so. Sorry.

I deduced that behaviour from the DS explanation:

A DMA operation for the S2MM channel is set up and started by the following sequence:

  1. Start the S2MM channel running by setting the run/stop bit to 1 (S2MM_DMACR.RS =
    1). The halted bit (DMASR.Halted) should deassert indicating the S2MM channel is
    running.
  2. If desired, enable interrupts by writing a 1 to S2MM_DMACR.IOC_IrqEn and
    S2MM_DMACR.Err_IrqEn. The delay interrupt, delay count, and threshold count are not
    used when the AXI DMA is configured for Direct Register Mode.
  3. Write a valid destination address to the S2MM_DA register. If AXI DMA is configured for
    an address space greater than 32, program the S2MM_DA MSB register.
  4. If the AXI DMA is not configured for Data Re-Alignment then a valid address must be
    aligned or undefined results occur. What is considered aligned or unaligned is based on
    the stream data width.
    For example, if Memory Map Data Width= 32, data is aligned if it is located at word
    offsets (32-bit offset), that is, 0x0, 0x4, 0x8, 0xC, and so forth. If DRE is enabled and
    Streaming Data Width < 128 then the Destination Addresses can be of any byte offset.
  5. Write the length in bytes of the receive buffer in the S2MM_LENGTH register. A value of
    zero has no effect. A non-zero value causes a write on the S2MM AXI4 interface of the
    number of bytes received on the S2MM AXI4-Stream interface. A value greater than or
    equal to the largest received packet must be written to S2MM_LENGTH. A receive buffer
    length value that is less than the number of bytes received produces undefined results.
    When AXI DMA is configured in Micro mode, this value should exactly match the bytes
    received on the S2MM AXI4-Stream interface. The S2MM_LENGTH register must be
    written last. All other S2MM registers can be written in any order.

 

Also, @demarco in this reply says:

"In an S2MM transfer, AXI DMA moves transfers from from the streaming side to the memory mapped side until is sees TLAST assert. You can set S2MM_LENGTH to indicate a transfer of many more bytes, but once AXI DMA sees TLAST, that means the simple mode transfer is done and you need to reprogram AXI DMA to do another transfer.

AXI DMA has a small buffer in it where it will take in a few additional streaming data transfers. This is why you see it accept a few more data beats before it drops TREADY. If you re-armed the S2MM engine, you would see the next dword from the stream side get written on the memory mapped side."

 

If I do not  restart the pointer to the same address value, I start to read garbage from data[08].

 

0 Kudos
Scholar jg_bds
Scholar
2,581 Views
Registered: ‎02-01-2013

Re: AXI DMA S2MM issue

Jump to solution

 

My bad. Based on your code, it looked like you intended to 1) run a single DMA then 2) try to scan through the Rx buffer to report the Rx'd data--but you kept looping through the same addresses at the beginning of the buffer. It now seems to me you expect DMA activity to overwrite the small section of memory that you keep reading.

If you expect to do a subsequent DMA and overwrite the bottom of the buffer (which appears to be what you want to review, to detect DMA activity), I'm pretty sure you're going to have to re-start the DMA after each transfer by re-setting the DMACR.RS bit. I see you start it only once before your While loop, and not after each 'interrupt'. You should also resolve the Error that's being reported in the DMASR.

I'm not sure why the While(1) loop is continuing to work, unless you're not really clearing the DMASR... Why exactly are you getting an Error Interrupt?

-Joe G.

P.S. Do you really want to be DMA'ing to the 64-bit address {0x3FFFFFFF, 0x01000000}?

    setS2MMLAddr(MEM_DDR_ADDR);
    setS2MMHAddr(0x3fffffff);

Explorer
Explorer
2,550 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

Hello @jg_bds ,

> It now seems to me you expect DMA activity to overwrite the small section of memory that you keep reading.

Correct .

> If you expect to do a subsequent DMA and overwrite the bottom of the buffer (which appears to be what you want to review, to detect DMA activity), I'm pretty sure you're going to have to re-start the DMA after each transfer by re-setting the DMACR.RS bit. I see you start it only once before your While loop, and not after each 'interrupt'.

Unfortunately, it doesn't work. Even if I reset and/or restart the DMA, the content of the memory mapped area is not rewritten.

The only way to read successive counter values is to update/set the S2MM_DA to the address pointed from the pointer after the for loop. Something like this:

#define MEM_DDR_ADDR 0x01000000
s2mmLAddr = MEM_DDR_ADDR;
while(1)
{
    setS2MMLenght(0x000001ff);
    // CONFIGURE S2MM_DA
setS2MMLAddr(s2mmLAddr); uint32_t *datap = (uint32_t *) s2mmLAddr; while (!getIOCIRQU()) ; clearIOCIRQU(); for(uint8_t i = 0; i < 8; i++) { printf("@%.8lx => data[%02d] = 0x%.8lx\r\n", datap, i, *datap); datap++; } // UPDATE S2MM_DA s2mmLAddr = (datap > MEM_DDR_ADDR + 0xffff) ? MEM_DDR_ADDR : (uint32_t) datap; }

but updating the address is not the behaviour I expect. Also, if I reset the S2MM_DA to lower address MEM_DDR_ADDR, the content is not updated. Also, the limit MEM_DDR_ADDR + 0xffff is not secure...

> Do you really want to be DMA'ing to the 64-bit address {0x3FFFFFFF, 0x01000000}?

Of course I don't. In the code of the 1st message I left the settings of S2MM_DA_MSB after a few tests.

 

I am thinking about using the AXI datamover, but I am not sure to obtain the behaviour I expect..

simozz

0 Kudos
Scholar jg_bds
Scholar
2,540 Views
Registered: ‎02-01-2013

Re: AXI DMA S2MM issue

Jump to solution

 

"The AxiStreamCounter_0 IP  is a 32 bit counter that increments TDATA on each positive clock edge with no upper limit and it asserts TLAST every 256 bits." So you assert TLAST every 8 words/32 bytes?

Have you put an ILA on the data paths to confirm the pipe is operating as you expect?

Are you still getting error indications from the DMA engine? If so, you should forget the loop for now and try to complete reliable single DMA transfers--ones that don't result in an error.  Then put a loop around that code.

-Joe G.

 

0 Kudos
Explorer
Explorer
2,531 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

> So you assert TLAST every 8 words/32 bytes?

This is what it should do. The module code is the following:

module AxiStreamCounter
    #(
        parameter WIDTH = 32,
        parameter TLAST_CLK_COUNT = 8
    )
    (   
        aresetn,
        m_axis_aclk,
        M_AXIS_TDATA,
        M_AXIS_TREADY,
        M_AXIS_TVALID,
        M_AXIS_TKEEP,
        M_AXIS_TSTRB,
        M_AXIS_TLAST
    );
    
    // just compute the needed bits given a certain value
    function integer clogb2 (input integer bit_depth);                                   
	  begin                                                                              
	    for(clogb2=0; bit_depth>0; clogb2=clogb2+1)                                      
	      bit_depth = bit_depth >> 1;                                                    
	  end                                                                                
	endfunction    
    
    localparam RESET = 0, ACTIVE = 1;
    localparam TLAST_CLK_COUNT_BITS = clogb2(TLAST_CLK_COUNT);
    
    // I/O ports
    input wire aresetn;
    (* gated_clock = "auto" *) input wire m_axis_aclk;
    output wire [WIDTH-1:0] M_AXIS_TDATA;
    input wire M_AXIS_TREADY;
    output wire M_AXIS_TVALID;
    output wire [(WIDTH/8)-1:0] M_AXIS_TKEEP;
    output wire [(WIDTH/8)-1:0] M_AXIS_TSTRB;
    output wire M_AXIS_TLAST;
    
    // registers
    reg [WIDTH-1:0] int_counter;
    reg [WIDTH-1:0] tdata;
    reg tvalid;
    reg [(WIDTH/8)-1:0] tkeep;
    reg [(WIDTH/8)-1:0] tstrb;
    (* fsm_safe_state = "default_state" *) reg state;
    reg [TLAST_CLK_COUNT_BITS-1:0] tlast_clk_counter;

    // reg to wire assignments
    assign M_AXIS_TDATA = tdata;
    assign M_AXIS_TVALID = tvalid;
    assign M_AXIS_TKEEP = tkeep;
    assign M_AXIS_TSTRB = tstrb;
    
    assign M_AXIS_TLAST = ((tlast_clk_counter == (TLAST_CLK_COUNT)-1) && M_AXIS_TVALID && M_AXIS_TREADY) ? 1:0;

    always @(posedge m_axis_aclk) begin
        // sync reset active low
        if(!aresetn)
            state <= RESET;
        else
            state <= ACTIVE;
    end

    always @(posedge m_axis_aclk) begin
        
        case (state)
        
            RESET:
            begin
                tdata <= 0;
                tlast_clk_counter <= {{TLAST_CLK_COUNT_BITS}{1'b1}};
                tvalid <= 0;
                tkeep <= 0;
                tstrb <= 0;
                int_counter <= {{WIDTH}{1'b1}};
            end

            ACTIVE:
            begin
                tvalid <= 1;                           
                if(M_AXIS_TREADY) begin
                    
                    int_counter <= int_counter + 1; 
                    tdata <= int_counter;
                    tkeep <= {{(WIDTH/8)}{1'b1}};
                    tstrb <= {{(WIDTH/8)}{1'b1}};

                    if(tlast_clk_counter == (TLAST_CLK_COUNT-1))
                        tlast_clk_counter <= 0;
                    else
                        tlast_clk_counter <= tlast_clk_counter + 1 ;
                end
            end
    
            default : 
            begin
                tdata <= 0;
                tlast_clk_counter <= {{TLAST_CLK_COUNT_BITS}{1'b1}};
                tvalid <= 0;
                tkeep <= 0;
                tstrb <= 0;
                int_counter <= {{WIDTH}{1'b1}};
            end
        endcase
    end
endmodule
  1. If I run the debug core without running the C code, I can't see any activity with the debugging. No transmission..
  2. I executed the debug when the C code was running and I could see as TLAST was asserted each 8words/32bits each transfer.

Now, after reprogramming the device, the Vivado hardware manager doesn't recognize debug cores... Seems to be that there are non fixed bugs from Vivado.......

simozz

0 Kudos
Explorer
Explorer
2,522 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

This is a screenshot of the ILA activity without the C code running:

Screenshot_2019-02-22_19-02-49.png

I did not take the screenshot while I could see the TLAST asserted each 8 words transfer.

Somehow I could see it before, now I don't...

0 Kudos
Explorer
Explorer
2,511 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

@jg_bds, sorry I forgot to answer this:

> Are you still getting error indications from the DMA engine?

No, I don't.

simozz

0 Kudos
Scholar jg_bds
Scholar
2,495 Views
Registered: ‎02-01-2013

Re: AXI DMA S2MM issue

Jump to solution

 

The Verilog looks right; there should be a TLAST every 8 clocks in the incoming data stream.

So you don't get any errors, but it doesn't work? Can you update the description of the behavior, if it has changed?

In your current While loop, are you checking the Halted and Idle bits now, after you see the IOC bit get set?

If it's Halted, there must've been an error. If it's just Idle, the machine should be ready to do another DMA.

Are you updating the DA and/or Length registers each time through the loop?

-Joe G.

 

0 Kudos
Explorer
Explorer
2,469 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

Hello @jg_bds 

> Can you update the description of the behavior, if it has changed ?

Sure. Even if I don't think it has changed too much too much, it's interesting to see what happens depending on the order of the setup of the DMA.This order has been copied from the AXI DMA's datasheet and pasted in the 3rd message.

The code I am using now, after implementing error notification, is the following:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_io.h"

#define ZERO_32_BITS 0x00000000
#define MEM_DDR_ADDR (UINTPTR) 0x01000000

#define C_BASE_ADDR (UINTPTR) 0x40400000

// S2MM S2MM DMA Control register
#define S2MM_DMACR (UINTPTR) C_BASE_ADDR + 0x30
// S2MM_DMASR S2MM DMA Status register
#define S2MM_DMASR (UINTPTR) C_BASE_ADDR + 0x34
// S2MM Destination Address. Lower 32 bit address.
#define S2MM_DA (UINTPTR) C_BASE_ADDR + 0x48
// S2MM Destination Address. Upper 32 bit address.
#define S2MM_DA_MSB (UINTPTR) C_BASE_ADDR + 0x4c
// S2MM Buffer Length (Bytes)
#define S2MM_LENGTH (UINTPTR) C_BASE_ADDR + 0x58

#define S2MM_DMACR_RUN_STOP ZERO_32_BITS | (1 << 0)
#define S2MM_DMACR_RESET ZERO_32_BITS | (1 << 2)
#define S2MM_DMACR_KEYHOLE ZERO_32_BITS | (1 << 3)
#define S2MM_DMACR_IOC_IRQEN ZERO_32_BITS | (1 << 12)
#define S2MM_DMACR_ERR_IRQEN ZERO_32_BITS | (1 << 14)

#define S2MM_DMASR_HALTED ZERO_32_BITS | (1 << 0)
#define S2MM_DMASR_IDLE ZERO_32_BITS | (1 << 1)
#define S2MM_DMASR_SGINCL ZERO_32_BITS | (1 << 3)
#define S2MM_DMASR_DMAINTERR ZERO_32_BITS | (1 << 4)
#define S2MM_DMASR_DMASLVERR ZERO_32_BITS | (1 << 5)
#define S2MM_DMASR_DMADECERR ZERO_32_BITS | (1 << 6)
#define S2MM_DMASR_IOCIRQF ZERO_32_BITS | (1 << 12)
#define S2MM_DMASR_ERRIRQF ZERO_32_BITS | (1 << 14)

u32 decimalToS2MMLength(u32 value)
{
	u32 reg = ZERO_32_BITS;
	for (u32 i = 0; i < value-1; i++)
	{
reg |= 0x1; reg <<= 1; } return reg; } void resetDMA(void) { Xil_Out32(S2MM_DMACR, S2MM_DMACR_RESET); } void enableInterrupts(void) { u32 value = Xil_In32(S2MM_DMACR); value |= (S2MM_DMACR_IOC_IRQEN | S2MM_DMACR_ERR_IRQEN); Xil_Out32(S2MM_DMACR, value); } void disableInterrupts(void) { u32 value = Xil_In32(S2MM_DMACR); value &= ~(S2MM_DMACR_IOC_IRQEN | S2MM_DMACR_ERR_IRQEN); Xil_Out32(S2MM_DMACR, value); } u32 getInterruptFlags(void) { return Xil_In32(S2MM_DMASR) & (S2MM_DMACR_IOC_IRQEN | S2MM_DMACR_ERR_IRQEN); } void clearInterruptFlags(void) { u32 value = Xil_In32(S2MM_DMASR); value |= (S2MM_DMASR_IOCIRQF | S2MM_DMASR_ERRIRQF); Xil_Out32(S2MM_DMASR, value); } void startDMA(void) { u32 reg = Xil_In32(S2MM_DMACR); reg |= S2MM_DMACR_RUN_STOP; Xil_Out32(S2MM_DMACR, reg); } void stopDMA(void) { u32 reg = Xil_In32(S2MM_DMACR); reg &= ~S2MM_DMACR_RUN_STOP; Xil_Out32(S2MM_DMACR, reg); } u32 isIdle(void) { return Xil_In32(S2MM_DMASR) & (S2MM_DMASR_IDLE); } u32 hasSG(void) { return Xil_In32(S2MM_DMASR) & (S2MM_DMASR_SGINCL); } u32 getS2MMLAddr(void) { return Xil_In32(S2MM_DA); } u32 getS2MMHAddr(void) { return Xil_In32(S2MM_DA_MSB); } void setS2MMLAddr(u32 value) { Xil_Out32(S2MM_DA, value); } void setS2MMHAddr(u32 value) { return Xil_Out32(S2MM_DA_MSB, value); } u32 getS2MMLenght(void) { return Xil_In32(S2MM_LENGTH); } void setS2MMLength(u32 value) { Xil_Out32(S2MM_LENGTH, value); } int main() { u32 address = MEM_DDR_ADDR; init_platform(); printf("! GO !\r\n"); while(1) {
// Step 1 startDMA();
// Step 2 enableInterrupts();
// Step 3 setS2MMLAddr(MEM_DDR_ADDR);
// Step 5 setS2MMLength(0x1ff); printf("(1) S2MM_DMACR = 0x%.8lx\r\n", Xil_In32(S2MM_DMACR)); printf("(1) S2MM_DMASR = 0x%.8lx\r\n", Xil_In32(S2MM_DMASR)); printf("(1) S2MM_DA = 0x%.8lx\r\n", Xil_In32(S2MM_DA)); printf("(1) S2MM_LENGTH = 0x%.8lx\r\n", Xil_In32(S2MM_LENGTH)); while (!getInterruptFlags()) { ; } printf("(2) S2MM_DMACR = 0x%.8lx\r\n", Xil_In32(S2MM_DMACR)); printf("(2) S2MM_DMASR = 0x%.8lx\r\n", Xil_In32(S2MM_DMASR)); printf("(2) S2MM_DA = 0x%.8lx\r\n", Xil_In32(S2MM_DA)); printf("(2) S2MM_LENGTH = 0x%.8lx\r\n", Xil_In32(S2MM_LENGTH)); u32 iFlag = getInterruptFlags(); if(iFlag & (S2MM_DMASR_ERRIRQF)) { u32 errs = Xil_In32(S2MM_DMASR); if(errs & (S2MM_DMASR_DMAINTERR)) printf("S2MM_DMASR_DMAINTERR\r\n"); if(errs & (S2MM_DMASR_DMASLVERR)) printf("S2MM_DMASR_DMASLVERR\r\n"); if(errs & (S2MM_DMASR_DMADECERR)) printf("S2MM_DMASR_DMADECERR\r\n"); } clearInterruptFlags(); u32 *datap = (u32 *) address; for(uint8_t i = 0; i < 8; i++) { printf("@0x%.8lx => data[%02d] = 0x%.8lx\r\n", datap, i, *datap); datap++; }
// Don't reset the DMA, this cause it to block.. //resetDMA(); //address = (datap > MEM_DDR_ADDR + 0xffff) ? MEM_DDR_ADDR : (u32) datap; } printf("********* Out of [ %s ] *********\r\n", __FUNCTION__); cleanup_platform(); return 0; }

1) Whit this setup order, look at the register values:

! GO !
(1) S2MM_DMACR = 0x00015003
(1) S2MM_DMASR = 0x00005011
(1) S2MM_DA = 0x01000000
(1) S2MM_LENGTH = 0x0000001c
(2) S2MM_DMACR = 0x00015002
(2) S2MM_DMASR = 0x00005011
(2) S2MM_DA = 0x01000000
(2) S2MM_LENGTH = 0x0000001c
S2MM_DMASR_DMAINTERR
@0x01000000 => data[00] = 0x000049cb
@0x01000004 => data[01] = 0xffffffff
@0x01000008 => data[02] = 0x00000000
@0x0100000c => data[03] = 0x00000001
@0x01000010 => data[04] = 0x00000002
@0x01000014 => data[05] = 0x00000003
@0x01000018 => data[06] = 0x00000004
@0x0100001c => data[07] = 0x00000005
(1) S2MM_DMACR = 0x00015002
(1) S2MM_DMASR = 0x00000011
(1) S2MM_DA = 0x01000000
(1) S2MM_LENGTH = 0x000001ff

The S2MM_DMACR notifies an error, and S2MM_LENGTH is set to a strange value 0x1c while it has been previously set to 0x1ff...

 

2) If I uncomment the resetDMA() instruction after the for loop, and the S2MM_LENGTH is set to 0x10 (why ?) , DMASR notifies Halted before the first reading, the memory area is not updated and no error is being notified after the first loop:

! GO !
(1) S2MM_DMACR = 0x00015003
(1) S2MM_DMASR = 0x00005011
(1) S2MM_DA = 0x01000000
(1) S2MM_LENGTH = 0x0000001c
(2) S2MM_DMACR = 0x00015002
(2) S2MM_DMASR = 0x00005011
(2) S2MM_DA = 0x01000000
(2) S2MM_LENGTH = 0x0000001c
S2MM_DMASR_DMAINTERR
@0x01000000 => data[00] = 0x000021fb
@0x01000004 => data[01] = 0xffffffff
@0x01000008 => data[02] = 0x00000000
@0x0100000c => data[03] = 0x00000001
@0x01000010 => data[04] = 0x00000002
@0x01000014 => data[05] = 0x00000003
@0x01000018 => data[06] = 0x00000004
@0x0100001c => data[07] = 0x00000005
(1) S2MM_DMACR = 0x00015003
(1) S2MM_DMASR = 0x00001002
(1) S2MM_DA = 0x01000000
(1) S2MM_LENGTH = 0x00000010
(2) S2MM_DMACR = 0x00015003
(2) S2MM_DMASR = 0x00001002
(2) S2MM_DA = 0x01000000
(2) S2MM_LENGTH = 0x00000010
@0x01000000 => data[00] = 0x000021fb
@0x01000004 => data[01] = 0xffffffff
@0x01000008 => data[02] = 0x00000000
@0x0100000c => data[03] = 0x00000001
@0x01000010 => data[04] = 0x00000002
@0x01000014 => data[05] = 0x00000003
@0x01000018 => data[06] = 0x00000004
@0x0100001c => data[07] = 0x00000005
...

Quiet strange.

 

3) If I change the setup order as follows:

        // Step 3
        setS2MMLAddr(MEM_DDR_ADDR);
        // Step 5
        setS2MMLength(0x1ff);
        // Step 1
        startDMA();
        // Step 2
        enableInterrupts();

The S2MM_LENGTH register is set correctly but the program remain waiting forever into the while(!getInterruptFlags()) loop:

! GO !
(1) S2MM_DMACR = 0x00015003
(1) S2MM_DMASR = 0x00000000
(1) S2MM_DA = 0x01000000
(1) S2MM_LENGTH = 0x000001ff

In this case, enabling the interrupt before or after starting the DMA has no effect.

 

Perhaps there is a timeout that must be kept in mind before using the DMA after turning on the hardware ?

EDIT: of course I was wrong when I wrote that I got no errors. I was not checking the flags correctly. My bad.

simozz

0 Kudos
Explorer
Explorer
2,407 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

Hello,

Small update: the errors related to the strange values of S2MM_LENGTH depending on the setup in my last post was caused by the fact that I was not resetting the DMA through the S2MM_DMACR register before starting the DMA.. This is not stated into the Programming Sequence section of datasheet, and I assumed that a initial reset was applied on programming the bitstream.

However, the error S2MM DMAIntErr is flagged after setting the value for S2MM_LENGTH.

Do you have any hint about this ? I think I am respecting what the datasheet says about this setting.

simozz

0 Kudos
Scholar jg_bds
Scholar
2,396 Views
Registered: ‎02-01-2013

Re: AXI DMA S2MM issue

Jump to solution

 

Why are you enabling interrupts? Do you have an ISR? What's that routine doing?

For an experiment, try making the DMA block much larger (256? bytes), so you code has a chance to respond before the next stream arrives. The way your code is set-up right now, with printf's in the critical section, you're not reacting very quickly.

The Length Register is updated by the IP with the actual number of bytes written to the buffer. This needs to be refreshed before the next stream arrives.

-Joe G.

 

Explorer
Explorer
2,390 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

Hello @jg_bds ,

> Why are you enabling interrupts? Do you have an ISR? What's that routine doing?

It's true that I don't have an ISR and I am doing a polling on the IOC flag. But I am enabling interrupts just to know when I am ready to read data from DDR. This is the way I am understanding the IOC flag, Am I wrong about it ?

Perhaps I should only use the s2mm_introut interrupt for this purpose (if interrupts enabled) ?

Of course disabling the interrupts I don't get error flags..

> or an experiment, try making the DMA block much larger (256? bytes)

Right now nothing changes, but I am not saying that this is a bad suggestion.

If it's not too much , could I ask you to post the correct pseudo-code, so I can implement it in a reliable way ? Because I think I am quiet confused. :S

Thank you in advance.

simozz

0 Kudos
Explorer
Explorer
2,305 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

It seems I finally solved this issue.

The main problem seems to be the missing call to Xil_DCacheFlushRange function in my code. That's why the address content was not being updated.

Then I applied two small changes to the design:

1) I included an init clock counter after reset before asserting TVALID. This is not mandatory but I think it's not bad as well.

2) I used the memory address that Xilinx's examples use:

 

#define MEM_BASE_ADDR 0x01000000

#define TX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x004FFFFF)

so the pseudo-code for S2MM only (with no interrupts) is:

1  - reset DMA

2  - start DMA

3 - set Low address to RX_BUFFER_BASE

4 - init while(1) loop

4.1 - set S2MM Length

4.2 - while DMA is not Idle, wait

4.3 - read data according to the burst size (or the content updated S2MM Length)

4.4 - call Xil_DCacheFlushRange

4.5 - returns to 4.1

Regards,

simozz

 

View solution in original post

Contributor
Contributor
1,986 Views
Registered: ‎07-20-2018

Re: AXI DMA S2MM issue

Jump to solution

what if out board is running linux so do we have to use malloc or mmap?

0 Kudos
Explorer
Explorer
1,874 Views
Registered: ‎05-14-2017

Re: AXI DMA S2MM issue

Jump to solution

@motagiyash,

You have to use mmap.

Regards,

s,

0 Kudos