Zynq-linux PL与PS通过DMA数据交互
一、目标
在米尔科技的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数据交互相关推荐
- 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地址映射 ...
- 【ZYNQ Ultrascale+ MPSOC FPGA教程】第三十二章 PL读写PS端DDR数据
本原创教程由芯驿电子科技(上海)有限公司(ALINX)创作,版权归本公司所有,如需转载,需授权并注明出处. 适用于板卡型号: AXU2CGA/AXU2CGB/AXU3EG/AXU4EV-E/AXU4E ...
- ZYNQ的PL控制PS的DDR
本次工程是用PL端控制PS的DDR,下面是一些过程 1. 创建一个ZYNQ核 选择高速互联总线,因为DDR速率比较快,所以PS与PL端的交互,我们选择HP,高速AXI连接,位宽选择32,和DDR位宽保 ...
- ZYNQ linux环境下PS I2C配置OV5640
平台:ubuntu虚拟机 ZYNQ7035 1.vivado编辑BD文件,设置两个IIC接口 2.设备树搭建,应用petalinux调用hdf直接生成 在Ubuntu虚拟机内搭建工程 source / ...
- ZYNQ PS PL 数据交互 Bram
文章目录 前言 一.ZYNQ数据交互方式 二.Create Block Design 1.创建硬件工程 2.封装的ip代码 3.使用vitis写ps端程序 输出效果 前言 关于zynq PS PL 数 ...
- 米联客 ZYNQ/SOC 精品教程 S02-CH19 利用BRAM进行PS与PL间数据交互
软件版本:VIVADO2017.4 操作系统:WIN10 64bit 硬件平台:适用米联客 ZYNQ系列开发板 米联客(MSXBO)论坛:www.osrc.cn答疑解惑专栏开通,欢迎大家给我提问!! ...
- zynq sdk 开发之通过 BRAM 进行 PL 与 PS 的数据交互
如何在 zynq 中进行 PL 端与 PS 端的数据交互? 在zynq的使用中,高效的进行 BRAM 与 zynq 硬核的数据交换至关重要,当我们需要进行小批量的数据交换时,可以考虑采用 BRAM 作 ...
- ZYNQ学习之路(三):自定义IP实现PL处理PS写入BRAM的数据
目录 一.实验简介 二.vivado部分处理 三.SDK编程 四.实验测试 五.总结 一.实验简介 ZYNQ系列嵌入式FPGA可以使PS将数据写入PL部分BRAM,PL可以将数据读取后再重新写入BRA ...
- Linux下pl与ps端的通信,Overlay设计方法篇之第四章PS与PL的交互
Overlay由两个主要部分组成--bitstream文件和hwh(Hardware Handoff)文件.可以说Overlay设计其实就是一种PL与PS的交互设计.通常PL设计针对特定任务进行高度优 ...
最新文章
- inux 软件编译、安装、删除
- Emgu-WPF学习使用-识别二维码的位置
- 【学术相关】考研初试成绩出来了,然后呢...选导师!
- 异常php有必要吗,关于php异常的问题,到底有什么好处?
- CodeForces - 1335F Robots on a Grid(拓扑找环+反向dfs/倍增)
- eclipse注释日期格式修改
- Notes of the scrum meeting(2013/10/23)
- SpringBoot 集成 mybatisPlus
- Oracle游标使用
- numpy 矩阵拼接_Numpy学习笔记(下篇)
- 室内三维地图编辑器,制作地图软件哪个最好
- Golang编程语言简介 go语言特点
- Android Studio一键汉化,分享一个自己写的小工具
- Word 2016双击格式刷无法连用的问题
- 从TCL的40年变革史,看中国制造之路
- Unity打开.exe文件
- js 实现网页内容语音朗读功能
- 百度Sugar BI 数据可视化里的标签页组件如何实现
- IDEA配置远程debug调试
- Python 将TXT格式转换为手机通讯录格式vcf
热门文章
- document.all详解
- layuiAdmin std v1.x 【iframe版】开发者文档
- Python文本分析——词云图(wordcloud+jieba)
- python成语填空_python一步一步解析成语
- vue使用高德地图加载kml文件
- mbr病毒程序源代码分享2
- VOSviewer使用体验
- PADS版本历史,这么看来我装的PADS VX2.3还是比较新的,不会纠结百度搜到很多别人的什么PADS9.5,以为是什么不同版本,其实是旧的版本。
- Delphi组件源码:HTML Component Library 3.9
- 计算机二级c语言公共基础知识重点,计算机二级C语言公共基础知识与考点汇总...