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

本文介绍了如何在米尔科技的Z-Turn开发板上利用Linux设备树驱动实现PL数据流通过双DMA循环切换传输至PS,以防止数据丢失。自定义IP核产生数据,通过axi-gpio启动,并通过DMA切换模块分批发送。设备树、GPIO及DMA驱动代码、Makefile和测试文件详述了整个流程。在下板验证中,中断计数结果显示符合预期。
摘要由CSDN通过智能技术生成

一.目标
在米尔科技的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)
begin
    if(!rst)
    begin
        data_out <= 0;
        data_enable <= 0;
        cnt <= 0;
    end
    else
    begin
        if(enable)
        begin
            if(cnt>=13)cnt <= 0;
            else cnt <= cnt + 1;
            if(cnt>=1&&cnt<=8)
            begin
                data_out <= data_out + 1;
                data_enable <= 1;
            end
            else
            begin
                data_out <= data_out;
                data_enable <= 0;
            end
        end
        else
        begin
            data_enable <= 0;
            data_out   <= data_out;
            cnt <= cnt;
        end
    end
end
endmodule

②DMA切换模块:selet_dma.v

module selet_dma(
 clk,
 rst,
 data_valid,
 data_in,
 //dma1
 dma1_tvalid,
 dma1_tkeep,
 dma1_tlast,
 dma1_tready,
 dma1_tdata,
 //dma2
 dma2_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;
//dma1
output dma1_tvalid;
output [3:0]dma1_tkeep;
output dma1_tlast;
output [31:0]dma1_tdata;
input dma1_tready;
//dma2
output dma2_tvalid;
output [3:0]dma2_tkeep;
output dma2_tlast;
output [31:0]dma2_tdata;
input dma2_tready;
//count data
output[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)
begin
    if(!rst)selet <= 0;
    else selet <= temp_selet;
end
always@(negedge clk or negedge rst)
begin
    if(!rst)temp_selet <= 0;
    else
    begin
        if(count_dma1==20'd99999) temp_selet <= 1;
        else if(count_dma2==20'd99999)temp_selet <= 0;
        else temp_selet <= temp_selet;
    end
end
always@(negedge clk or negedge rst)
begin
    if(!rst)
    begin
        count_dma1 <= 0;
        count_dma2 <= 0;
    end
    else
    begin
        if(selet==0 && dma1_tready==1 && data_valid==1 )
        begin
            count_dma2 <= 0;
            count_dma1 <= count_dma1 + 1;
        end
        else if(selet==1 && dma2_tready==1 && data_valid==1)
        begin
            count_dma1 <= 0;
            count_dma2 <= count_dma2 + 1;
        end
        else
        begin
            count_dma2 <= count_dma2;
            count_dma1 <= count_dma1;
        end
    end
end
always@(negedge clk or negedge rst)
begin
    if(!rst)
    begin
        dma1_tvalid <= 0;
        dma1_tkeep  <= 0;
        dma1_tlast  <= 0;
        dma1_tdata  <= 0;
        
        dma2_tvalid <= 0;
        dma2_tkeep  <= 0;
        dma2_tlast  <= 0;
        dma2_tdata  <= 0;
    end
    else
    begin
        if(data_valid)
        begin
            if(selet==0)//dma1
            begin
                dma1_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)
                begin
                    dma1_tlast <= 1;
                end
                else
                begin
                    dma1_tlast <= 0;
                end    
            end
            else //dma2
            begin
                dma2_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;   
                end                              
                else                             
                begin                            
                    dma2_tlast <= 0;     
                end                                                             
            end
        end
        else
        begin           
            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;         
        end
    end
end
endmodule

③设备树代码 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
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值