From 740e850c3aac363c8e1dac77c67f2a03cdfbd679 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Wed, 10 Jan 2018 05:24:58 +0000 Subject: [PATCH] Compile with xcpp --- requirements.txt | 3 +- setup.py | 22 +++- src/host.cpp | 149 +++++++++++++++++++++++++ src/host.hpp | 9 ++ src/xcl2.cpp | 279 +++++++++++++++++++++++++++++++++++++++++++++++ src/xcl2.hpp | 109 ++++++++++++++++++ 6 files changed, 566 insertions(+), 5 deletions(-) create mode 100644 src/host.cpp create mode 100644 src/host.hpp create mode 100755 src/xcl2.cpp create mode 100755 src/xcl2.hpp diff --git a/requirements.txt b/requirements.txt index ac38c0d..4d0c4c9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ -e . -pytest \ No newline at end of file +pytest +ipython \ No newline at end of file diff --git a/setup.py b/setup.py index 3972f06..7f3c213 100644 --- a/setup.py +++ b/setup.py @@ -19,9 +19,7 @@ AUTHOR = 'Joshua Massover' # What packages are required for this module to be executed? -REQUIRED = [ - 'psycopg2', 'sqlalchemy', 'sqlalchemy-wrapper', 'sqlalchemy-redshift', -] +REQUIRED = [] # The rest you shouldn't have to touch too much :) # ------------------------------------------------ @@ -74,6 +72,12 @@ def run(self): sys.exit() +extra_compile_args = ["-DSDX_PLATFORM=xilinx:aws-vu9p-f1:4ddr-xpr-2pr:4.0", + "-D__USE_XOPEN2K8", "-I/opt/Xilinx/SDx/2017.1.op/runtime/include/1_2/", + "-I/opt/Xilinx/SDx/2017.1.op/Vivado_HLS/include/"] + +extra_link_args = ["-shared", "-lxilinxopencl", "-lpthread", "-lrt", + "-L/opt/Xilinx/SDx/2017.1.op/runtime/lib/x86_64", "-lstdc++", ] # Where the magic happens: setup( @@ -85,7 +89,17 @@ def run(self): author_email=EMAIL, url=URL, packages=find_packages(exclude=('tests',)), - ext_modules=[Extension("_core", ["accel/_core.c", ])], + ext_modules=[Extension( + "_core", + ["accel/_core.c", "src/host.cpp", "src/xcl2.cpp"], + extra_compile_args=extra_compile_args, + extra_link_args=extra_link_args, + language='c++14', + runtime_library_dirs=[ + "/opt/Xilinx/SDx/2017.1.rte/runtime/lib/x86_64/", + ], + + )], # If your package is a single module, use this instead of 'packages': # py_modules=['slideshows'], diff --git a/src/host.cpp b/src/host.cpp new file mode 100644 index 0000000..9a0eb95 --- /dev/null +++ b/src/host.cpp @@ -0,0 +1,149 @@ +/********** +Copyright (c) 2017, Xilinx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********/ +#include "xcl2.hpp" +#include + +using std::vector; +static const int DATA_SIZE = 256; +static const std::string error_message = + "Error: Result mismatch:\n" + "i = %d CPU result = %d Device result = %d\n"; + + + + +vector> vector_sub( + vector> source_a, + vector> source_b +) { + + size_t size_in_bytes = DATA_SIZE * sizeof(int); + vector> source_results(DATA_SIZE); + + // The get_xil_devices will return vector of Xilinx Devices + std::vector devices = xcl::get_xil_devices(); + cl::Device device = devices[0]; + + //Creating Context and Command Queue for selected Device + cl::Context context(device); + cl::CommandQueue q(context, device, CL_QUEUE_PROFILING_ENABLE); + std::string device_name = device.getInfo(); + std::cout << "Found Device=" << device_name.c_str() << std::endl; + + // import_binary() command will find the OpenCL binary file created using the + // xocc compiler load into OpenCL Binary and return as Binaries + // OpenCL and it can contain many functions which can be executed on the + // device. + std::string binaryFile = xcl::find_binary_file(device_name,"src/vector_subtraction"); + cl::Program::Binaries bins = xcl::import_binary_file(binaryFile); + devices.resize(1); + cl::Program program(context, devices, bins); + + // These commands will allocate memory on the FPGA. The cl::Buffer objects can + // be used to reference the memory locations on the device. The cl::Buffer + // object cannot be referenced directly and must be passed to other OpenCL + // functions. + cl::Buffer buffer_a(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, + size_in_bytes, source_a.data()); + cl::Buffer buffer_b(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, + size_in_bytes, source_b.data()); + cl::Buffer buffer_result(context, CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, + size_in_bytes, source_results.data()); + //Separate Read/write Buffer vector is needed to migrate data between host/device + std::vector inBufVec, outBufVec; + inBufVec.push_back(buffer_a); + inBufVec.push_back(buffer_b); + outBufVec.push_back(buffer_result); + + + // These commands will load the source_a and source_b vectors from the host + // application and into the buffer_a and buffer_b cl::Buffer objects. The data + // will be be transferred from system memory over PCIe to the FPGA on-board + // DDR memory. + q.enqueueMigrateMemObjects(inBufVec,0/* 0 means from host*/); + + // This call will extract a kernel out of the program we loaded in the + // previous line. A kernel is an OpenCL function that is executed on the + // FPGA. This function is defined in the src/vetor_addition.cl file. + cl::Kernel krnl_vector_sub(program,"vector_sub"); + + //set the kernel Arguments + int narg=0; + krnl_vector_sub.setArg(narg++,buffer_result); + krnl_vector_sub.setArg(narg++,buffer_a); + krnl_vector_sub.setArg(narg++,buffer_b); + krnl_vector_sub.setArg(narg++,DATA_SIZE); + + //Launch the Kernel + q.enqueueTask(krnl_vector_sub); + + // The result of the previous kernel execution will need to be retrieved in + // order to view the results. This call will write the data from the + // buffer_result cl_mem object to the source_results vector + q.enqueueMigrateMemObjects(outBufVec,CL_MIGRATE_MEM_OBJECT_HOST); + q.finish(); + + return source_results; +} + + + +// This example illustrates the very simple OpenCL example that performs +// an addition on two vectors +int main(int argc, char **argv) { + if (argc != 3){ + std::cout << "Please provide integers to add" << std::endl; + return 1; + } + + // Creates a vector of DATA_SIZE elements with an initial value + // passed in as arguments + vector> source_a(DATA_SIZE, atoi(argv[1])); + vector> source_b(DATA_SIZE, atoi(argv[2])); + vector> source_results(DATA_SIZE); + + source_results = vector_sub(source_a, source_b); + + int match = 0; + printf("Result = \n"); + for (int i = 0; i < DATA_SIZE; i++) { + int host_result = source_a[i] - source_b[i]; + if (source_results[i] != host_result) { + printf(error_message.c_str(), i, host_result, source_results[i]); + match = 1; + break; + } else { + printf("%d ", source_results[i]); + if (((i + 1) % 16) == 0) printf("\n"); + } + } + + std::cout << "TEST " << (match ? "FAILED" : "PASSED") << std::endl; + return (match ? EXIT_FAILURE : EXIT_SUCCESS); +} diff --git a/src/host.hpp b/src/host.hpp new file mode 100644 index 0000000..eb710b5 --- /dev/null +++ b/src/host.hpp @@ -0,0 +1,9 @@ +#include +#include "xcl2.hpp" + +using std::vector; + +vector> vector_sub( + vector> source_a, + vector> source_b +); \ No newline at end of file diff --git a/src/xcl2.cpp b/src/xcl2.cpp new file mode 100755 index 0000000..eb2805b --- /dev/null +++ b/src/xcl2.cpp @@ -0,0 +1,279 @@ +/********** +Copyright (c) 2017, Xilinx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********/ + +#include +#include +#include +#include "xcl2.hpp" +namespace xcl { +std::vector get_devices(const std::string& vendor_name) { + + size_t i; + std::vector platforms; + cl::Platform::get(&platforms); + cl::Platform platform; + for (i = 0 ; i < platforms.size(); i++){ + platform = platforms[i]; + std::string platformName = platform.getInfo(); + std::cout << "platform Name: " << platformName.c_str() << std::endl + << "Vendor Name : " << vendor_name.c_str() + << std::endl; + if (platformName == vendor_name){ + std::cout << "Found Platform" << 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 devices; + platform.getDevices(CL_DEVICE_TYPE_ACCELERATOR, &devices); + return devices; +} + +std::vector get_xil_devices() { + return get_devices("Xilinx"); +} +cl::Program::Binaries import_binary_file(std::string xclbin_file_name) +{ + std::cout << "INFO: Importing " << 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); + unsigned nb = bin_file.tellg(); + bin_file.seekg (0, bin_file.beg); + char *buf = new char [nb]; + bin_file.read(buf, nb); + + cl::Program::Binaries bins; + bins.push_back({buf,nb}); + return bins; +} + +std::string +find_binary_file(const std::string& _device_name, const std::string& xclbin_name) +{ + std::cout << "XCLBIN File Name: " << xclbin_name.c_str() << std::endl; + char *xcl_mode = getenv("XCL_EMULATION_MODE"); + char *xcl_target = getenv("XCL_TARGET"); + std::string mode; + + /* Fall back mode if XCL_EMULATION_MODE is not set is "hw" */ + if(xcl_mode == NULL) { + mode = "hw"; + } else { + /* if xcl_mode is set then check if it's equal to true*/ + if(strcmp(xcl_mode,"true") == 0) { + /* if it's true, then check if xcl_target is set */ + if(xcl_target == NULL) { + /* default if emulation but not specified is software emulation */ + mode = "sw_emu"; + } else { + /* otherwise, it's what ever is specified in XCL_TARGET */ + mode = xcl_target; + } + } else { + /* if it's not equal to true then it should be whatever + * XCL_EMULATION_MODE is set to */ + mode = xcl_mode; + } + } + char *xcl_bindir = getenv("XCL_BINDIR"); + + // typical locations of directory containing xclbin files + const char *dirs[] = { + xcl_bindir, // $XCL_BINDIR-specified + "xclbin", // command line build + "..", // gui build + run + ".", // gui build, run in build directory + NULL + }; + const char **search_dirs = dirs; + if (xcl_bindir == NULL) { + search_dirs++; + } + + char *device_name = strdup(_device_name.c_str()); + if (device_name == NULL) { + printf("Error: Out of Memory\n"); + exit(EXIT_FAILURE); + } + + // fix up device name to avoid colons and dots. + // xilinx:xil-accel-rd-ku115:4ddr-xpr:3.2 -> xilinx_xil-accel-rd-ku115_4ddr-xpr_3_2 + for (char *c = device_name; *c != 0; c++) { + if (*c == ':' || *c == '.') { + *c = '_'; + } + } + + char *device_name_versionless = strdup(_device_name.c_str()); + if (device_name_versionless == NULL) { + printf("Error: Out of Memory\n"); + exit(EXIT_FAILURE); + } + + unsigned short colons = 0; + bool colon_exist = false; + for (char *c = device_name_versionless; *c != 0; c++) { + if (*c == ':') { + colons++; + *c = '_'; + colon_exist = true; + } + /* Zero out version area */ + if (colons == 3) { + *c = '\0'; + } + } + + // versionless support if colon doesn't exist in device_name + if(!colon_exist) { + int len = strlen(device_name_versionless); + device_name_versionless[len - 4] = '\0'; + } + + const char *aws_file_patterns[] = { + "%1$s/%2$s.%3$s.%4$s.awsxclbin", // ...awsxclbin + "%1$s/%2$s.%3$s.%5$s.awsxclbin", // ...awsxclbin + "%1$s/binary_container_1.awsxclbin", // default for gui projects + "%1$s/%2$s.xclbin", // .awsxclbin + NULL + }; + + const char *file_patterns[] = { + "%1$s/%2$s.%3$s.%4$s.xclbin", // ...xclbin + "%1$s/%2$s.%3$s.%5$s.xclbin", // ...xclbin + "%1$s/binary_container_1.xclbin", // default for gui projects + "%1$s/%2$s.xclbin", // .xclbin + NULL + }; + char xclbin_file_name[PATH_MAX]; + memset(xclbin_file_name, 0, PATH_MAX); + ino_t aws_ino = 0; // used to avoid errors if an xclbin found via multiple/repeated paths + for (const char **dir = search_dirs; *dir != NULL; dir++) { + struct stat sb; + if (stat(*dir, &sb) == 0 && S_ISDIR(sb.st_mode)) { + for (const char **pattern = aws_file_patterns; *pattern != NULL; pattern++) { + char file_name[PATH_MAX]; + memset(file_name, 0, PATH_MAX); + snprintf(file_name, PATH_MAX, *pattern, *dir, xclbin_name.c_str(), mode.c_str(), device_name, device_name_versionless); + if (stat(file_name, &sb) == 0 && S_ISREG(sb.st_mode)) { + char* bindir = strdup(*dir); + if (bindir == NULL) { + printf("Error: Out of Memory\n"); + exit(EXIT_FAILURE); + } + if (*xclbin_file_name && sb.st_ino != aws_ino) { + printf("Error: multiple xclbin files discovered:\n %s\n %s\n", file_name, xclbin_file_name); + exit(EXIT_FAILURE); + } + aws_ino = sb.st_ino; + strncpy(xclbin_file_name, file_name, PATH_MAX); + } + } + } + } + ino_t ino = 0; // used to avoid errors if an xclbin found via multiple/repeated paths + // if no awsxclbin found, check for xclbin + if (*xclbin_file_name == '\0') { + for (const char **dir = search_dirs; *dir != NULL; dir++) { + struct stat sb; + if (stat(*dir, &sb) == 0 && S_ISDIR(sb.st_mode)) { + for (const char **pattern = file_patterns; *pattern != NULL; pattern++) { + char file_name[PATH_MAX]; + memset(file_name, 0, PATH_MAX); + snprintf(file_name, PATH_MAX, *pattern, *dir, xclbin_name.c_str(), mode.c_str(), device_name, device_name_versionless); + if (stat(file_name, &sb) == 0 && S_ISREG(sb.st_mode)) { + char* bindir = strdup(*dir); + if (bindir == NULL) { + printf("Error: Out of Memory\n"); + exit(EXIT_FAILURE); + } + if (*xclbin_file_name && sb.st_ino != ino) { + printf("Error: multiple xclbin files discovered:\n %s\n %s\n", file_name, xclbin_file_name); + exit(EXIT_FAILURE); + } + ino = sb.st_ino; + strncpy(xclbin_file_name, file_name, PATH_MAX); + } + } + } + } + } + // if no xclbin found, preferred path for error message from xcl_import_binary_file() + if (*xclbin_file_name == '\0') { + snprintf(xclbin_file_name, PATH_MAX, file_patterns[0], *search_dirs, xclbin_name.c_str(), mode.c_str(), device_name); + } + free(device_name); + return (xclbin_file_name); +} + +bool is_emulation() +{ + bool ret =false; + char *xcl_mode = getenv("XCL_EMULATION_MODE"); + if (xcl_mode != NULL){ + ret = true; + } + return ret; +} + +bool is_hw_emulation() +{ + bool ret =false; + char *xcl_mode = getenv("XCL_EMULATION_MODE"); + if ((xcl_mode != NULL) && !strcmp(xcl_mode, "hw_emu")){ + ret = true; + } + return ret; +} + +bool is_xpr_device(const char *device_name) { + const char *output = strstr(device_name,"xpr"); + + if(output==NULL) { + return false; + } + else { + return true; + } +} + + +}; diff --git a/src/xcl2.hpp b/src/xcl2.hpp new file mode 100755 index 0000000..dd15a60 --- /dev/null +++ b/src/xcl2.hpp @@ -0,0 +1,109 @@ +/********** +Copyright (c) 2017, Xilinx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**********/ + + +#pragma once + +#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 + +#include +#include +#include + +// When creating a buffer with user pointer (CL_MEM_USE_HOST_PTR), under the hood +// User ptr is used if and only if it is properly aligned (page aligned). When not +// aligned, runtime has no choice but to create its own host side buffer that backs +// user ptr. This in turn implies that all operations that move data to and from +// device incur an extra memcpy to move data to/from runtime's own host buffer +// from/to user pointer. So it is recommended to use this allocator if user wish to +// Create Buffer/Memory Object with CL_MEM_USE_HOST_PTR to align user buffer to the +// page boundary. It will ensure that user buffer will be used when user create +// Buffer/Mem Object with CL_MEM_USE_HOST_PTR. +template +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(ptr); + } + void deallocate(T* p, std::size_t num) + { + free(p); + } +}; + +namespace xcl { +std::vector get_xil_devices(); +std::vector get_devices(const std::string& vendor_name); +/* find_xclbin_file + * + * + * Description: + * Find precompiled program (as commonly created by the Xilinx OpenCL + * flow). Using search path below. + * + * Search Path: + * $XCL_BINDIR/...xclbin + * $XCL_BINDIR/...xclbin + * $XCL_BINDIR/binary_container_1.xclbin + * $XCL_BINDIR/.xclbin + * xclbin/...xclbin + * xclbin/...xclbin + * xclbin/binary_container_1.xclbin + * xclbin/.xclbin + * ../...xclbin + * ../...xclbin + * ../binary_container_1.xclbin + * ../.xclbin + * ./...xclbin + * ./...xclbin + * ./binary_container_1.xclbin + * ./.xclbin + * + * Inputs: + * _device_name - Targeted Device name + * xclbin_name - base name of the xclbin to import. + * + * Returns: + * An opencl program Binaries object that was created from xclbin_name file. + */ +std::string find_binary_file(const std::string& _device_name, const std::string& xclbin_name); +cl::Program::Binaries import_binary_file(std::string xclbin_file_name); +bool is_emulation () ; +bool is_hw_emulation () ; +bool is_xpr_device (const char *device_name); + +}