VirtIOのブロックデバイスをZynqで動かしてみた

VirtIOによって様々なデバイスを動かすことができますが,
今回はブロックデバイスを実装し動かしてみました.

実行環境はUbuntu 18.04, Vivado 2021.2, Petalinux 2021.2を使用しています.
使用したデバイスはDigilent社のZYBO(アナログ出力がある古いタイプ)です.
実装したブロックデバイスの仕様は以下のとおりです.

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

必要なファイルはe-trees / fpga_virtio_devicesにあります.
モジュール名をmmiodev_topからblkmmio_topに変更したため画像が一部異なっています.

デバイスの準備を行う

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

Project nameは「blkdevmmio」
Project locationは「/home/dev/vivadoprj」に設定しNextをクリックする.


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

Add ConstraintsはそのままNext
Default PartではBoardsから「Zybo」を選択する.

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

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

DiagramからAdd IPでZYNQ7 Processing Systemを選択し追加する.

Run Block AutomationをクリックしそのままOKをクリックする.

ZYNQのブロックをダブルクリックし設定画面を表示する.
PS-PL Configurationを開き
ACP Slave AXI Interface->S AXI ACP interface, Tie off AxUSERの両方にチェックを入れる.

次にPeripheral I/O PinsからEthernet 0->MDIOをEMIOからMDIOに変更する.

次にInterruptsからFabric Interrupts->PL-PS Interrupt Ports->IRQ_F2P[15:0]にチェックを入れる.
右下のOKで設定を終える.

Critical Messagesが出るがOKで閉じる.

Diagramの空白で右クリックしAdd Module…をクリックする.

Add Moduleではmmiodev_topを選択しOKをクリックする.
モジュール名を変更したため現在は「blkmmio_top」となります.

Run Connection AutomationをクリックしAll Automationにチェックを入れてOKをクリックする.
Critical Messagesが出るがOKで閉じる.

blkmmio_topのinterruptピンとZYNQのIRQ_F2Pピンを接続してDiagramの設計は完了です.
Ctrl+Sなどで保存します.

Diagramの隣のAddress Editorから/blkmmio_top_0/s_axiのノード名と割り当てられているベースアドレスをメモします.
今回は「mmiodev_top_0」で「0x43C0_0000」です.
モジュール名を変更したため現在は「blkmmio_top_0」となります.

PROJECT MANAGERから先程作成した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…をクリックする.

Outputではinclude bitstreamを選択し最後までNextをクリックし, Finishで終了する.

Linuxのビルド

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

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

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

$ petalinux-config -c kernel

Device Drivers->Block devices->Virtio block driverを<*>に設定します.
Device Drivers->Virtio drivers->Platform bus driver for memory mapped virtio devicesを<*>にするともう一つ項目が出てくるのでMemory mapped virtio devices parameter parsingも[*]に設定します.
Exitを繰り返し最後にYesで保存をして終了します.

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

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

&blkmmio_top_0 {
	compatible = "virtio,mmio";
	reg = <0x43c00000 0x10000>;
	interrupt-parent = <&intc>;
	interrupts = <0 29 1>;
};

デバイスツリーへの追記を終えたらビルドを行います.

$ petalinux-build
$ petalinux-package --boot --force --fsbl images/linux/zynq_fsbl.elf --fpga images/linux/system.bit --u-boot

実行する

ZYNQ上で実際に動かすために必要なファイルをコピーします.
適当なmicroSDカードをFAT32でフォーマットします.
「~/vivadoprj/blkdevmmio/petaprj/images/linux」ディレクトリ下にある3つのファイルをSDカード直下にコピーします.

  • BOOT.BIN
  • boot.scr
  • image.ub

ZYBOに書き込んだmicroSDカードを挿入しJP5のブート選択をSDに切り替えます.
電源を入れると起動します.
今回はシリアル通信用のターミナルとしてscreenを使用しました.

$ sudo screen /dev/ttyUSB1 115200

電源を入れブートします.
しばらく待つとプロンプトが出て起動が完了します.

root@petaprj:~#

カーネルログを確認すると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:~# echo "Hello world!" > /dev/vda 
root@petaprj:~# od -c /dev/vda | head
0000000   H   e   l   l   o       w   o   r   l   d   !  \n  \0  \0  \0
0000020  \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
0001140 030  \0 001  \0 031  \0 001  \0 032  \0 001  \0 033  \0 001  \0
root@petaprj:~# 


簡易的ではありますが読み書きが可能なVirtIOのブロックデバイスとして認識されていることがわかりました.

まとめ

VirtIOのブロックデバイスを実装しZynq上で動作を確認しました. VirtIOとして振る舞うにはVirtqueueの読み書きが多く結果としてAXIトランザクションを読み書きするための処理が多くなりました. しかし, FPGAを用いて簡易的ではありますがVirtIOを実現できることがわかりました. VirtIOデバイスによって簡単にOS側との連携が行うための手段が増えたと考えます.

コメント

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