一、Data Cache与DMA一致性问题
在ZYNQ的PS侧存在Cache,CPU与DDR之间通过Cache进行交互,数据暂存在Data cache中,在处理器对DDR进行写数据操作时,如果不将数据通过Cache送入DDR,DDR中的数据不会变化。
在进行DMA操作时,如果没有对Cache进行适当的操作,可能导致以下两种错误:
1、DMA从外设读取数据供处理器使用,DMA将外部数据直接传到内存中,但是Cache中保留的是旧数据,这样处理器在访问数据时直接访问缓冲将得到错误的数据。
2、DMA向外设写入由处理器(Cortex-A9)提供的数据,处理器在处理数据时会先将数据存放到Cache中,此时Cache中可能还有没来得及写到内存中的数据,这时如果DMA直接从内存中取出数据传送到外设,外设可能得到错误的数据。
将两种错误翻译一下就是说:
(1)、当AXI_DMA从DDR中读取读取数据时,处理器写给DDR的数据可能还没进入到DDR,停留在Cache上,DMA读取DDR时就会得到错误的数据。
(2)、当处理器(Cortex-A9)向DDR写数据时候,DMA将数据写入到DDR时,此时处理器读取到的DDR数据可能是错误的(或者是上一次的),因为cache此时可能还有其他进程残留的数据,这样处理器读取到的数据就是错误的。
为了正确进行DMA传输,要进行必要的Cache操作,Cache操作主要分为invalidate(作废)和writeback(写回),有时也将两者一起使用。
在DMA使用Cache时,根据DMA缓冲区期望保留的时间长短来决策。DMA的映射就分为:一致性DMA映射和流式DMA映射。
一致性DMA映射申请的缓存区能够使用Cache,并且保持cache一致性。一致性映射具有很长的生命周期,在这段时间内占用的映射寄存器,即使不使用也不会被释放,生命周期为该驱动的生命周期。
所以,分析SDK中DMA使用部分常用的一句代码:
Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);
关于该函数的官方介绍为:
Flush the Datacache for the given address range.If the bytes specified by the address rangeare cached by the data cache, the cachelines containing those bytes areinvalidated.
If thecachelines are modified (dirty), they are written to the system memory beforethe lines are invalidated.
也就是说,在将数据发送到DMA之前,先将要传输数据的起始地址和传输的范围长度通过该函数设置,以该地址为起始地址,共最大传输长度个数据就会从Cache刷入DDR中,专门用于向DMA发送数据,即使在这之前Cache上的这部分区域有数据,这些原本的字节缓冲也会失效,有效的保证了数据传输的有效性。
基于以上分析之后,分析一下SDK中常用的XAxiDma_SimpleTransfer()函数。
该函数的原型为:
u32 XAxiDma_SimpleTransfer(XAxiDma *InstancePtr, UINTPTR BuffAddr, u32 Length, int Direction)
该函数的传入参数为:
@ XAxiDma *InstancePtr:实例地址
这个参数的作用是用于查询该DMA是否处于空闲状态,如果是busy的话就不能传输,或者是查询该DMA是否配置成SG模式,如果是SG模式的话也不能传输。第一个查询很好理解,busy时肯定不能传输,第二个查询不能传输的原因是SG模式,数据传输是一种链表的形式,不满足传入参数中,一个起始地址+传输长度即可完成全部传输的形式。
@ UINTPTR BuffAddr:传输的数据起始地址
@ u32 Length:传输的数据个数
@ int Direction:数据传输的方向,即是将数据通过DMA发送到外设,宏定义:(XAXIDMA_DMA_TO_DEVICE),还是将数据从外设通过DMA接收,宏定义:(XAXIDMA_DEVICE_TO_DMA)。
所以,分析以下代码:
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 DMAtransfer, 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;
}
在处理器中对要写入DDR的数据通过数组的方式进行赋值,赋值结束后数值并未进入DDR中,需要通过Cache刷入,这个可以从架构中看到:
这也就是该代码的含义:
Xil_DCacheFlushRange((u32)TxBufferPtr, MAX_PKT_LEN);
将数据刷入DDR之后,DMA就可以直接从DDR中发送缓冲区的起始地址处读取测试长度个(MAX_PKT_LEN)数据。
在SDK断点调试时候,对数据数组赋值结束后,从内存监控器memory monitor中即可看到数据已经发生了变化,但是实际上数据并未真正的进入DDR中,是因为memory monitor上显示的数据是数据Cache中的数据。