一.目标
在米尔科技的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