Zynq7000 PS DMA "Done" 信号太快

Zynq7000 PS DMA "Done" Signals Too Soon

TL;DR:Zynq7000 PS 内置 DMA returns "Done" 信号太快了。它似乎会在(我假设)填满其内部 "MFIFO" 并且不再需要访问数据源时立即发出信号。但是我的软件也需要知道它什么时候真正完成了数据传输。

PSDMA是否有状态位指示传输是否完成? Xilinx 文档中的一些 DMA 寄存器不清楚 (http://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf,第 276 页)

我正在使用 DMA 将一大块数据从 DDR 内存泵送到 PL IP,使用以下 C 代码:

// Allocate memory in DDR, 1600 bytes PLZ.
char *mem_block = malloc(1600*sizeof(char));

// Fill memory with data (not shown)

// Configure the DMA command
memset(&dmaCmd, 0, sizeof(XDmaPS_Cmd));
dmaCmd.ChanCtrl.SrcBurstSize = 1;
dmaCmd.ChanCtrl.SrcBurstLen = 4;
dmaCmd.ChanCtrl.SrcInc = 1;
dmaCmd.ChanCtrl.DstBurstSize = 1;
dmaCmd.ChanCtrl.DstBurstLen = 4;
dmaCmd.ChanCtrl.DstInc = 0; // Do not increment, (Fixed DST Addr.)
dmaCmd.BD.SrcAddr = (u32)mem_block;
dmaCmd.BD.DstAddr = (u32)0x43c10000; // Destination address (in PL)
dmaCmd.BD.Length  = 1600; // Bytes

然后我开始 DMA 传输...

status = XDmaPs_Start(&dmaInst, 0, &dmaCmd, 0);
if (status != XST_SUCCESS) {
    printf("ERROR, could not start DMA Txfer.");
    return XST_FAILURE;
}

等待它完成,然后从我的 IP 读回结果。

while (!(Xil_In32(XPAR_PS7_DMA_S_BASEADDR + XDMAPS_INTSTATUS_OFFSET) & 0x00000001)) {
   i++; // Waiting for DMA to finish...
}
result = Xil_In32(0x43c10004); // read result from my IP 
// Result is available as soon as DMA is finished--on the very next 100MHz fabric clock.

我遇到的问题是,在数据传输完成之前,经常过早地读回此结果。显然,DMA 有时会在 DMA 传输仍在进行时说它已完成(我可以说是因为,从芯片示波器,我可以看到在写入突发仍在发生时发生读取)。

如何设置我的软件以等待 DMA 实际上 完成数据传输?

文档伪装得很好,但我找到了我要找的东西。

当我输入这个问题时,我意识到那些 DMA 通道寄存器中必须有一些状态位。结果,实际上每个通道都有一个通道状态寄存器,其低 4 位代表一个状态频道的。 (我不得不深入研究他们文档的附录 B 才能找到它,即问题中链接的文档的第 1201 页)。当 DMA 已触发 DONE 但仍在传输数据时,此 4 位状态为 1001 ("Completing"),并在通道实际完成时转到 0000 ("Stopped")。

Xilinx 甚至在其自动生成的 BSP 中提供了一个方便的宏来获取每个通道的状态寄存器地址的偏移量:XDmaPs_CSn_OFFSET(n) 其中 n 是通道号--在我的例子中是 0。

我的修复只是更改我的 while 循环以检查通道状态以转到 'Stopped',表示传输已完成。

while (!(Xil_In32(XPAR_PS_DMA_S_BASEADDR + XDmaPs_CSn_OFFSET(0) & 0x0000000F) != 0x00000000) {
    // Wait until done
    i++;
}
// Now DMA is truly, really done transferring data.
// get real result
result = Xil_In32(0x43C10004); // Read from my IP

希望这可以节省一些时间和挫败感! :D