FC2カウンター FPGAの部屋 Vivado HLS の Vitis Bottom Up Flow を使用する3
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

Vivado HLS の Vitis Bottom Up Flow を使用する3

Vivado HLS の Vitis Bottom Up Flow を使用する2”の続き。

前回は、Vitis 2019.2 のアクセラレーション・プロジェクト用のVivado HLS 2019.2 プロジェクトの square_cubed プロジェクトで xo ファイルを作成した。今回は、Vitis 2019.2 で square_cubed アクセラレーション・アプリケーション・プロジェクトを作成して実機で動作を確認しよう。

1. Vitis 2019.2 で ultra96v2_min2 プラットフォームを使用した square_cubed アプリケーション・プロジェクトを作成した。
2. ”Vivado HLS の Vitis Bottom Up Flow を使用する2”で作成した square_cubed.xo を square_cubed_system -> square_cubed -> src に追加した。
3. square_cubed_system -> square_cubed -> src に square_cubed_host.cpp を新規作成した。
4. xclbin ファイル名を squzre_cubed に変更した。
5. Assistant ウインドウで square_cubed_system -> square_cubed -> Hardware を右クリックし右クリックメニューからBuild を選択してビルドし、成功した。
square_cubed_19_200108.png

square_cubed_host.cpp を貼っておく。

// square_cubed_host.cpp
// 2020/01/06 by marsee
//

// Vitis-Tutorials/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp のコードを引用します
// https://github.com/Xilinx/Vitis-Tutorials/blob/master/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp
#define CL_HPP_CL_1_2_DEFAULT_BUILD
#define CL_HPP_TARGET_OPENCL_VERSION 120
#define CL_HPP_MINIMUM_OPENCL_VERSION 120
#define CL_HPP_ENABLE_PROGRAM_CONSTRUCTION_FROM_ARRAY_COMPATIBILITY 1
#define CL_USE_DEPRECATED_OPENCL_1_2_APIS

#include <vector>
#include <CL/cl2.hpp>
#include <iostream>
#include <fstream>
#include <CL/cl_ext_xilinx.h>
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>

static const std::string error_message =
    "Error: Result mismatch:\n"
    "i = %d CPU result = %d Device result = %d\n";

//Some Library functions to be used.
template <typename T>
struct aligned_allocator
{
  using value_type = T;
  T* allocate(std::size_t num)
  {
    void* ptr = nullptr;
    if (posix_memalign(&ptr,4096,num*sizeof(T)))
      throw std::bad_alloc();
    return reinterpret_cast<T*>(ptr);
  }
  void deallocate(T* p, std::size_t num)
  {
    free(p);
  }
};


#define OCL_CHECK(error,call)                                       \
    call;                                                           \
    if (error != CL_SUCCESS) {                                      \
      printf("%s:%d Error calling " #call ", error code is: %d\n",  \
              __FILE__,__LINE__, error);                            \
      exit(EXIT_FAILURE);                                           \
    }

namespace xcl {
std::vector<cl::Device> get_devices(const std::string& vendor_name) {

    size_t i;
    cl_int err;
    std::vector<cl::Platform> platforms;
    OCL_CHECK(err, err = cl::Platform::get(&platforms));
    cl::Platform platform;
    for (i  = 0 ; i < platforms.size(); i++){
        platform = platforms[i];
        OCL_CHECK(err, std::string platformName = platform.getInfo<CL_PLATFORM_NAME>(&err));
        if (platformName == vendor_name){
            std::cout << "Found Platform" << std::endl;
            std::cout << "Platform Name: " << platformName.c_str() << std::endl;
            break;
        }
    }
    if (i == platforms.size()) {
        std::cout << "Error: Failed to find Xilinx platform" << std::endl;
        exit(EXIT_FAILURE);
    }

    //Getting ACCELERATOR Devices and selecting 1st such device
    std::vector<cl::Device> devices;
    OCL_CHECK(err, err = platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices));
    return devices;
}

std::vector<cl::Device> get_xil_devices() {
    return get_devices("Xilinx");
}

char* read_binary_file(const std::string &xclbin_file_name, unsigned &nb)
{
    std::cout << "INFO: Reading " << xclbin_file_name << std::endl;

    if(access(xclbin_file_name.c_str(), R_OK) != 0) {
        printf("ERROR: %s xclbin not available please build\n", xclbin_file_name.c_str());
        exit(EXIT_FAILURE);
    }
    //Loading XCL Bin into char buffer
    std::cout << "Loading: '" << xclbin_file_name.c_str() << "'\n";
    std::ifstream bin_file(xclbin_file_name.c_str(), std::ifstream::binary);
    bin_file.seekg (0, bin_file.end);
    nb = bin_file.tellg();
    bin_file.seekg (0, bin_file.beg);
    char *buf = new char [nb];
    bin_file.read(buf, nb);
    return buf;
}
};
// Vitis-Tutorials/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp のコードを引用終了

#define DATA_SIZE 10

// Vitis-Tutorials/docs/mixing-c-rtl-kernels/reference-files/src/host/host_step1.cpp のコードを自分用に変更して引用します
int main(int argc, char* argv[])
{
    const char* xclbinFilename;

    if (argc==2) {
        xclbinFilename = argv[1];
        std::cout <<"Using FPGA binary file specfied through the command line: " << xclbinFilename << std::endl;
    }
    else {
        xclbinFilename = "../lap_filter_axim.xclbin";
        std::cout << "No FPGA binary file specified through the command line, using:" << xclbinFilename <<std::endl;
    }

    std::vector<int32_t,aligned_allocator<int32_t>> in_data(DATA_SIZE);
    std::vector<int32_t,aligned_allocator<int32_t>> square_data(DATA_SIZE);
    std::vector<int32_t,aligned_allocator<int32_t>> cubed_data(DATA_SIZE);
    size_t size_in_bytes = (DATA_SIZE) * sizeof(int32_t);

    // input data
    for(int i=0; i<DATA_SIZE; i++){
        in_data[i] = i;
        square_data[i] = 0;
    }

    std::vector<cl::Device> devices = xcl::get_xil_devices();
    cl::Device device = devices[0];
    devices.resize(1);


    // Creating Context and Command Queue for selected device
    cl::Context context(device);
    cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE);

    // Load xclbin
    std::cout << "Loading: '" << xclbinFilename << "'\n";
    std::ifstream bin_file(xclbinFilename, std::ifstream::binary);
    bin_file.seekg (0, bin_file.end);
    unsigned nb = bin_file.tellg();
    bin_file.seekg (0, bin_file.beg);
    char *buf = new char [nb];
    bin_file.read(buf, nb);

    // Creating Program from Binary File
    cl::Program::Binaries bins;
    bins.push_back({buf,nb});
    cl::Program program(context, devices, bins);

    // This call will get the kernel object from program. A kernel is an
    // OpenCL function that is executed on the FPGA.
    cl::Kernel krnl_squara_cubed(program,"square_cubed");

    // These commands will allocate memory on the Device. The cl::Buffer objects can
    // be used to reference the memory locations on the device.
    cl::Buffer ind_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY,
            size_in_bytes, in_data.data());
    cl::Buffer squared_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE,
            size_in_bytes, square_data.data());
    cl::Buffer cubed_buf(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_WRITE,
            size_in_bytes, cubed_data.data());

    // Data will be transferred from system memory over PCIe to the FPGA on-board
    // DDR memory.
    q.enqueueMigrateMemObjects({ind_buf},0/* 0 means from host*/);

    //set the kernel Arguments
    krnl_squara_cubed.setArg(0,ind_buf);
    krnl_squara_cubed.setArg(1,squared_buf);
    krnl_squara_cubed.setArg(2,cubed_buf);

    //Launch the Kernel
    q.enqueueTask(krnl_squara_cubed);

    // The result of the previous kernel execution will need to be retrieved in
    // order to view the results. This call will transfer the data from FPGA to
    // source_results vector

    q.enqueueMigrateMemObjects({squared_buf, cubed_buf},CL_MIGRATE_MEM_OBJECT_HOST);

    q.finish();

    // Compare the results
    int error = 0;
    for(int i=0; i<DATA_SIZE; i++){
        if(square_data[i] != i*i || cubed_data[i] != i*i*i){
            std::cout << "Error: i = " << i << " i^2 = " << i*i << "  square_data = " << int(square_data[i]) <<
                    "  cubed_data = " << int(cubed_data[i]) << std::endl;
            error = 1;
        }else{
            //std::cout << "Error: i = " << i << " i^2 = " << i*i << "  square_data = " << int(square_data[i]) <<
                //"  cubed_data = " << int(cubed_data[i]) << std::endl;
        }
    }

    std::cout << "TEST " << (error ? "FAILED" : "PASSED") << std::endl;
    return (error ? EXIT_FAILURE : EXIT_SUCCESS);
}


まずは、Ultra96-V2 を起動して、scp で square_cubed の BOOT.BIN を Micro SD カードの第 1 パーティションにコピーした。
scp BOOT.BIN 192.168.3.23:/run/media/mmcblk0p1
square_cubed_13_200108.png

reboot した。
リブート後に zocl ドライバを起動した。
insmod /lib/modules/4.19.0-xilinx-v2019.2/extra/zocl.ko
square_cubed_16_200108.png

Assistant ウインドウで square_cubed_system -> square_cubed -> Hardware を右クリックし右クリックメニューから Run -> Run Configurations... を選択して、Debugger_square_cubed コンフィギュレーションを作成した。
square_cubed_14_200108.png

square_cubed_15_200108.png

Run ボタンをクリックしたところ、Vitis の Console に TEST PASSED が表示された。成功だ。
square_cubed_17_200108.png

Debugger_square_cubed コンフィギュレーションを起動した時のシリアル・コンソールの様子を示す。
square_cubed_18_200108.png
  1. 2020年01月09日 04:54 |
  2. Vitis
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
https://marsee101.blog.fc2.com/tb.php/4759-f0dce6a2
この記事にトラックバックする(FC2ブログユーザー)