Coding a SystemC Model - 2023.2 English

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

Document ID
Release Date
2023.2 English

The process for defining a SystemC model uses the following steps:

  1. Include header files "xtlm_ap_ctrl.h" and "xtlm.h".
  2. Derive your kernel from a predefined class based on the supported kernel types: ap_ctrl_chain, ap_ctrl_hs, etc.
  3. Declare and define the AXI interfaces used on your kernel.
  4. Add required kernel arguments with the correct address offset and size.
  5. Write the kernel body in main() thread.

This process is demonstrated in the code example below.

When creating the SystemC model, you derive the kernel from a class-based on a supported control protocol: xtlm_ap_ctrl_chain, xtlm_ap_ctrl_hs, and xtlm_ap_ctrl_none. Use the following structure to create your SystemC model.

Tip: The following example is based on the simple vector addition (VADD) example design found in the Vitis_Accel_Examples on GitHub.
#include "xtlm.h"
#include "xtlm_ap_ctrl.h"
class vadd : public xsc::xtlm_ap_ctrl_hs
        vadd(sc_module_name name, xsc::common_cpp::properties& _properties):
            DEFINE_XTLM_AXIMM_MASTER_IF(in1, 32);
            DEFINE_XTLM_AXIMM_MASTER_IF(in2, 32);
            DEFINE_XTLM_AXIMM_MASTER_IF(out_r, 32);
           ADD_MEMORY_IF_ARG(in1,   0x10, 0x8);
           ADD_MEMORY_IF_ARG(in2,   0x18, 0x8);
           ADD_MEMORY_IF_ARG(out_r, 0x20, 0x8);
           ADD_SCALAR_ARG(size,     0x28, 0x4);
        //! Declare aximm interfaces..
        //! Declare scalar args...
        unsigned int size;
        void main_thread()
            wait(ev_ap_start); //! Wait on ap_start event...
            //! Copy kernel args configured by host...
            uint64_t  in1_base_addr = kernel_args[0];
            uint64_t  in2_base_addr = kernel_args[1];
            uint64_t  out_r_base_addr = kernel_args[2];
            size = kernel_args[3];
            unsigned data1, data2, data_r;
            for(int i = 0; i < size; i++) {
                in1->read(in1_base_addr + (i*4), (unsigned char*)&data1);  //! Read from in1 interface
                in2->read(in2_base_addr + (i*4), (unsigned char*)&data2);  //! Read from in2 interface
                //! Add data1 & data2 and write back result
                data_r = data1 + data2;                //! Add
                out_r->write(out_r_base_addr + (i*4), (unsigned char*)&data_r); //! Write the result
            ap_done(); //! completed Kernel computation...

The include files are available in the Vitis installation hierarchy under the $XILINX_Vivado/data/systemc/simlibs/ folder.

The kernel name is specified when defining the class for the SystemC model, as shown above, inheriting from the xtlm_ap_ctrl_hs class.

You must declare and define the AXI interfaces associated with the kernel arguments as shown by the following constructs:
The declaration associates the interface type with the argument. The definition defines the data width of the interface. You must also declare the register offsets and size for the kernel arguments as shown in the following:
ADD_MEMORY_IF_ARG(in1, 0x10, 0x8);

When specifying the kernel arguments, offsets, and size, these values should match the values reflected in the AXI4-Lite interface of the XRT-managed kernel as described in SW-Controllable Kernels and Control Requirements for XRT-Managed Kernels.

The addresses below 0x10 are reserved for use by XRT for managing kernel execution. Kernel arguments can be specified from 0x10 onwards. Most importantly the arguments, offsets, and size specified in the SystemC model should match the values used in the Vitis HLS or RTL kernel.

The kernel is executed in the SystemC main_thread. This thread waits until the ap_start bit is set by the host application, or XRT, at which time the kernel can process argument values as shown:

  1. The kernel waits for a signal to begin from XRT (ev_ap_start).
  2. Kernel arguments are mapped to variables in the SystemC model.
  3. The inputs are read.
  4. The vectors are added and the result is captured.
  5. The output is written back to the host.
  6. The finished signal is sent to XRT (ap_done).