网站建设专业简介一键建站免费
文章目录
- 1 实验任务
- 2 系统框图
- 3 硬件设计
- 4 软件设计
1 实验任务
本实验任务是使用PL端的AXI DMA IP核从从PS端的DDR3中读取数据,并将数
据写回到PS端的DDR3中。
2 系统框图
3 硬件设计
- 添加ZYNQ7 Processing System IP核
- 在PS-PL Configuration页面
- 勾选FCLK_RESET0_N
- 勾选M AXI GP0 interface
- 勾选S AXI HP0 interface
- 在Peripheral I/O页面
- 勾选UART
- 在Clock Configuration页面
- 勾选FCLK_CLK0
- 勾选FCLK_CLK1
- 在Interrupts页面
- 勾选IRQ_F2P[15:0]
- 在PS-PL Configuration页面
- 添加AXI Direct Memory Access IP核
- 添加AXI4-Stream Data FIFO IP核
- 添加Clocking Wizard IP核
- 添加Concat IP核
- 添加System ILA IP核
- 自动连线
注意:
- 本设计共有4个时钟,PS和外部晶振各提供两个时钟
- PS FCLK_CLK0:50MHz,为配置接口M_AXI_GP0提供时钟
- PS FCLK_CLK1:100MHz,为数据接口S_AXI_GP0提供时钟
- 晶振clk_out1:120MHz,为MM2S数据流提供时钟(AXI DMA读-下行)
- 晶振clk_out2:150MHz,为S2MM数据流提供时钟(AXI DMA写-上行)
- 在使用自动连线时,一定要头脑清楚,正确设置Master Interface、Slave Interface和AXI Connect的时钟。
4 软件设计
注意事项:
- 缓存一致性问题:在进行PS-PL之间的DMA传输时,不可避免会遇到Cache问题
- 产生原因:
- PS访问DDR时,为了提高访问速度,通常会将数据缓存Data Cache中
- 好处:加快下一次访问速度
- 坏处:Data Cache中的数据与DDR中的数据不一致
- 解决办法
- 方法1:使用Xil_DCacheDisable函数禁用Cache(简单粗暴)
- 方法2:使用Xil_DCacheFlushRange函数和Xil_DCacheInvalidateRange函数
- 在AXIDMA发送前,使用Xil_DCacheFlushRange函数,将Data Cache中缓存的数据强制写回到DDR中去
- 在AXIDMA接收后,使用Xil_DCacheInvalidateRange,使Data Cache中缓存的数据无效,直接从DDR中读取数据
- 产生原因:
/***************************** Include Files *********************************/
#include "stdio.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xscugic.h"
#include "xstatus.h"
#include "xaxidma.h"
/************************** Constant Definitions *****************************/
#define AXIDMA_DEVICE_ID XPAR_AXIDMA_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID#define AXIDMA_RX_INTR_ID XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define AXIDMA_TX_INTR_ID XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID#define DDR_BASE_ADDR XPAR_PS7_DDR_0_S_AXI_BASEADDR //0x00100000
#define MEM_BASE_ADDR (DDR_BASE_ADDR + 0x01000000) //0x01100000
#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000) //0x01200000
#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000) //0x01400000#define RESET_TIMEOUT_COUNTER 10000 //复位时间
#define TEST_START_VALUE 0x0 //测试起始值
#define AXIDMA_LENGTH 0x2000 //DMA长度 = 8192Byte
/************************** Function Prototypes ******************************/
int AxiDmaSetup(XAxiDma* AxiDmaInstPtr);
int SetupIntrSystem(XScuGic* IntcInstPtr, XAxiDma* AxiDmaInstPtr, u16 AxiDmaTxIntrId, u16 AxiDmaRxIntrId);
void AxiDmaTxIntrHandler(void* Callback);
void AxiDmaRxIntrHandler(void* Callback);
int CheckData(u32* RxBuffer);
/************************** Variable Definitions *****************************/
XAxiDma AxiDmaInst; //XAxiDma实例
XScuGic IntcInst; //中断控制器的实例int AxiDmaTxDone; //发送完成标志
int AxiDmaRxDone; //接收完成标志
int AxiDmaTxError; //发送出错标志
int AxiDmaRxError; //接收出错标志
/************************** Function Definitions *****************************/int main(void){//int Status;u32 TestValue;u32* AxiDmaTxBufferPtr;u32* AxiDmaRxBufferPtr;//AxiDmaTxBufferPtr = (u32 *) TX_BUFFER_BASE;AxiDmaRxBufferPtr = (u32 *) RX_BUFFER_BASE;//printf("AXIDMA Read and Write Loop Test.\n");// 设置AXIDMAStatus = AxiDmaSetup(&AxiDmaInst);if (Status != XST_SUCCESS) {printf("AXIDMA Setup Failed\n");return XST_FAILURE;}// 设置中断系统Status = SetupIntrSystem(&IntcInst, &AxiDmaInst, AXIDMA_TX_INTR_ID, AXIDMA_RX_INTR_ID);if (Status != XST_SUCCESS) {printf("AXIDMA Setup Interrupt System Failed");return XST_FAILURE;}// 初始化标志信号AxiDmaTxDone = 0;AxiDmaRxDone = 0;AxiDmaTxError = 0;AxiDmaRxError = 0;// 注意:在嵌入式系统中,程序直接运行在硬件上,内存地址由硬件设计确定,开发者可以直接访问这些地址,而不需要动态申请内存TestValue = TEST_START_VALUE;for (int i = 0; i < AXIDMA_LENGTH / sizeof(u32); i++) {AxiDmaTxBufferPtr[i] = TestValue;TestValue++;}// 在启动AXIDMA发送之前,将Data Cache中缓存的数据强制写回到DDR中去Xil_DCacheFlushRange((INTPTR)AxiDmaTxBufferPtr, AXIDMA_LENGTH);//char ch;while(1){//printf("Enter 'T' to Start AXIDMA TX :>\n");scanf(" %c", &ch);if (ch == 'T') {// AXIDMA读操作:DDR -> FPGAStatus = XAxiDma_SimpleTransfer(&AxiDmaInst, (UINTPTR)AxiDmaTxBufferPtr, AXIDMA_LENGTH, XAXIDMA_DMA_TO_DEVICE);if (Status != XST_SUCCESS) {printf("AXIDMA SimpleTransfer TX Failed\n");return XST_FAILURE;}//while(!AxiDmaTxDone && !AxiDmaTxError);if (AxiDmaTxDone == 1) {printf("AXIDMA TX Done\n");AxiDmaTxDone = 0;}else if (AxiDmaTxError == 1) {printf("AXIDMA TX Error\n");AxiDmaTxError = 0;}}else {break;}//printf("Enter 'R' to Start AXIDMA RX :>\n");scanf(" %c", &ch);if (ch == 'R') {//Status = XAxiDma_SimpleTransfer(&AxiDmaInst, (UINTPTR)AxiDmaRxBufferPtr, AXIDMA_LENGTH, XAXIDMA_DEVICE_TO_DMA);if (Status != XST_SUCCESS) {printf("AXIDMA SimpleTransfer RX Failed\n");return XST_FAILURE;}//while(!AxiDmaRxDone && !AxiDmaRxError);if (AxiDmaRxDone == 1) {printf("AXIDMA RX Done\n");AxiDmaRxDone = 0;}else if (AxiDmaRxError == 1) {printf("AXIDMA RX Error\n");AxiDmaRxError = 0;}// 在启动AXIDMA接收之后,使Data Cache中缓存的数据无效,直接从DDR中读取数据Xil_DCacheInvalidateRange((INTPTR)AxiDmaRxBufferPtr, AXIDMA_LENGTH);// 检查数据是否正确Status = CheckData(AxiDmaRxBufferPtr);if (Status != XST_SUCCESS) {printf("AXIDMA RX Data Check Failed\n");return XST_FAILURE;}}else {break;}}//return XST_SUCCESS;}// 初始化AXIDMAint AxiDmaSetup(XAxiDma* AxiDmaInstPtr){//int Status;XAxiDma_Config* AxiDmaConfigPtr;// 初始化AXIDMAAxiDmaConfigPtr = XAxiDma_LookupConfig(AXIDMA_DEVICE_ID);if (AxiDmaConfigPtr == NULL) {printf("AXIDMA LookupConfig Failed\n");return XST_FAILURE;}Status = XAxiDma_CfgInitialize(AxiDmaInstPtr, AxiDmaConfigPtr);if (Status != XST_SUCCESS) {printf("AXIDMA CfgInitialize Failed\n");return XST_FAILURE;}// 判断AXIDMA模式:S/G或Simple DMAif (XAxiDma_HasSg(AxiDmaInstPtr)) {printf("AXIDMA Configured as SG Mode\n");return XST_FAILURE;}// 使能AXIDMA中断XAxiDma_IntrEnable(AxiDmaInstPtr, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);XAxiDma_IntrEnable(AxiDmaInstPtr, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);//return XST_SUCCESS;}// 设置AXIDMA中断系统int SetupIntrSystem(XScuGic* IntcInstPtr, XAxiDma* AxiDmaInstPtr, u16 AxiDmaTxIntrId, u16 AxiDmaRxIntrId)
{//int Status;XScuGic_Config* IntcConfigPtr;// step1. 初始化中断控制器GICIntcConfigPtr = XScuGic_LookupConfig(INTC_DEVICE_ID);if (IntcConfigPtr == NULL) {printf("INTC LookupConfig Failed\n");return XST_FAILURE;}Status = XScuGic_CfgInitialize(IntcInstPtr, IntcConfigPtr, IntcConfigPtr->CpuBaseAddress);if (Status != XST_SUCCESS) {printf("INTC CfgInitialize Failed\n");return XST_FAILURE;}// step2. 在处理器中初始化异常处理功能Xil_ExceptionInit();// step3. 在处理器中为IRQ中断异常注册处理程序Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler,(void *) IntcInstPtr);// step4. 在处理器中使能IRQ中断异常Xil_ExceptionEnable();// step5. 在GIC中为外设注册中断处理程序Status = XScuGic_Connect(IntcInstPtr, AxiDmaTxIntrId, (Xil_InterruptHandler) AxiDmaTxIntrHandler, AxiDmaInstPtr);if (Status != XST_SUCCESS) {printf("AXIDMA TX Interrupt Connect Failed\n");return XST_FAILURE;}// step5. 在GIC中为外设注册中断处理程序Status = XScuGic_Connect(IntcInstPtr, AxiDmaRxIntrId, (Xil_InterruptHandler) AxiDmaRxIntrHandler, AxiDmaInstPtr);if (Status != XST_SUCCESS) {printf("AXIDMA RX Interrupt Connect Failed\n");return XST_FAILURE;}// step6. 在GIC中设置外设中断的优先级和触发类型XScuGic_SetPriorityTriggerType(IntcInstPtr, AxiDmaTxIntrId, 0xA0, 0x3); // Rising EdgeXScuGic_SetPriorityTriggerType(IntcInstPtr, AxiDmaRxIntrId, 0xA0, 0x3); // Rising Edge// step7. 在GIC中使能外设中断XScuGic_Enable(IntcInstPtr, AxiDmaTxIntrId);XScuGic_Enable(IntcInstPtr, AxiDmaRxIntrId);//return XST_SUCCESS;
}// AXIDMA发送中断处理函数
void AxiDmaTxIntrHandler(void *Callback)
{//XAxiDma* AxiDmaInstPtr = (XAxiDma*)Callback;u32 IsrStatus ;int AxiDmaResetTimeOut;// 获取中断状态IsrStatus = XAxiDma_IntrGetIrq(AxiDmaInstPtr, XAXIDMA_DMA_TO_DEVICE);// 清除中断状态XAxiDma_IntrAckIrq(AxiDmaInstPtr, IsrStatus, XAXIDMA_DMA_TO_DEVICE);// AXIDMA发送出错if ((IsrStatus & XAXIDMA_IRQ_ERROR_MASK)) {AxiDmaTxError = 1;XAxiDma_Reset(AxiDmaInstPtr);AxiDmaResetTimeOut = RESET_TIMEOUT_COUNTER;while (AxiDmaResetTimeOut) {if (XAxiDma_ResetIsDone(AxiDmaInstPtr)) {break;}AxiDmaResetTimeOut -= 1;}return;}// AXIDMA发送完成if ((IsrStatus & XAXIDMA_IRQ_IOC_MASK)) {AxiDmaTxDone = 1;}//return;
}
// AXIDMA接收中断处理函数
void AxiDmaRxIntrHandler(void *Callback)
{//XAxiDma* AxiDmaInstPtr = (XAxiDma*)Callback;u32 IsrStatus ;int AxiDmaResetTimeOut;// 获取中断状态IsrStatus = XAxiDma_IntrGetIrq(AxiDmaInstPtr, XAXIDMA_DEVICE_TO_DMA);// 清除中断状态XAxiDma_IntrAckIrq(AxiDmaInstPtr, IsrStatus, XAXIDMA_DEVICE_TO_DMA);// AXIDMA接收出错if ((IsrStatus & XAXIDMA_IRQ_ERROR_MASK)) {AxiDmaRxError = 1;XAxiDma_Reset(AxiDmaInstPtr);AxiDmaResetTimeOut = RESET_TIMEOUT_COUNTER;while (AxiDmaResetTimeOut) {if (XAxiDma_ResetIsDone(AxiDmaInstPtr)) {break;}AxiDmaResetTimeOut -= 1;}return;}// AXIDMA接收完成if ((IsrStatus & XAXIDMA_IRQ_IOC_MASK)) {AxiDmaRxDone = 1;}return;
}// 检查数据缓冲区
int CheckData(u32* RxBuffer)
{//u32 TestValue;//TestValue = TEST_START_VALUE;//for (int i = 0; i < AXIDMA_LENGTH / sizeof(u32); i++) {if (RxBuffer[i] != TestValue) {printf("The %d Data Error : Received Data is %lx, Expected Data is %lx\n", i, RxBuffer[i], TestValue);return XST_FAILURE;}TestValue++;}//return XST_SUCCESS;
}