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で読み込めば良い。