본문 바로가기

VERILOG

DMA(Direct Memory Access) Loop test

DMA의 루프테스트 란

 

테스트를 통해 DMA가 메모리 간 데이터 전송을 올바르게 수행하는지, 루프 전송이 효율적으로 이루어지는지 등을 확인할 수 있습니다. 이는 DMA가 많은 데이터를 빠르게 전송하는 시스템에서 중요한 기능이므로 효과적인 동작을 보장하기 위해 수행됩니다.

 

DMA 모듈은
세 종류의 버스. "AXI4-Lite"는 레지스터를 구성하는 데 사용됩니다.

"AXI4 메모리 맵"은 메모리와 상호 작용하는 데 사용됩니다.

 

"AXI4 메모리 맵 읽기" 및 "AXI4 메모리 맵" 쓰기'는 별도로 분리되어 있는데,

이를 "M_AXI_MM2S"와 "M_AXI_S2MM"이라고 합니다.


"AXI4 Stream" 인터페이스는 주변기기를 읽고 쓰는 데 사용됩니다.

"AXI4 스트림 마스터" (MM2S)"는 주변 장치에 쓰는 데 사용되며,

"AXI4-Stream"은 슬레이브(S2MM)는 주변 장치를 읽을 때 사용됩니다.

("MM2S"는 "Memory Map to Stream"을 의미하고, "S2MM"은 "Stream to Memory Map"을 의미합니다.)

 

"AXI 메모리 맵" 데이터 폭 지원 "32 ,64 ,128 ,256 ,512 , 1024비트" 지원 가능
"AXI Stream" 데이터 폭 지원 ", 16 , 32 , 64 , 128 , 256 ,512 , 1024비트" 지원 가능

AXI DMA 블록 다이어그램 입니다. 

 

 

위의 블록다이어그램에서 MM2S Cntl/Sts Logic 중에 Control 부분 레지스터 그림입니다.

 

 

위의 블록다이어그램에서 MM2S Cntl/Sts Logic 중에 status 부분 레지스터 그림입니다.

 

 

Zynq MPSoC의 S_AXI_HP0_FPD에서 나온 데이터를 Axi_interconnect가 받아 S00_AXI 신호 DMA모듈을 거쳐 FIFO를 통해 데이터를 전송받고 S01_AXI로 결과 데이터를 받습니다.

 

DMA 루프테스트를 진행해보겠습니다.

 

우선 프로젝트를 만들어줍니다.

 

본인의 보드 칩을 선택해줍니다.

저는 하면서 오류를 많이 겪었는데 보드를 선택하니까 디버깅에서 진행이 안됐습니다.

칩을 선택해줍니다.

Add IP를 눌러서 Zynq UltraScale+ MPSoC를 불러옵니다.

 

 

더블클릭해서 

PS-PL Configuration 에서

PL(프로그래블 로직)에서 PS(프로세서 시스템)로의 IRQ0[0-7]은 Interrupt Request(인터럽트 요청) 라인의 일부

인터럽트를 위해 IRQ0[0-7] ON해줍니다

리셋기능 Fabric Reset Enable ON해줍니다

AXI HPM0 LPD (High Performance Master 0 Low Power Domain) 고성능 데이터 전송

AXI HP0 FPD (High Performance Slave 0 Full Power Domain) 데이터를 PS로 다시 전송

 

 

CLOCK은 100MHz 로 설정해줍니다.

 

 

DMA 모듈 추가하고 더블클릭 해줍니다.

 

그림과 같이 설정해줍니다.

 

AXI4-Stream Data FIFO를 불러옵니다

더블클릭합니다.

 

위의 그림과 같이 설정해줍니다.

 

Run Connection Automation 해줍니다.

 

다 연결 해줍니다.

 

그리고 fifo의 입력 S_AXIS와 dma의 M_AXIS_MM2S를 연결해주고

fifo의 M_AXIS와 dma의 S_AXIS_S2MM을 수동으로 연결해줍니다.

 

 

concat을 불러옵니다

 

수동으로 연결해줍니다.

 

다 만들었으니 

새로고침해주고

F6이나 디자인 유효한지 눌러서 확인해줍니다.

 

여기까지 온다음

디버깅을 위해 fifo의 S_AXIS, M_AXIS, axis_wr_data_count[31:0], axis_rd_data_count[31:0]

를 Debug해줍니다.

 

해주면

자동 연결을 한번 더 해주고

 

 

 

위의 ila가 생성되었는데 더블클릭해줍니다.

 

프로브수를 4개로 늘려줍니다.

 

늘려준 프로브 2개는 각각 concat에 In0과 In1으로 연결해줍니다.

 

 

HDL Wrapper 해줍니다.

저장하고 비트스트림 돌립니다.

VIVADO상 Block Design Diagram

 

 

비트스트림 파일을 VITIS에서 플랫폼으로 만든 후

 

ad7606_pss 라는 플랫폼을 만들었고 빌드해줍니다

 

 

 

platform.spr의 BSP(Board Support Package에 예제를 선택해줍니다.

 

 

 

 

/* ------------------------------------------------------------ */
/* Include File Definitions */
/* ------------------------------------------------------------ */


#include "xaxidma.h"
#include "xparameters.h"
#include "xil_printf.h"
#include "xscugic.h"


#define DMA_DEV_ID   XPAR_AXIDMA_0_DEVICE_ID
#define INT_DEVICE_ID     XPAR_SCUGIC_SINGLE_DEVICE_ID
#define INTR_ID           XPAR_FABRIC_AXI_DMA_0_S2MM_INTROUT_INTR

#define FIFO_DATABYTE   4
#define TEST_COUNT      80
#define MAX_PKT_LEN TEST_COUNT*FIFO_DATABYTE

#define TEST_START_VALUE 0xC

#define NUMBER_OF_TRANSFERS 2

/*
 * Function declaration
 */
int XAxiDma_Setup(u16 DeviceId);
static int CheckData(void);
int SetInterruptInit(XScuGic *InstancePtr, u16 IntrID, XAxiDma *XAxiDmaPtr) ;

XScuGic INST ;

XAxiDma AxiDma;

u8 TxBufferPtr[MAX_PKT_LEN] ;
u8 RxBufferPtr[MAX_PKT_LEN] ;


int main()
{
int Status;

xil_printf("\r\n--- Entering main() --- \r\n");

Status = XAxiDma_Setup(DMA_DEV_ID);

if (Status != XST_SUCCESS) {
xil_printf("XAxiDma Test Failed\r\n");
return XST_FAILURE;
}

xil_printf("Successfully Ran XAxiDma Test\r\n");

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

return XST_SUCCESS;

}



int SetInterruptInit(XScuGic *InstancePtr, u16 IntrID, XAxiDma *XAxiDmaPtr)
{

XScuGic_Config * Config ;
int Status ;

Config = XScuGic_LookupConfig(INT_DEVICE_ID) ;

Status = XScuGic_CfgInitialize(&INST, Config, Config->CpuBaseAddress) ;
if (Status != XST_SUCCESS)
return XST_FAILURE ;

Status = XScuGic_Connect(InstancePtr, IntrID,
(Xil_ExceptionHandler)CheckData,
XAxiDmaPtr) ;

if (Status != XST_SUCCESS) {
return Status;
}

XScuGic_Enable(InstancePtr, IntrID) ;

Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
InstancePtr);

Xil_ExceptionEnable();


return XST_SUCCESS ;

}


int XAxiDma_Setup(u16 DeviceId)
{
XAxiDma_Config *CfgPtr;
int Status;
int Tries = NUMBER_OF_TRANSFERS;
int Index;
u8 Value;

/* Initialize the XAxiDma device.
 */
CfgPtr = XAxiDma_LookupConfig(DeviceId);
if (!CfgPtr) {
xil_printf("No config found for %d\r\n", DeviceId);
return XST_FAILURE;
}

Status = XAxiDma_CfgInitialize(&AxiDma, CfgPtr);
if (Status != XST_SUCCESS) {
xil_printf("Initialization failed %d\r\n", Status);
return XST_FAILURE;
}

if(XAxiDma_HasSg(&AxiDma)){
xil_printf("Device configured as SG mode \r\n");
return XST_FAILURE;
}

Status = SetInterruptInit(&INST,INTR_ID, &AxiDma) ;
if (Status != XST_SUCCESS)
         return XST_FAILURE ;

/* Disable MM2S interrupt, Enable S2MM interrupt */
XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_IOC_MASK,
XAXIDMA_DEVICE_TO_DMA);
XAxiDma_IntrDisable(&AxiDma, XAXIDMA_IRQ_ALL_MASK,
XAXIDMA_DMA_TO_DEVICE);

Value = TEST_START_VALUE;

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((UINTPTR)TxBufferPtr, MAX_PKT_LEN);
Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, MAX_PKT_LEN);

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

Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) TxBufferPtr,
MAX_PKT_LEN, XAXIDMA_DMA_TO_DEVICE);

if (Status != XST_SUCCESS) {
return XST_FAILURE;
}

    Status = XAxiDma_SimpleTransfer(&AxiDma,(UINTPTR) RxBufferPtr,
MAX_PKT_LEN, XAXIDMA_DEVICE_TO_DMA);


if (Status != XST_SUCCESS) {
return XST_FAILURE;
}


while ((XAxiDma_Busy(&AxiDma,XAXIDMA_DEVICE_TO_DMA)) ||
(XAxiDma_Busy(&AxiDma,XAXIDMA_DMA_TO_DEVICE)))
{
/* Wait */
}


}

/* Test finishes successfully
 */
return XST_SUCCESS;
}


static int CheckData(void)
{
u8 *RxPacket;
int Index = 0;
u8 Value;

RxPacket = RxBufferPtr;
Value = TEST_START_VALUE;

xil_printf("Enter Interrupt\r\n");
/*Clear Interrupt*/
XAxiDma_IntrAckIrq(&AxiDma, XAXIDMA_IRQ_IOC_MASK,
XAXIDMA_DEVICE_TO_DMA) ;
/* Invalidate the DestBuffer before receiving the data, in case the
 * Data Cache is enabled
 */
Xil_DCacheInvalidateRange((UINTPTR)RxPacket, MAX_PKT_LEN);


for(Index = 0; Index < MAX_PKT_LEN; Index++) {
if (RxPacket[Index] != Value) {
xil_printf("Data error %d: %x/%x\r\n",
Index, (unsigned int)RxPacket[Index],
(unsigned int)Value);

return XST_FAILURE;
}
Value = (Value + 1) & 0xFF;
}

return XST_SUCCESS;
}

 

xaxidma_example_simple_poll.c파일에 위와 같이 코드해줍니다.

 

마찬가지로 빌드해줍니다.

 

디버그 as를 해줍니다

보드 on 상태에서 다음과 같이 디버깅화면이 나오고 재생버튼을 눌러서 트리거 신호를 기다립니다.

 

axi_dma_0_s2mm_introut 은 상승엣지할 수 있게 Value를 R로 바꿔줍니다

 

 

RESUME 버튼을 눌러주면 vivado상에서 밑의 그림과 같이 신호를 읽어옵니다.

 

 

디버깅 후 웨이브폼에서 관찰할 수 있습니다. slot0이 먼저 데이터가 나가고 그 후에 slot1이 3번에 걸쳐서 데이터를 받는 것을 확인할 수 있습니다.

 

 

확대

 

 

Sirial 통신 Comportmaster로 인터럽트 성공 메시지 확인

 

 

VITIS상 Memory 모니터 기능에서도 확인할 수 있습니다. TxBufferPtr 송신하는 데이터 메모리 확인

 

 

RxBufferPtr 수신하는 데이터 메모리 확인

 

4바이트씩 80번 보내므로 320바이트의 데이터가 전송되며 

전송된 데이터는 위에 보이는 것처럼 0F0E0D0C -> 13121110 모든 자리에서 4씩 차이가 나서 전체적으로 4바이트 차이가 발생하는 것을 확인할 수 있습니다.