AXI DMA研究 - minichao9901/TangNano-20k-Zynq-7020 GitHub Wiki

ILA抓取的stream端的波形

mm2s

image image image image

可以清楚看到,读memory期间,一次性读了256个bytes,共64个word。在整个读数据期间,tvalid一直有效,读完之后tlast信号变为1,产生了mm2s_introut中断信号。

s2mm

用xsct分析dma的寄存器配置情况

image

对比寄存器手册知道:

MM2S_DMA_CR的RS=1, IOC_IrqEn=1, Dly_IrqEn=1, Err_IrqEn=1。 IRQThreshold=0x1(默认值) MM2S_SA=0x12000000 S2MM_DMA_CR的RS=1, IOC_IrqEn=1, Dly_IrqEn=1, Err_IrqEn=1。 IRQThreshold=0x1(默认值) S2MM_SA=0x14000000

源寄存器和目的寄存器的内容:

image image

从这里看,src的内容没有被正确写入dest,因此print failed了。后来发现,如果我们把ILA去掉就可以正确读写了。看样子ILA影响了数据传输。原因不清楚。

搞懂了dma的寄存器后,我们明白了更简单的驱动方法

1)中断不是必须的,直接用下面2句话就可以驱动dma传输。这种方式可以采用轮询的方法等待标志位。

XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) TxBufferPtr,
			256, XAXIDMA_DMA_TO_DEVICE);

XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) RxBufferPtr,
			256, XAXIDMA_DEVICE_TO_DMA);
usleep(1000);

Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, 1024);

2)可以直接用寄存器搞,也非常简单

u32 base=0x40400000;
Xil_Out32(base, 0x00010003);
Xil_Out32(base+0x30, 0x00010003);
Xil_Out32(base+0x18, TX_BUFFER_BASE);
Xil_Out32(base+0x48, RX_BUFFER_BASE);

Xil_Out32(base+0x28, 1024);
Xil_Out32(base+0x58, 1024);
sleep(1);


Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, 1024);

3) xsct加断点或者读取寄存器非常方便

connect
tar
tar 2
bpadd -file main.c -line 65
mrd 0x40400000 20
mrd 0x12000000 64
mrd 0x14000000 64
nxt 10000

4)关于小梅哥dma读取数量超过256的错误

经过xsct分析后,我们才知道,小梅哥的程序读写数量>256会报错。修改后,结果正确。

//如果发生错误,则打印失败提示
if (Error) {
	printf("DMA Transfer Failed!\n");
} else {
	//测试完成,检查数据
	for(Index = 0; Index < 1024; Index++)
	{
                //原始程序是这一句:if(RxBufferPtr[Index] != Index) {
                //导致当Index数据量>256时,比较结果会出错。实际内存是对的。原因不清楚。
		if(RxBufferPtr[Index] != TxBufferPtr[Index]) {
			Error_Cnt++;
		}
	}
	if(Error_Cnt > 0)
		printf("Test Failed! %d\n", Error_Cnt);
	else
		printf("Test Successfully!\n");
}

return 0;
}

5)后来原因搞清楚了

  • 与TxBuffer, RxBuffer数组的定义类型有关系。DMA传输和CacheClean操作,都是以bytes为单位。长度上要留意,要匹配。
  • 如果TxBuffer, RxBuffer数组是u8类型,那么DMA传输和CacheClean操作的长度与TxBuffer, RxBuffer数组保持一致即可。
  • 如果TxBuffer, RxBuffer数组是u16类型,那么DMA传输和CacheClean操作的长度是TxBuffer, RxBuffer数组长度的2倍。
  • 如果TxBuffer, RxBuffer数组是u32类型,那么DMA传输和CacheClean操作的长度是TxBuffer, RxBuffer数组长度的4倍。

u32类型

	uint32_t *TxBufferPtr;		//传输数据的指针
	uint32_t *RxBufferPtr;		//接收数据的指针

	//将指针TxBufferPtr指向TX_BUFFER_BASE
	TxBufferPtr = (uint32_t *)TX_BUFFER_BASE;
	//将指针RxBufferPtr指向RX_BUFFER_BASE
	RxBufferPtr = (uint32_t *)RX_BUFFER_BASE;

	//初始化AXI DMA
	AXI_DMA_Init(&AxiDma0, XPAR_AXIDMA_0_DEVICE_ID);

	//给TxBufferPtr赋值为0~255
	for(Index = 0; Index < 1024; Index ++) {
		TxBufferPtr[Index] = Index;
	}

	//在DMA传输之前刷新Buffer
	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, 1024*4);

	//开启数据传输
	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) TxBufferPtr,
				1024*4, XAXIDMA_DMA_TO_DEVICE);

	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) RxBufferPtr,
				1024*4, XAXIDMA_DEVICE_TO_DMA);

	sleep(1);

	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, 1024*4);

u16类型

	uint16_t *TxBufferPtr;		//传输数据的指针
	uint16_t *RxBufferPtr;		//接收数据的指针

	//将指针TxBufferPtr指向TX_BUFFER_BASE
	TxBufferPtr = (uint16_t *)TX_BUFFER_BASE;
	//将指针RxBufferPtr指向RX_BUFFER_BASE
	RxBufferPtr = (uint16_t *)RX_BUFFER_BASE;

	//初始化AXI DMA
	AXI_DMA_Init(&AxiDma0, XPAR_AXIDMA_0_DEVICE_ID);

	//给TxBufferPtr赋值为0~255
	for(Index = 0; Index < 1024; Index ++) {
		TxBufferPtr[Index] = Index;
	}

	//在DMA传输之前刷新Buffer
	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, 1024*2);

	//开启数据传输
	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) TxBufferPtr,
				1024*2, XAXIDMA_DMA_TO_DEVICE);

	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) RxBufferPtr,
				1024*2, XAXIDMA_DEVICE_TO_DMA);

	sleep(1);

	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, 1024*2);

u8类型

	uint8_t *TxBufferPtr;		//传输数据的指针
	uint8_t *RxBufferPtr;		//接收数据的指针

	//将指针TxBufferPtr指向TX_BUFFER_BASE
	TxBufferPtr = (uint8_t *)TX_BUFFER_BASE;
	//将指针RxBufferPtr指向RX_BUFFER_BASE
	RxBufferPtr = (uint8_t *)RX_BUFFER_BASE;

	//初始化AXI DMA
	AXI_DMA_Init(&AxiDma0, XPAR_AXIDMA_0_DEVICE_ID);

	//给TxBufferPtr赋值为0~255
	for(Index = 0; Index < 1024; Index ++) {
		TxBufferPtr[Index] = Index;
	}

	//在DMA传输之前刷新Buffer
	Xil_DCacheFlushRange((UINTPTR)TxBufferPtr, 1024);

	//开启数据传输
	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) TxBufferPtr,
				1024, XAXIDMA_DMA_TO_DEVICE);

	XAxiDma_SimpleTransfer(&AxiDma0,(UINTPTR) RxBufferPtr,
				1024, XAXIDMA_DEVICE_TO_DMA);

	sleep(1);

	Xil_DCacheFlushRange((UINTPTR)RxBufferPtr, 1024);