ZYNQ linux dma驱动及其单向读写

一,DMA相关定义和注意事项
DMA是Direct Memory Access 的缩写,也就是内存到内存,不通过 CPU。DMA 的可以支持内存到外设、外设到内存、内存到内存的数据交互,必要时节省径多 CPU 资源。虽然 DMA 几乎不占用 CPU,但还是会占用系统总线。
1,PS通过DMA向PL写数据
 (1)调用:Xil_DCacheFlushRange(INTPTR adr, u32 len);
 (2)DMA写PL:XAXIDMA_DMA_TO_DEVICE
2,PS通过DMA从PL读数据
 (1)DMA读PL:XAXIDMA_DEVICE_TO_DMA
 (2)调用:Xil_DCacheInvalidateRange(INTPTR adr, u32 len)

3,注意事项:

DMA IP 中,需要关注的就是M_AXIS_MM2S、S_AXIS_S2MM、mm2s_introut、s2mm_introut四个IOs 其它系统自动连线。M_AXIS_MM2S、S_AXIS_S2MM为数据流总线,mm2s_introut、s2mm_introut为中断信号,可连接到PS端,在PS端编程控制处理。其中MM2S为DDR3 到 PL, S2MM为PL到DDR3。transfer size,transfer wide,burst size,transfer wide 可以理解为单次传输数据的大小,串口来比较的话,串口一次只能传一个字节,而DMA则可以选择一次能传输的数据大小。在这基础上的transfer size则是传输的次数,而不是单纯总大小,也就是DMA传输总长度实际上是transfer size 乘上 transfer wide。burst size 是乘以DMA内部缓存大小。DMA传输目的是内存memory时,DMA会先读取数据到缓存,再传入或传出。
4,scatter-gather
DMA操作必须是连续的物理内存,在实际应用中难免会遇到处理物理内存,不连续的数据的情况,scatter-gather 指的就是把不连续的数据拷贝到连续的 buffer 中的操作。返个操作过程可以用软件实现,也有直接的硬件支持,这里主要是强调 DMA 操作必项是连续的物理内存这件事。

5,参数对齐

(1)地址addr对齐的示例代码举例

    const u32 CACHE_LINE = 64U;
    u32 adr = (u32)malloc(DMA_LENGTH + 2*CACHE_LINE);
    //对齐CACHE_LINE
    adr += CACHE_LINE;
    adr &= ~(CACHE_LINE - 1U);
    g_txDma_mgr.orig_ptr =(u8*) adr;

(2)数据长度len对齐的示例代码举例

//从DDR更新DCache
if(g_tcp_mgr.total_byte_num % CACHE_LINE>0)
{ 
   dcacheInvalidLen = (g_tcp_mgr.total_byte_num / CACHE_LINE + 1)*CACHE_LINE; 
}
else
{ 
   dcacheInvalidLen = (g_tcp_mgr.total_byte_num ;  
}
    Xil_DCacheInvalidateRange((INTPTR)g_rxDma_mgr.orig_ptr, dcacheInvalidLen);

4,DMA与Dcache一致性问题解决

CPU <——>  Dcache  <——>  DDR3  <——>  XDMA <——>  Stream FIFO(PL) 

在无操作系统ZYNQ应用中,Dcache技术的应用的优点是提高CPU访问DDR3内存的速率;但会带来一个潜在的问题:cache数据发生更新时,不能马上同步更新到DDR中。用DMA进行数据传输时,需要解决DCache(Data Cache)和DDR Memory之间的数据一致性问题。调用Xil_DCacheInvalidateRange(INTPTR adr, u32 len)时,地址adr和长度len必须和CacheLine的长度对齐(A9的CacheLine=32字节,A53的是64字节)。

如果首地址/尾地址没有和CacheLine对齐,数据会出错,原因是:

Xil_DCacheInvalidateRange函数内部会先把未对齐的首地址/尾地址对应的CacheLine先Flush到DDR里,导致DDR里的数据被覆盖出错。

例如:CPU拟通过Dcache间接读取DDR3内存起始地址为0x0010_0000,长度为0x100,共计256个数据;该起始地址处开始的256个数据已被PL端刚更新过,但是Dcache尚未通过PS程序进行同步更新,则CPU从当前Dcache拿到的数据就是未更新过的数据,从而造成数据交互错误。

解决办法:关闭cache或更新cache

(1):直接关闭Dcache功能;即CPU直接与DDR内存数据交互,不需要Cache的介入;该方法坏处在于降低CPU处理数据的性能,所以不建议这个方法。

(2):程序中调用函数进行更新cache

Xil_DcacheFlushRange(Address, Length) 该函数功能:将Cache关联地址的数据写入到DDR中,并把Cache里的数据清空;

Xil_DcacheInvalidRange(Address, Length)该函数功能:将当前Cache制定地址和长度的缓存的数据无效,则CPU就直接从DDR3中对应地址处获取数据。

二,vivado搭建

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

三,在SDK裸机进行验证

 1,dma_intr.h

 2,dma_intr.c

#include "dma_intr.h"
volatile int TxDone;
volatile int RxDone;
volatile int Error;
 int DMA_CheckData(int Length, u8 StartValue)
{
    u8 *RxPacket;
    int Index = 0;
    u8 Value;
    RxPacket = (u8 *) RX_BUFFER_BASE;
    Value = StartValue;
#ifndef __aarch64__
    Xil_DCacheInvalidateRange((u32)RxPacket, Length);
#endif
    for(Index = 0; Index < Length; Index++)
    {
        if (RxPacket[Index] != Value)
        {
            xil_printf("Data error %d: %x/%x\r\n",Index, RxPacket[Index], Value);
            return XST_FAILURE;
        }
        Value = (Value + 1) & 0xFF;
    }
    return XST_SUCCESS;
}
 void DMA_DisableIntrSystem(XScuGic * IntcInstancePtr,u16 TxIntrId, u16 RxIntrId)
{
#ifdef XPAR_INTC_0_DEVICE_ID
    XIntc_Disconnect(IntcInstancePtr, TxIntrId);
    XIntc_Disconnect(IntcInstancePtr, RxIntrId);
#else
    XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
    XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
#endif
}
static void DMA_TxIntrHandler(void *Callback)
{
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) { return; }
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK))
    {
        Error = 1;
        XAxiDma_Reset(AxiDmaInst);
        TimeOut = RESET_TIMEOUT_COUNTER;
        while (TimeOut)
        {
            if (XAxiDma_ResetIsDone(AxiDmaInst)) { break; }
            TimeOut -= 1;
        }
        return;
    }
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {TxDone = 1;}
}
static void DMA_RxIntrHandler(void *Callback)
{
    u32 IrqStatus;
    int TimeOut;
    XAxiDma *AxiDmaInst = (XAxiDma *)Callback;
    IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);
    if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {return;}
    if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK))
    {
        Error = 1;
        XAxiDma_Reset(AxiDmaInst);
        TimeOut = RESET_TIMEOUT_COUNTER;
        while (TimeOut)
        {
            if(XAxiDma_ResetIsDone(AxiDmaInst))
            {
                break;
            }
            TimeOut -= 1;
        }
        return;
    }
    if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {RxDone = 1;}
}
int DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
    int Status;
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
    XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
    Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,(Xil_InterruptHandler)DMA_TxIntrHandler,AxiDmaPtr);
    if (Status != XST_SUCCESS) {return Status;}
    Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,(Xil_InterruptHandler)DMA_RxIntrHandler,AxiDmaPtr);
    if (Status != XST_SUCCESS) {return Status;}
    XScuGic_Enable(IntcInstancePtr, TxIntrId);
    XScuGic_Enable(IntcInstancePtr, RxIntrId);
    return XST_SUCCESS;
}
int DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr)
{
    XAxiDma_IntrDisable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrDisable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    XAxiDma_IntrEnable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DMA_TO_DEVICE);
    XAxiDma_IntrEnable(DMAPtr, XAXIDMA_IRQ_ALL_MASK,XAXIDMA_DEVICE_TO_DMA);
    return XST_SUCCESS;
}
int DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId)
{
    int Status;
    XAxiDma_Config *Config=NULL;
    Config = XAxiDma_LookupConfig(DeviceId);
    if (!Config)
    {
        xil_printf("No config found for %d\r\n", DeviceId);
        return XST_FAILURE;
    }
    Status = XAxiDma_CfgInitialize(DMAPtr, Config);
    if (Status != XST_SUCCESS)
    {
        xil_printf("Initialization failed %d\r\n", Status);
        return XST_FAILURE;
    }
    if(XAxiDma_HasSg(DMAPtr))
    {
        xil_printf("Device configured as SG mode \r\n");
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

3,main.c

#include "dma_intr.h"
#include "sys_intr.h"
static XScuGic Intc;
static  XAxiDma AxiDma;
volatile u32 success;
char oled_str[16]="";
int Tries = NUMBER_OF_TRANSFERS;
int i,Index;
u8 *TxBufferPtr= (u8 *)TX_BUFFER_BASE;
u8 *RxBufferPtr=(u8 *)RX_BUFFER_BASE;
u8 Value;
int axi_dma_test()
{
    int Status;
    TxDone = 0;
    RxDone = 0;
    Error = 0;
    xil_printf("\r\n----DMA Test----\r\n");
    for(i = 0; i < Tries; i ++)
    {
        Value = TEST_START_VALUE + (i & 0xFF);
        for(Index = 0; Index < MAX_PKT_LEN; Index ++)
        {
                TxBufferPtr[Index] = Value;
                Value = (Value + 1) & 0xFF;
        }
        Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);
        Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) RxBufferPtr,MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);
        if (Status != XST_SUCCESS)
        {
            return XST_FAILURE;
        }
        Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) TxBufferPtr,MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);
        if (Status != XST_SUCCESS)
        {
            return XST_FAILURE;
        }
        success++;
        TxDone = 0;
        RxDone = 0;
        if (Error)
        {
            xil_printf("Failed test transmit%s done, ""receive%s done\r\n", TxDone? "":" not",RxDone? "":" not");
            goto Done;
        }
        Status = DMA_CheckData(MAX_PKT_LEN, (TEST_START_VALUE + (i & 0xFF)));
        if (Status != XST_SUCCESS)
        {
            xil_printf("Data check failed\r\n");
            goto Done;
        }
    }
    xil_printf("AXI DMA interrupt example test passed\r\n");
    xil_printf("success=%d\r\n",success);
    DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
Done:
    xil_printf("--- Exiting Test --- \r\n");
    return XST_SUCCESS;
}
int init_intr_sys(void)
{
    DMA_Intr_Init(&AxiDma,0);//initial interrupt system
    Init_Intr_System(&Intc); // initial DMA interrupt system
    Setup_Intr_Exception(&Intc);
    DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system
    DMA_Intr_Enable(&Intc,&AxiDma);
}
int main(void)
{
    init_intr_sys();
    axi_dma_test();
}

四,DMA linux驱动:详细内容参考下面这篇博客

ZYNQ DMA linux设备驱动详解_wangjie36的博客-CSDN博客

1,DMA框架示意图:

DMA controller 框架抽象出channel对应DMA的物理通道,也定义了虚拟的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定义:

struct dma_chan 
{
struct dma_device *device;
dma_cookie_t cookie;
dma_cookie_t completed_cookie;


int chan_id;
struct dma_chan_dev *dev;

struct list_head device_node;
struct dma_chan_percpu __percpu *local;
int client_count;
int table_count;

/* DMA router */
struct dma_router *router;
void *route_data;
void *private;
};

3,DMA驱动流程总结

(1)申请DMA中断。

(2)申请内存空间作为src和dts的空间。

(3)注册设备注册节点

(4)将申请到的src和dst内存空间地址的指针,映射到dma设备:ioremap。

五,添加驱动

   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)运行程序:


版权声明:本文为wangjie36原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。