ZYNQ基础---AXI DMA使用

前言

  在ZYNQ中进行PL-PS数据交互的时候,经常会使用到DMA,其实在前面的ZYNQ学习当中,也有学习过DMA的使用,那就是通过使用自定义的IP,完成HP接口向内存写入和读取数据的方式。同样Xilinx官方也提供有一些DMA的IP,通过调用API函数能够更加灵活地使用DMA。

1. AXI DMA的基本接口

  axi dma IP的基本结构如下,主要分为三个部分,分别是控制axi dma寄存器通道,从ddr读出数据通道和向ddr写入数据通道。其IP结构的两边分别对应着用于访问内存的AXI总线和用于用户简单操作的axis stream总线。axi stream总线相较于axi总线来说要简单很多,它没有地址,靠主机和从机之间进行握手来传递数据。
在这里插入图片描述
在这里插入图片描述

2 Block design搭建

  做一个简单的例子来测试一下axi dma。先自定义一个IP,用于缓存从zynq通过axi dma发来的数据,一次突发传输结束之后,将接收到的数据写回到内存中。

2.1 自定义一个IP

  时序设计如下,将接收到的数据缓存到FIFO中,当zynq一次axi stream 传输结束的时候,开始将数据从FIFO中读出,并将数据写入到内存中。
在这里插入图片描述

module dma_loop(
    //====================================================
    //clock and reset
    //====================================================
    input   wire            axis_clk            ,
    input   wire            rst_n               ,
    //====================================================
    //input axis port
    //====================================================
    input   wire    [7:0]   axis_in_tdata       ,
    input   wire            axis_in_tvalid      ,
    output  wire            axis_in_tready      ,
    input   wire            axis_in_tlast       ,

    //====================================================
    //output axis port
    //====================================================
    output  wire    [7:0]   axis_out_tdata      ,
    output  reg             axis_out_tvalid     ,
    input   wire            axis_out_tready     ,
    output  wire            axis_out_tlast      
);

//====================================================
//input axis port
//====================================================
wire            wr_fifo_en      ;
wire            rd_fifo_en      ;
wire            full,empty      ;
reg             rd_start        ;
reg     [9:0]   cnt_data_in     ;
wire            add_cnt_data_in ;
wire            end_cnt_data_in ;
reg     [9:0]   data_len        ;

reg     [9:0]   cnt_data_out    ;
wire            add_cnt_data_out;
wire            end_cnt_data_out;

assign wr_fifo_en = axis_in_tvalid & axis_in_tready;
assign axis_in_tready = ~full;

always @(posedge axis_clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        rd_start <= 1'b0;
    end
    else if (axis_in_tvalid & axis_in_tready & axis_in_tlast) begin
        rd_start <= 1'b1;
    end
    else begin
        rd_start <= 1'b0;
    end
end
//----------------cnt_data------------------
always @(posedge axis_clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        cnt_data_in <= 'd0;
    end
    else if (add_cnt_data_in) begin
        if(end_cnt_data_in)
            cnt_data_in <= 'd0;
        else
            cnt_data_in <= cnt_data_in + 1'b1;
    end

end

assign add_cnt_data_in = axis_in_tvalid & axis_in_tready;
assign end_cnt_data_in = add_cnt_data_in && axis_in_tlast;

//----------------data_len------------------
always @(posedge axis_clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        data_len <= 'd0;
    end
    else if (end_cnt_data_in) begin
        data_len <= cnt_data_in + 1'b1;
    end
end

sfifo_wr1024x8_rd1024x8 inst_sfifo (
    .clk(axis_clk),         // input wire clk
    .din(axis_in_tdata),    // input wire [7 : 0] din
    .wr_en(wr_fifo_en),     // input wire wr_en
    .rd_en(rd_fifo_en),     // input wire rd_en
    .dout(axis_out_tdata),  // output wire [7 : 0] dout
    .full(full),            // output wire full
    .empty(empty)           // output wire empty
);

//----------------axis_out_tvalid------------------
always @(posedge axis_clk or negedge rst_n) begin
    if (rst_n==1'b0) begin
        axis_out_tvalid <= 1'b0;
    end
    else if (end_cnt_data_out) begin
        axis_out_tvalid <= 1'b0;
    end
    else if (rd_start == 1'b1) begin
        axis_out_tvalid <= 1'b1;
    end
end

//----------------cnt_data_out------------------
always @(posedge axis_clk or negedge rst_n) begin
    if (rst_n == 1'b0) begin
        cnt_data_out <= 'd0;
    end
    else if (add_cnt_data_out) begin
        if(end_cnt_data_out)
            cnt_data_out <= 'd0;
        else
            cnt_data_out <= cnt_data_out + 1'b1;
    end
end

assign add_cnt_data_out = axis_out_tvalid & axis_out_tready;
assign end_cnt_data_out = add_cnt_data_out && cnt_data_out == data_len - 1;

assign axis_out_tlast =  end_cnt_data_out;
assign rd_fifo_en = axis_out_tvalid & axis_out_tready;


wire [63:0] probe0;

assign probe0 = {

    cnt_data_in         ,
    cnt_data_out        ,
    rd_start            ,
    data_len            ,
    axis_in_tdata       ,
    axis_in_tvalid      ,
    axis_in_tready      ,
    axis_in_tlast       ,

    axis_out_tdata      ,
    axis_out_tvalid     ,
    axis_out_tready     ,
    axis_out_tlast        
};

ila_0 inst_ila (
	.clk(axis_clk), // input wire clk


	.probe0(probe0) // input wire [63:0] probe0
);

endmodule  

搭建block design

在这里插入图片描述

3. vitis

   xilinx的东西就有这点优点,基本上fpga里面有的资源,都有一个示例工程,把它导入进来就可以参考一下具体这个外设改怎么来使用了。这个demo里面使用到了串口。串口在前面的串口中断中已经使用到,本次实验需要使用到串口的中断和DMA的中断。
  PC的串口向FPGA发送数据,一帧数据发送完成之后,会触发串口的中断。串口中断函数会接收数据,并且记录数据长度。然后将通过DMA将数据写入到自定义的IP当中,自定义IP接收完数据之后,将把数据写回到内存中。

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xparameters.h"
#include "xaxidma.h"
#include "xuartps.h"
#include "xscugic.h"
#include "sleep.h"

#define GIC_DEVICE_ID	XPAR_PS7_SCUGIC_0_DEVICE_ID
#define UART_DEV_ID 	XPAR_PS7_UART_1_DEVICE_ID
#define DMA_DEVICE_ID 	XPAR_AXI_DMA_0_DEVICE_ID

#define UART_INTR_ID 	XPAR_PS7_UART_1_INTR
#define MM2S_INTR_ID 	XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define S2MM_INTR_ID 	XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID

XUartPs UartInst;
XScuGic GicInst;
XAxiDma DmaInst;

volatile int TxDone;
volatile int RxDone;
volatile int Error;

u32 UartRxLen = 0;
int UartRxFlag = 0;
u8 * UartRxBuf = (u8 *)(0x2000000);
u8 * UartTxBuf = (u8 *)(0x4000000);
/***********************************
 * Uart Init function
 **********************************/
int Uart_Init();
/********************************
 * DMA initialize function
 ********************************/
int Dma_Init();

int Setup_Interrupt_System();

void Uart_Intr_Handler(void *CallBackRef, u32 Event, unsigned int EventData);

void Mm2s_Intr_Handler();
void S2mm_Intr_Handler();

int main()
{
    init_platform();
    Uart_Init();
    Dma_Init();
    Setup_Interrupt_System();
    while(1)
    {
    	/*****************************************************************************
		* Uart has receive one frame
		*****************************************************************************/
    	if (UartRxFlag == 1) {
    		// clear the flag
			UartRxFlag = 0;

			/*****************************************************************************
			* Transfer data from axidma to device
			*****************************************************************************/
			Xil_DCacheFlushRange((INTPTR)UartRxBuf, UartRxLen);//flush data into ddr
			usleep(2);
			// transfer data from axi dma to device
			XAxiDma_SimpleTransfer(&DmaInst, (UINTPTR)UartRxBuf, UartRxLen, XAXIDMA_DMA_TO_DEVICE);
			while(!TxDone);
			TxDone=0;//reset txdone flag; complete  txtransfer

			/*****************************************************************************
			* Transfer data from device to dma
			*****************************************************************************/
			Xil_DCacheInvalidateRange((INTPTR)UartTxBuf, UartRxLen);
			usleep(2);
			XAxiDma_SimpleTransfer(&DmaInst, (UINTPTR)UartTxBuf, UartRxLen, XAXIDMA_DEVICE_TO_DMA);
			while(!RxDone);
			RxDone = 0;
			XUartPs_Send(&UartInst, UartTxBuf, UartRxLen);

			XUartPs_Recv(&UartInst, UartRxBuf, 4096);//reset conter and start recv from uart
		}
    }

    cleanup_platform();
    return 0;
}

/*****************************************************************************
 * @ function : init uart and set the callback fuction
*****************************************************************************/
int Uart_Init()
{
	int Status;
	u32 IntrMask;

	XUartPs_Config *UartCfgPtr;
	UartCfgPtr = XUartPs_LookupConfig(UART_DEV_ID);
	Status = XUartPs_CfgInitialize(&UartInst, UartCfgPtr, UartCfgPtr->BaseAddress);
	if(Status != XST_SUCCESS)
	{
		printf("initialize UART failed\n");
		return XST_FAILURE;
	}
	/****************************************
	 * Set uart interrput mask
	 ****************************************/
	IntrMask =
		XUARTPS_IXR_TOUT | XUARTPS_IXR_PARITY | XUARTPS_IXR_FRAMING |
		XUARTPS_IXR_OVER | XUARTPS_IXR_TXEMPTY | XUARTPS_IXR_RXFULL |
		XUARTPS_IXR_RXOVR;
	XUartPs_SetInterruptMask(&UartInst, IntrMask);

	/*****************************************************************************
	* Set Uart interrput callback function
	*****************************************************************************/
	XUartPs_SetHandler(&UartInst, (XUartPs_Handler)Uart_Intr_Handler, &UartInst);

	/*****************************************************************************
	* Set Uart baud rate
	*****************************************************************************/
	XUartPs_SetBaudRate(&UartInst, 115200);

	/*****************************************************************************
	* Set Uart opertion mode
	*****************************************************************************/
	XUartPs_SetOperMode(&UartInst, XUARTPS_OPER_MODE_NORMAL);

	/*****************************************************************************
	* Set Uart Receive timeout
	*****************************************************************************/
	XUartPs_SetRecvTimeout(&UartInst, 8);

	/*****************************************************************************
	* Start to listen
	*****************************************************************************/
	XUartPs_Recv(&UartInst, UartRxBuf, 4096);
	return Status;
}

void Uart_Intr_Handler(void *CallBackRef, u32 Event, unsigned int EventData)
{
	if (Event == XUARTPS_EVENT_RECV_TOUT) {
		if(EventData == 0)
		{
			XUartPs_Recv(&UartInst, UartRxBuf, 4096);
		}
		else if(EventData > 0) {
			UartRxLen = EventData;
			UartRxFlag = 1;
		}
	}
}

/*****************************************************************************
 * @ function : init Axi DMA
*****************************************************************************/
int Dma_Init()
{
	int Status;
	XAxiDma_Config * DmaCfgPtr;
	DmaCfgPtr = XAxiDma_LookupConfig(DMA_DEVICE_ID);
	Status = XAxiDma_CfgInitialize(&DmaInst, DmaCfgPtr);
	if(Status != XST_SUCCESS)
	{
		printf("initialize AXI DMA failed\n");
		return XST_FAILURE;
	}
	/*****************************************************************************
	* Disable all the interrupt before setup
	*****************************************************************************/
	XAxiDma_IntrDisable(&DmaInst, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
	XAxiDma_IntrDisable(&DmaInst, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);

	/*****************************************************************************
	*Enable all the interrput
	*****************************************************************************/
	XAxiDma_IntrEnable(&DmaInst, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DEVICE_TO_DMA);
	XAxiDma_IntrEnable(&DmaInst, XAXIDMA_IRQ_ALL_MASK, XAXIDMA_DMA_TO_DEVICE);

	return Status;
}
/*****************************************************************************/
/*
*
* This is the DMA TX Interrupt handler function.
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then sets the TxDone.flag
*
* @param	Callback is a pointer to TX channel of the DMA engine.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void Mm2s_Intr_Handler(void *Callback)
{

	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE);

	/* Acknowledge pending interrupts */


	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {

		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/*
		 * Reset should never fail for transmit channel
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = 10000;

		while (TimeOut) {
			if (XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If Completion interrupt is asserted, then set the TxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		TxDone = 1;
	}
}

/*****************************************************************************/
/*
*
* This is the DMA RX interrupt handler function
*
* It gets the interrupt status from the hardware, acknowledges it, and if any
* error happens, it resets the hardware. Otherwise, if a completion interrupt
* is present, then it sets the RxDone flag.
*
* @param	Callback is a pointer to RX channel of the DMA engine.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void S2mm_Intr_Handler(void *Callback)
{
	u32 IrqStatus;
	int TimeOut;
	XAxiDma *AxiDmaInst = (XAxiDma *)Callback;

	/* Read pending interrupts */
	IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);

	/* Acknowledge pending interrupts */
	XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);

	/*
	 * If no interrupt is asserted, we do not do anything
	 */
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
		return;
	}

	/*
	 * If error interrupt is asserted, raise error flag, reset the
	 * hardware to recover from the error, and return with no further
	 * processing.
	 */
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {

		Error = 1;

		/* Reset could fail and hang
		 * NEED a way to handle this or do not call it??
		 */
		XAxiDma_Reset(AxiDmaInst);

		TimeOut = 10000;

		while (TimeOut) {
			if(XAxiDma_ResetIsDone(AxiDmaInst)) {
				break;
			}

			TimeOut -= 1;
		}

		return;
	}

	/*
	 * If completion interrupt is asserted, then set RxDone flag
	 */
	if ((IrqStatus & XAXIDMA_IRQ_IOC_MASK)) {

		RxDone = 1;
	}
}


/*****************************************************************************
 * @ function : Set up the interrupt system
*****************************************************************************/
int Setup_Interrupt_System()
{
	int Status;
	XScuGic_Config * GicCfgPtr;
	GicCfgPtr = XScuGic_LookupConfig(GIC_DEVICE_ID);
	Status = XScuGic_CfgInitialize(&GicInst, GicCfgPtr, GicCfgPtr->CpuBaseAddress);
	if(Status != XST_SUCCESS)
	{
		printf("initialize GIC failed\n");
		return XST_FAILURE;
	}
	/*****************************************************************************
	* initialize exception system
	*****************************************************************************/
	Xil_ExceptionInit();

	/*****************************************************************************
	* register interrput type exception
	*****************************************************************************/
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler, &GicInst);

	/*****************************************************************************
	* connect interrput to scugic controller
	*****************************************************************************/
	Status = XScuGic_Connect(&GicInst, UART_INTR_ID, (Xil_ExceptionHandler) XUartPs_InterruptHandler, &UartInst);
	if(Status != XST_SUCCESS)
	{
		printf("Connect Uart interrput to GIC failed\n");
		return XST_FAILURE;
	}
	Status = XScuGic_Connect(&GicInst, MM2S_INTR_ID, (Xil_ExceptionHandler) Mm2s_Intr_Handler, &DmaInst);
	if(Status != XST_SUCCESS)
	{
		printf("Connect DMA tx interrput to GIC failed\n");
		return XST_FAILURE;
	}

	Status = XScuGic_Connect(&GicInst, S2MM_INTR_ID, (Xil_ExceptionHandler) S2mm_Intr_Handler, &DmaInst);
	if(Status != XST_SUCCESS)
	{
		printf("Connect DMA tx interrput to GIC failed\n");
		return XST_FAILURE;
	}

	/*****************************************************************************
	* Enable the interrput
	*****************************************************************************/
	XScuGic_Enable(&GicInst, UART_INTR_ID);
	XScuGic_Enable(&GicInst, S2MM_INTR_ID);
	XScuGic_Enable(&GicInst, MM2S_INTR_ID);

	/*****************************************************************************
	* Enable the exception system
	*****************************************************************************/
	Xil_ExceptionEnable();
	return Status;
}

测试结果

在这里插入图片描述
  从串口发出的数据被成功的接收。再看看ila抓取到的波形。
在这里插入图片描述
  输入到自定义IP的数据:
在这里插入图片描述   从自定义IP输出的数据。一次传输之间隔了较长的时间。

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值