Using Traffic Generators in AI Engine Graphs - 2023.2 English

Vitis Unified Software Platform Documentation: Application Acceleration Development (UG1393)

Document ID
UG1393
Release Date
2023-12-13
Version
2023.2 English

Overview

This section describes how to interface external traffic generators with AI Engine components. Simulation using external traffic generators can be run by launching the simulator/emulator and the traffic generator in parallel. The traffic generators can be written either in Python, MATLAB, or in C++, using multi-threading capabilities of these languages. Then the AI Engine must be written to work with the traffic generator during simulation or emulation. The steps below describe interfacing the traffic generator into your AI Engine graph for standalone AI Engine simulation or the full system emulation flow (hw_emu/sw_emu).

In order to interface external traffic generators with AI Engine components, you need to use certain APIs in your external script or source code written in Python, MATLAB, or C++:

Using Python or Matlab API

You must write a Python script using dedicated APIs to interface the external traffic generator process with the AI Engine simulation process running the AI Engine graph.

# Mandatory
import os, sys

import multiprocessing as mp
import threading
import struct

from aie_input_plio import aie_input_plio
from aie_output_plio import aie_output_plio

# Optional for ease of use
import numpy as np
import logging

The Python traffic generator uses API described in Writing Python Traffic Generators. MATLAB API is described in Writing Traffic Generators in MATLAB.

Use the following steps to integrate the external traffic generator data into the graph:

  1. Modify the graph code to declare the PLIO, but do not specify the PLIO based data text file in the PLIO constructors. This is needed for the external traffic generator to work properly. Take a note of the names (first argument) of the PLIO constructors. These will be used to hook up the external traffic generators and the same name should be used for creating the ports in the external traffic generators:
    plin = input_plio::create("DataIn1",adf::plio_32_bits);
    plout = output_plio::create("DataOut1",adf::plio_32_bits);
    
  2. Instantiate the corresponding AXI master and slave connections in the Python script. This will establish connections to the PLIO ports of the graph:
    pl_in = aie_input_plio("DataIn1", 'int16')
    pl_out = aie_output_plio("DataOut1", 'int16')
    Tip: You need to specify the PLIO datatype during object creation.
  3. Send data to the AI Engine.

    The data to be sent to the PLIO port in the AI Engine graph must be set up in the Python script. Using the send_data() API, you can send the data in the form of a list.

    plin.send_data send_data(<data_list>, tlast)
  4. Receive Data from the AI Engine.

    Use the receive_data_with_size() API to receive data from the AI Engine. This API returns the received values in recv_data from the following example. This is a blocking API and will wait until expected bytes of data are received. For more information, refer to Writing Python Traffic Generators.

    recv_data = plout.receive_data_with_size(<expected_bytes>)

Using C++ Traffic Generator APIs

When using the C++ language to implement an external traffic generator, various headers are necessary to use some libraries in the external traffic generator source code. The headers useful for handling these libraries are:

# For the traffic generator
#include "xtlm_ipc.h"
#include <thread>

Also, the C++ traffic generator uses APIs available as part of xtlm_ipc sources. For a list of the APIs and their corresponding usage, see Writing Traffic Generators in C++.

The Makefile dependencies are:

# Libraries directories
PROTO_PATH=$(XILINX_VIVADO)/data/simmodels/xsim/2023.1/lnx64/6.2.0/ext/protobuf/
IPC_XTLM= $(XILINX_VIVADO)/data/emulation/ip_utils/xtlm_ipc/xtlm_ipc_v1_0/cpp/src/
IPC_XTLM_INC= $(XILINX_VIVADO)/data/emulation/ip_utils/xtlm_ipc/xtlm_ipc_v1_0/cpp/inc/
LOCAL_IPC= $(IPC_XTLM)../

LD_LIBRARY_PATH:=$(XILINX_VIVADO)/data/simmodels/xsim/2023.1/lnx64/6.2.0/ext/protobuf/:$(XILINX_VIVADO)/lib/lnx64.o/Default:$(XILINX_VIVADO)/lib/lnx64.o/:$(LD_LIBRARY_PATH)

# Kernel directories
PLKERNELS_DIR := ../../pl_kernels
PLKERNELS := $(PLKERNELS_DIR)/polar_clip.cpp
PLHEADERS := $(PLKERNELS_DIR)/polar_clip.hpp $(PLKERNELS_DIR)/s2mm.hpp $(PLKERNELS_DIR)/mm2s.hpp

# XTLM source files
IPC_SRC := $(LOCAL_IPC)/src/axis/*.cpp $(LOCAL_IPC)/src/common/*.cpp $(LOCAL_IPC)/src/common/*.cc

# Compiler/linker flags
INC_FLAGS := -I$(LOCAL_IPC)/inc -I$(LOCAL_IPC)/inc/axis/ -I$(LOCAL_IPC)/inc/common/ -I$(PROTO_PATH)/include/ -I$(PLKERNELS_DIR) -I$(XILINX_HLS)/include
LIB_FLAGS := -L$(PROTO_PATH)/ -lprotobuf -L$(XILINX_VIVADO)/lib/lnx64.o/ -lrdizlib -L$(GCC)/../../lib64/ -lstdc++ -lpthread

# Compilation
compile: main.cpp $(PLHEADERS) $(PLKERNELS)
  $(GCC) -g main.cpp $(PLKERNELS) $(IPC_SRC) $(INC_FLAGS) $(LIB_FLAGS) -o chain

Below are the steps to integrate external traffic generator using C++ APIs in the AI Engine component:

  1. Declare the external PLIOs in the graph code as below:
    plin = input_plio::create("DataIn1",adf::plio_32_bits);
    plout = output_plio::create("DataOut1",adf::plio_32_bits);
    
  2. Instantiate AXI master and sender.
    xtlm_ipc::axis_master plin("DataIn1");
    xtlm_ipc::axis_slave plout("DataOut1");
    
  3. Prepare the data. This is the user logic.
  4. Send the data.

    A simple API is available if you prefer not to have fine granular control and send the data.

    std::vector<char> data; // The sender API expects data to be in the form of vector of char

    The C++ XTLM library provides the utility conversion APIs to easily convert user readible data type to byte array:

    std::vector<char> type 
    std::vector<char> byte_array = plin.uInt8ToByteArray(user_list)
    

    Here uint8 is the user data type and contains user list/array for uint8 type, such as std::vector<uint8> user_list;. For more details on conversion API, see Writing Traffic Generators in C++.

    // Write a user logic to fill in the data
    // Send the data using send_data() API call
    plin.send_data(byte_array, tlast) 
    

    For advanced users who need fine granular control over AXI4-Stream, see Advanced C++ Traffic Generator API.

  5. Receive the data.

    A simple API is available if you do not need fine grain control. It returns the data in the form of byte array (std::vector<char>) and waits until expected data_size is received.

    std::vector<char> data; // create empty vector of char
    plout.receive_data_with_size(data, data_size);
    

    The C++ library provides another set of utility conversion API to convert the received byte array into user readable data format. For example:

    std::vector<int8> recv_data;
    recv_data = plout .byteArrayTouInt8(data)
    

    For advanced users who need fine grain control over AXI4-Stream, see Advanced C++ Traffic Generator API.

    For more details on the API and their usage in the external traffic generator CPP code, see Writing Traffic Generators in C++.

The interest of the C++ traffic generator is that you can use and test your HLS kernels as soon as they are created, without having to synthesize them in a .xo file. This allows you to add more and more realism and flexibility to your simulations without having to recreate a .xclbin file.

Using SystemVerilog Traffic Generator APIs

You can also drive traffic generators from external System Verilog/Verilog traffic generators and test benches to the aiesimulator or x86simulator.

Prior to integrating a traffic generator module, declare the external PLIOs in the graph.

pl_in0 = adf::input_plio::create("in_classifier",adf::plio_32_bits);
out0 = adf::output_plio::create("out_interpolator",adf::plio_32_bits);

To establish the connection between the SystemVerilog/Verilog traffic generator and the AI Engine graph's external PLIOs, the xtlm_ipc SystemC modules are required. The external AI Engine wrapper is generated based on the external PLIO declarations in the ADF graph. Follow the steps below to generate this wrapper module for the AI Engine.

  1. Use the following command to perform the ADF graph compilation to generate a scsim_config.json file that resides in the work/config/scsim_config.json directory. This config file contains information on the PLIOs declared in the graph.

    aiecompiler --platform=$(PLATFORM) -v -log-level=3 --pl-freq=500 -include=./aie --output=graph.json aie/graph.cpp
  2. This config file is passed as an argument to the python script available inside ${XILINX_VITIS}/data/emulation/scripts/gen_aie_wrapper.py to generate the Verilog-based AI Engine wrapper module. For more details on how to generate the AI Engine wrapper module, see External RTL Traffic Generator and AI Engine Simulation.
  3. After generating the AI Engine wrapper module, you need to instantiate it in an external test bench to make the connection between the System Verilog/Verilog traffic generator and the AI Engine graph PLIOs. For details on integrating the wrapper module into the external RTL test bench, see Instantiating AI Engine Wrapper in the Test Bench.

After the connection is established, you can launch the HDL simulation in parallel with AI Enginesimulation or x86 simulator. For details on how to launch the HDL simulation, see Running the RTL Traffic Generator with AI Engine Simulation.