打印

zedboard axiDMA linux驱动

[复制链接]
980|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
zhangmangui|  楼主 | 2019-6-11 18:44 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本篇是AXI DMA在linux下使用的例子。
包括PL端设计,基于vivado 2015.4,petalinux 2016.1,基于linux 4.4内核。
我在git hub 上托管了代码,https://github.com/shichaog/zynq-dma,如果想加入github上这个项目,给我邮箱发信息shichaog@126.com,方便为你配权限。

1.PL端设计:PL端设计包括四个AXI DMA IP,它们分别和zynq处理IP的HP口相连接。

这个设计是基于Avnet-Digilent-ZedBoard-v2016.1-final.bsp,由于其它的ip都是xilinx开发环境开发环境就有,所以这里就不详细每一步设计过程了。
这些IP包括AXI interconnect, system reset,axi dma,concat。
注意concat是用来将AXI DMA的中断传递给zynq之用的,这是必须有的,否则在hdf导入时,会出现如下错误:

2 接下来是创建module和app了


这两个命令执行后,会分配在components/apps和components/modules目录下生成dmaBench和ds_axidma两个文件夹,该文件下的两个文件内容使用如下的文件替换。

《dmaBench.c》#include <stdio.h>#include <fcntl.h>#include <string.h>#include <stdlib.h>#include <sys/time.h>unsigned long tStart, tEnd;unsigned long data;unsigned long getTime(){        struct timeval temp;        gettimeofday(&temp, NULL);        return temp.tv_sec * 1000 * 1000 + temp.tv_usec;}void report(char *msg, unsigned long data, unsigned long time, unsigned long dmaUsed){        printf("%s\t%ld\t%ld\t%f\t%d\n", msg, data, time, data * 1.0 / time, dmaUsed);        FILE *f = fopen("report.dat", "a");        fprintf(f, "%s\t%ld\t%ld\t%f\t%d\n", msg, data, time, data * 1.0 / time, dmaUsed);        fclose(f);}#define REPORT(f, timeStart, timeEnd, dataPtr, msg, dmaUsed) *timeStart = getTime(); *dataPtr = f; *timeEnd = getTime(); report(msg, *dataPtr, *timeEnd - *timeStart, dmaUsed);void checkData(char *bufferIn, char *bufferOut, unsigned int elems){        int i;        if(!memcmp(bufferIn, bufferOut, elems*sizeof(char))){                printf("DMA Ok!\n");        }        else{                for(i=0;i<elems;i++)                        printf("%d\t%d\t%d\t%d\n", i, bufferIn, bufferOut, (i==0 ? 0 : bufferOut - bufferOut[i-1]));        }}unsigned long memCpy_ARM(char *bufferIn, char *bufferOut, unsigned long elems, size_t size){        int i;        for(i=0; i<elems; i++)                bufferOut = bufferIn;        return elems * size;}unsigned long memCpy_DMA(char *bufferIn, char *bufferOut, unsigned long elems, size_t size, int dmaToUse){#define FIFO_LEN 4000#define DMA_NUM 4        int fd[DMA_NUM];        fd[0] = open("/dev/axi_dma_0", O_RDWR);        fd[1] = open("/dev/axi_dma_1", O_RDWR);        fd[2] = open("/dev/axi_dma_2", O_RDWR);        fd[3] = open("/dev/axi_dma_3", O_RDWR);        unsigned long byteMoved = 0;        unsigned long byteToMove = 0;        int i;        while(byteMoved!=size * elems){                byteToMove = size * elems - byteMoved > FIFO_LEN ? FIFO_LEN : size * elems - byteMoved;                for(i=0; i<dmaToUse; i++){                        write(fd, &bufferIn[byteMoved], byteToMove);                }                for(i=0; i<dmaToUse; i++)                        read(fd, &bufferOut[byteMoved], byteToMove);                byteMoved += byteToMove;        }        close(fd[0]);        close(fd[1]);        close(fd[2]);        close(fd[3]);        return elems * size * dmaToUse;}int main(int argc, char **argv){    char *bufferIn, *bufferOut_ARM, *bufferOut_DMA;    if(argc!=3){            printf("Usage: ./dmaBench DATA DMA_TO_USE\n");            exit(0);    }    unsigned long DATA = atoi(argv[1]);    unsigned int DMA_TO_USE = atoi(argv[2]);    bufferIn = (char *) malloc(sizeof(char) * DATA);    bufferOut_ARM = (char *) malloc(sizeof(char) * DATA);    bufferOut_DMA = (char *) malloc(sizeof(char) * DATA);    int i;    for(i=0; i<DATA; i++){            bufferIn = i;    }    memset(bufferOut_ARM, 0, sizeof(char) * DATA);    memset(bufferOut_DMA, 0, sizeof(char) * DATA);    REPORT(memCpy_ARM(bufferIn, bufferOut_ARM, DATA, sizeof(char)), &tStart, &tEnd, &data, "ARM", 0);    for(i=0; i<DMA_TO_USE; i++){            REPORT(memCpy_DMA(bufferIn, bufferOut_DMA, DATA/(i+1), sizeof(char), (i+1)), &tStart, &tEnd, &data, "DMA", (i+1));    }    checkData(bufferIn, bufferOut_ARM, DATA);    checkData(bufferIn, bufferOut_DMA, DATA);    return 0;}和<ds_axidma>


/* * Xilinx AXI DMA Driver * * Authors:  *    Fabrizio Spada - fabrizio.spada@mail.polimi.it *    Gianluca Durelli - durelli@elet.polimi.it *    Politecnico di Milano * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */#include <linux/module.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/list.h>#include <linux/device.h>#include <linux/cdev.h>#include <linux/dma-mapping.h>#include <linux/pm_runtime.h>#include <linux/slab.h>#include <linux/of.h>#include <linux/of_platform.h>#include <linux/of_address.h>#include <linux/mm.h>#include <asm/io.h>#define MM2S_DMACR        0x00#define MM2S_DMASR        0x04#define MM2S_SA        0x18#define MM2S_LENGTH        0x28#define S2MM_DMACR        0x30#define S2MM_DMASR        0x34#define S2MM_DA        0x48#define S2MM_LENGTH        0x58#define DRIVER_NAME "ds_axidma_pdrv"#define MODULE_NAME "ds_axidma"#define DMA_LENGTH        (32*1024)static struct class *cl;        // Global variable for the device class struct ds_axidma_device{        phys_addr_t bus_addr;        unsigned long bus_size;        char *virt_bus_addr;        dev_t dev_num;        const char *dev_name;        struct cdev c_dev;        char *ds_axidma_addr;        dma_addr_t ds_axidma_handle;        struct list_head dev_list;};LIST_HEAD( full_dev_list );static struct ds_axidma_device *get_elem_from_list_by_inode(struct inode *i){        struct list_head *pos;        struct ds_axidma_device *obj_dev = NULL;        list_for_each( pos, &full_dev_list ) {                struct ds_axidma_device *tmp;            tmp = list_entry( pos, struct ds_axidma_device, dev_list );            if (tmp->dev_num == i->i_rdev)            {                    obj_dev = tmp;                    break;            }          }          return obj_dev;        }// static void dmaHalt(void){//         unsigned long mm2s_halt = ioread32(virt_bus_addr + MM2S_DMASR) & 0x1;//         unsigned long s2mm_halt = ioread32(virt_bus_addr + S2MM_DMASR) & 0x1;//         int count = 0;//         printk(KERN_INFO "Halting...\n");//         iowrite32(0, virt_bus_addr + S2MM_DMACR);//         iowrite32(0, virt_bus_addr + MM2S_DMACR);//         while( !mm2s_halt || !s2mm_halt){//                 // mm2s_halt = ioread32(virt_bus_addr + MM2S_DMASR) & 0x1;//                 mm2s_halt = virt_bus_addr[MM2S_DMASR] & 0x1;//                 //s2mm_halt = ioread32(virt_bus_addr + S2MM_DMASR) & 0x1;//                 s2mm_halt = virt_bus_addr[S2MM_DMASR] & 0x1;//                 count++;//                 if (count>100 )//                 {//                         break;//                 }//         }//         printk(KERN_INFO "DMA Halted!\n");// }static int my_strcmp(const char *str1, const char *str2){  int i;  i = 0;  while (str1 || str2)    {      if (str1 != str2)        return (str1 - str2);      i++;    }  return (0);}static int dmaSynchMM2S(struct ds_axidma_device *obj_dev){        //        sleep(6);        //        return;        unsigned int mm2s_status = ioread32(obj_dev->virt_bus_addr + MM2S_DMASR);        while(!(mm2s_status & 1<<12) || !(mm2s_status & 1<<1) ){                mm2s_status = ioread32(obj_dev->virt_bus_addr + MM2S_DMASR);        }        return 0;}static int dmaSynchS2MM(struct ds_axidma_device *obj_dev){        unsigned int s2mm_status = ioread32(obj_dev->virt_bus_addr + S2MM_DMASR);        while(!(s2mm_status & 1<<12) || !(s2mm_status & 1<<1)){                s2mm_status = ioread32(obj_dev->virt_bus_addr + S2MM_DMASR);        }        return 0;}static int ds_axidma_open(struct inode *i, struct file *f){        /* printk(KERN_INFO "<%s> file: open()\n", MODULE_NAME); */        struct ds_axidma_device *obj_dev = get_elem_from_list_by_inode(i);        if (!request_mem_region(obj_dev->bus_addr, obj_dev->bus_size, MODULE_NAME))        {                return -1;        }                obj_dev->virt_bus_addr = (char *) ioremap_nocache(obj_dev->bus_addr, obj_dev->bus_size);        return 0;}static int ds_axidma_close(struct inode *i, struct file *f){        /* printk(KERN_INFO "<%s> file: close()\n", MODULE_NAME); */        struct ds_axidma_device *obj_dev = get_elem_from_list_by_inode(i);        iounmap(obj_dev->virt_bus_addr);        release_mem_region(obj_dev->bus_addr, obj_dev->bus_size);        return 0;}static ssize_t ds_axidma_read(struct file *f, char __user * buf, size_t                         len, loff_t * off){        /* printk(KERN_INFO "<%s> file: read()\n", MODULE_NAME); */        struct ds_axidma_device *obj_dev;        if (len >= DMA_LENGTH)        {                return 0;        }        obj_dev = get_elem_from_list_by_inode(f->f_inode);        iowrite32(1, obj_dev->virt_bus_addr + S2MM_DMACR);        iowrite32(obj_dev->ds_axidma_handle, obj_dev->virt_bus_addr + S2MM_DA);        iowrite32(len, obj_dev->virt_bus_addr + S2MM_LENGTH);        dmaSynchS2MM(obj_dev);        memcpy(buf, obj_dev->ds_axidma_addr, len);        return len;}static ssize_t ds_axidma_write(struct file *f, const char __user * buf,                          size_t len, loff_t * off){        /* printk(KERN_INFO "<%s> file: write()\n", MODULE_NAME); */        struct ds_axidma_device *obj_dev;        if (len >= DMA_LENGTH)        {                return 0;        }        obj_dev = get_elem_from_list_by_inode(f->f_inode);        memcpy(obj_dev->ds_axidma_addr, buf, len);        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMASR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMACR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMASR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMACR));        iowrite32(1, obj_dev->virt_bus_addr + MM2S_DMACR);        iowrite32(obj_dev->ds_axidma_handle, obj_dev->virt_bus_addr + MM2S_SA);        iowrite32(len, obj_dev->virt_bus_addr + MM2S_LENGTH);        // dmaSynchMM2S(obj_dev);        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMASR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMACR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMASR));        // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMACR));                // printk(KERN_INFO "%X\n", bus_addr);        // printk(KERN_INFO "%lu\n", bus_size);        return len;}static struct file_operations fops = {        .owner = THIS_MODULE,        .open = ds_axidma_open,        .release = ds_axidma_close,        .read = ds_axidma_read,        .write = ds_axidma_write,        /* .mmap = ds_axidma_mmap, */        /* .unlocked_ioctl = ds_axidma_ioctl, */};static int ds_axidma_pdrv_probe(struct platform_device *pdev){        /* device constructor */        struct ds_axidma_device *obj_dev = (struct ds_axidma_device *)            kmalloc( sizeof(struct ds_axidma_device), GFP_KERNEL );    obj_dev->bus_addr = pdev->resource[0].start;    obj_dev->bus_size = pdev->resource[0].end - pdev->resource[0].start + 1;        obj_dev->dev_name = pdev->name + 9;                printk(KERN_INFO "<%s> init: registered\n", obj_dev->dev_name);        if (alloc_chrdev_region(&(obj_dev->dev_num), 0, 1, obj_dev->dev_name) < 0) {                return -1;        }        if (cl == NULL && (cl = class_create(THIS_MODULE, "chardrv")) == NULL) {                unregister_chrdev_region(obj_dev->dev_num, 1);                return -1;        }        if (device_create(cl, NULL, obj_dev->dev_num, NULL, obj_dev->dev_name) == NULL) {                class_destroy(cl);                unregister_chrdev_region(obj_dev->dev_num, 1);                return -1;        }        cdev_init(&(obj_dev->c_dev), &fops);        if (cdev_add(&(obj_dev->c_dev), obj_dev->dev_num, 1) == -1) {                device_destroy(cl, obj_dev->dev_num);                class_destroy(cl);                unregister_chrdev_region(obj_dev->dev_num, 1);                return -1;        }        printk(KERN_INFO "DMA_LENGTH = %u \n", DMA_LENGTH);        /* allocate mmap area */        obj_dev->ds_axidma_addr =            dma_zalloc_coherent(NULL, DMA_LENGTH, &(obj_dev->ds_axidma_handle), GFP_KERNEL);        list_add( &obj_dev->dev_list, &full_dev_list );        return 0;}static int ds_axidma_pdrv_remove(struct platform_device *pdev){        /* device destructor */        struct list_head *pos, *q;        list_for_each_safe( pos, q, &full_dev_list ) {                struct ds_axidma_device *obj_dev;            obj_dev = list_entry( pos, struct ds_axidma_device, dev_list );            if (!my_strcmp(obj_dev->dev_name, pdev->name + 9))            {                    list_del( pos );                    cdev_del(&(obj_dev->c_dev));                    device_destroy(cl, obj_dev->dev_num);                    unregister_chrdev_region(obj_dev->dev_num, 1);                    /* free mmap area */                        if (obj_dev->ds_axidma_addr) {                                dma_free_coherent(NULL, DMA_LENGTH, obj_dev->ds_axidma_addr, obj_dev->ds_axidma_handle);                        }                    kfree(obj_dev);                    break;            }          }          if (list_empty(&full_dev_list))          {                  class_destroy(cl);          }        printk(KERN_INFO "<%s> exit: unregistered\n", MODULE_NAME);        return 0;}static int ds_axidma_pdrv_runtime_nop(struct device *dev){        /* Runtime PM callback shared between ->runtime_suspend()         * and ->runtime_resume(). Simply returns success.         *         * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()         * are used at open() and release() time. This allows the         * Runtime PM code to turn off power to the device while the         * device is unused, ie before open() and after release().         *         * This Runtime PM callback does not need to save or restore         * any registers since user space is responsbile for hardware         * register reinitialization after open().         */        return 0;}static const struct dev_pm_ops ds_axidma_pdrv_dev_pm_ops = {        .runtime_suspend = ds_axidma_pdrv_runtime_nop,        .runtime_resume = ds_axidma_pdrv_runtime_nop,};static struct of_device_id ds_axidma_of_match[] = {        { .compatible = "ds_axidma", },        { /* This is filled with module_parm */ },        { /* Sentinel */ },};MODULE_DEVICE_TABLE(of, ds_axidma_of_match);module_param_string(of_id, ds_axidma_of_match[1].compatible, 128, 0);MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");static struct platform_driver ds_axidma_pdrv = {        .probe = ds_axidma_pdrv_probe,        .remove = ds_axidma_pdrv_remove,        .driver = {                .name = DRIVER_NAME,                .owner = THIS_MODULE,                .pm = &ds_axidma_pdrv_dev_pm_ops,                .of_match_table = of_match_ptr(ds_axidma_of_match),        },};module_platform_driver(ds_axidma_pdrv);MODULE_AUTHOR("Fabrizio Spada, Gianluca Durelli");MODULE_DESCRIPTION("AXI DMA driver");MODULE_LICENSE("GPL v2");
3.编译,生成BOOT.BIN文件


images/linux/目录下将生成的BOOT.BIN和image.ub文件拷贝到SD卡,插上SD卡。启动串口敲入用户名和密码(均root):


这里可以看到ds_axidma.ko这个内核module。同时可以看到apps


测试方法如下:


至此,DMA的简单实例就完成了,PS侧的DMA可以参考Audio侧,另外,如果有一些文件挂载分区,则如下:



https://blog.csdn.net/shichaog/article/details/51771247

使用特权

评论回复

相关帖子

沙发
zhangmangui|  楼主 | 2019-6-11 23:09 | 只看该作者
全是乱码   补充上传一下

dmaBench.rar (1.11 KB)

使用特权

评论回复
板凳
zhangmangui|  楼主 | 2019-6-11 23:09 | 只看该作者
ds_axidma.rar (2.86 KB)

使用特权

评论回复
地板
zhangmangui|  楼主 | 2019-6-16 23:41 | 只看该作者
https://lauri.võsandi.com/hdl/zynq/xilinx-dma.html

使用特权

评论回复
5
zhangmangui|  楼主 | 2019-6-16 23:41 | 只看该作者
6
670199012| | 2020-2-22 19:44 | 只看该作者
这话从何说起那~~~

使用特权

评论回复
发新帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:欢迎进入【TI DSP 论坛】 & 【DSP 技术】           TI忠诚粉丝!

934

主题

26373

帖子

585

粉丝