Io

訪問記憶體映射 I/O 很慢

  • November 3, 2015

我有一個 Terasic-SoCKIT(fpga & arm cortex a9),並且我在 HPS 上執行了 Linux。我正在嘗試訪問記憶體映射 I/O,編寫了一個帶有“request_mem_region”和“ioremap”功能的簡單字元驅動程序。

記憶體映射 IO 是 AXI 匯流排,我可以使用它向 FPGA 傳輸數據。我看到每次寫入幾乎需要 6us,對於我的應用程序,我需要它小於 1us。此外,驅動程序在幾次寫入後停止寫入映射的 IO(看不到 fpga 中正在更改的數據;驅動程序中的緩衝區是否已滿??)。

問題是我錯過了什麼,還是因為寫入是從虛擬地址到物理地址的,它不能再快了?如果從虛擬地址寫入速度變慢,有沒有辦法加快速度?我知道,ARM 有一個 DMAC,但我還沒有探索它。

謝謝你,卡提克

對不起,我錯過了我正在測量使用者空間程式碼中的時間。後來,我檢查了寫入驅動程序所需的時間,以納秒為單位。所以,我認為大部分時間都是從使用者空間寫入核心。

所以,我做了一些進一步的閱讀並理解了 ioremap() 將物理地址映射到核心虛擬地址,並且 remap_pfn_range() 將物理地址/IO記憶體映射到使用者虛擬空間(這是我需要的;從使用者寫入 IO 記憶體空間)。我使用了簡單的 mmap 範例 - http://web.cecs.pdx.edu/~jrb/ui/linux/examples.dir/simple/simple.c作為核心驅動程序。以下程式碼是我的使用者空間程式碼:

   using namespace std;
   #include <iostream>
   #include <sys/mman.h>
   #include <sys/types.h>
   #include <sys/stat.h>
   #include <sys/mman.h>
   #include <fcntl.h>
   #include <stdio.h>
   #include <stdlib.h>
   #include <unistd.h>
   #include <stdint.h>
   #include <ctime>


   #define PAGE_SIZE 4096
   #define HPS2FPGA_BRIDGE_BASE 0xc0000000
   #define BLINK_OFFSET 0x0

   volatile unsigned char *blink_mem;
   void *bridge_map;

   int main()
   {
       int fd, ret = EXIT_FAILURE;
       unsigned int i;
       unsigned char value;
       int dummy;
       off_t blink_base = HPS2FPGA_BRIDGE_BASE;
       clock_t start, stop;
       double duration;

       /* open the memory device file */
       fd = open("/dev/HPS2FPGA", O_RDWR|O_SYNC);
       if (fd < 0) {
           perror("open");
           exit(EXIT_FAILURE);
       }

       /* map the LWHPS2FPGA bridge into process memory */
       bridge_map = mmap(NULL, PAGE_SIZE, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_SHARED,
                   fd, blink_base);
       if (bridge_map == MAP_FAILED) {
           perror("mmap");
           goto cleanup;
       }


       /* get the delay_ctrl peripheral's base address */
       blink_mem = (unsigned char *) (bridge_map + BLINK_OFFSET);

       start = clock();
       /* write the value */
       for(i = 0; i < 1000000; i++)
       {
           *blink_mem = i;
           dummy = *blink_mem;
       }
       stop = clock();
       duration = ( stop - start ) / (double) CLOCKS_PER_SEC;
       printf("%f", duration);

       if (munmap(bridge_map, PAGE_SIZE) < 0) {
           perror("munmap");
           goto cleanup;
       }

       ret = 0;

   cleanup:
       close(fd);
       return ret;
   }

我正在寫入虛擬地址空間 mmap 返回,我可以通過讀取該地址處的值來驗證寫入,但我沒有看到 FPGA 中的值得到更新。

當我寫入使用者虛擬空間時,如何寫入物理地址?有沒有辦法調試並查看物理地址空間是否實際被寫入?

好的,看起來這個問題的主題是一個毛茸茸的……記憶體映射I / O(正確完成)將與處理器可以為正在訪問的硬體一樣快,並且這樣做不會產生任何成本使用者模式而不是核心模式(即沒有“從使用者空間寫入核心”)。

但是,您仍然必須考慮讀取或寫入地址時發生的情況(這是問題轉移到的地方)。在大多數架構中,有兩種​​映射——虛擬到物理,以及物理到設備。第一個設置在虛擬記憶體硬體中,第二個設置在記憶體控制器中。

除了映射之外,所有訪問通常都通過記憶體硬體進行,因此您必須決定是否要記憶體訪問。如果被訪問的底層設備是某種 RAM,那麼您通常希望訪問被記憶體。對於其他類型的設備,通常不需要。

可能還有很多其他的事情需要考慮(例如,VM 映射是否駐留在 VM 硬體中、訪問的寬度和時間、優先級、權限等),但記憶體是第一位的。

在@Karthik的情況下,因為他沒有在他的映射中關閉記憶體,取決於記憶體的類型,當他寫入地址(直寫)時正在寫入整個記憶體,或者寫入被延遲(回寫)(如果你想了解一些關於記憶體的細節,試試這個)。

為了回答具體的(後續)問題,一旦虛擬地址映射完成並且記憶體完成了它的工作,訪問就轉到記憶體控制器——這個硬體決定訪問哪個匯流排和/或設備,並執行“正確硬體的事情”,通常涉及斷言晶片選擇和/或寫使能信號,可能將部分或全部物理地址複製到地址線上,可能是一些設置時序等。

…調試這些東西的最佳方法是將某種分析儀連接到您的設備或匯流排,或者如果這太困難/太昂貴,可能會支持在記憶體控制器中進行調試。

另一個次要但重要的一點……注意blink_mem上面程式碼中的聲明 - volatile類型限定符非常重要。它告訴編譯器不要對地址進行訪問。除此之外,您應該了解與記憶體訪問有關的任何特殊管道指令(查看 powerpc 中的eieio指令 - 有人有幽默感 :-)

最後,只是為了重申評論中所說的內容,結果證明這是問題的真正答案,當呼叫remap_pfn_range()時,您可以通過修改最後一個參數 ( prot ) 中指定的頁面保護來關閉記憶體,使用**pgprot_noncached()**宏。另請閱讀thisthis,尤其是this。乾杯!

引用自:https://unix.stackexchange.com/questions/237783