ZYNQ linux dma单向数据读写

一,DMA相关定义和理解

DMA 是 Direct Memory Access 的缩冐,也就是直接内存诺冐,所谓的直接,也就是内存到
内存,丌通过 CPU。DMA 的可以支持内存到外讴、外讴到内存、内存到内存的数据交互,必要
时节省径多 CPU 资源。弼然,虽然 DMA 几乎丌占用 CPU,但迓是会占用系统总线。

1,DMA channels
大部分 DMAC 支持多路 DMA,也就是 DMA channels。DMA channel 相弼亍是 DMAC 提供的数据传输通道也就是数据传输的方法,解决了如何传输的问题。实际上 DMA channel 是一个抽象概念,不是物理通道。前面我们说过,DMA 传输迓是会占用总线资源的,总线资源十分有限,因此同时迕行多路 DMA 传输不太现实。抽象出 DMA channel 概念的目的实际是为了方便管理 DMA的 client,让每个 client 都独占一格通道,但实际上传输时 DMAC 会迕行先后仲裁,串行传输。
2, DMA request line
request line 挃代 DMA 讴备呾 DMAC 乀间的物理连线。返条线用亍 DMA 讴备通知 DMAC 是
否可以开书传输,即解决了何时传输的问题。通常每个 DMA 数据收収节点(endpoint)都有 DMA
request line 连接到 DMAC。
3,transfer size、transfer wide、burst size
transfer wide 可以理解为单次传输数据的大小,串口来比较的话,串口一次叧能传一个字节,而DMA 则可以选择一次能传输的数据大小。在返基础上的 transfer size 则是传输的次数,而不是单纯的总大小,也就是说 DMA 传输总长度实际上是 transfer size 乘上 transfer wide。burst size 是乘以 DMAC 内部缓存大小。弼 DMA 传输的源戒目的是内存 memory 时,DMAC 会先读取数据到缓存,再传入或传出。
4,scatter-gather
DMA 操作必项是连续的物理内存,在实际应用中,难免会遇到处理物理内存,不连续的数据的情况,scatter-gather 指的就是把不连续的数据拷贝到连续的 buffer 中的操作。返个操作过程可以用软件实现,也有直接的硬件支持。具体的实现暂不用关心,返里主要是强调 DMA 操作必项是连续的物理内存返件事。

二,vivado搭建

单个DMA每次只能发送一定量的数据,但对于数据源来说数据时源源不断产生的,所以在单个DMA单次发送完成至下一次传输期间,这段时间的数据流失了,所以采用两个DMA实现循环发送数据,防止数据丢失。自定义一个IP核用于产生源源不断的测试数据模拟数据源,再自定义一个IP用于切换DMA发送数据。

三,DMA linux驱动

1,DMA框架示意图:

DMA controller 框架抽象出 channel 对应 DMAC 的物理通道,又定定义了虚拟的 channel,软件上可以实现多个虚拟 channel 对应一个物理通道。

2,DMA controller 框架中主要涉及到的数据结构:

channels:链表头。
cap_mask:表示 controller 的传输能力,需要后面 device_prep_dma_xxx 形式的回调函数对应。常见形式函数如下:
DMA_MEMCPY:可迕行 memory copy。
DMA_MEMSET:可迕行 memory set。
DMA_SG:可迕行 scatter list 传输。
DMA_CYCLIC:可迕行 cyclic 类的传输。
DMA_INTERLEAVE:可进行交叉传输。
src_addr_widths:表示 controller 支持哪些宽度的 src类型。
dst_addr_widths:表示 controller 支持哪些宽度的 dst 类型。
directions:表示 controller 支持的传输方向取值参考构枚举 dma_transfer_direction。
max_burst:最大的 burst 传输的 size。
descriptor_reuse:表示 controller 的传输描述能不能复用。
device_alloc_chan_resources:client 申请 channel 时会调用。
device_free_chan_resources:client 释放 channel 时会调用。
device_prep_dma_xxx:client 通过 dmaengine_prep_xxx 获取传输描述符时会调用。
device_config:client 调用 dmaengine_slave_configchannel 时会调用。
device_pause:client 调用 dmaengine_pause 时会调用。
device_resume:client 调用 dmaengine_resume 时会调用。
device_terminate_all:client 调用 dmaengine_terminate_xxx 时会调用。
device_issue_pending:client 调用 dma_async_issue_pending 启动传输时会调用。
DMA controller 驱动需要实现返些凼数的具体处理内容,相当于字符讴备框架中的 ops操作凼数。

struct dma_chan定义:

1. struct dma_chan {
2. struct dma_device *device;
3. dma_cookie_t cookie;
4. dma_cookie_t completed_cookie;
5.
6. /* sysfs */
7. int chan_id;
8. struct dma_chan_dev *dev;
9.
10. struct list_head device_node;
11. struct dma_chan_percpu __percpu *local;
12. int client_count;
13. int table_count;
14.
15. /* DMA router */
16. struct dma_router *router;
17. void *route_data;
18.
19. void *private;
20. };

 

四,添加驱动

   dma驱动代码dma_driver.c

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/device.h>

#include <asm/io.h>

#include <linux/init.h>

#include <linux/platform_device.h>

#include <linux/miscdevice.h>

#include <linux/ioport.h>

#include <linux/of.h>

#include <linux/uaccess.h>

#include <linux/interrupt.h>

#include <asm/irq.h>

#include <linux/irq.h>

#include <asm/uaccess.h>

#include <linux/dma-mapping.h>

#include <linux/delay.h>



/**

 *DMA驱动程序

 * **/

//DMA 基地址

#define DMA_S2MM_ADDR		0X40400000

#define DMA_MM2S_ADDR       0X40410000


//DMA MM2S控制寄存器

volatile unsigned int  * mm2s_cr;

#define MM2S_DMACR		0X00000000


//DMA MM2S状态控制寄存器

volatile unsigned int * mm2s_sr;

#define MM2S_DMASR		0X00000004



//DMA MM2S源地址低32位

volatile unsigned int * mm2s_sa;

#define MM2S_SA			0X00000018



//DMA MM2S传输长度(字节)

volatile unsigned int * mm2s_len;

#define MM2S_LENGTH		0X00000028



//DMA S2MM控制寄存器

volatile unsigned int  * s2mm_cr;

#define S2MM_DMACR		0X00000030



//DMA S2MM状态控制寄存器

volatile unsigned int  * s2mm_sr;

#define S2MM_DMASR		0X00000034



//DMA S2MM目标地址低32位

volatile unsigned int  * s2mm_da;

#define S2MM_DA			0X00000048



//DMA S2MM传输长度(字节)

volatile unsigned int  * s2mm_len;

#define S2MM_LENGTH		0X00000058



#define DMA_LENGTH		524288



dma_addr_t axidma_handle;

void* axidma_addr = NULL;

static irqreturn_t dma_mm2s_irq(int irq,void *dev_id)

{

    printk("\nPs write data to fifo is over! irq=%d\n",irq);

    iowrite32(0x00001000,mm2s_sr);

    return IRQ_HANDLED;

}

static irqreturn_t dma_s2mm_irq(int irq,void *dev_id)

{

    iowrite32(0x00001000,s2mm_sr);

    printk("\nps read data from fifo is over! irq=%d\n",irq);//读出了FIFO里的数据触发中断

    return IRQ_HANDLED;

}

int major;



static struct class *dma_class   = NULL;

static int dma_init(void);

static void dma_exit(void);

static int dma_open(struct inode *inode,struct file *file);

static int dma_release(struct inode *inode,struct file *file);



static int dma_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos);

static int dma_read(struct file *file,char __user *buf,size_t size,loff_t *ppos);

/*

 *file_operations 结构数据,沟通内核与操作系统桥梁


 * */

static struct file_operations dma_lops=

{

.owner = THIS_MODULE,

.read  = dma_read,

.open  = dma_open,

.write = dma_write,

.release = dma_release,





};

/*

 * 初始化,用于module init

 * */

static int dma_init(void)

{

	int err;

    major=register_chrdev(0,"dma_dev",&dma_lops);

    dma_class= class_create(THIS_MODULE,"dma_dev");

    device_create(dma_class,NULL,MKDEV(major,0),NULL,"dma_dev");

    printk("major dev number= %d\n",major);

    err = request_irq(61,dma_mm2s_irq,IRQF_TRIGGER_RISING,"dma_dev",NULL);

    printk("err=%d\n",err);

    err = request_irq(62,dma_s2mm_irq,IRQF_TRIGGER_RISING,"dma_dev",NULL);

    printk("err=%d\n",err);

    mm2s_cr  =  ioremap(DMA_MM2S_ADDR+MM2S_DMACR, 4);

    mm2s_sr  =  ioremap(DMA_MM2S_ADDR+MM2S_DMASR, 4);

    mm2s_sa  =  ioremap(DMA_MM2S_ADDR+MM2S_SA,    4);

    mm2s_len =  ioremap(DMA_MM2S_ADDR+MM2S_LENGTH,4);



    s2mm_cr  =  ioremap(DMA_S2MM_ADDR+S2MM_DMACR, 4);

    s2mm_sr  =  ioremap(DMA_S2MM_ADDR+S2MM_DMASR, 4);

    s2mm_da  =  ioremap(DMA_S2MM_ADDR+S2MM_DA,    4);

    s2mm_len =  ioremap(DMA_S2MM_ADDR+S2MM_LENGTH,4);


    printk("dma_init OK\n");

   return 0;

}

/*

 *退出 用于 module exit

 * */

static void dma_exit(void)

{

    unregister_chrdev(major,"dma_dev");

    

    device_destroy(dma_class,MKDEV(major,0));

    class_destroy(dma_class);

	free_irq(dma_mm2s_irq,NULL);

    free_irq(dma_s2mm_irq,NULL);



    iounmap(mm2s_cr);

    iounmap(mm2s_sr);

    iounmap(mm2s_sa);

    iounmap(mm2s_len);



    iounmap(s2mm_cr);

    iounmap(s2mm_sr);

    iounmap(s2mm_da);

    iounmap(s2mm_len);

    return ;

}

/*

 *open 接口函数


 * */

static int dma_open(struct inode *inode,struct file *file)

{	

	int err;

    printk("DMA open\n");

	axidma_addr = dma_alloc_coherent(NULL,DMA_LENGTH,&axidma_handle,GFP_KERNEL);

	if(axidma_addr == NULL)

	{

		printk("dma_alloc_coherent error\n");

		return -1;

	}



    return 0;

}

/*

 *release 接口函数

 *

 * */

static int dma_release(struct inode *inode,struct file *file)

{

    	printk("DMA release\n");

	if(axidma_addr != NULL)
        {

		dma_free_coherent(NULL,DMA_LENGTH,axidma_addr,axidma_handle);

	}

    return 0;

}



/*

 * write 接口函数

 *

 * */

static int dma_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos)

{

    unsigned int mm2s_status = 0;

    printk("dma write start !\n");

    if(count>DMA_LENGTH)

    {

		printk("the number of data is too large!\n");

		return 0;

    }

    if(copy_from_user(axidma_addr,buf,count))

		return -EFAULT;

    iowrite32(0x00001001,mm2s_cr);

    iowrite32(axidma_handle,mm2s_sa);

    iowrite32(count,mm2s_len);

    mm2s_status = ioread32(mm2s_sr);

    while((mm2s_status&(1<<1))==0)

    {

    	msleep(100);

        mm2s_status = ioread32(mm2s_sr);

    }

     printk("write mm2s_status =0x%x\n",mm2s_status);

     printk("dma write is over!\n");



    return count;

}

/*

 * read 接口函数

 *DMA读取数据是按照32bit读取的

 * */

static int dma_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)

{

	static int flag = 0;

    unsigned int s2mm_status=0;

    printk("dma read start!\n");

    if(size>DMA_LENGTH)

    {

		printk("the number of data is not enough!\n");

		return 0;

    }



	if(flag==0)

	{

		iowrite32(0x00001001,s2mm_cr);

    	         iowrite32(axidma_handle,s2mm_da);

		iowrite32(size,s2mm_len);

	}

	

    s2mm_status=ioread32(s2mm_sr);

    while(((s2mm_status&(1<<1))==0))

    {

		msleep(100);

		if(signal_pending(current))
                {

			flag = 1;

			return 0;

		}

         printk("~~~~~~~~~~~~~~\n");

        s2mm_status=ioread32(s2mm_sr);

    }

    printk("s2mm_sr=0x%x\n",s2mm_status);

	flag = 0;

     printk("+++++++++++++++++++\n");

    if(copy_to_user(buf,axidma_addr,size))

		return -EFAULT;



    printk("\ndma read is over!\n");

    return size;

}



module_init(dma_init);

module_exit(dma_exit);



MODULE_AUTHOR("TEST@dma");

MODULE_DESCRIPTION("dma driver");

MODULE_ALIAS("dma linux driver");

MODULE_LICENSE("GPL");

Makefile文件:

KDIR=/home/alinx/Downloads/dma_7010/dma_linux/build/tmp/work/plnx_arm-xilinx-linux-gnueabi/linux-xlnx/4.9-xilinx-v2017.4+git999-r0/linux-xlnx-4.9-xilinx-v2017.4+git999


PWD := $(shell pwd)

CC 	= $(CROSS_COMPILE)gcc

ARCH =arm

MAKE =make

obj-m:=dma_driver.o



modules:

	$(MAKE) -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPILE) M=$(PWD) modules

clean:

	make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPILE) M=$(PWD) clean

五,设备树文件pl.dtsi:

/*
 * CAUTION: This file is automatically generated by Xilinx.
 * Version:  
 * Today is: Thu Aug 26 20:49:09 2021
 */


/ {
	amba_pl: amba_pl {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		ranges ;
		axi_dma_0: dma@40400000 {
			#dma-cells = <1>;
			clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_s2mm_aclk";
			clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>;
			compatible = "xlnx,axi-dma-1.00.a";
			interrupt-parent = <&intc>;
			interrupts = <0 62 4>;
			reg = <0x40400000 0x10000>;
			xlnx,addrwidth = <0x20>;
			dma-channel@40400030 {
				compatible = "xlnx,axi-dma-s2mm-channel";
				dma-channels = <0x1>;
				interrupts = <0 62 4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x0>;
			};
		};
		axi_dma_1: dma@40410000 {
			#dma-cells = <1>;
			clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk";
			clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>;
			compatible = "xlnx,axi-dma-1.00.a";
			interrupt-parent = <&intc>;
			interrupts = <0 61 4>;
			reg = <0x40410000 0x10000>;
			xlnx,addrwidth = <0x20>;
			dma-channel@40410000 {
				compatible = "xlnx,axi-dma-mm2s-channel";
				dma-channels = <0x1>;
				interrupts = <0 61 4>;
				xlnx,datawidth = <0x20>;
				xlnx,device-id = <0x1>;
			};
		};
	};
};

六,测试文件 my_dma.c

#include <fcntl.h>

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <string.h>

#include <signal.h>

#include <stdlib.h>

   int fd;

void delay(void)

{

    int i,j;

    for(i=0;i<20000;i++)

        for(j=0;j<10000;j++);

}

void bibi(int n)

{

	printf("---------------n=%d\n",n);

	close(fd);

	exit(-1);

}

int main(int argc , char ** argv)

{

	int ret;

   

        int i=0;

	fd_set     rfds;

	struct timeval  tv;

	unsigned char buf[1024]={0x1,0x2,0x3,0x4,5,6,7,8,9,10,11,12,13,14};
        for(int k=0;k<1024;k++)
         {
              buf[k]=k;
         } 

	 signal(SIGINT, bibi); 

    fd = open("/dev/dma_dev",O_RDWR);

    if(fd<0) 

      {

		printf("can not open file\n");

		while(1);

      }

    else 
    {

		printf("open file sucuss\n");

    } 	
    

       		//ret = write(fd,buf,strlen(buf));
                ret = write(fd,buf,1024);

		printf("write ret -%d\n",ret);

		 for(i=0;i<ret;i++)
         {
		 printf("%04x   ",buf[i]);
         }
		 printf("\n");


		 memset(buf,0,sizeof(buf));
		 printf("regin to read:\n");
		ret = read(fd,buf,1024);

		printf(" xxxxxxxxxx read ret -%d\n",ret);

		printf("%04x   ",buf);

	        close(fd);

        return 0;
}

七,测试

(1)petalinux启动打印:

 (2)查看中断号

 (3)加载驱动

root@dma_0827:/mnt# insmod dma_driver.ko
dma_driver: loading out-of-tree module taints kernel.
major dev number= 244
genirq: Flags mismatch irq 45. 00000001 (dma_dev) vs. 00000084 (xilinx-dma-controller)
genirq: Flags mismatch irq 46. 00000001 (dma_dev) vs. 00000084 (xilinx-dma-controller)
dma_init OK

(4)运行程序:

猜你喜欢

转载自blog.csdn.net/wangjie36/article/details/119954949