100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > ZYNQ-Linux设备树驱动下的双DMA循环切换传输数据

ZYNQ-Linux设备树驱动下的双DMA循环切换传输数据

时间:2019-10-22 21:20:09

相关推荐

ZYNQ-Linux设备树驱动下的双DMA循环切换传输数据

一.目标

在米尔科技的z-turn 开发板上实现PL数据流送往PS。

二.流程分析

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

自定义一个IP核用于产生源源不断的测试数据模拟数据源,再自定义一个IP用于切换DMA发送数据。

系统框图如下:

通过axi-gpio启动数据源产生数据,数据流再通过DMA切换模块分批次将数据送往DMA。每个DMA发送10万个数据便进行切换,所以数据源采用从1开始累加产生数据,每次在DMA中断中验证最后一个数据是否是10万,20万,30万等等。

三.代码

①数据源模块data_product.v

module data_product(clk,rst,enable,data_out,data_enable,cnt);input clk;input rst;input enable;output [31:0]data_out;output data_enable;output[3:0]cnt;reg [31:0]data_out;reg data_enable;reg[3:0]cnt;always@(posedge clk or negedge rst)beginif(!rst)begindata_out <= 0;data_enable <= 0;cnt <= 0;endelsebeginif(enable)beginif(cnt>=13)cnt <= 0;else cnt <= cnt + 1;if(cnt>=1&&cnt<=8)begindata_out <= data_out + 1;data_enable <= 1;endelsebegindata_out <= data_out;data_enable <= 0;endendelsebegindata_enable <= 0;data_out <= data_out;cnt <= cnt;endendendendmodule

②DMA切换模块:selet_dma.v

module selet_dma(clk,rst,data_valid,data_in,//dma1dma1_tvalid,dma1_tkeep,dma1_tlast,dma1_tready,dma1_tdata,//dma2dma2_tvalid,dma2_tkeep,dma2_tlast,dma2_tready,dma2_tdata,//count_dma1,count_dma2);input clk;input rst;input data_valid;input [31:0]data_in;//dma1output dma1_tvalid;output [3:0]dma1_tkeep;output dma1_tlast;output [31:0]dma1_tdata;input dma1_tready;//dma2output dma2_tvalid;output [3:0]dma2_tkeep;output dma2_tlast;output [31:0]dma2_tdata;input dma2_tready;//count dataoutput[19:0]count_dma1;//数据输出个数output[19:0]count_dma2;//reg dma1_tvalid;reg [3:0]dma1_tkeep;reg dma1_tlast;reg [31:0]dma1_tdata;//reg dma2_tvalid;reg [3:0]dma2_tkeep;reg dma2_tlast;reg [31:0]dma2_tdata;//reg[19:0]count_dma1;//数据输出个数reg[19:0]count_dma2;reg selet;reg temp_selet;always@(negedge clk or negedge rst)beginif(!rst)selet <= 0;else selet <= temp_selet;endalways@(negedge clk or negedge rst)beginif(!rst)temp_selet <= 0;elsebeginif(count_dma1==20'd99999) temp_selet <= 1;else if(count_dma2==20'd99999)temp_selet <= 0;else temp_selet <= temp_selet;endendalways@(negedge clk or negedge rst)beginif(!rst)begincount_dma1 <= 0;count_dma2 <= 0;endelsebeginif(selet==0 && dma1_tready==1 && data_valid==1 )begincount_dma2 <= 0;count_dma1 <= count_dma1 + 1;endelse if(selet==1 && dma2_tready==1 && data_valid==1)begincount_dma1 <= 0;count_dma2 <= count_dma2 + 1;endelsebegincount_dma2 <= count_dma2;count_dma1 <= count_dma1;endendendalways@(negedge clk or negedge rst)beginif(!rst)begindma1_tvalid <= 0;dma1_tkeep <= 0;dma1_tlast <= 0;dma1_tdata <= 0;dma2_tvalid <= 0;dma2_tkeep <= 0;dma2_tlast <= 0;dma2_tdata <= 0;endelsebeginif(data_valid)beginif(selet==0)//dma1begindma1_tvalid <= 1;dma1_tkeep <= 4'b1111;dma1_tdata <= data_in;dma2_tvalid <= 0;dma2_tkeep <= 0;dma2_tlast <= 0;dma2_tdata <= 0;if(count_dma1>=20'd99999)begindma1_tlast <= 1;endelsebegindma1_tlast <= 0;end endelse //dma2begindma2_tvalid <= 1;dma2_tkeep <= 4'b1111; dma2_tdata <= data_in; dma1_tvalid <= 0;dma1_tkeep <= 0;dma1_tlast <= 0;dma1_tdata <= 0;if(count_dma2>=20'd99999) begin dma2_tlast <= 1; endelse begin dma2_tlast <= 0;end endendelsebegin dma1_tvalid <= 0;dma1_tkeep <= 0;dma1_tdata <= dma1_tdata;dma1_tlast <= dma1_tlast;dma2_tvalid <= 0; dma2_tkeep <= 0; dma2_tdata <= dma2_tdata;dma2_tlast <= dma2_tlast; endendendendmodule

③设备树代码 pl.dtsi

/ {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_s2mm_aclk";clocks = <&clkc 15>, <&clkc 15>;compatible = "xlnx,axi-dma0";interrupt-names = "s2mm_introut";interrupt-parent = <&intc>;interrupts = <0 29 4>;reg = <0x40400000 0x10000>;xlnx,addrwidth = <0x20>;xlnx,sg-length-width = <0x14>;dma-channel@40400030 {compatible = "xlnx,axi-dma-s2mm-channel";dma-channels = <0x1>;interrupts = <0 29 4>;xlnx,datawidth = <0x20>;xlnx,device-id = <0x0>;};};axi_dma_1: dma@40410000 {#dma-cells = <1>;clock-names = "s_axi_lite_aclk", "m_axi_s2mm_aclk";clocks = <&clkc 15>, <&clkc 15>;compatible = "xlnx,axi-dma1";interrupt-names = "s2mm_introut";interrupt-parent = <&intc>;interrupts = <0 30 4>;reg = <0x40410000 0x10000>;xlnx,addrwidth = <0x20>;xlnx,sg-length-width = <0x14>;dma-channel@40410030 {compatible = "xlnx,axi-dma-s2mm-channel";dma-channels = <0x1>;interrupts = <0 30 4>;xlnx,datawidth = <0x20>;xlnx,device-id = <0x1>;};};axi_gpio_0: gpio@41200000 {#gpio-cells = <3>;clock-names = "s_axi_aclk";clocks = <&clkc 15>;compatible = "xlnx,axi-gpio-test";gpio-controller ;reg = <0x41200000 0x10000>;xlnx,all-inputs = <0x0>;xlnx,all-inputs-2 = <0x0>;xlnx,all-outputs = <0x1>;xlnx,all-outputs-2 = <0x0>;xlnx,dout-default = <0x00000000>;xlnx,dout-default-2 = <0x00000000>;xlnx,gpio-width = <0x1>;xlnx,gpio2-width = <0x20>;xlnx,interrupt-present = <0x0>;xlnx,is-dual = <0x0>;xlnx,tri-default = <0xFFFFFFFF>;xlnx,tri-default-2 = <0xFFFFFFFF>;};};};

④gpio驱动代码gpio_driver.c

#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/proc_fs.h>#include <linux/seq_file.h> /* Needed for Sequence File Operations */#include <linux/mutex.h>#include <linux/sysctl.h>#include <linux/fs.h>#include <linux/cdev.h>#include "linux/device.h"#include "linux/miscdevice.h"#include <linux/platform_device.h>#include <linux/clk.h>#include <linux/of_irq.h>#include <linux/slab.h>#include <linux/bitops.h>#include <linux/dmapool.h>#include <linux/of_dma.h>#include <linux/dma-mapping.h>#include <linux/kfifo.h>#include "linux/kthread.h"#include "linux/wait.h"#include "linux/completion.h"#include "linux/workqueue.h"#include <linux/io.h>#include <linux/uaccess.h>int major;static struct class *gpio_class = NULL;void __iomem *base_regs;/**open 接口函数** */static int axi_gpio_open(struct inode *inode,struct file *file){printk("enter axi_gpio_open \r\n");return 0;}/** write 接口函数** */static int axi_gpio_write(struct file *file,const char __user *buf, size_t count,loff_t *ppos){unsigned int ret=0;int data;printk("gpio write start !\n");ret=copy_from_user(&data,buf,count);if(ret!=0){printk("data read fail\n");return -1;}iowrite32(data,base_regs);return 0;}/** read 接口函数** */static int axi_gpio_read(struct file *file,char __user *buf,size_t size,loff_t *ppos){printk("gpio read start!\n");return 0;}static struct file_operations axi_gpio_fops={.owner = THIS_MODULE,.read = axi_gpio_read,.write = axi_gpio_write,.open = axi_gpio_open,};unsigned int irq_num;static int gpio_probe(struct platform_device *pdev){struct resource *io;printk("enter gpio probe\r\n");//从设备树中获取gpio基地址,同时进行地址映射,虚拟地址保存在 base_regs 中io = platform_get_resource(pdev, IORESOURCE_MEM, 0);base_regs = devm_ioremap_resource(&pdev->dev, io);printk("gpio base address is: 0X:%x\r\n",base_regs);iowrite32(0x0,base_regs);major = register_chrdev( 0 , "axi_gpio_dev" , &axi_gpio_fops);gpio_class= class_create (THIS_MODULE , "axi_gpio_dev");device_create(gpio_class, NULL, MKDEV(major,0), NULL, "axi_gpio_dev");printk("major dev number= %d",major);return 0;}static int gpio_remove(struct platform_device *pdev){unregister_chrdev(major,"axi_gpio_dev");device_destroy(gpio_class,MKDEV(major,0));class_destroy(gpio_class);iounmap(base_regs);return 0;}static const struct of_device_id my_axigpio_of_ids[] = {{.compatible = "xlnx,axi-gpio-test",},{}};static struct platform_driver my_axigpio_driver = {.driver = {.name = "my_axi_gpio",.of_match_table = my_axigpio_of_ids,},.probe = gpio_probe,.remove = gpio_remove,};module_platform_driver(my_axigpio_driver);MODULE_AUTHOR("TES@gpio");MODULE_DESCRIPTION("my gpio driver");MODULE_ALIAS("my gpio linux driver");MODULE_LICENSE("GPL");

⑥dma驱动代码dma0_driver.c

#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/fcntl.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/proc_fs.h>#include <linux/seq_file.h> /* Needed for Sequence File Operations */#include <linux/mutex.h>#include <linux/sysctl.h>#include <linux/fs.h>#include <linux/cdev.h>#include "linux/device.h"#include "linux/miscdevice.h"#include <linux/platform_device.h>#include <linux/clk.h>#include <linux/of_irq.h>#include <linux/slab.h>#include <linux/bitops.h>#include <linux/dmapool.h>#include <linux/of_dma.h>#include <linux/dma-mapping.h>#include <linux/kfifo.h>#include "linux/kthread.h"#include "linux/wait.h"#include "linux/completion.h"#include "linux/workqueue.h"#include <linux/io.h>#include <linux/uaccess.h>/***DMA驱动程序** **///DMA MM2S控制寄存器volatile unsigned int * mm2s_cr;#define MM2S_DMACR0X00000000//DMA MM2S状态控制寄存器volatile unsigned int * mm2s_sr;#define MM2S_DMASR0X00000004//DMA MM2S源地址低32位volatile unsigned int * mm2s_sa;#define MM2S_SA0X00000018//DMA MM2S传输长度(字节)volatile unsigned int * mm2s_len;#define MM2S_LENGTH0X00000028//DMA S2MM控制寄存器volatile unsigned int * s2mm_cr;#define S2MM_DMACR0X00000030//DMA S2MM状态控制寄存器volatile unsigned int * s2mm_sr;#define S2MM_DMASR0X00000034//DMA S2MM目标地址低32位volatile unsigned int * s2mm_da;#define S2MM_DA0X00000048//DMA S2MM传输长度(字节)volatile unsigned int * s2mm_len;#define S2MM_LENGTH0X00000058#define DMA_BUF_SIZE 1024*1024void __iomem *base_regs;int major;dma_addr_t phyaddr;//物理地址char *buf;//虚拟地址static struct class *dma_class = NULL;//DMA interrupt functionsint dma_flag=0;unsigned int status=0;static int irq_cnt=0;static irqreturn_t dma0_s2mm_irq(int irq,void *dev_id){unsigned int data;irq_cnt++;if(irq_cnt%5==0){data = (buf[399999]<<24)|(buf[399998]<<16)|(buf[399997]<<8)|(buf[399996]);printk(" %d",data);}iowrite32(0x0000f000,base_regs + S2MM_DMASR);iowrite32(0x00001001,base_regs + S2MM_DMACR);//open int & enable DMAiowrite32(phyaddr ,base_regs + S2MM_DA);iowrite32(4000000 ,base_regs + S2MM_LENGTH);//write transmission length and DMA start transmissionreturn IRQ_HANDLED;}/**open 接口函数** */static int axi_dma_open(struct inode *inode,struct file *file){printk("enter axi_dma_open \r\n");iowrite32(0x00001001,base_regs + S2MM_DMACR);//open int & enable DMAiowrite32(phyaddr ,base_regs + S2MM_DA);iowrite32(400000 ,base_regs + S2MM_LENGTH);//write transmission length and DMA start transmissionreturn 0;}/** write 接口函数** */static int axi_dma_write(struct file *file,const char __user *buff, size_t count,loff_t *ppos){printk("dma write start !\n");return 0;}/** read 接口函数** */static int axi_dma_read(struct file *file,char __user *buff,size_t size,loff_t *ppos){printk("dma read start !\n");return 0;}static unsigned int axi_dma_poll(struct file *file, poll_table *wait){printk("enter axi_dma_poll \r\n");return 0;}static int axi_dma_mmap (struct file *file, struct vm_area_struct *vma){printk("enter axi_dma_mmap \r\n");vma->vm_flags |= VM_IO;vma->vm_flags |= VM_LOCKED;vma->vm_pgoff = phyaddr >> PAGE_SHIFT;vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);if(remap_pfn_range(vma, vma->vm_start,vma->vm_pgoff, vma->vm_end-vma->vm_start, vma->vm_page_prot)){printk("axi_dma_mmap failed.\n");return -EAGAIN;}printk("base start=0x%08lx, \n",(unsigned long)phyaddr);printk("vma start=0x%08lx, size=%ld.\n",(unsigned long)vma->vm_start,(unsigned long)vma->vm_end - (unsigned long)vma->vm_start); return 0;}static struct file_operations axi_dma_fops={.owner = THIS_MODULE,.read = axi_dma_read,.write = axi_dma_write,.open = axi_dma_open,.mmap = axi_dma_mmap,/* mmap method */ };unsigned int irq_num;static int my_dma_probe(struct platform_device *pdev){struct resource *io;struct device_node *node = pdev->dev.of_node;struct device_node *child;int err;struct device *dev;//从设备树中获取dma基地址,同时进行地址映射,虚拟地址保存在 base_regs 中io = platform_get_resource(pdev, IORESOURCE_MEM, 0);base_regs = devm_ioremap_resource(&pdev->dev, io);printk("dma base address is: 0X:%x\r\n",base_regs);//申请中断for_each_child_of_node(node, child){if(of_device_is_compatible(child, "xlnx,axi-dma-s2mm-channel")){printk("axi dma s2mm valid\r\n");irq_num = irq_of_parse_and_map(child, 0);err = request_irq(irq_num, dma0_s2mm_irq, IRQF_TRIGGER_RISING, "dma0_s2mm_irq", NULL);if (err) {printk( "dma0 unable to request IRQ %d\n", irq_num);return err;}}else{printk("dma0 Invalid channel compatible node\n");return -EINVAL;}}///申请dma缓冲区--起始dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);if (!dev)return -ENOMEM;dev = &pdev->dev;dev->coherent_dma_mask = 0xffffffff;buf = dma_alloc_coherent(dev, PAGE_ALIGN(DMA_BUF_SIZE), &phyaddr, GFP_KERNEL);if(!buf){printk("axi vdma buffer memory allocation failed\r\n");return -ENOMEM;}else{printk("buf=0x%x\n",buf);printk(" phyaddr=0x%x\r\n",phyaddr);}///申请dma缓冲区--结束major = register_chrdev( 0 , "axi_dma0_dev" , &axi_dma_fops);dma_class= class_create (THIS_MODULE , "axi_dma0_dev");device_create(dma_class, NULL, MKDEV(major,0), NULL, "axi_dma0_dev");printk("major dev number= %d",major);return 0;}static int my_dma_remove(struct platform_device *pdev){unregister_chrdev(major,"axi_dma0_dev");device_destroy(dma_class,MKDEV(major,0));class_destroy(dma_class);free_irq(irq_num,NULL);dma_free_coherent(NULL, PAGE_ALIGN(DMA_BUF_SIZE), buf, phyaddr);iounmap(base_regs);return 0;}static const struct of_device_id my_dma_of_ids[] = {{.compatible = "xlnx,axi-dma0",},{}};static struct platform_driver my_dma_driver = {.driver = {.name = "my_axi_dma0",.of_match_table = my_dma_of_ids,},.probe = my_dma_probe,.remove = my_dma_remove,};module_platform_driver(my_dma_driver);MODULE_AUTHOR("TEST@dma0");MODULE_DESCRIPTION("dma0 driver");MODULE_ALIAS("dma0 linux driver");MODULE_LICENSE("GPL");

⑦Makefile 文件

KDIR = /home/python/Hard_disk_21G/04-Linux_Source/Kernel/linux-xlnxPWD := $(shell pwd)CC = $(CROSS_COMPILE)gccARCH =armMAKE =makeobj-m:=dma0_driver.omodules:$(MAKE) -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPLE) M=$(PWD) modulesclean:make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPLE) M=$(PWD) clean

⑧测试文件 test_my_dma.c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <getopt.h> #include <fcntl.h> #include <unistd.h>#include <errno.h>#include <malloc.h>#include <sys/stat.h>#include <sys/types.h>#include <sys/time.h>#include <sys/mman.h>#include <sys/ioctl.h>#define DMA_BUF_SIZE 1024*1024void delay(){int i=0,j=0;for(i=0;i<10000;i++)for(j=0;j<1000;j++)j++;}int main(){int dma0_fd,dma1_fd,gpio_fd;int i=0,j=0;unsigned int * dma0_mmapBuf;unsigned int * dma1_mmapBuf;int dma0_flag,dma1_flag,gpio_flag;int *dest;dma0_fd = open("/dev/axi_dma0_dev", O_RDWR, 0);if (dma0_fd < 0) {printf("Open %s failed\n", "/dev/axivdma_dev");return -1;}dma0_mmapBuf = mmap(NULL, DMA_BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dma0_fd, 0);dma1_fd = open("/dev/axi_dma1_dev", O_RDWR, 0);if (dma1_fd < 0) {printf("Open %s failed\n", "/dev/axivdma_dev");return -1;}dma1_mmapBuf = mmap(NULL, DMA_BUF_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, dma1_fd, 0);gpio_fd = open("/dev/axi_gpio_dev", O_RDWR, 0);if (gpio_fd < 0) {printf("Open %s failed\n", "/dev/axi_gpio_dev");return -1;}gpio_flag=1;printf("======start====\r\n");delay();write(gpio_fd,&gpio_flag,4);delay();while(1);printf("dma test Done.\n");return 0;}

四.linux设备树编译

①在Ubuntu里下载device-tree-xlnx,执行下面代码

git clone /Xilinx/device-tree-xlnx.gitcd device-tree-xlnxgit checkout xilinx-v.4

会得到下面文件

将其拷贝到windows上,进入SDK进行配置,可以得到以下文件

其中pl.dtsi为使用的设备树驱动文件。将pcw.dtsi、pl.dtsi、system-top.dts、zynq-7000.dtsi拷贝到ubuntu中,使用内核自带工具dtc工具(位于linux-xlnx/scripts/dtc/dtc下)将其编译成设备树。但需要先将system-top.dts修改成如下

/dts-v1/;/include/ "zynq-7000.dtsi"/include/ "pl.dtsi"/include/ "pcw.dtsi"

再使用dtc工具编译设备树,指令如下:

Kernel/linux-xlnx/scripts/dtc/dtc -I dts -O dtb -o devicetree.dtb system-top.dts

五.下板验证

在DMA0驱动中,每次传输完成在中断里进行计数,每5次中断输出一次数据,可以看见结果符合预期。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。