web-dev-qa-db-ja.com

メモリマップI / Oへのアクセスが遅い

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で値が更新されません。

ユーザー仮想空間に書き込むときに、物理アドレスはどのように書き込まれますか?物理アドレス空間が実際に書き込まれているかどうかをデバッグして確認する方法はありますか?

6
Karthik

OK、この質問の件名は furphy ...のようです。メモリマップI/O(正しく行われます)は、アクセスするハードウェアに対してプロセッサが実行できるのと同じ速さで、カーネルモードとは対照的に、ユーザーモードからこれを実行するためのオーバーヘッドがない(つまり、「ユーザー空間からカーネルへの書き込み」がない)。

ただし、アドレスの読み取りまたは書き込みを行うときに何が起こっているのかを考える必要があります(ここから質問に移ります)。ほとんどのアーキテクチャでは、仮想から物理へのマッピングと、物理からデバイスへのマッピングという2つのマッピングがあります。 1つ目は仮想メモリハードウェアでセットアップされ、2つ目はメモリコントローラでセットアップされます。

マッピングに加えて、すべてのアクセスは通常 cache ハードウェアを経由するため、アクセスをキャッシュするかどうかを決定する必要があります。アクセスされている基になるデバイスがRAM=ある種の場合)は、通常、アクセスをキャッシュする必要があります。他のタイプのデバイスの場合、通常はキャッシュしません。

(VMマッピングがVMハードウェアに常駐するかどうか、アクセスの幅とタイミング、優先度など) 、権限など)がキャッシュが最初です。

@Karthikの場合、彼はマッピングでキャッシュをオフにしなかったため、キャッシュのタイプに応じて、キャッシュ全体 line が書き込まれていました彼がアドレスに書き込んだとき(ライトスルー)、または書き込みが遅延していたとき(ライトバック)(キャッシュについて細かい点が必要な場合は、 this を試してください)。

特定の(フォローアップ)質問に答えるために、仮想アドレスマッピングが行われ、キャッシュがその仕事を終えると、アクセスはメモリコントローラーに行きます-このハードウェアは、アクセスされているバスやデバイスを決定し、通常はチップセレクトおよび/またはwrite enable信号をアサートし、一部またはすべてをコピーします。 物理的なアドレスのアドレス行、おそらくいくつかのセットアップタイミングなど.

...そしてこれをデバッグする最良の方法は、デバイスまたはバスに接続された何らかのアナライザーを接続することです。または、これが難しすぎる/費用がかかる場合は、メモリコントローラーでのデバッグのサポート。

もう1つのマイナーだが重要なポイント...上記のコードでのblink_memの宣言に注意してください。volatile型修飾子は非常に重要です。これは、アドレスへのアクセスに干渉しないようにコンパイラーに指示します。これに加えて、メモリアクセスに関連する特別なパイプライン命令に注意する必要があります( eieio 命令を確認してくださいpowerpc-誰かユーモアのセンスがあります:-)

最後に、 remap_pfn_range() を呼び出すときに、コメントの真実の答えであることが判明したコメントで述べられた内容を繰り返すために、あなたはoff最後の引数(prot)で指定されたページ保護をpgprot_noncached() 大きい。 this および this 、特に this もお読みください。乾杯!

3
Murray Jensen