Zynq Cache问题的解决方法

mac2024-03-17  31

原文转自:http://www.openhw.org/module/forum/thread-546879-1-1.html

在进行PS-PL之间的DMA传输时,不可避免会遇到Cache问题。今天在这里讲一下Cache的解决方法。其中参考了forums.xilinx.com的处理方法。 首先解释为什么DMA会引入Cache问题(专业名称为Cache一致性问题)。 PS和PL都在独立运行,PS通过DDR控制器来对DDR存储器进行访问,为了加速,常常将一些数据缓存(Cache),而且不是针对一个数据缓存,而是针对一批(Xilinx称为一行,即Line,一行长度为32)。这样好处很明显,下一次访问速度会加快;但坏处也很明显,就是Cache里的数据如果发生了改变,不能迅速反映到DDR2实际数据中,反之亦然。因此,当PL通过DMA修改了DDR2数据时,CPU可能还不知道发生了些什么,拿到的数据仍然是Cache中的没有改过的数据。 在裸机开发时,规避Cache最简单的方法就是禁用Cache。 #include "xil_cache.h" void Xil_DCacheDisable(void); 这样操作后,CPU将直接访问DDR内存,读写都是直接的。这样显然会降低CPU性能,但简化了数据传输操作,属于极端的方法。   另外一种操作要多加一道手续,在我的文章【参赛手记】详细介绍AXI-HP接口+DMA+GIC编程中,给出的例程里有Cache Flush和Cache Invalidate操作。从字面理解,Flush就是把Cache里的数据流放出去,清空Cache,也就是将Cache的内容推到DDR中去;而Cache Invalidate表示当场宣布Cache里的内容无效,需要从DDR中重新加载,即把数据从DDR中拉到Cache中来。 理解了这个原理,在编程的时候心里就非常有底气了! #include "xil_cache.h" //写点什么到发送缓冲区sendram  Xil_DCacheFlushRange((u32)sendram,sizeofbuffer);//将内容刷新至DDR //启动发送DMA过程。。。。   //启动接收DMA过程。。。。。 Xil_DCacheInvalidateRange((u32)recvram,sizeofbuffer);//将DDR内容拉进Cache //从recvram中读取数据吧!     好了,裸机工程下面开发是非常简单的,出错了也容易定位,调试起来方便。在Linux下开发时,由于编写的接口模块处于驱动层面,调试可能不如SDK中那么直观,只能关键部位打印printk,然后慢慢去寻找错误,定位比较麻烦。 在Linux下,Cache的Flush和Invalidate操作需要调用内核函数 dma_sync_single_for_device和 dma_sync_single_for_cpu。 这两个函数包含时可以这样: #include  其代码可以在内核源码的/arch/arm/include/asm中看到。 同样,在驱动程序中,涉及到DMA操作时,也需要在DMA写之前先Flush,DMA读之后Invalidate操作。 其参数为:第一个参数是device结构体,第二个参数为DMA的实际地址,需要通过虚拟地址到实际地址的映射才能实现(这是Linux的本身特点),第三个参数为方向,可以选择DMA_TO_DEVICE或 DMA_FROM_DEVICE(需要包含头文件#include )。

具体驱动代码如下: #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #include   //use this for dma_sync_single_for_cpu #include    //use this for DMA_FROM_DEVICE #define DEVICE_NAME "AXI_DMA_DEV" #define MAX_LEN 8 #define WITH_SG 0 #define AXI_DMA_BASE 0x40440000 #define MM2S_DMACR 0 #define MM2S_DMASR 1 #if WITH_SG #define MM2S_CURDESC 2 #define MM2S_TAILDESC 4 #else #define MM2S_SA 6 #define MM2S_LENGTH 10 #endif #define S2MM_DMACR 12 #define S2MM_DMASR 13 #if WITH_SG #define S2MM_CURDESC 14 #define S2MM_TAILDESC 16 #else #define S2MM_DA 18 #define S2MM_LENGTH 22 #endif   static void __iomem* Regs; int *sendbuffer; int *recvbuffer; static int axi_dma_open(struct inode*inode,struct file*filp) {  return 0; } static int axi_dma_release(struct inode*inode,struct file *filp) {  return 0; } static int axi_dma_ioctl(struct file*filp,unsigned int reg_num,unsigned long arg) {  if((reg_num==MM2S_DMACR)||(reg_num==MM2S_DMASR)||(reg_num==MM2S_SA)||(reg_num==MM2S_LENGTH)||(reg_num==S2MM_DMACR)||(reg_num==S2MM_DMASR)||(reg_num==S2MM_DA)||(reg_num==S2MM_LENGTH))  {   iowrite32(arg,Regs+reg_num*4);   printk("axi_dma:Write data 0x%x to address 0x%x!\n",arg,Regs+reg_num*4);  }  else  {   printk("axi_dma:[ERROR] Wrong register number!\n");   return -EINVAL;  }  return 0; } static int axi_dma_read(struct file*filp,char *buffer,size_t length,loff_t * offset) {  int bytes_read = 0;  int i = 0;  if(filp->f_flags&O_NONBLOCK)  {   return -EAGAIN;  }  if((length>0)&&(length

 

最新回复(0)