搭建如下图所示的硬件系统:
该硬件系统的数据流向为:
DDR–>AXI VDMA–>AXI DATA FIFO–>AXI VDMA–>DDR
即将一幅图像由一段地址空间搬运至另一段地址空间。
其中,AXI VDMA配置如下:
地址位宽32,缓存3帧,读写通道的配置如上图所示。
写通道Fsync Options选择s2mm tuser,读通道选择None,即保持默认。
AXI4-Stream DATA FIFO配置如下:
此外,为了更直观地感受整个数据传输过程,我们还在硬件系统中加入了ILA,以实时抓取波形观察。
vdma_api.c
/******************************************************************************
*
* Copyright (C) 2014 - 2018 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
*
******************************************************************************/
/*****************************************************************************/
/**** @file vdma_api.c** This file has high level API to configure and start the VDMA.The file assumes that:* The design has VDMA with both MM2S and S2MM path enable.* The API's has tested with hardware that has tow VDMA and MM2S to S2MM are back* to back connected for each VDMA.** MODIFICATION HISTORY:** Ver Who Date Changes* ----- ---- -------- -------------------------------------------------------* 4.0 adk 11/26/15 First release****************************************************************************/#define DEBUG_MODE 0/******************** Include files **********************************/
#include "xaxivdma.h"
#include "xparameters.h"
#include "xil_exception.h"/******************** Data structure Declarations *****************************/typedef struct vdma_handle
{/* The device ID of the VDMA */unsigned int device_id;/* The state variable to keep track if the initialization is done*/unsigned int init_done;/** The XAxiVdma driver instance data. */XAxiVdma* InstancePtr;/* The XAxiVdma_DmaSetup structure contains all the necessary information to* start a frame write or read. */XAxiVdma_DmaSetup ReadCfg;XAxiVdma_DmaSetup WriteCfg;/* Horizontal size of frame */unsigned int hsize;/* Vertical size of frame */unsigned int vsize;/* Buffer address from where read and write will be done by VDMA */unsigned int src_buffer_address;unsigned int dest_buffer_address;/* Flag to tell VDMA to interrupt on frame completion*/unsigned int enable_frm_cnt_intr;/* The counter to tell VDMA on how many frames the interrupt should happen*/unsigned int number_of_frame_count;
}vdma_handle;/******************** Constant Definitions **********************************//** Device related constants. These need to defined as per the HW system.*/
vdma_handle vdma_context[XPAR_XAXIVDMA_NUM_INSTANCES];
static unsigned int context_init=0;/******************* Function Prototypes ************************************/static int ReadSetup(vdma_handle *vdma_context);
static int WriteSetup(vdma_handle *vdma_context);
static int StartTransfer(XAxiVdma *InstancePtr);/*****************************************************************************/
/**
*
* run_triple_frame_buffer API
*
* This API is the interface between application and other API. When application will call
* this API with right argument, This API will call rest of the API to configure the read
* and write path of VDMA,based on ID. After that it will start both the read and write path
* of VDMA
*
* @param InstancePtr is the handle to XAxiVdma data structure.
* @param DeviceId is the device ID of current VDMA
* @param hsize is the horizontal size of the frame. It will be in Pixels.
* The actual size of frame will be calculated by multiplying this
* with tdata width.
* @param vsize is the Vertical size of the frame.
* @param buf_base_addr is the buffer address where frames will be written
* and read by VDMA.
* @param number_frame_count specifies after how many frames the interrupt
* should come.
* @param enable_frm_cnt_intr is for enabling frame count interrupt
* when set to 1.
* @return
* - XST_SUCCESS if example finishes successfully
* - XST_FAILURE if example fails.
*
******************************************************************************/
int run_triple_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize,int vsize, int src_buf_base_addr,int dest_buf_base_addr, int number_frame_count,int enable_frm_cnt_intr)
{int Status,i;XAxiVdma_Config *Config;XAxiVdma_FrameCounter FrameCfgPtr;/* This is one time initialization of state machine context.* In first call it will be done for all VDMA instances in the system.*/if(context_init==0) {for(i=0; i < XPAR_XAXIVDMA_NUM_INSTANCES; i++) {vdma_context[i].InstancePtr = NULL;vdma_context[i].device_id = -1;vdma_context[i].hsize = 0;vdma_context[i].vsize = 0;vdma_context[i].init_done = 0;vdma_context[i].src_buffer_address = 0;vdma_context[i].dest_buffer_address = 0;vdma_context[i].enable_frm_cnt_intr = 0;vdma_context[i].number_of_frame_count = 0;}context_init = 1;}/* The below initialization will happen for each VDMA. The API argument* will be stored in internal data structure*//* The information of the XAxiVdma_Config comes from hardware build.* The user IP should pass this information to the AXI DMA core.*/Config = XAxiVdma_LookupConfig(DeviceId);if (!Config) {xil_printf("No video DMA found for ID %d\r\n",DeviceId );return XST_FAILURE;}if(vdma_context[DeviceId].init_done ==0) {vdma_context[DeviceId].InstancePtr = InstancePtr;/* Initialize DMA engine */Status = XAxiVdma_CfgInitialize(vdma_context[DeviceId].InstancePtr,Config, Config->BaseAddress);if (Status != XST_SUCCESS) {xil_printf("Configuration Initialization failed %d\r\n",Status);return XST_FAILURE;}vdma_context[DeviceId].init_done = 1;}vdma_context[DeviceId].device_id = DeviceId;vdma_context[DeviceId].vsize = vsize;vdma_context[DeviceId].src_buffer_address = src_buf_base_addr;vdma_context[DeviceId].dest_buffer_address = dest_buf_base_addr;vdma_context[DeviceId].enable_frm_cnt_intr = enable_frm_cnt_intr;vdma_context[DeviceId].number_of_frame_count = number_frame_count;vdma_context[DeviceId].hsize = hsize * (Config->Mm2SStreamWidth>>3);/* Setup the write channel */Status = WriteSetup(&vdma_context[DeviceId]);if (Status != XST_SUCCESS) {xil_printf("Write channel setup failed %d\r\n", Status);if(Status == XST_VDMA_MISMATCH_ERROR)xil_printf("DMA Mismatch Error\r\n");return XST_FAILURE;}/* Setup the read channel */Status = ReadSetup(&vdma_context[DeviceId]);if (Status != XST_SUCCESS) {xil_printf("Read channel setup failed %d\r\n", Status);if(Status == XST_VDMA_MISMATCH_ERROR)xil_printf("DMA Mismatch Error\r\n");return XST_FAILURE;}/* The frame counter interrupt is enabled, setting VDMA for same */if(vdma_context[DeviceId].enable_frm_cnt_intr) {FrameCfgPtr.ReadDelayTimerCount = 1;FrameCfgPtr.ReadFrameCount = number_frame_count;FrameCfgPtr.WriteDelayTimerCount = 1;FrameCfgPtr.WriteFrameCount = number_frame_count;XAxiVdma_SetFrameCounter(vdma_context[DeviceId].InstancePtr,&FrameCfgPtr);/* Enable DMA read and write channel interrupts. The configuration for interrupt* controller will be done by application */XAxiVdma_IntrEnable(vdma_context[DeviceId].InstancePtr,XAXIVDMA_IXR_ERROR_MASK |XAXIVDMA_IXR_FRMCNT_MASK,XAXIVDMA_WRITE);XAxiVdma_IntrEnable(vdma_context[DeviceId].InstancePtr,XAXIVDMA_IXR_ERROR_MASK |XAXIVDMA_IXR_FRMCNT_MASK,XAXIVDMA_READ);} else {/* Enable DMA read and write channel interrupts. The configuration for interrupt* controller will be done by application */XAxiVdma_IntrEnable(vdma_context[DeviceId].InstancePtr,XAXIVDMA_IXR_ERROR_MASK,XAXIVDMA_WRITE);XAxiVdma_IntrEnable(vdma_context[DeviceId].InstancePtr,XAXIVDMA_IXR_ERROR_MASK ,XAXIVDMA_READ);}/* Start the DMA engine to transfer */Status = StartTransfer(vdma_context[DeviceId].InstancePtr);if (Status != XST_SUCCESS) {if(Status == XST_VDMA_MISMATCH_ERROR)xil_printf("DMA Mismatch Error\r\n");return XST_FAILURE;}
#if DEBUG_MODExil_printf("Code is in Debug mode, Make sure that buffer addresses are at valid memory \r\n");xil_printf("In triple mode, there has to be six consecutive buffers for Debug mode \r\n");{u32 pixels,j,Addr = vdma_context[DeviceId].buffer_address;u8 *dst,*src;u32 total_pixel = vdma_context[DeviceId].stride * vdma_context[DeviceId].vsize;src = (unsigned char *)Addr;dst = (unsigned char *)Addr + (total_pixel * vdma_context->InstancePtr->MaxNumFrames);for(j=0;jInstancePtr->MaxNumFrames;j++) {for(pixels=0;pixelsif(src[pixels] != dst[pixels]) {xil_printf("VDMA transfer failed: SRC=0x%x, DST=0x%x\r\n",src[pixels],dst[pixels]);exit(-1);}}src = src + total_pixel;dst = dst + total_pixel;}}xil_printf("VDMA transfer is happening and checked for 3 frames \r\n");
#endifreturn XST_SUCCESS;
}/*****************************************************************************/
/**
*
* This function sets up the read channel
*
* @param vdma_context is the context pointer to the VDMA engine.
*
* @return XST_SUCCESS if the setup is successful, XST_FAILURE otherwise.
*
* @note None.
*
******************************************************************************/
static int ReadSetup(vdma_handle *vdma_context)
{int Index;u32 Addr;int Status;vdma_context->ReadCfg.VertSizeInput = vdma_context->vsize;vdma_context->ReadCfg.HoriSizeInput = vdma_context->hsize;vdma_context->ReadCfg.Stride = vdma_context->hsize;vdma_context->ReadCfg.FrameDelay = 0; /* This example does not test frame delay */vdma_context->ReadCfg.EnableCircularBuf = 1;vdma_context->ReadCfg.EnableSync = 1; /* Gen-Lock */vdma_context->ReadCfg.PointNum = 0;vdma_context->ReadCfg.EnableFrameCounter = 0; /* Endless transfers */vdma_context->ReadCfg.FixedFrameStoreAddr = 0; /* We are not doing parking *//* Configure the VDMA is per fixed configuration, This configuration is being used by majority* of customer. Expert users can play around with this if they have different configurations */Status = XAxiVdma_DmaConfig(vdma_context->InstancePtr, XAXIVDMA_READ, &vdma_context->ReadCfg);if (Status != XST_SUCCESS) {xil_printf("Read channel config failed %d\r\n", Status);return XST_FAILURE;}/* Initialize buffer addresses** These addresses are physical addresses*/Addr = vdma_context->src_buffer_address;for(Index = 0; Index < vdma_context->InstancePtr->MaxNumFrames; Index++) {vdma_context->ReadCfg.FrameStoreStartAddr[Index] = Addr;/* Initializing the buffer in case of Debug mode */#if DEBUG_MODE{u32 i;u8 *src;u32 total_pixel = vdma_context->stride * vdma_context->vsize;src = (unsigned char *)Addr;xil_printf("Read Buffer %d address: 0x%x \r\n",Index,Addr);for(i=0;isrc[i] = i & 0xFF;}}
#endifAddr += vdma_context->hsize * vdma_context->vsize;}/* Set the buffer addresses for transfer in the DMA engine* The buffer addresses are physical addresses*/Status = XAxiVdma_DmaSetBufferAddr(vdma_context->InstancePtr, XAXIVDMA_READ,vdma_context->ReadCfg.FrameStoreStartAddr);if (Status != XST_SUCCESS) {xil_printf("Read channel set buffer address failed %d\r\n", Status);return XST_FAILURE;}return XST_SUCCESS;
}/*****************************************************************************/
/**
*
* This function sets up the write channel
*
* @param dma_context is the context pointer to the VDMA engine..
*
* @return XST_SUCCESS if the setup is successful, XST_FAILURE otherwise.
*
* @note None.
*
******************************************************************************/
static int WriteSetup(vdma_handle *vdma_context)
{int Index;u32 Addr;int Status;vdma_context->WriteCfg.VertSizeInput = vdma_context->vsize;vdma_context->WriteCfg.HoriSizeInput = vdma_context->hsize;vdma_context->WriteCfg.Stride = vdma_context->hsize;vdma_context->WriteCfg.FrameDelay = 0; /* This example does not test frame delay */vdma_context->WriteCfg.EnableCircularBuf = 1;vdma_context->WriteCfg.EnableSync = 1; /* Gen-Lock */vdma_context->WriteCfg.PointNum = 0;vdma_context->WriteCfg.EnableFrameCounter = 0; /* Endless transfers */vdma_context->WriteCfg.FixedFrameStoreAddr = 0; /* We are not doing parking *//* Configure the VDMA is per fixed configuration, This configuration* is being used by majority of customers. Expert users can play around* with this if they have different configurations*/Status = XAxiVdma_DmaConfig(vdma_context->InstancePtr, XAXIVDMA_WRITE, &vdma_context->WriteCfg);if (Status != XST_SUCCESS) {xil_printf("Write channel config failed %d\r\n", Status);return Status;}/* Initialize buffer addresses** Use physical addresses*/Addr = vdma_context->dest_buffer_address;/* If Debug mode is enabled write frame is shifted 3 Frames* store ahead to compare read and write frames*/
#if DEBUG_MODEAddr = Addr + vdma_context->InstancePtr->MaxNumFrames * \(vdma_context->stride * vdma_context->vsize);
#endiffor(Index = 0; Index < vdma_context->InstancePtr->MaxNumFrames; Index++) {vdma_context->WriteCfg.FrameStoreStartAddr[Index] = Addr;
#if DEBUG_MODExil_printf("Write Buffer %d address: 0x%x \r\n",Index,Addr);
#endifAddr += (vdma_context->hsize * vdma_context->vsize);}/* Set the buffer addresses for transfer in the DMA engine */Status = XAxiVdma_DmaSetBufferAddr(vdma_context->InstancePtr,XAXIVDMA_WRITE,vdma_context->WriteCfg.FrameStoreStartAddr);if (Status != XST_SUCCESS) {xil_printf("Write channel set buffer address failed %d\r\n",Status);return XST_FAILURE;}/* Clear data buffer*/
#if DEBUG_MODEmemset((void *)vdma_context->buffer_address, 0,vdma_context->ReadCfg.Stride * vdma_context->ReadCfg.VertSizeInput * vdma_context->InstancePtr->MaxNumFrames);
#endifreturn XST_SUCCESS;
}/*****************************************************************************/
/**
*
* This function starts the DMA transfers. Since the DMA engine is operating
* in circular buffer mode, video frames will be transferred continuously.
*
* @param InstancePtr points to the DMA engine instance
*
* @return
* - XST_SUCCESS if both read and write start successfully
* - XST_FAILURE if one or both directions cannot be started
*
* @note None.
*
******************************************************************************/
static int StartTransfer(XAxiVdma *InstancePtr)
{int Status;/* Start the write channel of VDMA */Status = XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_WRITE);if (Status != XST_SUCCESS) {xil_printf("Start Write transfer failed %d\r\n", Status);return XST_FAILURE;}/* Start the Read channel of VDMA */Status = XAxiVdma_DmaStart(InstancePtr, XAXIVDMA_READ);if (Status != XST_SUCCESS) {xil_printf("Start read transfer failed %d\r\n", Status);return XST_FAILURE;}return XST_SUCCESS;
}
vdma.c
/******************************************************************************
*
* Copyright (C) 2014 - 2018 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*
*
******************************************************************************/
/*****************************************************************************/
/**** @file vdma.c** This file comprises sample application to usage of VDMA APi's in vdma_api.c.* .** MODIFICATION HISTORY:** Ver Who Date Changes* ----- ---- -------- -------------------------------------------------------* 4.0 adk 11/26/15 First release* 4.1 adk 01/07/16 Updated DDR base address for Ultrascale (CR 799532) and* removed the defines for S6/V6.* ms 04/05/17 Modified Comment lines in functions to* recognize it as documentation block for doxygen* generation of examples.****************************************************************************//*** Include file ***/
#include "xparameters.h"
#include "xstatus.h"
#include "xil_exception.h"
#include "xil_assert.h"
#include "xaxivdma.h"
#include "xaxivdma_i.h"
#include "xil_io.h"
#include
#include "xil_cache.h"#ifdef XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#define MEMORY_BASE XPAR_AXI_7SDDR_0_S_AXI_BASEADDR
#elif XPAR_MIG7SERIES_0_BASEADDR
#define MEMORY_BASE XPAR_MIG7SERIES_0_BASEADDR
#elif XPAR_MIG_0_BASEADDR
#define MEMORY_BASE XPAR_MIG_0_BASEADDR
#elif XPAR_PSU_DDR_0_S_AXI_BASEADDR
#define MEMORY_BASE XPAR_PSU_DDR_0_S_AXI_BASEADDR
#else
#warning CHECK FOR THE VALID DDR ADDRESS IN XPARAMETERS.H, \DEFAULT SET TO 0x01000000
#define MEMORY_BASE 0x01000000
#endif/*** Global Variables ***/
unsigned int srcBuffer = (MEMORY_BASE + 0x1000000);
unsigned int destBuffer = (MEMORY_BASE + 0x2000000);int run_triple_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize,int vsize, int src_buf_base_addr, int dest_buf_base_addr, int number_frame_count,int enable_frm_cnt_intr);/*****************************************************************************/
/**
* Main function
*
* This is main entry point to demonstrate this example.
*
* @return None
*
******************************************************************************///void vdma_test(){
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x4); //reset S2MM VDMA Control Register
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x8); //genlock
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC, 0x08000000);//S2MM Start Addresses
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC+4, 0x0A000000);
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC+8, 0x09000000);
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA4, 16*3);//S2MM Horizontal Size
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA8, 0x01002000);//S2MM Frame Delay and Stride
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x3);//S2MM VDMA Control Register
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA0, 16);//S2MM Vertical Size start an S2MM transfer
//
// //AXI VDMA1
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x4); //reset MM2S VDMA Control Register
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x8); //gen-lock
//
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C, 0x08000000); //MM2S Start Addresses
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C+4, 0x0A000000);
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C+8, 0x09000000);
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x54, 16*3);//MM2S HSIZE Register
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x58, 0x01002000);//S2MM FRMDELAY_STRIDE Register 1920*3=5760 ����֮��Ϊ8192=0x2000
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x0, 0x03);//MM2S VDMA Control Register
// Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x50, 16);//MM2S_VSIZE ��������
//
//}int main(){int Status;Xil_DCacheDisable();XAxiVdma InstancePtr;//printf("\n--- Entering main() --- \r\n");printf("Starting the VDMA \n\r");for(int i=0;i<16*16*3/4;i++){Xil_Out32(srcBuffer+i*4,(u32)i);Xil_Out32(destBuffer+i*4,(u32)0);u32 x=Xil_In32(destBuffer+i*4);printf("%d\n",x);}Status = run_triple_frame_buffer(&InstancePtr, 0, 16, 16,srcBuffer, destBuffer, 1, 0);//if(Status != XST_SUCCESS) {xil_printf("Transfer of frames failed with error = %d\r\n",Status);return XST_FAILURE;}else {xil_printf("Transfer of frames started \r\n");}for(int i=0;i<16*16*3/4;i++){u32 x=Xil_In32(destBuffer+i*4);printf("%d\n",x);}
}
其中,函数
int run_triple_frame_buffer(XAxiVdma* InstancePtr, int DeviceId, int hsize,int vsize, int src_buf_base_addr, int dest_buf_base_addr, int number_frame_count,int enable_frm_cnt_intr);
用于启动vdma传输,这里,hsize是图像的宽度,vsize是图像的高度,src_buf_base_addr和dest_buf_base_addr分别是读通道起始地址和写通道起始地址。
将SDK程序烧写至开发板,串口打印如下:
可以看到,VDMA成功将srcBuffer处的数据搬运至了destBuffer,表明我们搭建的回环测试硬件平台无误。
实时抓取的波形如下
可以看到,图像的第一个数据,tuser信号为高
上图中,共有16行数据,每一行的最后一个数据在发送时,tlast信号也需要拉高。
上述两点也是AXI VDMA和AXI DMA的区别。