I/O トラフィック ジェネレーターの使用 - 2020.2 Japanese

Vitis 統合ソフトウェア プラットフォームの資料: アプリケーション アクセラレーション開発 (UG1393)

Document ID
UG1393
Release Date
2021-03-22
Version
2020.2 Japanese

概要

ビデオ ストリーミングやイーサネット ベースのアプリケーションなどの一部のユーザーアプリケーションでは、プラットフォーム上の 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/ にあるライブラリを使用して、上記のように外部プロセス バイナリを生成したら、次の手順を使用してエミュレーションを実行できます。

  1. 標準プロセスを使用する Vitis ハードウェア エミュレーションまたは Vivado シミュレーションを起動し、シミュレーションが開始されるのを待ちます。
  2. 別のターミナルから、 Python や C++ などの外部プロセスを起動します。