2022/12/10

vitisのサンプルプログラムを自分用に修正した

XILINX   

mibroによるPixabayからの画像

Vitisのサンプルプログラムvector addを修正して、自分のプログラムを動作させる

カーネル関数

HLSするカーネルの関数は、自作した簡単なプログラムだ。引数を加算するだけだ。この関数がFPGA内で動作しているはずだ。

#include <stdint.h>

extern "C" {


void krnl_vadd(uint32_t* in1, uint32_t* in2, uint32_t* out, int size) {
	int i;
	for (i=0;i<size;i++) {
		out[i] = in1[i] + in2[i];
	}
}
}

引数の準備

HLS関数に渡す入力だ。適当に、値を設定する

void set_input_data(int * ptr_a, int *ptr_b) {
    int i;
    for(i=0;i<DATA_SIZE;i++) {
    	ptr_a[i] = i;
    }
    for(i=0;i<DATA_SIZE;i++) {
    	ptr_b[i] = DATA_SIZE - i + 33;
    }
}

結果の確認

カーネル関数の結果をCPU上で確認する

bool verify_result_ng(int *ptr_a, int* ptr_b, int *ptr_result)  {
    for (int i = 0; i < DATA_SIZE; i++) {
        int host_result = ptr_a[i] + ptr_b[i];
        if (ptr_result[i] != host_result) {
            printf(error_message.c_str(), i, ptr_a[i], ptr_b[i], host_result, ptr_result[i]);
            return true;
        }
    }
    return false;

}

main関数

ほぼ、サンプルプログラム通り。意味がわからない部分もあるが、転記する

Xilinxのプラットフォームの確認

    // traversing all Platforms To find Xilinx Platform and targeted
    // Device in Xilinx Platform
    cl::Platform::get(&platforms);
    for (size_t i = 0; (i < platforms.size()) & (found_device == false); i++) {
        cl::Platform platform = platforms[i];
        std::string platformName = platform.getInfo<CL_PLATFORM_NAME>();
        if (platformName == "Xilinx") {
            devices.clear();
            platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices);
            if (devices.size()) {
                found_device = true;
                break;
            }
        }
    }

カーネル関数をファイルから読み出して確認する

    FILE* fp;
    if ((fp = fopen(xclbinFilename.c_str(), "r")) == nullptr) {
        printf("ERROR: %s xclbin not available please build\n", xclbinFilename.c_str());
        exit(EXIT_FAILURE);
    }
    // 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});
    bool valid_device = false;
    for (unsigned int i = 0; i < devices.size(); i++) {
        auto device = devices[i];
        // Creating Context and Command Queue for selected Device
        OCL_CHECK(err, context = cl::Context(device, nullptr, nullptr, nullptr, &err));
        OCL_CHECK(err, q = cl::CommandQueue(context, device, CL_QUEUE_PROFILING_ENABLE, &err));
        std::cout << "Trying to program device[" << i << "]: " << device.getInfo<CL_DEVICE_NAME>() << std::endl;
        cl::Program program(context, {device}, bins, nullptr, &err);
        if (err != CL_SUCCESS) {
            std::cout << "Failed to program device[" << i << "] with xclbin file!\n";
        } else {
            std::cout << "Device[" << i << "]: program successful!\n";
            OCL_CHECK(err, krnl_vector_add = cl::Kernel(program, "krnl_vadd", &err));
            valid_device = true;
            break; // we break because we found a valid device
        }
    }

kernel関数に渡すようのメモリを準備

    OCL_CHECK(err, cl::Buffer buffer_a(context, CL_MEM_READ_ONLY, size_in_bytes, NULL, &err));
    OCL_CHECK(err, cl::Buffer buffer_b(context, CL_MEM_READ_ONLY, size_in_bytes, NULL, &err));
    OCL_CHECK(err, cl::Buffer buffer_result(context, CL_MEM_WRITE_ONLY, size_in_bytes, NULL, &err));

引数の設定

    OCL_CHECK(err, err = krnl_vector_add.setArg(narg++, buffer_a));
    OCL_CHECK(err, err = krnl_vector_add.setArg(narg++, buffer_b));
    OCL_CHECK(err, err = krnl_vector_add.setArg(narg++, buffer_result));
    OCL_CHECK(err, err = krnl_vector_add.setArg(narg++, DATA_SIZE));

引数のメモリのポインタを取得

    OCL_CHECK(err,
              ptr_a = (int*)q.enqueueMapBuffer(buffer_a, CL_TRUE, CL_MAP_WRITE, 0, size_in_bytes, NULL, NULL, &err));
    OCL_CHECK(err,
              ptr_b = (int*)q.enqueueMapBuffer(buffer_b, CL_TRUE, CL_MAP_WRITE, 0, size_in_bytes, NULL, NULL, &err));
    OCL_CHECK(err, ptr_result = (int*)q.enqueueMapBuffer(buffer_result, CL_TRUE, CL_MAP_READ, 0, size_in_bytes, NULL,
                                                         NULL, &err));

引数を設定して、Kernel関数を実行して、データを移動して、結果を確認する

    set_input_data(ptr_a, ptr_b);

    // Data will be migrated to kernel space
    OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_a, buffer_b}, 0 /* 0 means from host*/));

    // Launch the Kernel
    OCL_CHECK(err, err = q.enqueueTask(krnl_vector_add));



    // 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
    OCL_CHECK(err, q.enqueueMigrateMemObjects({buffer_result}, CL_MIGRATE_MEM_OBJECT_HOST));

    OCL_CHECK(err, q.finish());

    // Verify the result
    bool match = verify_result_ng(ptr_a, ptr_b,ptr_result);

実行方法

生成したプログラムをkv260に転送して実行する

kv260に転送する

shell.jsonは、自分で作成する

scp shell.json pl.dtbo binary_container_1.bin add2  petalinux@192.168.1.89:/home/petalinux/add2/

次にkv260上で実行する

chmod +x add2
sudo cp pl.dtbo binary_container_1.bin shell.json /lib/firmware/xilinx/add2/
sudo xmutil listapps
sudo xmutil unloadapp
sudo xmutil loadapp add2
./add2 binary_container_1.bin

作成したworkspaceを以下に置く。Vitisで読み込めば良い。

https://github.com/y38y38/hls_kernel_platform

Prev:«

Next: »