S03_CH01_AXI_DMA_LOOP 環路測試


S03_CH01_AXI_DMA_LOOP 環路測試

1.1概述

本課程是本季課程里面最簡單,也是后面DMA課程的基礎,讀者務必認真先閱讀和學習。

本課程的設計原理分析。

本課程是設計一個最基本的DMA環路,實現DMA的環路測試,在SDK里面發送數據到DMA然后DMA在把數據發回到DDR里面,SDK讀取內存地址里面的數據,對比接收的數據是否和發送出去的一致。DMA的接口部分使用了data_fifo IP鏈接。本課程會詳細介紹創建工程的每個步驟,后面的課程將不再詳細介紹創建工程的步驟。

1.2搭建硬件系統

1.2.1新建VIVADO工程

Step1:啟動VIVADO,單擊Create New Project

wps2A3.tmp

Step2:單擊NEXT

wps2A4.tmp

Step3:創建名為Miz_sys的工程到對應的文件目錄,之后單擊NEXT

wps2B5.tmp

Step4:選擇RTL Project並且勾選復選框,之后單擊NEXT

wps2B6.tmp

Step5:選擇芯片的型號和封裝速度等級。

MIZ702/MIZ702N選擇Zynq-7000-xc7z020clg484-1

MIZ701N-7010選擇Zynq-7000-xc7z010clg400-1

MIZ701N-7020選擇Zynq-7000-xc7z020clg400-2

wps2B7.tmpwps2B8.tmp wps2C9.tmp

Step:6 單機Finish完成工程創建。

1.2.2創建VIVADO硬件構架

Step1:單擊 Create Block Design

wps2CA.tmp

Step2:命名為system 之后單擊OK

wps2CB.tmp

Step3:創建完成后如下圖所示

wps2CC.tmp

Step3:添加各個模塊如圖:

wps2DC.tmp

Step4雙擊ZYNQ IP 進行如下步驟配置

Step5: MIZ702和MIZ702N的輸入時鍾是333.333333MHZ

wps2DD.tmp

Step6:MIZ701N PS的輸入時鍾是50MHZ

wps2DE.tmp

Step7:MIZ702的開發板采用的是單片256MB的MT41K128M16JI-125

wps2EF.tmp

wps2F0.tmp

Step8:MIZ701N和MIZ702N的內存型號一樣,都是單片512MB的MT41K256M16RE-125

wps2F1.tmp

Step9:PS的PLL提供本系統的時鍾100MHZ

wps2F2.tmp

Step10:啟動1路HP接口,HP接口是ZYNQ個高速數據接口

wps302.tmp

Step11:勾選PL到PS的中斷資源(關於中斷,在第二季的課程中有詳細講解,不熟悉的讀者可以到第二季課程中溫習一下)

wps303.tmp

Step12:設置完成后單擊OK

Step13:雙擊DMA  IP 設置如下:

下圖中,同時勾選讀通道和寫通道,另外設置,Wideh of buffer length register 為23bit 這個含義是2的23次方8,388,607bytes 8M大小,這里設置14bit 就夠用了,長度越大需要的資源也就越多。

wps304.tmp

Step14:Data FIFO 設置

wps315.tmp

Step15:Concat IP設置

Concate IP實現了單個分散的信號,整合成總線信號。這里2個獨立的中斷信號,可以合並在一起介入到ZYNQ IP的中斷信號上。

wps316.tmp

Step16:Run Automation 自動配置ZYNQ IP 如下圖所示

wps317.tmp

Step17:單擊Run Connection Automation 自動連線,只要軟件提示你需要自動連線,一般都需要進行自動連線,除非自己知道如何連線,有特殊需求。

wps318.tmp

Step18:如果還有提示需要自動連線的繼續讓軟件自動連線,直到出下如下。可以看到,還有未連線的模塊。

wps329.tmp

Step19:把DMA收發中斷信號,通過contact IP連接到ZYNQ

wps32A.tmp

Step20:

連接FIFO的S_AXIS(寫端口)到DMA的M_AXIS(DMA讀端口);

連接FIFO的M_AXIS(讀端口)到DMA的S_AXIS(DMA寫端口);

連接FIFO 的a_axis_aresetn到 復位IP的peripheral aresetn ;

連接FIFO的s_axis_ack到ZYNQ  IP 的FCLK0;

連接完成后如下圖

wps32B.tmp

Step21:把OLED 模塊的IO引出來,后面C代碼部分會用OLED顯示一些信息(MIZ701N需要配OLED模塊)。連接完成后的工程如下圖

wps33B.tmp

Step22:未來調試的時候可以觀察到中斷信號的產生,添加ila 調試IP並且進行如下設置

wps33C.tmp

wps33D.tmp

Step23:把中斷信號連接到 ila IP上,另外,把時鍾信號也連接起來。

wps33E.tmp

Step24:

以上就完成獨立工程的創建。

之后的過程是Validate Design->Gerate Out products->Create wrappers->Generate Bitstream 產生完成后導入到SDK進行軟件開發。

1.3 PS部分軟件分析

1.3.1新建SDK工程

Step1:新建一個名為AXI_DMA_Test的空的軟件工程

wps34F.tmp

wps350.tmp

Step2:直接把源碼復制過來,軟件會自動編譯

wps351.tmp

1.3.2 main.c源碼的分析

init_intr_sys();是對中斷資源的初始化,使能中斷資源。這個函數里面調用的函數是筆者封裝好的初始化函數,使用起來比較方便。一般只要給出中斷對象,中斷號,就可以對中斷進行初始化。

DMA_Intr_Init(&AxiDma,0);中第一參數是DMA的對象,第二參數是硬件ID

Init_Intr_System(&Intc); 對象是中斷對象

DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID); //注冊中斷函數,最后2個參數是中斷號

DMA_Intr_Enable(&Intc,&AxiDma); 就是啟動DMA傳輸

表1-3-2-1 init_intr_sys函數

int init_intr_sys(void)

{

DMA_Intr_Init(&AxiDma,0);//initial interrupt system

Init_Intr_System(&Intc); // initial DMA interrupt system

Setup_Intr_Exception(&Intc);

DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

DMA_Intr_Enable(&Intc,&AxiDma);

}

為了發送的數據是已知是確定數據,先對TxBufferPtr 發送緩沖進行初始化,初始化后用Xil_DCacheFlushRange 函數把數據全部刷到DDR中。

XAxiDma_SimpleTransfer 函數為啟動一次DMA接收傳輸。

XAxiDma_SimpleTransfer 函數為啟動一次DMA發送傳輸

DMA_CheckData 函數為對接收的數據進行校驗和對比。

表1-3-2-1 DMA_CheckData

int axi_dma_test()

{

int Status;

TxDone = 0;

RxDone = 0;

Error = 0;

xil_printf("\r\n----DMA Test----\r\n");

print_message( "----DMA Test----",0);//oled print

xil_printf("PKT_LEN=%d\r\n",MAX_PKT_LEN);

sprintf(oled_str,"PKT_LEN=%d",MAX_PKT_LEN);

print_message(oled_str,1);//oled print

//while(1)

for(i = 0; i < Tries; i ++)

{

Value = TEST_START_VALUE + (i & 0xFF);

for(Index = 0; Index < MAX_PKT_LEN; Index ++) {

TxBufferPtr[Index] = Value;

Value = (Value + 1) & 0xFF;

}

/* Flush the SrcBuffer before the DMA transfer, in case the Data Cache

* is enabled

*/

Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);

Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) RxBufferPtr,

MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}

Status = XAxiDma_SimpleTransfer(&AxiDma,(u32) TxBufferPtr,

MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {

return XST_FAILURE;

}

/*

* Wait TX done and RX done

*/

while (!TxDone || !RxDone) {

/* NOP */

}

success++;

TxDone = 0;

RxDone = 0;

if (Error) {

xil_printf("Failed test transmit%s done, "

"receive%s done\r\n", TxDone? "":" not",

RxDone? "":" not");

goto Done;

}

/*

* Test finished, check data

*/

Status = DMA_CheckData(MAX_PKT_LEN, (TEST_START_VALUE + (i & 0xFF)));

if (Status != XST_SUCCESS) {

xil_printf("Data check failed\r\n");

goto Done;

}

}

xil_printf("AXI DMA interrupt example test passed\r\n");

xil_printf("success=%d\r\n",success);

sprintf(oled_str,"success=%d",success);

print_message(oled_str,2);

/* Disable TX and RX Ring interrupts and return success */

DMA_DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);

Done:

xil_printf("--- Exiting Test --- \r\n");

print_message("--Exiting Test---",3);

return XST_SUCCESS;

}

int init_intr_sys(void)

{

DMA_Intr_Init(&AxiDma,0);//initial interrupt system

Init_Intr_System(&Intc); // initial DMA interrupt system

Setup_Intr_Exception(&Intc);

DMA_Setup_Intr_System(&Intc,&AxiDma,TX_INTR_ID,RX_INTR_ID);//setup dma interrpt system

DMA_Intr_Enable(&Intc,&AxiDma);

}

int main(void)

{

init_intr_sys();

oled_fresh_en();// enable oled

axi_dma_test();

}

1.3.3 dma_intr.c 源碼分析

XAxiDma *AxiDmaInst = (XAxiDma *)Callback;這句代碼是為了獲取當前中斷的對象。void *Callback是一個無符號的指針,傳遞進來的闡述可以強制轉換成其他任何的對象,這里就是強制轉換成 XAxiDma 對象了。

IrqStatus =XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DMA_TO_DEVICE)這個函數獲取當前中斷號。

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DMA_TO_DEVICE);這個函數是響應當前中斷,通知CPU 當前中斷已經被接收,並且清除中斷標志位。

如果中斷全部正確,TxDone將被置為1表示發送中斷完成。

如果有錯誤,則復位DMA,並且設置超時參數

表1-3-3-1 DMA_TxIntrHandler函數

/*****************************************************************************/

/*

*

* 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.

*

******************************************************************************/

static void DMA_TxIntrHandler(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 = RESET_TIMEOUT_COUNTER;

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;

}

}

接收中斷函數的原理和發送一樣

XAxiDma *AxiDmaInst = (XAxiDma *)Callback;這句代碼是為了獲取當前中斷的對象。void *Callback是一個無符號的指針,傳遞進來的闡述可以強制轉換成其他任何的對象,這里就是強制轉換成 XAxiDma 對象了。

IrqStatus = XAxiDma_IntrGetIrq(AxiDmaInst, XAXIDMA_DEVICE_TO_DMA);這個函數是獲取當前中斷號。

XAxiDma_IntrAckIrq(AxiDmaInst, IrqStatus, XAXIDMA_DEVICE_TO_DMA);這個函數是響應當前中斷,通知CPU 當前中斷已經被接收,並且清除中斷標志位。

如果中斷全部正確,RxDone將被置為1表示接收中斷完成。

如果有錯誤,則復位DMA,並且設置超時參數

表1-3-3-2 DMA_RxIntrHandler函數

/*****************************************************************************/

/*

*

* 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.

*

******************************************************************************/

static void DMA_RxIntrHandler(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 = RESET_TIMEOUT_COUNTER;

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;

}

}

表1-3-3-3 DMA_CheckData函數

/*****************************************************************************/

/*

*

* This function checks data buffer after the DMA transfer is finished.

*

* We use the static tx/rx buffers.

*

* @param Length is the length to check

* @param StartValue is the starting value of the first byte

*

* @return

* - XST_SUCCESS if validation is successful

* - XST_FAILURE if validation is failure.

*

* @note None.

*

******************************************************************************/

int DMA_CheckData(int Length, u8 StartValue)

{

u8 *RxPacket;

int Index = 0;

u8 Value;

RxPacket = (u8 *) RX_BUFFER_BASE;

Value = StartValue;

/* Invalidate the DestBuffer before receiving the data, in case the

* Data Cache is enabled

*/

#ifndef __aarch64__

Xil_DCacheInvalidateRange((u32)RxPacket, Length);

#endif

for(Index = 0; Index < Length; Index++) {

if (RxPacket[Index] != Value) {

xil_printf("Data error %d: %x/%x\r\n",

    Index, RxPacket[Index], Value);

return XST_FAILURE;

}

Value = (Value + 1) & 0xFF;

}

return XST_SUCCESS;

}

1.3.4 dam_intr.h 文件分析

一般把DMA相關變量、常量、函數的聲明或者定義放到頭文件中,dam_intr.h比較關鍵的參數有

TX_BUFFER_BASE定義了DMA發送緩存的基地址

RX_BUFFER_BASE 定義了DMA接收緩存的基地址

MAX_PKT_LEN 表示每一包數據傳輸的長度

NUMBER_OF_TRANSFERS 用在連續測試的時候的測試次數

TEST_START_VALUE 用於 測試的起始參數

int  DMA_CheckData(int Length, u8 StartValue); 對數據進行對比

int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);DMA 中斷注冊

int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr); DMA中斷使能

int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);DMA中斷初始化

表1-3-4 dam_intr.h

/*

*

* www.osrc.cn

* www.milinker.com

* copyright by nan jin mi lian dian zi www.osrc.cn

*/

#ifndef DMA_INTR_H

#define DMA_INTR_H

#include "xaxidma.h"

#include "xparameters.h"

#include "xil_exception.h"

#include "xdebug.h"

#include "xscugic.h"

/************************** Constant Definitions *****************************/

/*

* Device hardware build related constants.

*/

#define DMA_DEV_ID XPAR_AXIDMA_0_DEVICE_ID

#define MEM_BASE_ADDR 0x01000000

#define RX_INTR_ID XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

#define TX_INTR_ID XPAR_FABRIC_AXI_DMA_0_MM2S_INTROUT_INTR

#define TX_BUFFER_BASE (MEM_BASE_ADDR + 0x00100000)

#define RX_BUFFER_BASE (MEM_BASE_ADDR + 0x00300000)

#define RX_BUFFER_HIGH (MEM_BASE_ADDR + 0x004FFFFF)

/* Timeout loop counter for reset

*/

#define RESET_TIMEOUT_COUNTER 10000

/* test start value

*/

#define TEST_START_VALUE 0xC

/*

* Buffer and Buffer Descriptor related constant definition

*/

#define MAX_PKT_LEN 256//4MB

/*

* transfer times

*/

#define NUMBER_OF_TRANSFERS 100000

extern volatile int TxDone;

extern volatile int RxDone;

extern volatile int Error;

int  DMA_CheckData(int Length, u8 StartValue);

int  DMA_Setup_Intr_System(XScuGic * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);

int  DMA_Intr_Enable(XScuGic * IntcInstancePtr,XAxiDma *DMAPtr);

int  DMA_Intr_Init(XAxiDma *DMAPtr,u32 DeviceId);

#endif

1.4測試結果

Step1:在VIVADO工程中點擊Open Target 然后點擊Auto Connect(前面必須先啟動SDK)

wps371.tmp

Step2:連接成功后入下圖

wps372.tmp

Step3:設置中斷條件,以及觀察波形的偏移為500,當中斷觸發的時候,如下圖所示

wps373.tmp

點擊添加接收內存部分地址用於觀察內存中的數據 地址為 0X01300000

wps374.tmp

為了觀察到以下數據,設置斷點,之讓收發程序先跑一次,可以看到第一個數據是0X0C 后面是依次加1

wps385.tmp


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM