一、目标
在米尔科技的z-turn板上,采用AXI DMA 实现zynq的PS与PL数据交互。
二、分析
①PS数据传PL
驱动中的测试程序中给出一堆数据,通过DMA传输到AXI4-Stream Data FIFO ,PL端从DATA FIFO中把数据读出来。
②PL数据传PS
将PS传入PL的数据回传,在PS端显示出数据,最后将数据乘2再送入DMA。
③PL端代码思路
1)读数据
在加上DATA FIFO的情况下,PL从DATA FIFO中读取数据。将DATA -FIFO的M_AXIS端引出,得到下面的信号。

信号名称 作用 方向
m_axis_tvalid 主机向从机发数据的信号声明,置1表示主机有数据要发向从机。 输出
m_axis_tready 主机判断从机是否准备好接收数据,当为1时表示从机准备好接收数据。 输入
m_axis_tdata 主机将要发送的数据。 输出
m_axis_tkeep 主机发送数据时需拉高。 输出
m_axis_tlast 表示主机发送是否是最后一个数据,置1时表示主机正在发送最后一个数据。 输出

从上表可以看出,DATA FIFO接收完数据后,想要从FIFO中读取数据,靠的就是上面5根线,FIFO作主机,PL端作从机。所以FIFO会自动将m_axis_tvalid置1,表明可以从主机中读取数据。PL只需要给出回应m_axis_tready置1,便可以在时钟上升沿来临时读取数据。
同时,AXI-DMA将PS数据传输完成后有完成标志mm2s_introut置1。通过这个标准来确定是否从FIFO中读取数据。所以这个信号用于PL中断的触发信号。
2)写数据
将PL读取出来的数据进行乘2,再传入PS,PS再将数据打印出来。PL端接的是AXI-DMA的S_AXIS_S2MM端口。其信号如下:

信号名称 作用 方向
s_axis_s2mm_tdata 从机接收数据线 输入
s_axis_s2mm_tkeep 数据有效信号线 输入
s_axis_s2mm_tlast 是否最后一个数据 输入
s_axis_s2mm_tready 从机准备是否准备好接收数据 输出
s_axis_s2mm_tvalid 主机是否有数据发向从机 输入

从信号列表可以看出,此时DMA端口是作从机,PL端口作主机向DMA端口发送数据。PL端想发送数据,通过s_axis_s2mm_tvalid表明有数据发往从机。等待从机响应s_axis_s2mm_tready信号,响应过后便可以发送数据。发送数据时需要将s_axis_s2mm_tkeep拉高,同时当传到最后一个数据时,需要将s_axis_s2mm_tlast置1。
3)整体架构

局部放大图:



三、代码实现
①pl_read.v

module pl_read(
clk,
rst,m_axis_tvalid,
m_axis_tdata,
m_axis_tkeep,
m_axis_tlast,
m_axis_tready,m_ready,
m_data,
m_datavalid,
m_datalast
);
input clk;
input rst;input m_axis_tvalid;
input [31:0]m_axis_tdata;
input [3:0]m_axis_tkeep;
input m_axis_tlast;
output m_axis_tready;input m_ready;
output [31:0]m_data;
output m_datavalid;
output m_datalast;reg m_axis_tready;
reg [31:0]m_data;
reg m_datavalid;
reg m_datalast;
always@(posedge clk or negedge rst)
beginif(!rst)beginm_axis_tready <= 0;m_data <= 0;m_datavalid <= 0;m_datalast <= 0;endelsebeginif(m_ready==1)beginm_axis_tready <= 1;if(m_axis_tvalid==1&&m_axis_tkeep==4'b1111)beginm_data <= m_axis_tdata;m_datavalid <= 1;if(m_axis_tlast==1) m_datalast <= 1;else m_datalast <= 0;endelsebeginm_data <= m_data;m_datavalid <= 0;m_datalast <= 0;endendelsebeginm_axis_tready <= 0;m_data <= m_data;m_datavalid <= 0;m_datalast <= 0;endend
endendmodule

②pl_write.v

module pl_write(
clk,
rst,s_data,
s_datavalid,
s_datalast,
s_ready,s_axis_tdata,
s_axis_tkeep,
s_axis_tlast,
s_axis_tready,
s_axis_tvalid
);
input clk;
input rst;input[31:0] s_data;//data stream
input s_datalast;//data last flag
input s_datavalid;//input data valid flag
output s_ready;//数据可以写入标志input s_axis_tready;
output[31:0] s_axis_tdata;
output[3:0]s_axis_tkeep;
output s_axis_tlast;
output s_axis_tvalid;reg [31:0] s_axis_tdata;
reg [3:0]s_axis_tkeep;
reg s_axis_tlast;
reg s_axis_tvalid;
reg s_ready;always@(posedge clk or negedge rst)
beginif(!rst)begins_axis_tkeep <= 4'b0000;s_axis_tvalid <= 0;s_axis_tdata <= 0;s_axis_tlast<=0;s_ready <= 0;        endelsebeginif(s_axis_tready==1)beginif(s_datavalid==1)begins_axis_tvalid <= 1;s_axis_tkeep <= 4'b1111;s_axis_tdata <= s_data;if(s_datalast)s_axis_tlast <= 1;else s_axis_tlast <= 0;endelse begins_axis_tvalid <= 0;s_axis_tkeep <= 4'b0000;s_axis_tdata <= s_axis_tdata;s_axis_tlast<=0;ends_ready <= 1;endelse begins_axis_tkeep <= 4'b0000;s_axis_tvalid <= 0;s_axis_tdata <= s_axis_tdata;s_axis_tlast<=0;s_ready <= 0;endend
endendmodule

③top.v

module top(
);
wire clk;
wire rst;wire m_axis_tvalid;
wire [31:0]m_axis_tdata;
wire [3:0]m_axis_tkeep;
wire m_axis_tlast;
wire m_axis_tready;wire m_ready;
wire [31:0]m_data;
wire m_datavalid;
wire m_datalast;wire s_axis_tready;
wire[31:0] s_axis_tdata;
wire[3:0]s_axis_tkeep;
wire s_axis_tlast;
wire s_axis_tvalid;pl_write u1(
.clk(clk),
.rst(rst),.s_data(m_data),
.s_datalast(m_datalast),
.s_datavalid(m_datavalid),
.s_ready(m_ready),.s_axis_tready(s_axis_tready),
.s_axis_tdata(s_axis_tdata),
.s_axis_tkeep(s_axis_tkeep),
.s_axis_tlast(s_axis_tlast),
.s_axis_tvalid(s_axis_tvalid)
);
pl_read u2(
.clk(clk),
.rst(rst),.m_data(m_data),
.m_datalast(m_datalast),
.m_datavalid(m_datavalid),
.m_ready(m_ready),.m_axis_tready(m_axis_tready),
.m_axis_tdata(m_axis_tdata),
.m_axis_tkeep(m_axis_tkeep),
.m_axis_tlast(m_axis_tlast),
.m_axis_tvalid(m_axis_tvalid)
);
system_wrapper u3(
.FCLK_CLK0(clk),
.peripheral_aresetn(rst),
.s_axis_aclk(clk),
.s_axis_aclk_1(clk),
.s_axis_aresetn(rst),
.s_axis_aresetn_1(rst),.s_axis_tready(s_axis_tready),
.s_axis_tdata(s_axis_tdata),
.s_axis_tkeep(s_axis_tkeep),
.s_axis_tlast(s_axis_tlast),
.s_axis_tvalid(s_axis_tvalid),.m_axis_tready(m_axis_tready),
.m_axis_tdata(m_axis_tdata),
.m_axis_tkeep(m_axis_tkeep),
.m_axis_tlast(m_axis_tlast),
.m_axis_tvalid(m_axis_tvalid)
);endmodule

④dma驱动代码

#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>
/***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        524288dma_addr_t axidma_handle;
volatile unsigned int * axidma_addr;
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 int dma_exit(void);
static int dma_open(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,
};
/** 初始化,用于module init** */
static int dma_init(void)
{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",major);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);return 0;
}
/**退出 用于 module exit** */
static int 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);dma_free_coherent(NULL,DMA_LENGTH,axidma_addr,axidma_handle);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 0;
}
/**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);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);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;}memcpy(axidma_addr,buf,count);iowrite32(0x00001001,mm2s_cr);iowrite32(axidma_handle,mm2s_sa);iowrite32(count,mm2s_len);mm2s_status = ioread32(mm2s_sr);while((mm2s_status&(1<<1))==0){mm2s_status = ioread32(mm2s_sr);}printk("mm2s_status =0x%x\n",mm2s_status);printk("dma write is over!\n");return 0;
}
/** read 接口函数*DMA读取数据是按照32bit读取的* */
static int dma_read(struct file *file,char __user *buf,size_t size,loff_t *ppos)
{unsigned int s2mm_status=0;printk("dma read start!\n");if(size>DMA_LENGTH){printk("the number of data is not enough!\n");return 1;}iowrite32(0x00001001,s2mm_cr);iowrite32(axidma_handle,s2mm_da);iowrite32(size,s2mm_len);s2mm_status=ioread32(s2mm_sr);while((s2mm_status&(1<<1))==0){s2mm_status=ioread32(s2mm_sr);}printk("s2mm_sr=0x%x\n",s2mm_status);memcpy(buf,axidma_addr,size);printk("\ndma read is over!\n");return 0;
}module_init(dma_init);
module_exit(dma_exit);MODULE_AUTHOR("TEST@dma");
MODULE_DESCRIPTION("dma driver");
MODULE_ALIAS("dma linux driver");
MODULE_LICENSE("GPL");

⑤测试代码

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>void delay(void)
{int i,j;for(i=0;i<20000;i++)for(j=0;j<10000;j++);
}
unsigned int readarray[10001];
int main(int argc , char ** argv)
{int fd;int i=0;fd = open("/dev/dma_dev",O_RDWR);if(fd<0) {printf("can not open file\n");while(1);}else printf("open file sucuss\n");delay();for(i=0;i<4000;i++){readarray[i]=i+1;}while(1){write(fd,readarray,4000*4);if(read(fd,readarray,4000*4)==0){for(i=0;i<4000;i++){printf(" %d",readarray[i]);readarray[i]=readarray[i]*2;}printf("\n=====================================\n");printf("======================================\n");}delay();delay();}return 0;
}

⑥Makefile文件

KDIR = /home/python/Hard_disk_21G/04-Linux_Source/Kernel/linux-xlnx
PWD := $(shell pwd)
CC   = $(CROSS_COMPILE)gcc
ARCH =arm
MAKE =make
obj-m:=dma_driver.omodules:$(MAKE) -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPLE) M=$(PWD) modules
clean:make -C $(KDIR) ARCH=$(ARCH) CROSS_COMPLE=$(CROSS_COMPLE) M=$(PWD) clean

⑦运行结果


可以看见程序运行结果,符合预期值。
四、调试
在调试数据交互的过程中,遇到了很多坑。

 第一个大坑就是在DATA-FIFO中。数据位宽设置的8bit,PL将测试数据写入DMA,PS端读出数据,发现数据变得大而且只有前面部分数据不为0,后面数据全是0,后来发现是PS端读DMA是按照字节进行读取的,而PS端读取的数据放在整形数组里面,结果导致是PL端的4个数据合成了PS端一个数据,最后导致PL端发送200个数据,结果PS端只有前50个数据有值,后面全是0。后面将PL的位宽设置为32bit,PS端读出数据就正缺了。  第二大坑就是忽略了tlast信号。这块问题找了很久,最后通过慢慢测试发现了。当时在PL端发送10万个数据,PS端一次性读取10万个数据没问题,然后想测试读取少一点,结果读取一千,两千,一万个数据都可以,同时数据是正确的。到了第二次循环是就出现问题了,第二次读取的数据还是同第一次读取的数据一样。通过读取DMA状态寄存器的值,打印出来是0x4011,判断出DMA内部错误。后来调试出问题是:在传输期间没有tlast信号,所以报出DMA内部错误。再次调试,设置一万个数据给个tlast,读取2万个数据,最后发现只能读取出前面1万个数据,后面全是0,第二轮还是一样,只有前面1万个有数据,后面全是0。最后明白了DMA传输中在一次传输过程中,只能且必须有一次tlast信号,也不能读出tlast信号后的数据。第三个坑就是在模块设计时,报DMA的S_AXIS_S2MM与FIFO的M_AXIS时钟不匹配,还以为是哪儿没弄对,看别人的都没问题。后来把器件全删了,先把fifo与DMA联接起来,在进行自动连线,问题就解决了。第四个坑就在PL端的例化,连线上。在仿真fifo的时候,先编写一个模块与fifo对应,同时在模块里面对fifo的S_AXIS端连线,在simulation文件中将fifo的M_AXIS连线,功能仿真想观察波形,结果咋调,波形都是xxxx,AXI时序修改了又修改,还是出问题。后来发现是出现了两个fifo,一个是模块里连线的fifo,另一个是simulation里的fifo,所以就出现了波形是xxxx的情况。后面改成了,增加一个top.v在top里面进行各个模块的连线,时钟也不要在单独例化在底层模块里,全部在top里面连线,例化。

五、总结
①每次进行DMA传输是,读取DMA状态寄存器,根据状态寄存器的值,判断是否发生DMA错误。如果发生DMA内部错误,则是由于DMA传输过程中没有收到tlast信号。
②PS是按字节进行读取或写入DMA的,要注意存放数据的数组的变量类型,即整形数组和字符型数组和PL端的位宽匹配好。

Zynq-linux PL与PS通过DMA数据交互相关推荐

  1. Zynq 7000 PL和PS通信——使用BRAM

    Zynq 7000 PL和PS通信--使用BRAM 介绍 1 准备工作 2 PL搭建BRAM 3 下载PL程序 4 编写Linux应用程序 5 问题 5.1 BRAM的寻址的问题 5.2 PS地址映射 ...

  2. 【ZYNQ Ultrascale+ MPSOC FPGA教程】第三十二章 PL读写PS端DDR数据

    本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处. 适用于板卡型号: AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4E ...

  3. ZYNQ的PL控制PS的DDR

    本次工程是用PL端控制PS的DDR,下面是一些过程 1. 创建一个ZYNQ核 选择高速互联总线,因为DDR速率比较快,所以PS与PL端的交互,我们选择HP,高速AXI连接,位宽选择32,和DDR位宽保 ...

  4. ZYNQ linux环境下PS I2C配置OV5640

    平台:ubuntu虚拟机 ZYNQ7035 1.vivado编辑BD文件,设置两个IIC接口 2.设备树搭建,应用petalinux调用hdf直接生成 在Ubuntu虚拟机内搭建工程 source / ...

  5. ZYNQ PS PL 数据交互 Bram

    文章目录 前言 一.ZYNQ数据交互方式 二.Create Block Design 1.创建硬件工程 2.封装的ip代码 3.使用vitis写ps端程序 输出效果 前言 关于zynq PS PL 数 ...

  6. 米联客 ZYNQ/SOC 精品教程 S02-CH19 利用BRAM进行PS与PL间数据交互

    软件版本:VIVADO2017.4 操作系统:WIN10 64bit 硬件平台:适用米联客 ZYNQ系列开发板 米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!! ...

  7. zynq sdk 开发之通过 BRAM 进行 PL 与 PS 的数据交互

    如何在 zynq 中进行 PL 端与 PS 端的数据交互? 在zynq的使用中,高效的进行 BRAM 与 zynq 硬核的数据交换至关重要,当我们需要进行小批量的数据交换时,可以考虑采用 BRAM 作 ...

  8. ZYNQ学习之路(三):自定义IP实现PL处理PS写入BRAM的数据

    目录 一.实验简介 二.vivado部分处理 三.SDK编程 四.实验测试 五.总结 一.实验简介 ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM,PL可以将数据读取后再重新写入BRA ...

  9. Linux下pl与ps端的通信,Overlay设计方法篇之第四章PS与PL的交互

    Overlay由两个主要部分组成--bitstream文件和hwh(Hardware Handoff)文件.可以说Overlay设计其实就是一种PL与PS的交互设计.通常PL设计针对特定任务进行高度优 ...

最新文章

  1. inux 软件编译、安装、删除
  2. Emgu-WPF学习使用-识别二维码的位置
  3. 【学术相关】考研初试成绩出来了,然后呢...选导师!
  4. 异常php有必要吗,关于php异常的问题,到底有什么好处?
  5. CodeForces - 1335F Robots on a Grid(拓扑找环+反向dfs/倍增)
  6. eclipse注释日期格式修改
  7. Notes of the scrum meeting(2013/10/23)
  8. SpringBoot 集成 mybatisPlus
  9. Oracle游标使用
  10. numpy 矩阵拼接_Numpy学习笔记(下篇)
  11. 室内三维地图编辑器,制作地图软件哪个最好
  12. Golang编程语言简介 go语言特点
  13. Android Studio一键汉化,分享一个自己写的小工具
  14. Word 2016双击格式刷无法连用的问题
  15. 从TCL的40年变革史,看中国制造之路
  16. Unity打开.exe文件
  17. js 实现网页内容语音朗读功能
  18. 百度Sugar BI 数据可视化里的标签页组件如何实现
  19. IDEA配置远程debug调试
  20. Python 将TXT格式转换为手机通讯录格式vcf

热门文章

  1. document.all详解
  2. layuiAdmin std v1.x 【iframe版】开发者文档
  3. Python文本分析——词云图(wordcloud+jieba)
  4. python成语填空_python一步一步解析成语
  5. vue使用高德地图加载kml文件
  6. mbr病毒程序源代码分享2
  7. VOSviewer使用体验
  8. PADS版本历史,这么看来我装的PADS VX2.3还是比较新的,不会纠结百度搜到很多别人的什么PADS9.5,以为是什么不同版本,其实是旧的版本。
  9. Delphi组件源码:HTML Component Library 3.9
  10. 计算机二级c语言公共基础知识重点,计算机二级C语言公共基础知识与考点汇总...