上篇該系列博文中講述W5500接收到上位機傳輸的數據,此后需要將數據緩存起來。當數據量較大或者其他數據帶寬較高的情況下,片上緩存(OCM)已無法滿足需求,這時需要將大量數據保存在外掛的DDR SDRAM中。
最簡單的方式是使用Xilinx的讀寫地址庫函數Xil_In32()和Xil_Out32(),當然不僅支持32bit位寬,還包括8 16和64bit。但這種方式每次讀寫都要占用CPU,無法在讀寫的同時接收后續數據或者對之前的數據進一步處理,也就無法形成類似FPGA邏輯設計中的“流水線結構”,此時前段數據緩存過程中,后段數據會被丟棄。所以,需要利用PS端CPU子系統內的專用硬件DMA完成高速的批量數據搬移工作。
在Xilinx SDK的system.mss頁面下直接導入ps_dma示例工程。
#include
#include
#include "sleep.h"
#include "xparameters.h"
#include "xil_types.h"
#include "xil_assert.h"
#include "xil_io.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "xdmaps.h"
/************************** Constant Definitions *****************************/
/*
* The following constants map to the XPAR parameters created in the
* xparameters.h file. They are defined here such that a user can easily
* change all the needed parameters in one place.
*/
#define DMA_DEVICE_ID XPAR_XDMAPS_1_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DMA_DONE_INTR_0 XPAR_XDMAPS_0_DONE_INTR_0
#define DMA_DONE_INTR_1 XPAR_XDMAPS_0_DONE_INTR_1
#define DMA_DONE_INTR_2 XPAR_XDMAPS_0_DONE_INTR_2
#define DMA_DONE_INTR_3 XPAR_XDMAPS_0_DONE_INTR_3
#define DMA_DONE_INTR_4 XPAR_XDMAPS_0_DONE_INTR_4
#define DMA_DONE_INTR_5 XPAR_XDMAPS_0_DONE_INTR_5
#define DMA_DONE_INTR_6 XPAR_XDMAPS_0_DONE_INTR_6
#define DMA_DONE_INTR_7 XPAR_XDMAPS_0_DONE_INTR_7
#define DMA_FAULT_INTR XPAR_XDMAPS_0_FAULT_INTR
#define TEST_ROUNDS 1 /* Number of loops that the Dma transfers run.*/
#define DMA_LENGTH 1024 /* Length of the Dma Transfers */
#define TIMEOUT_LIMIT 0x2000 /* Loop count for timeout */
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
int XDmaPs_Example_W_Intr(XScuGic *GicPtr, u16 DeviceId);
int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr);
void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd,
void *CallbackRef);
/************************** Macro Definitions *****************************/
/************************** Variable Definitions *****************************/
#ifdef __ICCARM__
#pragma data_alignment=32
static int Src[DMA_LENGTH];
static int Dst[DMA_LENGTH];
#pragma data_alignment=4
#else
static int Src[DMA_LENGTH] __attribute__ ((aligned (32)));
static int Dst[DMA_LENGTH] __attribute__ ((aligned (32)));
#endif
XDmaPs DmaInstance;
#ifndef TESTAPP_GEN
XScuGic GicInstance;
#endif
/****************************************************************************/
/**
*
* This is the main function for the DmaPs interrupt example.
*
* @param None.
*
* @return XST_SUCCESS to indicate success, otherwise XST_FAILURE.
*
* @note None.
*
****************************************************************************/
#ifndef TESTAPP_GEN
int main(void)
{
int Status;
Status = XDmaPs_Example_W_Intr(&GicInstance,DMA_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("Error: XDMaPs_Example_W_Intr failed/r/n");
return XST_FAILURE;
}
xil_printf("XDMaPs_Example_W_Intr passed/r/n");
return XST_SUCCESS;
}
#endif
/*****************************************************************************/
/**
*
* Interrupt Example to test the DMA.
*
* @param DeviceId is the Device ID of the DMA controller.
*
* @return XST_SUCCESS to indicate success, otherwise XST_FAILURE.
*
* @note None.
*
****************************************************************************/
int XDmaPs_Example_W_Intr(XScuGic *GicPtr, u16 DeviceId)
{
int Index;
unsigned int Channel = 0;
int Status;
int TestStatus;
int TestRound;
int TimeOutCnt;
volatile int Checked[XDMAPS_CHANNELS_PER_DEV];
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;
DmaCmd.BD.DstAddr = (u32) Dst;
DmaCmd.BD.Length = DMA_LENGTH * sizeof(int);
/*
* Initialize the DMA Driver
*/
DmaCfg = XDmaPs_LookupConfig(DeviceId);
if (DmaCfg == NULL) {
return XST_FAILURE;
}
Status = XDmaPs_CfgInitialize(DmaInst,
DmaCfg,
DmaCfg->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Setup the interrupt system.
*/
Status = SetupInterruptSystem(GicPtr, DmaInst);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
TestStatus = XST_SUCCESS;
for (TestRound = 0; TestRound xil_printf("Test round %d/r/n", TestRound);
for (Channel = 0;
Channel Channel++) {
/* Initialize source */
for (Index = 0; Index Src[Index] = DMA_LENGTH - Index;
/* Clear destination */
for (Index = 0; Index Dst[Index] = 0;
Checked[Channel] = 0;
/* Set the Done interrupt handler */
XDmaPs_SetDoneHandler(DmaInst,
Channel,
DmaDoneHandler,
(void *)Checked);
Status = XDmaPs_Start(DmaInst, Channel, &DmaCmd, 0);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
TimeOutCnt = 0;
/* Now the DMA is done */
while (!Checked[Channel]
&& TimeOutCnt TimeOutCnt++;
}
if (TimeOutCnt >= TIMEOUT_LIMIT) {
TestStatus = XST_FAILURE;
}
if (Checked[Channel] /* DMA controller failed */
TestStatus = XST_FAILURE;
}
}
}
return TestStatus;
}
/******************************************************************************/
/**
*
* This function connects the interrupt handler of the interrupt controller to
* the processor. This function is seperate to allow it to be customized for
* each application. Each processor or RTOS may require unique processing to
* connect the interrupt handler.
*
* @param GicPtr is the GIC instance pointer.
* @param DmaPtr is the DMA instance pointer.
*
* @return None.
*
* @note None.
*
****************************************************************************/
int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr)
{
int Status;
#ifndef TESTAPP_GEN
XScuGic_Config *GicConfig;
Xil_ExceptionInit();
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == GicConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(GicPtr, GicConfig,
GicConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicPtr);
#endif
/*
* Connect the device driver handlers that will be called when an interrupt
* for the device occurs, the device driver handler performs the specific
* interrupt processing for the device
*/
/*
* Connect the Fault ISR
*/
Status = XScuGic_Connect(GicPtr,
DMA_FAULT_INTR,
(Xil_InterruptHandler)XDmaPs_FaultISR,
(void *)DmaPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the Done ISR for all 8 channels of DMA 0
*/
Status = XScuGic_Connect(GicPtr,
DMA_DONE_INTR_0,
(Xil_InterruptHandler)XDmaPs_DoneISR_0,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_1,
(Xil_InterruptHandler)XDmaPs_DoneISR_1,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_2,
(Xil_InterruptHandler)XDmaPs_DoneISR_2,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_3,
(Xil_InterruptHandler)XDmaPs_DoneISR_3,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_4,
(Xil_InterruptHandler)XDmaPs_DoneISR_4,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_5,
(Xil_InterruptHandler)XDmaPs_DoneISR_5,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_6,
(Xil_InterruptHandler)XDmaPs_DoneISR_6,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_7,
(Xil_InterruptHandler)XDmaPs_DoneISR_7,
(void *)DmaPtr);
if (Status != XST_SUCCESS)
return XST_FAILURE;
/*
* Enable the interrupts for the device
*/
XScuGic_Enable(GicPtr, DMA_DONE_INTR_0);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_1);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_2);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_3);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_4);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_5);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_6);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_7);
XScuGic_Enable(GicPtr, DMA_FAULT_INTR);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* DmaDoneHandler.
*
* @param Channel is the Channel number.
* @param DmaCmd is the Dma Command.
* @param CallbackRef is the callback reference data.
*
* @return None.
*
* @note None.
*
******************************************************************************/
void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef)
{
/* done handler */
volatile int *Checked = (volatile int *)CallbackRef;
int Index;
int Status = 1;
int *Src;
int *Dst;
Src = (int *)DmaCmd->BD.SrcAddr;
Dst = (int *)DmaCmd->BD.DstAddr;
/* DMA successful */
/* compare the src and dst buffer */
for (Index = 0; Index if ((Src[Index] != Dst[Index]) ||
(Dst[Index] != DMA_LENGTH - Index)) {
Status = -XST_FAILURE;
}
}
Checked[Channel] = Status;
}
ps_dma_demo
其實demo中做的操作非常簡單,僅僅是定義了兩個數組Src和Dst,之后利用PS_DMA將Src中數據搬移到Dst中,搬移完成后進入中斷函數比較兩部分地址數據是否一致。Xilinx的SDK軟件代碼有固定的套路,“上有政策,下有對策”,我們可以將其封裝成固定格式的一個個子函數,方便今后調用。這里把整個工程分為:系統中斷,PS_DMA專有中斷以及主函數三個部分。
#include "xscugic.h"
#include "sys_intr.h"
int sys_IntrInit(XScuGic *GicPtr)
{
XScuGic_Config *GicConfig;
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
int Status;
GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == GicConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(GicPtr, GicConfig,
GicConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
void setupIntrException(XScuGic *GicPtr)
{
Xil_ExceptionInit();
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicPtr);
Xil_ExceptionEnable();
}
sys_intr.c
#ifndef SRC_SYS_INTR_H_
#define SRC_SYS_INTR_H_
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
int sys_IntrInit(XScuGic *GicPtr);
void setupIntrException(XScuGic *GicPtr);
#endif /* SRC_SYS_INTR_H_ */
sys_intr.h
#include "xil_types.h"
#include "xdmaps.h"
#include "xscugic.h"
#include "psdma_intr.h"
int PS_DMA_IntrInit(XDmaPs *DmaInst,u16 DeviceId)
{
/*
* Initialize the DMA Driver
*/
int Status;
XDmaPs_Config *DmaCfg = NULL;
DmaCfg = XDmaPs_LookupConfig(DeviceId);
if (DmaCfg == NULL) {
return XST_FAILURE;
}
Status = XDmaPs_CfgInitialize(DmaInst,
DmaCfg,
DmaCfg->BaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
return XST_SUCCESS;
}
int PS_DMA_SetupIntr(XScuGic *GicPtr,XDmaPs *DmaPtr,unsigned Channel)
{
int Status;
/*
* Connect the device driver handlers that will be called when an interrupt
* for the device occurs, the device driver handler performs the specific
* interrupt processing for the device
*/
/*
* Connect the Fault ISR
*/
Status = XScuGic_Connect(GicPtr,
DMA_FAULT_INTR,
(Xil_InterruptHandler)XDmaPs_FaultISR,
(void *)DmaPtr);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the Done ISR for all 8 channels of DMA 0
*/
Status = XScuGic_Connect(GicPtr,
DMA_DONE_INTR_0,
(Xil_InterruptHandler)XDmaPs_DoneISR_0,
(void *)DmaPtr);
/*Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_1,
(Xil_InterruptHandler)XDmaPs_DoneISR_1,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_2,
(Xil_InterruptHandler)XDmaPs_DoneISR_2,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_3,
(Xil_InterruptHandler)XDmaPs_DoneISR_3,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_4,
(Xil_InterruptHandler)XDmaPs_DoneISR_4,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_5,
(Xil_InterruptHandler)XDmaPs_DoneISR_5,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_6,
(Xil_InterruptHandler)XDmaPs_DoneISR_6,
(void *)DmaPtr);
Status |= XScuGic_Connect(GicPtr,
DMA_DONE_INTR_7,
(Xil_InterruptHandler)XDmaPs_DoneISR_7,
(void *)DmaPtr);*/
if (Status != XST_SUCCESS)
return XST_FAILURE;
/* Set the Done interrupt handler */
XDmaPs_SetDoneHandler(DmaPtr,
Channel,//Channel
DmaDoneHandler,//真正的中斷函數
(void *)Checked);
/*
* Enable the interrupts for the device
*/
XScuGic_Enable(GicPtr, DMA_DONE_INTR_0);
/*
XScuGic_Enable(GicPtr, DMA_DONE_INTR_1);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_2);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_3);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_4);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_5);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_6);
XScuGic_Enable(GicPtr, DMA_DONE_INTR_7);*/
XScuGic_Enable(GicPtr, DMA_FAULT_INTR);
return XST_SUCCESS;
}
void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef)
{
/* done handler */
volatile int *Checked = (volatile int *)CallbackRef;
//int Index;
int Status = 1;
xil_printf("Enter into the interrupt/n");
Checked[Channel] = Status;
}
void PS_DMA_InitPara(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;
DmaCmd->BD.DstAddr = (u32) DDR_BASEADDR;//Dst
DmaCmd->BD.Length = DMA_LENGTH * sizeof(int);
}
psdma_intr.c
#ifndef SRC_PSDMA_INTR_H_
#define SRC_PSDMA_INTR_H_
#define DMA_DONE_INTR_0 XPAR_XDMAPS_0_DONE_INTR_0
#define DMA_DONE_INTR_1 XPAR_XDMAPS_0_DONE_INTR_1
#define DMA_DONE_INTR_2 XPAR_XDMAPS_0_DONE_INTR_2
#define DMA_DONE_INTR_3 XPAR_XDMAPS_0_DONE_INTR_3
#define DMA_DONE_INTR_4 XPAR_XDMAPS_0_DONE_INTR_4
#define DMA_DONE_INTR_5 XPAR_XDMAPS_0_DONE_INTR_5
#define DMA_DONE_INTR_6 XPAR_XDMAPS_0_DONE_INTR_6
#define DMA_DONE_INTR_7 XPAR_XDMAPS_0_DONE_INTR_7
#define DMA_FAULT_INTR XPAR_XDMAPS_0_FAULT_INTR
#define DDR_BASEADDR 0x00600000//XPAR_PS7_DDR_0_S_AXI_BASEADDR 0x00100000
#define DMA_LENGTH 1024 /* Length of the Dma Transfers */
int Src[DMA_LENGTH] __attribute__ ((aligned (32)));
volatile int Checked[XDMAPS_CHANNELS_PER_DEV];
int PS_DMA_IntrInit(XDmaPs *DmaInst,u16 DeviceId);
int PS_DMA_SetupIntr(XScuGic *GicPtr,XDmaPs *DmaPtr,unsigned Channel);
void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd, void *CallbackRef);
void PS_DMA_InitPara(XDmaPs_Cmd* DmaCmd);
#endif /* SRC_PSDMA_INTR_H_ */
psdma_intr.h
#include
#include
#include "sleep.h"
#include "xparameters.h"
#include "xil_types.h"
#include "xil_assert.h"
#include "xil_io.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "xil_printf.h"
#include "xscugic.h"
#include "xdmaps.h"
#include "sys_intr.h"
#include "psdma_intr.h"
#define DMA_DEVICE_ID XPAR_XDMAPS_1_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define TEST_ROUNDS 1 /* Number of loops that the Dma transfers run.*/
#define TIMEOUT_LIMIT 0x2000 /* Loop count for timeout */
static XScuGic GicInstance;
static XDmaPs DmaInstance;
static XDmaPs_Cmd DmaCmd;
unsigned int Channel = 0;
/************************** Function Prototypes ******************************/
int PS_DMA_WriteTest();
int SetupInterruptSystem(XScuGic *GicPtr, XDmaPs *DmaPtr);
void DmaDoneHandler(unsigned int Channel, XDmaPs_Cmd *DmaCmd,
void *CallbackRef);
int dataCheck(u32 baseAddr,u32 len);
int systemInit(XScuGic *GicPtr,u16 DeviceId);
int main(void)
{
int Status;
Status = systemInit(&GicInstance,DMA_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("System initialization is failed/r/n");
return XST_FAILURE;
}
Status = PS_DMA_WriteTest();
if (Status != XST_SUCCESS) {
xil_printf("Error: XDMaPs_Example_W_Intr failed/r/n");
return XST_FAILURE;
}
xil_printf("Checking data.../n");
Status = dataCheck(DDR_BASEADDR,DMA_LENGTH);
if(Status != XST_SUCCESS)
{
xil_printf("Error:check failed/n");
return XST_FAILURE;
}
xil_printf("Writing data to DDR using DMA test passed!/r/n");
return XST_SUCCESS;
}
int dataCheck(u32 baseAddr,u32 len)
{
u32 DDR_ReadData[1024];
int i;
for(i=0;i {
DDR_ReadData[i] = Xil_In32(baseAddr+i*4);
if(DDR_ReadData[i]!=Src[i])
return XST_FAILURE;
//else //將寫入DDR數據讀回 并打印
// xil_printf("data at %x is %d/n",baseAddr+i*4,DDR_ReadData[i]);
}
return XST_SUCCESS;
}
/*****************************************************************************/
/**
*
* Interrupt Example to test the DMA.
*
* @param DeviceId is the Device ID of the DMA controller.
*
* @return XST_SUCCESS to indicate success, otherwise XST_FAILURE.
*
* @note None.
*
****************************************************************************/
int PS_DMA_WriteTest()
{
int Index;
int Status;
int TestStatus;
int TestRound;
int TimeOutCnt;
TestStatus = XST_SUCCESS;
for (TestRound = 0; TestRound xil_printf("Test round %d/r/n", TestRound);
for (Channel = 0;Channel {
/* Initialize source */
for (Index = 0; Index Src[Index] = DMA_LENGTH - Index;
Checked[Channel] = 0;
Status = XDmaPs_Start(&DmaInstance, Channel, &DmaCmd, 0);
if (Status != XST_SUCCESS) {
xil_printf("Starting the DMA is failed./n");
return XST_FAILURE;
}
xil_printf("Starting the DMA is successful./n");
TimeOutCnt = 0;
while (!Checked[Channel]
&& TimeOutCnt TimeOutCnt++;
}
/* Now the DMA is done */
xil_printf("Jump out of the interrupt/n");
if (TimeOutCnt >= TIMEOUT_LIMIT) {
xil_printf("Overtime!/n");
TestStatus = XST_FAILURE;
}
if (Checked[Channel] /* DMA controller failed */
xil_printf("Checking failure!/n");
TestStatus = XST_FAILURE;
}
}
}
return TestStatus;
}
int systemInit(XScuGic *GicPtr,u16 DeviceId)
{
xil_printf("Start to initialize interrupt system./n");
PS_DMA_InitPara(&DmaCmd);//主要設置DMA的源目的地址
//xil_printf("Configuring DMA parameters is successful./n");
int Status;
Status = PS_DMA_IntrInit(&DmaInstance,DeviceId);
if (Status != XST_SUCCESS) {
xil_printf("DMA initialization is failed./n");
return XST_FAILURE;
}
//xil_printf("DMA initialization is successful./n");
Status = sys_IntrInit(GicPtr);
if (Status != XST_SUCCESS) {
xil_printf("Initialization of the interrupt system is failed./n");
return XST_FAILURE;
}
//xil_printf("Initialization of the interrupt system is successful./n");
setupIntrException(GicPtr);
Status = PS_DMA_SetupIntr(GicPtr,&DmaInstance,Channel);//////////////////////////DMA中斷入口///////////////////////
if (Status != XST_SUCCESS) {
xil_printf("Setting up DMA interrupt is failed./n");
return XST_FAILURE;
}
//xil_printf("Setting up DMA interrupt is successful./n");
xil_printf("System initialization is finished./n");
xil_printf("------------------------------------------/n");
return XST_SUCCESS;
}
main.c
上述代碼的封裝方式參考了米聯客教程中的思想。先說明系統中斷部分:sys_IntrInit()函數中進行查找表配置和中斷控制器初始化操作,setupIntrException()函數負責使能中斷異常處理。再來說說PS_DMA中斷部分:PS_DMA_IntrInit()函數與系統中斷中sys_IntrInit()從操作到格式幾乎完成相同,亦是查找表配置和DMA的初始化。PS_DMA_SetupIntr()函數完成了中斷源和中斷控制器的連接和設置中斷處理器,以及中斷使能,也就是所有PS_DMA的專用中斷操作。
PS_DMA_SetupIntr()內最重要的部分是XDmaPs_SetDoneHandler(),其相當于一個調用中斷函數的通用處理框架,它的第三個參數DoneHandler才是真正的中斷處理函數。這里涉及到C語言的高級話題:函數通過函數指針調用另一個函數,被函數指針調用的函數就是通常講的“回調函數”了。指針調用函數的方式兼顧了程序的通用架構和靈活性,具體參考文章:不懂C語言回調函數,那就看這篇文章吧! - 簡書 https://www.jianshu.com/p/2f695d6fd64f 在該程序中,中斷回調函數為DmaDoneHandler()。
PS_DMA_InitPara()是自行添加的PS_DMA參數初始化函數,內部的參數更是重中之重了,我們來查看Xilinx官方文檔ug585的DMA Controller章節。
簡要來說,DMA以burst形式傳輸數據,意思是分批次搬移。手冊說明原或目的burst_size位寬不能超過64bit,這也是其掛載AXI總線的數據位寬。PS_DMA_InitPara()里的SrcBurstSize為源突發傳輸位寬字節數,最大為8.SrcBurstLen是手冊中所說的“burst length”,即突發傳輸數據個數。SrcInc表示burst types為地址自增(1)還是地址固定(0)模式。目的控制字同理。剩下的三個參數最重要:SrcAddr DstAddr Length分別代表源首地址 目的首地址和一共需要搬移的數據字節數。需要注意的是,一定要滿足srcburstsize*srcburstlen == dstburstsize*dstburstlen,否則發生錯誤。這一點也比較好理解,相當于FPGA邏輯設計中的異步FIFO兩側數據帶寬要匹配。
那么要想完成OCM到DDR的數據搬移,改動下地址就可以嘛。由于讀寫DDR要訪問絕對地址,所以要格外注意讀寫操作的地址不能和DDR內存儲程序代碼和中間數據的地址段重疊。避免程序崩潰很簡單的做法就是在XPAR_PS7_DDR_0_S_AXI_BASEADDR 的基礎上加一段偏移量,具體加多少的問題本人也不是很明確,希望看到的朋友能在評論中指點一二。
對于ZYNQ這一SOC架構來說,PS端連接如以太網,USB等高帶寬外設計接口更加方便,所以PS_DMA的靈活運用還好是十分必要的,更靈活高效的利用這一硬件資源還要后期繼續探索。PS端和PL端高速數據交互就需要用到另一個DMA成員AXI_DMA,可以說它利用片內總線打破了CPU+FPGA架構的性能瓶頸,該部分內容將在后續說明。
編輯:hfy
-
FPGA
+關注
關注
1630文章
21775瀏覽量
604672 -
以太網
+關注
關注
40文章
5450瀏覽量
172183 -
usb
+關注
關注
60文章
7965瀏覽量
265283 -
Zynq
+關注
關注
10文章
610瀏覽量
47224
發布評論請先 登錄
相關推薦
評論