前言
在ZYNQ中進(jìn)行PL-PS數(shù)據(jù)交互的時候,經(jīng)常會使用到DMA,其實在前面的ZYNQ學(xué)習(xí)當(dāng)中,也有學(xué)習(xí)過DMA的使用,那就是通過使用自定義的IP,完成HP接口向內(nèi)存寫入和讀取數(shù)據(jù)的方式。同樣Xilinx官方也提供有一些DMA的IP,通過調(diào)用API函數(shù)能夠更加靈活地使用DMA。
1. AXI DMA的基本接口
axi dma IP的基本結(jié)構(gòu)如下,主要分為三個部分,分別是控制axi dma寄存器通道,從ddr讀出數(shù)據(jù)通道和向ddr寫入數(shù)據(jù)通道。其IP結(jié)構(gòu)的兩邊分別對應(yīng)著用于訪問內(nèi)存的AXI總線和用于用戶簡單操作的axis stream總線。axi stream總線相較于axi總線來說要簡單很多,它沒有地址,靠主機(jī)和從機(jī)之間進(jìn)行握手來傳遞數(shù)據(jù)。
2 Block design搭建
做一個簡單的例子來測試一下axi dma。先自定義一個IP,用于緩存從zynq通過axi dma發(fā)來的數(shù)據(jù),一次突發(fā)傳輸結(jié)束之后,將接收到的數(shù)據(jù)寫回到內(nèi)存中。
2.1 自定義一個IP
時序設(shè)計如下,將接收到的數(shù)據(jù)緩存到FIFO中,當(dāng)zynq一次axi stream 傳輸結(jié)束的時候,開始將數(shù)據(jù)從FIFO中讀出,并將數(shù)據(jù)寫入到內(nèi)存中。
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的東西就有這點優(yōu)點,基本上fpga里面有的資源,都有一個示例工程,把它導(dǎo)入進(jìn)來就可以參考一下具體這個外設(shè)改怎么來使用了。這個demo里面使用到了串口。串口在前面的串口中斷中已經(jīng)使用到,本次實驗需要使用到串口的中斷和DMA的中斷。
PC的串口向FPGA發(fā)送數(shù)據(jù),一幀數(shù)據(jù)發(fā)送完成之后,會觸發(fā)串口的中斷。串口中斷函數(shù)會接收數(shù)據(jù),并且記錄數(shù)據(jù)長度。然后將通過DMA將數(shù)據(jù)寫入到自定義的IP當(dāng)中,自定義IP接收完數(shù)據(jù)之后,將把數(shù)據(jù)寫回到內(nèi)存中。
#include
#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_IDXPAR_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 ");
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 ");
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
*
* @paramCallback is a pointer to TX channel of the DMA engine.
*
* @returnNone.
*
* @noteNone.
*
******************************************************************************/
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.
*
* @paramCallback is a pointer to RX channel of the DMA engine.
*
* @returnNone.
*
* @noteNone.
*
******************************************************************************/
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 ");
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 ");
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 ");
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 ");
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;
}
測試結(jié)果
從串口發(fā)出的數(shù)據(jù)被成功的接收。再看看ila抓取到的波形。
輸入到自定義IP的數(shù)據(jù):
從自定義IP輸出的數(shù)據(jù)。一次傳輸之間隔了較長的時間。
原文鏈接:
https://openatomworkshop.csdn.net/67401f383a01316874d6e783.html
-
dma
+關(guān)注
關(guān)注
3文章
565瀏覽量
100657 -
AXI
+關(guān)注
關(guān)注
1文章
128瀏覽量
16645
原文標(biāo)題:ZYNQ基礎(chǔ)---AXI DMA使用
文章出處:【微信號:gh_9d70b445f494,微信公眾號:FPGA設(shè)計論壇】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論