概要
ビデオ ストリーミングやイーサネット ベースのアプリケーションなどの一部のユーザーアプリケーションでは、プラットフォーム上の I/O ポートを使用して、プラットフォームを出入りするデータをストリーミングします。これらのアプリケーションの場合、デザインのハードウェア エミュレーションを実行するには、I/O ポートのハードウェア動作を模倣して、ポートを通過するデータ トラフィックをシミュレーションするメカニズムが必要です。I/O トラフィック ジェネレーターを使用すると、Vitis アプリケーション アクセラレーション開発フローのハードウェア エミュレーション中、または Vivado Design Suite のロジック シミュレーション中に、I/O ポートを通過するトラフィックをモデル化できます。
デザインへのトラフィック ジェネレーターの追加
ザイリンクス デバイスには豊富な I/O インターフェイスがあります。Alveo アクセラレータ カードには、独自のモデルを持つ PCIe および DDR メモリ インターフェイスがあります。ただし、プラットフォームに GT カーネル ベースの汎用 I/O、ビデオ ストリーム、センサー データなどのほかの I/O が含まれることもあります。I/O トラフィック ジェネレーター カーネルを使用すると、シミュレーション中にプラットフォームとアプリケーションが I/O にトラフィックを挿入できるようになります。
このソリューションでは、ストリーミング I/O カーネル (XO) または IP の両方をデザインに含める必要があるほか、ザイリンクスの提供する Python または C++ API を使用して、トラフィックを挿入したり、エミュレーション プロセスからの出力データを取り込んだりする必要があります。ザイリンクスの提供する Python または C++ ライブラリを使用すると、トラフィック ジェネレーター コードをアプリケーションに統合し、別のプロセスとして実行し、エミュレーション プロセスとインターフェイスさせることができます。現在のところ、ザイリンクスでは、ストリーミング I/O を模倣する AXI4-Stream レベルでのインターフェイスをイネーブルにするライブラリを提供しています。
ストリーミング I/O モデルを使用すると、プラットフォーム上のストリーミング トラフィックをエミュレーションしたり、遅延モデリングをサポートしたりできます。アプリケーションにストリーミング I/O を追加することも、次に説明するようにカスタムプラットフォーム デザインに追加することもできます。
- ストリーミング I/O カーネルはほかのコンパイル済みカーネル オブジェクト (XO) ファイルと同様、
v++ --link
コマンドを使用してデバイス バイナリ (XCLBIN) ファイルに追加できます。Vitis インストールには、さまざまなデータ幅の AXI4-Stream インターフェイス用のカーネルが含まれます。これらは、ソフトウェアのインストール ディレクトリの $XILINX_VITIS/data/emulation/XO に含まれます。次のコマンド例を使用して、これらをデザインに追加します。
v++ -t hw_emu --link $XILINX_VITIS/data/emulation/XO/sim_ipc_axis_master_32.xo $XILINX_VITIS/data/emulation/XO/sim_ipc_axis_slave_32.xo ...
上記の例では、sim_ipc_axis_master_32.xo および sim_ipc_axis_slave_32.xo が 32 ビットのマスターおよびスレーブ カーネルを提供し、ターゲット プラットフォームおよびデザイン内のほかのカーネルとリンクしてハードウェア エミュレーション ビルド用の .xclbin ファイルを作成可能にしています。
- また、Versal および Zynq UltraScale+ MPSoC カスタム プラットフォームの Vivado IP インテグレーター機能を使用して、IPC モジュールをプラットフォーム ブロック デザインに追加することもできます。このツールには、プラットフォーム デザインに追加する
sim_ipc_axis_master_v1_0
およびsim_ipc_axis_slave_v1_0
IP が含まれます。これらは、ソフトウェアのインストール ディレクトリの $XILINX_VIVADO/data/emulation/hw_em/ip_repo に含まれます。次に、IPC IP をプラットフォーム デザインに追加する Tcl スクリプトの例を示します。このスクリプトを使用すると、Python または C++ で記述された外部プロセスからシミュレーションにデータ トラフィックを挿入できます。
#Update IP Repository path if required set_property ip_repo_paths $XILINX_VIVADO/data/emulation/hw_em/ip_repo [current_project] ## Add AXIS Master create_bd_cell -type ip -vlnv xilinx.com:ip:sim_ipc_axis_master:1.0 sim_ipc_axis_master_0 #Change Model Property if required set_property -dict [list CONFIG.C_M00_AXIS_TDATA_WIDTH {64}] [get_bd_cells sim_ipc_axis_master_0] ##Add AXIS Slave create_bd_cell -type ip -vlnv xilinx.com:ip:sim_ipc_axis_slave:1.0 sim_ipc_axis_slave_0 #Change Model Property if required set_property -dict [list CONFIG.C_S00_AXIS_TDATA_WIDTH {64}] [get_bd_cells sim_ipc_axis_slave_0]
Python でのトラフィック ジェネレーターの記述
I/O トラフィック ジェネレーターでデータ トラフィックを生成するため、またはエミュレーション プロセスから出力データを取り込むためには、アプリケーションをシミュレーションするときにトラフィック ジェネレーター プロセスも含める必要があります。ザイリンクスの提供する Python または C++ ライブラリを使用すると、次に説明するようにトラフィック ジェネレーター コードを作成できます。
- Python の場合は、コマンド ターミナルで
$PYTHONPATH
を次のように設定します。setenv PYTHONPATH $XILINX_VIVADO/data/emulation/hw_em/lib/python:\ $XILINX_VIVADO/data/python/xtlm_ipc/xtlm_ipc_v1_0
-
gt_master
インスタンスに接続する Python コードの例は、次のようになります。Blocking Send from xilinx_xtlm import ipc_axis_master_util from xilinx_xtlm import xtlm_ipc import struct import binascii #Instantiating AXI Master Utilities master_util = ipc_axis_master_util("gt_master") #Create payload payload = xtlm_ipc.axi_stream_packet() payload.data = "BINARY_DATA" # One way of getting "BINARY_DATA" from integer can be like payload.data = bytes(bytearray(struct.pack("i", int_number))) More info @ https://docs.python.org/3/library/struct.html payload.tlast = True #AXI Stream Fields #Optional AXI Stream Parameters payload.tuser = "OPTIONAL_BINARY_DATA" payload.tkeep = "OPTIONAL_BINARY_DATA" #Send Transaction master_util.b_transport(payload) master_util.disconnect() #Disconnect connection between Python & Emulation
-
gt_slave
インスタンスに接続する Python コードの例は、次のようになります。Blocking Receive from xilinx_xtlm import ipc_axis_slave_util from xilinx_xtlm import xtlm_ipc #Instantiating AXI Slave Utilities slave_util = ipc_axis_slave_util("gt_slave") #Sample payload (Blocking Call) payload = slave_util.sample_transaction() slave_util.disconnect() #Disconnect connection between Python & Emulation
C++ でのトラフィック ジェネレーターの記述
- C++ の場合、API は $XILINX_VIVADO/data/cpp/xtlm_ipc/xtlm_ipc_v1_0/src/ にあります。C++ API は、ブロッキング関数とノンブロッキング関数の両方をサポートします。次のコード部分は、その使用方法を示しています。 ヒント: 実行ファイルを生成するサンプル Makefile もあります。
- ブロッキング送信:
#include "xtlm_ipc.h" //Include file void send_packets() { //! Instantiate IPC socket with name matching in IPI diagram... xtlm_ipc::axis_initiator_socket_util<xtlm_ipc::BLOCKING> socket_util("gt_master"); const unsigned int NUM_TRANSACTIONS = 8; xtlm_ipc::axi_stream_packet packet; std::cout << "Sending " << NUM_TRANSACTIONS << " Packets..." <<std::endl; for(int i = 0; i < NUM_TRANSACTIONS; i++) { xtlm_ipc::axi_stream_packet packet; // generate_data() is your custom code to generate traffic std::vector<char> data = generate_data(); //! Set packet attributes... packet.set_data(data.data(), data.size()); packet.set_data_length(data.size()); packet.set_tlast(1); //Additional AXIS attributes can be set if required socket_util.transport(packet); //Blocking transport API to send the transaction } }
- ブロッキング受信:
#include "xtlm_ipc.h" void receive_packets() { //! Instantiate IPC socket with name matching in IPI diagram... xtlm_ipc::axis_target_socket_util<xtlm_ipc::BLOCKING> socket_util("gt_slave"); const unsigned int NUM_TRANSACTIONS = 8; unsigned int num_received = 0; xtlm_ipc::axi_stream_packet packet; std::cout << "Receiving " << NUM_TRANSACTIONS << " packets..." <<std::endl; while(num_received < NUM_TRANSACTIONS) { socket_util.sample_transaction(packet); //API to sample the transaction //Process the packet as per requirement. num_received += 1; } }
- ノンブロッキング送信:
#include <algorithm> // std::generate #include "xtlm_ipc.h" //A sample implementation of generating random data. xtlm_ipc::axi_stream_packet generate_packet() { xtlm_ipc::axi_stream_packet packet; // generate_data() is your custom code to generate traffic std::vector<char> data = generate_data(); //! Set packet attributes... packet.set_data(data.data(), data.size()); packet.set_data_length(data.size()); packet.set_tlast(1); //packet.set_tlast(std::rand()%2); //! Option to set tuser tkeep optional attributes... return packet; } void send_packets() { //! Instantiate IPC socket with name matching in IPI diagram... xtlm_ipc::axis_initiator_socket_util<xtlm_ipc::NON_BLOCKING> socket_util("gt_master"); // Instantiate Non Blocking specialization const unsigned int NUM_TRANSACTIONS = 8; xtlm_ipc::axi_stream_packet packet; std::cout << "Sending " << NUM_TRANSACTIONS << " Packets..." <<std::endl; for(int i = 0; i < NUM_TRANSACTIONS; i++) { packet = generate_packet(); // Or user's test patter / live data etc. socket_util.transport(packet); } }
- ノンブロッキング受信:
#include <unistd.h> #include "xtlm_ipc.h" void receive_packets() { //! Instantiate IPC socket with name matching in IPI diagram... xtlm_ipc::axis_target_socket_util<xtlm_ipc::NON_BLOCKING> socket_util("gt_slave"); const unsigned int NUM_TRANSACTIONS = 8; unsigned int num_received = 0, num_outstanding = 0; xtlm_ipc::axi_stream_packet packet; std::cout << "Receiving " << NUM_TRANSACTIONS << " packets..." <<std::endl; while(num_received < NUM_TRANSACTIONS) { num_outstanding = socket_util.get_num_transactions(); num_received += num_outstanding; if(num_outstanding != 0) { std::cout << "Outstanding packets = "<< num_outstanding <<std::endl; for(int i = 0; i < num_outstanding; i++) { socket_util.sample_transaction(packet); print(packet); } } usleep(100000); //As transaction is non-blocking we would like to give some delay between consecutive samplings } }
- 次は、上記のブロッキング受信の Makefile の例です。
GCC=/usr/bin/g++ IPC_XTLM=$(XILINX_VIVADO)/data/cpp/xtlm_ipc/xtlm_ipc_v1_0/src/ PROTO_PATH=$(XILINX_VIVADO)/data/simmodels/xsim/2020.2/lnx64/6.2.0/ext/protobuf/ BOOST=$(XILINX_VIVADO)/tps/boost_1_64_0/ SRC_FILE=b_receive.cpp .PHONY: run all default: all all : b_receive b_receive: $(SRC_FILE) $(GCC) $(SRC_FILE) $(IPC_XTLM)xtlm_ipc.pb.cc -I$(IPC_XTLM)/ -I$(PROTO_PATH)/include/ -L$(PROTO_PATH) -lprotobuf -o $@ -lpthread -I$(BOOST)/
トラフィック ジェネレーターの実行
$XILINX_VIVADO/data/cpp/xtlm_ipc/xtlm_ipc_v1_0/src/
にあるライブラリを使用して、上記のように外部プロセス バイナリを生成したら、次の手順を使用してエミュレーションを実行できます。
- 標準プロセスを使用する Vitis ハードウェア エミュレーションまたは Vivado シミュレーションを起動し、シミュレーションが開始されるのを待ちます。
- 別のターミナルから、 Python や C++ などの外部プロセスを起動します。