SOCを上げる:ARM + FPGA





先日、 EBV SoCrates評価ボードが私の手に落ちました。 簡単に言えば、これはアルテラの SoC搭載したボードで、デュアルコアARMおよびCyclone V FPGAが搭載されています。



同じチップ上のARMFPGA-非常に興味深いはずです! しかし、そもそも、このすべての善は「高められる」必要があります。

このプロセスについては、この記事で説明します。



これまたは同様のボードを手に入れて、それをどうするか完全にわからない場合。 FPGAが複雑なものであり、これにアプローチする方法が明確でないと常に考えている場合。 または、あなたは単に好奇心engineer盛なエンジニアですか? それから入って 私たちはすべてに満足しています。



ちょっとしたボーナスとして、 CPUFPGA間の帯域幅を測定します。



作業計画



私たちの計画は、次の項目で構成されています。



行こう!



FPGAファームウェアの作成



まず、FPGAファームウェアを入手する必要があります。

このツールにはCAD Quartusが必要になります。 公式Webサイトからダウンロードできます。

私はインストールについて説明しません-すべてがそこにかなり明白です。



プロジェクト作成



Quartusを実行し、 [ファイル]-> [新しいプロジェクトウィザード ]に移動して、[ 次へ ]をクリックし、ディレクトリとプロジェクト名を入力します。

プロジェクト名






次のページをスキップし、FPGAのファミリとタイプを選択します。

FPGAの選択






残りの設定は重要ではありません。[ 完了 ]をクリックします。



Qsysプロジェクト



Qsysは初心者向けの優れたツールです。 コードを1行も書かずにファームウェアを取得できます。 代わりに、開発者は事前定義されたキューブ(IPピール)からコンストラクターを組み立てます。 各ピールを適切に設定し、適切に接続するだけです。



したがって、 ツール-> Qsysでは、左側のウィンドウ( IPカタログ )に2つのIPピールが必要です。



ハードプロセッサシステム(HPS)は当社のARMです。 その設定から始めます。



最初のタブでは、CPUから内部FPGAメモリにアクセスできるように、 HPSからFPGAへのインターフェイス幅に関心があります。

FPGAインターフェース






次に、さまざまなインターフェースの設定があります-どのモードで動作するか、どのピンが使用されるか:

周辺ピン






次のタブは細断処理の設定です。 入力クロックでは 、すべてを変更しません。

入力クロック






[ 出力クロック]で、[ HPS-to-FPGAユーザー0クロックを有効にする]をオンにします。

出力クロック






次に、DDR3メモリのさまざまな設定を含む大きなサブセクションがあります。

DDR3 PHY設定






DDR3メモリパラメータ






DDR3メモリタイミング






DDR3ボード設定






HPSを使用して、 オンチップメモリの構成に進みました。 これは、FPGA内に直接配置されているメモリです。

設定がはるかに少ない:

オンチップメモリ






次に、ブロックを接続する必要があります。 すべてが非常に直感的です( s1の反対側のベースアドレスの値に注意してください):

Qsys接続






できた socとして保存( ファイル->保存



ファイルを生成するために残ります。 [ HDLの生成 ]ボタンをクリックし、表示されるウィンドウで、[ 生成 ] もう一度クリックして、待機し、[ 完了 ] クリックします。



プロジェクトのコンパイル



次に、生成されたファイルをプロジェクトに追加する必要があります。

[割り当て ] -> [設定 ]タブ[ ファイル ]で、ファイルsoc / Synthesis / soc.qipを追加します。



DDRピンの設定を適用する必要があります。 ただし、その前に、コンパイルの最初の段階を実行する必要があります。

処理->開始->分析と合成の開始



スクリプトを実行してピンを構成します。

ツール-> Tclスクリプト 表示されるウィンドウで、[ プロジェクト]-> [soc]-> [合成]-> [サブモジュール]-> [hps_sdram_p0_pin_assignments.tcl] 、[ 実行]を選択します



プロジェクトの最終コンパイル:

処理->コンパイルの開始



FPGAファームウェアを含むsoc.sofファイルを取得しました。 しかし、FPGAをCPUから直接フラッシュしたいので、別のフォーマットが必要です。 変換しましょう。 これはGUIから実行できますが、コンソールの方が簡単です。 とにかく、GUIから抜け出す時間です:)。



変換するには、ターミナルを実行し、プロジェクトのあるディレクトリに移動する必要があります。 次に、 output_filesに移動してコマンドを実行します(Quartusユーティリティを含むディレクトリがPATH変数に存在する必要があることを忘れないでください)。

quartus_cpf -c soc.sof soc.rbf
      
      





やった! FPGAファームウェアを入手しました。



カーネルアセンブリ



それでは、ARMのカーネルをビルドしましょう。

ツールのうち、 アルテラSoC EDSが必要になります。 ここから、クロスコンパイルにarm-linux-gnueabihf-コンパイラーを使用します。



カーネルをポンプアウトします。

 git clone https://github.com/coliby/terasic_MTL.git
      
      





スクリプトを起動し、コンパイラディレクトリをPATHに追加してbashを実行します。

 /opt/altera/quartus14.0/embedded/embedded_command_shell.sh
      
      





環境変数を設定します。

 export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- export LOADADDR=0x8000
      
      





カーネルがあるディレクトリに移動し、構成を実行します。

 cd terasic_MTL/ make socfpga_defconfig
      
      





U-Boot用のカーネルイメージをビルドします。

 make -j 4 uImage
      
      





次に、いわゆる.dtb (デバイスツリーBLOB)ファイルを取得する必要があります。 これは、プラットフォームに関する情報(インターフェイス、ピン、クロック信号、アドレス空間など)を含むバイナリファイルです。 カーネルは初期化中にこのファイルを読み取り、変更します。 これにより、複数のハードウェアプラットフォームで1つのアセンブルされたコアを使用できます。

したがって、 .dtbファイルを取得します。

 make socfpga_cyclone5.dtb
      
      





ただし、このファイルはプラットフォーム用ではないため、少し変更する必要があります。 これを行うには、ファイルをテキスト形式の.dts (デバイスツリーソース)に変換します。

 ./scripts/dtc/dtc -I dtb -O dts -o soc.dts arch/arm/boot/dts/socfpga_cyclone5.dtb
      
      





ここで、 soc.dtsで bridge @ 0xff200000ブロックを削除する必要があります 。 これは、手動またはパッチを適用することで実行できます。

 patch soc.dts dts.patch
      
      





dts.patch
 942,966d941 < bridge@0xff200000 { < compatible = "altr,h2f_lw_bridge-1.0", "simple-bus"; < reg = <0xff200000 0x200000>; < #address-cells = <0x1>; < #size-cells = <0x1>; < ranges = <0x200 0xff200200 0x80 0x100 0xff200100 0x80>; < < tsc@0x200 { < compatible = "terasic,mlt_touch_screen"; < reg = <0x200 0x80>; < width_pixel = <0x320>; < height_pixel = <0x1e0>; < interrupts = <0x0 0x28 0x4>; < }; < < vip2@0x100 { < compatible = "ALTR,vip-frame-reader-13.0", "ALTR,vip-frame-reader-9.1"; < reg = <0x100 0x80>; < max-width = <0x320>; < max-height = <0x1e0>; < mem-word-width = <0x100>; < bits-per-color = <0x8>; < }; < }; <
      
      





ファイルを変換して.dtbに戻します。

 ./scripts/dtc/dtc -I dts -O dtb -o soc.dtb soc.dts
      
      





合計で、2つのファイルに興味があります。





U-Bootとプリローダーを構築する



SoCの起動プロセスは次のとおりです。

  1. ブートROM
  2. プリローダー
  3. ブートローダー
  4. OS


ブートROMは、電源投入直後に実行される最初のブートフェーズです。 その主な機能は、第2段階であるPreloaderを特定して完了することです。



プリローダー機能は、ほとんどの場合、 SDRAMインターフェイスの初期化とHPSピンの構成です。 SDRAMの初期化では、コードが利用可能な内部メモリの60 KBに収まらない可能性があるため、次のステージを外部メモリからロードできます。



ブートローダーHPSのさらなる初期化に参加できます。 また、このステージはオペレーティングシステムまたはユーザーアプリケーションをロードします。 通常(そして私たちの場合)、 U-BootBootloaderとして機能します。



OS-すべてがシンプルです。 これが私たちのお気に入りのLinuxです。 カーネルはすでにあります。ルートファイルシステムは少し後で取得します。

次にプリローダーU-Bootを扱います



ターミナルを開き、既知のスクリプトを実行します。

 /opt/altera/quartus14.0/embedded/embedded_command_shell.sh
      
      





プロジェクトのあるディレクトリに移動します。

 cd ~/src/soc_test/
      
      





コンパイル後、 hps_isw_handoffディレクトリがそこに表示されるはずです。

 cd hps_isw_handoff
      
      





必要なファイルの生成を開始します。

 bsp-create-settings --type spl --bsp-dir build --preloader-settings-dir soc_hps_0 --settings build/settings.bsp --set spl.boot.WATCHDOG_ENABLE false
      
      





その後、 ビルドディレクトリが表示されます。

プリローダーの構築:

 make -C build
      
      





U-bootのビルド:

 make -C build uboot
      
      





ここで、 U-Bootの変数を構成する必要があります。 最初に、テキストファイルu-boot-env.txtを作成します。

u-boot-env.txt
 console=ttyS0 baudrate=115200 bootfile=uImage bootdir=boot bootcmd=run mmcboot bootdelay=3 fdt_file=soc.dtb fdt_addr_r=0xf00000 ethaddr=00:01:02:03:04:05 kernel_addr_r=0x10000000 mmcroot=/dev/mmcblk0p2 mmcpart=2 con_args=setenv bootargs ${bootargs} console=${console},${baudrate} misc_args=setenv bootargs ${bootargs} uio_pdrv_genirq.of_id=generic-uio mmc_args=setenv bootargs ${bootargs} root=${mmcroot} rw rootwait mmcboot=mmc rescan; ext2load mmc 0:${mmcpart} ${kernel_addr_r} ${bootdir}/${bootfile}; ext2load mmc 0:${mmcpart} ${fdt_addr_r} ${bootdir}/${fdt_file}; run mmc_args con_args misc_args; bootm ${kernel_addr_r} - ${fdt_addr_r} verify=n
      
      





次に、変数を含む領域のサイズを指定することを忘れずに、バイナリ形式に変換します-4096バイトで十分です。 実際のサイズが指定されたサイズを超えた場合でも、 mkenvimageはこれを報告します。

 ./build/uboot-socfpga/tools/mkenvimage -s 4096 -o u-boot-env.img u-boot-env.txt
      
      





3つのファイルに興味があります。





Rootfsビルド



このセクションは、 Debianを使用している人(またはディストリビューションにdebootstrapがある場合 )を対象としています。 あなたがそれらの中にいない場合、 Yoctoまたはあなたに便利な他の方法を使用できます。



必要なパッケージをインストールします。

 sudo apt-get install debootstrap qemu-user-static binfmt-support
      
      





ディレクトリを作成し、そこに必要なファイルをダウンロードします。

 mkdir rootfs sudo debootstrap --arch armel --foreign wheezy rootfs http://ftp.debian.org/debian
      
      





ARMアーキテクチャ用に構築されたアプリケーションを実行するには、 qemu staticを使用します 。 これを行うには、ファイルをrootfsにコピーします。

 sudo cp /usr/bin/qemu-arm-static rootfs/usr/bin/
      
      





新しいファイルシステムに移動します。

 sudo chroot rootfs /bin/bash
      
      





インタプリタのプロンプトが「名前がありません!@ホスト名:/#」に変更された場合、すべてがうまくいきました。

インストールプロセスを完了します。

 /debootstrap/debootstrap --second-stage
      
      





/ etc / inittabに次の行残します。

/ etc / inittab
 id:5:initdefault: si::sysinit:/etc/init.d/rcS ~~:S:wait:/sbin/sulogin l0:0:wait:/etc/init.d/rc 0 l1:1:wait:/etc/init.d/rc 1 l2:2:wait:/etc/init.d/rc 2 l3:3:wait:/etc/init.d/rc 3 l4:4:wait:/etc/init.d/rc 4 l5:5:wait:/etc/init.d/rc 5 l6:6:wait:/etc/init.d/rc 6 z6:6:respawn:/sbin/sulogin S:2345:respawn:/sbin/getty 115200 console
      
      





パスワードを設定します。

 passwd
      
      





アーカイブを作成します。

 tar -cpzf rootfs.tar.gz --exclude=rootfs.tar.gz /
      
      







テストプログラムを書く



簡単に言うと、 SoCコンポーネント間のほとんどすべての相互作用は、1つのコンポーネントのアドレス空間を別のコンポーネントのアドレス空間にマッピングすることによって発生します。

例を考えてみましょう。 Qsysを使用したプロジェクトは、アドレス0から始まるHPS-to-FPGAインターフェイスに、サイズが262144バイトのオンチップメモリブロックが配置されていることを示しました。 HPS-FPGAインターフェイス自体は、 0xC0000000の CPUアドレス空間にマップされます( Cyclone Vのドキュメントを参照)。 その結果、( 0xC0000000 + 0 )から( 0xC0000000 + 262143 )のアドレスでCPUにアクセスすると、FPGAの内部メモリにアクセスします。



したがって、作業には、任意のメモリアドレスについて読み書きできるユーティリティが必要です。 ソースコードは次のとおりです。

mem.c
 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> #include <sys/types.h> #include <sys/mman.h> #include <unistd.h> #include <errno.h> #define MAP_SIZE (4096) #define MAP_MASK (MAP_SIZE-1) int main( int argc, char *argv[] ) { int fd; if( argc < 2 ) { printf( "Usage:\n" ); printf( "%s byte_addr [write_data]\n", argv[ 0 ] ); exit( -1 ); } // /dev/mem    ,    . fd = open( "/dev/mem", O_RDWR | O_SYNC ); if( fd < 0 ) { perror( "open" ); exit( -1 ); } void *map_page_addr, *map_byte_addr; off_t byte_addr; byte_addr = strtoul( argv[ 1 ], NULL, 0 ); //    /dev/mem     .   . map_page_addr = mmap( 0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, byte_addr & ~MAP_MASK ); if( map_page_addr == MAP_FAILED ) { perror( "mmap" ); exit( -1 ); } //     (   ) map_byte_addr = map_page_addr + (byte_addr & MAP_MASK); uint32_t data; //   ,   ,  --     . if( argc > 2 ) { data = strtoul( argv[ 2 ], NULL, 0 ); *( ( uint32_t *) map_byte_addr ) = data; } else { data = *( ( uint32_t *) map_byte_addr ); printf( "data = 0x%08x\n", data ); } //  . if( munmap( map_page_addr, MAP_SIZE ) ) { perror( "munmap" ); exit( -1 ); } close( fd ); return 0; }
      
      





次に、クロスコンパイラを使用してビルドする必要があります。 これを行うには、スクリプトを実行します。

 /opt/altera/quartus14.0/embedded/embedded_command_shell.sh
      
      





そしてコンパイルします:

 arm-linux-gnueabihf-gcc -o mem.o mem.c
      
      





帯域幅を測定するユーティリティも必要です。

memblock.c
 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <fcntl.h> #include <sys/types.h> #include <sys/mman.h> #include <unistd.h> #include <errno.h> //    #define COP_WRITE (0) #define COP_READ (1) #define COP_CHECK (2) int main( int argc, char *argv[ 0 ] ) { int fd; void *map_addr; if( argc < 5 ) { printf( "Usage:\n" ); printf( "%s <cop> <address> <word_count> <cycles>\n", argv[ 0 ] ); exit( -1 ); } // /dev/mem    ,    . fd = open( "/dev/mem", O_RDWR | O_SYNC ); if( fd < 0 ) { perror( "open" ); exit( -1 ); } uint8_t cop; off_t addr; uint32_t word_cnt; uint32_t cycle_cnt; //   cop = strtoul( argv[ 1 ], NULL, 0 ); //   addr = strtoul( argv[ 2 ], NULL, 0 ); //    / word_cnt = strtoul( argv[ 3 ], NULL, 0 ); //    cycle_cnt = strtoul( argv[ 4 ], NULL, 0 ); //    /dev/mem     . map_addr = mmap( 0, word_cnt * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, addr ); if( map_addr == MAP_FAILED ) { perror( "map" ); exit( -1 ); } uint32_t cycle; uint32_t word; uint32_t data; //      switch( cop ) { //    "". case( COP_WRITE ): for( cycle = 0; cycle < cycle_cnt; cycle++ ) { for( word = 0; word < word_cnt; word++ ) { *( ( uint32_t *) map_addr + word ) = word; } } break; //      . case( COP_READ ): for( cycle = 0; cycle < cycle_cnt; cycle++ ) { for( word = 0; word < word_cnt; word++ ) { data = *( ( uint32_t *) map_addr + word ); printf( "idx = 0x%x, data = 0x%08x\n", word, data ); } } break; //      " ". case( COP_CHECK ): for( cycle = 0; cycle < cycle_cnt; cycle++ ) { for( word = 0; word < word_cnt; word++ ) { data = *( ( uint32_t *) map_addr + word ); if( data != word ) { printf( "Error! write = 0x%x, read = 0x%x\n", word, data ); exit( -1 ); } } } break; default: printf( "Error! Unknown COP\n" ); exit( -1 ); } if( munmap( map_addr, word_cnt * 4 ) ) { perror( "munmap" ); exit( -1 ); } close( fd ); return 0; }
      
      





コンパイルします:

 arm-linux-gnueabihf-gcc -o memblock.o memclock.c
      
      







したがって、対象のファイルは次のとおりです。





SDカードを作成



すべてをまとめるときです。 現時点では、次のファイルが必要です。



それらのいずれかが存在しない場合、あなたは何かを見逃した:)



ディレクトリを作成し、指定されたすべてのファイルをそのディレクトリにコピーします。 次に、microSDカードを見つけて接続する必要があります。

次のコマンドは、カードがdevice / dev / sdbとして定義されていることを前提としています。 その上に2つのセクションを作成します。



カードが別の名前で識別されている場合は、適切な変更を加えます。



念のため、すべてをゼロで上書きします。

注意! もう一度、/ dev / sdbが2番目のハードドライブではなくカードであることを確認します。

 sudo dd if=/dev/zero of=/dev/sdb bs=10M
      
      





パーティションを作成するには、 fdiskユーティリティを使用します。

 sudo fdisk /dev/sdb
      
      





次に、次のコマンドを入力します(空の行-Enterキーを押します)。

fdiskのコマンド
 o n p 1 2048 +1M n p 2 t 1 a2 t 2 83 w
      
      





成功したことを確認できます:

 sudo fdisk -l /dev/sdb
      
      





次のようなものがあるはずです。

Fdisk -lの出力
 Disk /dev/sdb: 1966 MB, 1966080000 bytes 61 heads, 62 sectors/track, 1015 cylinders, total 3840000 sectors Units = sectors of 1 * 512 = 512 bytes Sector size (logical/physical): 512 bytes / 512 bytes I/O size (minimum/optimal): 512 bytes / 512 bytes Disk identifier: 0x02be07e5 Device Boot Start End Blocks Id System /dev/sdb1 2048 4095 1024 a2 Unknown /dev/sdb2 4096 3839999 1917952 83 Linux
      
      





次に、 U-Boot変数を含むイメージをマップにコピーします。

 sudo dd if=u-boot-env.img of=/dev/sdb bs=1 seek=512
      
      





その後、 プリローダーをコピーします:

 sudo dd if=preloader-mkpimage.bin of=/dev/sdb1
      
      





そして、 U-Boot自体

 sudo dd if=u-boot.img of=/dev/sdb1 bs=64k seek=4
      
      





ext3ファイルシステムを作成します。

 sudo mkfs.ext3 /dev/sdb2
      
      





マウントする:

 sudo mount /dev/sdb2 /mnt/
      
      





そして、 rootfsを展開します

 sudo tar xvf rootfs.tar.gz -C /mnt/
      
      





次に、カーネルイメージ、 dtb 、FPGAファームウェア、およびテストプログラムをコピーします。

 sudo cp uImage /mnt/boot/ sudo cp soc.dtb /mnt/boot/ sudo cp soc.rbf /mnt/boot/ sudo cp mem.o /mnt/root/ sudo cp memblock.o /mnt/root/
      
      





ファイルシステムをアンマウントします。

 sudo umount /dev/sdb2
      
      





すべて、カードの準備ができました!



ボードの起動と帯域幅の測定



すべてが最終的に作業の準備ができました。 カードを挿入し、USBと電源を接続します。

コンソールに行きます:

 minicom -D /dev/ttyUSB0 -b 115200 -s
      
      





まず、 FPGAをフラッシュします。

これを行うには、ボードのP18スイッチを「On On On On On」位置(スイッチ1〜5)に設定します。

FPGAの現在の状態を確認します。

 cat /sys/class/fpga/fpga0/status
      
      





構成フェーズが表示されるはずです

ファームウェアを入力します。

  dd if=/boot/soc.rbf of=/dev/fpga0 bs=4096
      
      





状態をもう一度確認します。

 cat /sys/class/fpga/fpga0/status
      
      





ステータスがユーザーモードに変更されます 。 これは、FPGAが構成されて準備が整ったことを意味します。



次に、ユーティリティを確認します。 しかしその前に、もう少し「ファイル作業」。

クロスコンパイラとDebianは 、動的リンカの名前異なります。 したがって、ユーティリティが機能するためには、正しいリンカーへのリンクを作成する必要があります。

 ln -s /lib/ld-linux.so.3 /lib/ld-linux-armhf.so.3
      
      





そのため、ユーティリティを起動します(アドレスの説明は少し下になります)。

 ./mem.o 0xFFD0501C
      
      





その結果、行データ= 0x00000007が表示された場合、すべてが正常です。



上で書いたように、内部FPGAメモリはアドレス0xC0000000から始まるアドレス空間にマップされます。 しかし、このメモリを使用する前に、さらに2つのアクションを実行する必要があります。



まず、デフォルトではCPUFPGA間のすべてのインターフェイスがリセットされているため、削除する必要があります。 リセットマネージャー(rstmgr)ブロックは、ベースアドレスが0xFFD05000であり、具体的にはオフセットが0x1Cの brgmodrstレジスタで、これを担当します。 最終レジスタアドレスは0xFFD0501Cです。 関与する最下位ビットは3つだけです。



すべてのビットの動作のロジックは同じです-ユニットがそこに書き込まれている場合、対応するインターフェースはリセットされています。 その結果、このレジスタのデフォルト値は0x7であり、ユーティリティを使用して読み取ったときに見ました。 HPS-to-FPGAインターフェイスからリセットを削除する必要があります。つまり、レジスタに0x6の数値を書き込む必要があります。

 ./mem.o 0xFFD0501C 0x6
      
      





その後、レジスタを再度読み取り、データが正しく書き込まれたことを確認します。

 ./mem.o 0xFFD0501C
      
      





次に、 HPSからFPGAへのインターフェイスのCPUのアドレス空間へのマッピングを有効にする必要があります 。 これは、ベースアドレスが0xFF800000L3(NIC-301)GPVブロック(l3regs) 、および具体的にはオフセット0の リマップレジスタが原因です。 HPS-to-FPGAの場合、ビット番号3が責任を負います。 そのため、レジスタに数値0x8を書き込む必要があります。

 ./mem.o 0xFF800000 0x8
      
      





残念ながら、このレジスタは書き込みのみ可能なため、検証のためにデータを読み取ることはできません。



これで、 FPGAメモリの読み取りと書き込みができるようになりました。 これをチェックしてください。 私たちは読みます:

 ./mem.o 0xC0000000
      
      





当然、ゼロが必要です。 そこに何かを書きます:

 ./mem.o 0xC0000000 0x12345678
      
      





もう一度読んでください:

 ./mem.o 0xC0000000
      
      





記録と一致する必要があります。



やった! やっとやった! FPGAが動作するSoCを入手し、 CPUからメモリへのアクセスを手配しました。

しかし、ただ読み書きするだけでは完全に退屈です。 少なくともインターフェースのスループットを測定しましょう。 また、時間がかかりません。



これを行うには、2番目のmemblockユーティリティが必要です。

 root@desktop:~# ./memblock.o Usage: ./memblock.o <cop> <address> <word_count> <cycles>
      
      





次のように機能します。最初の引数copが0の場合、アドレスaddressから始まる32ビットワードのword_countに0からword_count-1までの数字のシーケンスが書き込まれます。 手順全体がサイクル時間実行されます(これは帯域幅をより正確に測定するために行われます)。

copが1の場合、これらの同じ単語が読み取られて表示されます。

copが2の場合、単語が読み取られ、その意味が仮想的に書かれたものと比較されます。



ご覧ください。 いくつかのデータを書きましょう:

 ./memblock.o 0 0xC0000000 10 1
      
      





今それらを考慮してください:

 ./memblock.o 1 0xC0000000 10 1
      
      





結果は次のようになります。

結論memblock.o
 data = 0x00000000 data = 0x00000001 data = 0x00000002 data = 0x00000003 data = 0x00000004 data = 0x00000005 data = 0x00000006 data = 0x00000007 data = 0x00000008 data = 0x00000009
      
      





ここで、データを比較し、具体的にはわずかに多くの単語を設定してみましょう。

 ./memblock.o 2 0xC0000000 11 1
      
      





この行を取得する必要があります:

 Error! write = 0xa, read = 0x0
      
      





次に、1000回の繰り返しでメモリ全体の記録を開始し、記録時間を測定します。

 time ./memblock.o 0 0xC0000000 0x10000 1000
      
      





5回の開始時の平均値は11.17秒です。 スループットを考慮します。

 1000  * 65536  * 4  * 8 /_ / ( 11.17 * 10^6 ) = 187.75 /c
      
      





それほど厚くない。 読書はどうですか?

 time ./memblock.o 2 0xC0000000 0x10000 1000
      
      





平均時間10.5秒。 結果:

 1000 * 65536 * 4 * 8 / ( 10.5 * 10^6 ) = 199.73 /c
      
      





ほぼ同じ。 当然、これらの操作のいずれかの期間中、2つのコアのいずれかが100%ロードされます。



コンパイル中に-O3フラグを追加すると、書き込みおよび読み取りのスループットはそれぞれ212 Mbit / sおよび228 Mbit / sになります。 少し良くなりましたが、流星ではありません。



しかし、これは驚くことではありません-このスループットを上げるために何もしませんでした。 トリッキーな最適化を試したり、カーネルに目を向けたり、最悪の場合は少なくともDMAを固定してプロセッサを解放したりするのは良いことです。

しかし、これはもちろん、誰かにとって興味深いものになる場合は、次の記事で既に説明しています。



最後に達した人に感謝します! 頑張って



便利なリンク



Cyclone V に関するホワイトペーパー

Rocketboards.org -SoCを搭載したボードに関するさまざまな記事

EBV SoCrates評価ボードに関する具体的な情報



All Articles