Zynq Cache問題的解決方法


原文轉自: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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM