@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそ、すべては、SystemC v0.9公開から始まった
はじめに
前回は、QEMU 上で起動した Ubuntu cloud image に SystemC/Verilatorをインストールし、SystemC側のモデルをビルド後、vfio-pci デバイスを登録しました。
今回は、SystemC側のモデル、test-pcie-ep-master-vfio.cc の中を見ていきます。
test-pcie-ep-master-vfio.cc の内容
test-pcie-ep-master-vfio.cc のブロック図を下記に示します。
図の右側にある vfio_dev が vfio と接続することになります。
vfio_dev
vfio_dev は、ここ で定義されています。
vfio_dev の constructor は、下記のようになっていて、/dev/vfio/vfio および /dev/vfio/3 (I/O MMUのグループ、3)を open して、いろいろとやっています。vfio_pci デバイスにして何をしなければいけないのかは、こちら に説明があるので、確認してみてください。
vfio_dev::vfio_dev(const char *devname, int iommu_group) { ..... 途中略 container = open("/dev/vfio/vfio", O_RDWR); if (container < 0) { printf("Failed to open /dev/vfio/vfio, %d (%s)\n", container, strerror(errno)); print_vfio_iommu_err(); goto error; } snprintf(path, sizeof(path), "/dev/vfio/%d", iommu_group); group = open(path, O_RDWR); if (group < 0) { printf("Failed to open %s, %d (%s)\n", path, group, strerror(errno)); print_vfio_device_id_err(); print_vfio_iommu_err(); goto error; } ..... };
tlm2vfio_bridge
vfio_dev は、SystemCのモジュールではありません。。vfio_dev は、下記のように tlm2vfio_bridge の constructor にて bindされています。
tlm2vfio_bridge::tlm2vfio_bridge(sc_module_name name, int nr_sockets, class vfio_dev& dev, int region, uint64_t offset, bool handle_irq) : sc_module(name), tgt_socket("tgt-socket", nr_sockets), irq("irq"), dev(dev), offset(offset), region(region), event("ev"), irq_val(false), irq_dummy("irq-dummy"), handle_irq(handle_irq)
vfio_dev が使われるのは、
です。b_transport と get_direct_mem_ptr は tlm2vfio が target デバイスとして動作する場合に使われます。また、irq_ackは、b_transport 内で使われています。
一方、irq_poll は、poll_trampoline という static な関数内で使われています。
poll_trampoline 関数は、tlm2vfio_bridge の constractor 内で thread として起動しています。引数は自分自身(this) です。
if (handle_irq) { SC_THREAD(irq_proxy); pthread_create(&thread, NULL, poll_trampoline, this); }
irq_proxy は、下記のようになっていて、割り込み信号 irq の出力を event によって制御しています。
void tlm2vfio_bridge::irq_proxy(void) { while (true) { irq.write(irq_val); wait(event); } }
ここまでに見てきたことから、vfio_dev は tlm2vfio_bridge 経由で割り込み付の target device の働きをするようです。
test-pcie-ep-master-vfio
test-pcie-ep-master-vfio コマンドによって、SystemC側のモデルを起動します。この時に、デバイス、I/O MMU のグループ等を引数に渡しています。実行すると以下のようなメッセージが表示されます。
sudo が必要なのは、/dev/vfio/
$ sudo ./test-pcie-ep-master-vfio 0000:01:00.0 3 0 SystemC 2.3.2-Accellera --- Jan 8 2021 14:33:28 Copyright (c) 1996-2017 by all Contributors, ALL RIGHTS RESERVED Device supports 9 regions, 5 irqs mapped 0 at 0x7fb2352b3000 Info: (I702) default timescale unit used for tracing: 1 ps (./test-pcie-ep-master-vfio.vcd) Bridge ID c3a89fe1 Position 0 version=100 type=12 pcie-axi-master Bridge version 1.0 Bridge data-width 128 Bridge nr-descriptors: 16 -------------------------------------------------------------------------------- [15 us] Write : 0x0, length = 16384, streaming_width = 16384 data = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0....
上記のログの内容:
Bridge ID から Bridge nr-descriptors: までは、tlm_hw_bridge_base の bridge_probe メソッドにて出力されているようです。tlm2axi_hw_bridge の親クラスが tlm_hw_bridge_base なので、tlm2axi_hw_bridge のインスタンスの tlm_hw_bridge が出力しているっぽいですね。
Write: 0x0, length = ... の部分は、tg-tlm.h の debugWrite メソッドからのようです。
最初のブロック図を再度見てみましょう。左側のTLMTrafficGeneratorが tg-tlm.h の debugWriteメソッドを実行しています。
TLMTrafiicGererator に送られてくる traffic は、TLMTrafficGenerator の上の RandomTraffic によって生成されます。下記のようなコードで RandonTraffic のインスタンス rand_xfers を TLMTrafficGeneratorのインスタンス tgに addTransfers で割り当てています。
rand_xfers = new RandomTraffic(0, ram_size, UINT64_MAX, 1, ram_size, ram_size, 40000); // Wire up the clock and reset signals. tlm_hw_bridge.rst(rst); rand_xfers->setInitMemory(true); rand_xfers->setMaxStreamingWidthLen(ram_size); tg.enableDebug(); tg.addTransfers(*rand_xfers, 0, DoneCallback); tg.setStartDelay(sc_time(15, SC_US));
生成された Trans は、splitter によって、memory あるいは、tlm2axi_hw_bridge に渡されます。tlm2axi_hw_bridge にて適当さサイズの trans に分割され、tlm2vfio_bridge に送られ、vfio_dev に bind された vfio_pci から mmap された PCIe の BAR[x] のメモリ空間にアクセスしています。