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