先日、 EBV SoCrates評価ボードが私の手に落ちました。 簡単に言えば、これはアルテラの SoCを搭載したボードで、デュアルコアARMおよびCyclone V FPGAが搭載されています。
同じチップ上のARMとFPGA-非常に興味深いはずです! しかし、そもそも、このすべての善は「高められる」必要があります。
このプロセスについては、この記事で説明します。
これまたは同様のボードを手に入れて、それをどうするか完全にわからない場合。 FPGAが複雑なものであり、これにアプローチする方法が明確でないと常に考えている場合。 または、あなたは単に好奇心engineer盛なエンジニアですか? それから入って 私たちはすべてに満足しています。
ちょっとしたボーナスとして、 CPUとFPGA間の帯域幅を測定します。
作業計画
私たちの計画は、次の項目で構成されています。
- FPGAファームウェアの入手
- カーネルアセンブリ
- U-Bootとプリローダーを構築する
- Rootfsビルド
- テストプログラムを書く
- SDカードを作成
- ボードの起動と帯域幅の測定
行こう!
FPGAファームウェアの作成
まず、FPGAファームウェアを入手する必要があります。
このツールにはCAD Quartusが必要になります。 公式Webサイトからダウンロードできます。
私はインストールについて説明しません-すべてがそこにかなり明白です。
プロジェクト作成
Quartusを実行し、 [ファイル]-> [新しいプロジェクトウィザード ]に移動して、[ 次へ ]をクリックし、ディレクトリとプロジェクト名を入力します。
プロジェクト名
次のページをスキップし、FPGAのファミリとタイプを選択します。
FPGAの選択
残りの設定は重要ではありません。[ 完了 ]をクリックします。
Qsysプロジェクト
Qsysは初心者向けの優れたツールです。 コードを1行も書かずにファームウェアを取得できます。 代わりに、開発者は事前定義されたキューブ(IPピール)からコンストラクターを組み立てます。 各ピールを適切に設定し、適切に接続するだけです。
したがって、 ツール-> Qsysでは、左側のウィンドウ( IPカタログ )に2つのIPピールが必要です。
- プロセッサーと周辺機器->ハードプロセッサーシステム-> Arria V / Cyclone Vハードプロセッサーシステム
- 基本機能->オンチップメモリ->オンチップメモリ(RAMまたはROM)
ハードプロセッサシステム(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つのファイルに興味があります。
- アーチ/アーム/ブーツ/ uImage
- soc.dtb
U-Bootとプリローダーを構築する
SoCの起動プロセスは次のとおりです。
- ブートROM
- プリローダー
- ブートローダー
- OS
ブートROMは、電源投入直後に実行される最初のブートフェーズです。 その主な機能は、第2段階であるPreloaderを特定して完了することです。
プリローダー機能は、ほとんどの場合、 SDRAMインターフェイスの初期化とHPSピンの構成です。 SDRAMの初期化では、コードが利用可能な内部メモリの60 KBに収まらない可能性があるため、次のステージを外部メモリからロードできます。
ブートローダーはHPSのさらなる初期化に参加できます。 また、このステージはオペレーティングシステムまたはユーザーアプリケーションをロードします。 通常(そして私たちの場合)、 U-BootはBootloaderとして機能します。
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つのファイルに興味があります。
- ビルド/ uboot-socfpga / u-boot.img
- u-boot-env.img
- build / preloader-mkpimage.bin
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
したがって、対象のファイルは次のとおりです。
- mem.o
- memblock.o
SDカードを作成
すべてをまとめるときです。 現時点では、次のファイルが必要です。
- soc.rbf
- uImage
- soc.dtb
- preloader-mkpimage.bin
- u-boot.img
- u-boot-env.img
- rootfs.tar.gz
- mem.o
- memblock.o
それらのいずれかが存在しない場合、あなたは何かを見逃した:)
ディレクトリを作成し、指定されたすべてのファイルをそのディレクトリにコピーします。 次に、microSDカードを見つけて接続する必要があります。
次のコマンドは、カードがdevice / dev / sdbとして定義されていることを前提としています。 その上に2つのセクションを作成します。
- / dev / sdb1-プリローダーおよびU-Boot用
- / dev / sdb2-ファイルシステム用
カードが別の名前で識別されている場合は、適切な変更を加えます。
念のため、すべてをゼロで上書きします。
注意! もう一度、/ 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つのアクションを実行する必要があります。
まず、デフォルトではCPUとFPGA間のすべてのインターフェイスがリセットされているため、削除する必要があります。 リセットマネージャー(rstmgr)ブロックは、ベースアドレスが0xFFD05000であり、具体的にはオフセットが0x1Cの brgmodrstレジスタで、これを担当します。 最終レジスタアドレスは0xFFD0501Cです。 関与する最下位ビットは3つだけです。
- 0th - HPS-FPGAインターフェイスをリセットします
- 1番目 — LWHPSからFPGAへのインターフェイスをリセットします
- 2番目 — FPGAからHPSへのインターフェイスをリセットします
すべてのビットの動作のロジックは同じです-ユニットがそこに書き込まれている場合、対応するインターフェースはリセットされています。 その結果、このレジスタのデフォルト値は0x7であり、ユーティリティを使用して読み取ったときに見ました。 HPS-to-FPGAインターフェイスからリセットを削除する必要があります。つまり、レジスタに0x6の数値を書き込む必要があります。
./mem.o 0xFFD0501C 0x6
その後、レジスタを再度読み取り、データが正しく書き込まれたことを確認します。
./mem.o 0xFFD0501C
次に、 HPSからFPGAへのインターフェイスのCPUのアドレス空間へのマッピングを有効にする必要があります 。 これは、ベースアドレスが0xFF800000のL3(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評価ボードに関する具体的な情報