当前位置: 首页 > news >正文

13 - AXI DMA环路实验

文章目录

  • 1 实验任务
  • 2 系统框图
  • 3 硬件设计
  • 4 软件设计

1 实验任务

本实验任务是使用PL端的AXI DMA IP核从从PS端的DDR3中读取数据,并将数
据写回到PS端的DDR3中。

2 系统框图

在这里插入图片描述

3 硬件设计

  1. 添加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]
  2. 添加AXI Direct Memory Access IP核
  3. 添加AXI4-Stream Data FIFO IP核
  4. 添加Clocking Wizard IP核
  5. 添加Concat IP核
  6. 添加System ILA IP核
  7. 自动连线

注意:

  1. 本设计共有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写-上行)
  2. 在使用自动连线时,一定要头脑清楚,正确设置Master Interface、Slave Interface和AXI Connect的时钟。
    在这里插入图片描述

4 软件设计

注意事项:

  1. 缓存一致性问题:在进行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");

     // 设置AXIDMA
     Status = 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 -> FPGA
    	     Status = 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;
 }

// 初始化AXIDMA
 int AxiDmaSetup(XAxiDma* AxiDmaInstPtr)
 {
	 //
	 int Status;
	 XAxiDma_Config* AxiDmaConfigPtr;

	 // 初始化AXIDMA
	 AxiDmaConfigPtr = 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 DMA
     if (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. 初始化中断控制器GIC
    IntcConfigPtr = 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 Edge
    XScuGic_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;
}

相关文章:

  • 自动驾驶04:点云预处理03
  • 25大唐杯赛道一本科B组大纲总结(上)
  • linux0.11内核源码修仙传第十一章——硬盘初始化
  • trae初体验-java开发
  • 网络相关知识总结2
  • 基于神经网络的文本分类的设计与实现
  • P3379 【模板】最近公共祖先(LCA)【题解】(重链剖分法)
  • Python 装饰器(Decorators)
  • 华为虚墙配置实验
  • FLEXlm如何通过web调用
  • 银河麒麟高级服务器操作系统基础docker镜像封装http和docker端口映射操作
  • 大模型AI Agent:简介(第一部分)—— 特性、组件、运作及应用全景
  • Nature招牌1区Top认证!可解释多模态融合模型取得重大突破!
  • 座舱与智驾“双轮驱动”,芯擎科技打造智能汽车“芯”标杆
  • 香港电讯企业托管服务,助企业实现高效IT管理与运营
  • MySQL8.4 InnoDB Cluster高可用集群使用指南
  • 嵌入式c学习第十天
  • 留记录excel 模板导入
  • 深度学习处理时间序列(3)
  • AOA与TOA混合定位,MATLAB例程,三维空间下的运动轨迹,滤波使用EKF,附下载链接
  • 贯彻落实《生态环境保护督察工作条例》,充分发挥生态环境保护督察利剑作用
  • 假冒政府机构账号卖假货?“假官号”为何屡禁不绝?媒体调查
  • 上海明天短暂恢复晴热最高32℃,一大波雨水在候场
  • 官方数据显示:我国心血管疾病患者已超3亿人
  • 国际博物馆日|在辽宁省博物馆遇见敦煌
  • “先增聘再离任”又添一例,景顺长城基金经理鲍无可官宣辞职