VIRTIOをexStickGEで動かしてみた

未分類

前回まではZYNQ上でVIRTIOを動かしましたが
今回はexStickGE上に構成されたソフトプロセッサコアであるMicroblazeで動かしました.

ブロックデバイスやネットワークデバイスの構成は以前と同様です.

ブロックデバイスの仕様

  • 容量 2KiB (ソースコード書き換えで変更可能)
  • 読み書き可能デバイス
  • 書き込み可能な領域は先頭から512byte
  • 読み込みは先頭512byteを書き込んだ内容を出力
    それ以降は4byte単位で前2byteがインクリメント値
    後2byteがセクター番号(リトルエンディアン)

ネットワークデバイスの仕様

  • Linuxからネットワークデバイスとして認識されるデバイス
  • Linuxから送信したパケットは読み出すだけで破棄する
    (ILAから確認する)
  • FPGAからスライドスイッチの状況を含むパケットを送信する
    • 192.168.0.1:12345(FPGA)から192.168.0.2:12345(Linux)へ送信

VIRTIOを実現するために必要なファイルはe-trees / fpga_virtio_devicesにあります.
またexStickGEのボードファイルはe-trees / board_filesにあります.
どちらもホームディレクトリ以下vivadoprjという名前のディレクトリを作成し配置しておきます.

Vivadoでの作業

ボードファイルの準備

Vivadoを起動しツールバーからTools->Settingsを開きます.
左の設定からVivado Store->Board Repositoryを選択します.
vivadoprjの下に配置したboard_filesを追加しOKで設定を終了します.

デバイスの準備を行う

File->Project->New…から新しいプロジェクトを作成します.
New ProjectでNextをクリックする.

Vivadoを起動しツールバーからFile->Project->New…から新しいプロジェクトを作成します.
New ProjectでNextをクリックする.
Project nameは「exstickge_virtio」
Project locationは「/home/dev/vivadoprj」に設定しNextをクリックする.

「RTL Project」を選択したままNextをクリックする.
Add SourcesではAdd Filesからe-trees / fpga_virtio_devices
blkdevmmioディレクトリ内の2つのVerilogファイルと
nicdevmmioディレクトリ内の5つのVerilogファイルを追加しNextをクリックする.

Add ConstraintsではAdd Filesからe-trees / fpga_virtio_devices
nicdevmmioディレクトリ内のmain_exstickge.xdcを追加しNextをクリックする.

Default PartではBoardsからexStickGEを選択しNextをクリックする.

SuammaryではそのままFinishをクリックする.

左のナビゲータからIP Cilaatalogをクリックする.

表示されたカタログの中からILAを探しダブルクリックする.

GeneralOptionsでは以下の設定に変更する.

  • Component Nameをpacket_send_ila
  • Number of Probesを3

Probe_portsでは以下の設定に変更しOkをクリックする.

  • Probe1のWidthを32

General Output ProductsではSynthesis OptionsをGlobalに変更しGenerateをクリックする.

再度カタログの中からFIFO Generatorを探しダブルクリックする.

Native Portsを以下の設定に変更しOKをクリックする.

  • Read ModeはFirst Word Fall Through
  • Write Widthは32
  • Write Depthは128

General Output ProductsではSynthesis OptionsをGlobalに変更しGenerateをクリックする.

ブロックデザインの作成

左のナビゲーターからCreate Block Designをクリックする.
Design nameはそのままでOKをクリックする.

まずはBoardからDDR3 SDRAMを追加します.

その他にSystem ResetSystem Differential Clockを追加します.
どれも初期設定で生成します.

Add IP から Clocking Wizard を1個追加します

クロックの設定を変えていきます.
clk_wiz_0をダブルクリックしOutput Clocksで以下の変更を行います.

  • 出力クロックは 100MHz と 200MHz
  • リセットタイプはActive Low

clk_wiz_1をダブルクリックしOutput Clocksで以下の変更を行います.

  • 出力クロックは 310MHz
  • リセットタイプはActive Low

現状で配線を整理します.
自動生成されたclk_ref_isys_clk_iの2つの外部ポートを削除します.
外部ポートのresetをclk_wiz_0とclk_wiz_1のresetnに接続します.
clk_wiz_0のCLK100MHzをclk_wiz_1のclk_in1に接続します.
clk_wiz_0のCLK200MHzをmigのclk_ref_iに接続します.
clk_wiz_1のCLK310MHzをmigのsys_clk_iに接続します.

現時点での接続図です.

Add IPからMicroBlazeを選択します.

Run Block AutomationをクリックしInerrupt Controllerにチェックを入れOKをクリックします.

Predefined ConfigurationsからLinux with MMUを選択しOKをクリックします.

Run Connection AutomationをクリックしmigのS_AXIにチェックを入れOKをクリックします.

Microblazeを動かすに必要なデバイスを追加します.
Add IPからAXI UartliteAXI Timerを追加します.
AXI UartliteはBaud Rateを115200に設定しておきます.

Run Connection AutomationからAll Automationにチェックを入れOKをクリックします.
割り込みの設定が必要ですがVIRTIOデバイスも必要なので後で一緒に設定します.

現時点での接続図です.

VIRTIOデバイスを追加する

ブロックデザインのどこかで右クリックをしAdd Moduleをクリックします.

blkmmio_top, nicmmio_top, packetgen_conv, packetgensimpleの4種類を繰り返して追加します.

上部のRun Connection AutomationからAll Automationにチェックを入れOKをクリックします.

packetgensimpleとpacketgen_convのsrc_*, dst_*を同じ名前同士接続する.
また, packet_len, data, trig, rdenも図を参考に接続する.

packetgen_convとnicmmio_topも同様に接続する.

ネットワークデバイスはスイッチの状態を出力するのでそれに必要な入力を接続します.
Add IPからConstant及びConcatを追加します.
ConstantはWidthを2, Valを0に設定します.
ConcatはWidthをマニュアル設定でそれぞれ2に設定します.
ConstantのdoutをConcatのIn1
Concatのdoutをpacketgensimpleのswitchに接続します.

ConcatのIn0を右クリックしMake Externalをクリックします.
作成されたピンにdipsw2p_tri_iと名前をつけます.

残るは割り込みの配線です.
割り込みはAXI Timer, AXI Uartlite, VIRTIOのネットワークデバイス, ブロックデバイスの計4本です.
AXI Interrupt Controllerに接続されているConcatのNumber of Portsを4に設定しOKをクリックします.

ConcatのIn0~3を接続します.
今回は以下の接続としました.

  • 0 : AXI Timer
  • 1 : AXI Uartlite
  • 2 : ブロックデバイス
  • 3 : ネットワークデバイス

最終的な接続図です.

またAddress Editorからそれぞれのデバイスの配置アドレスを確認しておきます.

ブロックデザインを保存しProject Manager->Sourcesから
先程作成したdesign_1をtopモジュールにします.
design_1を右クリック, Create HDL Wrapper…を選択し Let ~のままOKをクリックする.
Critical Messagesが出ますがOKで閉じます.

作成されたdesign_1_wrapperを右クリックしSet as TOPを選択します.

左下のGenerate Bitstreamを選択し論理合成を行います.

合成が終わったらハードウェアファイルを出力します.
File->Export->Export Hardware…をクリックする.

Linuxのビルド

次はPetalinuxのビルドを行います.
便宜上Vivadoプロジェクトのディレクトリ内にpetalinuxのディレクトリを作成します.
コンフィグが出ますがそのままExit->Yesで終了します.

$ cd ~/vivadoprj/exstickge_virtio
$ petalinux-create --type project --template microblaze --name petaprj
$ cd petaprj/
$ petalinux-config --get-hw-description=../

カーネルコンフィグにVIRTIOを組み込むために設定を行います.

$ petalinux-config -c kernel

Device Drivers->Virtio drivers->Platform bus driver for memory mapped virtio devicesを<*>にするともう一つ項目が出てくるのでMemory mapped virtio devices parameter parsingも[*]に設定します.
Device Drivers->Block devices->Virtio block driverを<*>に
Device Drivers-> Network device support > Virtio network driverを<*>に設定します.

netcatを導入する

ZYNQではwiresharkやnetcatがオプションで導入することができるのですが
Microblazeではその設定ができなくなっていました
そのため自分でnetcatを独自にアプリケーションとしてGitHub – guzlewski/netcat を使用して
組み込むことにします.

$ petalinux-create -t apps --template c --name mync --enable
$ curl -o ~/vivadoprj/exstickge_virtio/petaprj/project-spec/meta-user/recipes-apps/mync/files/mync.c https://raw.githubusercontent.com/guzlewski/netcat/master/netcat.c
$ sed '/APP_OBJS/r ~/vivadoprj/exstickge_virtio/petaprj/project-spec/meta-user/recipes-apps/mync/files/mync.c' sedsample.txt
$ sed -i -e "/^APP_OBJS/a LDLIBS += -lpthread" ~/vivadoprj/exstickge_virtio/petaprj/project-spec/meta-user/recipes-apps/mync/files/Makefile

デバイスツリーに追記する

VirtIOデバイスであることを認識してもらうためにデバイスツリーに追記します.
ファイルパスは「~/vivadoprj/exstickge_virtio/petaprj/project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi」です.
regの項目は先程メモしたたアドレスを書き込みます.
参考 : ZYNQ Linuxが動作するPS部への割り込み
元々の記述はビルドすると「~/vivadoprj/exstickge_virtio/petaprj/components/plnx_workspace/device-tree/device-tree/pl.dtsi」に出てきます.

/include/ "system-conf.dtsi"
/ {
};

&blkmmio_top_0 {
	compatible = "virtio,mmio";
	reg = <0x44a00000 0x10000>;
	interrupt-parent = <µblaze_0_axi_intc>;
	interrupts = <2 2>;
};

&nicmmio_top_0 {
	compatible = "virtio,mmio";
	reg = <0x44a10000 0x10000>;
	interrupt-parent = <µblaze_0_axi_intc>;
	interrupts = <3 2>;
};

すべての準備が完了したのでビルドをします.

$ petalinux-build

実機で動かす

動作確認をするためにシリアル通信モジュールを接続します.
FPGA側から見たピンアサインは 1:RXD , 2:TXD, 5:GND で IO レベルは 3.3V です.
別ウィンドウでscreenなどを用いてシリアル通信を行います.

$ sudo screen /dev/ttyUSB1 115200

JTAG ケーブルを接続し次のコマンドを実行します.

$ petalinux-boot --jtag --fpga
$ petalinux-boot --jtag --kernel

ブートログが流れて起動が完了したらそれぞれのデバイスの動作を確認します.

ブロックデバイス

カーネルログを確認すると2KiBのディスクとして認識されています.

root@petaprj:~# dmesg | grep virt
virtio_blk virtio0: [vda] 4 512-byte logical blocks (2.05 kB/2.00 KiB)
root@petaprj:~#

ブロックデバイスの中身をダンプしてみます.
先頭から512byteは読み書き可能な領域となっていて0埋めされています.
512byte以降は4byte単位で前2byteはインクリメントした値, 後2byteはセクター番号となっています.

root@petaprj:~# od -x /dev/vda | head
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0001000 0000 0001 0001 0001 0002 0001 0003 0001
0001020 0004 0001 0005 0001 0006 0001 0007 0001
0001040 0008 0001 0009 0001 000a 0001 000b 0001
0001060 000c 0001 000d 0001 000e 0001 000f 0001
0001100 0010 0001 0011 0001 0012 0001 0013 0001
0001120 0014 0001 0015 0001 0016 0001 0017 0001
0001140 0018 0001 0019 0001 001a 0001 001b 0001
0001160 001c 0001 001d 0001 001e 0001 001f 0001
root@petaprj:~# 

適当な文字列を書き込んでみます.
書き込んだ内容が先頭に保存されていることがわかります.

root@petaprj:~# od -x /dev/vda | head
0000000 7246 2069 614d 2072 3920 3120 3a34 3230
0000020 353a 2038 5455 2043 3032 3831 000a 0000
0000040 0000 0000 0000 0000 0000 0000 0000 0000
*
0001000 0000 0001 0001 0001 0002 0001 0003 0001
0001020 0004 0001 0005 0001 0006 0001 0007 0001
0001040 0008 0001 0009 0001 000a 0001 000b 0001
0001060 000c 0001 000d 0001 000e 0001 000f 0001
0001100 0010 0001 0011 0001 0012 0001 0013 0001
0001120 0014 0001 0015 0001 0016 0001 0017 0001
root@petaprj:~# od -c /dev/vda | head
0000000   F   r   i       M   a   r           9       1   4   :   0   2
0000020   :   5   8       U   T   C       2   0   1   8  \n  \0  \0  \0
0000040  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0001000  \0  \0 001  \0 001  \0 001  \0 002  \0 001  \0 003  \0 001  \0
0001020 004  \0 001  \0 005  \0 001  \0 006  \0 001  \0  \a  \0 001  \0
0001040  \b  \0 001  \0  \t  \0 001  \0  \n  \0 001  \0  \v  \0 001  \0
0001060  \f  \0 001  \0  \r  \0 001  \0 016  \0 001  \0 017  \0 001  \0
0001100 020  \0 001  \0 021  \0 001  \0 022  \0 001  \0 023  \0 001  \0
0001120 024  \0 001  \0 025  \0 001  \0 026  \0 001  \0 027  \0 001  \0
root@petaprj:~# 

ブロックデバイスが問題なく動作していることが確認できました.

ネットワークデバイス

次にネットワークデバイスの動作を確認します.
MACアドレスがaa:bb:cc:dd:ee:ffのデバイスが認識されていることがわかります.

root@petaprj:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
root@petaprj:~#

Linuxが送信したパケットを確認します.

Linuxが稼働している状態でVivadoのFlow NavigatorからPROGRAM AND DEBUG -> Open Hardware Managerをクリックします.
Open target -> Auto Connectをクリックしデバッグ画面を開きます.

右下のTrigger Setupからpacket_enを選択しValueをRにします.

Run triggerボタンをクリックし開始します.

ILAの先頭3クロック分(12byte)はVirtIOで使用しているヘッダーであるため4クロック目以降が目的のパケットです.
ちゃんとパケットが受信できていることがわかりました.

FPGAが送信するパケットの受信

FPGA側からはスイッチの状況を報告するパケットが流れているので受信してみます.
まずNICにIPアドレスを設定します.
(パケットはIPアドレス192.168.0.2, ポート12345に対して送信しています.)

root@petaprj:~# ip a add 192.168.0.2/24 dev eth0
root@petaprj:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether aa:bb:cc:dd:ee:ff brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.2/24 scope global eth0
       valid_lft forever preferred_lft forever
root@petaprj:~# 

独自に追加したmyncコマンドで受信します.
FPGAボード上のS1のDIPスイッチを変化させることで受信する内容が変化することが確認できました.

root@petaprj:~# mync -l -u -p 12345 < /dev/null 
Hello!0
Hello!0
Hello!0
Hello!0
Hello!1
Hello!1
Hello!1
Hello!3
Hello!3
Hello!3
Hello!3
Hello!2
Hello!2
Hello!0
^C
root@petaprj:~#

まとめ

ハードプロセッサのZYNQに限らずソフトプロセッサであるMicroblaze上でも
VIRTIOが動作しデータをやり取りすることが確認できました.
VIRTIOを用いることでLinux側での作業をほとんど省くことができるため
簡単にFPGAのハードウェアとの連携がしやすくなると考えます.
VIRTIOには他にも様々なデバイスが定義されているので幅広く活用できる技術であると感じました.

コメント

タイトルとURLをコピーしました