Sorry, you need to enable JavaScript to visit this website.

AXI_DMA device driver for Linux

Unsolved
28 posts / 0 new
Silviu Popescu's picture
Silviu Popescu
Junior(0)
AXI_DMA device driver for Linux

Does any one know how to use the AXI_DMA device driver linux-xlnx/drivers/dma/xilinx_dma.c?
Does it work, has anyone tested it for AXI_DMA IP?
I need to use it as a module.

I have a bare metal AXI_DMA driver but porting it to Linux seems more complicated than I though.

I am new to Linux device drivers, this might be a trivial question for some of you.

Thanks,
Silviu

Vatu's picture
Vatu
Junior(0)
VDMA driver

Hi Silviu,
I use VDMA IP. I try to use exactly same driver, but without succeed. I dont know how to use it. I found information about xvdma.c driver (http://forums.xilinx.com/t5/Embedded-Linux/Xvdma-Driver/td-p/279964) who works as bridge between base VDMA kernel driver and user application, but I am not able to colmpile it. I spent 2 days without succeed.
My final solution is easy. I use mmap to access VDMA IP registers and program it. I have working solution after 2 hours.

Linux is little hell. :-)

Silviu Popescu's picture
Silviu Popescu
Junior(0)
VDMA driver

Hi Vatu,
In my case AXI DMA is at 0x40400000 but
mmap() needs a file descriptor, fd.
How do you create this fd if there is only a physical address that I need to access?
Can fd be NULL?
Does it mean that there must be a driver running already?
Do I need to configure the device-tree file before I use the mmap()?

Vatu's picture
Vatu
Junior(0)
Access to physical address

Hi Silviu,
you can use fd=open("/dev/mem", O_RDWR) to get file descriptor.
Function mmap maps physical addres to virtual adress space.

mmap(NULL, <length>, PROT_READ | PROT_WRITE, MAP_SHARED, fd, <physical adress>);

This solution access directly to VDMA IP registers, therefore you does not need any driver.

You does not need instantiate any driver in device-tree, but is good idea to limit memory used by kernel to reserve physical memory for store data by DMA engine.

Silviu Popescu's picture
Silviu Popescu
Junior(0)
Access to physical address

Thanks, I spent more time reading a device driver book and I got to the same conclusion. The quick way to drive and get data from the AXI-DMA device is with mmap function. After that is like a bare metal driver.

Another interesting way is to implement a small character driver that redefines the mmap to map it to a physical address. In theory you can use kmalloc() convert its return address to physical address with virt_to_phys().
But I get a fragmentation issue when using kmalloc().

Thanks for your help,
Silviu

Silviu Popescu's picture
Silviu Popescu
Junior(0)
VDMA driver

Hi Vatu,
Can you please share the code that uses mmap to control the driver from user space?

Thank you,
Silviu

Vatu's picture
Vatu
Junior(0)
VDMA driver
Silviu Popescu's picture
Silviu Popescu
Junior(0)
VDMA driver

Thanks, it helps a lot.

Silviu

Silviu Popescu's picture
Silviu Popescu
Junior(0)
un-cacheable memory

Hi Vatu,
How do you invalidate the cache for one of your mmap regions?

Thanks,
Silviu

Vatu's picture
Vatu
Junior(0)
un-cacheable memory

Hi Silviu,
I have not yet figured out.
One option is use ACP.

Functional solution is interesting for me.

Vatu

Silviu Popescu's picture
Silviu Popescu
Junior(0)
mmap DMA driver

Hi Vatu,
A user space driver is like this:

int dma_initUser(DmaDevId_t devid, XAxiDma_User_t *pUserConfig)
{
const XAxiDma_Config_t *pPhyConfig;
int fd;
void *vAddr;

pPhyConfig = LookupConfig(devid);
printf("DMA phys addr = 0x%x \n", (int)pPhyConfig->physDmaAddr);

//-------------------------------------
//Get base virtual address for the DMA registers
//-------------------------------------
fd = open("/dev/mem", O_RDWR | O_SYNC);
pUserConfig->fd = fd;

vAddr = mmap(NULL, pPhyConfig->dmaSize,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd,
pPhyConfig->physDmaAddr);

if(vAddr == MAP_FAILED) {
goto mmap1_err;
}

pUserConfig->virtDmaAddr = (u32)vAddr;
pUserConfig->dmaSize = pPhyConfig->dmaSize;

//-------------------------------------
//Get virtual address for the DMA RX buffer
//-------------------------------------
fd = open("/dev/mem", O_RDWR | O_SYNC);
vAddr = mmap(NULL, pPhyConfig->buffPoolSize,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd,
pPhyConfig->physBuffAddr);

if(vAddr == MAP_FAILED) {
goto mmap2_err;
}

pUserConfig->virtBuffAddr = (u32)vAddr;
pUserConfig->physBuffAddr = pPhyConfig->physBuffAddr;
pUserConfig->buffPoolSize = pPhyConfig->buffPoolSize;
pUserConfig->fpgaPktSize = pPhyConfig->fpgaPktSize;
pUserConfig->devid = pPhyConfig->DeviceId;

return 1;

mmap1_err:
perror("[dma_driver]ERROR: DMA mmap() failed \n");
close(fd);
assert(0);

mmap2_err:
perror("[dma_driver]ERROR: Buffer mmap failed \n");
munmap((void*)pUserConfig->virtDmaAddr, pUserConfig->dmaSize);
close(fd);
assert(0);
}

michaelrissi's picture
michaelrissi
Junior(0)
Thanks a lot for these tipps!

Thanks a lot for these tipps! I just tried the mmaped access to the dma. Unfortunately, when I try to read out the status register, it freezes the system. My code is:

#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#define u32 unsigned int
int main(int argc, char**argv) {
int fd = open("/dev/mem", O_RDWR);
u32 physDMAAddr = 0x40400000;
int DMASize = 4096;
char* virtAddr;
virtAddr = (char*)mmap(NULL, DMASize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, physDMAAddr);
if(virtAddr == MAP_FAILED) {

printf ("DMA map failed %d\n",(u32)virtAddr);
return -1;
}
u32 virtDMAAddr = (u32)virtAddr;
printf("DMA done. Address: %x\n", virtDMAAddr);

/// now set up the registers of the DMA ///

u32 * MM2S_DMACR = virtDMAAddr + 0x00; // control register
u32 * MM2S_DMASR = virtDMAAddr + 0x04; // status register

printf("MM2S_DMASR: %x\n", *MM2S_DMASR);

}

When I leave away the last print statement, it runs nicely. Does anyone know what the problem could be?

Silviu Popescu's picture
Silviu Popescu
Junior(0)
Hi Michael,

Hi Michael,
Does it freez or you get a segmentation fault?
You can use Xilinx example code for bare metal ADMA driver where you should change the base address to be your mmap virtual address.

--Silviu

zxj's picture
zxj
Junior(0)
How to use AXI_CDMA driver of linux

The linux system also freezes when I test AXI_CDMA driver with zedboard.

Do you solve it already?

michaelrissi's picture
michaelrissi
Junior(0)
It freezes completely Linux.

It freezes completely Linux.
Using the example described linked here:
http://forums.xilinx.com/t5/Embedded-Processors-and/Problems-setting-up-...
works with the code given there in bare metal mode. The same firmware though still freezes Linux. I will check if the addresses etc. are correct.
Btw, I guess to use the mmap approach in Linux, I do not need to load the xilinx_dma driver?

Silviu Popescu's picture
Silviu Popescu
Junior(0)
You have to make sure that

You have to make sure that the physical address you are mapping is not visible to Linux. In devicetree.dts change the size of the RAM. For instance if you have 500MB you get:

chosen {
bootargs = "console=ttyPS0,115200 mem=500M root=/dev/ram rw initrd=0x2000000,32M earlyprintk maxcpus=2 debug ip=10.10.55.88";
linux,stdout-path = "/amba@0/uart@E0000000";
};

If you want to make 100MB not visible to Linux change it to:

chosen {
bootargs = "console=ttyPS0,115200 mem=400M root=/dev/ram rw initrd=0x2000000,32M earlyprintk maxcpus=2 debug ip=10.10.55.88";
linux,stdout-path = "/amba@0/uart@E0000000";
};

--Silviu

Silviu Popescu's picture
Silviu Popescu
Junior(0)
Map your buffers above

Map your buffers above address (RAM base address)+400M.

--Silviu

cuti's picture
cuti
Junior(0)
Dma Driver

Hello,

I built a bare-metal application which transfers 32bit in a frequency
5Mhz from extern Hardware to PS. In hardware i write the 32 bit data. In
baremetal application i read that data. For that, i use the IP "DMA
engine" and my own peripheral. Now i want to create a linux application just like you do. I already have memory access to DMA but now i don't know how to read/write from DMA.

Thanks in advance,
Davide

cuti's picture
cuti
Junior(0)
Dma Application

Hello again,

I made the following steps to read from DMA:

- Write in S2MM_DMACR to reset
- Wait to conclude the last step
- Write in S2MM_DMASR (0xffffffff) to remove errorsl
- Write in S2MM_DMACR to start (RS)
- Wait for run
- Write the buffer in S2MM_DA
- Write in S2MM_LENGTH the lenght of the package
- Wait for transfer to complete

The problem is that it waits forever for transfer. I miss a few steps but i dont know what.

Thanks,
Davide

min.json's picture
min.json
Junior(0)
Dma Application

how can i check transfer to complete??

shreyask's picture
shreyask
Junior(0)
AXI DMA using mmap in userspace

In Linux, when I use mmap() call and write to the returned virtual address(base_virtual_address + S2MM_DMACR) I am getting this error:

"Unhandled fault: external abort on non-linefetch (0x818) at 0xb6f34030
Bus error"

->I have reserved the last 8Mb for DMA buffer(0x1F800000 to 0x1FFFFFFF)
-> I am using the HP0 port for data transfer from AXI DMA to DDR
-> I changed the bootargs for kernel to use only 500Mb (mem=500M)

Please help.

shreyask's picture
shreyask
Junior(0)
AXI DMA using mmap in userspace

I have resolved the issue by changing the mapped address range from 0x40400000 to 0x41200000. But now when I enable the interrupt and poll the status register for transfer to complete, the program waits forever. Never seems to come out. Please help.

shreyask's picture
shreyask
Junior(0)
AXI DMA using mmap in userspace

THe program which I have written in user-space, is working only once. After executing the program for the first time, I have to reboot my zed-board everytime to make the program work. I did a strace in the program and it stops after mapping the DMA buffer, just before configuring the DMA registers. Here is my code:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>

#define DMA_BASEADDR 0x41210000
#define S2MM_DMACR 0xC
#define S2MM_DMASR 0xD
#define S2MM_DA 0x12
#define S2MM_LENGTH 0x16
#define COUNTER_BASEADDR 0x43C00000
#define DEST_ADDR 0x1FF00000
#define PAGE_SIZE 0x1000

typedef struct {
char data[4];
}byte;

int counter_config(volatile unsigned int *counter_virt_addr) {
unsigned int reg=0;

*counter_virt_addr = 0;
reg = *counter_virt_addr;

reg = 0x3FF;
*counter_virt_addr = reg;

reg=0;
reg = *counter_virt_addr;
printf("\nCounter reg:0x%08x\n", reg);

return 0;
}

int dma_config(volatile unsigned int *dma_virt_addr, unsigned int dest_addr) {

unsigned int reg=0;

*(dma_virt_addr + S2MM_DMACR) = 0;

*(dma_virt_addr + S2MM_DMACR) |= 0x1;

while((*(dma_virt_addr + S2MM_DMASR)) & 0x1);

*(dma_virt_addr + S2MM_DA) = dest_addr;

reg=0;
reg = *(dma_virt_addr + S2MM_DMACR);
printf("\nDMA engine started\n DMA_CR:0x%08x\n", reg);

reg=0;
reg = *(dma_virt_addr + S2MM_DA);
printf("\nDest_addr:0x%08x\n", reg);

return 0;
}

void start_transfer(volatile unsigned int *dma_virt_addr, unsigned int bytes) {

*(dma_virt_addr + S2MM_LENGTH) = bytes;

while ((*(dma_virt_addr+S2MM_DMASR) & (0x1<<12)) == 0);
printf("\nTransfer complete\n");
}

int main(void) {
int fd=0, page_size=0, val=0, i=0, j=0;
unsigned int page_addr=0, page_off=0;
volatile unsigned int *counter_virt_addr=NULL, *dma_virt_addr=NULL;
unsigned int *buff_virt_addr=NULL, *data=NULL;
byte *int_val;

data = (unsigned int*)calloc(256, sizeof(unsigned int*));

if ( (fd = open("/dev/mem", O_RDWR)) < 1) // open the /dev/mem device file
{
perror("\nError opening device file\n");
return -1;
}

fprintf(stdout, "\nOpened device file successfully\n");

dma_virt_addr = (volatile unsigned int*)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, DMA_BASEADDR);
if (dma_virt_addr == NULL)
perror("\nUnable fetch virt. addr. for AXI DMA\n");

counter_virt_addr = (volatile unsigned int*)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, COUNTER_BASEADDR);
if (counter_virt_addr == NULL)
perror("\nUnable fetch virt. addr. for AXI Counter\n");

buff_virt_addr = (unsigned int*)mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, DEST_ADDR);
if (buff_virt_addr == NULL)
perror("\nUnable fetch virt. addr. for AXI DMA buffer\n");

dma_config(dma_virt_addr, (unsigned int)DEST_ADDR);
counter_config(counter_virt_addr);
start_transfer(dma_virt_addr, 256);
/*
memset(buff_virt_addr, 0, 256);

start_transfer(dma_virt_addr, 256);
memcpy(data, buff_virt_addr, 256);

int_val = (byte*)data;

for (i=0; i<64; i++) {
for(j=3; j>=0; j--)
printf("\nData:0x%02x\n", (int_val+i)->data[j]);
}
*/
munmap((void*)buff_virt_addr, PAGE_SIZE);
munmap((void*)counter_virt_addr, PAGE_SIZE);
munmap((void*)dma_virt_addr, PAGE_SIZE);

free(data);
close(fd);

return 0;
}

 

 
fd = open("/dev/mem", O_RDWR|O_SYNQ)  // open the /dev/mem device file
in this line i am getting segmentation fault when i am debug remotely on zedboard in disassembly view it shows cannot access the memory.
how to solve this problem
actually i am running led_switches on linux provided with zedboard CTT.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

JFoster's picture
JFoster
Moderator(48)
Hello Princy,

Hello Princy,

Please ask this question over at the Digilent forum as they are the main point of contact for education support.

https://forum.digilentinc.com/

--Josh

Thank you for your help 

Thank you for your help 
I have resolved the issue. But now i am trying to implement the DMA on linux and when i check the destination address after run the code it will give mmu session fault.
can anyone suggest what can be the error .
I have implemented the code same as the code of shreyask which he posted above.

Thank you for your help 

Thank you for your help 
I have resolved the issue. But now i am trying to implement the DMA on linux and when i check the destination address after run the code it will give mmu session fault.
can anyone suggest what can be the error .
I have implemented the code same as the code of shreyask which he posted above.

yathindra's picture
yathindra
Junior(0)
devmem

If you are facing issues with the code. I would suggest you use the inbuilt utility called "devmem".
Just type devmem on the target and u would get the CLI for the same. Petalinux also has two inbuilt utilities, I forget the name but if you do 
"petalinux-config -c rootfs" and go to apps, you should be able to find the names of the utilities