ZYNQ的PS硬核DMA(PL330)使用(PS内部数据搬移)

前言:

在学习ZYNQ的时候,我就很好奇,传统的单片机都会存在一个叫DMA的东西。在实际的工程项目中,常常会选用DMA来做大量数据的搬移,比如:把从串口发送的数据,搬移到一个暂存的Buffer中

而ZYNQ作为FPGA的分支,搭载了片上的ARM核,按理来说应该也会具有片内DMA这一功能,而我在查阅资料的时候,发现网络上的文章大多是介绍AXI-DMA这一软核。虽然这两个DMA功能相近,但是两者的应用场景却是天差地别

AXI-DMA软核常常用于PL->PS的数据传输,也可以用作PS->PL的大量数据搬移

PS DMA则常常用于PS内部的数据的搬移。这个搬移可以是,从DDR3->DDR3,也可以是从DDR3->PL.......

举个例子:

上图是一个AXI BRAM Controller模块Block Memory模块的互联,AXI BRAM Controller模块在ZYNQ内地址映射为:

在ZYNQ中我们可以通过对0x4000_0000~0x4000_0FFF的地址来对BRAM进行访问,而AXI BRAM Controller模块主要实现一个地址对应和时序控制的功能

在SDK的主函数中,我们经常会使用for循环来进行地址的访问,如下图:

而使用PS的DMA,我们可以更快捷的来实现对地址的访问和编写

本文不会讲解原理,而会从实际的应用出发,简单说下这个硬核如何使用

1.PL设计

PL设计相对简单,如下图:

2.PS设计

#include <stdio.h>
#include "xscugic.h"
#include "xdmaps.h"
#include "xil_exception.h"
#include "xil_types.h"
#include "xil_printf.h"
#include "sleep.h"

#define DMA_DEVICE_ID 			XPAR_XDMAPS_1_DEVICE_ID
#define INTC_DEVICE_ID			XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DMA_FAULT_INTR			XPAR_XDMAPS_0_FAULT_INTR
#define DMA_DONE_INTR_0			XPAR_XDMAPS_0_DONE_INTR_0
#define DMA_LENGTH	1024


void XDma_Config(u16 DeviceId);
void SetupInterrupt(XScuGic *GicPtr, XDmaPs *DmaPtr);

void DmaISR(XDmaPs *InstPtr);

u32 Src[DMA_LENGTH];
u32 Dstaddr = 0x02000000;
unsigned int DMA_Channel = 0;
u32	Value;
int i;

XDmaPs DmaInstance;
XScuGic GicInstance;

int main()
{
	printf("DMA搬移之前的数据:\r\n");
	for(i = 0;i < DMA_LENGTH;i++)
	{
		Value = Xil_In32(Dstaddr + i*4);
		printf("%d \r\n",(int)Value);
	}
	printf("------------\r\n");

 	XDma_Config(DMA_DEVICE_ID);

 	printf("DMA搬移之后的数据:\r\n");
	for(i = 0;i < DMA_LENGTH;i++)
	{
		Value = Xil_In32(Dstaddr + i*4);
		printf("%d \r\n",(int)Value);
	}
	printf("\r\n------------\r\n");

 	while(1)
 	{

 	}
    return 0;

}

void XDma_Config(u16 DeviceId)
{
		int Index;
		XDmaPs_Config *DmaCfg;
		XDmaPs *DmaInst = &DmaInstance;
		XDmaPs_Cmd DmaCmd;

		memset(&DmaCmd, 0, sizeof(XDmaPs_Cmd));

		//器件初始化
		DmaCmd.ChanCtrl.SrcBurstSize = 4;
		DmaCmd.ChanCtrl.SrcBurstLen = 4;
		DmaCmd.ChanCtrl.SrcInc = 1;
		DmaCmd.ChanCtrl.DstBurstSize = 4;
		DmaCmd.ChanCtrl.DstBurstLen = 4;
		DmaCmd.ChanCtrl.DstInc = 1;
		DmaCmd.BD.SrcAddr = (u32)&Src[0];
		DmaCmd.BD.DstAddr = (u32)Dstaddr;
		DmaCmd.BD.Length = DMA_LENGTH * sizeof(u32);

		DmaCfg = XDmaPs_LookupConfig(DeviceId);
		XDmaPs_CfgInitialize(DmaInst,DmaCfg,DmaCfg->BaseAddress);
		//中断配置
		SetupInterrupt(&GicInstance, DmaInst);

		/* Initialize source */
		for (Index = 0; Index < DMA_LENGTH; Index++){
			Src[Index] = DMA_LENGTH - Index;
		}

		XDmaPs_Start(DmaInst, DMA_Channel, &DmaCmd, 0);
		XDmaPs_Print_DmaProg(&DmaCmd);
}

void SetupInterrupt(XScuGic *GicPtr, XDmaPs *DmaPtr)
{
	//int Status;
	XScuGic_Config *GicConfig;


	Xil_ExceptionInit();

	GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);

	XScuGic_CfgInitialize(GicPtr, GicConfig,GicConfig->CpuBaseAddress);

	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
			     (Xil_ExceptionHandler)XScuGic_InterruptHandler,
			     GicPtr);

	XScuGic_Connect(GicPtr,DMA_FAULT_INTR,(Xil_InterruptHandler)XDmaPs_FaultISR,(void *)DmaPtr);


	XScuGic_Connect(GicPtr,DMA_DONE_INTR_0,(Xil_InterruptHandler)XDmaPs_DoneISR_0,(void *)DmaPtr);

	XScuGic_Enable(GicPtr, DMA_DONE_INTR_0);

	Xil_ExceptionEnable();
}

代码并不难懂,基本上是按照官方例程来进行修改的,与官方例程相对比,值得注意的点有以下几个:

1.地址的选择

在本文章中,我实现的功能是将数据从DDR3搬移到DDR3中。数组src的地址是在DDR3中,可以在main函数中用printf打印输出。

而根据UG585的手册,我们知道DDR3在ZYNQ中地址的映射为:

很显然,数组src的数据是储存在DDR中的,而我们在进行源地址的选择时,DstAddr的值也必须在0010_0000~3FFF_FFFF(dma搬运的目的地是在DDR中,若是搬移到外设中,也必须确定外设对应的映射地址)

2.dma的初始化

在整段代码中,最为核心的一段,就是以下这一段:

		DmaCmd.ChanCtrl.SrcBurstSize = 4;
		DmaCmd.ChanCtrl.SrcBurstLen = 8;
		DmaCmd.ChanCtrl.SrcInc = 1;
		DmaCmd.ChanCtrl.DstBurstSize = 4;
		DmaCmd.ChanCtrl.DstBurstLen = 8;
		DmaCmd.ChanCtrl.DstInc = 1;
		DmaCmd.BD.SrcAddr = (u32)&Src[0];
		DmaCmd.BD.DstAddr = (u32)Dstaddr;
		DmaCmd.BD.Length = DMA_LENGTH * sizeof(u32);

其中burstsize和burstlen的含义与AXI-FuLL协议中的Transaction和Transfer类似。可以简单理解为单次传送的字节数在一次传输中包含几次传送

3.dma的中断

在官方的例程中,实际上存在三种中断的处理函数,分别是:

1)Xil_ExceptionRegisterHandler:注册自定义的异常处理程序

2)XScuGic_Connect:中断关联函数,在中断控制器层面(GIC)实现中断处理函数与中断序列号的关联

在官方的例程中,将XPAR_XDMAPS_0_FAULT_INTRXDmaPs_FaultISR、XPAR_XDMAPS_0_DONE_INTR_0XDmaPs_DoneISR_0相互关联

这两个相关联的函数都是提前在xdmaps.c 中提前定义好的,其主要功能为:清除中断标志位更新DMA的缓存状态......

3)使用函数XDmaPs_SetDoneHandler关联用户自己编写的处理函数,可以在自己编写的处理函数中实现发送数据和目的数据的比对,来判断dma操作是否正确,如下:

在本文中并未使用这一种中断

3.实验现象

———————————————————————————————————————————

原创不易,感谢点赞收藏😋😋

有不同看法欢迎评论区或私信讨论🥰🥰

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值