diff options
Diffstat (limited to 'python/openvino/runtime/coredla_device/mmd/de10_agilex')
43 files changed, 11917 insertions, 0 deletions
diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/.gitignore b/python/openvino/runtime/coredla_device/mmd/de10_agilex/.gitignore new file mode 100644 index 0000000..66e06bf --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/.gitignore @@ -0,0 +1,18 @@ +*~ +*# +*.marks +release_build/ +build/ +example_designs/mem_bandwidth/bin/ +example_designs/mem_bandwidth/simulation.tar.gz +example_designs/mem_bandwidth/temp_simulation/ +linux64/lib/ +linux64/libexec/diagnose +linux64/libexec/program +ase/mpf_src +*.pyc +*.swp +*.kwlp +*.kwps +temp_simulation/ +simulation.tar.gz diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/.sync_master b/python/openvino/runtime/coredla_device/mmd/de10_agilex/.sync_master new file mode 100644 index 0000000..835c7e0 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/.sync_master @@ -0,0 +1 @@ +sc diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/CMakeLists.txt b/python/openvino/runtime/coredla_device/mmd/de10_agilex/CMakeLists.txt new file mode 100644 index 0000000..e7e4584 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/CMakeLists.txt @@ -0,0 +1,144 @@ +# (C) 2017 Intel Corporation. All rights reserved. +# Your use of Intel Corporation's design tools, logic functions and other +# software and tools, and its AMPP partner logic functions, and any output +# files any of the foregoing (including device programming or simulation +# files), and any associated documentation or information are expressly subject +# to the terms and conditions of the Intel Program License Subscription +# Agreement, Intel MegaCore Function License Agreement, or other applicable +# license agreement, including, without limitation, that your use is for the +# sole purpose of programming logic devices manufactured by Intel and sold by +# Intel or its authorized distributors. Please refer to the applicable +# agreement for further details. + +cmake_minimum_required(VERSION 2.8.12) +project(mmd) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +# DLA specific modifications made to the MMD +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDLA_MMD") + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_MAX_DEVICE=128") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOPTION3=1 -DACL_USE_DMA=1") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_HAS_STDLIB_STDIO") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_OPENCL_HOST_BIT=64 -DACL_TARGET_BIT=64") + +# Select PCIE Gen3 x16 +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGEN3_x16") + +if (WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DAOCL_MMD_CALL=__declspec(dllexport)") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_COMPILER_IS_MSVC=1 -DACL_HOST_RUNTIME_IS_STATIC=1") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_OPENCL_HOST_SYS=windows -DACL_TARGET_SYS=windows -DWINDOWS") +endif() + +# from the opencl makefile +if (NOT WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKERNEL_64BIT -O3 -DACL_COMPILER_IS_MSVC=0 -DACL_HOST_RUNTIME_IS_STATIC=0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -fstack-protector -Wformat -Wformat-security -D_GLIBCXX_USE_CXX11_ABI=0 -O2") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DACL_OPENCL_HOST_SYS=linux -DACL_TARGET_SYS=linux -DLINUX") + # Release build only + set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2") +endif() + +enable_language(C ASM) + +set(ASM_OPTIONS "-x assembler-with-cpp") +if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang") + set(ASM_OPTIONS "${ASM_OPTIONS} -no-integrated-as") +endif() + +set(CMAKE_ASM_FLAGS "${CFLAGS} ${ASM_OPTIONS}") + +set(MMD_SRC + ./host/acl_pcie_config.cpp + ./host/acl_pcie.cpp + ./host/acl_pcie_debug.cpp + ./host/acl_pcie_device.cpp + ./host/acl_pcie_dma_linux.cpp + ./host/acl_pcie_dma_windows.cpp + ./host/acl_pcie_hostch.cpp + ./host/acl_pcie_mm_io.cpp + ./host/acl_pcie_timer.cpp +) + +add_library(de10_agilex_mmd SHARED ${MMD_SRC}) + +target_include_directories(de10_agilex_mmd PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +if (WIN32) + # Terrasic production BSP Linux kernel space driver header files + set(TERASIC_KERNEL_HEADER_DIR $ENV{AOCL_BOARD_PACKAGE_ROOT}/linux64/driver) + set(TERASIC_KERNEL_HEADER_FILES + fpga_cmd_guids.h + hw_host_channel.h + hw_pcie_constants.h + hw_pcie_dma.h + ) + if (EXISTS ${TERASIC_KERNEL_HEADER_DIR}) + foreach(header ${TERASIC_KERNEL_HEADER_FILES}) + if (EXISTS ${TERASIC_KERNEL_HEADER_DIR}/${header}) + file(COPY ${TERASIC_KERNEL_HEADER_DIR}/${header} DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/include) + else() + message(WARNING "Header file ${header} does not exist in ${TERASIC_KERNEL_HEADER_DIR}") + endif() + endforeach() + else() + message(FATAL_ERROR "Source directory ${TERASIC_KERNEL_HEADER_DIR} does not exist.") + endif() + + set(HW_PCI_DMA_H ${CMAKE_CURRENT_SOURCE_DIR}/include/hw_pcie_dma.h) + file(READ ${HW_PCI_DMA_H} HW_PCI_DMA_H_CONTENT) + # Remove any end-of-line whitespace from the file content (spaces and tabs) + string(REGEX REPLACE "[ \t]+(\r?\n)" "\\1" HW_PCI_DMA_H_CONTENT "${HW_PCI_DMA_H_CONTENT}") + set(OLD_CODE_BLOCK +"PACK( +struct DMA_DESC_ENTRY { + UINT32 src_addr_ldw; + UINT32 src_addr_udw; + UINT32 dest_addr_ldw; + UINT32 dest_addr_udw; + UINT32 ctl_dma_len; + UINT32 reserved[3]; +});") + set(NEW_CODE_BLOCK +"#if defined(GEN3_x8) +PACK( +struct DMA_DESC_ENTRY { + UINT32 src_addr_ldw; + UINT32 src_addr_udw; + UINT32 dest_addr_ldw; + UINT32 dest_addr_udw; + UINT32 ctl_dma_len; + UINT32 reserved[3]; +}); +#elif defined(GEN3_x16) +PACK( +struct DMA_DESC_ENTRY { + UINT64 src_addr; + UINT64 dst_addr; + UINT32 ctrl; + UINT32 reserved[3]; +}); +#endif") + string(REPLACE "${OLD_CODE_BLOCK}" "${NEW_CODE_BLOCK}" HW_PCI_DMA_H_CONTENT "${HW_PCI_DMA_H_CONTENT}") + file(WRITE ${HW_PCI_DMA_H} "${HW_PCI_DMA_H_CONTENT}") + + set_target_properties(de10_agilex_mmd PROPERTIES LINK_FLAGS "-subsystem:console -nologo -fixed:no -incremental:no -opt:noref -ignore:4089 /NXCOMPAT /DYNAMICBASE") + + find_library(ACL_CHECK_SYS_CMD_LIB + acl_check_sys_cmd + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib/win64) + find_library(FPGA_LIB + FpgaLib + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib/win64) + + target_link_libraries(de10_agilex_mmd ${ACL_CHECK_SYS_CMD_LIB} ${FPGA_LIB}) +else() + target_link_libraries(de10_agilex_mmd) +endif() + +install(TARGETS de10_agilex_mmd + RUNTIME DESTINATION "dla/runtime/bin" COMPONENT de10_agilex_mmd + LIBRARY DESTINATION "dla/runtime/lib" COMPONENT de10_agilex_mmd + ARCHIVE DESTINATION "dla/runtime/lib" COMPONENT de10_agilex_mmd +) diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.cpp new file mode 100644 index 0000000..527d8bf --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.cpp @@ -0,0 +1,951 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie.cpp ------------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the functions that are defined in aocl_mmd.h */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// common and its own header files +#include "acl_pcie.h" + +// other header files inside MMD driver +#include "acl_pcie_debug.h" +#include "acl_pcie_device.h" +#include "hw_pcie_constants.h" +#ifndef DLA_MMD +#include "acl_check_sys_cmd.h" +#endif + +// other standard header files +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include <map> +#include <sstream> +#include <string> +#include <utility> + +#ifdef DLA_MMD +#include <chrono> +#include <thread> +#endif + +#if defined(LINUX) +#include <fcntl.h> +#include <semaphore.h> +#include <signal.h> +#include <unistd.h> +#endif // LINUX + +// MAX size of line read from pipe-ing the output of system call to MMD +#define BUF_SIZE 1024 +// MAX size of command passed to system for invoking system call from MMD +#define SYSTEM_CMD_SIZE 4 * 1024 + +#ifndef DLA_MMD +// static helper functions +static bool blob_has_elf_signature(void *data, size_t data_size); +#endif + +// global variables used for handling multi-devices and its helper functions +// Use a DeviceMapManager to manage a heap-allocated map for storing device information +// instead of using a static global map because of a segmentation fault which occurs in +// the following situation: +// 1) Host program contains a global variable which calls clReleaseContext in its destructor. +// When the program ends the global goes out of scope and the destructor is called. +// 2) clReleaseContext calls a function in the MMD library which modifies the static global map in +// the MMD library. +// In this situation it was discovered that the destructor of the static global map is called before +// the destructor of the global in the host program, thus resulting in a segmentation fault when +// clReleaseContext calls a function that modifies the internal map after it has been destroyed. +// Using a heap-allocated map avoids this issue as the lifetime of the map persists until it is +// deleted or the process is completely terminated. +class DeviceMapManager { + public: + typedef std::pair<const std::string, ACL_PCIE_DEVICE *> DeviceInfo; + typedef std::map<int, DeviceInfo> DeviceMap; + + static inline bool empty() { return !s_device_map; } + + // Returns the underlying device map. The map must not be empty when this is called. + static inline const DeviceMap &get_device_map() { + ACL_PCIE_ASSERT(s_device_map, "no devices are open -- aborting\n"); + return *s_device_map; + } + + // Returns the device info associated with the given handle. The handle must exist. + static inline const DeviceInfo &get_pcie_device_info(int handle) { return get_device_it_for_handle(handle)->second; } + + // Returns the device associated with the given handle. The handle must exist. + static inline ACL_PCIE_DEVICE *get_pcie_device(int handle) { return get_pcie_device_info(handle).second; } + + // Adds a device with the specified name for the given handle. If a device with the same handle already exists + // it is discarded first. The caller must ensure they don't associate the same device with multiple handles. + static inline void add_pcie_device_handle(int handle, const std::string &name, ACL_PCIE_DEVICE *dev) { + // To avoid memory leaks ensure that only this function ever allocates a new device map because + // we only ever delete the map when the size of the map goes from non-empty to empty. + if (!s_device_map) s_device_map = new DeviceMap(); + + if (s_device_map->count(handle)) discard_pcie_device_handle(handle); + s_device_map->insert(std::pair<int, DeviceInfo>(handle, DeviceInfo(name, dev))); + } + + // Removes the device associated with the given handle. The handle must exist. + static inline void discard_pcie_device_handle(int handle) { + DeviceMap::iterator it = get_device_it_for_handle(handle); + + delete it->second.second; + s_device_map->erase(it); + if (s_device_map->empty()) { + // From a functional perspective the map can remain allocated for + // the entire lifetime the MMD is loaded but there + // is no other good place to clean it up except here. + delete s_device_map; + s_device_map = NULL; + } + } + + // Removes all devices. + static inline void discard_all_pcie_device_handles() { + if (!s_device_map) return; + + for (DeviceMapManager::DeviceMap::iterator it = s_device_map->begin(); it != s_device_map->end(); ++it) { + delete it->second.second; + } + + delete s_device_map; + s_device_map = NULL; + } + + // Returns true if any device is currently being programmed. + static inline bool is_any_device_being_programmed() { + if (!s_device_map) return false; + + for (DeviceMap::iterator it = s_device_map->begin(); it != s_device_map->end(); ++it) { + if (it->second.second->is_being_programmed()) { + return true; + } + } + return false; + } + + private: + static inline DeviceMap::iterator get_device_it_for_handle(int handle) { + ACL_PCIE_ASSERT(s_device_map, "can't find handle %d -- aborting\n", handle); + DeviceMap::iterator it = s_device_map->find(handle); + ACL_PCIE_ASSERT(it != s_device_map->end(), "can't find handle %d -- aborting\n", handle); + return it; + } + + static DeviceMap *s_device_map; +}; +DeviceMapManager::DeviceMap *DeviceMapManager::s_device_map = NULL; + +static int test_device_exception_signal_number = 63; + +// Functions for handling interrupts or signals for multiple devices +// This functions are used inside the ACL_PCIE_DEVICE class +#if defined(WINDOWS) +void pcie_interrupt_handler(void *data) { + ACL_PCIE_DEVICE *device = static_cast<ACL_PCIE_DEVICE *>(data); + device->service_interrupt(); +} + +BOOL ctrl_c_handler(DWORD fdwCtrlType) { + if (fdwCtrlType != CTRL_C_EVENT) return FALSE; + + if (DeviceMapManager::is_any_device_being_programmed()) { + ACL_PCIE_INFO("The device is still being programmed, cannot terminate at this point.\n"); + return TRUE; + } + + // On Windows, the signal handle function is executed by another thread, + // so we cannot simply free all the open devices. + // Just exit when received a ctrl-c event, the OS will take care of the clean-up. + exit(1); +} +#endif // WINDOWS +#if defined(LINUX) +// On Linux, driver will send a SIG_INT_NOTIFY *signal* to notify about an interrupt. +void pcie_linux_signal_handler(int sig, siginfo_t *info, void *unused) { + // test_device_exception_signal_number is reserved for device exception testing + if (sig == test_device_exception_signal_number) { + ACL_PCIE_ERROR_IF(DeviceMapManager::get_device_map().empty(), + return, + "No devices available to trigger test_device_exception_signal_number on.\n"); + // Pick the last (most recent) handle for device exception testing + unsigned int handle = DeviceMapManager::get_device_map().rbegin()->first; + DeviceMapManager::get_pcie_device(handle)->test_trigger_device_interrupt(); + } else { + // the last bit indicates the DMA completion + unsigned int irq_type_flag = info->si_int & 0x1; + // other bits shows the handle value of the device that sent the interrupt + unsigned int handle = info->si_int >> 1; + if (DeviceMapManager::empty() || !DeviceMapManager::get_device_map().count(handle)) { + ACL_PCIE_DEBUG_MSG(":: received an unknown handle %d in signal handler, ignore this.\n", handle); + return; + } + + DeviceMapManager::get_pcie_device(handle)->service_interrupt(irq_type_flag); + } +} + +void ctrl_c_handler(int sig_num) { + if (DeviceMapManager::is_any_device_being_programmed()) { + ACL_PCIE_INFO("The device is still being programmed, cannot terminate at this point.\n"); + return; + } + + // Free all the resource allocated for open devices before exiting the program. + // It also notifies the kernel driver about the termination of the program, + // so that the kernel driver won't try to talk to any user-allocated memory + // space (mainly for the DMA) after the program exit. + DeviceMapManager::discard_all_pcie_device_handles(); + exit(1); +} + +void abort_signal_handler(int sig_num) { + DeviceMapManager::discard_all_pcie_device_handles(); + exit(1); +} + +int allocate_and_register_linux_signal_number_helper(int pid) { + char buffer[4096], *locOfSigCgt; + FILE *fp; + int bytes_read, status, ret = -1; + unsigned long long sigmask = 0; + struct sigaction sigusr {}, sigabrt {}; + + snprintf(buffer, sizeof(buffer), "/proc/%d/status", pid); + fp = fopen(buffer, "rb"); + ACL_PCIE_ERROR_IF(fp == NULL, return -1, "Unable to open file %s\n", buffer); + bytes_read = fread(buffer, sizeof(buffer[0]), sizeof(buffer) - 1, fp); + fclose(fp); + buffer[bytes_read] = 0; // null terminate the string + locOfSigCgt = strstr(buffer, "SigCgt:"); // returns null if can't find, shouldn't happen + ACL_PCIE_ERROR_IF(locOfSigCgt == NULL, return -1, "Did not find SigCgt: for PID %d\n", pid); + sscanf(locOfSigCgt + 7, "%llx", &sigmask); + + // Find an unused signal number + for (int i = SIGRTMAX; i >= SIGRTMIN; i--) { + if (!((sigmask >> (i - 1)) & 1)) { + ret = i; + break; + } + } + ACL_PCIE_ERROR_IF(ret == -1, return -1, "Unable to find an unused signal number\n"); + + // Enable if driver is using signals to communicate with the host. + sigusr.sa_sigaction = pcie_linux_signal_handler; + sigusr.sa_flags = SA_SIGINFO; + status = sigaction(ret, &sigusr, NULL); + if (getenv("ACL_MMD_TEST_INTELFPGA")) { + ACL_PCIE_ERROR_IF(((sigmask >> (test_device_exception_signal_number - 1)) & 1), + return -1, + "Signal number %i cannot be occupied\n", + test_device_exception_signal_number); + status = sigaction(test_device_exception_signal_number, &sigusr, NULL); + } + ACL_PCIE_ERROR_IF(status != 0, return -1, "sigaction failed with status %d, signal number %d\n", status, ret); + + // Install signal handler for SIGABRT from assertions in the upper layers + sigabrt.sa_handler = abort_signal_handler; + sigemptyset(&sigabrt.sa_mask); + sigabrt.sa_flags = 0; + status = sigaction(SIGABRT, &sigabrt, NULL); + ACL_PCIE_ERROR_IF(status != 0, return -1, "sigaction failed with status %d, signal number %d\n", status, SIGABRT); + + // if it makes it here, the user got an unused signal number and we installed all signal handlers + return ret; +} + +// returns an unused signal number, -1 means ran into some error +int allocate_and_register_linux_signal_number(pthread_mutex_t *mutex) { + int pid = getpid(); + int err = pthread_mutex_lock(mutex); + ACL_PCIE_ERROR_IF(err != 0, return -1, "pthread_mutex_lock error %d\n", err); + + // this has multiple return points, put in separate function so that we don't bypass releasing the mutex + int ret = allocate_and_register_linux_signal_number_helper(pid); + + err = pthread_mutex_unlock(mutex); + ACL_PCIE_ERROR_IF(err != 0, return -1, "pthread_mutex_unlock error %d\n", err); + + return ret; +} +#endif // LINUX + +// Function to install the signal handler for Ctrl-C +// If ignore_sig != 0, the ctrl-c signal will be ignored by the program +// If ignore_sig = 0, the custom signal handler (ctrl_c_handler) will be used +int install_ctrl_c_handler(int ingore_sig) { +#if defined(WINDOWS) + SetConsoleCtrlHandler((ingore_sig ? NULL : (PHANDLER_ROUTINE)ctrl_c_handler), TRUE); +#endif // WINDOWS +#if defined(LINUX) + struct sigaction sig; + sig.sa_handler = (ingore_sig ? SIG_IGN : ctrl_c_handler); + sigemptyset(&sig.sa_mask); + sig.sa_flags = 0; + sigaction(SIGINT, &sig, NULL); +#endif // LINUX + + return 0; +} + +// Function to return the number of boards installed in the system +unsigned int get_offline_num_boards() { + unsigned int num_boards = 0; + + // These are for reading/parsing the environment variable + const char *override_count_string = 0; + long parsed_count; + char *endptr; + +// Windows MMD will try to open all the devices +#if defined(WINDOWS) + fpga_result result; + fpga_properties filter = NULL; + + result = fpgaGetProperties(NULL, &filter); + if (result != FPGA_OK) { + num_boards = ACL_MAX_DEVICE; + ACL_PCIE_ERROR_IF(1, goto End, "failed to get properties.\n"); + } + + result = fpgaPropertiesSetObjectType(filter, FPGA_DEVICE); + if (result != FPGA_OK) { + num_boards = ACL_MAX_DEVICE; + + if (filter != NULL) fpgaDestroyProperties(&filter); + + ACL_PCIE_ERROR_IF(1, goto End, "failed to set object type.\n"); + } + + result = fpgaPropertiesSetVendorID(filter, ACL_PCI_INTELFPGA_VENDOR_ID); + if (result != FPGA_OK) { + num_boards = ACL_MAX_DEVICE; + + if (filter != NULL) fpgaDestroyProperties(&filter); + + ACL_PCIE_ERROR_IF(1, goto End, "failed to set vendor ID.\n"); + } + + result = fpgaEnumerate(&filter, 1, NULL, 1, &num_boards); + if (result != FPGA_OK) { + num_boards = ACL_MAX_DEVICE; + + if (filter != NULL) fpgaDestroyProperties(&filter); + + ACL_PCIE_ERROR_IF(1, goto End, "failed to scan for the PCI device.\n"); + } + + if (filter != NULL) fpgaDestroyProperties(&filter); + + if (num_boards == 0) { + num_boards = ACL_MAX_DEVICE; + } + +End: +#endif // WINDOWS + +// Linux MMD will look into the number of devices +#if defined(LINUX) + FILE *fp; + char str_line_in[BUF_SIZE]; + char str_board_pkg_name[BUF_SIZE]; + char str_cmd[SYSTEM_CMD_SIZE]; + + snprintf(str_board_pkg_name, sizeof(str_board_pkg_name), "acl%s", ACL_BOARD_PKG_NAME); + snprintf(str_cmd, sizeof(str_cmd), "ls /sys/class/aclpci_%s 2>/dev/null", ACL_BOARD_PKG_NAME); + +#ifndef DLA_MMD + ACL_PCIE_ASSERT(system_cmd_is_valid(str_cmd), "Invalid popen() function parameter: %s\n", str_cmd); +#endif + fp = popen(str_cmd, "r"); + + if (fp == NULL) { + ACL_PCIE_INFO("Couldn't open pipe stream\n"); + return false; + } + // Read every line from output + while (fgets(str_line_in, BUF_SIZE, fp) != NULL) { + if (strncmp(str_board_pkg_name, str_line_in, strnlen(str_board_pkg_name, MAX_NAME_SIZE)) == 0) { + num_boards++; + } + } + + pclose(fp); + +#endif // LINUX + + override_count_string = getenv("CL_OVERRIDE_NUM_DEVICES_INTELFPGA"); + if (override_count_string) { + endptr = 0; + parsed_count = strtol(override_count_string, &endptr, 10); + if (endptr == override_count_string // no valid characters + || *endptr // an invalid character + || (parsed_count < 0 || parsed_count >= (long)ACL_MAX_DEVICE)) { + // malformed override string, do nothing + } else { + // Was ok. + num_boards = (unsigned int)parsed_count; + } + } + + return num_boards; +} + +// Get information about the board using the enum aocl_mmd_offline_info_t for +// offline info (called without a handle), and the enum aocl_mmd_info_t for +// info specific to a certain board. +#define RESULT_INT(X) \ + { \ + *((int *)param_value) = X; \ + if (param_size_ret) *param_size_ret = sizeof(int); \ + } +#define RESULT_UNSIGNED(X) \ + { \ + *((unsigned *)param_value) = X; \ + if (param_size_ret) *param_size_ret = sizeof(unsigned); \ + } +#define RESULT_SIZE_T(X) \ + { \ + *((size_t *)param_value) = X; \ + if (param_size_ret) *param_size_ret = sizeof(size_t); \ + } +#if defined(WINDOWS) +#define RESULT_STR(X) \ + do { \ + size_t Xlen = strnlen(X, MAX_NAME_SIZE) + 1; \ + memcpy_s((void *)param_value, param_value_size, X, (param_value_size <= Xlen) ? param_value_size : Xlen); \ + if (param_size_ret) *param_size_ret = Xlen; \ + } while (0) +#else +#define RESULT_STR(X) \ + do { \ + size_t Xlen = strnlen(X, MAX_NAME_SIZE) + 1; \ + memcpy((void *)param_value, X, (param_value_size <= Xlen) ? param_value_size : Xlen); \ + if (param_size_ret) *param_size_ret = Xlen; \ + } while (0) +#endif +int aocl_mmd_get_offline_info(aocl_mmd_offline_info_t requested_info_id, + size_t param_value_size, + void *param_value, + size_t *param_size_ret) { + // It might be helpful to cache the info if function aocl_mmd_get_offline_info is called frequently. + unsigned int num_boards; + switch (requested_info_id) { + case AOCL_MMD_VERSION: + RESULT_STR(MMD_VERSION); + break; + case AOCL_MMD_NUM_BOARDS: { + num_boards = get_offline_num_boards(); + RESULT_INT((int)num_boards); + break; + } + case AOCL_MMD_BOARD_NAMES: { + // Construct a list of all possible devices supported by this MMD layer + std::ostringstream boards; + num_boards = get_offline_num_boards(); + for (unsigned i = 0; i < num_boards; i++) { + boards << "acl" << ACL_BOARD_PKG_NAME << i; + if (i < num_boards - 1) boards << ";"; + } + RESULT_STR(boards.str().c_str()); + break; + } + case AOCL_MMD_VENDOR_NAME: { + RESULT_STR(ACL_VENDOR_NAME); + break; + } + case AOCL_MMD_VENDOR_ID: + RESULT_INT(ACL_PCI_INTELFPGA_VENDOR_ID); + break; + case AOCL_MMD_USES_YIELD: + RESULT_INT(0); + break; + case AOCL_MMD_MEM_TYPES_SUPPORTED: + RESULT_INT(AOCL_MMD_PHYSICAL_MEMORY); + break; + } + return 0; +} + +int aocl_mmd_get_info( + int handle, aocl_mmd_info_t requested_info_id, size_t param_value_size, void *param_value, size_t *param_size_ret) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF(!pcie_dev->is_initialized(), + return -1, + "aocl_mmd_get_info failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + switch (requested_info_id) { + case AOCL_MMD_BOARD_NAME: { + std::ostringstream board_name; + board_name << ACL_BOARD_NAME << " (" << DeviceMapManager::get_pcie_device_info(handle).first << ")"; + RESULT_STR(board_name.str().c_str()); + break; + } + case AOCL_MMD_NUM_KERNEL_INTERFACES: + RESULT_INT(1); + break; + case AOCL_MMD_KERNEL_INTERFACES: + RESULT_INT(AOCL_MMD_KERNEL); + break; + case AOCL_MMD_PLL_INTERFACES: + RESULT_INT(AOCL_MMD_PLL); + break; + case AOCL_MMD_MEMORY_INTERFACE: + RESULT_INT(AOCL_MMD_MEMORY); + break; + case AOCL_MMD_PCIE_INFO: + RESULT_STR(pcie_dev->get_dev_pcie_info()); + break; + case AOCL_MMD_CONCURRENT_READS: + RESULT_INT(1); + break; + case AOCL_MMD_CONCURRENT_WRITES: + RESULT_INT(1); + break; + case AOCL_MMD_CONCURRENT_READS_OR_WRITES: + RESULT_INT(1); + break; + case AOCL_MMD_MIN_HOST_MEMORY_ALIGNMENT: + RESULT_SIZE_T(0); + break; + case AOCL_MMD_HOST_MEM_CAPABILITIES: + RESULT_UNSIGNED(0); + break; + case AOCL_MMD_SHARED_MEM_CAPABILITIES: + RESULT_UNSIGNED(0); + break; + case AOCL_MMD_DEVICE_MEM_CAPABILITIES: + RESULT_UNSIGNED(0); + break; + case AOCL_MMD_HOST_MEM_CONCURRENT_GRANULARITY: + RESULT_SIZE_T(0); + break; + case AOCL_MMD_SHARED_MEM_CONCURRENT_GRANULARITY: + RESULT_SIZE_T(0); + break; + case AOCL_MMD_DEVICE_MEM_CONCURRENT_GRANULARITY: + RESULT_SIZE_T(0); + break; + + case AOCL_MMD_TEMPERATURE: { + float *r; + int temp; + pcie_dev->get_ondie_temp_slow_call(&temp); + r = (float *)param_value; + *r = ACL_PCIE_TEMP_FORMULA; + if (param_size_ret) *param_size_ret = sizeof(float); + break; + } + + // currently not supported + case AOCL_MMD_BOARD_UNIQUE_ID: + return -1; + } + return 0; +} + +#undef RESULT_INT +#undef RESULT_STR + +// Open and initialize the named device. +int AOCL_MMD_CALL aocl_mmd_open(const char *name) { + static int signal_handler_installed = 0; + static int unique_id = 0; + int dev_num = -1; + static int user_signal_number = -1; +#if defined(LINUX) + static pthread_mutex_t linux_signal_arb_mutex = + PTHREAD_MUTEX_INITIALIZER; // initializes as unlocked, static = no cleanup needed + + if (sscanf(name, "acl" ACL_BOARD_PKG_NAME "%d", &dev_num) != 1) { + return -1; + } +#endif // LINUX + +#if defined(WINDOWS) + if (sscanf_s(name, "acl" ACL_BOARD_PKG_NAME "%d", &dev_num) != 1) { + return -1; + } +#endif + if (dev_num < 0 || dev_num >= ACL_MAX_DEVICE) { + return -1; + } + if (++unique_id <= 0) { + unique_id = 1; + } + + ACL_PCIE_ASSERT(DeviceMapManager::empty() || DeviceMapManager::get_device_map().count(unique_id) == 0, + "unique_id %d is used before.\n", + unique_id); + + if (signal_handler_installed == 0) { +#if defined(LINUX) + user_signal_number = allocate_and_register_linux_signal_number(&linux_signal_arb_mutex); + if (user_signal_number == -1) return -1; +#endif // LINUX + + install_ctrl_c_handler(0 /* use the custom signal handler */); + signal_handler_installed = 1; + } + + ACL_PCIE_DEVICE *pcie_dev = NULL; + + try { + pcie_dev = new ACL_PCIE_DEVICE(dev_num, name, unique_id, user_signal_number); + } + + // Catch any memory allocation failures + catch (std::bad_alloc &) { + delete pcie_dev; + return -1; + } + + if (!pcie_dev->is_valid()) { + delete pcie_dev; + return -1; + } + + DeviceMapManager::add_pcie_device_handle(unique_id, name, pcie_dev); + if (pcie_dev->is_initialized()) { + return unique_id; + } else { + // Perform a bitwise-not operation to the unique_id if the device + // do not pass the initial test. This negative unique_id indicates + // a fail to open the device, but still provide actual the unique_id + // to allow reprogram executable to get access to the device and + // reprogram the board when the board is not usable. + return ~unique_id; + } +} + +// Close an opened device, by its handle. +int AOCL_MMD_CALL aocl_mmd_close(int handle) { + DeviceMapManager::discard_pcie_device_handle(handle); + + return 0; +} + +// Set the interrupt handler for the opened device. +int AOCL_MMD_CALL aocl_mmd_set_interrupt_handler(int handle, aocl_mmd_interrupt_handler_fn fn, void *user_data) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), + return -1, + "aocl_mmd_set_interrupt_handler failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->set_kernel_interrupt(fn, user_data); +} + +// Set the device interrupt handler for the opened device. +int AOCL_MMD_CALL aocl_mmd_set_device_interrupt_handler(int handle, + aocl_mmd_device_interrupt_handler_fn fn, + void *user_data) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), + return -1, + "aocl_mmd_set_interrupt_handler failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->set_device_interrupt(fn, user_data); +} + +// Set the operation status handler for the opened device. +int AOCL_MMD_CALL aocl_mmd_set_status_handler(int handle, aocl_mmd_status_handler_fn fn, void *user_data) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), + return -1, + "aocl_mmd_set_status_handler failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->set_status_handler(fn, user_data); +} + +// Called when the host is idle and hence possibly waiting for events to be +// processed by the device +int AOCL_MMD_CALL aocl_mmd_yield(int handle) { return DeviceMapManager::get_pcie_device(handle)->yield(); } + +// Read, write and copy operations on a single interface. +int AOCL_MMD_CALL aocl_mmd_read(int handle, aocl_mmd_op_t op, size_t len, void *dst, int mmd_interface, size_t offset) { + void *host_addr = dst; + size_t dev_addr = offset; + + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF(!pcie_dev->is_initialized(), + return -1, + "aocl_mmd_read failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->read_block(op, (aocl_mmd_interface_t)mmd_interface, host_addr, dev_addr, len); +} + +int AOCL_MMD_CALL +aocl_mmd_write(int handle, aocl_mmd_op_t op, size_t len, const void *src, int mmd_interface, size_t offset) { + void *host_addr = const_cast<void *>(src); + size_t dev_addr = offset; + + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF(!pcie_dev->is_initialized(), + return -1, + "aocl_mmd_write failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->write_block(op, (aocl_mmd_interface_t)mmd_interface, host_addr, dev_addr, len); +} + +int AOCL_MMD_CALL +aocl_mmd_copy(int handle, aocl_mmd_op_t op, size_t len, int mmd_interface, size_t src_offset, size_t dst_offset) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF(!pcie_dev->is_initialized(), + return -1, + "aocl_mmd_copy failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->copy_block(op, (aocl_mmd_interface_t)mmd_interface, src_offset, dst_offset, len); +} + +// Initialize host channel specified in channel_name +int AOCL_MMD_CALL aocl_mmd_hostchannel_create(int handle, char *channel_name, size_t queue_depth, int direction) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), + return -1, + "aocl_mmd_create_hostchannel failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->create_hostchannel(channel_name, queue_depth, direction); +} + +// reset the host channel specified with channel handle +int AOCL_MMD_CALL aocl_mmd_hostchannel_destroy(int handle, int channel) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), + return -1, + "aocl_mmd_create_hostchannel failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->destroy_channel(channel); +} + +// Get the pointer to buffer the user can write/read from the kernel with +AOCL_MMD_CALL void *aocl_mmd_hostchannel_get_buffer(int handle, int channel, size_t *buffer_size, int *status) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF(!pcie_dev->is_initialized(), + return NULL, + "aocl_mmd_read failed due to the target device (handle %d) is not properly initialized.\n", + handle); + + return pcie_dev->hostchannel_get_buffer(buffer_size, channel, status); +} + +// Acknolwedge from the user that they have written/read send_size amount of buffer obtained from get_buffer +size_t AOCL_MMD_CALL aocl_mmd_hostchannel_ack_buffer(int handle, int channel, size_t send_size, int *status) { + ACL_PCIE_DEVICE *pcie_dev = DeviceMapManager::get_pcie_device(handle); + ACL_PCIE_ERROR_IF( + !pcie_dev->is_initialized(), *status = -1; + return 0, "aocl_mmd_read failed due to the target device (handle %d) is not properly initialized.\n", handle); + + return pcie_dev->hostchannel_ack_buffer(send_size, channel, status); +} + +#ifdef DLA_MMD + +AOCL_MMD_CALL int aocl_mmd_save_pcie(int handle) +{ + auto ret = DeviceMapManager::get_pcie_device(handle)->pause_and_save_pcie(); + if (ret) { + return -1; + } + return 0; +} +AOCL_MMD_CALL int aocl_mmd_restore_pcie(int handle) +{ + auto ret = DeviceMapManager::get_pcie_device(handle)->restore_and_resume_pcie(); + if (ret) { + return -1; + } + return 0; +} +// Reprogram the device given the sof file name +int AOCL_MMD_CALL aocl_mmd_program_sof(int handle, const char *sof_filename, const bool skipSaveRestore) { + if (DeviceMapManager::get_pcie_device(handle)->reprogram_sof(sof_filename, skipSaveRestore)) + { + return -1; + } + return 0; +} +#else +// Reprogram the device based on the program mode +int AOCL_MMD_CALL aocl_mmd_program(int handle, void *data, size_t data_size, aocl_mmd_program_mode_t program_mode) { + // assuming the an ELF-formatted blob. + if (!blob_has_elf_signature(data, data_size)) { + ACL_PCIE_DEBUG_MSG("ad hoc fpga bin\n"); + return -1; + } + + // program the device based on the certain mode + if (program_mode & AOCL_MMD_PROGRAM_PRESERVE_GLOBAL_MEM) { + if (DeviceMapManager::get_pcie_device(handle)->reprogram(data, data_size, ACL_PCIE_PROGRAM_PR)) return -1; + return handle; + } else { + if (DeviceMapManager::get_pcie_device(handle)->reprogram(data, data_size, ACL_PCIE_PROGRAM_JTAG)) return -1; + // Re-open the device to reinitialize hardware + const std::string device_name = DeviceMapManager::get_pcie_device_info(handle).first; + DeviceMapManager::discard_pcie_device_handle(handle); + + return aocl_mmd_open(device_name.c_str()); + } +} +#endif +// Shared memory allocator +AOCL_MMD_CALL void *aocl_mmd_shared_mem_alloc(int handle, size_t size, unsigned long long *device_ptr_out) { + return DeviceMapManager::get_pcie_device(handle)->shared_mem_alloc(size, device_ptr_out); +} + +// Shared memory de-allocator +AOCL_MMD_CALL void aocl_mmd_shared_mem_free(int handle, void *host_ptr, size_t size) { + DeviceMapManager::get_pcie_device(handle)->shared_mem_free(host_ptr, size); +} + +#ifndef DLA_MMD +// This function checks if the input data has an ELF-formatted blob. +// Return true when it does. +static bool blob_has_elf_signature(void *data, size_t data_size) { + bool result = false; + if (data && data_size > 4) { + unsigned char *cdata = (unsigned char *)data; + const unsigned char elf_signature[4] = {0177, 'E', 'L', 'F'}; // Little endian + result = (cdata[0] == elf_signature[0]) && (cdata[1] == elf_signature[1]) && (cdata[2] == elf_signature[2]) && + (cdata[3] == elf_signature[3]); + } + return result; +} +#endif + +// Return a positive number when single device open. Otherwise, return -1 +AOCL_MMD_CALL int get_open_handle() { + if (DeviceMapManager::empty() || DeviceMapManager::get_device_map().size() != 1) { + return -1; + } + return DeviceMapManager::get_device_map().begin()->first; +} + +AOCL_MMD_CALL void *aocl_mmd_host_alloc(int *handles, + size_t num_devices, + size_t size, + size_t alignment, + aocl_mmd_mem_properties_t *properties, + int *error) { + // Not supported on this BSP + return NULL; +} + +AOCL_MMD_CALL int aocl_mmd_free(void *mem) { + // Not supported on this BSP + return 0; +} + +AOCL_MMD_CALL void *aocl_mmd_device_alloc( + int handle, size_t size, size_t alignment, aocl_mmd_mem_properties_t *properties, int *error) { + // Not supported on this BSP + return NULL; +} + +AOCL_MMD_CALL void *aocl_mmd_shared_alloc( + int handle, size_t size, size_t alignment, aocl_mmd_mem_properties_t *properties, int *error) { + // Not supported on this BSP + return NULL; +} + +AOCL_MMD_CALL int aocl_mmd_shared_migrate(int handle, void *shared_ptr, size_t size, aocl_mmd_migrate_t destination) { + // Not supported on this BSP + return 0; +} + +#ifdef DLA_MMD +// Query functions to get board-specific values +AOCL_MMD_CALL int dla_mmd_get_max_num_instances() { return 4; } +AOCL_MMD_CALL uint64_t dla_mmd_get_ddr_size_per_instance() { return 1ULL << 32; } +AOCL_MMD_CALL double dla_mmd_get_ddr_clock_freq() { return 333.333333; } // MHz + +// Helper functions for the wrapper functions around CSR and DDR +uint64_t dla_get_raw_csr_address(int instance, uint64_t addr) { return 0x38000 + (0x1000 * instance) + addr; } +uint64_t dla_get_raw_ddr_address(int instance, uint64_t addr) { return (1ULL << 33) * instance + addr; } + +// Wrappers around CSR and DDR reads and writes to abstract away board-specific offsets +AOCL_MMD_CALL int dla_mmd_csr_write(int handle, int instance, uint64_t addr, const uint32_t *data) { + return aocl_mmd_write( + handle, NULL, sizeof(uint32_t), data, ACL_MMD_KERNEL_HANDLE, dla_get_raw_csr_address(instance, addr)); +} +AOCL_MMD_CALL int dla_mmd_csr_read(int handle, int instance, uint64_t addr, uint32_t *data) { + return aocl_mmd_read( + handle, NULL, sizeof(uint32_t), data, ACL_MMD_KERNEL_HANDLE, dla_get_raw_csr_address(instance, addr)); +} +AOCL_MMD_CALL int dla_mmd_ddr_write(int handle, int instance, uint64_t addr, uint64_t length, const void *data) { + return aocl_mmd_write(handle, NULL, length, data, ACL_MMD_MEMORY_HANDLE, dla_get_raw_ddr_address(instance, addr)); +} +AOCL_MMD_CALL int dla_mmd_ddr_read(int handle, int instance, uint64_t addr, uint64_t length, void *data) { + return aocl_mmd_read(handle, NULL, length, data, ACL_MMD_MEMORY_HANDLE, dla_get_raw_ddr_address(instance, addr)); +} + +// Get the PLL clock frequency in MHz, returns a negative value if there is an error +AOCL_MMD_CALL double dla_mmd_get_coredla_clock_freq(int handle) { + constexpr uint64_t hw_timer_address = 0x37000; + const uint32_t start_bit = 1; + const uint32_t stop_bit = 2; + + // Send the start command to the hardware counter + std::chrono::high_resolution_clock::time_point time_before = std::chrono::high_resolution_clock::now(); + int status = aocl_mmd_write(handle, NULL, sizeof(uint32_t), &start_bit, ACL_MMD_KERNEL_HANDLE, hw_timer_address); + assert(status == 0); + + // Unlikely to sleep for exactly 10 milliseconds, but it doesn't matter since we use a high resolution clock to + // determine the amount of time between the start and stop commands for the hardware counter + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + + // Send the stop command to the hardware counter + std::chrono::high_resolution_clock::time_point time_after = std::chrono::high_resolution_clock::now(); + status = aocl_mmd_write(handle, NULL, sizeof(uint32_t), &stop_bit, ACL_MMD_KERNEL_HANDLE, hw_timer_address); + assert(status == 0); + + // Read back the value of the counter + uint32_t counter = 0; + status = aocl_mmd_read(handle, NULL, sizeof(uint32_t), &counter, ACL_MMD_KERNEL_HANDLE, hw_timer_address); + assert(status == 0); + + // Calculate the clock frequency of the counter, which is running on clk_dla + double elapsed_seconds = std::chrono::duration_cast<std::chrono::duration<double>>(time_after - time_before).count(); + return 1.0e-6 * counter / elapsed_seconds; // 1.0e-6 is to convert to MHz +} + +#endif diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.h new file mode 100644 index 0000000..cfba6a3 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie.h @@ -0,0 +1,177 @@ +#ifndef ACL_PCIE_H +#define ACL_PCIE_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie.h --------------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file defines macros and types that are used inside the MMD driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#ifndef ACL_PCIE_EXPORT +#define ACL_PCIE_EXPORT __declspec(dllimport) +#endif + +#include <assert.h> +#include <stddef.h> +#include <stdio.h> +#ifdef DLA_MMD +#include <cstdint> +#else +#include <CL/cl_platform.h> +#endif +#include "aocl_mmd.h" +#include "hw_pcie_constants.h" + +#define MMD_VERSION AOCL_MMD_VERSION_STRING + +#ifdef DLA_MMD +#include "version.h" +#else +#include <version.h> +#endif + +#define KERNEL_DRIVER_VERSION_EXPECTED ACL_DRIVER_VERSION + +#if defined(_WIN32) || defined(_WIN64) +// Need DWORD, UINT32, etc. +// But windows.h spits out a lot of spurious warnings. +#pragma warning(push) +#pragma warning(disable : 4668) +#include <windows.h> +#pragma warning(pop) + +// OPAE header files +#include <initguid.h> +#include <opae/fpga.h> +#include "fpga_cmd_guids.h" + +#define INVALID_DEVICE (NULL) + +// define for the format string for DWORD type +#define DWORD_FMT_U "%lu" +#define DWORD_FMT_X "%lx" +#define DWORD_FMT_4X "%04lX" + +// define for the format string for size_t type +#ifdef _WIN64 +#define SIZE_FMT_U "%zu" +#define SIZE_FMT_X "%zx" +#else +#define SIZE_FMT_U "%Iu" +#define SIZE_FMT_X "%Ix" +#endif + +typedef ULONG64 KPTR; +typedef UINT64 DMA_ADDR; +#endif // WINDOWS + +#if defined(LINUX) +typedef uintptr_t KPTR; +typedef int fpga_handle; +typedef unsigned int fpga_result; +#define FPGA_OK 0 + +typedef unsigned int DWORD; +typedef unsigned long long QWORD; +typedef char INT8; +typedef unsigned char UINT8; +typedef int16_t INT16; +typedef uint16_t UINT16; +typedef int INT32; +typedef unsigned int UINT32; +typedef long long INT64; +typedef unsigned long long UINT64; + +#define INVALID_HANDLE_VALUE ((int)(-1)) + +// Linux driver-specific exports +#include "pcie_linux_driver_exports.h" + +#define INVALID_DEVICE (-1) +#define WD_STATUS_SUCCESS 0 + +// define for the format string for DWORD type +#define DWORD_FMT_U "%u" +#define DWORD_FMT_X "%x" +#define DWORD_FMT_4X "%04X" + +// define for the format string for size_t type +#define SIZE_FMT_U "%zu" +#define SIZE_FMT_X "%zx" + +#endif // LINUX + +#define MAX_NAME_SIZE (1204) + +typedef enum { + AOCL_MMD_KERNEL = ACL_MMD_KERNEL_HANDLE, // Control interface into kernel interface + AOCL_MMD_MEMORY = ACL_MMD_MEMORY_HANDLE, // Data interface to device memory + AOCL_MMD_PLL = ACL_MMD_PLL_HANDLE, // Interface for reconfigurable PLL + AOCL_MMD_HOSTCH = ACL_MMD_HOSTCH_HANDLE +} aocl_mmd_interface_t; + +// Describes the properties of key components in a standard ACL device +#define PCIE_INFO_STR_LEN 1024 +#define PCIE_SLOT_INFO_STR_LEN 128 + +struct ACL_PCIE_DEVICE_DESCRIPTION { + DWORD vendor_id; + DWORD device_id; + char pcie_slot_info_str[PCIE_SLOT_INFO_STR_LEN]; + char pcie_info_str[PCIE_INFO_STR_LEN]; + bool interrupt_valid; + UINT32 interrupt_data; + UINT64 interrupt_addr; +}; + +#define ACL_PCIE_ASSERT(COND, ...) \ + do { \ + if (!(COND)) { \ + printf("\nMMD FATAL: %s:%d: ", __FILE__, __LINE__); \ + printf(__VA_ARGS__); \ + fflush(stdout); \ + assert(0); \ + } \ + } while (0) + +#define ACL_PCIE_ERROR_IF(COND, NEXT, ...) \ + do { \ + if (COND) { \ + printf("\nMMD ERROR: " __VA_ARGS__); \ + fflush(stdout); \ + NEXT; \ + } \ + } while (0) + +#define ACL_PCIE_INFO(...) \ + do { \ + printf("MMD INFO : " __VA_ARGS__); \ + fflush(stdout); \ + } while (0) + +// Define the flag of program +#define ACL_PCIE_PROGRAM_PR 1 +#define ACL_PCIE_PROGRAM_JTAG 0 + +#endif // ACL_PCIE_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.cpp new file mode 100644 index 0000000..03c76dd --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.cpp @@ -0,0 +1,1049 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_config.cpp ------------------------------------------ C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle functions that program the FPGA. */ +/* The declaration of the class lives in the acl_pcie_config.h. */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// common and its own header files +#include "acl_pcie_config.h" +#include "acl_pcie.h" + +// other header files inside MMD driver +#include "acl_pcie_debug.h" +#if defined(WINDOWS) +#include "acl_pcie_dma_windows.h" +#endif // WINDOWS + +// other standard header files +#include <stdlib.h> +#include <string.h> +#include <iostream> +#include <sstream> +#if defined(WINDOWS) +#include <process.h> +#endif // WINDOWS + +#if defined(LINUX) +#include <unistd.h> +#endif // LINUX + +#if defined(WINDOWS) +#define FREEZE_STATUS_OFFSET 0 +#define FREEZE_CTRL_OFFSET 4 +#define FREEZE_VERSION_OFFSET 12 +#define FREEZE_BRIDGE_SUPPORTED_VERSION 0xad000003 + +#define FREEZE_REQ 1 +#define RESET_REQ 2 +#define UNFREEZE_REQ 4 + +#define FREEZE_REQ_DONE 1 +#define UNFREEZE_REQ_DONE 2 + +#define ALT_PR_DATA_OFST 0x00 +#define ALT_PR_CSR_OFST 0x04 +#define ALT_PR_VER_OFST 0x08 + +#define ALT_PR_CSR_PR_START 1 +#define ALT_PR_CSR_STATUS_SFT 1 +#define ALT_PR_CSR_STATUS_MSK (7 << ALT_PR_CSR_STATUS_SFT) +#define ALT_PR_CSR_STATUS_NRESET (0 << ALT_PR_CSR_STATUS_SFT) +#define ALT_PR_CSR_STATUS_BUSY (1 << ALT_PR_CSR_STATUS_SFT) +#define ALT_PR_CSR_STATUS_PR_IN_PROG (2 << ALT_PR_CSR_STATUS_SFT) +#define ALT_PR_CSR_STATUS_PR_SUCCESS (3 << ALT_PR_CSR_STATUS_SFT) +#define ALT_PR_CSR_STATUS_PR_ERR (4 << ALT_PR_CSR_STATUS_SFT) + +#define ACL_DMA_PR_ALIGNMENT_BYTES 4096 + +#define PLL_OFFSET_VERSION_ID 0x000 +#define PLL_OFFSET_ROM 0x400 +#define PLL_OFFSET_RECONFIG_CTRL_S10 0x800 +#define PLL_OFFSET_COUNTER 0x100 +#define PLL_OFFSET_RESET 0x110 +#define PLL_OFFSET_LOCK 0x120 + +#define PLL_M_HIGH_REG_S10 0x104 +#define PLL_M_LOW_REG_S10 0x107 +#define PLL_M_BYPASS_ENABLE_REG_S10 0x105 +#define PLL_M_EVEN_DUTY_ENABLE_REG_S10 0x106 + +#define PLL_N_HIGH_REG_S10 0x100 +#define PLL_N_LOW_REG_S10 0x102 +#define PLL_N_BYPASS_ENABLE_REG_S10 0x101 +#define PLL_N_EVEN_DUTY_ENABLE_REG_S10 0x101 + +#define PLL_C0_HIGH_REG_S10 0x11B +#define PLL_C0_LOW_REG_S10 0x11E +#define PLL_C0_BYPASS_ENABLE_REG_S10 0x11C +#define PLL_C0_EVEN_DUTY_ENABLE_REG_S10 0x11D + +#define PLL_C1_HIGH_REG_S10 0x11F +#define PLL_C1_LOW_REG_S10 0x122 +#define PLL_C1_BYPASS_ENABLE_REG_S10 0x120 +#define PLL_C1_EVEN_DUTY_ENABLE_REG_S10 0x121 + +#define PLL_LF_REG_S10 0x10A + +#define PLL_CP1_REG_S10 0x101 +#define PLL_CP2_REG_S10 0x10D + +#define PLL_REQUEST_CAL_REG_S10 0x149 +#define PLL_ENABLE_CAL_REG_S10 0x14A +#endif // WINDOWS + +#ifndef DLA_MMD +#include "acl_check_sys_cmd.h" +#include "pkg_editor.h" +#endif + +// MAX size of line read from pipe-ing the output of find_jtag_cable.tcl to MMD +#define READ_SIZE 1024 +// MAX size of command passed to system for invoking find_jtag_cable.tcl from MMD +#define SYSTEM_CMD_SIZE 4 * 1024 + +// Function to install the signal handler for Ctrl-C +// Implemented inside acl_pcie.cpp +extern int install_ctrl_c_handler(int ingore_sig); + +ACL_PCIE_CONFIG::ACL_PCIE_CONFIG(fpga_handle Handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie, ACL_PCIE_DMA *dma) { + m_handle = Handle; + m_io = io; + m_pcie = pcie; + m_dma = dma; + +#if defined(WINDOWS) + fpga_result result = FPGA_OK; + UINT32 NumCmds = 0; + FpgaCmd = NULL; + + // Get the number of supported commands + result = fpgaGetSupportedCommands(Handle, NULL, &NumCmds); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return, "fpgaGetSupportedCommands failed in ACL_PCIE_CONFIG().\n"); + + // Allocate memory for the guid array based on NumCmds + FpgaCmd = (fpga_guid *)malloc(NumCmds * sizeof(fpga_guid)); + + if (FpgaCmd == NULL) { + throw std::bad_alloc(); + } + + ACL_PCIE_ERROR_IF(FpgaCmd == NULL, return, "malloc failed in ACL_PCIE_CONFIG().\n"); + + // Populate the guid array + result = fpgaGetSupportedCommands(Handle, FpgaCmd, &NumCmds); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return, "fpgaGetSupportedCommands failed in ACL_PCIE_CONFIG().\n"); +#endif // WINDOWS + + return; +} + +ACL_PCIE_CONFIG::~ACL_PCIE_CONFIG() { +#if defined(WINDOWS) + // Free the guid array + if (FpgaCmd) { + free(FpgaCmd); + FpgaCmd = NULL; + } +#endif +} + +// Change the kernel region using PR only via PCIe, using an in-memory image of the core.rbf +// For Linux, the actual implementation of PR is inside the kernel mode driver. +// Return 0 on success. +int ACL_PCIE_CONFIG::program_core_with_PR_file_a10(char *core_bitstream, size_t core_rbf_len) { + int pr_result = 1; // set to default - failure + + ACL_PCIE_ERROR_IF(core_bitstream == NULL, return 1, "core_bitstream is an NULL pointer.\n"); + ACL_PCIE_ERROR_IF(core_rbf_len < 1000000, return 1, "size of core rbf file is suspiciously small.\n"); + +#if defined(WINDOWS) + int i; + uint32_t version; + UINT32 to_send, status; + UINT32 *data; + fpga_result result; + + /* Get version ID */ + result = fpgaReadMMIO32(m_handle, ACL_VERSIONID_BAR, ACL_VERSIONID_OFFSET, &version); + ACL_PCIE_DEBUG_MSG(":: VERSION_ID is 0x%08X\n", (int)version); + + /* Check if PR is supported */ + if (version < (unsigned int)ACL_PR_PIO_VERSIONID) { + ACL_PCIE_DEBUG_MSG(":: Currently programmed image does not support PR\n"); + pr_result = 1; + return pr_result; + } + + ACL_PCIE_DEBUG_MSG(":: OK to proceed with PR!\n"); + + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + 4, &status); + ACL_PCIE_DEBUG_MSG(":: Reading 0x%08X from PR IP status register\n", (int)status); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReadMMIO32 failed.\n"); + + to_send = 0x00000001; + ACL_PCIE_DEBUG_MSG(":: Writing 0x%08X to PR IP status register\n", (int)to_send); + result = fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + 4, to_send); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaWriteMMIO32 failed.\n"); + + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + 4, &status); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReadMMIO32 failed.\n"); + ACL_PCIE_DEBUG_MSG(":: Reading 0x%08X from PR IP status register\n", (int)status); + + if ((status != 0x10) && (status != 0x0)) { + ACL_PCIE_ERROR_IF(1, return 1, ":: PR IP not in an usable state.\n"); + } + + data = (UINT32 *)core_bitstream; + ACL_PCIE_DEBUG_MSG(":: Writing %d bytes of bitstream file to PR IP at BAR %d, OFFSET 0x%08X\n", + (int)core_rbf_len, + (int)ACL_PRCONTROLLER_BAR, + (int)ACL_PRCONTROLLER_OFFSET); + for (i = 0; i < (int)core_rbf_len / 4; i++) { + result = fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET, data[i]); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaWriteMMIO32 failed.\n"); + } + + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Reading 0x%08X from PR IP data register\n", (int)status); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReadMMIO32 failed.\n"); + + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + 4, &status); + ACL_PCIE_DEBUG_MSG(":: Reading 0x%08X from PR IP status register\n", (int)status); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReadMMIO32 failed.\n"); + + if (status == 0x14) { + ACL_PCIE_DEBUG_MSG(":: PR done!: 0x%08X\n", (int)status); + pr_result = 0; + } else { + ACL_PCIE_DEBUG_MSG(":: PR error!: 0x%08X\n", (int)status); + pr_result = 1; + } + + ACL_PCIE_DEBUG_MSG(":: PR completed!\n"); + +#endif // WINDOWS +#if defined(LINUX) + struct acl_cmd cmd_pr = {ACLPCI_CMD_BAR, ACLPCI_CMD_DO_PR, NULL, NULL}; + + cmd_pr.user_addr = core_bitstream; + cmd_pr.size = core_rbf_len; + + pr_result = read(m_handle, &cmd_pr, sizeof(cmd_pr)); + +#endif // LINUX + + return pr_result; +} + +// Change the kernel region using PR only via PCIe, using an in-memory image of the core.rbf +// For Linux, the actual implementation of PR is inside the kernel mode driver. +// Return 0 on success. +int ACL_PCIE_CONFIG::program_core_with_PR_file_s10(char *core_bitstream, size_t core_rbf_len, char *pll_config_str) { + int pr_result = 1; // set to default - failure +#if defined(WINDOWS) + uint32_t pll_config_array[8] = {0}; +#else + int pll_config_array[8] = {0}; +#endif // WINDOWS + std::stringstream converter(pll_config_str); + + ACL_PCIE_ERROR_IF(core_bitstream == NULL, return 1, "core_bitstream is an NULL pointer.\n"); + ACL_PCIE_ERROR_IF(core_rbf_len < 1000000, return 1, "size of core rbf file is suspiciously small.\n"); + + /* parse PLL string */ + converter >> pll_config_array[0] >> pll_config_array[1] >> pll_config_array[2] >> pll_config_array[3] >> + pll_config_array[4] >> pll_config_array[5] >> pll_config_array[6] >> pll_config_array[7]; + if (converter.fail() == true) { + ACL_PCIE_ERROR_IF(1, return 1, "PLL configuration string requires 8 integer elements\n"); + }; + +#if defined(WINDOWS) + int i, j, k, result, count, chunk_num, frames; + size_t offset; + uint32_t to_send, status; + uint32_t version; + uint32_t *data; + uint32_t pll_freq_khz, pll_m, pll_n, pll_c0, pll_c1, pll_lf, pll_cp, pll_rc; + uint32_t pll_m_high, pll_m_low, pll_m_bypass_enable, pll_m_even_duty_enable; + uint32_t pll_n_high, pll_n_low, pll_n_bypass_enable, pll_n_even_duty_enable; + uint32_t pll_c0_high, pll_c0_low, pll_c0_bypass_enable, pll_c0_even_duty_enable; + uint32_t pll_c1_high, pll_c1_low, pll_c1_bypass_enable, pll_c1_even_duty_enable; + uint32_t pll_cp1, pll_cp2; + uint32_t pll_byte; + + /* Get version ID */ + result = fpgaReadMMIO32(m_handle, ACL_VERSIONID_BAR, ACL_VERSIONID_OFFSET, &version); + ACL_PCIE_DEBUG_MSG(":: VERSION_ID is 0x%08X\n", (int)version); + + /* Check if PR is supported */ + if (version < (unsigned int)ACL_PR_PIO_VERSIONID) { + ACL_PCIE_DEBUG_MSG(":: Currently programmed image does not support PR\n"); + pr_result = 1; + return pr_result; + } + + ACL_PCIE_DEBUG_MSG(":: OK to proceed with PR!\n"); + + /* freeze bridge */ + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_VERSION_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge version is 0x%08X\n", (int)status); + + result = fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_STATUS_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge status is 0x%08X\n", (int)status); + + ACL_PCIE_DEBUG_MSG(":: Asserting region freeze\n"); + fpgaWriteMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_CTRL_OFFSET, FREEZE_REQ); + Sleep(1); + + result = fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_STATUS_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge status is 0x%08X\n", (int)status); + + ACL_PCIE_DEBUG_MSG(":: PR Beginning\n"); + + /* PR IP write initialisation */ + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_VER_OFST, &status); + ACL_PCIE_DEBUG_MSG(":: ALT_PR_VER_OFST version is 0x%08X\n", (int)status); + + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + ACL_PCIE_DEBUG_MSG(":: ALT_PR_CSR_OFST status is 0x%08X\n", (int)status); + + to_send = ALT_PR_CSR_PR_START; + ACL_PCIE_DEBUG_MSG(":: Starting PR by writing 0x%08X to ALT_PR_CSR_OFST\n", (int)to_send); + fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, to_send); + + /* Wait for PR to be in progress */ + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + i = 0; + while (status != ALT_PR_CSR_STATUS_PR_IN_PROG) { + Sleep(1); + i++; + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + }; + ACL_PCIE_DEBUG_MSG(":: PR IP initialization took %d ms, ALT_PR_CSR_OFST status is 0x%08X\n", i, (int)status); + + // --------------------------------------------------------------- + // Legacy PR using PIO + // --------------------------------------------------------------- + if ((version >= (unsigned int)ACL_PR_PIO_VERSIONID) && (version < (unsigned int)ACL_PR_DMA_VERSIONID)) { + /* PR IP write bitstream */ + MemoryBarrier(); + data = (UINT32 *)core_bitstream; + count = (int)core_rbf_len; + ACL_PCIE_DEBUG_MSG(":: Size of PR RBF is 0x%08X\n", (int)count); + + /* Write out the complete 32-bit chunks */ + /* Wait for a designated amount of time between 4K chunks */ + i = 0; + j = 0; + chunk_num = 0; + while (count >= 4) { + fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_DATA_OFST, data[i]); + i++; + j++; + count = count - 4; + if (j >= 1024) { + chunk_num++; + j = 0; + Sleep(1); + } + } + ACL_PCIE_DEBUG_MSG(":: Number of 4K chunks written: %d\n", (int)chunk_num); + ACL_PCIE_DEBUG_MSG(":: Number of bytes in PR bitstream remaining: %d\n", (int)count); + + /* Write out remaining non 32-bit chunks */ + to_send = data[i]; + switch (count) { + case 3: + to_send = to_send & 0x00ffffff; + fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_DATA_OFST, to_send); + break; + case 2: + to_send = to_send & 0x0000ffff; + fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_DATA_OFST, to_send); + break; + case 1: + to_send = to_send & 0x000000ff; + fpgaWriteMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_DATA_OFST, to_send); + break; + case 0: + break; + default: + /* This will never happen */ + return 1; + } + } + + // --------------------------------------------------------------- + // PR using DMA + // --------------------------------------------------------------- + if (version >= (unsigned int)ACL_PR_DMA_VERSIONID) { + /* PR IP write bitstream */ + MemoryBarrier(); + ACL_PCIE_DEBUG_MSG(":: Size of PR RBF is 0x%08X, initiating DMA transfer to PR IP\n", (int)core_rbf_len); + + /* Write PR bitstream using DMA */ + frames = (int)core_rbf_len / ACL_DMA_PR_ALIGNMENT_BYTES; + ACL_PCIE_DEBUG_MSG( + ":: PR bitstream will be sent in %d Byte frames, a total of %d frames\n", ACL_DMA_PR_ALIGNMENT_BYTES, frames); + + // sending in 4kB frames + for (k = 0; k < frames; k++) { + offset = (size_t)k * ACL_DMA_PR_ALIGNMENT_BYTES; + void *host_addr_new = reinterpret_cast<void *>(core_bitstream + offset); + size_t dev_addr_new = ACL_PCIE_PR_DMA_OFFSET; + + status = (uint32_t)m_dma->read_write(host_addr_new, dev_addr_new, ACL_DMA_PR_ALIGNMENT_BYTES, NULL, false); + + while (!m_dma->is_idle()) { + ACL_PCIE_DEBUG_MSG(":: DMA still in progress...\n"); + } + } + } + + // Wait for PR complete + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + ACL_PCIE_DEBUG_MSG(":: ALT_PR_CSR_OFST status is 0x%08X\n", (int)status); + i = 0; + // wait till we get a PR_SUCCESS, or PR_ERROR, or a 1 second timeout + while (status != ALT_PR_CSR_STATUS_PR_SUCCESS && status != ALT_PR_CSR_STATUS_PR_ERR && i < 100000) { + Sleep(100); + i++; + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + ACL_PCIE_DEBUG_MSG(":: ALT_PR_CSR_OFST status is 0x%08X\n", (int)status); + }; + + if (status == ALT_PR_CSR_STATUS_PR_SUCCESS) { + /* dynamically reconfigure IOPLL for kernel clock */ + /* read kernel clock generation version ID */ + result = fpgaReadMMIO32( + m_handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_VERSION_ID, &status); + ACL_PCIE_DEBUG_MSG(":: Kernel clock generator version ID is 0x%08X\n", (int)status); + + /* extract PLL settings from PLL configuration array */ + pll_freq_khz = pll_config_array[0]; + pll_m = pll_config_array[1]; + pll_n = pll_config_array[2]; + pll_c0 = pll_config_array[3]; + pll_c1 = pll_config_array[4]; + pll_lf = pll_config_array[5]; + pll_cp = pll_config_array[6]; + pll_rc = pll_config_array[7]; + + ACL_PCIE_DEBUG_MSG(":: PLL settings are %d %d %d %d %d %d %d %d\n", + pll_freq_khz, + pll_m, + pll_n, + pll_c0, + pll_c1, + pll_lf, + pll_cp, + pll_rc); + + // Measure kernel clock frequency + fpgaWriteMMIO32( + m_handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_COUNTER, 0); + Sleep(1000); + result = fpgaReadMMIO32( + m_handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_COUNTER, &status); + ACL_PCIE_DEBUG_MSG(":: Before reconfig, kernel clock set to %d Hz\n", (int)status); + + // extract all PLL parameters + pll_m_high = (pll_m >> 8) & 0xFF; + pll_m_low = pll_m & 0xFF; + pll_m_bypass_enable = (pll_m >> 16) & 0x01; + pll_m_even_duty_enable = (pll_m >> 17) & 0x01; + + pll_n_high = (pll_n >> 8) & 0xFF; + pll_n_low = pll_n & 0xFF; + pll_n_bypass_enable = (pll_n >> 16) & 0x01; + pll_n_even_duty_enable = (pll_n >> 17) & 0x01; + + pll_c0_high = (pll_c0 >> 8) & 0xFF; + pll_c0_low = pll_c0 & 0xFF; + pll_c0_bypass_enable = (pll_c0 >> 16) & 0x01; + pll_c0_even_duty_enable = (pll_c0 >> 17) & 0x01; + + pll_c1_high = (pll_c1 >> 8) & 0xFF; + pll_c1_low = pll_c1 & 0xFF; + pll_c1_bypass_enable = (pll_c1 >> 16) & 0x01; + pll_c1_even_duty_enable = (pll_c1 >> 17) & 0x01; + + pll_lf = (pll_lf >> 6) & 0xFF; + + pll_cp = pll_cp & 0xFF; + pll_cp1 = pll_cp & 0x07; + pll_cp2 = (pll_cp >> 3) & 0x07; + + pll_rc = pll_rc & 0x03; + + /* read and write PLL settings */ + to_send = pll_m_high; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_M_HIGH_REG_S10, + &to_send, + 1); + to_send = pll_m_low; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_M_LOW_REG_S10, + &to_send, + 1); + to_send = pll_m_bypass_enable; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_M_BYPASS_ENABLE_REG_S10, + &to_send, + 1); + to_send = (pll_m_even_duty_enable << 7); + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_M_EVEN_DUTY_ENABLE_REG_S10, + &to_send, + 1); + + to_send = pll_n_high; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_N_HIGH_REG_S10, + &to_send, + 1); + to_send = pll_n_low; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_N_LOW_REG_S10, + &to_send, + 1); + to_send = (pll_n_even_duty_enable << 7) | (pll_cp1 << 4) | pll_n_bypass_enable; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_N_BYPASS_ENABLE_REG_S10, + &to_send, + 1); + + to_send = pll_c0_high; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C0_HIGH_REG_S10, + &to_send, + 1); + to_send = pll_c0_low; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C0_LOW_REG_S10, + &to_send, + 1); + to_send = pll_c0_bypass_enable; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C0_BYPASS_ENABLE_REG_S10, + &to_send, + 1); + to_send = (pll_c0_even_duty_enable << 7); + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C0_EVEN_DUTY_ENABLE_REG_S10, + &to_send, + 1); + + to_send = pll_c1_high; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C1_HIGH_REG_S10, + &to_send, + 1); + to_send = pll_c1_low; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C1_LOW_REG_S10, + &to_send, + 1); + to_send = pll_c1_bypass_enable; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C1_BYPASS_ENABLE_REG_S10, + &to_send, + 1); + to_send = (pll_c1_even_duty_enable << 7); + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_C1_EVEN_DUTY_ENABLE_REG_S10, + &to_send, + 1); + + to_send = (pll_cp2 << 5); + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_CP2_REG_S10, + &to_send, + 1); + + to_send = (pll_lf << 3) | (pll_rc << 1); + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_LF_REG_S10, + &to_send, + 1); + + // start PLL calibration + /* read/modify/write the request calibration */ + ACL_PCIE_DEBUG_MSG(":: Requesting PLL calibration\n"); + result = fpgaReadMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_REQUEST_CAL_REG_S10, + &pll_byte, + 1); + to_send = pll_byte | 0x40; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_REQUEST_CAL_REG_S10, + &to_send, + 1); + /* write 0x03 to enable calibration interface */ + to_send = 0x03; + fpgaWriteMmio(m_handle, + ACL_PCIE_KERNELPLL_RECONFIG_BAR, + ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_RECONFIG_CTRL_S10 + PLL_ENABLE_CAL_REG_S10, + &to_send, + 1); + ACL_PCIE_DEBUG_MSG(":: PLL calibration done\n"); + + // Measure kernel clock frequency + fpgaWriteMMIO32( + m_handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_COUNTER, 0); + Sleep(1000); + result = fpgaReadMMIO32( + m_handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET + PLL_OFFSET_COUNTER, &status); + ACL_PCIE_DEBUG_MSG(":: After reconfig, kernel clock set to %d Hz\n", (int)status); + + /* assert reset */ + MemoryBarrier(); + ACL_PCIE_DEBUG_MSG(":: Asserting region reset\n"); + fpgaWriteMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_CTRL_OFFSET, RESET_REQ); + Sleep(10); + + /* unfreeze bridge */ + MemoryBarrier(); + result = + fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_VERSION_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge version is 0x%08X\n", (int)status); + + result = + fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_STATUS_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge status is 0x%08X\n", (int)status); + + ACL_PCIE_DEBUG_MSG(":: Removing region freeze\n"); + fpgaWriteMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_CTRL_OFFSET, UNFREEZE_REQ); + Sleep(1); + + ACL_PCIE_DEBUG_MSG(":: Checking freeze bridge status\n"); + result = + fpgaReadMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_STATUS_OFFSET, &status); + ACL_PCIE_DEBUG_MSG(":: Freeze bridge status is 0x%08X\n", (int)status); + + /* deassert reset */ + MemoryBarrier(); + ACL_PCIE_DEBUG_MSG(":: Deasserting region reset\n"); + fpgaWriteMMIO32(m_handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET + FREEZE_CTRL_OFFSET, 0); + + MemoryBarrier(); + result = fpgaReadMMIO32(m_handle, ACL_PRCONTROLLER_BAR, ACL_PRCONTROLLER_OFFSET + ALT_PR_CSR_OFST, &status); + ACL_PCIE_DEBUG_MSG(":: Reading 0x%08X from PR IP status register\n", (int)status); + if (status == 0x6) { + ACL_PCIE_DEBUG_MSG(":: PR done! Status is 0x%08X\n", (int)status); + pr_result = 0; + } else { + ACL_PCIE_DEBUG_MSG(":: PR error! Status is 0x%08X\n", (int)status); + pr_result = 1; + } + } else { + ACL_PCIE_DEBUG_MSG(":: PR error! Status is 0x%08X\n", (int)status); + pr_result = 1; + } + + ACL_PCIE_DEBUG_MSG(":: PR completed!\n"); + +#endif // WINDOWS +#if defined(LINUX) + struct acl_cmd cmd_pr = {ACLPCI_CMD_BAR, ACLPCI_CMD_DO_PR, NULL, NULL}; + + cmd_pr.user_addr = core_bitstream; + cmd_pr.size = core_rbf_len; + cmd_pr.device_addr = pll_config_array; + + pr_result = read(m_handle, &cmd_pr, sizeof(cmd_pr)); + +#endif // LINUX + + return pr_result; +} + +// Windows specific code to disable PCIe advanced error reporting on the +// upstream port. +// No-op in Linux because save_pcie_control_regs() has already disabled +// AER on the upstream port. +// Returns 0 on success +int ACL_PCIE_CONFIG::disable_AER_windows(void) { + fpga_result result = FPGA_OK; + +#if defined(WINDOWS) + // IOCTL call to disable AER in kernel mode + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_DISABLE_AER), NULL, NULL, 0); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when disabling AER.\n"); +#endif // WINDOWS + return result; +} + +// Windows specific code to enable PCIe advanced error reporting on the +// upstream port. +// No-op in Linux because load_pcie_control_regs() has already enabled +// AER on the upstream port. +// Returns 0 on success +int ACL_PCIE_CONFIG::enable_AER_and_retrain_link_windows(void) { + fpga_result result = FPGA_OK; + +#if defined(WINDOWS) + // IOCTL call to enable AER and retrain link in kernel mode + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_ENABLE_AER_RETRAIN_LINK), NULL, NULL, 0); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when enabling AER.\n"); +#endif // WINDOWS + return result; +} + +// Program the FPGA using a given SOF file +// Quartus is needed for this, because, +// quartus_pgm is used to program the board through USB blaster +// For Linux, when the kernel driver is asked to save/load_pcie_control_regs(), +// it will also disable/enable the aer on the upstream, so no need to +// implement those here. +// NOTE: This function only works with single device machines - if there +// are multiple cards (and multiple USB-blasters) in the system, it doesn't +// properly determine which card is which. Only the first device will be +// programmed. +// Return 0 on success. +int ACL_PCIE_CONFIG::program_with_SOF_file(const char *filename, const char *ad_cable, const char *ad_device_index) { + const int MAX_ATTEMPTS = 3; + int program_failed = 1; + int status; + bool use_cable_autodetect = true; + + // If ad_cable value is "0", either JTAG cable autodetect failed or not + // supported, then use the default value + if (strcmp(ad_cable, "0") == 0) use_cable_autodetect = false; + + const char *cable = getenv("ACL_PCIE_JTAG_CABLE"); + if (!cable) { + if (use_cable_autodetect) { + cable = ad_cable; + ACL_PCIE_DEBUG_MSG("setting Cable to autodetect value %s\n", cable); + } else { + cable = "1"; + ACL_PCIE_DEBUG_MSG("setting Cable to default value %s\n", cable); + } + } + + const char *device_index = getenv("ACL_PCIE_JTAG_DEVICE_INDEX"); + if (!device_index) { + if (use_cable_autodetect) { + device_index = ad_device_index; + ACL_PCIE_DEBUG_MSG("setting Device Index to autodetect value %s\n", device_index); + } else { + device_index = "1"; + ACL_PCIE_DEBUG_MSG("setting Device Index to default value %s\n", device_index); + } + } + + char cmd[4 * 1024]; +#ifdef DLA_MMD +#if defined(WINDOWS) + if ((ACL_PCIE_DEBUG | 0) >= VERBOSITY_DEFAULT) { + snprintf(cmd, sizeof(cmd), "quartus_pgm -c %s -m jtag -o \"P;%s@%s\"", cable, filename, device_index); + } else { + snprintf(cmd, sizeof(cmd), "quartus_pgm -c %s -m jtag -o \"P;%s@%s\" > nul 2>&1", cable, filename, device_index); + } +#else + snprintf(cmd, sizeof(cmd), "quartus_pgm -c %s -m jtag -o \"P;%s@%s\" 2>&1 >/dev/null", cable, filename, device_index); +#endif + ACL_PCIE_INFO("Executing \"%s\"\n", cmd); +#else +#if defined(WINDOWS) + snprintf( + cmd, sizeof(cmd), "aocl do quartus_pgm -c %s -m jtag -o \"P;%s@%s\" > nul 2>&1", cable, filename, device_index); +#endif +#if defined(LINUX) + snprintf(cmd, + sizeof(cmd), + "aocl do quartus_pgm -c %s -m jtag -o \"P;%s@%s\" 2>&1 >/dev/null", + cable, + filename, + device_index); +#endif + ACL_PCIE_DEBUG_MSG("Executing \"%s\"\n", cmd); +#endif + + // Disable AER + status = disable_AER_windows(); + ACL_PCIE_ERROR_IF(status, return -1, "Failed to disable AER on Windows before programming SOF.\n"); + + // Set the program to ignore the ctrl-c signal + // This setting will be inherited by the system() function call below, + // so that the quartus_pgm call won't be interrupt by the ctrl-c signal. + install_ctrl_c_handler(1 /* ignore the signal */); + + // Program FPGA by executing the command +#ifndef DLA_MMD + ACL_PCIE_ASSERT(system_cmd_is_valid(cmd), "Invalid system() function parameter: %s\n", cmd); +#endif + for (int attempts = 0; attempts < MAX_ATTEMPTS && program_failed; attempts++) { + if (attempts > 0) { + ACL_PCIE_INFO("Execution failed. Will try again in case the error was transient.\n"); + } + program_failed = system(cmd); +#if defined(WINDOWS) + Sleep(2000); +#endif // WINDOWS +#if defined(LINUX) + sleep(2); +#endif // LINUX + } + + // Restore the original custom ctrl-c signal handler + install_ctrl_c_handler(0 /* use the custom signal handler */); + + // Enable AER + status = enable_AER_and_retrain_link_windows(); + ACL_PCIE_ERROR_IF(status, return -1, "Failed to enable AER and retrain link on Windows after programming SOF.\n"); + + return program_failed; +} + +bool ACL_PCIE_CONFIG::find_cable_with_ISSP(unsigned int cade_id, char *ad_cable, char *ad_device_index) { + FILE *fp; + int status; + char line_in[READ_SIZE]; + bool found_cable = false; + + char cmd[SYSTEM_CMD_SIZE]; + const char *aocl_boardpkg_root = getenv("AOCL_BOARD_PACKAGE_ROOT"); + if (!aocl_boardpkg_root) { + ACL_PCIE_INFO("AOCL_BOARD_PACKAGE_ROOT not set!!!"); + return false; + } + + snprintf(cmd, sizeof(cmd), "aocl do quartus_stp -t %s/scripts/find_jtag_cable.tcl %X", aocl_boardpkg_root, cade_id); + ACL_PCIE_DEBUG_MSG("executing \"%s\"\n", cmd); + + // Open PIPE to tcl script +#ifndef DLA_MMD + ACL_PCIE_ASSERT(system_cmd_is_valid(cmd), "Invalid popen() function parameter: %s\n", cmd); +#endif +#if defined(WINDOWS) + fp = _popen(cmd, "r"); +#endif // WINDOWS +#if defined(LINUX) + fp = popen(cmd, "r"); +#endif // LINUX + + if (fp == NULL) { + ACL_PCIE_INFO("Couldn't open fp file\n"); + } else { + // Read everyline and look for matching string from tcl script + while (fgets(line_in, READ_SIZE, fp) != NULL) { + ACL_PCIE_DEBUG_MSG("%s", line_in); + const char *str_match_cable = "Matched Cable:"; + const char *str_match_dev_name = "Device Name:@"; + const char *str_match_end = ":"; + // parsing the string and extracting the cable/index value + // from the output of find_jtag_cable.tcl script + char *pos_cable = strstr(line_in, str_match_cable); + if (pos_cable) { + found_cable = true; + // find the sub-string locations in the line + char *pos_dev_name = strstr(line_in, str_match_dev_name); + if (pos_dev_name) { + char *pos_end = + strstr(pos_dev_name + strnlen(str_match_dev_name, MAX_NAME_SIZE), str_match_end); // Find the last ":" + if (pos_end) { + // calculate the cable/index string size + size_t i_cable_str_len = pos_dev_name - pos_cable - strnlen(str_match_cable, MAX_NAME_SIZE); + size_t i_dev_index_str_len = pos_end - pos_dev_name - strnlen(str_match_dev_name, MAX_NAME_SIZE); + // extract the cable/index value from the line + snprintf(ad_cable, + AD_CABLE_SIZE, + "%.*s", + (int)i_cable_str_len, + pos_cable + strnlen(str_match_cable, MAX_NAME_SIZE)); + snprintf(ad_device_index, + AD_CABLE_SIZE, + "%.*s", + (int)i_dev_index_str_len, + pos_dev_name + strnlen(str_match_dev_name, MAX_NAME_SIZE)); + ACL_PCIE_DEBUG_MSG("JTAG Autodetect device found Cable:%s, Device Index:%s\n", ad_cable, ad_device_index); + break; + } + } + } + } + +#if defined(WINDOWS) + status = _pclose(fp); +#endif // WINDOWS +#if defined(LINUX) + status = pclose(fp); +#endif // LINUX + + if (status == -1) { + /* Error reported by pclose() */ + ACL_PCIE_INFO("Couldn't close find_cable_with_ISSP file\n"); + } else { + /* Use macros described under wait() to inspect `status' in order + * to determine success/failure of command executed by popen() + * */ + } + } + + if (!found_cable) { + ACL_PCIE_INFO("Autodetect Cable not found!!\n"); + } + + return found_cable; +} + +// Functions to save/load control registers form PCI Configuration Space +// This saved registers are used to restore the PCIe link after reprogramming +// through methods other than PR +// For Windows, the register values are stored in this class, and do +// nothing else +// For Linux, the register values are stored inside the kernel driver, +// And, it will disable the interrupt and the aer on the upstream, +// when the save_pci_control_regs() function is called. They will +// be enable when load_pci_control_regs() is called. +// Return 0 on success +int ACL_PCIE_CONFIG::save_pci_control_regs() { + int save_failed = 1; + +#if defined(WINDOWS) + fpga_result result = FPGA_OK; + + // IOCTL call to save PCI control register + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SAVE_PCI_CTRL_REG), NULL, NULL, 0); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when saving PCI Control registers.\n"); + + save_failed = (result == FPGA_OK) ? (0) : (-1); +#endif // WINDOWS +#if defined(LINUX) + struct acl_cmd cmd_save = {ACLPCI_CMD_BAR, ACLPCI_CMD_SAVE_PCI_CONTROL_REGS, NULL, NULL}; + save_failed = read(m_handle, &cmd_save, 0); +#endif // LINUX + + return save_failed; +} + +int ACL_PCIE_CONFIG::load_pci_control_regs() { + int load_failed = 1; +#if defined(WINDOWS) + + fpga_result result = FPGA_OK; + // IOCTL call to load PCI control register + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_LOAD_PCI_CTRL_REG), NULL, NULL, 0); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when loading PCI Control registers.\n"); + + load_failed = (result == FPGA_OK) ? (0) : (-1); +#endif // WINDOWS +#if defined(LINUX) + struct acl_cmd cmd_load = {ACLPCI_CMD_BAR, ACLPCI_CMD_LOAD_PCI_CONTROL_REGS, NULL, NULL}; + load_failed = read(m_handle, &cmd_load, 0); +#endif // LINUX + + return load_failed; +} + +// Functions to query the PCI related information +// Use NULL as input for the info that you don't care about +// Return 0 on success. +int ACL_PCIE_CONFIG::query_pcie_info(unsigned int *pcie_gen, unsigned int *pcie_num_lanes, char *pcie_slot_info_str) { + int status = 0; +#if defined(WINDOWS) + fpga_result result = FPGA_OK; + // IOCTL call to obtain PCIe gen information + result = fpgaProcessDeviceCmd( + m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_GET_PCI_GEN), NULL, pcie_gen, sizeof(unsigned int)); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when finding PCI device gen info.\n"); + + result = fpgaProcessDeviceCmd( + m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_GET_PCI_LANES), NULL, pcie_num_lanes, sizeof(unsigned int)); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "fpgaProcessDeviceCmd failed when finding PCI device lanes info.\n"); + + status = (result == FPGA_OK) ? (0) : (-1); +#endif // WINDOWS +#if defined(LINUX) + struct acl_cmd driver_cmd; + + if (pcie_gen != NULL) { + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_GET_PCI_GEN; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = pcie_gen; + driver_cmd.size = sizeof(*pcie_gen); + status |= read(m_handle, &driver_cmd, sizeof(driver_cmd)); + } + + if (pcie_num_lanes != NULL) { + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_GET_PCI_NUM_LANES; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = pcie_num_lanes; + driver_cmd.size = sizeof(*pcie_num_lanes); + status |= read(m_handle, &driver_cmd, sizeof(driver_cmd)); + } + + if (pcie_slot_info_str != NULL) { + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_GET_PCI_SLOT_INFO; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = pcie_slot_info_str; + driver_cmd.size = sizeof(pcie_slot_info_str); + status |= read(m_handle, &driver_cmd, sizeof(driver_cmd)); + } +#endif // LINUX + return status; +} + +void ACL_PCIE_CONFIG::wait_seconds(unsigned seconds) { +#if defined(WINDOWS) + Sleep(seconds * 1000); +#endif // WINDOWS + +#if defined(LINUX) + sleep(seconds); +#endif // LINUX +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.h new file mode 100644 index 0000000..3f07634 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_config.h @@ -0,0 +1,109 @@ +#ifndef ACL_PCIE_CONFIG_H +#define ACL_PCIE_CONFIG_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_config.h -------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle functions that program the FPGA. */ +/* The actual implementation of the class lives in the acl_pcie_config.cpp, */ +/* so look there for full documentation. */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#ifdef DLA_MMD +#include <cstddef> //size_t +#endif + +// Forward declaration for classes used by ACL_PCIE_DEVICE +class ACL_PCIE_DMA; +class ACL_PCIE_DEVICE; +class ACL_PCIE_MM_IO_MGR; + +#define PCIE_AER_CAPABILITY_ID ((DWORD)0x0001) +#define PCIE_AER_UNCORRECTABLE_STATUS_OFFSET ((DWORD)0x4) +#define PCIE_AER_UNCORRECTABLE_MASK_OFFSET ((DWORD)0x8) +#define PCIE_AER_CORRECTABLE_STATUS_OFFSET ((DWORD)0x10) +#define PCIE_AER_SURPRISE_DOWN_BIT ((DWORD)(1 << 5)) + +// The size of the char array that holds the name of autodetect JTAG cable and device index +#define AD_CABLE_SIZE 10 + +#if defined(LINUX) +typedef int fpga_handle; +#else +#include <opae/fpga.h> +#endif // LINUX + +class ACL_PCIE_CONFIG { + public: + ACL_PCIE_CONFIG(fpga_handle handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie, ACL_PCIE_DMA *dma); + ~ACL_PCIE_CONFIG(); + + // Change the core only via PCIe, using an in-memory image of the core.rbf + // This is supported only for Stratix V and newer devices. + // Return 0 on success. + int program_core_with_PR_file_a10(char *core_bitstream, size_t core_rbf_len); + int program_core_with_PR_file_s10(char *core_bitstream, size_t core_rbf_len, char *pll_config_str); + + // Program the FPGA using a given SOF file + // Input filename, autodetect cable, autodetect device index + // Return 0 on success. + int program_with_SOF_file(const char *filename, const char *ad_cable, const char *ad_device_index); + + // Look up CADEID using ISSP + // Return TRUE with cable value in ad_cable, ad_device_index if cable found + // Otherwise return FALSE + bool find_cable_with_ISSP(unsigned int cade_id, char *ad_cable, char *ad_device_index); + + // Functions to save/load control registers from PCI Configuration Space + // Return 0 on success. + int save_pci_control_regs(); + int load_pci_control_regs(); + + // Functions to query the PCI related information + // Use NULL as input for the info that you don't care about + // Return 0 on success. + int query_pcie_info(unsigned int *pcie_gen, unsigned int *pcie_num_lanes, char *pcie_slot_info_str); + + // Windows-specific code to control AER, and retrain the link + int enable_AER_and_retrain_link_windows(void); + int disable_AER_windows(void); + + // Platform agnostic sleep (in seconds) + void wait_seconds(unsigned seconds); + + private: + ACL_PCIE_CONFIG &operator=(const ACL_PCIE_CONFIG &) { return *this; } + + ACL_PCIE_CONFIG(const ACL_PCIE_CONFIG &src) {} + + fpga_handle m_handle; + ACL_PCIE_MM_IO_MGR *m_io; + ACL_PCIE_DEVICE *m_pcie; + ACL_PCIE_DMA *m_dma; +#if defined(WINDOWS) + fpga_guid *FpgaCmd; +#endif // WINDOWS +}; + +#endif // ACL_PCIE_CONFIG_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.cpp new file mode 100644 index 0000000..8afc1c7 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.cpp @@ -0,0 +1,61 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_debug.cpp ------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#include "acl_pcie_debug.h" +#include <stdio.h> +#include <stdlib.h> + +int ACL_PCIE_DEBUG = 0; +int ACL_PCIE_WARNING = 1; // turn on the warning message by default + +int ACL_PCIE_DEBUG_FLASH_DUMP_BOOT_SECTOR = 0; + +void set_mmd_debug() { + char* mmd_debug_var = getenv("ACL_PCIE_DEBUG"); + if (mmd_debug_var) { + char* endptr = NULL; + long parsed_count; + parsed_count = strtol(mmd_debug_var, &endptr, 10); + if (endptr == mmd_debug_var // no valid characters + || *endptr // an invalid character + || (parsed_count < 0 || parsed_count >= (long)VERBOSITY_EVERYTHING)) { + // malformed string, do nothing + } else { + ACL_PCIE_DEBUG = (int)parsed_count; + printf("\n:: MMD DEBUG LEVEL set to %d\n", ACL_PCIE_DEBUG); + } + } + + char* hal_debug_dump_flash_bootsect = getenv("ACL_PCIE_DEBUG_FLASH_DUMP_BOOT_SECTOR"); + if (hal_debug_dump_flash_bootsect) ACL_PCIE_DEBUG_FLASH_DUMP_BOOT_SECTOR = atoi(hal_debug_dump_flash_bootsect); +} + +void set_mmd_warn_msg() { + char* mmd_warn_var = getenv("ACL_PCIE_WARNING"); + if (mmd_warn_var) { + ACL_PCIE_WARNING = atoi(mmd_warn_var); + } +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.h new file mode 100644 index 0000000..072eabc --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_debug.h @@ -0,0 +1,64 @@ +#ifndef ACL_PCIE_DEBUG_H +#define ACL_PCIE_DEBUG_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_debug.h --------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +enum ACL_VERBOSITY { + VERBOSITY_DEFAULT = 1, + VERBOSITY_INVOCATION = 2, // Dump kernel invocation details + VERBOSITY_OP = 3, // Dump operation invocation details + VERBOSITY_IRQ = 5, + VERBOSITY_BLOCKTX = 9, // Dump PCIe block transfers + VERBOSITY_PCIE = 10, // Dump all PCIe transactions + VERBOSITY_EVERYTHING = 100 +}; + +extern int ACL_PCIE_DEBUG; +extern int ACL_PCIE_WARNING; +extern int ACL_PCIE_DEBUG_FLASH_DUMP_BOOT_SECTOR; + +// This function gets the value of ACL_PCIE_DEBUG from the environment variable +void set_mmd_debug(); +void set_mmd_warn_msg(); + +#include <stdio.h> + +#define ACL_PCIE_DEBUG_MSG(m, ...) ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_DEFAULT, m, ##__VA_ARGS__) +#define ACL_PCIE_DEBUG_MSG_VERBOSE(verbosity, m, ...) \ + if ((ACL_PCIE_DEBUG | 0) >= verbosity) do { \ + printf((m), ##__VA_ARGS__), fflush(stdout); \ + } while (0) + +#define ACL_PCIE_WARN_MSG(...) \ + do { \ + if (ACL_PCIE_WARNING) { \ + printf("** WARNING: " __VA_ARGS__); \ + fflush(stdout); \ + } \ + } while (0) + +#endif // ACL_PCIE_DEBUG_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.cpp new file mode 100644 index 0000000..8489c32 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.cpp @@ -0,0 +1,2029 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_device.cpp ------------------------------------------ C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle operations on a single device. */ +/* The declaration of the class lives in the acl_pcie_device.h */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(WINDOWS) +#define NOMINMAX +#include <time.h> +#endif // WINDOWS + +// common and its own header files +#include "acl_pcie.h" +#include "acl_pcie_device.h" + +// other header files inside MMD driver +#include "acl_pcie_config.h" +#include "acl_pcie_debug.h" +#include "acl_pcie_dma.h" +#include "acl_pcie_mm_io.h" +#if !defined(DLA_MMD) || defined(WINDOWS) +#include "pkg_editor.h" +#endif + +// other standard header files +#include <stdlib.h> +#include <string.h> +#include <fstream> +#include <limits> +#include <random> +#include <sstream> +#include <stdexcept> +#include "acl_pcie_hostch.h" + +#if defined(LINUX) +#include <fcntl.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#endif // LINUX + +#define MAX_LEN 1024 + +#define FREEZE_CTRL_OFFSET 4 +#ifndef PAGE_SIZE +#define PAGE_SIZE 4096 +#endif +#define ACL_VERSIONID_MIN 0xA0C7C1E0 + +static int num_open_devices = 0; + +#if defined(WINDOWS) +fpga_handle open_device_windows(ACL_PCIE_DEVICE_DESCRIPTION *info, int dev_num); + +// Interrupt service routine for all interrupts on the PCIe interrupt line +// PCIe interrupts in Windows XP are level-based. The KMD is responsible for +// masking off the interrupt until this routine can service the request at +// user-mode priority. +extern void pcie_interrupt_handler(void *data); +#endif // WINDOWS +#if defined(LINUX) +fpga_handle open_device_linux(ACL_PCIE_DEVICE_DESCRIPTION *info, int dev_num); +#endif // LINUX + +ACL_PCIE_DEVICE::ACL_PCIE_DEVICE(int dev_num, const char *name, int handle, int user_signal_number) + : kernel_interrupt(NULL), + kernel_interrupt_user_data(NULL), + device_interrupt(NULL), + device_interrupt_user_data(NULL), + event_update(NULL), + event_update_user_data(NULL), + m_user_signal_number(0), + m_io(NULL), + m_dma(NULL), + m_hostch(NULL), + m_config(NULL), + m_handle(-1), + m_device(INVALID_HANDLE_VALUE), +#if ACL_USE_DMA == 1 + m_use_dma_for_big_transfers(true), +#else + m_use_dma_for_big_transfers(false), +#endif + m_mmd_irq_handler_enable(false), + m_initialized(false), + m_being_programmed(false), + m_skip_quartus_version_check(false), + m_segment(0) { + if (NULL == name) { + // Throw an error and bail out + throw std::runtime_error("Invalid argument, passed in an empty name pointer when creating device object!"); + } + + int status = 0; + + // Set debug level from the environment variable ACL_PCIE_DEBUG + // Determine if warning messages should be disabled depends on ACL_PCIE_WARNING + if (num_open_devices == 0) { + set_mmd_debug(); + set_mmd_warn_msg(); + } + +#if defined(WINDOWS) + strncpy_s(m_name, MAX_NAME_LENGTH, name, (MAX_NAME_LENGTH - 1)); +#else + strncpy(m_name, name, (MAX_NAME_LENGTH - 1)); +#endif + m_name[(MAX_NAME_LENGTH - 1)] = '\0'; + + m_handle = handle; + m_info.vendor_id = ACL_PCI_INTELFPGA_VENDOR_ID; + m_info.device_id = 0; // search for all device id + m_info.interrupt_valid = false; + m_info.interrupt_data = 0x00; + m_info.interrupt_addr = 0x00; + +#if defined(WINDOWS) + m_device = open_device_windows(&m_info, dev_num); +#endif // WINDOWS +#if defined(LINUX) + m_device = open_device_linux(&m_info, dev_num); +#endif // LINUX + + // Return to caller if this is simply an invalid device. + if (m_device == INVALID_HANDLE_VALUE) { + return; + } + + // Initialize device IO and CONFIG objects + m_io = new ACL_PCIE_MM_IO_MGR(m_device); + + // Initialize the DMA object and enable interrupts on the DMA controller + try { + m_dma = new ACL_PCIE_DMA(m_device, m_io, this); + } + + // Catch any memory allocation failures + catch (std::bad_alloc &) { + throw std::bad_alloc(); + } + + try { + m_config = new ACL_PCIE_CONFIG(m_device, m_io, this, m_dma); + } + + catch (std::bad_alloc &) { + throw std::bad_alloc(); + } + + // Set the segment ID to 0 first forcing cached "segment" to all 1s + m_segment = ~m_segment; + if (this->set_segment(0x0)) { + return; + } + + // performance basic I/O tests + if (this->version_id_test()) { + return; + } + if (this->wait_for_uniphy()) { + return; + } + + // Get PCIE information + unsigned int pcie_gen, pcie_num_lanes; + char pcie_slot_info_str[PCIE_SLOT_INFO_STR_LEN] = {0}; + + status = m_config->query_pcie_info(&pcie_gen, &pcie_num_lanes, pcie_slot_info_str); + ACL_PCIE_ERROR_IF(status, return, "[%s] fail to query PCIe related information.\n", m_name); + snprintf(m_info.pcie_info_str, + PCIE_INFO_STR_LEN, + "dev_id = " DWORD_FMT_4X ", bus:slot.func = %s, Gen%u x%u", + m_info.device_id, + pcie_slot_info_str, + pcie_gen, + pcie_num_lanes); + + m_user_signal_number = user_signal_number; + + // Initialize the Host Channel object + m_hostch = new ACL_PCIE_HOSTCH(m_device, m_io, this, m_dma); + + if (this->enable_interrupts(m_user_signal_number)) { + return; + } + + char *str_test_quartus_ver = getenv("ACL_SKIP_QUARTUS_VERSION_CHECK"); + if (str_test_quartus_ver) m_skip_quartus_version_check = 1; + +#if defined(WINDOWS) + enable_msi(true); +#endif + +#ifdef DLA_MMD + // software reset + uint32_t software_reset_data = 0; // value doesn't matter, any write to software reset will cause it to trigger + constexpr int software_reset_offset = 0x8000; + status = m_io->kernel_if->write_block(software_reset_offset, sizeof(uint32_t), &software_reset_data); + ACL_PCIE_ERROR_IF(status, return, "[%s] failed to write block.\n", m_name); + // software reset applies backpressure to the avalon interface while the reset counter is running + // issue a read request, which will not return until the reset counter is done + status = m_io->kernel_if->read_block(software_reset_offset, sizeof(uint32_t), &software_reset_data); + ACL_PCIE_ERROR_IF(status, return, "[%s] failed to read block.\n", m_name); +#endif + // Done! + m_initialized = true; + ACL_PCIE_DEBUG_MSG(":: [%s] successfully initialized (device id: " DWORD_FMT_X ").\n", m_name, m_info.device_id); + ACL_PCIE_DEBUG_MSG(":: Using DMA for big transfers? %s\n", (m_use_dma_for_big_transfers ? "yes" : "no")); +} + +ACL_PCIE_DEVICE::~ACL_PCIE_DEVICE() { +#if defined(WINDOWS) + enable_msi(false); +#endif + + int status = this->disable_interrupts(); + ACL_PCIE_ERROR_IF(status, /* do nothing */, "[%s] fail disable interrupt in device destructor.\n", m_name); + + if (m_hostch) { + delete m_hostch; + m_hostch = NULL; + } + if (m_config) { + delete m_config; + m_config = NULL; + } + if (m_dma) { + delete m_dma; + m_dma = NULL; + } + if (m_io) { + delete m_io; + m_io = NULL; + } + + if (is_valid()) { + --num_open_devices; +#if defined(WINDOWS) + fpga_result result = fpgaClose(m_device); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return, "[%s] failed to close the device handle.\n", m_name); + +#endif // WINDOWS +#if defined(LINUX) + close(m_device); +#endif // LINUX + } +} + +#if defined(WINDOWS) +// Enable/Disable MSI +void ACL_PCIE_DEVICE::enable_msi(bool enable) { + int status; + + if (!m_info.interrupt_valid) { + return; + } + + if (!enable) { + // disable MSI DATA + m_io->pcie_cra->write32(PCIE_CRA_MSI_DATA, 0x00); + } else { + status = m_io->pcie_cra->write32(PCIE_CRA_MSI_ADDR_L, m_info.interrupt_addr & 0xffffffff); + status = m_io->pcie_cra->write32(PCIE_CRA_MSI_ADDR_H, (m_info.interrupt_addr >> 0x20) & 0xffffffff); + MemoryBarrier(); + // enable MSI DATA + status = m_io->pcie_cra->write32(PCIE_CRA_MSI_DATA, PCIE_CRA_MSI_ENABLE | m_info.interrupt_data ); + } + MemoryBarrier(); +} + +fpga_handle open_device_windows(ACL_PCIE_DEVICE_DESCRIPTION *info, int dev_num) { + fpga_result result; + fpga_handle device = INVALID_HANDLE_VALUE; + DWORD pci_class_code_rev = 0; + DWORD pci_subsystem_ids = 0; + DWORD pci_link_info = 0; + + // Variables for fpga enumerate + fpga_properties filter = NULL; + UINT32 numMatches; + fpga_token afcToken; + volatile PUINT64 mmioPtr = NULL; + + // Variables for fpga properties + fpga_properties prop = nullptr; + UINT8 bus; + UINT8 l_device; + UINT8 function; + + const UINT8 CAP_PTR_ADDRESS = 0x34; + const UINT8 MSI_CAP_ID = 0x05; + UINT8 nextCapPtr; + UINT8 msiCapPtr; + UINT8 capID; + bool hasFound = false; + UINT8 capArray[2]; + UINT16 msi_control; + UINT16 data16 = 0x00; + UINT32 data32 = 0x00; + UINT64 data64 = 0x00; + + // Initialize filter structure + result = fpgaGetProperties(NULL, &filter); + if (result != FPGA_OK) { + device = INVALID_HANDLE_VALUE; + ACL_PCIE_ERROR_IF(1, goto End, "failed to get properties.\n"); + } + + // Set object type in filter structure + result = fpgaPropertiesSetObjectType(filter, FPGA_DEVICE); + if (result != FPGA_OK) { + device = INVALID_HANDLE_VALUE; + ACL_PCIE_ERROR_IF(1, goto DestroyProp, "failed to set object type.\n"); + } + + // Set vendor ID in the filter structure + result = fpgaPropertiesSetVendorID(filter, (uint16_t)info->vendor_id); + if (result != FPGA_OK) { + device = INVALID_HANDLE_VALUE; + ACL_PCIE_ERROR_IF(1, goto DestroyProp, "failed to set vendor ID.\n"); + } + + // Enumerate all PCI devices and find devices matching the filters + result = fpgaEnumerate(&filter, 1, &afcToken, 1, &numMatches); + if (result != FPGA_OK) { + device = INVALID_HANDLE_VALUE; + ACL_PCIE_ERROR_IF(1, goto DestroyProp, "failed to scan for the PCI device.\n"); + } + + if (numMatches < 1) { + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] Device not found\n", dev_num); + device = INVALID_HANDLE_VALUE; + goto DestroyTok; + } + + // Open the device handle + result = fpgaOpen(afcToken, &device, 0); + if (result != FPGA_OK) { + device = INVALID_HANDLE_VALUE; + ACL_PCIE_ERROR_IF(1, goto DestroyTok, "[acl" ACL_BOARD_PKG_NAME "%d] failed to open the device.\n", dev_num); + } + + // Map MMIO number 0 + result = fpgaMapMMIO(device, 0, (PUINT64 *)&mmioPtr); + if (result != FPGA_OK) { + ACL_PCIE_ERROR_IF(1, goto Close, "[acl" ACL_BOARD_PKG_NAME "%d] failed to map MMIO.\n", dev_num); + } + + // Read SubSystem IDs out of PCI config space + result = fpgaReadPciConfigSpace(device, 0x2C, (PVOID)&pci_subsystem_ids, sizeof(pci_subsystem_ids)); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI SubSystem IDs found: 0x%lx\n", dev_num, pci_subsystem_ids); + if ((ACL_PCIE_READ_BIT_RANGE(pci_subsystem_ids, 31, 16) != ACL_PCI_SUBSYSTEM_DEVICE_ID) || + (ACL_PCIE_READ_BIT_RANGE(pci_subsystem_ids, 15, 0) != ACL_PCI_SUBSYSTEM_VENDOR_ID)) { + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME + "%d] PCI SubSystem IDs do not match, found %08lx but expected %04x%04x\n", + dev_num, + pci_subsystem_ids, + ACL_PCI_SUBSYSTEM_DEVICE_ID, + ACL_PCI_SUBSYSTEM_VENDOR_ID); + goto Close; + } + // Save device id + info->device_id = ACL_PCI_SUBSYSTEM_DEVICE_ID; + + // Read Class code out of PCI config space + result = fpgaReadPciConfigSpace(device, 8, (PVOID)&pci_class_code_rev, sizeof(pci_class_code_rev)); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Class Code and Rev is: %lx\n", dev_num, pci_class_code_rev); + if (((pci_class_code_rev & (0xff00ff00)) >> 8) != ACL_PCI_CLASSCODE) { + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Class Code does not match, expected %x, read %ld\n", + dev_num, + ACL_PCI_CLASSCODE, + (pci_class_code_rev & 0xff00ff00) >> 8); + goto Close; + } + + // Check PCI Revision + if ((pci_class_code_rev & 0x0ff) != ACL_PCI_REVISION) { + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Revision does not match\n", dev_num); + goto Close; + } + + // Read MSI data and address + info->interrupt_valid = false; + result = fpgaReadPciConfigSpace(device, CAP_PTR_ADDRESS, (PVOID)&nextCapPtr, sizeof(nextCapPtr)); + while (!hasFound && nextCapPtr > CAP_PTR_ADDRESS && FPGA_OK == result) { + result = fpgaReadPciConfigSpace(device, nextCapPtr, (PVOID)&capArray, sizeof(capArray)); + if (FPGA_OK == result) { + capID = capArray[0]; + if (capID == MSI_CAP_ID) { + hasFound = true; + info->interrupt_valid = true; + info->interrupt_addr = 0x00; + info->interrupt_data = 0x00; + msiCapPtr = nextCapPtr; + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x02, (PVOID)&msi_control, sizeof(msi_control)); + if (FPGA_OK == result) { + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] %d-bit address, %d-bit data\n", + dev_num, + (msi_control & 0x0080) ? 64 : 32, + (msi_control & 0x0200) ? 32 : 16); + if (msi_control & 0x0080) { // 64-bit address + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x04, (PVOID)&data64, sizeof(data64)); + if (FPGA_OK == result) { + info->interrupt_addr = data64; + if (msi_control & 0x0200) { // Extended message enable + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x0C, (PVOID)&data32, sizeof(data32)); + if (FPGA_OK == result) { + info->interrupt_data = data32; + } + } else { + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x0C, (PVOID)&data16, sizeof(data16)); + if (FPGA_OK == result) { + info->interrupt_data = data16; + } + } + } + } else { // 32-bit address + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x04, (PVOID)&data32, sizeof(data32)); + if (FPGA_OK == result) { + info->interrupt_addr = data32; + if (msi_control & 0x0200) { // Extended message enable + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x08, (PVOID)&data32, sizeof(data32)); + if (FPGA_OK == result) { + info->interrupt_data = data32; + } + } else { + result = fpgaReadPciConfigSpace(device, msiCapPtr + 0x08, (PVOID)&data16, sizeof(data16)); + if (FPGA_OK == result) { + info->interrupt_data = data16; + } + } + } + } + } + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME + "%d] MSI Control = 0x%04x, MSI Address = 0x%llx, MSI Data = 0x%x\n", + dev_num, + msi_control, + info->interrupt_addr, + info->interrupt_data); + } else { + nextCapPtr = capArray[1]; + } + } + } + + if (result != FPGA_OK || !info->interrupt_valid) + { + ACL_PCIE_ERROR_IF(1, goto Close, "[acl" ACL_BOARD_PKG_NAME "%d] failed to read MSI interrupt address/data.\n", dev_num); + } + + result = fpgaGetProperties(afcToken, &prop); + if (prop) { + result = fpgaPropertiesGetBus(prop, &bus); + if (result != FPGA_OK) { + ACL_PCIE_ERROR_IF(1, goto Close, "failed to get bus.\n"); + } + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] bus is: %d\n", dev_num, bus); + result = fpgaPropertiesGetDevice(prop, &l_device); + if (result != FPGA_OK) { + ACL_PCIE_ERROR_IF(1, goto Close, "failed to get device.\n"); + } + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] device is: %d\n", dev_num, l_device); + result = fpgaPropertiesGetFunction(prop, &function); + if (result != FPGA_OK) { + ACL_PCIE_ERROR_IF(1, goto Close, "failed to get function.\n"); + } + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] function is: %d\n", dev_num, function); + snprintf(info->pcie_slot_info_str, + PCIE_SLOT_INFO_STR_LEN, + "%u:%u.%u", + bus, l_device, function); + fpgaDestroyProperties(&prop); + } + // Read Link status out of PCI config space + result = fpgaReadPciConfigSpace(device, 0x80, (PVOID)&pci_link_info, sizeof(pci_link_info)); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Link Status is: 0x%lx\n", dev_num, pci_link_info); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Link Speed is: %d\n", + dev_num, + ((pci_link_info >> 16) & 0x0F)); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Negotiated Link Width is: %d\n", + dev_num, + ((pci_link_info >> 20) & 0x3F)); + + // Read Maximum Payload Size out of PCI config space + result = fpgaReadPciConfigSpace(device, 0x78, (PVOID)&pci_link_info, sizeof(pci_link_info)); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size raw data is: 0x%lx\n", dev_num, pci_link_info); + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is: %d\n", dev_num, ((pci_link_info >> 5) & 0x0007)); + switch ((pci_link_info >> 5) & 0x0007) { + case 0: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is 128-byte\n", dev_num); + break; + case 1: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is 256-byte\n", dev_num); + break; + case 2: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is 512-byte\n", dev_num); + break; + case 3: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is 1024-byte\n", dev_num); + break; + case 4: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is 2048-byte\n", dev_num); + break; + default: + ACL_PCIE_DEBUG_MSG(":: [acl" ACL_BOARD_PKG_NAME "%d] PCI Maximum Payload Size is Unknown\n", dev_num); + break; + } + + ++num_open_devices; + goto DestroyTok; + + // Resource cleanup + +Close: + fpgaClose(device); + device = INVALID_HANDLE_VALUE; + +DestroyTok: + + if (afcToken != NULL) fpgaDestroyToken(&afcToken); + +DestroyProp: + + if (filter != NULL) fpgaDestroyProperties(&filter); + +End: + return device; +} +#endif // WINDOWS + +#if defined(LINUX) +fpga_handle open_device_linux(ACL_PCIE_DEVICE_DESCRIPTION *info, int dev_num) { + char buf[128] = {0}; + char expected_ver_string[128] = {0}; + int descriptor; + int oldflags; + int bytes_read; + struct acl_cmd driver_cmd; + + snprintf(buf, sizeof(buf), "/dev/acl" ACL_BOARD_PKG_NAME "%d", dev_num); + ssize_t device = open(buf, O_RDWR); + + // Return INVALID_DEVICE when the device is not available + if (device == -1) { + goto Close; + } + + // Make sure the Linux kernel driver is recent + driver_cmd = {ACLPCI_CMD_BAR, ACLPCI_CMD_GET_DRIVER_VERSION, NULL, buf, 0}; + bytes_read = read(device, &driver_cmd, 0); + ACL_PCIE_ERROR_IF(bytes_read == -1, goto Close, "Failed to read driver command"); + + snprintf( + expected_ver_string, sizeof(expected_ver_string), "%s.%s", ACL_BOARD_PKG_NAME, KERNEL_DRIVER_VERSION_EXPECTED); + ACL_PCIE_ERROR_IF(strstr(buf, expected_ver_string) != buf, + goto Close, + "Kernel driver mismatch: The board kernel driver version is %s, but\nthis host program expects " + "%s.\n Please reinstall the driver using aocl install.\n", + buf, + expected_ver_string); + + // Save the device id for the selected board + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_GET_PCI_DEV_ID; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = &info->device_id; + driver_cmd.size = sizeof(info->device_id); + bytes_read = read(device, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ERROR_IF(bytes_read == -1, goto Close, "Failed to read driver command"); + + // Set the FD_CLOEXEC flag for the file handle to disable the child to + // inherit this file handle. So the jtagd will not hold the file handle + // of the device and keep sending bogus interrupts after we call quartus_pgm. + oldflags = fcntl(device, F_GETFD, 0); + descriptor = fcntl(device, F_SETFD, oldflags | FD_CLOEXEC); + if (descriptor < 0) { + goto Close; + } + + ++num_open_devices; + goto End; + +// I really don't want to use goto but it's for consistency with windows version, and convenience with macros +Close: + if (device >= 0) { + close(device); + } + device = INVALID_HANDLE_VALUE; + +End: + return device; +} + +#endif // LINUX + +// This function can be used for triggering a fake device exception for testing +void ACL_PCIE_DEVICE::test_trigger_device_interrupt() { + // Example: + // Raising ECC NON CORRECTABLE exception (exception code 2) + // Providing integer-type private_info (say, equals to 5) + unsigned long long int exception_type = 2; + int test_private_info = 5; + aocl_mmd_interrupt_info interrupt_data = {exception_type, &test_private_info, sizeof(test_private_info)}; + this->device_interrupt(m_handle, &interrupt_data, this->device_interrupt_user_data); +} + +// Perform operations required when an interrupt is received for this device +void ACL_PCIE_DEVICE::service_interrupt(unsigned int irq_type_flag) { + unsigned int kernel_update = 0; + unsigned int dma_update = 0; + + int status = this->get_interrupt_type(&kernel_update, &dma_update, irq_type_flag); + ACL_PCIE_ERROR_IF(status, return, "[%s] fail to service the interrupt.\n", m_name); + + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_IRQ, + ":: [%s] Irq service routine called, kernel_update=%d, dma_update=%d \n", + m_name, + kernel_update, + dma_update); + + if (kernel_update && kernel_interrupt != NULL) { +#if defined(WINDOWS) + status = this->mask_irqs(); + ACL_PCIE_ERROR_IF(status, return, "[%s] failed to mask kernel interrupt.\n", m_name); +#endif + // A kernel-status interrupt - update the status of running kernels + ACL_PCIE_ASSERT(kernel_interrupt, "[%s] received kernel interrupt before the handler is installed.\n", m_name); + kernel_interrupt(m_handle, kernel_interrupt_user_data); + } else if (dma_update) { + // A DMA-status interrupt - let the DMA object handle this + m_dma->service_interrupt(); + } + + // Unmask the kernel_irq to enable the interrupt again. + if (m_mmd_irq_handler_enable) { + status = this->unmask_irqs(); + } else if (kernel_update) { + status = this->unmask_kernel_irq(); + } + ACL_PCIE_ERROR_IF(status, return, "[%s] fail to service the interrupt.\n", m_name); + + return; +} + +// Enable all interrupts (DMA and Kernel) +// Won't enable kernel irq unless kernel interrupt callback has been initialized +// Return 0 on success +int ACL_PCIE_DEVICE::unmask_irqs() { + int status = 0; + if (kernel_interrupt == NULL) { + // No masking for DMA interrupt. + + } else { + status = m_io->pcie_cra->write32(PCIE_CRA_IRQ_ENABLE, ACL_PCIE_GET_BIT(ACL_PCIE_KERNEL_IRQ_VEC)); + } + ACL_PCIE_ERROR_IF(status, return -1, "[%s] fail to unmask all interrupts.\n", m_name); + + return 0; // success +} + +// Disable all interrupts to service kernel that triggered interrupt +// If other kernels finish while the interrupt is masked, MSI will trigger again when +// interrupts are re-enabled. +int ACL_PCIE_DEVICE::mask_irqs() { + int status = 0; + UINT32 val = 0; + status = m_io->pcie_cra->write32(PCIE_CRA_IRQ_ENABLE, val); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] fail to mask the kernel interrupts.\n", m_name); + + return 0; // success +} + +// Enable the kernel interrupt only +// Return 0 on success +int ACL_PCIE_DEVICE::unmask_kernel_irq() { + int status = 0; + UINT32 val = 0; + + status |= (int)(m_io->pcie_cra->read32(PCIE_CRA_IRQ_ENABLE, &val)); + val |= ACL_PCIE_GET_BIT(ACL_PCIE_KERNEL_IRQ_VEC); + status |= (int)(m_io->pcie_cra->write32(PCIE_CRA_IRQ_ENABLE, val)); + + ACL_PCIE_ERROR_IF(status, return -1, "[%s] fail to unmask the kernel interrupts.\n", m_name); + + return 0; // success +} + +// Disable the interrupt +// Return 0 on success +int ACL_PCIE_DEVICE::disable_interrupts() { + int status; + + if (m_mmd_irq_handler_enable) { + ACL_PCIE_DEBUG_MSG(":: [%s] Disabling interrupts.\n", m_name); + + status = m_io->pcie_cra->write32(PCIE_CRA_IRQ_ENABLE, 0); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to disable pcie interrupt.\n", m_name); + +#if defined(WINDOWS) + // Disable KMD interrupt handling for Windows + fpga_properties prop = {0}; + fpga_result result = FPGA_OK; + uint32_t num_interrupts = 0; + uint32_t i = 0; + + // Get number of interrupts in the device from the properties structure + result = fpgaGetPropertiesFromHandle(m_device, &prop); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "[%s] fpgaGetPropertiesFromHandle Failed\n", m_name); + + result = fpgaPropertiesGetNumInterrupts(prop, &num_interrupts); + if (result != FPGA_OK) { + fpgaDestroyProperties(&prop); + ACL_PCIE_ERROR_IF(1, return -1, "[%s] fpgaPropertiesGetNumInterrupts Failed\n", m_name); + } + + if (dev_event_handle != NULL) { + // Loop through all the interrupts and unregister the event and + // destroy event handle associated with the interrupt + for (i = 0; i < num_interrupts; i++) { + result = fpgaUnregisterEvent(m_device, FPGA_EVENT_INTERRUPT, dev_event_handle[i]); + + if (result != FPGA_OK) { + fpgaDestroyProperties(&prop); + ACL_PCIE_ERROR_IF(1, return -1, "[%s] fpgaRegisterEvent Failed\n", m_name); + } + + result = fpgaDestroyEventHandle(&dev_event_handle[i]); + if (result != FPGA_OK) { + fpgaDestroyProperties(&prop); + ACL_PCIE_ERROR_IF(1, return -1, "[%s] fpgaCreateEventHandle Failed\n", m_name); + } + } + free(dev_event_handle); + dev_event_handle = NULL; + } + fpgaDestroyProperties(&prop); +#endif // WINDOWS + m_mmd_irq_handler_enable = false; + } + + return 0; // success +} + +#if defined(WINDOWS) + +// Enable PCI express interrupts. Set up the KMD to mask the interrupt enable bit when +// an interrupt is received to prevent the level-sensitive interrupt from immediately +// firing again. +// Return 0 on success +int ACL_PCIE_DEVICE::enable_interrupts(int user_signal_number) { + int status; + fpga_properties prop = NULL; + fpga_result result = FPGA_OK; + uint32_t num_interrupts = 0; + uint32_t i = 0; + HANDLE deviceStopWaitObj = NULL; + BOOLEAN flag; + int ret_value = 0; // return 0 on success + + ACL_PCIE_DEBUG_MSG(":: [%s] Enabling PCIe interrupts.\n", m_name); + + // Mask off hardware interrupts before enabling them + status = m_io->pcie_cra->write32(PCIE_CRA_IRQ_ENABLE, 0); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to mask off all interrupts before enabling them.\n", m_name); + + // Enable interrupts in the KMD + + // Get number of interrupts in the device from the properties structure + result = fpgaGetPropertiesFromHandle(m_device, &prop); + ACL_PCIE_ERROR_IF(result != FPGA_OK, return -1, "[%s] fpgaGetPropertiesFromHandle Failed\n", m_name); + + result = fpgaPropertiesGetNumInterrupts(prop, &num_interrupts); + if (result != FPGA_OK) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] fpgaPropertiesGetNumInterrupts Failed\n", m_name); + } + + dev_event_handle = NULL; + dev_event_handle = (fpga_event_handle *)malloc(sizeof(fpga_event_handle) * num_interrupts); + if (dev_event_handle == NULL) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] malloc for event handle array Failed\n", m_name); + } + + // Loop through all the interrupts and register an event and + // create event handle associated with the interrupt + + for (i = 0; i < num_interrupts; i++) { + result = fpgaCreateEventHandle(&dev_event_handle[i]); + if (result != FPGA_OK) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] fpgaCreateEventHandle Failed\n", m_name); + } + + result = fpgaRegisterEvent(m_device, FPGA_EVENT_INTERRUPT, dev_event_handle[i], i); + if (result != FPGA_OK) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] fpgaRegisterEvent Failed\n", m_name); + } + + // Register the user-mode interrupt handler + // Executed after interrupt is recieved and processed in kernel + flag = (BOOLEAN)RegisterWaitForSingleObject(&deviceStopWaitObj, + dev_event_handle[i], + (WAITORTIMERCALLBACK)pcie_interrupt_handler, + static_cast<void *>(this), + INFINITE, + WT_EXECUTEINWAITTHREAD); + + if (flag == 0) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] fpgaRegisterEvent Failed\n", m_name); + } + } + status = this->unmask_irqs(); + if (status) { + ret_value = -1; + ACL_PCIE_ERROR_IF(1, goto End, "[%s] failed to enable interrupts.\n", m_name); + } + + m_mmd_irq_handler_enable = true; + + // Resource cleanup +End: + fpgaDestroyProperties(&prop); + return ret_value; +} + +// Use irq status to determine type of interrupt +// Result is returned in kernel_update/dma_update arguments. +// Return 0 on success +int ACL_PCIE_DEVICE::get_interrupt_type(unsigned int *kernel_update, + unsigned int *dma_update, + unsigned int irq_type_flag) { + UINT32 irq_status; + unsigned int dma_status; + int status; + + status = m_io->pcie_cra->read32(PCIE_CRA_IRQ_STATUS, &irq_status); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] fail to interrupt type.\n", m_name); + + *kernel_update = ACL_PCIE_READ_BIT(irq_status, ACL_PCIE_KERNEL_IRQ_VEC); + + status = m_dma->check_dma_interrupt(&dma_status); + if (status != 1) { + *dma_update = dma_status; + } + + return 0; // success +} + +#endif // WINDOWS +#if defined(LINUX) + +// For Linux, it will set-up a signal handler for signals for kernel driver +// Return 0 on success +int ACL_PCIE_DEVICE::enable_interrupts(int user_signal_number) { + int status; + ACL_PCIE_DEBUG_MSG(":: [%s] Enabling PCIe interrupts on Linux (via signals).\n", m_name); + + // All interrupt controls are in the kernel driver. + m_mmd_irq_handler_enable = false; + + // Send the globally allocated signal number to the driver + struct acl_cmd signal_number_cmd {}; + signal_number_cmd.bar_id = ACLPCI_CMD_BAR; + signal_number_cmd.command = ACLPCI_CMD_SET_SIGNAL_NUMBER; + signal_number_cmd.device_addr = NULL; + signal_number_cmd.user_addr = &user_signal_number; + signal_number_cmd.size = sizeof(user_signal_number); + status = write(m_device, &signal_number_cmd, sizeof(signal_number_cmd)); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to set signal number for interrupts.\n", m_name); + + // Sanity check, did the driver get it + int readback_signal_number; + signal_number_cmd.user_addr = &readback_signal_number; + signal_number_cmd.command = ACLPCI_CMD_GET_SIGNAL_NUMBER; + signal_number_cmd.size = sizeof(readback_signal_number); + status = read(m_device, &signal_number_cmd, sizeof(signal_number_cmd)); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to get signal number for interrupts.\n", m_name); + ACL_PCIE_ERROR_IF(readback_signal_number != user_signal_number, + return -1, + "[%s] got wrong signal number %d, expected %d\n", + m_name, + readback_signal_number, + user_signal_number); + + // Set "our" device id (the handle id received from acl_pcie.cpp) to correspond to + // the device managed by the driver. Will get back this id + // with signal from the driver. Will allow us to differentiate + // the source of kernel-done signals with multiple boards. + + // the last bit is reserved as a flag for DMA completion + int result = m_handle << 1; + struct acl_cmd read_cmd = {ACLPCI_CMD_BAR, ACLPCI_CMD_SET_SIGNAL_PAYLOAD, NULL, &result}; + status = write(m_device, &read_cmd, sizeof(result)); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to enable interrupts.\n", m_name); + + return 0; // success +} + +// Determine the interrupt type using the irq_type_flag +// Return 0 on success +int ACL_PCIE_DEVICE::get_interrupt_type(unsigned int *kernel_update, + unsigned int *dma_update, + unsigned int irq_type_flag) { + // For Linux, the interrupt type is mutually exclusive + *kernel_update = irq_type_flag ? 0 : 1; + *dma_update = 1 - *kernel_update; + + return 0; // success +} + +#endif // LINUX + +// Called by the host program when there are spare cycles +int ACL_PCIE_DEVICE::yield() { + // Give the DMA object a chance to crunch any pending data + return m_dma->yield(); +} + +// Set kernel interrupt and event update callbacks +// return 0 on success +int ACL_PCIE_DEVICE::set_kernel_interrupt(aocl_mmd_interrupt_handler_fn fn, void *user_data) { + int status; + + kernel_interrupt = fn; + kernel_interrupt_user_data = user_data; + + if (m_device != INVALID_HANDLE_VALUE) { + status = this->unmask_kernel_irq(); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to set kernel interrupt callback funciton.\n", m_name); + } + + return 0; // success +} + +int ACL_PCIE_DEVICE::set_device_interrupt(aocl_mmd_device_interrupt_handler_fn fn, void *user_data) { + int status; + + device_interrupt = fn; + device_interrupt_user_data = user_data; + + if (m_device != INVALID_HANDLE_VALUE) { + status = this->unmask_kernel_irq(); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to set device interrupt callback funciton.\n", m_name); + } + + return 0; // success +} + +int ACL_PCIE_DEVICE::set_status_handler(aocl_mmd_status_handler_fn fn, void *user_data) { + event_update = fn; + event_update_user_data = user_data; + + return 0; // success +} + +// The callback function set by "set_status_handler" +// It's used to notify/update the host whenever an event is finished +void ACL_PCIE_DEVICE::event_update_fn(aocl_mmd_op_t op, int status) { + ACL_PCIE_ASSERT(event_update, "[%s] event_update is called with a empty update function pointer.\n", m_name); + + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_OP, ":: [%s] Update for event e=%p.\n", m_name, op); + event_update(m_handle, event_update_user_data, op, status); +} + +// Forward get buffer call to host channel +void *ACL_PCIE_DEVICE::hostchannel_get_buffer(size_t *buffer_size, int channel, int *status) { + return m_hostch->get_buffer(buffer_size, channel, status); +} +// Forward ack call to host channel +size_t ACL_PCIE_DEVICE::hostchannel_ack_buffer(size_t send_size, int channel, int *status) { + return m_hostch->ack_buffer(send_size, channel, status); +} + +// Memory I/O +// return 0 on success +int ACL_PCIE_DEVICE::write_block( + aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, void *host_addr, size_t dev_addr, size_t size) { +#ifdef DLA_MMD + ACL_PCIE_ASSERT(e == nullptr, "DLA_MMD does not support callback events in ACL_PCIE_DEVICE::write_block"); +#else + ACL_PCIE_ASSERT(event_update, "[%s] event_update callback function is not provided.\n", m_name); +#endif + int status = -1; // assume failure + + switch (mmd_interface) { + case AOCL_MMD_KERNEL: + status = m_io->kernel_if->write_block(dev_addr, size, host_addr); + break; + case AOCL_MMD_MEMORY: + status = read_write_block(e, host_addr, dev_addr, size, false /*writing*/); + break; + case AOCL_MMD_PLL: + status = m_io->pll->write_block(dev_addr, size, host_addr); + break; + case AOCL_MMD_HOSTCH: + default: + ACL_PCIE_ASSERT(0, "[%s] unknown MMD interface.\n", m_name); + } + + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to write block.\n", m_name); + + return 0; // success +} + +int ACL_PCIE_DEVICE::read_block( + aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, void *host_addr, size_t dev_addr, size_t size) { +#ifdef DLA_MMD + ACL_PCIE_ASSERT(e == nullptr, "DLA_MMD does not support callback events in ACL_PCIE_DEVICE::read_block"); +#else + ACL_PCIE_ASSERT(event_update, "[%s] event_update callback function is not provided.\n", m_name); +#endif + int status = -1; // assume failure + + switch (mmd_interface) { + case AOCL_MMD_KERNEL: + status = m_io->kernel_if->read_block(dev_addr, size, host_addr); + break; + case AOCL_MMD_MEMORY: + status = read_write_block(e, host_addr, dev_addr, size, true /*reading*/); + break; + case AOCL_MMD_PLL: + status = m_io->pll->read_block(dev_addr, size, host_addr); + break; + case AOCL_MMD_HOSTCH: + default: + ACL_PCIE_ASSERT(0, "[%s] unknown MMD interface.\n", m_name); + } + + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to read block.\n", m_name); + + return 0; // success +} + +// Copy a block between two locations in device memory +// return 0 on success +int ACL_PCIE_DEVICE::copy_block( + aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, size_t src, size_t dst, size_t size) { + ACL_PCIE_ASSERT(event_update, "[%s] event_update callback function is not provided.\n", m_name); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_OP, + ":: [%s] Copying " SIZE_FMT_U " bytes data from 0x" SIZE_FMT_X " (device) to 0x" SIZE_FMT_X + " (device), with e=%p\n", + m_name, + size, + src, + dst, + e); + +#define BLOCK_SIZE (8 * 1024 * 1024) +#if defined(WINDOWS) + __declspec(align(128)) static unsigned char data[BLOCK_SIZE]; +#endif // WINDOWS +#if defined(LINUX) + static unsigned char data[BLOCK_SIZE] __attribute__((aligned(128))); +#endif // LINUX + + do { + size_t transfer_size = (size > BLOCK_SIZE) ? BLOCK_SIZE : size; + read_block(NULL /* blocking read */, mmd_interface, data, src, transfer_size); + write_block(NULL /* blocking write */, mmd_interface, data, dst, transfer_size); + + src += transfer_size; + dst += transfer_size; + size -= transfer_size; + } while (size > 0); + + if (e) { + this->event_update_fn(e, 0); + } + + return 0; // success +} + +// Forward create hostchannel call to host channel +int ACL_PCIE_DEVICE::create_hostchannel(char *name, size_t queue_depth, int direction) { + return m_hostch->create_hostchannel(name, queue_depth, direction); +} + +// Forward destroy hostchannel call to host channel +int ACL_PCIE_DEVICE::destroy_channel(int channel) { return m_hostch->destroy_hostchannel(channel); } + +// Read or Write a block of data to device memory. +// Use either DMA or directly read/write through BAR +// Return 0 on success +int ACL_PCIE_DEVICE::read_write_block(aocl_mmd_op_t e, void *host_addr, size_t dev_addr, size_t size, bool reading) { + const uintptr_t uintptr_host = reinterpret_cast<uintptr_t>(host_addr); + + int status = 0; + size_t dma_size = 0; + +#ifdef DLA_MMD + // CoreDLA runtime assumes host/device transfers are thread safe, enforce that here + // mutex will unlock when its lock goes out of scope + std::unique_lock<std::mutex> dma_mutex_lock(m_dma_mutex); +#endif + + if (reading) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_OP, + ":: [%s] Reading " SIZE_FMT_U " bytes data from 0x" SIZE_FMT_X + " (device) to %p (host), with e=%p\n", + m_name, + size, + dev_addr, + host_addr, + e); + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_OP, + ":: [%s] Writing " SIZE_FMT_U " bytes data from %p (host) to 0x" SIZE_FMT_X + " (device), with e=%p\n", + m_name, + size, + host_addr, + dev_addr, + e); + } + + // Return immediately if size is zero + if (size == 0) { + if (e) { + this->event_update_fn(e, 0); + } + return 0; + } + + bool aligned = ((uintptr_host & DMA_ALIGNMENT_BYTE_MASK) | (dev_addr & DMA_ALIGNMENT_BYTE_MASK)) == 0; + if (m_use_dma_for_big_transfers && aligned && (size >= 1024)) { + // DMA transfers must END at aligned boundary. + // If that's not the case, use DMA up to such boundary, and regular + // read/write for the remaining part. + dma_size = size - (size & DMA_ALIGNMENT_BYTE_MASK); + } else if (m_use_dma_for_big_transfers && (size >= 1024)) { + ACL_PCIE_WARN_MSG("[%s] NOT using DMA to transfer " SIZE_FMT_U + " bytes from %s to %s because of lack of alignment\n" + "** host ptr (%p) and/or dev offset (0x" SIZE_FMT_X + ") is not aligned to %u bytes\n", + m_name, + size, + (reading ? "device" : "host"), + (reading ? "host" : "device"), + host_addr, + dev_addr, + DMA_ALIGNMENT_BYTES); + } + + // Perform read/write through BAR if the data is not fit for DMA or if there is remaining part from DMA + if (dma_size < size) { + void *host_addr_new = reinterpret_cast<void *>(uintptr_host + dma_size); + size_t dev_addr_new = dev_addr + dma_size; + size_t remain_size = size - dma_size; + + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_OP, + ":: [%s] Perform read/write through BAR for remaining " SIZE_FMT_U + " bytes (out of " SIZE_FMT_U " bytes)\n", + m_name, + remain_size, + size); + + status = read_write_block_bar(host_addr_new, dev_addr_new, remain_size, reading); + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to perform read/write through BAR.\n", m_name); + } + + if (dma_size != 0) { + m_dma->read_write(host_addr, dev_addr, dma_size, e, reading); + + // Block if event is NULL + if (e == NULL) { + m_dma->stall_until_idle(); + } + } else { + if (e != NULL) { + this->event_update_fn(e, 0); + } + } + + return 0; // success +} + +// Read or Write a block of data to device memory through BAR +// Return 0 on success +int ACL_PCIE_DEVICE::read_write_block_bar(void *host_addr, size_t dev_addr, size_t size, bool reading) { + void *cur_host_addr = host_addr; + size_t cur_dev_addr = dev_addr; + size_t bytes_transfered = 0; + + for (bytes_transfered = 0; bytes_transfered < size;) { + // decide the size to transfer for current iteration + size_t cur_size = ACL_PCIE_MEMWINDOW_SIZE - (cur_dev_addr % ACL_PCIE_MEMWINDOW_SIZE); + if (bytes_transfered + cur_size >= size) { + cur_size = size - bytes_transfered; + } + + // set the proper window segment + set_segment(cur_dev_addr); + size_t window_rel_ptr_start = cur_dev_addr % ACL_PCIE_MEMWINDOW_SIZE; + size_t window_rel_ptr = window_rel_ptr_start; + + // A simple blocking read + // The address should be in the global memory range, we assume + // any offsets are already accounted for in the offset + ACL_PCIE_ASSERT(window_rel_ptr + cur_size <= ACL_PCIE_MEMWINDOW_SIZE, + "[%s] trying to access out of the range of the memory window.\n", + m_name); + + // Workaround a bug in Jungo driver. + // First, transfer the non 8 bytes data at the front, one byte at a time + // Then, transfer multiple of 8 bytes (size of size_t) using read/write_block + // At the end, transfer the remaining bytes, one byte at a time + size_t dev_odd_start = std::min(sizeof(size_t) - window_rel_ptr % sizeof(size_t), cur_size); + if (dev_odd_start != sizeof(size_t)) { + read_write_small_size(cur_host_addr, window_rel_ptr, dev_odd_start, reading); + incr_ptrs(&cur_host_addr, &window_rel_ptr, &bytes_transfered, dev_odd_start); + cur_size -= dev_odd_start; + } + + size_t tail_size = cur_size % sizeof(size_t); + size_t size_mul_8 = cur_size - tail_size; + + if (size_mul_8 != 0) { + if (reading) { + m_io->mem->read_block(window_rel_ptr, size_mul_8, cur_host_addr); + } else { + m_io->mem->write_block(window_rel_ptr, size_mul_8, cur_host_addr); + } + incr_ptrs(&cur_host_addr, &window_rel_ptr, &bytes_transfered, size_mul_8); + } + + if (tail_size != 0) { + read_write_small_size(cur_host_addr, window_rel_ptr, tail_size, reading); + incr_ptrs(&cur_host_addr, &window_rel_ptr, &bytes_transfered, tail_size); + cur_size -= tail_size; + } + + // increase the current device address to be transferred + cur_dev_addr += (window_rel_ptr - window_rel_ptr_start); + } + + return 0; // success +} + +// Read or Write a small size of data to device memory, one byte at a time +// Return 0 on success +int ACL_PCIE_DEVICE::read_write_small_size(void *host_addr, size_t dev_addr, size_t size, bool reading) { + UINT8 *ucharptr_host = static_cast<UINT8 *>(host_addr); + int status; + + for (size_t i = 0; i < size; ++i) { + if (reading) { + status = m_io->mem->read8(dev_addr + i, ucharptr_host + i); + } else { + status = m_io->mem->write8(dev_addr + i, ucharptr_host[i]); + } + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to read write with odd size.\n", m_name); + } + + return 0; // success +} + +// Set the segment that the memory windows is accessing to +// Return 0 on success +int ACL_PCIE_DEVICE::set_segment(size_t addr) { + UINT64 segment_readback; + UINT64 cur_segment = addr & ~(ACL_PCIE_MEMWINDOW_SIZE - 1); + int status = 0; + + // Only execute the PCI write if we need to *change* segments + if (cur_segment != m_segment) { + // PCIe reordering rules could cause the segment change to get reordered, + // so read before and after! + status |= (int)(m_io->window->read64(0, &segment_readback)); + + status |= (int)(m_io->window->write64(0, cur_segment)); + m_segment = cur_segment; + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::::: [%s] Changed segment id to %llu.\n", m_name, m_segment); + + status |= (int)(m_io->window->read64(0, &segment_readback)); + } + + ACL_PCIE_ERROR_IF(status, return -1, "[%s] failed to set segment for memory access windows.\n", m_name); + + return 0; // success +} + +void ACL_PCIE_DEVICE::incr_ptrs(void **host, size_t *dev, size_t *counter, size_t incr) { + const uintptr_t uintptr_host = reinterpret_cast<uintptr_t>(*host); + + *host = reinterpret_cast<void *>(uintptr_host + incr); + *dev += incr; + *counter += incr; +} + +// Query the on-chip temperature sensor +bool ACL_PCIE_DEVICE::get_ondie_temp_slow_call(cl_int *temp) { + cl_int read_data; + + // We assume this during read later + ACL_PCIE_ASSERT(sizeof(cl_int) == sizeof(INT32), "sizeof(cl_int) != sizeof(INT32)"); + +#ifndef ACL_PCIE_HAS_TEMP_SENSOR + ACL_PCIE_DEBUG_MSG(":: [%s] On-chip temperature sensor not supported by this board.\n", m_name); + return false; +#endif + + ACL_PCIE_DEBUG_MSG(":: [%s] Querying on-chip temperature sensor...\n", m_name); + + // read temperature sensor + m_io->temp_sensor->read32(0, (UINT32 *)&read_data); + + ACL_PCIE_DEBUG_MSG(":: [%s] Read temp sensor data. Value is: %i\n", m_name, read_data); + *temp = read_data; + return true; +} + +void *ACL_PCIE_DEVICE::shared_mem_alloc(size_t size, unsigned long long *device_ptr_out) { +#if defined(WINDOWS) + return NULL; +#endif // WINDOWS +#if defined(LINUX) +#ifdef ACL_HOST_MEMORY_SHARED + void *host_ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, m_device, 0); + + if (device_ptr_out != NULL && host_ptr == (void *)-1) { + // when mmap fails, it returns (void*)-1, not NULL + host_ptr = NULL; + *device_ptr_out = (unsigned long long)0; + + } else if (device_ptr_out != NULL) { + /* map received host_ptr to FPGA-usable address. */ + void *dev_ptr = NULL; + struct acl_cmd read_cmd = {ACLPCI_CMD_BAR, ACLPCI_CMD_GET_PHYS_PTR_FROM_VIRT, &dev_ptr, &host_ptr, sizeof(dev_ptr)}; + + bool failed_flag = (read(m_device, &read_cmd, sizeof(dev_ptr)) != 0); + ACL_PCIE_DEBUG_MSG( + " Mapped vaddr %p to phys addr %p. %s\n", host_ptr, dev_ptr, failed_flag == 0 ? "OK" : "FAILED"); + if (failed_flag) { + *device_ptr_out = (unsigned long long)NULL; + } else { + /* When change to 64-bit pointers on the device, update driver code + * to deal with larger-than-void* ptrs. */ + *device_ptr_out = (unsigned long long)dev_ptr; + + /* Now need to add offset of the shared system. */ + } + } + + return host_ptr; +#else + return NULL; +#endif +#endif // LINUX +} + +void ACL_PCIE_DEVICE::shared_mem_free(void *vptr, size_t size) { +#if defined(WINDOWS) + return; +#endif // WINDOWS +#if defined(LINUX) + if (vptr != NULL) { + munmap(vptr, size); + } +#endif // LINUX +} + +#ifdef DLA_MMD + +int ACL_PCIE_DEVICE::pause_and_save_pcie() +{ + int failed_cont_reg_save; + + // set the being_programmed flag + m_being_programmed = true; + + // disable interrupt and save control registers + const int failed_int_disable = this->disable_interrupts(); + ACL_PCIE_ERROR_IF(failed_int_disable, goto cleanup_save, "could not disable interrupt.\n"); + + // Do this last before programming + failed_cont_reg_save = m_config->save_pci_control_regs(); + ACL_PCIE_ERROR_IF(failed_cont_reg_save, goto cleanup_save, "could not save control regs\n"); + + return 0; + + cleanup_save: + + m_being_programmed = false; + return 1; +} + +int ACL_PCIE_DEVICE::restore_and_resume_pcie() +{ +#if defined(LINUX) + m_config->load_pci_control_regs(); +#endif + + if (wait_for_uniphy()) { + ACL_PCIE_DEBUG_MSG(":: [%s] Uniphy failed to calibrate.\n", m_name); + + m_being_programmed = false; + + return 1; + } + + m_being_programmed = false; + return 0; +} + +// JTAG full-chip programming (using quartus_pgm via USB-Blaster) to replace periphery + core +// Return 0 on success +int ACL_PCIE_DEVICE::reprogram_sof(const char *sof_filename, const bool skipSaveRestore) { + int saveRetCode = 0; + + if (!skipSaveRestore) + { + saveRetCode = pause_and_save_pcie(); + if (saveRetCode) + { + return saveRetCode; + } + } + + int reprogram_failed = 1; // assume failure + + // JTAG programming the device + ACL_PCIE_DEBUG_MSG(":: [%s] Starting JTAG programming of the device...\n", m_name); + reprogram_failed = m_config->program_with_SOF_file(sof_filename, "0" /*ad_cable*/, "0" /*ad_device_index*/); + + int restoreRetCode = 0; + + if (!skipSaveRestore) + { + restoreRetCode = restore_and_resume_pcie(); + if (restoreRetCode) + { + return restoreRetCode; + } + } + + if (!(reprogram_failed)) { + ACL_PCIE_DEBUG_MSG(":: [%s] JTAG programming passed.\n", m_name); + } + + return reprogram_failed; +} +#else +// perform PR reprogram by attempting to program the board using an RBF. If this is not possible due to +// 1) Envoking the user of JTAG_PROGRAMMING via ACL_PCIE_USE_JTAG_PROGRAMMING +// 2) RBF or HASH are not present +// 3) PR Base ID does not match that with which the RBF was compiled +// 4) UniPhy fails to calibrate +// Then returns 1. Returns 0 on success. Always returns flag from arguments indicating source of failure +int ACL_PCIE_DEVICE::pr_reprogram(struct acl_pkg_file *pkg, + const char *SOFNAME, + int *rbf_or_hash_not_provided, + int *hash_mismatch, + unsigned *use_jtag_programming, + int *quartus_compile_version_mismatch) { + // Environment variable to control when to use JTAG instead of PR (overriding the default programming method: PR) + int reprogram_failed = 1; + size_t core_rbf_len = 0, pr_import_version_len = 0, quartus_version_len = 0, pll_config_len = 0; + *use_jtag_programming = 0; + char *str_use_jtag_programming = getenv("ACL_PCIE_USE_JTAG_PROGRAMMING"); + if (str_use_jtag_programming) *use_jtag_programming = 1; + + // 1. Default programming method: PR + if (!*use_jtag_programming) { + // checking that rbf and hash sections exist in fpga.bin + if (acl_pkg_section_exists(pkg, ACL_PKG_SECTION_CORE_RBF, &core_rbf_len) && + acl_pkg_section_exists(pkg, ACL_PKG_SECTION_HASH, &pr_import_version_len) && + (acl_pkg_section_exists(pkg, ACL_PKG_SECTION_QVERSION, &quartus_version_len) || m_skip_quartus_version_check)) { + *rbf_or_hash_not_provided = 0; + ACL_PCIE_DEBUG_MSG( + ":: [%s] Programming kernel region using PR with rbf file size %i\n", m_name, (UINT32)core_rbf_len); + + // read rbf and hash from fpga.bin + char *core_rbf; + acl_aligned_malloc((void **)&core_rbf, core_rbf_len + 1); + int read_core_rbf_ok = acl_pkg_read_section(pkg, ACL_PKG_SECTION_CORE_RBF, core_rbf, core_rbf_len + 1); + + if (!m_skip_quartus_version_check) { + char *quartus_compile_version_str = (char *)malloc(quartus_version_len + 1); + if (quartus_compile_version_str) { + int quartus_compile_version_ok = + acl_pkg_read_section(pkg, ACL_PKG_SECTION_QVERSION, quartus_compile_version_str, quartus_version_len + 1); + + if (quartus_compile_version_ok) { + // Remove Linux and Windows new-line ending in .acl.qversion + if ((quartus_version_len > 0) && (quartus_compile_version_str[quartus_version_len - 1] == '\n' || + quartus_compile_version_str[quartus_version_len - 1] == '\r')) { + quartus_compile_version_str[quartus_version_len - 1] = '\0'; + } + if ((quartus_version_len > 1) && (quartus_compile_version_str[quartus_version_len - 2] == '\r')) { + quartus_compile_version_str[quartus_version_len - 2] = '\0'; + } + + *quartus_compile_version_mismatch = quartus_ver_test(quartus_compile_version_str); + } else { + *quartus_compile_version_mismatch = 1; + } + free(quartus_compile_version_str); + quartus_compile_version_str = NULL; + } else { + *quartus_compile_version_mismatch = 1; + } + } else { + *quartus_compile_version_mismatch = 0; + } + + if (*quartus_compile_version_mismatch == 0) { + char *pr_import_version_str = (char *)malloc(pr_import_version_len + 1); + if (pr_import_version_str) { + int pr_import_version_ok = + acl_pkg_read_section(pkg, ACL_PKG_SECTION_HASH, pr_import_version_str, pr_import_version_len + 1); + + // checking that hash was successfully read from section .acl.hash within fpga.bin + if (pr_import_version_ok) { + unsigned int pr_import_version = (unsigned int)strtol(pr_import_version_str, NULL, 10); + + // checking that base revision hash matches import revision hash and aocx and programmed sof is from same + // Quartus version + if (pr_base_id_test(pr_import_version) == 0) { + *hash_mismatch = 0; + + // Kernel driver wants it aligned to 4 bytes. + int aligned_to_4_bytes(0 == (3 & (uintptr_t)(core_rbf))); + reprogram_failed = 1; // Default to fail before PRing + + // checking that rbf was successfully read from section .acl.core.rbf within fpga.bin + if (read_core_rbf_ok && !(core_rbf_len % 4) && aligned_to_4_bytes && !version_id_test()) { + // reprogram Arria 10 devices + if (strcmp(ACL_BSP_TYPE, "Arria10") == 0) { + ACL_PCIE_DEBUG_MSG(":: [%s] Starting PR programming of the device...\n", m_name); + reprogram_failed = m_config->program_core_with_PR_file_a10((char *)core_rbf, core_rbf_len); + ACL_PCIE_DEBUG_MSG(":: [%s] Finished PR programming of the device.\n", m_name); + }; + + // reprogram Stratix 10 devices + if (strcmp(ACL_BSP_TYPE, "Stratix10") == 0) { + acl_pkg_section_exists(pkg, ACL_PKG_SECTION_PLL_CONFIG, &pll_config_len); + char *pll_config_str = (char *)malloc(pll_config_len + 1); + if (pll_config_str) { + int pll_config_ok = + acl_pkg_read_section(pkg, ACL_PKG_SECTION_PLL_CONFIG, pll_config_str, pll_config_len + 1); + if (pll_config_ok) { + ACL_PCIE_DEBUG_MSG(":: [%s] Starting PR programming of the device...\n", m_name); + reprogram_failed = m_config->program_core_with_PR_file_s10( + (char *)core_rbf, core_rbf_len, (char *)pll_config_str); + ACL_PCIE_DEBUG_MSG(":: [%s] Finished PR programming of the device.\n", m_name); + }; + }; + free(pll_config_str); + pll_config_str = NULL; + }; + + if (reprogram_failed) { + ACL_PCIE_DEBUG_MSG(":: [%s] PR programming failed.\n", m_name); + // PR failed. Check if device I/O is blocked. + if (check_kernel_region_status() == -1) { + ACL_PCIE_INFO("[%s] Partial Reconfiguration of FPGA has failed.\n", m_name); + ACL_PCIE_INFO("[%s] FPGA device will not be available until host has been powercycled.\n", m_name); + exit(1); + } + } else if (version_id_test()) { + ACL_PCIE_DEBUG_MSG(":: [%s] version_id_test() failed.\n", m_name); + reprogram_failed = 1; + } else if (wait_for_uniphy()) { + ACL_PCIE_DEBUG_MSG(":: [%s] Uniphy failed to calibrate.\n", m_name); + reprogram_failed = 1; + } else { + ACL_PCIE_DEBUG_MSG(":: [%s] PR programming passed.\n", m_name); + } + } + } + } + free(pr_import_version_str); + pr_import_version_str = NULL; + } + } + acl_aligned_free(core_rbf); + } + } + + return reprogram_failed; +} + +// Reprogram the device with given binary file. +// There are two ways to program: +// 1. PR to replace the OpenCL kernel partition +// 2. JTAG full-chip programming (using quartus_pgm via USB-Blaster) to replace periphery + core +// Return 0 on success +int ACL_PCIE_DEVICE::reprogram(void *data, size_t data_size, int program_mode) { + int reprogram_failed = 1; // assume failure + int rbf_or_hash_not_provided = 1; // assume no rbf or hash are provided in fpga.bin + int hash_mismatch = 1; // assume base revision and import revision hashes do not match + unsigned use_jtag_programming = 0; // assume no need for jtag programming + int quartus_compile_version_mismatch = 1; + size_t quartus_version_len; + + const char *SOFNAME = "reprogram_temp.sof"; + size_t sof_len = 0; + + ACL_PCIE_DEBUG_MSG(":: [%s] Starting to program device...\n", m_name); + + struct acl_pkg_file *pkg = acl_pkg_open_file_from_memory((char *)data, data_size, ACL_PKG_SHOW_ERROR); + ACL_PCIE_ERROR_IF(pkg == NULL, return reprogram_failed, "cannot open file from memory using pkg editor.\n"); + + // set the being_programmed flag + m_being_programmed = true; + + // the new reprogram flow: first try PR, if failed falls back to the old reprogram flow + int try_pr_failed = 0; + // if choose to try reprogram with preserving memory + if (program_mode == ACL_PCIE_PROGRAM_PR) { + // only try PR, no fall back to JTAG + ACL_PCIE_DEBUG_MSG("[%s] Trying Partial Reconfiguration\n", m_name); + reprogram_failed = pr_reprogram(pkg, + SOFNAME, + &rbf_or_hash_not_provided, + &hash_mismatch, + &use_jtag_programming, + &quartus_compile_version_mismatch); + // clean up + if (reprogram_failed || use_jtag_programming || rbf_or_hash_not_provided || hash_mismatch || + (quartus_compile_version_mismatch && !m_skip_quartus_version_check)) { + // try PR failed + try_pr_failed = 1; + } + if (pkg) acl_pkg_close_file(pkg); + m_being_programmed = false; + return try_pr_failed; + } + + // the old reprogram flow. Try PR and then Try JTAG + // 1. Default to PR reprogramming + ACL_PCIE_DEBUG_MSG("[%s] Reprogram the device with data saving and restoring\n", m_name); + ACL_PCIE_DEBUG_MSG("[%s] Trying Partial Reconfiguration\n", m_name); + reprogram_failed = pr_reprogram(pkg, + SOFNAME, + &rbf_or_hash_not_provided, + &hash_mismatch, + &use_jtag_programming, + &quartus_compile_version_mismatch); + + // Autodetect JTAG cable & device index + // Cable and Index value should't overflow + char ad_cable[AD_CABLE_SIZE]; + char ad_device_index[AD_CABLE_SIZE]; + + // 2. Fallback programming method: JTAG full-chip programming + if (use_jtag_programming || rbf_or_hash_not_provided || hash_mismatch || + (quartus_compile_version_mismatch && !m_skip_quartus_version_check)) { + ACL_PCIE_DEBUG_MSG("[%s] Trying Full-Chip Reconfiguration (JTAG)\n", m_name); + + // checking that sof section exist in fpga.bin + if (acl_pkg_section_exists(pkg, ACL_PKG_SECTION_SOF, &sof_len)) { + // check if aocx is fast-compiled or not - if so, then sof is a base revision, + // and does not necessarily contain the desired kernel. Requires sof with + // matching pr_base.id to be programmed (base.sof) followed by PR programming + // with the given .rbf + size_t fast_compile_len = 0; + char *fast_compile_contents = NULL; + int fast_compile = 0; + if (acl_pkg_section_exists(pkg, ACL_PKG_SECTION_FAST_COMPILE, &fast_compile_len) && + acl_pkg_read_section_transient(pkg, ACL_PKG_SECTION_FAST_COMPILE, &fast_compile_contents)) { + fast_compile = 1; + ACL_PCIE_DEBUG_MSG(":: [%s] Fast-compile fpga.bin detected.\n", m_name); + } + // Find jtag cable for the board + // Returns 0 for both ad_cable,ad_device_index if not found + // or if Autodetect is disabled + this->find_jtag_cable(ad_cable, ad_device_index); + + // write out a SOF file + const int wrote_sof = acl_pkg_read_section_into_file(pkg, ACL_PKG_SECTION_SOF, SOFNAME); + ACL_PCIE_ERROR_IF(!wrote_sof, goto cleanup, "could not write %s.\n", SOFNAME); + + // disable interrupt and save control registers + const int failed_int_disable = this->disable_interrupts(); + ACL_PCIE_ERROR_IF(failed_int_disable, goto cleanup, "could not disable interrupt.\n"); + + // Do this last before programming + const int failed_cont_reg_save = m_config->save_pci_control_regs(); + ACL_PCIE_ERROR_IF(failed_cont_reg_save, goto cleanup, "could not save control regs\n"); + + // JTAG programming the device + ACL_PCIE_DEBUG_MSG(":: [%s] Starting JTAG programming of the device...\n", m_name); + reprogram_failed = m_config->program_with_SOF_file(SOFNAME, ad_cable, ad_device_index); + +#if defined(LINUX) + m_config->load_pci_control_regs(); +#endif + + ACL_PCIE_ERROR_IF(reprogram_failed, goto cleanup, "Failed to JTAG program\n"); + + if (!m_skip_quartus_version_check && + acl_pkg_section_exists(pkg, ACL_PKG_SECTION_QVERSION, &quartus_version_len)) { + char *quartus_compile_version_str = (char *)malloc(quartus_version_len + 1); + if (quartus_compile_version_str) { + int quartus_compile_version_ok = + acl_pkg_read_section(pkg, ACL_PKG_SECTION_QVERSION, quartus_compile_version_str, quartus_version_len + 1); + if (quartus_compile_version_ok) { + // Remove Linux and Windows new-line ending in .acl.qversion + if ((quartus_version_len > 0) && (quartus_compile_version_str[quartus_version_len - 1] == '\n' || + quartus_compile_version_str[quartus_version_len - 1] == '\r')) { + quartus_compile_version_str[quartus_version_len - 1] = '\0'; + } + if ((quartus_version_len > 1) && (quartus_compile_version_str[quartus_version_len - 2] == '\r')) { + quartus_compile_version_str[quartus_version_len - 2] = '\0'; + } + // Last character is NULL added by acl_pkg_read_section + m_io->quartus_ver->write_block(0, quartus_version_len + 1, quartus_compile_version_str); + } + free(quartus_compile_version_str); + quartus_compile_version_str = NULL; + } + } + + if (version_id_test()) { + ACL_PCIE_DEBUG_MSG(":: [%s] version_id_test() failed.\n", m_name); + reprogram_failed = 1; + } else if (wait_for_uniphy()) { + ACL_PCIE_DEBUG_MSG(":: [%s] Uniphy failed to calibrate.\n", m_name); + reprogram_failed = 1; + } + if (strcmp(ACL_BSP_TYPE, "Stratix10") == 0) { + // S10 PR + if (deassert_pr_reset()) { + ACL_PCIE_DEBUG_MSG(":: [%s] PR region controller reset source deasserted.\n", m_name); + } + }; + if (fast_compile) { + // need to rerun pr_reprogram because design should be loaded now + hash_mismatch = 0; + rbf_or_hash_not_provided = 0; + reprogram_failed = pr_reprogram(pkg, + SOFNAME, + &rbf_or_hash_not_provided, + &hash_mismatch, + &use_jtag_programming, + &quartus_compile_version_mismatch); + } + if (!(reprogram_failed)) { + ACL_PCIE_DEBUG_MSG(":: [%s] JTAG programming passed.\n", m_name); + } + + } else { + ACL_PCIE_DEBUG_MSG(":: [%s] Could not read SOF file from fpga.bin.\n", m_name); + reprogram_failed = 1; + } + } + +cleanup: + // Clean up + if (pkg) acl_pkg_close_file(pkg); + m_being_programmed = false; + + return reprogram_failed; +} +#endif + +// Perform a simple version id read to test the basic PCIe read functionality +// Return 0 on success +int ACL_PCIE_DEVICE::version_id_test() { + unsigned int version = ACL_VERSIONID ^ 1; // make sure it's not what we hope to find. + unsigned int iattempt; + unsigned int max_attempts = 1; + unsigned int usleep_per_attempt = 20; // 20 ms per. + + ACL_PCIE_DEBUG_MSG(":: [%s] Doing PCIe-to-fabric read test ...\n", m_name); + for (iattempt = 0; iattempt < max_attempts; iattempt++) { + m_io->version->read32(0, &version); + if ((version >= (unsigned int)ACL_VERSIONID_MIN) && (version <= (unsigned int)ACL_VERSIONID)) { + ACL_PCIE_DEBUG_MSG(":: [%s] PCIe-to-fabric read test passed\n", m_name); + return 0; + } +#if defined(WINDOWS) + Sleep(usleep_per_attempt); +#endif // WINDOWS +#if defined(LINUX) + usleep(usleep_per_attempt * 1000); +#endif // LINUX + } + + // Kernel read command succeed, but got bad data. (version id doesn't match) + ACL_PCIE_INFO("[%s] PCIe-to-fabric read test failed, read 0x%0x after %u attempts\n", m_name, version, iattempt); + return -1; +} + +// Perform a read of the kernel region status IP +// Return 0 on success (PR region is unfrozen and ready to use) +int ACL_PCIE_DEVICE::check_kernel_region_status() { +#if defined(LINUX) + unsigned int value; + struct acl_cmd driver_cmd = {ACLPCI_CMD_BAR, ACLPCI_CMD_GET_PR_REGION_STATUS, NULL, &value, sizeof(value)}; + if (read(m_device, &driver_cmd, sizeof(driver_cmd)) == -1) { + return -1; + } else { + return value; + } +#endif // Linux + return 0; +} + +// Performs a write to PR region controller to deassert reset to PR region +// Return 0 on success +int ACL_PCIE_DEVICE::deassert_pr_reset() { + ACL_PCIE_DEBUG_MSG(":: [%s] Deasserting PR region controller reset ...\n", m_name); + m_io->pr_region_ctrl->write32(FREEZE_CTRL_OFFSET, 0); + + return 0; +} + +// Quartus Compile Version check +// Return 0 on success +int ACL_PCIE_DEVICE::quartus_ver_test(char *pkg_qversion_str) { + char *fpga_qversion_str; + unsigned int version; + + // Check version ID to ensure feature supported in HW + m_io->version->read32(0, &version); + if (version < (unsigned int)ACL_QUARTUSVER_VERSIONID) { + ACL_PCIE_DEBUG_MSG(":: [%s] Programming on board without Quartus Version RAM\n", m_name); + return 1; + } + + // Allocate buffer for Quartus version read from FPGA with + // largest expected size + 1 for NULL + fpga_qversion_str = reinterpret_cast<char*>(malloc(ACL_QUARTUSVER_ROM_SIZE + 1)); + if (NULL == fpga_qversion_str) { + ACL_PCIE_DEBUG_MSG(":: Memory allocation failed, allocating %d bytes\n", ACL_QUARTUSVER_ROM_SIZE + 1); + free(fpga_qversion_str); + return 1; + } + // Make sure it's not what we hope to find + memset(fpga_qversion_str, 0, ACL_QUARTUSVER_ROM_SIZE + 1); + + m_io->quartus_ver->read_block(0, ACL_QUARTUSVER_ROM_SIZE, fpga_qversion_str); + + size_t fpga_qversion_len = 0; + fpga_qversion_len = strnlen(fpga_qversion_str, MAX_LEN); + + size_t pkg_qversion_len = 0; + if (pkg_qversion_str) { + pkg_qversion_len = strnlen(pkg_qversion_str, MAX_LEN); + + if (fpga_qversion_len != pkg_qversion_len) { + // Kernel read command succeed, but got bad data. (Quartus Version doesn't match) + ACL_PCIE_DEBUG_MSG("[%s] Quartus versions for base and import compile do not match\n", m_name); + ACL_PCIE_DEBUG_MSG("[%s] Board is currently programmed with sof from Quartus %s\n", m_name, fpga_qversion_str); + ACL_PCIE_DEBUG_MSG("[%s] PR import was compiled with Quartus %s\n", m_name, pkg_qversion_str); + free(fpga_qversion_str); + return 1; + } + + if (strncmp(pkg_qversion_str, fpga_qversion_str, fpga_qversion_len) == 0) { + ACL_PCIE_DEBUG_MSG(":: [%s] Quartus versions for base and import compile match\n", m_name); + ACL_PCIE_DEBUG_MSG(":: [%s] Board is currently programmed with sof from Quartus %s\n", m_name, fpga_qversion_str); + ACL_PCIE_DEBUG_MSG(":: [%s] PR import was compiled with Quartus %s\n", m_name, pkg_qversion_str); + free(fpga_qversion_str); + return 0; + } + + // Kernel read command succeed, but got bad data. (Quartus Version doesn't match) + ACL_PCIE_DEBUG_MSG("[%s] Quartus versions for base and import compile do not match\n", m_name); + ACL_PCIE_DEBUG_MSG("[%s] Board is currently programmed with sof from Quartus %s\n", m_name, fpga_qversion_str); + ACL_PCIE_DEBUG_MSG("[%s] PR import was compiled with Quartus %s\n", m_name, pkg_qversion_str); + } + free(fpga_qversion_str); + return 1; +} + +// Perform a simple read to the PR base ID in the static region and compare it with the given ID +// Return 0 on success +int ACL_PCIE_DEVICE::pr_base_id_test(unsigned int pr_import_version) { + unsigned int pr_base_version = 0; // make sure it's not what we hope to find. + + ACL_PCIE_DEBUG_MSG(":: [%s] Reading PR base ID from fabric ...\n", m_name); + m_io->pr_base_id->read32(0, &pr_base_version); + if (pr_base_version == pr_import_version) { + ACL_PCIE_DEBUG_MSG(":: [%s] PR base and import compile IDs match\n", m_name); + ACL_PCIE_DEBUG_MSG(":: [%s] PR base ID currently configured is 0x%0x\n", m_name, pr_base_version); + ACL_PCIE_DEBUG_MSG(":: [%s] PR import compile ID is 0x%0x\n", m_name, pr_import_version); + return 0; + }; + + // Kernel read command succeed, but got bad data. (version id doesn't match) + ACL_PCIE_DEBUG_MSG("[%s] PR base and import compile IDs do not match\n", m_name); + ACL_PCIE_DEBUG_MSG("[%s] PR base ID currently configured is 0x%0x\n", m_name, pr_base_version); + ACL_PCIE_DEBUG_MSG("[%s] PR import compile expects ID to be 0x%0x\n", m_name, pr_import_version); + return -1; +} + +// 1. Write a random value to cade_id register, do a read to confirm the write +// 2. Use the random value to find the JTAG cable for that board +// 3. Return "0" on ad_cable,ad_device_index if cable not found +void ACL_PCIE_DEVICE::find_jtag_cable(char *ad_cable, char *ad_device_index) { + bool jtag_ad_disabled = false; + bool jtag_ad_cable_found = false; + unsigned int version = 0; + + // Check if Autodetect is disabled + const char *cable = getenv("ACL_PCIE_JTAG_CABLE"); + const char *device_index = getenv("ACL_PCIE_JTAG_DEVICE_INDEX"); + if (cable || device_index) { + jtag_ad_disabled = true; + ACL_PCIE_DEBUG_MSG(":: [%s] JTAG cable autodetect disabled!!!\n", m_name); + } + + // Check version ID to ensure feature supported in HW + m_io->version->read32(0, &version); + if (version < (unsigned int)ACL_CADEID_VERSIONID) { + jtag_ad_disabled = true; + ACL_PCIE_DEBUG_MSG(":: [%s] JTAG cable autodetect disabled due to old HW version!!!\n", m_name); + } + + // If JTAG autodetect is enabled, program the CADEID register + // and look for the value using in system sources and probes + if (!jtag_ad_disabled) { + // Only use random device here because we only want one value. Normally use mersenne twister for more values + std::random_device rd; + std::uniform_int_distribution<unsigned int> dist(0u, 0xFFFFFFFFu); + unsigned int cade_id_write = dist(rd) & 0xFFFFFFFF; + cade_id_write = cade_id_write | 0x80000000; // Write a full 32 bit value + unsigned int cade_id_read = 0x0; + + ACL_PCIE_DEBUG_MSG(":: [%s] Writing Cade ID to fabric ...\n", m_name); + m_io->cade_id->write32(0, cade_id_write); + + ACL_PCIE_DEBUG_MSG(":: [%s] Reading Cade ID from fabric ...\n", m_name); + m_io->cade_id->read32(0, &cade_id_read); + + if (cade_id_write == cade_id_read) { + ACL_PCIE_DEBUG_MSG(":: [%s] Cade ID write/read success ...\n", m_name); + ACL_PCIE_DEBUG_MSG( + ":: [%s] Cade ID cade_id_write 0x%0x, cade_id_read 0x%0x\n", m_name, cade_id_write, cade_id_read); + + // Returns NULL on ad_cable,ad_device_index if no cable found + jtag_ad_cable_found = m_config->find_cable_with_ISSP(cade_id_write, ad_cable, ad_device_index); + + if (!jtag_ad_cable_found) { + ACL_PCIE_DEBUG_MSG(":: [%s] Using default cable 1 ...\n", m_name); + } else { + ACL_PCIE_DEBUG_MSG(":: [%s] Found Cable ...\n", m_name); + } + } else { + ACL_PCIE_DEBUG_MSG(":: [%s] Cade ID write/read failed. Check BSP version or PCIE link...\n", m_name); + ACL_PCIE_DEBUG_MSG( + ":: [%s] Cade ID cade_id_write 0x%0x, cade_id_read 0x%0x\n", m_name, cade_id_write, cade_id_read); + } + } + + if (jtag_ad_disabled || !jtag_ad_cable_found) { + snprintf(ad_cable, AD_CABLE_SIZE, "%s", "0"); + snprintf(ad_device_index, AD_CABLE_SIZE, "%s", "0"); + } +} + +// Wait until the uniphy calibrated +// Return 0 on success +int ACL_PCIE_DEVICE::wait_for_uniphy() { + const unsigned int ACL_UNIPHYSTATUS = 0; + unsigned int status = 1, retries = 0; + + while (retries++ < 8) { + m_io->uniphy_status->read32(0, &status); + + if (status == ACL_UNIPHYSTATUS) { + ACL_PCIE_DEBUG_MSG(":: [%s] Uniphys are calibrated\n", m_name); + return 0; // success + } + + ACL_PCIE_DEBUG_MSG(":: [%s] Uniphy status read was %x\n", m_name, status); + ACL_PCIE_DEBUG_MSG(":: [%s] Resetting Uniphy try %d\n", m_name, retries); + m_io->uniphy_reset->write32(0, 1); + +#if defined(WINDOWS) + Sleep(400); +#endif // WINDOWS +#if defined(LINUX) + usleep(400 * 1000); +#endif // LINUX + } + + ACL_PCIE_INFO("[%s] uniphy(s) did not calibrate. Expected 0 but read %x\n", m_name, status); + + // Failure! Was it communication error or actual calibration failure? + if (ACL_PCIE_READ_BIT(status, 3)) // This bit is hardcoded to 0 + ACL_PCIE_INFO( + " Uniphy calibration status is corrupt. This is likely a communication error with the board " + "and/or uniphy_status module.\n"); + else { + // This is a 32-bit interface with the first 4 bits aggregating the + // various calibration signals. The remaining 28-bits would indicate + // failure for their respective memory core. Tell users which ones + // failed + for (int i = 0; i < 32 - 4; i++) { + if (ACL_PCIE_READ_BIT(status, 4 + i)) ACL_PCIE_INFO(" Uniphy core %d failed to calibrate\n", i); + } + ACL_PCIE_INFO(" If there are more failures than Uniphy controllers connected, \n"); + ACL_PCIE_INFO(" ensure the uniphy_status core is correctly parameterized.\n"); + } + + return -1; // failure +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.h new file mode 100644 index 0000000..29f5128 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_device.h @@ -0,0 +1,209 @@ +#ifndef ACL_PCIE_DEVICE_H +#define ACL_PCIE_DEVICE_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_device.h -------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle operations on a single device. */ +/* The actual implementation of the class lives in the acl_pcie_device.cpp */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// Forward declaration for classes used by ACL_PCIE_DEVICE +class ACL_PCIE_DMA; +class ACL_PCIE_CONFIG; +class ACL_PCIE_MM_IO_MGR; +class ACL_PCIE_HOSTCH; + +#if defined(LINUX) +typedef int fpga_handle; +#else +#include <opae/fpga.h> +#endif // LINUX + +#ifdef DLA_MMD +// CoreDLA runtime assumes host/device transfers are thread safe +#include <mutex> +// don't assume opencl has been installed +typedef int cl_int; +#endif + +// Encapsulates the functionality of an ACL device connected to the host +// through a PCI express bus. +class ACL_PCIE_DEVICE { + public: + ACL_PCIE_DEVICE(int dev_num, const char *name, int handle, int user_signal_number); + ~ACL_PCIE_DEVICE(); + ACL_PCIE_DEVICE(const ACL_PCIE_DEVICE&) = delete; + ACL_PCIE_DEVICE& operator= (const ACL_PCIE_DEVICE&) = delete; + + bool is_valid() { return m_device != INVALID_HANDLE_VALUE; }; + bool is_initialized() { return m_initialized; }; + bool is_being_programmed() { return m_being_programmed; }; + + // Perform operations required when an interrupt is received for this device + void service_interrupt(unsigned int irq_type_flag = 0); + // This function can be used for triggering a fake device exception for + void test_trigger_device_interrupt(); + + // The callback function set by "set_status_handler" + // It's used to notify/update the host whenever an event is finished + void event_update_fn(aocl_mmd_op_t op, int status); + + // Called by the host program when there are spare cycles + int yield(); + + // Memory I/O + // return 0 on success + int write_block(aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, void *host_addr, size_t dev_addr, size_t size); + int read_block(aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, void *host_addr, size_t dev_addr, size_t size); + int copy_block(aocl_mmd_op_t e, aocl_mmd_interface_t mmd_interface, size_t src, size_t dst, size_t size); + + // Create channel. return handle to channel on success, negative otherwise + int create_hostchannel(char *name, size_t queue_depth, int direction); + + // return 0 on success + int destroy_channel(int channel); + + // return pointer that user can write to for write channel, and read from for read channel + void *hostchannel_get_buffer(size_t *buffer_size, int channel, int *status); + + // return the size in bytes of the amount of buffer that was acknlowedged to channel + size_t hostchannel_ack_buffer(size_t send_size, int channel, int *status); + + // Set kernel, device interrupts and event update callbacks + // return 0 on success + int set_kernel_interrupt(aocl_mmd_interrupt_handler_fn fn, void *user_data); + int set_device_interrupt(aocl_mmd_device_interrupt_handler_fn fn, void *user_data); + int set_status_handler(aocl_mmd_status_handler_fn fn, void *user_data); + + // Query PCIe information of the device + char *get_dev_pcie_info() { return m_info.pcie_info_str; }; + + // Query on-die temperature sensor, if available + bool get_ondie_temp_slow_call(cl_int *temp); + + // Shared memory manipulation functions + void *shared_mem_alloc(size_t size, unsigned long long *device_ptr_out); + void shared_mem_free(void *host_ptr, size_t size); + + // Reprogram the device with given binary file + // return 0 on success +#ifdef DLA_MMD + int pause_and_save_pcie(); + int restore_and_resume_pcie(); + int reprogram_sof(const char *sof_filename, const bool skipSaveRestore = false); +#else + int reprogram(void *data, size_t data_size, int program_mode); +#endif + + private: + // Helper routines for interrupts + // return 0 on success, negative on error + int mask_irqs(); + int unmask_irqs(); + int unmask_kernel_irq(); + int disable_interrupts(); + int enable_interrupts(int user_signal_number); + int get_interrupt_type(unsigned int *kernel_update, unsigned int *dma_update, unsigned int irq_type_flag); +#if defined(WINDOWS) + void enable_msi(bool enable); +#endif // WINDOWS + + // Helper routines for read or write operations + // return 0 on success, negative on error (except for the "incr_ptrs" routine) + int read_write_block(aocl_mmd_op_t e, void *host_addr, size_t dev_addr, size_t size, bool reading); + int read_write_block_bar(void *host_addr, size_t dev_addr, size_t size, bool reading); + int read_write_small_size(void *host_addr, size_t dev_addr, size_t size, bool reading); + int set_segment(size_t addr); + void incr_ptrs(void **host, size_t *dev, size_t *counter, size_t incr); + int does_base_periph_match_new_periph(struct acl_pkg_file *pkg, const char *dev_name); + + // Helper routines for simple functionality test + // return 0 on success, negative on error + int version_id_test(); + int wait_for_uniphy(); + int pr_base_id_test(unsigned int pr_import_version); + int deassert_pr_reset(); + int quartus_ver_test(char *pkg_qversion_str); + int check_kernel_region_status(); + + // Write a random value to cade_id register, do a read to confirm the write + // Use the random value to find the JTAG cable for that board + // Return 0 on ad_cable,ad_device_index if cable not found + void find_jtag_cable(char *ad_cable, char *ad_device_index); + +#ifndef DLA_MMD + // Performs PR reprogramming if possible, and returns different statuses on + // PR Hash, JTAG programming, RBF or Hash Presence + // Returns 0 on success, 1 on reprogram fail + int pr_reprogram(struct acl_pkg_file *pkg, + const char *SOFNAME, + int *rbf_or_hash_not_provided, + int *hash_mismatch, + unsigned *use_jtag_programming, + int *quartus_compile_version_mismatch); +#endif + + // Kernel interrupt handler and event update callbacks + aocl_mmd_interrupt_handler_fn kernel_interrupt; + void *kernel_interrupt_user_data; + aocl_mmd_device_interrupt_handler_fn device_interrupt; + void *device_interrupt_user_data; + aocl_mmd_status_handler_fn event_update; + void *event_update_user_data; + int m_user_signal_number; + + ACL_PCIE_MM_IO_MGR *m_io; + ACL_PCIE_DMA *m_dma; + ACL_PCIE_HOSTCH *m_hostch; + ACL_PCIE_CONFIG *m_config; + + static const int MAX_NAME_LENGTH = 32; + int m_handle; + char m_name[MAX_NAME_LENGTH]; + fpga_handle m_device; + ACL_PCIE_DEVICE_DESCRIPTION m_info; + + bool m_use_dma_for_big_transfers; + bool m_mmd_irq_handler_enable; + bool m_initialized; + bool m_being_programmed; + bool m_skip_quartus_version_check; + + // IRQ acknowledgement commands in the KMD + static const unsigned int NUM_ACK_CMDS = 3; +#if defined(WINDOWS) + fpga_event_handle *dev_event_handle; +#endif // WINDOWS + + // For the host, memory is segmented. This stores the last used segment + // ID so we don't needlessly update it in hardware + UINT64 m_segment; + +#ifdef DLA_MMD + std::mutex m_dma_mutex; +#endif +}; + +#endif // ACL_PCIE_DEVICE_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma.h new file mode 100644 index 0000000..ec9fdb1 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma.h @@ -0,0 +1,37 @@ +#ifndef ACL_PCIE_DMA_H +#define ACL_PCIE_DMA_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_dma.h ----------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(WINDOWS) +#include "acl_pcie_dma_windows.h" +#endif // WINDOWS +#if defined(LINUX) +#include "acl_pcie_dma_linux.h" +#endif // LINUX + +#endif // ACL_PCIE_DMA_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.cpp new file mode 100644 index 0000000..a83b0dd --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.cpp @@ -0,0 +1,141 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_dma_linux.cpp --------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle Linux-specific DMA operations. */ +/* The declaration of the class lives in the acl_pcie_dma_linux.h */ +/* The actual implementation of DMA operation is inside the Linux kernel driver. */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(LINUX) + +// common and its own header files +#include "acl_pcie_dma_linux.h" +#include "acl_pcie.h" + +// other header files inside MMD driver +#include "acl_pcie_device.h" +#include "acl_pcie_mm_io.h" + +// other standard header files +#include <stdio.h> +#include <sys/time.h> +#include <unistd.h> + +ACL_PCIE_DMA::ACL_PCIE_DMA(fpga_handle dev, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie) { + ACL_PCIE_ASSERT(dev != INVALID_DEVICE, "passed in an invalid device when creating dma object.\n"); + ACL_PCIE_ASSERT(io != NULL, "passed in an empty pointer for io when creating dma object.\n"); + ACL_PCIE_ASSERT(pcie != NULL, "passed in an empty pointer for pcie when creating dma object.\n"); + + m_handle = dev; + m_pcie = pcie; + m_io = io; + m_event = NULL; +} + +ACL_PCIE_DMA::~ACL_PCIE_DMA() { + struct acl_cmd driver_cmd = {ACLPCI_CMD_BAR, ACLPCI_CMD_DMA_STOP, NULL, NULL}; + int bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "failed to read driver command \n"); +} + +bool ACL_PCIE_DMA::is_idle() { + unsigned int result = 0; + int bytes_read; + struct acl_cmd driver_cmd; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_GET_DMA_IDLE_STATUS; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = &result; + driver_cmd.size = sizeof(result); + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + + return (bytes_read != -1 && result != 0); +} + +// Perform operations required when a DMA interrupt comes +// For Linux, +// All of the DMA related interrupts are handled inside the kernel driver, +// so when MMD gets a signal from the kernel driver indicating DMA is finished, +// it only needs to call the event_update_fn when it's needed. +void ACL_PCIE_DMA::service_interrupt() { + if (m_event) { + // Use a temporary variable to save the event data and reset m_event + // before calling event_update_fn to avoid race condition that the main + // thread may start a new DMA transfer before this work-thread is able to + // reset the m_event. + // therefore, an assertion is implemented here, as defensively preventing + // sending interrupt signals incorrectly. + ACL_PCIE_ASSERT( + this->is_idle(), + "The dma is still in running, cannot service an interrupt to invoke another read/write operation\n"); + aocl_mmd_op_t temp_event = m_event; + m_event = NULL; + + m_pcie->event_update_fn(temp_event, 0); + } +} + +// relinquish the CPU to let any other thread to run +// return 0 since there is no useful work to be performed here +int ACL_PCIE_DMA::yield() { + usleep(0); + return 0; +} + +// Transfer data between host and device +// This function returns right after the transfer is scheduled +// Return 0 on success +int ACL_PCIE_DMA::read_write(void *host_addr, size_t dev_addr, size_t bytes, aocl_mmd_op_t e, bool reading) { + // Currently dma cannot operate multiple read/write the same time. + // This means the read/write should be executed if and only if the dma is idle. + // Otherwise, it would cause assertion failure in the kernel space of the OS, + // which result in hanging, and even kernel panic and machine frozen as worst case. + // An assertion is implemented here, as defensively preventing race condition or incorrect sending of signal. + ACL_PCIE_ASSERT(this->is_idle(), + "The dma is still in running, cannot perform another %s operation concurrently.\n", + reading ? "read" : "write"); + + m_event = e; + + // There are two scenarios of the read/write operation + // 1. the referred event is NULL, MMD would be stalled and keep polling the DMA until it is idle. + // 2. the referred event is valid, MMD would return immediately, runtime will wait for + // the DMA service interrupt signal to update the status of the read/write operation. + // + // Therefore, the dma service interrupt is expected only when the event is valid. + struct acl_cmd driver_cmd {}; + driver_cmd.bar_id = ACLPCI_DMA_BAR; + driver_cmd.command = m_event ? ACLPCI_CMD_DMA_SERVICE_SIGNAL : ACLPCI_CMD_DMA_NO_SIGNAL; + driver_cmd.device_addr = reinterpret_cast<void *>(dev_addr); + driver_cmd.user_addr = host_addr; + driver_cmd.size = bytes; + if (reading) { + if (read(m_handle, &driver_cmd, sizeof(driver_cmd)) == -1) return -1; // reading failed + } else { + if (write(m_handle, &driver_cmd, sizeof(driver_cmd)) == -1) return -1; + } + return 0; // success +} + +#endif // LINUX diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.h new file mode 100644 index 0000000..2ad1762 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_linux.h @@ -0,0 +1,75 @@ +#ifndef ACL_PCIE_DMA_LINUX_H +#define ACL_PCIE_DMA_LINUX_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_dma_linux.h ----------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle Linux-specific DMA operations. */ +/* The actual implementation of the class lives in the acl_pcie_dma_linux.cpp */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(LINUX) + +#ifdef DLA_MMD +#include <cstddef> //size_t +#include "aocl_mmd.h" +typedef int fpga_handle; +#endif + +class ACL_PCIE_DEVICE; +class ACL_PCIE_MM_IO_MGR; + +class ACL_PCIE_DMA { + public: + ACL_PCIE_DMA(fpga_handle dev, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie); + ~ACL_PCIE_DMA(); + + bool is_idle(); + void stall_until_idle() { + while (!is_idle()) yield(); + }; + + // Perform operations required when a DMA interrupt comes + void service_interrupt(); + + // Relinquish the CPU to let any other thread to run + // Return 0 since there is no useful work to be performed here + int yield(); + + // Transfer data between host and device + // This function returns right after the transfer is scheduled + // Return 0 on success + int read_write(void *host_addr, size_t dev_addr, size_t bytes, aocl_mmd_op_t e, bool reading); + + private: + aocl_mmd_op_t m_event; + + fpga_handle m_handle; + ACL_PCIE_DEVICE *m_pcie; + ACL_PCIE_MM_IO_MGR *m_io; +}; + +#endif // LINUX + +#endif // ACL_PCIE_DMA_LINUX_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.cpp new file mode 100644 index 0000000..ab5e7b2 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.cpp @@ -0,0 +1,1381 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_dma_windows.cpp ------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle Windows-specific DMA operations. */ +/* The declaration of the class lives in the acl_pcie_dma_windows.h */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(WINDOWS) + +// common and its own header files +#include "acl_pcie.h" +#include "acl_pcie_dma_windows.h" +#include "hw_pcie_constants.h" + +// other header files inside MMD driver +#include "acl_pcie_device.h" +#include "acl_pcie_mm_io.h" +#include "acl_pcie_timer.h" +#include "acl_pcie_debug.h" +#include <iostream> +#include <stdlib.h> + +#define ACL_PCIE_DMA_DEBUG(m, ...) ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, m, __VA_ARGS__) + +// The callback function to be scheduled inside the interrupt handler +// It will release the semaphore to allow new work to be scheduled and +// perform the dma update function +void CALLBACK myWorkCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work) { + ACL_PCIE_DMA *m_dma = (ACL_PCIE_DMA *)context; + + ReleaseSemaphore(m_dma->m_workqueue_semaphore, 1, NULL); + + m_dma->update(true); +} + +void CALLBACK myWorkUnpinCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work) { + ACL_PCIE_DMA *m_dma = (ACL_PCIE_DMA *)context; + + m_dma->unpin_from_queue(); +} + +void CALLBACK myWorkPinCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work) { + ACL_PCIE_DMA *m_dma = (ACL_PCIE_DMA *)context; + + m_dma->prepin_memory(); +} + +ACL_PCIE_DMA::ACL_PCIE_DMA(fpga_handle handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie) + : hostch_data(), + m_table_virt_addr(NULL), + m_table_dma_addr(), + m_table_dma_phys_addr(0), + m_active_descriptor(NULL), + m_last_pinned_size(0), + m_last_pinned_addr(NULL), + m_prepinned(0), + m_last_id(0), + m_event(NULL), + m_dev_addr(0), + m_host_addr(NULL), + m_bytes(0), + m_bytes_sent(0), + m_bytes_rem(0), + m_read(0), + m_idle(0), + m_interrupt_disabled(0), + m_pcie(NULL), + m_io(NULL), + m_timer(NULL), + m_callback_env(), + m_work(NULL), + m_workqueue_semaphore(NULL), + m_dma_unpin_pending(), + m_unpin_callback_env(), + m_unpin_threadpool(NULL), + m_unpin_work(NULL), + m_pin_callback_env(), + m_pin_threadpool(NULL), + m_pin_work(NULL) { + ACL_PCIE_ASSERT(handle != INVALID_HANDLE_VALUE, "passed in an invalid device when creating dma object.\n"); + ACL_PCIE_ASSERT(io != NULL, "passed in an empty pointer for io when creating dma object.\n"); + ACL_PCIE_ASSERT(pcie != NULL, "passed in an empty pointer for pcie when creating dma object.\n"); + + m_handle = handle; + m_io = io; + m_pcie = pcie; + + HOSTCH_DESC *h = &hostch_data; + + const char *use_msi = getenv("ACL_PCIE_DMA_USE_MSI"); + if (use_msi) + m_use_polling = 0; + else + m_use_polling = 1; + + SecureZeroMemory(&m_active_mem, sizeof(PINNED_MEM)); + SecureZeroMemory(&m_pre_pinned_mem, sizeof(PINNED_MEM)); + SecureZeroMemory(&m_done_mem, sizeof(PINNED_MEM)); + + // Initialize Host Channel + SecureZeroMemory(&h->m_hostch_rd_mem, sizeof(PINNED_MEM)); + SecureZeroMemory(&h->m_hostch_wr_mem, sizeof(PINNED_MEM)); + SecureZeroMemory(&h->m_hostch_rd_pointer, sizeof(PINNED_MEM)); + SecureZeroMemory(&h->m_hostch_wr_pointer, sizeof(PINNED_MEM)); + SecureZeroMemory(&h->m_sync_thread_pointer, sizeof(PINNED_MEM)); + h->push_valid = 0; + h->pull_valid = 0; + + m_timer = new ACL_PCIE_TIMER(); + + // create the threadpool to perform work the interrupt + m_threadpool = CreateThreadpool(NULL); + ACL_PCIE_ERROR_IF(m_threadpool == NULL, return, "failed to create threadpool.\n"); + + // set the number of work threads to 1 + // so that no scheduled work will be running in parallel between them + SetThreadpoolThreadMaximum(m_threadpool, 1); + bool status = SetThreadpoolThreadMinimum(m_threadpool, 1); + ACL_PCIE_ERROR_IF(status == false, return, "failed to set # of work thread to 1.\n"); + + // create the work for threadpool and its semaphore + InitializeThreadpoolEnvironment(&m_callback_env); + SetThreadpoolCallbackPool(&m_callback_env, m_threadpool); + + m_work = CreateThreadpoolWork(myWorkCallback, (void *)this, &m_callback_env); + ACL_PCIE_ERROR_IF(m_work == NULL, return, "failed to create work for threadpool.\n"); + + m_workqueue_semaphore = CreateSemaphore(NULL, 1, 1, NULL); + ACL_PCIE_ERROR_IF(m_workqueue_semaphore == NULL, return, "failed to create semaphore.\n"); + + /////////////////////////////////////////////////////////////////////////////////////////// + // Unpin thread + m_unpin_threadpool = CreateThreadpool(NULL); + ACL_PCIE_ERROR_IF(m_unpin_threadpool == NULL, return, "failed to create threadpool.\n"); + + // set the number of work threads to 1 + // so that no scheduled work will be running in parallel between them + SetThreadpoolThreadMaximum(m_unpin_threadpool, 1); + status = SetThreadpoolThreadMinimum(m_unpin_threadpool, 1); + ACL_PCIE_ERROR_IF(status == false, return, "failed to set # of work thread to 1.\n"); + + // create the work for threadpool and its semaphore + InitializeThreadpoolEnvironment(&m_unpin_callback_env); + SetThreadpoolCallbackPool(&m_unpin_callback_env, m_unpin_threadpool); + + m_unpin_work = CreateThreadpoolWork(myWorkUnpinCallback, (void *)this, &m_unpin_callback_env); + ACL_PCIE_ERROR_IF(m_unpin_work == NULL, return, "failed to create work for unpin threadpool.\n"); + + /////////////////////////////////////////////////////////////////////////////////////////// + // pin thread + m_pin_threadpool = CreateThreadpool(NULL); + ACL_PCIE_ERROR_IF(m_pin_threadpool == NULL, return, "failed to create threadpool.\n"); + + // set the number of work threads to 1 + // so that no scheduled work will be running in parallel between them + SetThreadpoolThreadMaximum(m_pin_threadpool, 1); + status = SetThreadpoolThreadMinimum(m_pin_threadpool, 1); + ACL_PCIE_ERROR_IF(status == false, return, "failed to set # of work thread to 1.\n"); + + // create the work for threadpool and its semaphore + InitializeThreadpoolEnvironment(&m_pin_callback_env); + SetThreadpoolCallbackPool(&m_pin_callback_env, m_pin_threadpool); + + m_pin_work = CreateThreadpoolWork(myWorkPinCallback, (void *)this, &m_pin_callback_env); + ACL_PCIE_ERROR_IF(m_pin_work == NULL, return, "failed to create work for unpin threadpool.\n"); + + /////////////////////////////////////////////////////////////////////////////////////////// + // Contiguous DMA'able memory allocation for descriptor table + + fpga_result FPGA_status; + size_t desc_table_size = sizeof(struct DMA_DESC_TABLE); + size_t page_table_size = sizeof(struct HOSTCH_TABLE); + + // Lock DMA_DESC_TABLE using WsId + FPGA_status = fpgaPrepareBuffer( + m_handle, (UINT64)desc_table_size, (PVOID *)&m_table_virt_addr, &m_table_dma_addr.WsId, FPGA_BUF_QUIET); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaPrepareBuffer function failed.\n"); + + // IOCTL call to flush CPU buffers + FPGA_status = fpgaProcessDeviceCmd( + m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_CPU_BUFFERS), &m_table_dma_addr.WsId, NULL, 0); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + // Obtain Physical address for the Page associated with the buffer + FPGA_status = fpgaGetPhysicalAddress(m_handle, m_table_dma_addr.WsId, (uint64_t *)&m_table_dma_addr.dwPages, NULL); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + // Allocate memory for SG List + m_table_dma_addr.Page = (sg_element *)malloc(m_table_dma_addr.dwPages * sizeof(sg_element)); + + // Throw an exception in case of malloc failure + if (m_table_dma_addr.Page == NULL) throw std::bad_alloc(); + + FPGA_status = fpgaGetPhysicalAddress( + m_handle, m_table_dma_addr.WsId, (uint64_t *)&m_table_dma_addr.dwPages, (void *)m_table_dma_addr.Page); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Successfully locked DMA descriptor table memory.\n"); + ACL_PCIE_ASSERT(m_table_dma_addr.dwPages == 1, "fpgaPrepareBuffer function allocated more than 1 page.\n"); + + if (m_table_dma_addr.Page != NULL) m_table_dma_phys_addr = m_table_dma_addr.Page[0].phys_addr; + + // Lock HOSTCH_TABLE push channel using WsId + FPGA_status = fpgaPrepareBuffer(m_handle, + (UINT64)page_table_size, + (PVOID *)&h->push_page_table, + &hostch_data.push_page_table_addr.WsId, + FPGA_BUF_QUIET); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaPrepareBuffer function failed.\n"); + + // IOCTL call to flush CPU buffers + FPGA_status = fpgaProcessDeviceCmd(m_handle, + GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_CPU_BUFFERS), + (PVOID)&hostch_data.push_page_table_addr.WsId, + NULL, + 0); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + // Obtain Physical address for the Page associated with the buffer + FPGA_status = fpgaGetPhysicalAddress( + m_handle, hostch_data.push_page_table_addr.WsId, (uint64_t *)&hostch_data.push_page_table_addr.dwPages, NULL); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + // Allocate memory for SG List + hostch_data.push_page_table_addr.Page = + (sg_element *)malloc(hostch_data.push_page_table_addr.dwPages * sizeof(sg_element)); + + // Throw an exception in case of malloc failure + if (hostch_data.push_page_table_addr.Page == NULL) throw std::bad_alloc(); + + FPGA_status = fpgaGetPhysicalAddress(m_handle, + hostch_data.push_page_table_addr.WsId, + (uint64_t *)&hostch_data.push_page_table_addr.dwPages, + (void *)hostch_data.push_page_table_addr.Page); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Successfully locked descriptor table for Hostchannel memory.\n"); + ACL_PCIE_ASSERT(hostch_data.push_page_table_addr.dwPages == 1, + "fpgaPrepareBuffer function for HostChannel allocated more than 1 page.\n"); + + if (hostch_data.push_page_table_addr.Page != NULL) + hostch_data.push_page_table_bus_addr = hostch_data.push_page_table_addr.Page[0].phys_addr; + + // Lock HOSTCH_TABLE pull channel + FPGA_status = fpgaPrepareBuffer(m_handle, + (UINT64)page_table_size, + (PVOID *)&h->pull_page_table, + &hostch_data.pull_page_table_addr.WsId, + FPGA_BUF_QUIET); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaPrepareBuffer function for Hostchannel failed. \n"); + + // IOCTL call to flush CPU buffers + FPGA_status = fpgaProcessDeviceCmd(m_handle, + GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_CPU_BUFFERS), + (PVOID)&hostch_data.pull_page_table_addr.WsId, + NULL, + 0); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + // Obtain Physical address for the Page associated with the buffer + FPGA_status = fpgaGetPhysicalAddress( + m_handle, hostch_data.pull_page_table_addr.WsId, (uint64_t *)&hostch_data.pull_page_table_addr.dwPages, NULL); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + // Allocate memory for SG List + hostch_data.pull_page_table_addr.Page = + (sg_element *)malloc(hostch_data.pull_page_table_addr.dwPages * sizeof(sg_element)); + + // Throw an exception in case of malloc failure + if (hostch_data.pull_page_table_addr.Page == NULL) throw std::bad_alloc(); + + FPGA_status = fpgaGetPhysicalAddress(m_handle, + hostch_data.pull_page_table_addr.WsId, + (uint64_t *)&hostch_data.pull_page_table_addr.dwPages, + (void *)hostch_data.pull_page_table_addr.Page); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Successfully locked descriptor table memory.\n"); + ACL_PCIE_ASSERT(hostch_data.pull_page_table_addr.dwPages == 1, + "fpgaPrepareBuffer function for HostChannel allocated more than 1 page.\n"); + + if (hostch_data.pull_page_table_addr.Page != NULL) + hostch_data.pull_page_table_bus_addr = hostch_data.pull_page_table_addr.Page[0].phys_addr; + + // set idle status to true when finish initialization + m_idle = true; +} + +ACL_PCIE_DMA::~ACL_PCIE_DMA() { + fpga_result FPGA_status; + stall_until_idle(); + + // make sure no more work queued for threadpool + WaitForThreadpoolWorkCallbacks(m_work, FALSE); + + // hostch_destroy is expected to be called by user but to make sure, call in the destructor + hostch_destroy(ACL_HOST_CHANNEL_0_ID); + hostch_destroy(ACL_HOST_CHANNEL_1_ID); + + // Unlock all the previously allocated tables from the constructor + FPGA_status = fpgaReleaseBuffer(m_handle, m_table_dma_addr.WsId); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaReleaseBuffer was not successful\n"); + + if (m_table_dma_addr.Page != NULL) { + free(m_table_dma_addr.Page); + m_table_dma_addr.Page = NULL; + } + + FPGA_status = fpgaReleaseBuffer(m_handle, hostch_data.push_page_table_addr.WsId); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaReleaseBuffer was not successful\n"); + + if (hostch_data.push_page_table_addr.Page != NULL) { + free(hostch_data.push_page_table_addr.Page); + hostch_data.push_page_table_addr.Page = NULL; + } + + FPGA_status = fpgaReleaseBuffer(m_handle, hostch_data.pull_page_table_addr.WsId); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaReleaseBuffer was not successful\n"); + + if (hostch_data.pull_page_table_addr.Page != NULL) { + free(hostch_data.pull_page_table_addr.Page); + hostch_data.pull_page_table_addr.Page = NULL; + } + + CloseHandle(m_workqueue_semaphore); + CloseThreadpoolWork(m_work); + CloseThreadpool(m_threadpool); + + CloseThreadpoolWork(m_unpin_work); + CloseThreadpool(m_unpin_threadpool); + + CloseThreadpoolWork(m_pin_work); + CloseThreadpool(m_pin_threadpool); + + if (m_timer) { + delete m_timer; + m_timer = NULL; + } +} + +int ACL_PCIE_DMA::check_dma_interrupt(unsigned int *dma_update) { + if (!m_use_polling) { + if (m_last_id > 0 && m_last_id <= ACL_PCIE_DMA_DESC_MAX_ENTRIES) { + *dma_update = (m_table_virt_addr->header.flags[m_last_id - 1]); + } else { + return 1; + } + } + return 0; +} + +void ACL_PCIE_DMA::unpin_from_queue() { + fpga_result result; + ACL_PCIE_ASSERT(!m_dma_unpin_pending.empty(), "m_dma_unpin_pending is empty but unpin mem thread was called\n"); + + QUEUE_STRUCT entry; + + entry = m_dma_unpin_pending.front(); + m_dma_unpin_pending.pop(); + + // IOCTL call to flush IO buffers + result = fpgaProcessDeviceCmd( + m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_IO_BUFFERS), (PVOID) & (entry.WsId), NULL, 0); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + // Unlock the allocated tables associated with wsId + result = fpgaReleaseBuffer(m_handle, entry.WsId); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReleaseBuffer function failed.\n"); + if (entry.SGListPtr != NULL) free(entry.SGListPtr); +} + +void ACL_PCIE_DMA::prepin_memory() { pin_memory(&m_pre_pinned_mem, true); } + +void ACL_PCIE_DMA::wait_finish() { + UINT32 wait_timer; + + while (1) { + wait_timer = ACL_PCIE_DMA_TIMEOUT; + while (wait_timer > 0) { + wait_timer--; + + if (m_table_virt_addr->header.flags[m_last_id - 1] == 1) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] Wait done\n"); + set_desc_table_header(); + if (WaitForSingleObject(m_workqueue_semaphore, 0L) == WAIT_OBJECT_0) { + SubmitThreadpoolWork(m_work); + } + return; + } + } + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Wait timed out. Sleeping for 1ms.\n"); + Sleep(1); + } +} + +#if defined(GEN3_x16) + // Add extra descriptor for DMA controller to report 'done status' in the DMA table +void ACL_PCIE_DMA::add_extra_dma_desc() { + /* + One extra descriptor is required to be fetched. Two if using interrupts. + For reads (Host <-- FPGA), the last descriptor sets the DMA done status. + For writes (Host --> FPGA), the last descriptor fetches the status + descriptor which then sets the DMA done status. + When using interrupts, there is an additional descriptor that sends the + interrupt, handled in the same way as the above. + */ + // Clear done status flag. + m_table_virt_addr->header.flags[m_last_id - 1] = 0; // ID = m_last_id - 1 + + if (m_read) { + // descriptor[m_last_id]: write 0x1ULL to flags[m_last_id-1] which is used to indicate DMA done. + set_immediate_desc( // Set status bit + &(m_table_virt_addr->descriptors[m_last_id]), // descriptor[m_last_id] location in user space + m_table_dma_phys_addr + 4*(m_last_id - 1), // physical address for 0x1ULL to write (flags[m_last_id].. flag filed size is 4 byte) + 0x1ULL, + 255 + ); + } else { + // Need to fetch status desc into different destination. + // descriptor[m_last_id]: DMA Descriptor[m_last_id+1](32 byte) to WDP register set in DMA controller. + m_active_descriptor = &(m_table_virt_addr->descriptors[m_last_id]); + set_read_desc(m_table_dma_phys_addr + sizeof(DMA_DESC_HEADER) + (m_last_id + 1) * 32, // src: set_immediate_desc descriptor location + WRITE_DESC_PRIO_OFFSET + DESC_OFFSET, // des, location of WDP register set + 32/4 // copy 32-byte, 8 word + ); + + // descriptor[m_last_id+1]: write 0x1ULL(4-byte) to status[m_last_id-1] which is used to indicate DMA done. + set_immediate_desc( // Set status bit + &(m_table_virt_addr->descriptors[m_last_id + 1]), + m_table_dma_phys_addr + 4*(m_last_id - 1), //4: size per status entry + 0x1ULL, + 255 + ); + } + MemoryBarrier(); +} +#endif + +void ACL_PCIE_DMA::send_dma_desc() { + // Disabling interrupt is used in hostch_create function during polling +#if defined(GEN3_x8) + if (m_read) { + m_io->dma->write32(ACL_PCIE_DMA_RC_WR_DESC_BASE_LOW, m_table_dma_phys_addr & 0xffffffffUL); + m_io->dma->write32(ACL_PCIE_DMA_RC_WR_DESC_BASE_HIGH, m_table_dma_phys_addr >> 32); + m_io->dma->write32(ACL_PCIE_DMA_EP_WR_FIFO_BASE_LOW, ACL_PCIE_DMA_ONCHIP_WR_FIFO_BASE_LO); + m_io->dma->write32(ACL_PCIE_DMA_EP_WR_FIFO_BASE_HIGH, ACL_PCIE_DMA_ONCHIP_WR_FIFO_BASE_HI); + m_io->dma->write32(ACL_PCIE_DMA_WR_TABLE_SIZE, ACL_PCIE_DMA_TABLE_SIZE - 1); + if (m_interrupt_disabled) + m_io->dma->write32(ACL_PCIE_DMA_WR_INT_CONTROL, ACL_PCIE_DMA_DISABLE_INT); + else + m_io->dma->write32(ACL_PCIE_DMA_WR_INT_CONTROL, ACL_PCIE_DMA_ENABLE_INT); + MemoryBarrier(); + m_io->dma->write32(ACL_PCIE_DMA_WR_LAST_PTR, m_last_id - 1); + } else { + m_io->dma->write32(ACL_PCIE_DMA_RC_RD_DESC_BASE_LOW, m_table_dma_phys_addr & 0xffffffffUL); + m_io->dma->write32(ACL_PCIE_DMA_RC_RD_DESC_BASE_HIGH, m_table_dma_phys_addr >> 32); + m_io->dma->write32(ACL_PCIE_DMA_EP_RD_FIFO_BASE_LOW, ACL_PCIE_DMA_ONCHIP_RD_FIFO_BASE_LO); + m_io->dma->write32(ACL_PCIE_DMA_EP_RD_FIFO_BASE_HIGH, ACL_PCIE_DMA_ONCHIP_RD_FIFO_BASE_HI); + m_io->dma->write32(ACL_PCIE_DMA_RD_TABLE_SIZE, ACL_PCIE_DMA_TABLE_SIZE - 1); + if (m_interrupt_disabled) + m_io->dma->write32(ACL_PCIE_DMA_RD_INT_CONTROL, ACL_PCIE_DMA_DISABLE_INT); + else + m_io->dma->write32(ACL_PCIE_DMA_RD_INT_CONTROL, ACL_PCIE_DMA_ENABLE_INT); + MemoryBarrier(); + m_io->dma->write32(ACL_PCIE_DMA_RD_LAST_PTR, m_last_id - 1); + } +#elif defined(GEN3_x16) + DMA_DESC_ENTRY dt_fetch_desc; + UINT32 ctrl, *pValue32; + UINT64 dt_fetch_queue_addr64; + int i; + + add_extra_dma_desc(); + // init a descriptor for start dma + dt_fetch_desc.src_addr = m_table_dma_phys_addr + sizeof(DMA_DESC_HEADER); // physical addrees of first desciptor (assume dma always start from ID 0) + dt_fetch_desc.dst_addr = m_read ? WRITE_DESC_NORM_OFFSET : READ_DESC_NORM_OFFSET; + dt_fetch_desc.dst_addr += DESC_OFFSET; + ctrl = ((m_last_id - 1) + 2) * 8; // interrupt is not enabled case ... (ID+3)*8 if interrupted is enabled (note: ID = m_last_id-1) + ctrl |= 1 << 20; // Single destination + ctrl |= 0xFE << 24; // Special descriptor ID + dt_fetch_desc.ctrl = ctrl; + + dt_fetch_queue_addr64 = m_read ? READ_DESC_PRIO_OFFSET : READ_DESC_NORM_OFFSET; + pValue32 = (UINT32 *)(&dt_fetch_desc); + for (i = 0; i < 4; i++) { + m_io->dma->write32(DESC_CTRLLER_BASE + dt_fetch_queue_addr64 + i * 4, *(pValue32 + i)); + } + // Most significant DWord must be written last. + MemoryBarrier(); + m_io->dma->write32(DESC_CTRLLER_BASE + dt_fetch_queue_addr64 + 4 * 4,*(((uint32_t *)(&dt_fetch_desc)) + 4)); + MemoryBarrier(); +#else + #error "Define a PCIe 3.0/4.0/5.0 slot with x1, x2, x4, x8, and x16 lanes option" +#endif +} + +void ACL_PCIE_DMA::setup_dma_desc() { +#if defined(GEN3_x8) + m_io->dma->write32(ACL_PCIE_DMA_RC_WR_DESC_BASE_LOW, m_table_dma_phys_addr & 0xffffffffUL); + m_io->dma->write32(ACL_PCIE_DMA_RC_WR_DESC_BASE_HIGH, m_table_dma_phys_addr >> 32); + m_io->dma->write32(ACL_PCIE_DMA_EP_WR_FIFO_BASE_LOW, ACL_PCIE_DMA_ONCHIP_WR_FIFO_BASE_LO); + m_io->dma->write32(ACL_PCIE_DMA_EP_WR_FIFO_BASE_HIGH, ACL_PCIE_DMA_ONCHIP_WR_FIFO_BASE_HI); + m_io->dma->write32(ACL_PCIE_DMA_WR_TABLE_SIZE, ACL_PCIE_DMA_TABLE_SIZE - 1); + + m_io->dma->write32(ACL_PCIE_DMA_RC_RD_DESC_BASE_LOW, m_table_dma_phys_addr & 0xffffffffUL); + m_io->dma->write32(ACL_PCIE_DMA_RC_RD_DESC_BASE_HIGH, m_table_dma_phys_addr >> 32); + m_io->dma->write32(ACL_PCIE_DMA_EP_RD_FIFO_BASE_LOW, ACL_PCIE_DMA_ONCHIP_RD_FIFO_BASE_LO); + m_io->dma->write32(ACL_PCIE_DMA_EP_RD_FIFO_BASE_HIGH, ACL_PCIE_DMA_ONCHIP_RD_FIFO_BASE_HI); + m_io->dma->write32(ACL_PCIE_DMA_RD_TABLE_SIZE, ACL_PCIE_DMA_TABLE_SIZE - 1); +#endif +} + +void ACL_PCIE_DMA::set_read_desc(DMA_ADDR source, UINT64 dest, UINT32 ctl_dma_len) { +#if defined(GEN3_x8) + m_active_descriptor->src_addr_ldw = (source & 0xffffffffUL); + m_active_descriptor->src_addr_udw = (source >> 32); + m_active_descriptor->dest_addr_ldw = (dest & 0xffffffffUL); + m_active_descriptor->dest_addr_udw = (dest >> 32); + m_active_descriptor->ctl_dma_len = (ctl_dma_len | (m_last_id << 18)); + m_active_descriptor->reserved[0] = 0; + m_active_descriptor->reserved[1] = 0; + m_active_descriptor->reserved[2] = 0; +#elif defined(GEN3_x16) + m_active_descriptor->src_addr = source; + m_active_descriptor->dst_addr = dest; + m_active_descriptor->ctrl = (ctl_dma_len | (m_last_id << 24)); + m_active_descriptor->reserved[0] = 0; + m_active_descriptor->reserved[1] = 0; + m_active_descriptor->reserved[2] = 0; +#else + #error "Define a PCIe 3.0/4.0/5.0 slot with x1, x2, x4, x8, and x16 lanes option" +#endif +} + +void ACL_PCIE_DMA::set_write_desc(UINT64 source, DMA_ADDR dest, UINT32 ctl_dma_len) { +#if defined(GEN3_x8) + m_active_descriptor->src_addr_ldw = (source & 0xffffffffUL); + m_active_descriptor->src_addr_udw = (source >> 32); + m_active_descriptor->dest_addr_ldw = (dest & 0xffffffffUL); + m_active_descriptor->dest_addr_udw = (dest >> 32); + m_active_descriptor->ctl_dma_len = (ctl_dma_len | (m_last_id << 18)); + m_active_descriptor->reserved[0] = 0; + m_active_descriptor->reserved[1] = 0; + m_active_descriptor->reserved[2] = 0; +#elif defined(GEN3_x16) + set_read_desc(source, dest, ctl_dma_len); +#else + #error "Define a PCIe 3.0/4.0/5.0 slot with x1, x2, x4, x8, and x16 lanes option" +#endif +} + +#if defined(GEN3_x16) +void ACL_PCIE_DMA::set_immediate_desc(DMA_DESC_ENTRY *desc, UINT64 addr, UINT32 data, UINT32 id) { + uint32_t ctrl; + + desc->src_addr = data; // The data to write to given address + desc->dst_addr = addr; + ctrl = 1; // 1 DW status + ctrl |= 1 << 18; // Immediate access + ctrl |= id << 24; // Status descriptor ID + desc->ctrl = ctrl; + desc->reserved[0] = 0x0; + desc->reserved[1] = 0x0; + desc->reserved[2] = 0x0; +} +#endif + +void ACL_PCIE_DMA::set_hostch_page_entry(HOSTCH_ENTRY *page_entry, UINT64 page_addr, UINT32 page_num) { + page_entry->page_addr_ldw = (page_addr & 0xffffffffUL); + page_entry->page_addr_udw = (page_addr >> 32); + page_entry->page_num = page_num; + page_entry->reserved[0] = 0; + page_entry->reserved[1] = 0; + page_entry->reserved[2] = 1; + page_entry->reserved[3] = 0; + page_entry->reserved[4] = 0; +} + +void ACL_PCIE_DMA::set_desc_table_header() { + int i; + for (i = 0; i < ACL_PCIE_DMA_DESC_MAX_ENTRIES; i++) m_table_virt_addr->header.flags[i] = 0; +} + +// Perform operations required when a DMA interrupt comes +void ACL_PCIE_DMA::service_interrupt() { + if (!m_use_polling) { + // only submit a new work to the pool when there is not work in queued + if (WaitForSingleObject(m_workqueue_semaphore, 0L) == WAIT_OBJECT_0) { + set_desc_table_header(); + SubmitThreadpoolWork(m_work); + } + } +} + +void ACL_PCIE_DMA::spin_loop_ns(UINT64 wait_ns) { + cl_ulong start = m_timer->get_time_ns(); + cl_ulong finish; + + do { + finish = m_timer->get_time_ns(); + } while (finish - start < wait_ns); +} + +void ACL_PCIE_DMA::check_last_id(UINT32 *last_id) { + ACL_PCIE_ASSERT(*last_id <= (ACL_PCIE_DMA_RESET_ID + 1), "last id was greater than 255.\n"); + + if (*last_id == (ACL_PCIE_DMA_RESET_ID + 1)) { + *last_id = 0; + return; + } else if (*last_id == ACL_PCIE_DMA_TABLE_SIZE) { + *last_id = 0; + return; + } + ACL_PCIE_ASSERT(*last_id < (ACL_PCIE_DMA_TABLE_SIZE), "last id was greater than 127.\n"); +} + +// Relinquish the CPU to let any other thread to run +// Return 0 since there is no useful work to be performed here +int ACL_PCIE_DMA::yield() { + Sleep(0); + return 0; +} + +// Add a byte-offset to a void* pointer +inline void *ACL_PCIE_DMA::compute_address(void *base, uintptr_t offset) { + uintptr_t p = reinterpret_cast<uintptr_t>(base); + return reinterpret_cast<void *>(p + offset); +} + +int ACL_PCIE_DMA::hostch_buffer_lock(void *addr, size_t len, PINNED_MEM *new_mem) { + fpga_result FPGA_status; + UINT64 wsid; + + // No active segment of pinned memory - pin one + + // Lock HOSTCH_TABLE using WsId + FPGA_status = fpgaPrepareBuffer(m_handle, (UINT64)len, (PVOID *)&addr, &wsid, FPGA_BUF_PREALLOCATED); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "HostCh : fpgaPrepareBuffer function for Hostchannel failed.\n"); + + // Obtain Physical address for the Page associated with the buffer + FPGA_status = fpgaGetPhysicalAddress(m_handle, wsid, (PUINT64)&new_mem->pages_rem, NULL); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "HostCh : fpgaGetPhysicalAddress function for Hostchannel failed.\n"); + + new_mem->dma_page = (sg_element *)malloc(new_mem->pages_rem * sizeof(sg_element)); + + FPGA_status = fpgaGetPhysicalAddress(m_handle, wsid, (PUINT64)&new_mem->pages_rem, (void *)new_mem->dma_page); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "HostCh : fpgaGetPhysicalAddress function for Hostchannel failed.\n"); + + new_mem->WsId = wsid; + new_mem->UsrVa = (PVOID)addr; + new_mem->next_page = new_mem->dma_page; + + // IOCTL call to flush CPU buffers + FPGA_status = + fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_CPU_BUFFERS), (PVOID)&wsid, NULL, 0); + ACL_PCIE_ASSERT(FPGA_status == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh Pinning 0x%zx bytes at 0x%p.\n", len, addr); + + return 0; +} + +// Only 1 pin_memory can be running at a time +void ACL_PCIE_DMA::pin_memory(PINNED_MEM *new_mem, bool prepin) { + fpga_result result; + UINT64 wsid = 0x0; + + // No active segment of pinned memory - pin one + m_bytes_rem = prepin ? (m_bytes_rem - m_last_pinned_size) : (m_bytes - m_bytes_sent); + UINT32 last_id = prepin ? 0 : m_last_id; + check_last_id(&last_id); + size_t last_id_size_offset = last_id * PAGE_SIZE; + size_t lock_size = (m_bytes_rem > ACL_PCIE_DMA_MAX_PINNED_MEM_SIZE - last_id_size_offset) + ? ACL_PCIE_DMA_MAX_PINNED_MEM_SIZE - last_id_size_offset + : m_bytes_rem; + void *lock_addr = + prepin ? compute_address(m_last_pinned_addr, m_last_pinned_size) : compute_address(m_host_addr, m_bytes_sent); + uintptr_t last_page_portion = (reinterpret_cast<uintptr_t>(lock_addr) + lock_size) & ACL_PCIE_DMA_PAGE_ADDR_MASK; + + // If doing max pinning, check if will *end* on page boundary. If not, better + // to pin a bit less and end up on the boundary. This way, will have fewer + // descriptors to send. + if (lock_size == (ACL_PCIE_DMA_MAX_PINNED_MEM_SIZE - last_id_size_offset) && last_page_portion != 0) { + lock_size -= (size_t)last_page_portion; + } + + assert(lock_size < MAXDWORD); + + // Lock memory using WsId + result = fpgaPrepareBuffer(m_handle, (UINT64)lock_size, (PVOID *)&lock_addr, &wsid, FPGA_BUF_PREALLOCATED); + ACL_PCIE_ASSERT(result == FPGA_OK, "HostCh : fpgaPrepareBuffer function failed.\n"); + + // Obtain Physical address for the Page associated with the buffer + result = fpgaGetPhysicalAddress(m_handle, wsid, (PUINT64)&new_mem->pages_rem, NULL); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + new_mem->dma_page = (sg_element *)malloc(new_mem->pages_rem * sizeof(sg_element)); + + result = fpgaGetPhysicalAddress(m_handle, wsid, (PUINT64)&new_mem->pages_rem, (void *)new_mem->dma_page); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaGetPhysicalAddress function failed.\n"); + + new_mem->WsId = wsid; + new_mem->UsrVa = (PVOID)lock_addr; + new_mem->next_page = new_mem->dma_page; + + // IOCTL call to flush CPU buffers + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_CPU_BUFFERS), (PVOID)&wsid, NULL, 0); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + m_last_pinned_size = lock_size; + m_last_pinned_addr = lock_addr; + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Pinning 0x%zx bytes at 0x%p.\n", lock_size, lock_addr); +} + +// Unpin Memory +void ACL_PCIE_DMA::unpin_memory(PINNED_MEM *old_mem) { + fpga_result result = FPGA_OK; + UINT64 wsId = old_mem->WsId; + + // IOCTL call to flush I/O buffers + result = fpgaProcessDeviceCmd(m_handle, GUID_TO_FPGA_GUID(GUID_PCI_OPENCL_SYNC_IO_BUFFERS), (PVOID)&wsId, NULL, 0); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaProcessDeviceCmd function failed.\n"); + + // UnLock previously locked memory using WsId + result = fpgaReleaseBuffer(m_handle, wsId); + ACL_PCIE_ASSERT(result == FPGA_OK, "fpgaReleaseBuffer function failed.\n"); + + if (old_mem->dma_page != NULL) free(old_mem->dma_page); + + old_mem->next_page = NULL; + old_mem->dma_page = NULL; + old_mem->pages_rem = 0; + old_mem->UsrVa = NULL; +} + +// Check if user's 'ack' API updated end pointer of circular buf +// Update end pointer in IP +int ACL_PCIE_DMA::hostch_push_update() { + HOSTCH_DESC *h = &hostch_data; + + if (h->rd_buf_end_pointer != *h->user_rd_end_pointer) { + h->rd_buf_end_pointer = *h->user_rd_end_pointer; + } else { + h->loop_counter = (h->loop_counter > 0) ? h->loop_counter - 1 : h->loop_counter; + return 1; + } + h->loop_counter = HOSTCH_LOOP_COUNTER; + + m_io->dma->write32(ACL_HOST_CHANNEL_0_HOST_ENDP, (UINT32)h->rd_buf_end_pointer); + + return 0; +} + +// Check if user's 'ack' API updated front pointer of circular buf +// Update end pointer in IP +int ACL_PCIE_DMA::hostch_pull_update() { + HOSTCH_DESC *h = &hostch_data; + + if (h->wr_buf_front_pointer != *h->user_wr_front_pointer) { + h->wr_buf_front_pointer = *h->user_wr_front_pointer; + } else { + h->loop_counter = (h->loop_counter > 0) ? h->loop_counter - 1 : h->loop_counter; + return 1; + } + h->loop_counter = HOSTCH_LOOP_COUNTER; + + m_io->dma->write32(ACL_HOST_CHANNEL_1_HOST_FRONTP, (UINT32)h->wr_buf_front_pointer); + return 0; +} + +// Transfer data between host and device +// This function returns right after the transfer is scheduled +// Return 0 on success +int ACL_PCIE_DMA::read_write(void *host_addr, size_t dev_addr, size_t bytes, aocl_mmd_op_t e, bool reading) { + ACL_PCIE_ASSERT(m_event == NULL, "non-empty event before a new DMA read/write.\n"); + + // Copy the parameters over and mark the job as running + m_event = e; + m_read = reading; + m_bytes = bytes; + m_host_addr = host_addr; + m_dev_addr = dev_addr; + + // Start processing the request + m_bytes_sent = 0; + m_last_id = ACL_PCIE_DMA_RESET_ID; + m_prepinned = 0; + +#if defined(GEN3_x8) + if (m_read) { + m_io->dma->read32(ACL_PCIE_DMA_WR_LAST_PTR, &m_last_id); + m_last_id++; + } else { + m_io->dma->read32(ACL_PCIE_DMA_RD_LAST_PTR, &m_last_id); + m_last_id++; + } + +#elif defined(GEN3_x16) + m_last_id = 0; +#else + #error "Define a PCIe 3.0/4.0/5.0 slot with x1, x2, x4, x8, and x16 lanes option" +#endif + m_idle = false; + + // setup the work inside the threadpool to perform the first DMA transaction + ACL_PCIE_ERROR_IF(WaitForSingleObject(m_workqueue_semaphore, 0L) != WAIT_OBJECT_0, + return -1, + "failed to schedule the first work for DMA read/write.\n"); + + SubmitThreadpoolWork(m_work); + + return 0; // success +} + +// function to be scheduled to execute whenever an interrupt arrived +bool ACL_PCIE_DMA::update(bool forced) { + cl_ulong start; + int status; + UINT32 max_transfer; + unsigned int i; + HOSTCH_DESC *h = &hostch_data; + size_t current_transfer_size = 0; + + if (!forced) return false; + + if (h->pull_valid && m_idle) { + // Check user memory to see if there was update to user buffer pointer for pull + status = hostch_pull_update(); + } + + if (h->push_valid && m_idle) { + // Check user memory to see if there was update to user buffer pointer for push + status = hostch_push_update(); + } + + if ((h->push_valid | h->pull_valid) && m_idle && (h->thread_sync_valid && h->loop_counter > 0)) { + // setup the work inside the threadpool to perform the first DMA transaction + ACL_PCIE_ERROR_IF(WaitForSingleObject(m_workqueue_semaphore, 0L) != WAIT_OBJECT_0, + return false, + "HostCh : failed to schedule the first work for DMA read/write.\n"); + SubmitThreadpoolWork(m_work); + return false; + + } else if (m_idle && (h->thread_sync_valid && h->loop_counter == 0)) { + *h->user_thread_sync = 0; + return false; + + } else if (m_idle) { + return false; + } + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Bytes left %zu\n", m_bytes - m_bytes_sent); + // Process any descriptors that have completed + set_desc_table_header(); + cl_ulong finish = 0; + if (ACL_PCIE_DEBUG >= VERBOSITY_BLOCKTX) finish = m_timer->get_time_ns(); + + // Check if the transaction is complete + if (m_bytes_sent == m_bytes) { + if (m_active_mem.UsrVa != NULL) unpin_memory(&m_active_mem); + ACL_PCIE_DMA_DEBUG(":::: [DMA] Transaction complete!\n"); + ACL_PCIE_ASSERT(m_active_mem.UsrVa == NULL, "there is still active pinned memory after the DMA read/write.\n"); + WaitForThreadpoolWorkCallbacks(m_unpin_work, false); + if (!m_dma_unpin_pending.empty()) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] Done, but pinned memory still in queue. Wait until queue is empty.\n"); + if (WaitForSingleObject(m_workqueue_semaphore, 0L) == WAIT_OBJECT_0) { + SubmitThreadpoolWork(m_work); + } + + Sleep(0); + return true; + } + + m_last_id = ACL_PCIE_DMA_RESET_ID; + m_idle = true; + + if (m_event) { + // Use a temporary variable to save the event data and reset m_event before calling event_update_fn + // to avoid race condition that the main thread may start a new DMA transfer before this work-thread + // is able to reset the m_event. + aocl_mmd_op_t temp_event = m_event; + m_event = NULL; + + m_pcie->event_update_fn(temp_event, 0); + } + + if ((h->push_valid | h->pull_valid) && (h->thread_sync_valid && h->loop_counter > 0)) { + ACL_PCIE_ERROR_IF(WaitForSingleObject(m_workqueue_semaphore, 0L) != WAIT_OBJECT_0, + return false, + "HostCh : failed to schedule the first work for DMA read/write.\n"); + SubmitThreadpoolWork(m_work); + } + + return true; + } + + // Check if we are done with previously pinned memory. + if (m_active_mem.UsrVa == NULL || m_active_mem.pages_rem == 0) { + m_done_mem = m_active_mem; + + WaitForThreadpoolWorkCallbacks(m_pin_work, false); + + // Get pre-pinned memory if there are any. + if (m_pre_pinned_mem.UsrVa != NULL) { + m_active_mem = m_pre_pinned_mem; + m_pre_pinned_mem.UsrVa = NULL; + m_prepinned = 0; + } else if (m_prepinned) { + if (WaitForSingleObject(m_workqueue_semaphore, 0L) == WAIT_OBJECT_0) { + SubmitThreadpoolWork(m_work); + } + Sleep(1); + return true; + } else { + pin_memory(&m_active_mem, false); + } + } + + // Main DMA execution + // 1. Transfers up to 128 descriptors + // - Each descriptor can transfer up to ACL_PCIE_DMA_MAX_TRANSFER_SIZE bytes + // 2. Launch a thread to unpin memory + // 3. Launch a thread to pre-pin next memory + if (m_active_mem.pages_rem > 0) { + // Calculate how many descriptors can be sent + check_last_id(&m_last_id); + ACL_PCIE_DMA_DEBUG(":::: [DMA] last id was %u\n", m_last_id); + max_transfer = ACL_PCIE_DMA_TABLE_SIZE - m_last_id; + + ACL_PCIE_DMA_DEBUG(":::: [DMA] max_transfer %u\n", max_transfer); + + // Build descriptor table + for (i = 0; i < max_transfer; i++) { + if (strcmp(ACL_BSP_TYPE, "Arria10") == 0) { + // A10 DMA + m_active_descriptor = &(m_table_virt_addr->descriptors[i]); + }; + if (strcmp(ACL_BSP_TYPE, "Stratix10") == 0) { + // S10 DMA + m_active_descriptor = &(m_table_virt_addr->descriptors[m_last_id]); + }; + if (m_read) { + if (m_active_mem.next_page->length > ACL_PCIE_DMA_MAX_TRANSFER_SIZE) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] page size is larger than %u for read. Page size is %u bytes\n", + ACL_PCIE_DMA_MAX_TRANSFER_SIZE, + m_active_mem.next_page->length); + set_write_desc(m_dev_addr, m_active_mem.next_page->phys_addr, ACL_PCIE_DMA_MAX_TRANSFER_SIZE / 4); + m_active_mem.next_page->length -= ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_active_mem.next_page->phys_addr += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_dev_addr += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_bytes_sent += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + current_transfer_size += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + } else { + set_write_desc(m_dev_addr, m_active_mem.next_page->phys_addr, m_active_mem.next_page->length / 4); + m_dev_addr += m_active_mem.next_page->length; + m_bytes_sent += m_active_mem.next_page->length; + current_transfer_size += m_active_mem.next_page->length; + ++m_active_mem.next_page; + m_active_mem.pages_rem--; + } + } else { + if (m_active_mem.next_page->length > ACL_PCIE_DMA_MAX_TRANSFER_SIZE) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] page size is larger than %u for write. Page size is %u bytes\n", + ACL_PCIE_DMA_MAX_TRANSFER_SIZE, + m_active_mem.next_page->length); + set_read_desc(m_active_mem.next_page->phys_addr, m_dev_addr, ACL_PCIE_DMA_MAX_TRANSFER_SIZE / 4); + m_active_mem.next_page->length -= ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_active_mem.next_page->phys_addr += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_dev_addr += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + m_bytes_sent += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + current_transfer_size += ACL_PCIE_DMA_MAX_TRANSFER_SIZE; + } else { + set_read_desc(m_active_mem.next_page->phys_addr, m_dev_addr, m_active_mem.next_page->length / 4); + m_dev_addr += m_active_mem.next_page->length; + m_bytes_sent += m_active_mem.next_page->length; + current_transfer_size += m_active_mem.next_page->length; + ++m_active_mem.next_page; + m_active_mem.pages_rem--; + } + } + m_last_id++; + if (m_active_mem.pages_rem == 0) break; + } + ACL_PCIE_DMA_DEBUG(":::: [DMA] Transferring %zu bytes using %u descriptors\n", current_transfer_size, i); + + MemoryBarrier(); + // Send descriptor table to DMA + start = m_timer->get_time_ns(); + m_interrupt_disabled = FALSE; + send_dma_desc(); + int pinning = 0; + int unpinning = 0; + cl_ulong unpin_start = 0, unpin_finish = 0; + + // Launch unpin thread + if (m_done_mem.UsrVa != NULL) { + unpin_start = m_timer->get_time_ns(); + unpinning = 1; + + // wait for previous unpin to finish + WaitForThreadpoolWorkCallbacks(m_unpin_work, false); + + QUEUE_STRUCT entry; + + entry.WsId = m_done_mem.WsId; + entry.SGListPtr = (PVOID)(m_done_mem.dma_page); + + m_dma_unpin_pending.push(entry); + + // Make sure Push into unpin queue comes before launching unpin thread + MemoryBarrier(); + + // Launch unpin thread + SubmitThreadpoolWork(m_unpin_work); + + m_done_mem.next_page = NULL; + + // if (m_done_mem.dma_page != NULL) + // free(m_done_mem.dma_page); + + m_done_mem.dma_page = NULL; + + m_done_mem.UsrVa = NULL; + unpin_finish = m_timer->get_time_ns(); + } + + // Launch pre-pin thread + cl_ulong pin_start = 0, pin_finish = 0; + if (((m_bytes_rem - m_last_pinned_size) > 0) && (m_prepinned == 0)) { + pin_start = m_timer->get_time_ns(); + pinning = 1; + m_prepinned = 1; + + // This wait should pass right through. + // There is another wait above, before switching active and prepin memory + WaitForThreadpoolWorkCallbacks(m_pin_work, false); + SubmitThreadpoolWork(m_pin_work); + pin_finish = m_timer->get_time_ns(); + } + + if (m_use_polling) { + wait_finish(); + finish = m_timer->get_time_ns(); + ACL_PCIE_DMA_DEBUG( + ":::: [DMA] Transfer (%zu bytes) completed in %.2f us - %.2f MB/s :: pinning %i in %.2f us :: unpinning %i " + "in %.2f us :: pages rem %li\n", + current_transfer_size, + (finish - start) / 1000.0, + 1000000000.0 * current_transfer_size / (finish - start) / (1024.0 * 1024.0), + pinning, + (pin_finish - pin_start) / 1000.0, + unpinning, + (unpin_finish - unpin_start) / 1000.0, + m_active_mem.pages_rem); + } + + return true; + } + + ACL_PCIE_DMA_DEBUG(":::: [DMA] Nothing happened\n"); + return true; +} + +// Poll DMA transfer +// Only used during host channel create +// Used to transfer the page table of pinned down MMD circular buffer to host channel IP +// The size of this transfer is known to be small +void ACL_PCIE_DMA::poll_wait() { + UINT32 wait_timer; + + while (1) { + wait_timer = ACL_PCIE_DMA_TIMEOUT; + while (wait_timer > 0) { + wait_timer--; + + if (m_table_virt_addr->header.flags[m_last_id - 1] == 1) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh : Wait done\n"); + set_desc_table_header(); +#if defined(GEN3_x8) + if (m_read) + m_io->dma->write32(ACL_PCIE_DMA_WR_INT_CONTROL, ACL_PCIE_DMA_ENABLE_INT); + else + m_io->dma->write32(ACL_PCIE_DMA_RD_INT_CONTROL, ACL_PCIE_DMA_ENABLE_INT); +#endif + m_interrupt_disabled = FALSE; + + return; + } + // Delay the CPU from checking the memory for 1us. CPU is still running this thread. + // but reduces memory access from CPU + spin_loop_ns(1000); + } + + // If DMA hasn't finished yet, free up the CPU for 1ms + ACL_PCIE_DMA_DEBUG( + ":::: [DMA] HostCh : Poll wait failed while transferring host channel page table to IP. Sleeping for 1ms.\n"); + Sleep(1); + } +} + +// Set IP's parameters for host channel. +// Parameters are txs address to write updated front/end pointer to on host memory, +// Address to DMA data to, to stream data into kernel +void ACL_PCIE_DMA::hostch_start(int channel) { + HOSTCH_DESC *h = &hostch_data; + + if (channel == (int)ACL_HOST_CHANNEL_0_ID) { + // Fix this Line + h->user_rd_front_pointer_bus_addr = h->m_hostch_rd_pointer.dma_page[0].phys_addr; + + m_io->dma->write32(ACL_HOST_CHANNEL_0_TXS_ADDR_LOW, h->user_rd_front_pointer_bus_addr & 0xffffffffUL); + m_io->dma->write32(ACL_HOST_CHANNEL_0_TXS_ADDR_HIGH, (h->user_rd_front_pointer_bus_addr) >> 32); + m_io->dma->write32(ACL_HOST_CHANNEL_0_IP_ADDR_LOW, ACL_HOST_CHANNEL_0_DMA_ADDR & 0xffffffffUL); + m_io->dma->write32(ACL_HOST_CHANNEL_0_IP_ADDR_HIGH, ACL_HOST_CHANNEL_0_DMA_ADDR >> 32); + m_io->dma->write32(ACL_HOST_CHANNEL_0_BUF_SIZE, (UINT32)h->buffer_size); + m_io->dma->write32(ACL_HOST_CHANNEL_0_HOST_ENDP, 0); + m_io->dma->write32(ACL_HOST_CHANNEL_0_LOGIC_EN, 1); + + } else if (channel == (int)ACL_HOST_CHANNEL_1_ID) { + h->user_wr_end_pointer_bus_addr = h->m_hostch_wr_pointer.dma_page[0].phys_addr + sizeof(size_t); + + m_io->dma->write32(ACL_HOST_CHANNEL_1_TXS_ADDR_LOW, h->user_wr_end_pointer_bus_addr & 0xffffffffUL); + m_io->dma->write32(ACL_HOST_CHANNEL_1_TXS_ADDR_HIGH, (h->user_wr_end_pointer_bus_addr) >> 32); + m_io->dma->write32(ACL_HOST_CHANNEL_1_IP_ADDR_LOW, ACL_HOST_CHANNEL_1_DMA_ADDR & 0xffffffffUL); + m_io->dma->write32(ACL_HOST_CHANNEL_1_IP_ADDR_HIGH, ACL_HOST_CHANNEL_1_DMA_ADDR >> 32); + m_io->dma->write32(ACL_HOST_CHANNEL_1_BUF_SIZE, (UINT32)h->buffer_size); + m_io->dma->write32(ACL_HOST_CHANNEL_1_HOST_FRONTP, 0); + m_io->dma->write32(ACL_HOST_CHANNEL_1_LOGIC_EN, 1); + } +} + +void ACL_PCIE_DMA::hostch_thread_sync(void *user_addr) { + int status; + HOSTCH_DESC *h = &hostch_data; + + if ((user_addr == NULL) & (h->thread_sync_valid)) { + if ((h->push_valid | h->pull_valid) && m_idle && (*h->user_thread_sync == 0)) { + h->loop_counter = HOSTCH_LOOP_COUNTER; + SubmitThreadpoolWork(m_work); + *h->user_thread_sync = 1; + } + } else { + status = hostch_buffer_lock(user_addr, sizeof(size_t), &(h->m_sync_thread_pointer)); + h->user_thread_sync = (size_t *)h->m_sync_thread_pointer.UsrVa; + h->loop_counter = HOSTCH_LOOP_COUNTER; + *h->user_thread_sync = 0; + h->thread_sync_valid = 1; + } +} + +int ACL_PCIE_DMA::hostch_create(void *user_addr, void *buf_pointer, size_t size, int channel) { + int status; + uint32_t i; + HOSTCH_DESC *h = &hostch_data; + + DMA_ADDR dma_address; + h->buffer_size = size; + + setup_dma_desc(); +#if defined(GEN3_x8) + m_io->dma->read32(ACL_PCIE_DMA_RD_LAST_PTR, &m_last_id); + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh: read dma_rd_last_id %u\n", (unsigned)m_last_id); + + // Set variables before calling dma helper functions + m_last_id++; +#endif + m_read = 0; + + // Only create push channel if it's not already open + if ((int)ACL_HOST_CHANNEL_0_ID == channel && !h->push_valid) { + h->user_rd_buffer = user_addr; + + // Pin push user buffer + status = hostch_buffer_lock(user_addr, size, &(h->m_hostch_rd_mem)); + status |= hostch_buffer_lock(buf_pointer, 2 * sizeof(size_t), &(h->m_hostch_rd_pointer)); + + // Map circular push buffer's end pointer so that the driver can poll on it for update from user space + h->user_rd_front_pointer = (size_t *)h->m_hostch_rd_pointer.UsrVa; + h->user_rd_end_pointer = h->user_rd_front_pointer + 1; + + // Send the circular push buffer's pinned address to IP, so IP can initiate DMA transfer by itself. + for (i = 0; i < (size / PAGE_SIZE); i++) { + dma_address = h->m_hostch_rd_mem.next_page->phys_addr; + set_hostch_page_entry(&(h->push_page_table->page_entry[i]), (UINT64)dma_address, (UINT32)i); + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh: push page entry[%u] = %#016llx size = %#016x\n", + (unsigned)i, + (UINT64)dma_address, + h->m_hostch_rd_mem.next_page->length); + + // Make 4KB pages from an array of pages of m_hostch_rd_mem + if (h->m_hostch_rd_mem.next_page->length == PAGE_SIZE) { + ++h->m_hostch_rd_mem.next_page; + h->m_hostch_rd_mem.pages_rem--; + } else { + h->m_hostch_rd_mem.next_page->length -= PAGE_SIZE; + h->m_hostch_rd_mem.next_page->phys_addr += PAGE_SIZE; + } + } + + set_desc_table_header(); + check_last_id(&m_last_id); + +#if defined(GEN3_x8) + // Set variable before calling dma helper functions + m_active_descriptor = &(m_table_virt_addr->descriptors[0]); + set_read_desc( + h->push_page_table_bus_addr, (UINT64)(ACL_PCIE_DMA_RD_FIFO_BASE), (UINT32)((32 * size / PAGE_SIZE) / 4)); + m_last_id++; + + // Read Interrupt will be disabled from send_dma_desc till poll_wait + m_interrupt_disabled = TRUE; + send_dma_desc(); + poll_wait(); +#endif + + // Reset and enable the push channel on IP + UINT32 data; + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PUSH + HOSTCH_BASE, 0); + m_io->pcie_cra->read32(HOSTCH_CONTROL_ADDR_PUSH + HOSTCH_BASE, &data); + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PUSH + HOSTCH_BASE, 1); + m_io->pcie_cra->read32(HOSTCH_CONTROL_ADDR_PUSH + HOSTCH_BASE, &data); + + // Set IP's control registers for push channel + hostch_start((int)ACL_HOST_CHANNEL_0_ID); + + h->push_valid = 1; + + // Only launch queue if pull channel is not open and if there is no DMA transfer + if (!h->pull_valid && m_idle) { + ACL_PCIE_ERROR_IF(WaitForSingleObject(m_workqueue_semaphore, 0L) != WAIT_OBJECT_0, + return -1, + "HostCh : failed to schedule the first work for DMA read/write.\n"); + SubmitThreadpoolWork(m_work); + } + return 0; + + } else if ((int)ACL_HOST_CHANNEL_1_ID == channel && !h->pull_valid) { + h->user_wr_buffer = user_addr; + + // Pin pull user buffer + status = hostch_buffer_lock(user_addr, size, &(h->m_hostch_wr_mem)); + status |= hostch_buffer_lock(buf_pointer, 2 * sizeof(size_t), &(h->m_hostch_wr_pointer)); + + // Map circular pull buffer's end pointer so that the driver can poll on it for update from user space + h->user_wr_front_pointer = (size_t *)h->m_hostch_wr_pointer.UsrVa; + h->user_wr_end_pointer = h->user_wr_front_pointer + 1; + + // Send the circular pull buffer's pinned address to IP, so IP can initiate DMA transfer by itself. + for (i = 0; i < (size / PAGE_SIZE); i++) { + dma_address = h->m_hostch_wr_mem.next_page->phys_addr; + set_hostch_page_entry(&(h->pull_page_table->page_entry[i]), (UINT64)dma_address, (UINT32)i); + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh: pull page entry[%u] = %#016llx size = %#016x\n", + (unsigned)i, + (UINT64)dma_address, + h->m_hostch_wr_mem.next_page->length); + + // Make 4KB pages from an array of pages of m_hostch_wr_mem + if (h->m_hostch_wr_mem.next_page->length == PAGE_SIZE) { + ++h->m_hostch_wr_mem.next_page; + h->m_hostch_wr_mem.pages_rem--; + } else { + h->m_hostch_wr_mem.next_page->length -= PAGE_SIZE; + h->m_hostch_wr_mem.next_page->phys_addr += PAGE_SIZE; + } + } + + set_desc_table_header(); + check_last_id(&m_last_id); + +#if defined(GEN3_x8) + // Set variable before calling dma helper functions + m_active_descriptor = &(m_table_virt_addr->descriptors[0]); + set_read_desc( + h->pull_page_table_bus_addr, (UINT64)(ACL_PCIE_DMA_WR_FIFO_BASE), (UINT32)((32 * size / PAGE_SIZE) / 4)); + m_last_id++; + + // Read Interrupt will be disabled from send_dma_desc till poll_wait + m_interrupt_disabled = TRUE; + send_dma_desc(); + poll_wait(); +#endif + + // Reset and enable the pull channel on IP + UINT32 temp; + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PULL + HOSTCH_BASE, 0); + m_io->pcie_cra->read32(HOSTCH_CONTROL_ADDR_PULL + HOSTCH_BASE, &temp); + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PULL + HOSTCH_BASE, 1); + m_io->pcie_cra->read32(HOSTCH_CONTROL_ADDR_PULL + HOSTCH_BASE, &temp); + + // Set IP's control registers for pull channel + hostch_start((int)ACL_HOST_CHANNEL_1_ID); + + h->pull_valid = 1; + + // Only launch queue if push channel is not open and if there is no DMA transfer + if (!h->push_valid && m_idle) { + ACL_PCIE_ERROR_IF(WaitForSingleObject(m_workqueue_semaphore, 0L) != WAIT_OBJECT_0, + return -1, + "HostCh : failed to schedule the first work for DMA read/write.\n"); + SubmitThreadpoolWork(m_work); + } + return 0; + + } else { + return ERROR_INVALID_CHANNEL; + } +} + +// Destroy channel call from user. +// Unlock all buffers and reset IP +int ACL_PCIE_DMA::hostch_destroy(int channel) { + HOSTCH_DESC *h = &hostch_data; + + if ((int)ACL_HOST_CHANNEL_0_ID == channel) { + if (h->push_valid) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh: destroying push host channel."); + m_io->dma->write32(ACL_HOST_CHANNEL_0_LOGIC_EN, 0); + MemoryBarrier(); + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PUSH + HOSTCH_BASE, 0); + MemoryBarrier(); + + if (h->m_hostch_rd_mem.UsrVa != NULL) unpin_memory(&h->m_hostch_rd_mem); + if (h->m_hostch_rd_pointer.UsrVa != NULL) unpin_memory(&h->m_hostch_rd_pointer); + h->push_valid = 0; + + if (!h->pull_valid) { + if (h->thread_sync_valid) { + h->thread_sync_valid = 0; + if (h->m_sync_thread_pointer.UsrVa != NULL) unpin_memory(&h->m_sync_thread_pointer); + } + if (m_idle) WaitForThreadpoolWorkCallbacks(m_work, false); + } + } + } else if ((int)ACL_HOST_CHANNEL_1_ID == channel) { + if (h->pull_valid) { + ACL_PCIE_DMA_DEBUG(":::: [DMA] HostCh: destroying pull host channel."); + m_io->dma->write32(ACL_HOST_CHANNEL_1_LOGIC_EN, 0); + MemoryBarrier(); + m_io->pcie_cra->write32(HOSTCH_CONTROL_ADDR_PULL + HOSTCH_BASE, 0); + MemoryBarrier(); + + if (h->m_hostch_wr_mem.UsrVa != NULL) unpin_memory(&h->m_hostch_wr_mem); + if (h->m_hostch_wr_pointer.UsrVa != NULL) unpin_memory(&h->m_hostch_wr_pointer); + h->pull_valid = 0; + + if (!h->push_valid) { + if (h->thread_sync_valid) { + h->thread_sync_valid = 0; + if (h->m_sync_thread_pointer.UsrVa != NULL) unpin_memory(&h->m_sync_thread_pointer); + } + if (m_idle) WaitForThreadpoolWorkCallbacks(m_work, false); + } + } + } + + return 0; +} + +#endif // WINDOWS diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.h new file mode 100644 index 0000000..311c634 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_dma_windows.h @@ -0,0 +1,262 @@ +#ifndef ACL_PCIE_DMA_WINDOWS_H +#define ACL_PCIE_DMA_WINDOWS_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_dma_windows.h --------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle Windows-specific DMA operations. */ +/* The actual implementation of the class lives in the acl_pcie_dma_windows.cpp */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +// TODO: update DMA related stuff and add wsid + +#if defined(WINDOWS) + +#include "hw_host_channel.h" +#include "hw_pcie_dma.h" + +#include <windows.h> +#include <queue> + +class ACL_PCIE_DEVICE; +class ACL_PCIE_MM_IO_MGR; +class ACL_PCIE_TIMER; + +typedef struct _PAGE_INFO { + ULONG64 pPhysicalAddr; + UINT32 dwBytes; +} PAGE_INFO, *PPAGE_INFO; + +typedef struct _DMA_PAGE { + sg_element *Page; + DWORD dwPages; + UINT64 WsId; +} DMA_PAGE, *PDMA_PAGE; + +typedef struct _QUEUE_STRUCT { + UINT64 WsId; + PVOID SGListPtr; + +} QUEUE_STRUCT, *PQUEUE_STRUCT; + +class ACL_PCIE_DMA { + public: + ACL_PCIE_DMA(fpga_handle Handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie); + ~ACL_PCIE_DMA(); + + bool is_idle() { return m_idle; }; + void stall_until_idle() { + while (!is_idle()) yield(); + }; + + // Called by acl_pcie_device to check dma interrupt status + int check_dma_interrupt(unsigned int *dma_update); + + // Perform operations required when a DMA interrupt comes + void service_interrupt(); + + // Relinquish the CPU to let any other thread to run + // Return 0 since there is no useful work to be performed here + int yield(); + + // Transfer data between host and device + // This function returns right after the transfer is scheduled + // Return 0 on success + int read_write(void *host_addr, size_t dev_addr, size_t bytes, aocl_mmd_op_t e, bool reading); + + // the callback function to be scheduled inside the interrupt handler + friend void CALLBACK myWorkCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work); + + // Seperate function to unpin memory + friend void CALLBACK myWorkUnpinCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work); + + // Seperate function to pin memory + friend void CALLBACK myWorkPinCallback(PTP_CALLBACK_INSTANCE instance, void *context, PTP_WORK work); + + // Host channel functions + int hostch_create(void *user_addr, void *buf_pointer, size_t size, int reading); + int hostch_destroy(int reading); + void hostch_thread_sync(void *m_sync_thread); + + private: + ACL_PCIE_DMA &operator=(const ACL_PCIE_DMA &) { return *this; } + + ACL_PCIE_DMA(const ACL_PCIE_DMA &src) {} + + struct PINNED_MEM { + sg_element *next_page; + DWORD pages_rem; + sg_element *dma_page; // Pointer to the original array + UINT64 WsId; + PVOID UsrVa; + }; + + struct HOSTCH_DESC { + size_t buffer_size; + unsigned int loop_counter; + + // Host channel valid + // If channel is open, equal to 1 + int push_valid; + int pull_valid; + + // User memory circular buffer + void *user_rd_buffer; + void *user_wr_buffer; + + // Array of physical addresses of locked hostch pages + HOSTCH_TABLE *push_page_table; + HOSTCH_TABLE *pull_page_table; + + DMA_PAGE push_page_table_addr; + DMA_PAGE pull_page_table_addr; + + // Physical address of the page table + DMA_ADDR push_page_table_bus_addr; + DMA_ADDR pull_page_table_bus_addr; + + PINNED_MEM m_hostch_rd_mem; + PINNED_MEM m_hostch_wr_mem; + + // User memory circular buffer front and end pointers + size_t *user_rd_front_pointer; + size_t *user_rd_end_pointer; + size_t *user_wr_front_pointer; + size_t *user_wr_end_pointer; + + DMA_ADDR user_rd_front_pointer_bus_addr; + DMA_ADDR user_wr_end_pointer_bus_addr; + + PINNED_MEM m_hostch_rd_pointer; + PINNED_MEM m_hostch_wr_pointer; + + // Keep track of push end pointer + size_t rd_buf_end_pointer; + + // Keep track of pull front pointer + size_t wr_buf_front_pointer; + + // User and driver thread synchronizer + int thread_sync_valid; + size_t *user_thread_sync; + DMA_ADDR user_thread_sync_bus_addr; + PINNED_MEM m_sync_thread_pointer; + }; + + // function to be scheduled to execute whenever an interrupt arrived + bool update(bool force_update = false); + + // Helper functions + inline void *compute_address(void *base, uintptr_t offset); + void set_read_desc(DMA_ADDR source, UINT64 dest, UINT32 ctl_dma_len); + void set_write_desc(UINT64 source, DMA_ADDR dest, UINT32 ctl_dma_len); + void set_desc_table_header(); + void send_dma_desc(); + void check_last_id(UINT32 *last_id); + void pin_memory(PINNED_MEM *new_mem, bool prepin); + void unpin_memory(PINNED_MEM *old_mem); + void wait_finish(); + void unpin_from_queue(); + void prepin_memory(); + + void set_immediate_desc(DMA_DESC_ENTRY *desc, UINT64 addr, UINT32 data, UINT32 id); + void add_extra_dma_desc(); + // Hostchannel helper function + void hostch_start(int channel); + int hostch_push_update(); + int hostch_pull_update(); + int hostch_buffer_lock(void *addr, size_t len, PINNED_MEM *new_mem); + void poll_wait(); + void set_hostch_page_entry(HOSTCH_ENTRY *page_entry, UINT64 page_addr, UINT32 page_num); + void setup_dma_desc(); + void spin_loop_ns(UINT64 wait_ns); + + // From environment variable + int m_use_polling; + + // The dma object we are currently building transactions for + PINNED_MEM m_active_mem; + PINNED_MEM m_pre_pinned_mem; + PINNED_MEM m_done_mem; + + // Hostchannel Struct + HOSTCH_DESC hostch_data; + + // The transaction we are currently working on + DMA_DESC_TABLE *m_table_virt_addr; + DMA_PAGE m_table_dma_addr; + DMA_ADDR m_table_dma_phys_addr; + DMA_DESC_ENTRY *m_active_descriptor; + + size_t m_last_pinned_size; + void *m_last_pinned_addr; + + // Signal to stop multiple pre-pinning from running + bool m_prepinned; + + // Local copy of last transfer id. Read once when DMA transfer starts + UINT32 m_last_id; + + // variables for the read/write request + aocl_mmd_op_t m_event; + size_t m_dev_addr; + void *m_host_addr; + size_t m_bytes; + size_t m_bytes_sent; + size_t m_bytes_rem; + bool m_read; + bool m_idle; + bool m_interrupt_disabled; + + fpga_handle m_handle; + ACL_PCIE_DEVICE *m_pcie; + ACL_PCIE_MM_IO_MGR *m_io; + ACL_PCIE_TIMER *m_timer; + + // variables needed for the threadpool and works that submitted to it + TP_CALLBACK_ENVIRON m_callback_env; + PTP_POOL m_threadpool; + PTP_WORK m_work; + + // This variable is accessed by the callback function defined in acl_pcie_dma_windows.cpp + // This semaphore is intended to keep at most 1 work in queued (not running) + HANDLE m_workqueue_semaphore; + + // Seperate thread to unpin + + std::queue<QUEUE_STRUCT> m_dma_unpin_pending; + + TP_CALLBACK_ENVIRON m_unpin_callback_env; + PTP_POOL m_unpin_threadpool; + PTP_WORK m_unpin_work; + + // Separate thread to pre-pin + + TP_CALLBACK_ENVIRON m_pin_callback_env; + PTP_POOL m_pin_threadpool; + PTP_WORK m_pin_work; +}; + +#endif // WINDOWS + +#endif // ACL_PCIE_DMA_WINDOWS_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.cpp new file mode 100644 index 0000000..0dc6d74 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.cpp @@ -0,0 +1,764 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_hostch.cpp ------------------------------------------ C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle Linux-specific DMA operations. */ +/* The declaration of the class lives in the acl_pcie_dma_linux.h */ +/* The actual implementation of DMA operation is inside the Linux kernel driver. */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// common and its own header files +#include "acl_pcie_hostch.h" +#include "acl_pcie.h" + +// other header files inside MMD driver +#include "acl_pcie_debug.h" +#include "acl_pcie_device.h" +#include "acl_pcie_mm_io.h" +#include "acl_pcie_timer.h" +#include "hw_host_channel.h" + +// other standard header files +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> + +#if defined(LINUX) +#include <unistd.h> +#endif // LINUX +#if defined(WINDOWS) +#include "acl_pcie_dma_windows.h" +#endif // WINDOWS + +void acl_aligned_malloc(void **result, size_t size) { +#if defined(LINUX) + int posix_success; + *result = NULL; + posix_success = posix_memalign(result, PAGE_SIZE, size); + ACL_PCIE_ASSERT(posix_success == 0, "posix_memalign has failed.\n"); +#endif // LINUX +#if defined(WINDOWS) + *result = _aligned_malloc(size, PAGE_SIZE); +#endif // WINDOWS +} + +void acl_aligned_free(void *ptr) { +#if defined(LINUX) + free(ptr); +#endif // LINUX +#if defined(WINDOWS) + _aligned_free(ptr); +#endif // WINDOWS +} + +ACL_PCIE_HOSTCH::ACL_PCIE_HOSTCH(fpga_handle handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie, ACL_PCIE_DMA *dma) + : m_push_queue(NULL), + m_push_queue_local_end_p(0), + m_push_queue_size(0), + m_pull_queue(NULL), + m_pull_queue_local_front_p(0), + m_pull_queue_size(0), + m_pull_queue_available(0), + m_pull_queue_pointer(NULL), + m_push_queue_pointer(NULL), + m_pull_queue_front_p(NULL), + m_pull_queue_end_p(NULL), + m_push_queue_front_p(NULL), + m_push_queue_end_p(NULL), + m_sync_thread(NULL) { + ACL_PCIE_ASSERT(handle != INVALID_HANDLE_VALUE, "passed in an invalid device when creating dma object.\n"); + ACL_PCIE_ASSERT(io != NULL, "passed in an empty pointer for io when creating dma object.\n"); + ACL_PCIE_ASSERT(pcie != NULL, "passed in an empty pointer for pcie when creating dma object.\n"); + ACL_PCIE_ASSERT(dma != NULL, "passed in an empty pointer for dma when creating dma object.\n"); + + m_handle = handle; + m_pcie = pcie; + m_io = io; + m_dma = dma; + m_timer = new ACL_PCIE_TIMER(); + + // Set the valid for all the channels and helper function that checks status of driver thread + // to 0 + m_hostch_push_valid = 0; + m_hostch_pull_valid = 0; + m_sync_thread_valid = 0; + + const char *dma_timer = getenv("ACL_PCIE_DMA_TIMER"); + if (dma_timer) + m_use_timer = 1; + else + m_use_timer = 0; +} + +ACL_PCIE_HOSTCH::~ACL_PCIE_HOSTCH() { + // If push channel (channel 0) is valid, reset its IP and unpin the MMD buffer + if (m_hostch_push_valid) { +#if defined(LINUX) + struct acl_cmd driver_cmd; + int bytes_read; + // Save the device id for the selected board + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_DESTROY_RD; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = NULL; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_destroy(ACL_HOST_CHANNEL_0_ID); +#endif // WINDOWS + + if (m_push_queue) { + acl_aligned_free(m_push_queue); + m_push_queue = NULL; + } + + if (m_push_queue_pointer) { + acl_aligned_free(m_push_queue_pointer); + m_push_queue_pointer = NULL; + } + + m_hostch_push_valid = 0; + } + + // If pull channel (channel 1) is valid, reset its IP and unpin the MMD buffer + if (m_hostch_pull_valid) { +#if defined(LINUX) + struct acl_cmd driver_cmd; + int bytes_read; + // Save the device id for the selected board + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_DESTROY_WR; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = NULL; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_destroy(ACL_HOST_CHANNEL_1_ID); +#endif // WINDOWS + + if (m_pull_queue) { + acl_aligned_free(m_pull_queue); + m_pull_queue = NULL; + } + + if (m_pull_queue_pointer) { + acl_aligned_free(m_pull_queue_pointer); + m_pull_queue_pointer = NULL; + } + + m_hostch_pull_valid = 0; + } + + if (m_timer) { + delete m_timer; + m_timer = NULL; + } +} + +// Get host channel version of currently programmed device +unsigned int ACL_PCIE_HOSTCH::get_hostch_version() { + // Make sure version is not what you expect + unsigned int version = ACL_VERSIONID ^ 1; + unsigned int hostch_version = ACL_HOSTCH_ZERO_CHANNELS ^ 1; + + // Read device version + m_io->version->read32(0, &version); + + if (!ACL_HOSTCH_ENABLE) { + return ACL_HOSTCH_ZERO_CHANNELS; + } + + // Read hostchannel version + m_io->hostch_ver->read32(0, &hostch_version); + + return hostch_version; +} + +// Function to check that the driver thread that update host channel IP with +// user's updates to MMD buffer's end and front index, is still running. +// Ack call will call sync_thread() if driver thread has timed out. +// Linux kernel space driver thread is set to timeout in 1ms +// if there hasn't been any changes to circular buffer pointer from the host. +int ACL_PCIE_HOSTCH::launch_sync_thread() { + if (m_sync_thread_valid == 0) { + acl_aligned_malloc((void **)&m_sync_thread, sizeof(size_t)); + + if (m_sync_thread == NULL) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Internal buffer memory allocation failed.\n"); + return -1; + } + +#if defined(LINUX) + // Save the device id for the selected board + struct acl_cmd driver_cmd; + int bytes_read; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_THREAD_SYNC; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = m_sync_thread; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_thread_sync(m_sync_thread); +#endif // WINDOWS + + m_sync_thread_valid = 1; + } else { + return 1; + } + return 0; +} + +int ACL_PCIE_HOSTCH::sync_thread() { + if (m_sync_thread_valid && (*m_sync_thread == 0)) { +#if defined(LINUX) + // Save the device id for the selected board + struct acl_cmd driver_cmd; + int bytes_read; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_THREAD_SYNC; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = NULL; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_thread_sync(NULL); +#endif // WINDOWS + + return 0; + } + return 1; +} + +// This is called only when there aren't any host channels open +// m_sync_thread is unpinned as part of destroy call to driver. Now free it. +void ACL_PCIE_HOSTCH::destroy_sync_thread() { + if (m_sync_thread_valid) { + if (m_sync_thread != NULL) acl_aligned_free(m_sync_thread); + + m_sync_thread_valid = 0; + m_sync_thread = NULL; + } +} + +// Create host channel. Allocate circular buffer and pin it. +// Then set channel to valid. +int ACL_PCIE_HOSTCH::create_hostchannel(char *name, size_t queue_depth, int direction) { + int status; + unsigned int hostch_version; + + hostch_version = get_hostch_version(); + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel version read was %u\n", hostch_version); + + // Check if channel name user wants to open exists + if ((strnlen(name, MAX_NAME_SIZE) == strnlen(ACL_HOST_CHANNEL_0_NAME, MAX_NAME_SIZE)) && + (strncmp(ACL_HOST_CHANNEL_0_NAME, name, strnlen(ACL_HOST_CHANNEL_0_NAME, MAX_NAME_SIZE)) == 0)) { + int channel = ACL_HOST_CHANNEL_0_ID; + // Check if hostchannel version is one that has ACL_HOST_CHANNEL_0_ID + if (hostch_version != ACL_HOSTCH_TWO_CHANNELS) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, + ":::: [HOST CHANNEL] Host Channel %s does not exist in currently programmed device.\n", + ACL_HOST_CHANNEL_0_NAME); + return ERROR_INVALID_CHANNEL; + } + + // check if the direction for the channel is correct + if (direction != ACL_HOST_CHANNEL_0_WRITE) return ERROR_INCORRECT_DIRECTION; + + // Check if channel was already opened previously + if (m_hostch_push_valid) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel '%s' already open\n", ACL_HOST_CHANNEL_0_NAME); + return ERROR_CHANNEL_PREVIOUSLY_OPENED; + } + + // Make sure the channel depth is at most 1MB, power-of-2, and divisible by page_size + size_t queue_depth_upper_pow2 = (size_t)pow(2, ceil(log((double)queue_depth) / log(2.))); + size_t channel_depth = (queue_depth_upper_pow2 >= HOSTCH_MAX_BUF_SIZE) + ? HOSTCH_MAX_BUF_SIZE + : queue_depth_upper_pow2 & (HOSTCH_MAX_BUF_SIZE - PAGE_SIZE); + + // Make sure the channel depth is at least 4KB + if (!channel_depth) channel_depth = PAGE_SIZE; + + // Create circular buffer for push + acl_aligned_malloc(&m_push_queue, channel_depth); + + if (m_push_queue == NULL) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Internal buffer memory allocation failed.\n"); + return -1; + } + + // Create buffer to hold front and end pointer for the circular buffer + acl_aligned_malloc((void **)&m_push_queue_pointer, sizeof(size_t) * 2); + + if (m_push_queue_pointer == NULL) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Internal buffer memory allocation failed.\n"); + acl_aligned_free(m_push_queue); + return -1; + } + + // Set parameters for the push channel + m_push_queue_size = channel_depth; + m_push_queue_local_end_p = 0; + + m_push_queue_front_p = m_push_queue_pointer; + m_push_queue_end_p = (m_push_queue_pointer + 1); + + *m_push_queue_front_p = 0; + *m_push_queue_end_p = 0; + + // sync_thread() used to check if kernel thread is still running when user has additional data available. + status = launch_sync_thread(); + if (status == -1) { + acl_aligned_free(m_push_queue); + acl_aligned_free(m_push_queue_pointer); + return -1; + } + +#if defined(LINUX) + struct acl_cmd driver_cmd; + int bytes_read; + // Send the pointers for the 2 buffers to driver, along with queue size + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_CREATE_RD; + driver_cmd.device_addr = m_push_queue_pointer; + driver_cmd.user_addr = m_push_queue; + driver_cmd.size = channel_depth; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_create(m_push_queue, m_push_queue_pointer, channel_depth, channel); +#endif // WINDOWS + + m_hostch_push_valid = 1; + return channel; + } else if ((strnlen(name, MAX_NAME_SIZE) == strnlen(ACL_HOST_CHANNEL_1_NAME, MAX_NAME_SIZE)) && + (strncmp(ACL_HOST_CHANNEL_1_NAME, name, strnlen(ACL_HOST_CHANNEL_1_NAME, MAX_NAME_SIZE)) == 0)) { + int channel = ACL_HOST_CHANNEL_1_ID; + + // Check if hostchannel version is one that has ACL_HOST_CHANNEL_1_ID + if (hostch_version != ACL_HOSTCH_TWO_CHANNELS) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, + ":::: [HOST CHANNEL] Host Channel %s does not exist in currently programmed device.\n", + ACL_HOST_CHANNEL_1_NAME); + return ERROR_INVALID_CHANNEL; + } + + // Check if direction is correct + if (direction != ACL_HOST_CHANNEL_1_WRITE) return ERROR_INCORRECT_DIRECTION; + + // Make sure the channel depth is at most 1MB, power-of-2, and divisible by page_size + size_t queue_depth_upper_pow2 = (size_t)pow(2, ceil(log((double)queue_depth) / log(2.))); + size_t channel_depth = (queue_depth_upper_pow2 >= HOSTCH_MAX_BUF_SIZE) + ? HOSTCH_MAX_BUF_SIZE + : queue_depth_upper_pow2 & (HOSTCH_MAX_BUF_SIZE - PAGE_SIZE); + + // Make sure the circular buffer is at least 4KB + if (!channel_depth) channel_depth = PAGE_SIZE; + + // Check if pull channel was previously opened + if (m_hostch_pull_valid) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel '%s' already open\n", ACL_HOST_CHANNEL_1_NAME); + return ERROR_CHANNEL_PREVIOUSLY_OPENED; + } + + // Create circular buffer + acl_aligned_malloc(&m_pull_queue, channel_depth); + + if (m_pull_queue == NULL) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Internal buffer memory allocation failed.\n"); + return -1; + } + + // Create buffer to hold front and end pointer of the circular buffer + acl_aligned_malloc((void **)&m_pull_queue_pointer, sizeof(size_t) * 2); + + if (m_pull_queue_pointer == NULL) { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Internal buffer memory allocation failed.\n"); + acl_aligned_free(m_pull_queue); + return -1; + } + + // Set pull channel parameters + m_pull_queue_size = channel_depth; + m_pull_queue_available = 0; + m_pull_queue_local_front_p = 0; + + m_pull_queue_front_p = m_pull_queue_pointer; + m_pull_queue_end_p = (m_pull_queue_pointer + 1); + + *m_pull_queue_front_p = 0; + *m_pull_queue_end_p = 0; + + // sync_thread() used to check if kernel thread is dead or alive when user pulls data + status = launch_sync_thread(); + if (status == -1) { + acl_aligned_free(m_pull_queue); + acl_aligned_free(m_pull_queue_pointer); + return -1; + } + +#if defined(LINUX) + // Send the pointers for the 2 buffers to driver, along with queue size, and initiate IP + struct acl_cmd driver_cmd; + int bytes_read; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_CREATE_WR; + driver_cmd.device_addr = m_pull_queue_pointer; + driver_cmd.user_addr = m_pull_queue; + driver_cmd.size = channel_depth; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_create(m_pull_queue, m_pull_queue_pointer, channel_depth, channel); +#endif // WINDOWS + + m_hostch_pull_valid = 1; + return channel; + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Channel does not exist.\n"); + return ERROR_INVALID_CHANNEL; + } +} + +// Destroy Channel. Unlock all buffer, and set channel to invalid. +int ACL_PCIE_HOSTCH::destroy_hostchannel(int channel) { + if (channel == ACL_HOST_CHANNEL_0_ID) { + if (m_hostch_push_valid) { + // set pull IP to reset and unlock all buffers +#if defined(LINUX) + struct acl_cmd driver_cmd; + int bytes_read; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_DESTROY_RD; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = NULL; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_destroy(channel); +#endif // WINDOWS + + if (m_push_queue) { + acl_aligned_free(m_push_queue); + m_push_queue = NULL; + } + if (m_push_queue_pointer) { + acl_aligned_free(m_push_queue_pointer); + m_push_queue_pointer = NULL; + } + + m_hostch_push_valid = 0; + if (m_hostch_pull_valid == 0) { + destroy_sync_thread(); + } + return 0; + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_0_NAME); + return ERROR_CHANNEL_CLOSED; + } + } else if (channel == ACL_HOST_CHANNEL_1_ID) { + if (m_hostch_pull_valid) { +#if defined(LINUX) + // set push IP to reset and unlock all buffers + struct acl_cmd driver_cmd; + int bytes_read; + driver_cmd.bar_id = ACLPCI_CMD_BAR; + driver_cmd.command = ACLPCI_CMD_HOSTCH_DESTROY_WR; + driver_cmd.device_addr = NULL; + driver_cmd.user_addr = NULL; + driver_cmd.size = 0; + bytes_read = read(m_handle, &driver_cmd, sizeof(driver_cmd)); + ACL_PCIE_ASSERT(bytes_read != -1, "error reading driver command.\n"); +#endif // LINUX +#if defined(WINDOWS) + m_dma->hostch_destroy(channel); +#endif // WINDOWS + + if (m_pull_queue) { + acl_aligned_free(m_pull_queue); + m_pull_queue = NULL; + } + + if (m_pull_queue_pointer) { + acl_aligned_free(m_pull_queue_pointer); + m_pull_queue_pointer = NULL; + } + + m_hostch_pull_valid = 0; + + if (m_hostch_push_valid == 0) { + destroy_sync_thread(); + } + + return 0; + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_1_NAME); + return ERROR_CHANNEL_CLOSED; + } + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Channel with ID %i does not exist.\n", channel); + } + + return ERROR_INVALID_CHANNEL; +} + +// Call for user to get pointer to location in circular buffer +// User can then write data or read data from the buffer, depending on direction. +void *ACL_PCIE_HOSTCH::get_buffer(size_t *buffer_size, int channel, int *status) { + // Check if channel exists + if (channel == ACL_HOST_CHANNEL_0_ID) { + // Check if channel was created + if (m_hostch_push_valid == 0) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_0_NAME); + *status = ERROR_CHANNEL_CLOSED; + *buffer_size = 0; + return NULL; + } + *status = 0; + + char *temp_input_queue = (char *)m_push_queue; + + size_t push_queue_end, push_queue_front; + + // m_push_queue_front_p is directly updated by host channel IP + // through write over Txs. Save value in local variable, + // so it doesn't get modified in middle of get_buffer call + push_queue_end = *m_push_queue_end_p; + push_queue_front = *m_push_queue_front_p; + + // Calculate available free space in host to device push buffer + size_t push_buf_avail; + if (push_queue_end > push_queue_front) + push_buf_avail = m_push_queue_size - push_queue_end + push_queue_front - 32; + else if (push_queue_end < push_queue_front) + push_buf_avail = push_queue_front - push_queue_end - 32; + else + push_buf_avail = m_push_queue_size - 32; + + // Calculate how much of the free space is before loop around and after loop around + size_t cont_push = (m_push_queue_size > m_push_queue_local_end_p + push_buf_avail) + ? push_buf_avail + : m_push_queue_size - m_push_queue_local_end_p; + size_t loop_push = (m_push_queue_size > m_push_queue_local_end_p + push_buf_avail) + ? 0 + : (m_push_queue_local_end_p + push_buf_avail - m_push_queue_size); + + // Return to user the pointer to circular buffer for + // space that's available without loop around + if (cont_push > 0) { + *buffer_size = cont_push; + return temp_input_queue + m_push_queue_local_end_p; + } else if (loop_push > 0) { + *buffer_size = loop_push; + return temp_input_queue; + } else { + *status = 0; + *buffer_size = 0; + + // See if the driver thread is still running + sync_thread(); + + return NULL; + } + } else if (channel == ACL_HOST_CHANNEL_1_ID) { + if (m_hostch_pull_valid == 0) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_1_NAME); + *status = ERROR_CHANNEL_CLOSED; + *buffer_size = 0; + return NULL; + } + *status = 0; + + char *temp_output_queue = (char *)m_pull_queue; + + size_t pull_queue_end, pull_queue_front; + + // m_pull_queue_end_p is directly updated by host channel IP + // through write over Txs. Save value in local variable, + // so it doesn't get modified in middle of get_buffer call + pull_queue_end = *m_pull_queue_end_p; + pull_queue_front = *m_pull_queue_front_p; + + // Calculate available new data in device to host pull buffer + if (pull_queue_end > pull_queue_front) + m_pull_queue_available = pull_queue_end - pull_queue_front; + else if (pull_queue_end < pull_queue_front) + m_pull_queue_available = m_pull_queue_size - pull_queue_front + pull_queue_end; + else + m_pull_queue_available = 0; + + // Calculate how much of the data is before loop around and after loop around + size_t cont_pull = (m_pull_queue_size > m_pull_queue_local_front_p + m_pull_queue_available) + ? m_pull_queue_available + : (m_pull_queue_size - m_pull_queue_local_front_p); + size_t loop_pull = (m_pull_queue_size > m_pull_queue_local_front_p + m_pull_queue_available) + ? 0 + : (m_pull_queue_local_front_p + m_pull_queue_available - m_pull_queue_size); + + // Return to user the pointer to circular buffer for + // data that's available without loop around + if (cont_pull > 0) { + *buffer_size = cont_pull; + return temp_output_queue + m_pull_queue_local_front_p; + } else if (loop_pull > 0) { + *buffer_size = loop_pull; + return temp_output_queue; + } else { + *buffer_size = 0; + + // See if the driver thread is still running + sync_thread(); + + return NULL; + } + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Channel with ID %i does not exist.\n", channel); + *status = ERROR_INVALID_CHANNEL; + *buffer_size = 0; + return NULL; + } +} + +// User has acknowledged the buffer, meaning data was written to or read from the buffter. +// Hand off to API using end pointer if push channel, and front pointer if pull channel. +size_t ACL_PCIE_HOSTCH::ack_buffer(size_t send_size, int channel, int *status) { + if (channel == ACL_HOST_CHANNEL_0_ID) { + if (m_hostch_push_valid == 0) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_0_NAME); + *status = ERROR_CHANNEL_CLOSED; + return 0; + } + *status = 0; + + size_t push_queue_end, push_queue_front; + + // Same calculations as get buffer call to see how much + // space is available in MMD circular buffer + push_queue_end = *m_push_queue_end_p; + push_queue_front = *m_push_queue_front_p; + + size_t push_buf_avail; + if (push_queue_end > push_queue_front) + push_buf_avail = m_push_queue_size - push_queue_end + push_queue_front - 32; + else if (push_queue_end < push_queue_front) + push_buf_avail = push_queue_front - push_queue_end - 32; + else + push_buf_avail = m_push_queue_size - 32; + + // Check to see if user wants to send more than the space available in buffer + // Chose lesser of the two to send + size_t user_words = send_size / 32; + size_t current_push = ((user_words * 32) > push_buf_avail) ? push_buf_avail : (user_words * 32); + + // User can't write back to beginning of MMD buffer, since they can't loop around from the pointer + // they got from get_buffer. Only send up to the end of MMD circular buffer to host channel IP + size_t cont_push = (m_push_queue_size > m_push_queue_local_end_p + current_push) + ? current_push + : (m_push_queue_size - m_push_queue_local_end_p); + + // Update the end index that the driver thread will read, to write the update to host channel IP + // and loop around + m_push_queue_local_end_p = + (m_push_queue_local_end_p + current_push >= m_push_queue_size) ? 0 : m_push_queue_local_end_p + current_push; + *m_push_queue_end_p = m_push_queue_local_end_p; + + // See if the driver thread is still running + sync_thread(); + + return cont_push; + } else if (channel == ACL_HOST_CHANNEL_1_ID) { + if (m_hostch_pull_valid == 0) { + ACL_PCIE_DEBUG_MSG_VERBOSE( + VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Host Channel %s is not open.\n", ACL_HOST_CHANNEL_1_NAME); + *status = ERROR_CHANNEL_CLOSED; + return 0; + } + *status = 0; + + size_t driver_pulled; + + size_t pull_queue_end, pull_queue_front; + + // Same calculations as get buffer call to see how much + // data is available in MMD circular buffer + pull_queue_end = *m_pull_queue_end_p; + pull_queue_front = *m_pull_queue_front_p; + + if (pull_queue_end > pull_queue_front) + m_pull_queue_available = pull_queue_end - pull_queue_front; + else if (pull_queue_end < pull_queue_front) + m_pull_queue_available = m_pull_queue_size - pull_queue_front + pull_queue_end; + else + m_pull_queue_available = 0; + + // Check to see if user read more than the data available in buffer + // Chose lesser of the two to tell the user how much was actually + // freed up for host channel IP to write to. + driver_pulled = (send_size > m_pull_queue_available) ? m_pull_queue_available : send_size; + + // User can't loop around and read from the beginning of MMD buffer + // Tell the host channel IP that the buffer is free, only up to the end of the circular buffer + size_t cont_pull = (m_pull_queue_size > m_pull_queue_local_front_p + driver_pulled) + ? driver_pulled + : (m_pull_queue_size - m_pull_queue_local_front_p); + + // Update the front index that the driver thread will read, to write the update to host channel IP + // and loop around + m_pull_queue_local_front_p = (m_pull_queue_local_front_p + driver_pulled >= m_pull_queue_size) + ? 0 + : m_pull_queue_local_front_p + driver_pulled; + *m_pull_queue_front_p = m_pull_queue_local_front_p; + + // See if the driver thread is still running + sync_thread(); + + return cont_pull; + } else { + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_BLOCKTX, ":::: [HOST CHANNEL] Channel with ID %i does not exist.\n", channel); + *status = ERROR_INVALID_CHANNEL; + return 0; + } +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.h new file mode 100644 index 0000000..e86fa61 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_hostch.h @@ -0,0 +1,136 @@ +#ifndef ACL_PCIE_HOSTCH_H +#define ACL_PCIE_HOSTCH_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_hostch.h -------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle Linux-specific DMA operations. */ +/* The actual implementation of the class lives in the acl_pcie_dma_linux.cpp */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#ifdef DLA_MMD +#include <cstddef> //size_t +#if defined(LINUX) +typedef int fpga_handle; +#else +#include <opae/fpga.h> +#endif +#endif + +class ACL_PCIE_DEVICE; +class ACL_PCIE_MM_IO_MGR; +class ACL_PCIE_TIMER; +class ACL_PCIE_DMA; + +class ACL_PCIE_HOSTCH { + public: + ACL_PCIE_HOSTCH(fpga_handle handle, ACL_PCIE_MM_IO_MGR *io, ACL_PCIE_DEVICE *pcie, ACL_PCIE_DMA *dma); + + ~ACL_PCIE_HOSTCH(); + + // Initialize host channel specified by name, and return handle to it + int create_hostchannel(char *name, size_t queue_depth, int direction); + + // Destroy host channel specified by channel handle + // return 0 on success and negative otherwise + int destroy_hostchannel(int channel); + + // Provide pointer to user with pointer to write and read to host channel + // IP with. Pointer is pointer to MMD circular buffer, that's pre-pinned. + // Address of this pre-pinned memory is transferred to IP during create + void *get_buffer(size_t *buffer_size, int channel, int *status); + + // Acknowledge from user that send_size bytes of data has be written to + // or read from host channel MMD buffer, that's provided by the channel + // handle. This will move end index for push channel, and front index for + // pull channel + size_t ack_buffer(size_t send_size, int channel, int *status); + + private: + ACL_PCIE_HOSTCH &operator=(const ACL_PCIE_HOSTCH &) { return *this; } + + ACL_PCIE_HOSTCH(const ACL_PCIE_HOSTCH &src) {} + + // Host Channel version of programmed device + unsigned int get_hostch_version(); + + // Helper functions to see if the thread that updates + // host channel IP with user's buffer updates, is still running + int launch_sync_thread(); + int sync_thread(); + void destroy_sync_thread(); + + fpga_handle m_handle; + ACL_PCIE_DEVICE *m_pcie; + ACL_PCIE_MM_IO_MGR *m_io; + ACL_PCIE_DMA *m_dma; + + ACL_PCIE_TIMER *m_timer; + int m_use_timer; + + // Host Channel valid + // If channel is open, equal to 1 + int m_hostch_push_valid; + int m_hostch_pull_valid; + + // Input Queue + // Write data into circular buffer in MMD, that host channel + // can read from + void *m_push_queue; + size_t m_push_queue_local_end_p; + size_t m_push_queue_size; + + // Information to track input queue + void *m_pull_queue; + size_t m_pull_queue_local_front_p; + size_t m_pull_queue_size; + size_t m_pull_queue_available; + + // Shared front and end pointer with driver + // Circular buffer in MMD that the host channel IP can + // write into. Host will then read from it + size_t *m_pull_queue_pointer; + size_t *m_push_queue_pointer; + + size_t *m_pull_queue_front_p; + size_t *m_pull_queue_end_p; + size_t *m_push_queue_front_p; + size_t *m_push_queue_end_p; + + // User space memory that Linux kernel space has write + // access to. Since the MMD buffer is circular, whenever + // user writes to reads from it, the index for end and front + // changes, respectively. This needs to be sent to host channel IP + // and the thread in driver handles that. However, this thread will + // die after 1ms of inactivity to free up the CPU. When it does that, + // it will write to m_sync_thread with value of 0, so that MMD knows to + // launch it again, for subsequent get_buffer and ack_buffer calls. + int m_sync_thread_valid; + size_t *m_sync_thread; +}; + +void acl_aligned_malloc(void **result, size_t size); +void acl_aligned_free(void *ptr); + +#endif // ACL_PCIE_HOSTCH_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.cpp new file mode 100644 index 0000000..92c9cf0 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.cpp @@ -0,0 +1,556 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_mm_io.cpp ------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to handle memory mapped IO over PCIe. */ +/* The declaration of the class lives in the acl_pcie_mm_io.h. */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// common and its own header files +#include "acl_pcie_mm_io.h" +#include "acl_pcie.h" + +// other header files inside MMD driver +#include "acl_pcie_debug.h" + +// other standard header files +#include <string.h> + +#if defined(LINUX) +#include <unistd.h> // template +#endif // LINUX + +ACL_PCIE_MM_IO_DEVICE::ACL_PCIE_MM_IO_DEVICE( + fpga_handle handle, DWORD bar, KPTR device_offset, const char *name, bool diff_endian) { + ACL_PCIE_ASSERT(handle != INVALID_HANDLE_VALUE, "passed in an invalid handle when creating mm_io object.\n"); + +#if defined(WINDOWS) + strncpy_s(m_name, MAX_NAME_LENGTH - 1, name, (MAX_NAME_LENGTH - 1)); +#else + strncpy(m_name, name, (MAX_NAME_LENGTH - 1)); +#endif + m_name[(MAX_NAME_LENGTH - 1)] = '\0'; + + m_handle = handle; + m_bar = bar; + m_offset = device_offset; + m_diff_endian = diff_endian; + + ACL_PCIE_DEBUG_MSG(":: [%s] Init: Bar " DWORD_FMT_U ", Total offset 0x%zu, diff_endian is %d \n", + m_name, + m_bar, + (size_t)m_offset, + m_diff_endian ? 1 : 0); +} + +ACL_PCIE_MM_IO_DEVICE::~ACL_PCIE_MM_IO_DEVICE() {} + +#if defined(LINUX) +// Helper functions to implement all other read/write functions +template <typename T> +DWORD linux_read(fpga_handle device, DWORD bar, KPTR address, T *data) { + struct acl_cmd driver_cmd; + driver_cmd.bar_id = bar; + driver_cmd.command = ACLPCI_CMD_DEFAULT; + driver_cmd.device_addr = reinterpret_cast<void *>(address); + driver_cmd.user_addr = data; + driver_cmd.size = sizeof(*data); + // function invoke linux_read will not write to global memory. + // So is_diff_endian is always false + driver_cmd.is_diff_endian = 0; + + return read(device, &driver_cmd, sizeof(driver_cmd)); +} + +template <typename T> +DWORD linux_write(fpga_handle device, DWORD bar, KPTR address, T data) { + struct acl_cmd driver_cmd; + driver_cmd.bar_id = bar; + driver_cmd.command = ACLPCI_CMD_DEFAULT; + driver_cmd.device_addr = reinterpret_cast<void *>(address); + driver_cmd.user_addr = &data; + driver_cmd.size = sizeof(data); + // function invoke linux_write will not write to global memory. + // So is_diff_endian is always false + driver_cmd.is_diff_endian = 0; + + return write(device, &driver_cmd, sizeof(driver_cmd)); +} +#endif // LINUX + +int ACL_PCIE_MM_IO_DEVICE::read8(size_t addr, UINT8 *data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaReadMmio(m_handle, m_bar, bar_addr, (PVOID)data, sizeof(UINT8)); +#endif // WINDOWS +#if defined(LINUX) + status = linux_read(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Read 8 bits from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Read 8 bits (0x%x) from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + *data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::write8(size_t addr, UINT8 data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaWriteMmio(m_handle, m_bar, bar_addr, (PVOID)&data, sizeof(UINT8)); +#endif // WINDOWS +#if defined(LINUX) + status = linux_write(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Writing 8 bits to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Wrote 8 bits (0x%x) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::read16(size_t addr, UINT16 *data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaReadMmio(m_handle, m_bar, bar_addr, (PVOID)data, sizeof(UINT16)); +#endif // WINDOWS +#if defined(LINUX) + status = linux_read(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Read 16 bits from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Read 16 bits (0x%x) from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + *data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::write16(size_t addr, UINT16 data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaWriteMmio(m_handle, m_bar, bar_addr, (PVOID)&data, sizeof(UINT16)); +#endif // WINDOWS +#if defined(LINUX) + status = linux_write(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Writing 16 bits to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Wrote 16 bits (0x%x) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::read32(size_t addr, UINT32 *data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaReadMMIO32(m_handle, m_bar, bar_addr, data); +#endif // WINDOWS +#if defined(LINUX) + status = linux_read(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Read 32 bits from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Read 32 bits (0x%x) from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + *data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::write32(size_t addr, UINT32 data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + status = fpgaWriteMMIO32(m_handle, m_bar, bar_addr, data); +#endif // WINDOWS +#if defined(LINUX) + status = linux_write(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Writing 32 bits to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Wrote 32 bits (0x%x) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::read64(size_t addr, UINT64 *data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + // Original code had a 32-bit Read + status = fpgaReadMmio(m_handle, m_bar, bar_addr, data, 8); + +#endif // WINDOWS +#if defined(LINUX) + status = linux_read(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Read 64 bits from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Read 64 bits (0x%llx) from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + *data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::write64(size_t addr, UINT64 data) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); +#if defined(WINDOWS) + // Original code had a 32-bit Write + status = fpgaWriteMmio(m_handle, m_bar, bar_addr, (void *)&data, 8); + +#endif // WINDOWS +#if defined(LINUX) + status = linux_write(m_handle, m_bar, bar_addr, data); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Writing 64 bits to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + bar_addr, + (size_t)bar_addr); + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Wrote 64 bits (0x%llx) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + data, + addr, + (size_t)bar_addr); + + return 0; // success +} + +int ACL_PCIE_MM_IO_DEVICE::write_block(size_t addr, size_t size, void *src) { + fpga_result status; + KPTR bar_addr = convert_to_bar_addr(addr); + + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Writing block (" SIZE_FMT_U " bytes) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X + " with offset)\n", + m_name, + size, + addr, + (size_t)bar_addr); + +#if defined(WINDOWS) + DWORD FP_size = static_cast<DWORD>(size); + size_t alignment_size = size % 4; + DWORD FP_alignment_size = static_cast<DWORD>(alignment_size); + // 32-bit MMIO Write + status = fpgaWriteMmio(m_handle, m_bar, bar_addr, src, FP_size - FP_alignment_size); + if (alignment_size) { + void *alignment_addr = compute_address(src, size - alignment_size); + KPTR alignment_bar_addr = bar_addr + size - alignment_size; + status = fpgaWriteMmio(m_handle, m_bar, alignment_bar_addr, alignment_addr, FP_alignment_size); + } + +#endif // WINDOWS +#if defined(LINUX) + // Can't use templated linux_write here because *src doesn't give you the size to read. + struct acl_cmd driver_cmd {}; + driver_cmd.bar_id = m_bar; + driver_cmd.device_addr = reinterpret_cast<void *>(bar_addr); + driver_cmd.user_addr = src; + driver_cmd.size = size; + // Notify the driver if the host and device's memory have different endianess. + driver_cmd.is_diff_endian = m_diff_endian ? 1 : 0; + status = write(m_handle, &driver_cmd, sizeof(driver_cmd)); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Writing block (" SIZE_FMT_U " bytes) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + size, + addr, + (size_t)bar_addr); + return 0; // success +} + +inline void *ACL_PCIE_MM_IO_DEVICE::compute_address(void *base, uintptr_t offset) { + uintptr_t p = reinterpret_cast<uintptr_t>(base); + return reinterpret_cast<void *>(p + offset); +} + +int ACL_PCIE_MM_IO_DEVICE::read_block(size_t addr, size_t size, void *dst) { + DWORD status; + KPTR bar_addr = convert_to_bar_addr(addr); + + ACL_PCIE_DEBUG_MSG_VERBOSE(VERBOSITY_PCIE, + ":::::: [%s] Reading block (" SIZE_FMT_U " bytes) from 0x" SIZE_FMT_X " (0x" SIZE_FMT_X + " with offset)\n", + m_name, + size, + addr, + (size_t)bar_addr); + +#if defined(WINDOWS) + DWORD FP_size = static_cast<DWORD>(size); + size_t alignment_size = size % 4; + DWORD FP_alignment_size = static_cast<DWORD>(alignment_size); + // 32-bit MMIO Read + status = fpgaReadMmio(m_handle, m_bar, bar_addr, dst, FP_size - FP_alignment_size); + if (alignment_size) { + void *alignment_addr = compute_address(dst, size - alignment_size); + KPTR alignment_bar_addr = bar_addr + size - alignment_size; + status |= fpgaReadMmio(m_handle, m_bar, alignment_bar_addr, alignment_addr, FP_alignment_size); + } + +#endif // WINDOWS +#if defined(LINUX) + // Can't use templated linux_write here because *src doesn't give you the size to read. + struct acl_cmd driver_cmd; + driver_cmd.bar_id = m_bar; + driver_cmd.device_addr = reinterpret_cast<void *>(bar_addr); + driver_cmd.user_addr = dst; + driver_cmd.size = size; + // Notify the driver if the host and device's memory have different endianess. + driver_cmd.is_diff_endian = m_diff_endian ? 1 : 0; + status = read(m_handle, &driver_cmd, sizeof(driver_cmd)); +#endif // LINUX + + ACL_PCIE_ERROR_IF(status != FPGA_OK, + return -1, + "[%s] Reading block (" SIZE_FMT_U " bytes) to 0x" SIZE_FMT_X " (0x" SIZE_FMT_X " with offset)\n", + m_name, + size, + addr, + (size_t)bar_addr); + return 0; // success +} + +ACL_PCIE_MM_IO_MGR::ACL_PCIE_MM_IO_MGR(fpga_handle handle) + : mem(NULL), + pcie_cra(NULL), + window(NULL), + version(NULL), + pr_base_id(NULL), + pr_region_ctrl(NULL), + quartus_ver(NULL), + cade_id(NULL), + uniphy_status(NULL), + uniphy_reset(NULL), + kernel_if(NULL), + pll(NULL), + temp_sensor(NULL), + hostch_ver(NULL) { + ACL_PCIE_ASSERT(handle != INVALID_HANDLE_VALUE, "passed in an invalid device when creating mm_io_mgr.\n"); + + // This is the PCIe's interface for directly accessing memory (which is + // significantly slower than using DMA). This view of memory is segmented + // so that the size of this address space can be smaller than the amount of + // physical device memory. The window interface controls which region of + // physical memory this interface currently maps to. + // The last flag indicate if the device on both side of transferring have + // different endianess. +#ifdef ACL_BIG_ENDIAN + mem = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCI_GLOBAL_MEM_BAR, (KPTR)ACL_PCIE_MEMWINDOW_BASE, "GLOBAL-MEM", true); +#else + mem = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCI_GLOBAL_MEM_BAR, (KPTR)ACL_PCIE_MEMWINDOW_BASE, "GLOBAL-MEM", false); +#endif + + // This is the CRA port of our PCIe controller. Used for configuring + // interrupts and things like that. + pcie_cra = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCI_CRA_BAR, ACL_PCI_CRA_OFFSET, "PCIE-CRA"); + + // This interface sets the high order address bits for the PCIe's direct + // memory accesses via "mem" (above). + window = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCIE_MEMWINDOW_BAR, ACL_PCIE_MEMWINDOW_CRA, "MEMWINDOW"); + + // DMA interfaces + dma = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCIE_DMA_INTERNAL_BAR, ACL_PCIE_DMA_INTERNAL_CTR_BASE, "DMA-CTR"); + + // Version ID check + version = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_VERSIONID_BAR, ACL_VERSIONID_OFFSET, "VERSION"); + + // PR base ID check + pr_base_id = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PRBASEID_BAR, ACL_PRBASEID_OFFSET, "PRBASEID"); + + // PR region controller + pr_region_ctrl = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PRREGIONFREEZE_BAR, ACL_PRREGIONFREEZE_OFFSET, "PRREGIONCTRL"); + + // Quartus Version + quartus_ver = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_QUARTUSVER_BAR, ACL_QUARTUSVER_OFFSET, "QUARTUS-VERSION"); + + // Quartus Version + hostch_ver = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_HOSTCH_VERSION_BAR, ACL_HOSTCH_VERSION_OFFSET, "HOSTCH-VERSION"); + + // Cable auto detect ID + cade_id = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_CADEID_BAR, ACL_CADEID_OFFSET, "CADEID"); + + // Uniphy Status + uniphy_status = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_UNIPHYSTATUS_BAR, ACL_UNIPHYSTATUS_OFFSET, "UNIPHYSTATUS"); + + // Uniphy Reset + uniphy_reset = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_UNIPHYRESET_BAR, ACL_UNIPHYRESET_OFFSET, "UNIPHYRESET"); + + // Kernel interface + // The DLA BSP eliminates the kernel interface present in the original PR Terasic BSP + // We reuse the kernel_if object here to simplify the DLA-specific changes required +#ifdef DLA_MMD + kernel_if = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_KERNEL_CSR_BAR, ACL_DLA_CSR_OFFSET, "KERNEL"); +#else + kernel_if = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_KERNEL_CSR_BAR, ACL_KERNEL_CSR_OFFSET, "KERNEL"); +#endif // DLA_MMD + + // PLL interface + pll = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_PCIE_KERNELPLL_RECONFIG_BAR, ACL_PCIE_KERNELPLL_RECONFIG_OFFSET, "PLL"); + + // temperature sensor +#ifdef ACL_PCIE_HAS_TEMP_SENSOR + temp_sensor = new ACL_PCIE_MM_IO_DEVICE(handle, ACL_VERSIONID_BAR, ACL_PCIE_TEMP_SENSOR_ADDRESS, "TEMP-SENSOR"); +#endif +} + +ACL_PCIE_MM_IO_MGR::~ACL_PCIE_MM_IO_MGR() { + if (mem) { + delete mem; + mem = NULL; + } + if (pcie_cra) { + delete pcie_cra; + pcie_cra = NULL; + } + if (window) { + delete window; + window = NULL; + } + if (version) { + delete version; + version = NULL; + } + if (pr_base_id) { + delete pr_base_id; + pr_base_id = NULL; + } + if (pr_region_ctrl) { + delete pr_region_ctrl; + pr_region_ctrl = NULL; + } + if (quartus_ver) { + delete quartus_ver; + quartus_ver = NULL; + } + if (cade_id) { + delete cade_id; + cade_id = NULL; + } + if (uniphy_status) { + delete uniphy_status; + uniphy_status = NULL; + } + if (uniphy_reset) { + delete uniphy_reset; + uniphy_reset = NULL; + } + if (kernel_if) { + delete kernel_if; + kernel_if = NULL; + } + if (pll) { + delete pll; + pll = NULL; + } + if (temp_sensor) { + delete temp_sensor; + temp_sensor = NULL; + } + if (hostch_ver) { + delete hostch_ver; + hostch_ver = NULL; + } + if (dma) { + delete dma; + dma = NULL; + } +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.h new file mode 100644 index 0000000..4db5599 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_mm_io.h @@ -0,0 +1,109 @@ +#ifndef ACL_PCIE_MM_IO_H +#define ACL_PCIE_MM_IO_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_mm_io.h --------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to handle memory mapped IO over PCIe. */ +/* The actual implementation of the class lives in the acl_pcie_mm_io.cpp, */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#if defined(LINUX) +typedef int fpga_handle; +#define FPGA_OK 0 +#endif // LINUX + +#ifdef DLA_MMD +#include "acl_pcie.h" +#define ACL_DLA_CSR_OFFSET 0x0000 +#endif +/* + * + */ +class ACL_PCIE_MM_IO_DEVICE { + public: + ACL_PCIE_MM_IO_DEVICE(fpga_handle handle, DWORD bar, KPTR device_offset, const char *name, bool diff_endian = false); + ~ACL_PCIE_MM_IO_DEVICE(); + + DWORD bar_id() { return m_bar; }; + KPTR convert_to_bar_addr(size_t addr) { return addr + m_offset; }; + + // read/write functions to the memory-mapped io device + // return 0 on success, negative on error + int read8(size_t addr, UINT8 *data); + int write8(size_t addr, UINT8 data); + int read16(size_t addr, UINT16 *data); + int write16(size_t addr, UINT16 data); + int read32(size_t addr, UINT32 *data); + int write32(size_t addr, UINT32 data); + int read64(size_t addr, UINT64 *data); + int write64(size_t addr, UINT64 data); + + int read_block(size_t addr, size_t size, void *dst); + int write_block(size_t addr, size_t size, void *src); + + private: + static const int MAX_NAME_LENGTH = 32; + + // Helper functions + inline void *compute_address(void *base, uintptr_t offset); + + char m_name[MAX_NAME_LENGTH]; + fpga_handle m_handle; + DWORD m_bar; + KPTR m_offset; + bool m_diff_endian; // indicates if the host and this device have different endianess +}; + +/* + * Utility functions to clean up the various address translations for reads/writes + */ +class ACL_PCIE_MM_IO_MGR { + private: + ACL_PCIE_MM_IO_MGR &operator=(const ACL_PCIE_MM_IO_MGR &) { return *this; } + + ACL_PCIE_MM_IO_MGR(const ACL_PCIE_MM_IO_MGR &src) {} + + public: + ACL_PCIE_MM_IO_MGR(fpga_handle handle); + ~ACL_PCIE_MM_IO_MGR(); + + ACL_PCIE_MM_IO_DEVICE *mem; + ACL_PCIE_MM_IO_DEVICE *pcie_cra; + ACL_PCIE_MM_IO_DEVICE *dma; + ACL_PCIE_MM_IO_DEVICE *window; + ACL_PCIE_MM_IO_DEVICE *version; + ACL_PCIE_MM_IO_DEVICE *pr_base_id; + ACL_PCIE_MM_IO_DEVICE *pr_region_ctrl; + ACL_PCIE_MM_IO_DEVICE *quartus_ver; + ACL_PCIE_MM_IO_DEVICE *cade_id; + ACL_PCIE_MM_IO_DEVICE *uniphy_status; + ACL_PCIE_MM_IO_DEVICE *uniphy_reset; + ACL_PCIE_MM_IO_DEVICE *kernel_if; + ACL_PCIE_MM_IO_DEVICE *pll; + ACL_PCIE_MM_IO_DEVICE *temp_sensor; + ACL_PCIE_MM_IO_DEVICE *hostch_ver; +}; + +#endif // ACL_PCIE_MM_IO_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.cpp b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.cpp new file mode 100644 index 0000000..855d6ba --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.cpp @@ -0,0 +1,67 @@ +// (c) 1992-2021 Intel Corporation. +// Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words +// and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. +// and/or other countries. Other marks and brands may be claimed as the property +// of others. See Trademarks on intel.com for full list of Intel trademarks or +// the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) +// Your use of Intel Corporation's design tools, logic functions and other +// software and tools, and its AMPP partner logic functions, and any output +// files any of the foregoing (including device programming or simulation +// files), and any associated documentation or information are expressly subject +// to the terms and conditions of the Altera Program License Subscription +// Agreement, Intel MegaCore Function License Agreement, or other applicable +// license agreement, including, without limitation, that your use is for the +// sole purpose of programming logic devices manufactured by Intel and sold by +// Intel or its authorized distributors. Please refer to the applicable +// agreement for further details. + +/* ===- acl_pcie_timer.cpp ------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file implements the class to query the host's system timer. */ +/* The declaration of the class lives in the acl_pcie_timer.h */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +// common and its own header files +#include "acl_pcie_timer.h" +#include "acl_pcie.h" + +// other standard header files +#include <fstream> + +ACL_PCIE_TIMER::ACL_PCIE_TIMER() : m_ticks_per_second(0) { +#if defined(WINDOWS) + // Cache the performance counter frequency + LARGE_INTEGER li; + QueryPerformanceFrequency(&li); + m_ticks_per_second = li.QuadPart; + + ACL_PCIE_ASSERT(m_ticks_per_second != 0, "m_ticks_per_second == 0!\n"); +#endif // WINDOWS +} + +ACL_PCIE_TIMER::~ACL_PCIE_TIMER() {} + +cl_ulong ACL_PCIE_TIMER::get_time_ns() { +#if defined(WINDOWS) + const INT64 NS_PER_S = 1000000000; + LARGE_INTEGER li; + + QueryPerformanceCounter(&li); + INT64 ticks = li.QuadPart; + double seconds = ticks / (double)m_ticks_per_second; + + return static_cast<cl_ulong>(seconds * NS_PER_S + 0.5); +#endif // WINDOWS +#if defined(LINUX) + struct timespec a; + const cl_ulong NS_PER_S = 1000000000; + clock_gettime(CLOCK_REALTIME, &a); + + return static_cast<cl_ulong>(a.tv_nsec) + static_cast<cl_ulong>(a.tv_sec * NS_PER_S); +#endif // LINUX +} diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.h new file mode 100644 index 0000000..646d681 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/acl_pcie_timer.h @@ -0,0 +1,50 @@ +#ifndef ACL_PCIE_TIMER_H +#define ACL_PCIE_TIMER_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +/* ===- acl_pcie_timer.h --------------------------------------------- C++ -*-=== */ +/* */ +/* Intel(R) OpenCL MMD Driver */ +/* */ +/* ===-------------------------------------------------------------------------=== */ +/* */ +/* This file declares the class to query the host's system timer. */ +/* The actual implementation of the class lives in the acl_pcie_timer.cpp */ +/* */ +/* ===-------------------------------------------------------------------------=== */ + +#ifdef DLA_MMD +// don't assume opencl has been installed +#include "acl_pcie.h" +typedef UINT64 cl_ulong; +#endif + +class ACL_PCIE_TIMER { + public: + ACL_PCIE_TIMER(); + ~ACL_PCIE_TIMER(); + + // function to query the host's system timer + cl_ulong get_time_ns(); + + private: + INT64 m_ticks_per_second; +}; + +#endif // ACL_PCIE_TIMER_H diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/version.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/version.h new file mode 100644 index 0000000..ffecc32 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/host/version.h @@ -0,0 +1 @@ +#define ACL_DRIVER_VERSION "20.4.d41d8cd98f00b204e9800998ecf8427e" diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/aocl_mmd.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/aocl_mmd.h new file mode 100644 index 0000000..6d5c85e --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/aocl_mmd.h @@ -0,0 +1,640 @@ +#ifndef AOCL_MMD_H +#define AOCL_MMD_H + +/* (c) 1992-2021 Intel Corporation. */ +/* Intel, the Intel logo, Intel, MegaCore, NIOS II, Quartus and TalkBack words */ +/* and logos are trademarks of Intel Corporation or its subsidiaries in the U.S. */ +/* and/or other countries. Other marks and brands may be claimed as the property */ +/* of others. See Trademarks on intel.com for full list of Intel trademarks or */ +/* the Trademarks & Brands Names Database (if Intel) or See www.Intel.com/legal (if Altera) */ +/* Your use of Intel Corporation's design tools, logic functions and other */ +/* software and tools, and its AMPP partner logic functions, and any output */ +/* files any of the foregoing (including device programming or simulation */ +/* files), and any associated documentation or information are expressly subject */ +/* to the terms and conditions of the Altera Program License Subscription */ +/* Agreement, Intel MegaCore Function License Agreement, or other applicable */ +/* license agreement, including, without limitation, that your use is for the */ +/* sole purpose of programming logic devices manufactured by Intel and sold by */ +/* Intel or its authorized distributors. Please refer to the applicable */ +/* agreement for further details. */ + +#ifdef DLA_MMD +#include <cstddef> //size_t +#include <cstdint> //uint32_t +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Support for memory mapped ACL devices. + * + * Typical API lifecycle, from the perspective of the caller. + * + * 1. aocl_mmd_open must be called first, to provide a handle for further + * operations. + * + * 2. The interrupt and status handlers must be set. + * + * 3. Read and write operations are performed. + * + * 4. aocl_mmd_close may be called to shut down the device. No further + * operations are permitted until a subsequent aocl_mmd_open call. + * + * aocl_mmd_get_offline_info can be called anytime including before + * open. aocl_mmd_get_info can be called anytime between open and close. + */ + +#ifndef AOCL_MMD_CALL +#if defined(_WIN32) +#define AOCL_MMD_CALL __declspec(dllimport) +#else +#define AOCL_MMD_CALL __attribute__((visibility ("default"))) +#endif +#endif + +#ifndef WEAK +#if defined(_WIN32) +#define WEAK +#else +/* This normally comes with "__attribute__((weak))" but for reasons not presently + * understood, the shared library is not properly loaded on Ubuntu18 when the functions + * are weak. + */ +#define WEAK +#endif +#endif + +/* The MMD API's version - the runtime expects this string when + * AOCL_MMD_VERSION is queried. This changes only if the API has changed */ +#define AOCL_MMD_VERSION_STRING "20.3" + +/* Memory types that can be supported - bitfield. Other than physical memory + * these types closely align with the OpenCL SVM types. + * + * AOCL_MMD_PHYSICAL_MEMORY - The vendor interface includes IP to communicate + * directly with physical memory such as DDR, QDR, etc. + * + * AOCL_MMD_SVM_COARSE_GRAIN_BUFFER - The vendor interface includes support for + * caching SVM pointer data and requires explicit function calls from the user + * to synchronize the cache between the host processor and the FPGA. This level + * of SVM is not currently supported by Altera except as a subset of + * SVM_FINE_GAIN_SYSTEM support. + * + * AOCL_MMD_SVM_FINE_GRAIN_BUFFER - The vendor interface includes support for + * caching SVM pointer data and requires additional information from the user + * and/or host runtime that can be collected during pointer allocation in order + * to synchronize the cache between the host processor and the FPGA. Once this + * additional data is provided for an SVM pointer, the vendor interface handles + * cache synchronization between the host processor & the FPGA automatically. + * This level of SVM is not currently supported by Altera except as a subset + * of SVM_FINE_GRAIN_SYSTEM support. + * + * AOCL_MMD_SVM_FINE_GRAIN_SYSTEM - The vendor interface includes support for + * caching SVM pointer data and does not require any additional information to + * synchronize the cache between the host processor and the FPGA. The vendor + * interface handles cache synchronization between the host processor & the + * FPGA automatically for all SVM pointers. This level of SVM support is + * currently under development by Altera and some features may not be fully + * supported. + */ +#define AOCL_MMD_PHYSICAL_MEMORY (1 << 0) +#define AOCL_MMD_SVM_COARSE_GRAIN_BUFFER (1 << 1) +#define AOCL_MMD_SVM_FINE_GRAIN_BUFFER (1 << 2) +#define AOCL_MMD_SVM_FINE_GRAIN_SYSTEM (1 << 3) + +/* program modes - bitfield + * + * AOCL_MMD_PROGRAM_PRESERVE_GLOBAL_MEM - preserve contents of global memory + * when this bit is set to 1. If programming can't occur without preserving + * global memory contents, the program function must fail, in which case the + * runtime may re-invoke program with this bit set to 0, allowing programming + * to occur even if doing so destroys global memory contents. + * + * more modes are reserved for stacking on in the future + */ +#define AOCL_MMD_PROGRAM_PRESERVE_GLOBAL_MEM (1 << 0) +typedef int aocl_mmd_program_mode_t; + +typedef void* aocl_mmd_op_t; + +typedef struct { + unsigned lo; /* 32 least significant bits of time value. */ + unsigned hi; /* 32 most significant bits of time value. */ +} aocl_mmd_timestamp_t; + +/* Defines the set of characteristics that can be probed about the board before + * opening a device. The type of data returned by each is specified in + * parentheses in the adjacent comment. + * + * AOCL_MMD_NUM_BOARDS and AOCL_MMD_BOARD_NAMES + * These two fields can be used to implement multi-device support. The MMD + * layer may have a list of devices it is capable of interacting with, each + * identified with a unique name. The length of the list should be returned + * in AOCL_MMD_NUM_BOARDS, and the names of these devices returned in + * AOCL_MMD_BOARD_NAMES. The OpenCL runtime will try to call aocl_mmd_open + * for each board name returned in AOCL_MMD_BOARD_NAMES. + */ +typedef enum { + AOCL_MMD_VERSION = 0, /* Version of MMD (char*)*/ + AOCL_MMD_NUM_BOARDS = 1, /* Number of candidate boards (int)*/ + AOCL_MMD_BOARD_NAMES = 2, /* Names of boards available delimiter=; (char*)*/ + AOCL_MMD_VENDOR_NAME = 3, /* Name of vendor (char*) */ + AOCL_MMD_VENDOR_ID = 4, /* An integer ID for the vendor (int) */ + AOCL_MMD_USES_YIELD = 5, /* 1 if yield must be called to poll hw (int) */ + /* The following can be combined in a bit field: + * AOCL_MMD_PHYSICAL_MEMORY, AOCL_MMD_SVM_COARSE_GRAIN_BUFFER, AOCL_MMD_SVM_FINE_GRAIN_BUFFER, + * AOCL_MMD_SVM_FINE_GRAIN_SYSTEM. Prior to 14.1, all existing devices supported physical memory and no types of SVM + * memory, so this is the default when this operation returns '0' for board MMDs with a version prior to 14.1 + */ + AOCL_MMD_MEM_TYPES_SUPPORTED = 6, +} aocl_mmd_offline_info_t; + +/** Possible capabilities to return from AOCL_MMD_*_MEM_CAPABILITIES query */ +/** + * If not set allocation function is not supported, even if other capabilities are set. + */ +#define AOCL_MMD_MEM_CAPABILITY_SUPPORTED (1 << 0) +/** + * Supports atomic access to the memory by either the host or device. + */ +#define AOCL_MMD_MEM_CAPABILITY_ATOMIC (1 << 1) +/** + * Supports concurrent access to the memory either by host or device if the + * accesses are not on the same block. Block granularity is defined by + * AOCL_MMD_*_MEM_CONCURRENT_GRANULARITY., blocks are aligned to this + * granularity + */ +#define AOCL_MMD_MEM_CAPABILITY_CONCURRENT (1 << 2) +/** + * Memory can be accessed by multiple devices at the same time. + */ +#define AOCL_MMD_MEM_CAPABILITY_P2P (1 << 3) + +/* Defines the set of characteristics that can be probed about the board after + * opening a device. This can involve communication to the device + * + * AOCL_MMD_NUM_KERNEL_INTERFACES - The number of kernel interfaces, usually 1 + * + * AOCL_MMD_KERNEL_INTERFACES - the handle for each kernel interface. + * param_value will have size AOCL_MMD_NUM_KERNEL_INTERFACES * sizeof int + * + * AOCL_MMD_PLL_INTERFACES - the handle for each pll associated with each + * kernel interface. If a kernel interface is not clocked by acl_kernel_clk + * then return -1 + * + * */ +typedef enum { + AOCL_MMD_NUM_KERNEL_INTERFACES = 1, /* Number of Kernel interfaces (int) */ + AOCL_MMD_KERNEL_INTERFACES = 2, /* Kernel interface (int*) */ + AOCL_MMD_PLL_INTERFACES = 3, /* Kernel clk handles (int*) */ + AOCL_MMD_MEMORY_INTERFACE = 4, /* Global memory handle (int) */ + AOCL_MMD_TEMPERATURE = 5, /* Temperature measurement (float) */ + AOCL_MMD_PCIE_INFO = 6, /* PCIe information (char*) */ + AOCL_MMD_BOARD_NAME = 7, /* Name of board (char*) */ + AOCL_MMD_BOARD_UNIQUE_ID = 8, /* Unique ID of board (int) */ + AOCL_MMD_CONCURRENT_READS = 9, /* # of parallel reads; 1 is serial*/ + AOCL_MMD_CONCURRENT_WRITES = 10, /* # of parallel writes; 1 is serial*/ + AOCL_MMD_CONCURRENT_READS_OR_WRITES = 11, /* total # of concurrent operations read + writes*/ + AOCL_MMD_MIN_HOST_MEMORY_ALIGNMENT = 12, /* Min alignment that the BSP supports for host allocations (size_t) */ + AOCL_MMD_HOST_MEM_CAPABILITIES = 13, /* Capabilities of aocl_mmd_host_alloc() (unsigned int)*/ + AOCL_MMD_SHARED_MEM_CAPABILITIES = 14, /* Capabilities of aocl_mmd_shared_alloc (unsigned int)*/ + AOCL_MMD_DEVICE_MEM_CAPABILITIES = 15, /* Capabilities of aocl_mmd_device_alloc (unsigned int)*/ + AOCL_MMD_HOST_MEM_CONCURRENT_GRANULARITY = 16, /*(size_t)*/ + AOCL_MMD_SHARED_MEM_CONCURRENT_GRANULARITY = 17, /*(size_t)*/ + AOCL_MMD_DEVICE_MEM_CONCURRENT_GRANULARITY = 18, /*(size_t)*/ +} aocl_mmd_info_t; + +typedef struct { + unsigned long long int exception_type; + void* user_private_info; + size_t user_cb; +} aocl_mmd_interrupt_info; + +typedef void (*aocl_mmd_interrupt_handler_fn)(int handle, void* user_data); +typedef void (*aocl_mmd_device_interrupt_handler_fn)(int handle, aocl_mmd_interrupt_info* data_in, void* user_data); +typedef void (*aocl_mmd_status_handler_fn)(int handle, void* user_data, aocl_mmd_op_t op, int status); + +/* Get information about the board using the enum aocl_mmd_offline_info_t for + * offline info (called without a handle), and the enum aocl_mmd_info_t for + * info specific to a certain board. + * Arguments: + * + * requested_info_id - a value from the aocl_mmd_offline_info_t enum + * + * param_value_size - size of the param_value field in bytes. This should + * match the size of the return type expected as indicated in the enum + * definition. For example, the AOCL_MMD_TEMPERATURE returns a float, so + * the param_value_size should be set to sizeof(float) and you should + * expect the same number of bytes returned in param_size_ret. + * + * param_value - pointer to the variable that will receive the returned info + * + * param_size_ret - receives the number of bytes of data actually returned + * + * Returns: a negative value to indicate error. + */ +AOCL_MMD_CALL int aocl_mmd_get_offline_info(aocl_mmd_offline_info_t requested_info_id, + size_t param_value_size, + void* param_value, + size_t* param_size_ret) WEAK; + +AOCL_MMD_CALL int aocl_mmd_get_info(int handle, + aocl_mmd_info_t requested_info_id, + size_t param_value_size, + void* param_value, + size_t* param_size_ret) WEAK; + +/* Open and initialize the named device. + * + * The name is typically one specified by the AOCL_MMD_BOARD_NAMES offline + * info. + * + * Arguments: + * name - open the board with this name (provided as a C-style string, + * i.e. NUL terminated ASCII.) + * + * Returns: the non-negative integer handle for the board, otherwise a + * negative value to indicate error. Upon receiving the error, the OpenCL + * runtime will proceed to open other known devices, hence the MMD mustn't + * exit the application if an open call fails. + */ +AOCL_MMD_CALL int aocl_mmd_open(const char* name) WEAK; + +/* Close an opened device, by its handle. + * Returns: 0 on success, negative values on error. + */ +AOCL_MMD_CALL int aocl_mmd_close(int handle) WEAK; + +/* Set the interrupt handler for the opened device. + * The interrupt handler is called whenever the client needs to be notified + * of an asynchronous event signaled by the device internals. + * For example, the kernel has completed or is stalled. + * + * Important: Interrupts from the kernel must be ignored until this handler is + * set + * + * Arguments: + * fn - the callback function to invoke when a kernel interrupt occurs + * user_data - the data that should be passed to fn when it is called. + * + * Returns: 0 if successful, negative on error + */ +AOCL_MMD_CALL int aocl_mmd_set_interrupt_handler(int handle, aocl_mmd_interrupt_handler_fn fn, void* user_data) WEAK; + +/* Set the device interrupt handler for the opened device. + * The device interrupt handler is called whenever the client needs to be notified + * of a device event signaled by the device internals. + * For example, an ECC error has been reported. + * + * Important: Interrupts from the device must be ignored until this handler is + * set + * + * Arguments: + * fn - the callback function to invoke when a device interrupt occurs + * user_data - the data that should be passed to fn when it is called. + * + * Returns: 0 if successful, negative on error + */ +AOCL_MMD_CALL int aocl_mmd_set_device_interrupt_handler(int handle, + aocl_mmd_device_interrupt_handler_fn fn, + void* user_data) WEAK; + +/* Set the operation status handler for the opened device. + * The operation status handler is called with + * status 0 when the operation has completed successfully. + * status negative when the operation completed with errors. + * + * Arguments: + * fn - the callback function to invoke when a status update is to be + * performed. + * user_data - the data that should be passed to fn when it is called. + * + * Returns: 0 if successful, negative on error + */ +AOCL_MMD_CALL int aocl_mmd_set_status_handler(int handle, aocl_mmd_status_handler_fn fn, void* user_data) WEAK; + +/* If AOCL_MMD_USES_YIELD is 1, this function is called when the host is idle + * and hence possibly waiting for events to be processed by the device. + * If AOCL_MMD_USES_YIELD is 0, this function is never called and the MMD is + * assumed to provide status/event updates via some other execution thread + * such as through an interrupt handler. + * + * Returns: non-zero if the yield function performed useful work such as + * processing DMA transactions, 0 if there is no useful work to be performed + * + * NOTE: yield may be called continuously as long as it reports that it has useful work + */ +AOCL_MMD_CALL int aocl_mmd_yield(int handle) WEAK; + +/* Read, write and copy operations on a single interface. + * If op is NULL + * - Then these calls must block until the operation is complete. + * - The status handler is not called for this operation. + * + * If op is non-NULL, then: + * - These may be non-blocking calls + * - The status handler must be called upon completion, with status 0 + * for success, and a negative value for failure. + * + * Arguments: + * op - the operation object used to track this operations progress + * + * len - the size in bytes to transfer + * + * src - the host buffer being read from + * + * dst - the host buffer being written to + * + * mmd_interface - the handle to the interface being accessed. E.g. To + * access global memory this handle will be whatever is returned by + * aocl_mmd_get_info when called with AOCL_MMD_MEMORY_INTERFACE. + * + * offset/src_offset/dst_offset - the byte offset within the interface that + * the transfer will begin at. + * + * The return value is 0 if the operation launch was successful, and + * negative otherwise. + */ +AOCL_MMD_CALL int aocl_mmd_read( + int handle, aocl_mmd_op_t op, size_t len, void* dst, int mmd_interface, size_t offset) WEAK; +AOCL_MMD_CALL int aocl_mmd_write( + int handle, aocl_mmd_op_t op, size_t len, const void* src, int mmd_interface, size_t offset) WEAK; +AOCL_MMD_CALL int aocl_mmd_copy( + int handle, aocl_mmd_op_t op, size_t len, int mmd_interface, size_t src_offset, size_t dst_offset) WEAK; + +/* Host Channel create operation + * Opens channel between host and kernel. + * + * Arguments: + * channel_name - name of channel to initialize. Same name as used in board_spec.xml + * + * queue_depth - the size in bytes of pinned memory queue in system memory + * + * direction - the direction of the channel + * + * The return value is negative if initialization was unsuccessful, and + * positive otherwise. Positive return value is handle to the channel to be used for + * subsequent calls for the channel. + */ +AOCL_MMD_CALL int aocl_mmd_hostchannel_create(int handle, char* channel_name, size_t queue_depth, int direction) WEAK; + +/* Host Channel destroy operation + * Closes channel between host and kernel. + * + * Arguments: + * channel - the handle to the channel to close, that was obtained with + * create channel + * + * The return value is 0 if the destroy was successful, and negative + * otherwise. + */ +AOCL_MMD_CALL int aocl_mmd_hostchannel_destroy(int handle, int channel) WEAK; + +/* Host Channel get buffer operation + * Provide host with pointer to buffer they can access to write or + * read from kernel, along with space or data available in the buffer + * in bytes. + * + * Arguments: + * channel - the handle to the channel to get the buffer for + * + * buffer_size - the address that this call will write the amount of + * space or data that's available in the buffer, + * depending on direction of the channel, in bytes + * + * status - the address that this call will write to for result of this + * call. Value will be 0 for success, and negative otherwise + * + * The return value is the pointer to the buffer that host can write + * to or read from. NULL if the status is negative. + */ +AOCL_MMD_CALL void* aocl_mmd_hostchannel_get_buffer(int handle, int channel, size_t* buffer_size, int* status) WEAK; + +/* Host Channel acknowledge buffer operation + * Acknowledge to the channel that the user has written or read data from + * it. This will make the data or additional buffer space available to + * write to or read from kernel. + * + * Arguments: + * channel - the handle to the channel that user is acknowledging + * + * send_size - the size in bytes that the user is acknowledging + * + * status - the address that this call will write to for result of this + * call. Value will be 0 for success, and negative otherwise + * + * The return value is equal to send_size if send_size was less than or + * equal to the buffer_size from get buffer call. If send_size was + * greater, then return value is the amount that was actually sent. + */ +AOCL_MMD_CALL size_t aocl_mmd_hostchannel_ack_buffer(int handle, int channel, size_t send_size, int* status) WEAK; + +/* Program the device + * + * The host will guarantee that no operations are currently executing on the + * device. That means the kernels will be idle and no read/write/copy + * commands are active. Interrupts should be disabled and the FPGA should + * be reprogrammed with the data from user_data which has size size. The host + * will then call aocl_mmd_set_status_handler and aocl_mmd_set_interrupt_handler + * again. At this point interrupts can be enabled. + * + * The new handle to the board after reprogram does not have to be the same as + * the one before. + * + * Arguments: + * user_data - The binary contents of the fpga.bin file created during + * Quartus II compilation. + * size - the size in bytes of user_data + * program_mode - bit field for programming attributes. See + * aocl_mmd_program_mode_t definition + * + * Returns: the new non-negative integer handle for the board, otherwise a + * negative value to indicate error. + */ + +#ifdef DLA_MMD +AOCL_MMD_CALL int aocl_mmd_save_pcie(int handle) WEAK; +AOCL_MMD_CALL int aocl_mmd_restore_pcie(int handle) WEAK; +// CoreDLA BSP has removed some stuff that MMD tries to handshake with, so provide a "raw access" function to +// reprogram the FPGA directly from the sof. Can't call quartus_pgm directly since the MMD still needs to mask +// the PCIe surprise down error (when full-chip programming the FPGA, the CPU thinks a PCIe device has disappeared). +// BEWARE: reprogramming will invalidate the handle +AOCL_MMD_CALL int aocl_mmd_program_sof(int handle, const char* sof_filename, const bool skipSaveRestore = false) WEAK; +#else +AOCL_MMD_CALL int aocl_mmd_program(int handle, void* user_data, size_t size, aocl_mmd_program_mode_t program_mode) WEAK; +#endif + +/** Error values*/ +#define AOCL_MMD_ERROR_SUCCESS 0 +#define AOCL_MMD_ERROR_INVALID_HANDLE -1 +#define AOCL_MMD_ERROR_OUT_OF_MEMORY -2 +#define AOCL_MMD_ERROR_UNSUPPORTED_ALIGNMENT -3 +#define AOCL_MMD_ERROR_UNSUPPORTED_PROPERTY -4 +#define AOCL_MMD_ERROR_INVALID_POINTER -5 +#define AOCL_MMD_ERROR_INVALID_MIGRATION_SIZE -6 + +/** Memory properties*/ +typedef enum { + /** + * Specifies the name of a global memory that can be found in the + * board_spec.xml file for the BSP. Allocations will be allocated to this + * global memory interface. + */ + AOCL_MMD_MEM_PROPERTIES_GLOBAL_MEMORY = 1, + /** + * Specifies the index of a bank inside the global memory interface that can be found in + * the board_spec.xml file for the BSP. Allocations will be allocated to this + * memory bank. It is invalid to specify this property without also specifying + * AOCL_MMD_GLOBAL_MEMORY_INTERFACE. + */ + AOCL_MMD_MEM_PROPERTIES_MEMORY_BANK +} aocl_mmd_mem_properties_t; + +/** + * Host allocations provide memory that is allocated on the host. Host + * allocations are accessible by the host and one or more devices. + * The same pointer to a host allocation may be used on the host and all + * supported devices; they have address equivalence. This memory must be + * deallocated with aocl_mmd_free(); + * + * Once the device has signaled completion through + * aocl_mmd_interrupt_handler_fn() the host can assume it has access to the + * latest contents of the memory, allocated by this call. + * + * @param handles Handles for devices that will need access to this memory + * @param num_devices Number of devices in the handles + * @param size The size of the memory region + * @param alignment The alignment in bytes of the allocation + * @param properties Specifies additional information about the allocated + * memory, described by a property type name and its corresponding value. + * Each property type name is immediately followed by the corresponding + * desired value. The list is terminated with 0. Supported values are + * described above. Example: [<property1>, <value1>, <property2>, <value2>, 0] + * @param error The error code defined by AOCL_MMD_ERROR* + * @return valid pointer, on error NULL + */ +AOCL_MMD_CALL void* aocl_mmd_host_alloc(int* handles, + size_t num_devices, + size_t size, + size_t alignment, + aocl_mmd_mem_properties_t* properties, + int* error) WEAK; + +/** + * Frees memory that has been allocated by MMD + * + * @param mem The pointer to the memory region. Must be a pointer that is + * allocated by the MMD. + * @return AOCL_MMD_ERROR_SUCCESS if success, else error code + */ +AOCL_MMD_CALL int aocl_mmd_free(void* mem) WEAK; + +/** + * Allocate memory that is owned by the device. This pointer can only be + * accessed by the kernel; can't be accessed by the host. The host is able to + * manipulate the pointer (e.g. increment it) just not access the underlying + * data. This memory must be deallocated by aocl_mmd_free(); + * + * @param handle Device that will have access to this memory + * @param size The size of the memory region + * @param alignment The alignment in bytes of the memory region + * @param properties Specifies additional information about the allocated + * memory, described by a property type name and its corresponding value. + * Each property type name is immediately followed by the corresponding + * desired value. The list is terminated with 0. Supported values are + * described above. Example: [<property1>, <value1>, <property2>, <value2>, 0] + * @param error The error code defined by AOCL_MMD_ERROR* + * @return Pointer that can be passed into the kernel. NULL on failure. + */ +AOCL_MMD_CALL void* aocl_mmd_device_alloc( + int handle, size_t size, size_t alignment, aocl_mmd_mem_properties_t* properties, int* error) WEAK; + +/** + * Shared allocations may migrate between the host and one or more associated + * device. The same pointer to a shared allocation may be used on the host and + * the supported device; they have address equivalence. + * + * If the device does not support concurrent access to memory allocated by + * aocl_mmd_shared_alloc() then a call must be made to + * aocl_mmd_shared_mem_migrate() to indicate that the shared allocation should + * be migrated to the device before the device accesses this memory. For + * example, a call to aocl_mmd_shared_mem_migrate() should be made before a + * kernel accessing this memory is launched). Conversely, + * aocl_mmd_shared_mem_migrate() should be called again to indicate that the + * shared allocation should be migrated to the host before the host accesses + * this memory again. If the device supports concurrent access to memory + * allocated with aocl_mmd_shared_alloc(), then the call to + * aocl_mmd_shared_mem_migrate() is not necessary, but may still be made. In + * the case of concurrent access, it is the responsibility of the MMD to ensure + * both the device and host can access aocl_mmd_shared_alloc() allocations at + * all times. + * + * Memory allocated by aocl_mmd_shared_alloc() must be deallocated with + * aocl_mmd_free(). + * + * @param handle Device that will have access to this memory + * @param size The size of the memory region + * @param alignment The alignment in bytes of the memory region + * @param properties Specifies additional information about the allocated + * memory, described by a property type name and its corresponding value. + * Each property type name is immediately followed by the corresponding + * desired value. The list is terminated with 0. Supported properties are + * listed above and have the prefix AOCL_MMD_MEM_PROPERTIES_. + * Example: [<property1>, <value1>, <property2>, <value2>, 0] + * @param error The error code defined by AOCL_MMD_ERROR* + * @return valid pointer, on error NULL + */ +AOCL_MMD_CALL void* aocl_mmd_shared_alloc( + int handle, size_t size, size_t alignment, aocl_mmd_mem_properties_t* properties, int* error) WEAK; + +typedef enum { AOCL_MMD_MIGRATE_TO_HOST = 0, AOCL_MMD_MIGRATE_TO_DEVICE = 1 } aocl_mmd_migrate_t; + +/** + * A call to aocl_mmd_shared_migrate() must be made for non-concurrent shared + * allocations any time the accessor of the allocation changes. For example, + * aocl_mmd_shared_migrate() should be called indicating that the allocation + * should be migrated to the device before a kernel accessing the allocation + * is launched on the device. Similarly, aocl_mmd_shared_migrate() should be + * called indicating that the allocation is migrated to the host before the + * host accesses the memory after kernel completion. + * + * For concurrent allocations this call may be used as a performance hint, but + * is not strictly required for functionality. + * + * @param handle Device that will have access to this memory + * @param shared_ptr Pointer allocated by aocl_mmd_shared_alloc() + * @param size In bytes, the size of the migration. Must be of multiple of a + * page boundary that the BSP supports. + * @param destination The destination of migration + * @return The error code defined by AOCL_MMD_ERROR* + */ +AOCL_MMD_CALL int aocl_mmd_shared_migrate(int handle, + void* shared_ptr, + size_t size, + aocl_mmd_migrate_t destination) WEAK; + +// CoreDLA modifications +// To support multiple different FPGA boards, anything board specific must be implemented in a +// board-specific MMD instead of the CoreDLA runtime layer. +#ifdef DLA_MMD +// Query functions to get board-specific values +AOCL_MMD_CALL int dla_mmd_get_max_num_instances() WEAK; +AOCL_MMD_CALL uint64_t dla_mmd_get_ddr_size_per_instance() WEAK; +AOCL_MMD_CALL double dla_mmd_get_ddr_clock_freq() WEAK; + +// Wrappers around CSR and DDR reads and writes to abstract away board-specific offsets +AOCL_MMD_CALL int dla_mmd_csr_write(int handle, int instance, uint64_t addr, const uint32_t* data) WEAK; +AOCL_MMD_CALL int dla_mmd_csr_read(int handle, int instance, uint64_t addr, uint32_t* data) WEAK; +AOCL_MMD_CALL int dla_mmd_ddr_write(int handle, int instance, uint64_t addr, uint64_t length, const void* data) WEAK; +AOCL_MMD_CALL int dla_mmd_ddr_read(int handle, int instance, uint64_t addr, uint64_t length, void* data) WEAK; + +// Get the PLL clock frequency in MHz, returns a negative value if there is an error +AOCL_MMD_CALL double dla_mmd_get_coredla_clock_freq(int handle) WEAK; +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/access.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/access.h new file mode 100644 index 0000000..dc3eae2 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/access.h @@ -0,0 +1,100 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file access.h + * @brief Functions to acquire, release, and reset OPAE FPGA resources + */ + +#ifndef __FPGA_ACCESS_H__ +#define __FPGA_ACCESS_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Open an FPGA object + * + * Acquires ownership of the FPGA resource referred to by 'token'. + * + * Most often this will be used to open an accelerator object to directly interact + * with an accelerator function, or to open an FPGA object to perform + * management functions. + * + * @param[in] token Pointer to token identifying resource to acquire + * ownership of + * @param[out] handle Pointer to preallocated memory to place a handle in. + * This handle will be used in subsequent API calls. + * @param[in] flags One of the following flags: + * * FPGA_OPEN_SHARED allows the resource to be opened + * multiple times (not supported in ASE) + * @returns FPGA_OK on success. FPGA_NOT_FOUND if the resource for + * 'token' could not be found. FPGA_INVALID_PARAM if + * 'token' does not refer to a resource that can be + * opened, or if either argument is NULL or invalid. + * FPGA_EXCEPTION if an internal exception occurred while + * creating the handle. FPGA_NO_DRIVER if the driver is + * not loaded. FPGA_BUSY if trying to open a resource that + * has already been opened in exclusive mode. + * FPGA_NO_ACCESS if the current process' privileges are + * not sufficient to open the resource. + */ + __FPGA_API__ fpga_result fpgaOpen(fpga_token token, fpga_handle *handle, + int flags); + +/** + * Close a previously opened FPGA object + * + * Relinquishes ownership of a previously fpgaOpen()ed resource. This enables + * others to acquire ownership if the resource was opened exclusively. + * Also deallocates / unmaps MMIO and UMsg memory areas. + * + * @param[in] handle Handle to previously opened FPGA object + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does + * not refer to an acquired resource, or if handle is NULL. + * FPGA_EXCEPTION if an internal error occurred while + * accessing the handle. + */ +__FPGA_API__ fpga_result fpgaClose(fpga_handle handle); + +/** + * Reset an FPGA object + * + * Performs an accelerator reset. + * + * @param[in] handle Handle to previously opened FPGA object + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does + * not refer to an acquired resource or to a resoure that + * cannot be reset. FPGA_EXCEPTION if an internal error + * occurred while trying to access the handle or resetting + * the resource. + */ +__FPGA_API__ fpga_result fpgaReset(fpga_handle handle); + +END_C_DECL + +#endif // __FPGA_ACCESS_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/buffer.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/buffer.h new file mode 100644 index 0000000..e848182 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/buffer.h @@ -0,0 +1,154 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file buffer.h + * @brief Functions for allocating and sharing system memory with an FPGA + * accelerator + * + * To share memory between a software application and an FPGA accelerator, + * these functions set up system components (e.g. an IOMMU) to allow + * accelerator access to a provided memory region. + * + * There are a number of restrictions on what memory can be shared, depending + * on platform capabilities. Usually, FPGA accelerators to not have access to + * virtual address mappings of the CPU, so they can only access physical + * addresses. To support this, the OPAE C library on Linux uses hugepages to + * allocate large, contiguous pages of physical memory that can be shared with + * an accalerator. It also supports sharing memory that has already been + * allocated by an application, as long as that memory satisfies the + * requirements of being physically contigous and page-aligned. + */ + +#ifndef __FPGA_BUFFER_H__ +#define __FPGA_BUFFER_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Prepare a shared memory buffer + * + * Prepares a memory buffer for shared access between an accelerator and the calling + * process. This may either include allocation of physcial memory, or + * preparation of already allocated memory for sharing. The latter case is + * indicated by supplying the FPGA_BUF_PREALLOCATED flag. + * + * This function will ask the driver to pin the indicated memory (make it + * non-swappable), and program the IOMMU to allow access from the accelerator. If the + * buffer was not pre-allocated (flag FPGA_BUF_PREALLOCATED), the function + * will also allocate physical memory of the requested size and map the + * memory into the caller's process' virtual address space. It returns in + * 'wsid' an fpga_buffer object that can be used to program address registers + * in the accelerator for shared access to the memory. + * + * When using FPGA_BUF_PREALLOCATED, the input len must be a non-zero multiple + * of the page size, else the function returns FPGA_INVALID_PARAM. When not + * using FPGA_BUF_PREALLOCATED, the input len is rounded up to the nearest + * multiple of page size. + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] len Length of the buffer to allocate/prepare in bytes + * @param[inout] buf_addr Virtual address of buffer. Contents may be NULL (OS + * will choose mapping) or non-NULL (OS will take + * contents as a hint for the virtual address). + * @param[out] wsid Handle to the allocated/prepared buffer to be used + * with other functions + * @param[in] flags Flags. FPGA_BUF_PREALLOCATED indicates that memory + * pointed at in '*buf_addr' is already allocated an + * mapped into virtual memory. + * @returns FPGA_OK on success. FPGA_NO_MEMORY if the requested memory could + * not be allocated. FPGA_INVALID_PARAM if invalid parameters were provided, or + * if the parameter combination is not valid. FPGA_EXCEPTION if an internal + * exception occurred while trying to access the handle. + */ +__FPGA_API__ fpga_result fpgaPrepareBuffer(fpga_handle handle, + uint64_t len, + void **buf_addr, uint64_t *wsid, int flags); + +/** + * Release a shared memory buffer + * + * Releases a previously prepared shared buffer. If the buffer was allocated + * using fpgaPrepareBuffer (FPGA_BUF_PREALLOCATED was not specified), this call + * will deallocate/free that memory. Otherwise, it will only be returned to + * it's previous state (pinned/unpinned, cached/non-cached). + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] wsid Handle to the allocated/prepared buffer + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if invalid parameters were + * provided, or if the parameter combination is not valid. FPGA_EXCEPTION if an + * internal exception occurred while trying to access the handle. + */ +__FPGA_API__ fpga_result fpgaReleaseBuffer(fpga_handle handle, uint64_t wsid); + +/** + * Retrieve base IO address for buffer + * + * This function is used to acquire the physical base address (on some platforms + * called IO Virtual Address or IOVA) for a shared buffer identified by wsid. + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] wsid Buffer handle / workspace ID referring to the buffer for + * which the IO address is requested + * @param[out] ioaddr Pointer to memory where the IO address will be returned + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if invalid parameters were + * provided, or if the parameter combination is not valid. FPGA_EXCEPTION if an + * internal exception occurred while trying to access the handle. + * FPGA_NOT_FOUND if `wsid` does not refer to a previously shared buffer. + */ +__FPGA_API__ fpga_result fpgaGetIOAddress(fpga_handle handle, uint64_t wsid, + uint64_t *ioaddr); + +/** + * Retrieve physical address for buffer + * + * This function is used to acquire the physical addresses in a scatter gather + * list form for a shared buffer identified by wsid. + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] wsid Buffer handle / workspace ID referring to the buffer for + * which the physical address is requested + * @param[out] num_pages Number of physical pages + * @param[out] sglist SG list structure where physical addresses of pages and + * number of bytes in that page used will be returned. + * + * Note: Call this API with sg_list as NULL to update num_pages. Allocate upto + * (num_pages * sg_list) memory and call the API again with a pointer to this + * memory location as the last argument to retrieve the sg_list struct. + * + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if invalid parameters were + * provided, or if the parameter combination is not valid. FPGA_EXCEPTION if an + * internal exception occurred while trying to access the handle. + * FPGA_NOT_FOUND if `wsid` does not refer to a previously shared buffer. + */ +__FPGA_API__ fpga_result fpgaGetPhysicalAddress(fpga_handle handle, uint64_t wsid, uint64_t *num_pages, + void *sglist); + +END_C_DECL + +#endif // __FPGA_BUFFER_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/dma.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/dma.h new file mode 100644 index 0000000..8febd44 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/dma.h @@ -0,0 +1,144 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file dma.h + * @brief Functions to acquire, release, and reset OPAE FPGA DMA resources + */ + +#ifndef __DMA_ACCESS_H__ +#define __DMA_ACCESS_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/* +* The DMA driver supports host to FPGA, FPGA to host +* and FPGA to FPGA transfers. The FPGA interface can +* be streaming or memory-mapped. Streaming interfaces +* are not currently +* supported. +*/ +typedef enum { + HOST_TO_FPGA_MM = 0, + FPGA_TO_HOST_MM, + FPGA_TO_FPGA_MM, + FPGA_MAX_TRANSFER_TYPE, +}fpga_dma_transfer; + + +typedef enum +{ + DMA_OPEN = 1, + DMA_BUSY, + DMA_CLOSED +}fpga_dma_status; + +/* + * Dma handle in user space that will be populated during fpgaDmaOpen call. + */ +typedef struct _fpga_dma_handle +{ + // + // Stores the handle to the fpga that was opened after fpgaOpen + // + fpga_handle fpga_h; + + // + // Stores the current status of the DMA AFC + // Set to the following values: + // DMA_OPEN - After call to fpgaDmaOpen() and when fpgaDmaTransferSync() exits + // DMA_BUSY - When fpgaDmaTransferSync() is called + // + uint64_t dma_status; +}dma_handle, *fpga_dma_handle; + + + +/** +* +* Opens a handle to DMA +* Sets the status of DMA engine to DMA_OPEN +* @param[in] handle Handle to previously opened FPGA object +* @param[in] dma_h DMA handle allocated by the user +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does +* not refer to an acquired resource. +* +*/ +__FPGA_API__ +fpga_result +fpgaDmaOpen( + fpga_handle handle, + fpga_dma_handle *dma_h +); + +/** +* +* Closes a handle to DMA +* Sets the status of DMA engine to DMA_CLOSED +* @param[in] handle Handle to previously opened FPGA object +* @param[in] dma_h DMA handle allocated by the user +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does +* not refer to an acquired resource. +* +*/ +__FPGA_API__ +fpga_result +fpgaDmaClose( + fpga_dma_handle dma_h +); + + +/** +* +* Performs a synchronous DMA transfer between FPGA and host memory. +* +* @param[in] handle Handle to previously opened FPGA object +* @param[in] dst Destination address for the data transfer +* @param[in] src Source address for the data transfer +* @param[in] count Length of data to be transferred from src to dst +* @param[in] flag Flag to indicate nature of data transfer. Flag types = + HOST_TO_FPGA_MM and FPGA_TO_HOST_MM. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does +* not refer to an acquired resource or to a resoure that +* cannot be reset. FPGA_EXCEPTION if an internal error +* occurred while trying to access the handle or resetting +* the resource. +*/ +__FPGA_API__ +fpga_result +fpgaDmaTransferSync( + fpga_dma_handle handle, + ULONG64 dst, + ULONG64 src, + ULONG64 count, + ULONG64 flag +); + +END_C_DECL + +#endif // __DMA_ACCESS_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/enum.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/enum.h new file mode 100644 index 0000000..ee3349b --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/enum.h @@ -0,0 +1,129 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file enum.h + * @brief APIs for resource enumeration and managing tokens + * + * These APIs are the first step for any application using OPAE to discover + * resources that are present on the system. They allow selective enumeration + * (i.e. getting a list of resources that match a given list of criteria) and + * methods to manage the lifecycle of tokens generated by fpgaEnumerate(). + */ + +#ifndef __FPGA_ENUM_H__ +#define __FPGA_ENUM_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Enumerate FPGA resources present in the system + * + * This call allows the user to query the system for FPGA resources that match + * a certain set of criteria, e.g. all accelerators that are assigned to a host + * interface and available, all FPGAs of a specific type, etc. + * + * fpgaEnumerate() will create a number of `fpga_token`s to represent the + * matching resources and populate the array `tokens` with these tokens. The + * `max_tokens` argument can be used to limit the number of tokens + * allocated/returned by fpgaEnumerate(); i.e., the number of tokens in the + * returned `tokens` array will be either `max_tokens` or `num_matches` (the + * number of resources matching the filter), whichever is smaller. Use + * fpgaDestroyToken() to destroy tokens that are no longer needed. + * + * To query the number of matches for a particular set of filters (e.g. to + * allocate a `tokens` array of the appropriate size), call fpgaEnumerate() + * with the parameter `tokens` set to NULL; this will only return the number of + * matches in `num_matches`. + * + * @Note fpgaEnumerate() will allocate memory for the created tokens returned + * in `tokens`. It is the responsibility of the using application to free this + * memory after use by calling fpgaDestroyToken() for each of the returned + * tokens. + * + * @param[in] filters Array of `fpga_properties` objects describing the + * properties of the objects that should be returned. A + * resource is considered matching if its properties + * match any one of the supplied filters. Passing NULL + * will match all FPGA resources present in the system. + * @param[in] num_filters Number of entries in the `filters` array. + * @param[out] tokens Pointer to an array of fpga_token variables to be + * populated. If NULL is supplied, fpgaEnumerate() will + * not create any tokens, but it will return the + * number of possible matches in `num_match`. + * @param[in] max_tokens Maximum number of tokens that fpgaEnumerate() shall + * return (length of `tokens` array). There may be more + * or fewer matches than this number; `num_matches` is + * set to the number of actual matches. + * @param[out] num_matches Number of resources matching the `filter` criteria. + * This number can be higher than the number of tokens + * returned in the `tokens` array (depending on the + * value of `max_tokens`). + * @returns FPGA_OK on success. + * FPGA_INVALID_PARAM if invalid pointers or objects + * are passed into the function. + * FPGA_NO_DRIVER if OPAE can't find the respective + * enumeration data structures usually provided by the + * driver. + * FPGA_NO_MEMORY if there was not enough memory to + * create tokens. + */ +__FPGA_API__ fpga_result fpgaEnumerate(const fpga_properties *filters, + uint32_t num_filters, fpga_token *tokens, + uint32_t max_tokens ,uint32_t *num_matches); + +/** + * Clone a fpga_token object + * + * Creates a copy of an fpga_token object. + * + * @Note This call creates a new token object and allocates memory for it. It + * is the responsibility of the using application to free this memory after use + * by calling fpgaDestroyToken() for the cloned token. + * + * @param[in] src fpga_token object to copy + * @param[out] dst New fpga_token object cloned from 'src' + * @returns FPGA_OK on success + */ +__FPGA_API__ fpga_result fpgaCloneToken(fpga_token src, fpga_token *dst); + +/** + * Destroy a Token + * + * This function destroys a token created by fpgaEnumerate() and frees the + * associated memory. + * + * @param[in] token fpga_token to destroy + * @returns FPGA_OK on success + */ +__FPGA_API__ fpga_result fpgaDestroyToken(fpga_token *token); + +END_C_DECL + +#endif // __FPGA_ENUM_H__ + diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/event.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/event.h new file mode 100644 index 0000000..3d53554 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/event.h @@ -0,0 +1,151 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file event.h + * @brief Functions for registering events and managing the lifecycle for + * `fpga_event_handle`s. + * + * OPAE provides an interface to asynchronous events that can be generated by + * different FPGA resources. The event API provides functions to register for + * these events; associated with every event a process has registered for is an + * fpga_event_handle, which encapsulates the OS-specific data structure for + * event objects. On Linux, an fpga_event_handle can be used as a file + * descriptor and passed to select(), poll(), epoll() and similar functions to + * wait for asynchronous events. + */ + +#ifndef __FPGA_EVENT_H__ +#define __FPGA_EVENT_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Initialize an event_handle + * + * Platform independent way to initialize an event_handle used for + * notifications from the driver to application. For Linux, this function + * creates an eventfd and returns the eventfd file descriptor in + * `*event_handle`. + * + * @param[out] event_handle Pointer to event handle variable. + * + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if `event_handle` is NULL. + * FPGA_NOT_SUPPORTED if platform does not support events. + */ +__FPGA_API__ fpga_result fpgaCreateEventHandle(fpga_event_handle *event_handle); + +/** + * Destroy an event_handle + * + * Destroy handle and free resources. On Linux this corresponds + * to closing the file descriptor pointed to by handle + * + * @param[in] event_handle Pointer to handle to be destroyed + * + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if `event_handle` is NULL. + */ +__FPGA_API__ fpga_result fpgaDestroyEventHandle(fpga_event_handle *event_handle); + +/** + * Register an FPGA event + * + * This function tells the driver that the caller is interested in notification + * for the event specified by the type and flags pair. + * + * The event_handle points to an OS specific mechanism for event notification. + * An event_handle is associated with only a single event. + * + * @todo define if calling fpgaRegisterEvent multiple times with the + * same event_handle is an error condition or if it is silently ignored. + * + * @note This function is currently not supported. + * + * @param[in] handle Handle to previously opened FPGA resource. + * @param[in] event_type Type of event + * @param[in] event_handle Handle to previously opened resource for event + * notification. + * @param[in] flags Optional argument for specifying additional + * information about event. For example irq number + * for interrupt events. + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does not refer to + * a resource supporting the requested event, or if event_handle is not valid. + * FPGA_EXCEPTION if an internal exception occurred while accessing the handle + * or the event_handle. On Linux: FPGA_NO_DAEMON if the driver does not support the + * requested event and there is no FPGA Daemon (fpgad) running to proxy it. + */ +__FPGA_API__ fpga_result fpgaRegisterEvent(fpga_handle handle, + fpga_event_type event_type, + fpga_event_handle event_handle, + uint32_t flags); + +/** + * Unregister an FPGA event + * + * This function tells the driver that the caller is no longer interested in + * notification for the event associated with the event_handle + * + * The event_handle points to an OS specific mechanism for event notification. + * An event_handle is associated with only a single event. + * + * @todo define if calling fpgaUnregisterEvent multiple times with the + * same event_handle is an error condition or if it is silently ignored. + * + * @note This function is currently not supported. + * + * @param[in] handle Handle to previously opened FPGA resource. + * @param[in] event_type Type of event. + * @param[in] event_handle Handle to previously opened resource for event + * notification. + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if handle does + * not refer to a resource supporting the requested event, + * or if event_handle is not valid. FPGA_EXCEPTION if an + * internal error occurred accessing the handle or the + * event_handle. + */ +__FPGA_API__ fpga_result fpgaUnregisterEvent(fpga_handle handle, fpga_event_type event_type, + fpga_event_handle event_handle); + +/** +* Get OS object from event handle +* +* Check validity of event handle, and get the OS object used to +* subscribe and unsubscribe to events. On Linux, the obkect corresponds +* to a file descriptor. +* +* @param[in] event_handle Event handle to get the descriptor value from +* @param[out] fd integer to store the descriptor value +* +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if `event_handle` is invalid. +*/ +__FPGA_API__ fpga_result fpgaGetOSObjectFromEventHandle(const fpga_event_handle event_handle, + int *fd); + +END_C_DECL + +#endif // __FPGA_EVENT_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/flash.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/flash.h new file mode 100644 index 0000000..f7a2c5c --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/flash.h @@ -0,0 +1,87 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file flash.h + * @brief Functions to erase the flash memory and reconfigure a slot with a new bitstream . + */ + +#ifndef __FLASH_H__ +#define __FLASH_H__ + +BEGIN_C_DECL + +/** +* +* Erase flash memory +* +* This function erases the flash memory of the FPGA device +* +* Arguments: +* @param[in] fpga_handle handle to previously opened FPGA_DEVICE resource +* +* Return Value: +* FPGA_OK on success. +* FPGA_INVALID_PARAM if the handle does not refer to an owned resource. +* FPGA_NOT_FOUND if this host interface number is not found . +* FPGA_NOT_SUPPORTED if funcionality not supported +* +**/ +__FPGA_API__ fpga_result +fpgaEraseFlash( + fpga_handle fpga_handle + ); + + +/** +* Writes flash memory +* +* This function programs the flash chip on the FPGA with the provided bitstream. +* +* Arguments: +* @param[in] handle handle to an FPGA_DEVICE resource +* @param[in] flashBitstream pointer to memory holding the flash bitstream +* @param[in] flashBitstreamLen length of the bitstream in bytes +* @param[in] offset offset in flash controller to begin writing from +* +* Return Value: +* FPGA_OK on success. +* FPGA_INVALID_PARAM if the handle does not refer to an owned resource. +* FPGA_NOT_FOUND if this host interface number is not found . +* FPGA_NOT_SUPPORTED if funcionality not supported. +*/ + +__FPGA_API__ fpga_result +fpgaWriteFlash( + fpga_handle handle, + PUINT8 flashBitstream, + UINT64 flashBitstreamLen, + UINT64 offset +); + +END_C_DECL + +#endif // __FLASH_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/fpga.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/fpga.h new file mode 100644 index 0000000..e6668e8 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/fpga.h @@ -0,0 +1,60 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * \file fpga.h + * \brief FPGA API + * + * This conveniently includes all APIs that a part of the OPAE release (base and + * extensions). + */ + +#ifndef __FPGA_FPGA_H__ +#define __FPGA_FPGA_H__ + +#define FPGA_API_VERSION_MAJOR 0 +#define FPGA_API_VERSION_MINOR 1 + +#ifdef _WIN32 +#include <Windows.h> +#endif + +#include <opae/types.h> +#include <opae/access.h> +#include <opae/buffer.h> +#include <opae/dma.h> +#include <opae/enum.h> +#include <opae/event.h> +#include <opae/flash.h> +#include <opae/manage.h> +#include <opae/mmio.h> +#include <opae/properties.h> +#include <opae/umsg.h> +#include <opae/utils.h> +#include <opae/version.h> + +#endif // __FPGA_FPGA_H__ + diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/macrodefs.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/macrodefs.h new file mode 100644 index 0000000..365cdaf --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/macrodefs.h @@ -0,0 +1,70 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file macrodefs.h + * @brief Definitions of conveinence macros for the OPAE C API + * + * This file defines convenience macros for the OPAE C API functions. + */ + +#ifndef __FPGA_MACRODEFS_H__ +#define __FPGA_MACRODEFS_H__ + +// Check for conflicting definitions +#ifdef BEGIN_C_DECL +#error BEGIN_C_DECL already defined, but used by the OPAE library +#endif + +#ifdef END_C_DECL +#error END_C_DECL already defined, but used by the OPAE library +#endif + +#ifdef __FPGA_API__ +#error __FPGA_API__ already defined, but used by the OPAE library +#endif + +// Macro for symbol visibility +#ifdef _WIN32 +#ifdef FpgaLib_EXPORTS +#define __FPGA_API__ __declspec(dllexport) +#else +#define __FPGA_API__ __declspec(dllimport) +#endif +#else +#define __FPGA_API__ __attribute__((visibility("default"))) +#endif + +// Macro for disabling name mangling +#ifdef __cplusplus +#define BEGIN_C_DECL extern "C" { +#define END_C_DECL } +#else +#define BEGIN_C_DECL +#define END_C_DECL +#endif + +#endif // __FPGA_MACRODEFS_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/manage.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/manage.h new file mode 100644 index 0000000..f93a1b1 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/manage.h @@ -0,0 +1,176 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file manage.h + * @brief Functions for managing FPGA configurations + * + * FPGA accelerators can be reprogrammed at run time by providing new partial + * bitstreams ("green bitstreams"). This file defines API functions for + * programming green bitstreams as well as for assigning accelerators to host + * interfaces for more complex deployment setups, such as virtualized systems. + */ + +#ifndef __FPGA_MANAGE_H__ +#define __FPGA_MANAGE_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** +* Assign Port to a host interface. +* +* This function assign Port to a host interface for subsequent use. Only +* Port that have been assigned to a host interface can be opened by +* fpgaOpen(). +* +* @param[in] fpga Handle to an FPGA object previously opened that +* both the host interface and the slot belong to +* @param[in] interface_num Host interface number +* @param[in] slot_num Slot number +* @param[in] flags Flags (to be defined) +* @returns FPGA_OK on success +* FPGA_INVALID_PARAM if input parameter combination +* is not valid. +* FPGA_EXCEPTION if an exception occcurred accessing +* the `fpga` handle. +* FPGA_NOT_SUPPORTED if driver does not support +* assignment. +*/ +__FPGA_API__ fpga_result fpgaAssignPortToInterface(fpga_handle fpga, + uint32_t interface_num, + uint32_t slot_num, + int flags); + +/** + * Assign an accelerator to a host interface + * + * This function assigns an accelerator to a host interface for subsequent use. Only + * accelerators that have been assigned to a host interface can be opened by + * fpgaOpen(). + * + * @note This function is currently not supported. + * + * @param[in] fpga Handle to an FPGA object previously opened that + * both the host interface and the accelerator belong to + * @param[in] afc Accelerator to assign + * @param[in] host_interface Host interface to assign accelerator to + * @param[in] flags Flags (to be defined) + * @returns FPGA_OK on success + */ +__FPGA_API__ fpga_result fpgaAssignToInterface(fpga_handle fpga, + fpga_token afc, + uint32_t host_interface, + int flags); + +/** + * Unassign a previously assigned accelerator + * + * This function removes the assignment of an accelerator to an host interface (e.g. to + * be later assigned to a different host interface). As a consequence, the accelerator + * referred to by token 'accelerator' will be reset during the course of this function. + * + * @note This function is currently not supported. + * + * @param[in] fpga Handle to an FPGA object previously opened that + * both the host interface and the accelerator belong to + * @param[in] afc Accelerator to unassign/release + * @returns FPGA_OK on success + */ +__FPGA_API__ fpga_result fpgaReleaseFromInterface(fpga_handle fpga, + fpga_token afc); + +/** + * Reconfigure a slot + * + * Sends a green bitstream file to an FPGA to reconfigure a specific slot. This + * call, if successful, will overwrite the currently programmed AFU in that + * slot with the AFU in the provided bitstream. + * + * As part of the reconfiguration flow, all accelerators associated with this slot will + * be unassigned and reset. + * + * @param[in] fpga Handle to an FPGA object previously opened + * @param[in] slot Token identifying the slot to reconfigure + * @param[in] bitstream Pointer to memory holding the bitstream + * @param[in] bitstream_len Length of the bitstream in bytes + * @param[in] flags Flags (to be defined) + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if the provided parameters + * are not valid. FPGA_EXCEPTION if an internal error occurred accessing the + * handle or while sending the bitstream data to the driver. FPGA_RECONF_ERROR + * on errors reported by the driver (such as CRC or protocol errors). + */ +__FPGA_API__ fpga_result fpgaReconfigureSlot(fpga_handle fpga, + uint32_t slot, + const uint8_t *bitstream, + size_t bitstream_len, int flags); + +/** + * Process device specific commands + * + * Sends a device specific command to the driver and driver performs that action + * and returns if needed with the data. + * + * @param[in] fpga Handle to an FPGA object previously opened + * @param[in] cmd GUID identifying the command to process + * @param[in] buffer Pointer to memory where data will be returned. + * @param[in] buffer_len Length of the buffer passed. + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if the provided parameters + * are not valid. FPGA_EXCEPTION if an internal error occurred accessing the + * handle or while sending the data to the driver. + */ +__FPGA_API__ fpga_result fpgaProcessDeviceCmd(fpga_handle fpga, + fpga_guid cmd, + void *arg, + void *buffer, + size_t buffer_len); + +/** + * Enumerate all the commands supported by the device. + * + * To enumerate all the commands supported by a specific device, call this + * function by passing NULL to buffer arg and it returns the number of bytes + * that needs to be allocated to get all the commands. + * + * Then allocate buffer for that size and call this function to get the list + * of all device supported CMDs. + * + * @param[in] fpga Handle to an FPGA object previously opened + * @param[in] cmds Pointer to memory where cmds will be returned. + * @param[in] num_cmds Pointer to memory where num cmds will be returned. + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if the provided parameters + * are not valid. FPGA_EXCEPTION if an internal error occurred accessing the + * handle or while sending the data to the driver. + */ +__FPGA_API__ fpga_result fpgaGetSupportedCommands(fpga_handle fpga, + fpga_guid *cmds, + uint32_t *num_cmds); + +END_C_DECL + +#endif // __FPGA_MANAGE_H__ + diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/mmio.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/mmio.h new file mode 100644 index 0000000..7c26d3f --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/mmio.h @@ -0,0 +1,342 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file mmio.h + * @brief Functions for mapping and accessing MMIO space + * + * Most FPGA accelerators provide access to control registers through + * memory-mappable address spaces, commonly referred to as "MMIO spaces". This + * file provides functions to map, unmap, read, and write MMIO spaces. + * + * Note that an accelerator may have multiple MMIO spaces, denoted by the + * `mmio_num` argument of the APIs below. The meaning and properties of each + * MMIO space are up to the accelerator designer. + */ + +#ifndef __FPGA_MMIO_H__ +#define __FPGA_MMIO_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Write 64 bit value to MMIO space + * + * This function will write to MMIO space of the target object at a specified + * offset. + * + * In order to access a resource's MMIO space using this function, it has to be + * mapped to the application's address space using fpgaMapMMIO(). + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] mmio_num Number of MMIO space to access + * @param[in] offset Byte offset into MMIO space + * @param[in] value Value to write (64 bit) + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. FPGA_NOT_FOUND if the MMIO space + * `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. + */ +__FPGA_API__ fpga_result fpgaWriteMMIO64(fpga_handle handle, + uint32_t mmio_num, uint64_t offset, + uint64_t value); + +/** + * Read 64 bit value from MMIO space + * + * This function will read from MMIO space of the target object at a specified + * offset. + * + * In order to access a resource's MMIO space using this function, it has to be + * mapped to the application's address space using fpgaMapMMIO(). + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] mmio_num Number of MMIO space to access + * @param[in] offset Byte offset into MMIO space + * @param[out] value Pointer to memory where read value is returned (64 bit) + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. FPGA_NOT_FOUND if the MMIO space + * `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. + */ +__FPGA_API__ fpga_result fpgaReadMMIO64(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, uint64_t *value); + +/** + * Write 32 bit value to MMIO space + * + * This function will write to MMIO space of the target object at a specified + * offset. + * + * In order to access a resource's MMIO space using this function, it has to be + * mapped to the application's address space using fpgaMapMMIO(). + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] mmio_num Number of MMIO space to access + * @param[in] offset Byte offset into MMIO space + * @param[in] value Value to write (32 bit) + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. FPGA_NOT_FOUND if the MMIO space + * `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. + */ +__FPGA_API__ fpga_result fpgaWriteMMIO32(fpga_handle handle, + uint32_t mmio_num, uint64_t offset, + uint32_t value); + +/** + * Read 32 bit value from MMIO space + * + * This function will read from MMIO space of the target object at a specified + * offset. + * + * In order to access a resource's MMIO space using this function, it has to be + * mapped to the application's address space using fpgaMapMMIO(). + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[in] mmio_num Number of MMIO space to access + * @param[in] offset Byte offset into MMIO space + * @param[out] value Pointer to memory where read value is returned (32 bit) + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. FPGA_NOT_FOUND if the MMIO space + * `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. + */ +__FPGA_API__ fpga_result fpgaReadMMIO32(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, uint32_t *value); + +/** + * Map MMIO space + * + * This function will return a pointer to the specified MMIO space of the + * target object in process virtual memory. Some MMIO spaces may be restricted + * to privileged processes, depending on the used handle and type. + * + * After mapping the respective MMIO space, you can access it either through + * direct pointer operations (observing supported access sizes and alignments + * of the target platform and accelerator), or by using fpgaReadMMIO32(), + * fpgaWriteMMIO32(), fpgeReadMMIO64(), fpgaWriteMMIO64(), fpgaReadMmio() + * and fpgaWriteMmio(). + * + * @note This call only supports returning an actual mmio_ptr for hardware + * targets, not for ASE simulation. Use fpgaReadMMIO32(), fpgaWriteMMIO32(), + * fpgeReadMMIO64(), and fpgaWriteMMIO64() if you need ASE simulation + * capabilities. You will still need to call fpgaMapMMIO() before using these + * functions, though. + * + * If the caller passes in NULL for mmio_ptr, no virtual address will be + * returned. This implies that all accesses will be performed through + * fpgaReadMMIO32(), fpgaWriteMMIO32(), fpgeReadMMIO64(), fpgaWriteMMIO64(), + * fpgaReadMmio() and fpgaWriteMmio(). This is the only supported case for ASE. + * + * The number of available MMIO spaces can be retrieved through the num_mmio + * property (fpgaPropertyGetNumMMIO()). + * + * @param[in] handle Handle to previously opened resource + * @param[in] mmio_num Number of MMIO space to access + * @param[out] mmio_ptr Pointer to memory where a pointer to the MMIO space + * will be returned. May be NULL, in which case no pointer + * is returned. + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. FPGA_NO_ACCESS if the process' + * permissions are not sufficient to map the requested MMIO space. + */ +__FPGA_API__ fpga_result fpgaMapMMIO(fpga_handle handle, + uint32_t mmio_num, uint64_t **mmio_ptr); + +/** + * Unmap MMIO space + * + * This function will unmap a previously mapped MMIO space of the target opject, + * rendering any pointers to it invalid. + * + * @note This call is only supported by hardware targets, not by ASE + * simulation. + * + * @param[in] handle Handle to previously opened resource + * @param[in] mmio_num Number of MMIO space to access + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied + * parameters is invalid. FPGA_EXCEPTION if an internal exception occurred + * while trying to access the handle. + */ +__FPGA_API__ fpga_result fpgaUnmapMMIO(fpga_handle handle, + uint32_t mmio_num); + +/** +* Reads the value from MMIO space. +* +* This function will read from MMIO space of the target object at a specified +* offset and length. +* +* In order to access a resource's MMIO space using this function, it has to be +* mapped to the application's address space using fpgaMapMMIO(). +* +* @param[in] handle Handle to previously opened accelerator resource +* @param[in] mmio_num Number of MMIO space to access +* @param[in] offset Byte offset into MMIO space +* @param[out] buffer Pointer to memory where read value is returned +* @param[in] length Length of the MMIO to read. +* @param[in] accessType Read MMIO as 8/16/32/64-bit reads. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. FPGA_NOT_FOUND if the MMIO space +* `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. +*/ +__FPGA_API__ fpga_result fpgaReadMmioType(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, + void* buffer, + uint32_t length, + uint32_t accessType); + +/** +* Write the value to MMIO space. +* +* This function will write to MMIO space of the target object at a specified +* offset and length. +* +* In order to access a resource's MMIO space using this function, it has to be +* mapped to the application's address space using fpgaMapMMIO(). +* +* @param[in] handle Handle to previously opened accelerator resource +* @param[in] mmio_num Number of MMIO space to access +* @param[in] offset Byte offset into MMIO space +* @param[in] buffer Pointer to memory from where data to be written. +* @param[in] length Length of the MMIO to write. +* @param[in] accessType Write MMIO as 8/16/32/64-bit writes. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. FPGA_NOT_FOUND if the MMIO space +* `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. +*/ +__FPGA_API__ fpga_result fpgaWriteMmioType(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, + void* buffer, + uint32_t length, + uint32_t accessType); + + +/** +* Reads the value from MMIO space. +* +* This function will read from MMIO space of the target object at a specified +* offset and length. +* +* In order to access a resource's MMIO space using this function, it has to be +* mapped to the application's address space using fpgaMapMMIO(). +* +* @param[in] handle Handle to previously opened accelerator resource +* @param[in] mmio_num Number of MMIO space to access +* @param[in] offset Byte offset into MMIO space +* @param[out] buffer Pointer to memory where read value is returned +* @param[in] length Length of the MMIO to read. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. FPGA_NOT_FOUND if the MMIO space +* `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. +*/ +__FPGA_API__ fpga_result fpgaReadMmio(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, + void *buffer, + uint32_t length); + +/** +* Write the value to MMIO space. +* +* This function will write to MMIO space of the target object at a specified +* offset and length. +* +* In order to access a resource's MMIO space using this function, it has to be +* mapped to the application's address space using fpgaMapMMIO(). +* +* @param[in] handle Handle to previously opened accelerator resource +* @param[in] mmio_num Number of MMIO space to access +* @param[in] offset Byte offset into MMIO space +* @param[in] buffer Pointer to memory from where data to be written. +* @param[in] length Length of the MMIO to write. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. FPGA_NOT_FOUND if the MMIO space +* `mmio_num` was not mapped using fpgaMapMMIO() before calling this function. +*/ +__FPGA_API__ fpga_result fpgaWriteMmio(fpga_handle handle, + uint32_t mmio_num, + uint64_t offset, + void *buffer, + uint32_t length); + +/** +* Read the config space of the device. +* +* This function will read the configuration space of the FPGA device +* +* @note This call is only supported by PCIe hardware targets, not by ASE +* simulation. +* +* @param[in] handle Handle to previously opened resource +* @param[in] offset Offset within the config space of the device. +* @param[in] buffer Pointer to the buffer where data read will be returned. +* @param[in] length Number of bytes to read. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. +*/ +__FPGA_API__ fpga_result fpgaReadPciConfigSpace(fpga_handle handle, + uint32_t offset, + void* buffer, + uint32_t length); + +/** +* Write to config space of the device. +* +* This function will write to configuration space of the FPGA device +* +* @note This call is only supported by PCIe hardware targets, not by ASE +* simulation. +* +* @param[in] handle Handle to previously opened resource +* @param[in] offset Offset within the config space of the device. +* @param[in] buffer Pointer to the buffer where data read will be returned. +* @param[in] length Number of bytes to read. +* @returns FPGA_OK on success. FPGA_INVALID_PARAM if any of the supplied +* parameters is invalid. FPGA_EXCEPTION if an internal exception occurred +* while trying to access the handle. +*/ +__FPGA_API__ fpga_result fpgaWritePciConfigSpace(fpga_handle handle, + uint32_t offset, + void* buffer, + uint32_t length); + +END_C_DECL + +#endif // __FPGA_MMIO_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/properties.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/properties.h new file mode 100644 index 0000000..03e5e79 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/properties.h @@ -0,0 +1,689 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file properties.h + * @brief Functions for examining and manipulating `fpga_properties` objects + * + * In OPAE, `fpga_properties` objects are used both for obtaining information + * about resources and for selectively enumerating resources based on their + * properties. This file provides accessor functions (get/set) to allow reading + * and writing individual items of an `fpga_properties` object. Generally, not + * all object types supported by OPAE carry all properties. If you call a + * property accessor method on a `fpga_properties` object that does not support + * this particular property, it will return FPGA_INVALID_PARAM. + * + * # Accessor Return Values + * In addition to the return values specified in the documentation below, all + * accessor functions return FPGA_OK on success, FPGA_INVALID_PARAM if you pass + * NULL or invalid parameters (i.e. non-initialized properties objects), + * FPGA_EXCEPTION if an internal exception occurred trying to access the + * properties object, FPGA_NOT_FOUND if the requested property is not part of + * the supplied properties object. + */ + +#ifndef __FPGA_PROPERTIES_H__ +#define __FPGA_PROPERTIES_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Create a fpga_properties object + * + * Initializes the memory pointed at by `prop` to represent a properties + * object, and populates it with the properties of the resource referred to by + * `token`. Individual properties can then be queried using fpgaPropertiesGet*() + * accessor functions. + * + * If `token` is NULL, an "empty" properties object is created to be used as a + * filter for fpgaEnumerate(). All individual fields are set to `don`t care`, + * which implies that the fpga_properties object would match all FPGA resources + * if used for an fpgaEnumerate() query. The matching criteria can be further + * refined by using fpgaSet* functions on the properties object, or the + * object can be populated with the actual properties of a resource by using + * fpgaUpdateProperties(). + * + * @Note fpgaGetProperties() will allocate memory for the created properties + * object returned in `prop`. It is the responsibility of the using application + * to free this memory after use by calling fpgaDestroyProperties(). + * + * @param[in] token Token to get properties for. Can be NULL, which will + * create an empty properties object to be used as a + * filter for fpgaEnumerate(). + * @param[out] prop Pointer to a variable of type fpga_properties + * @returns FPGA_OK on success. FPGA_NO_MEMORY if no memory could be allocated + * to create the `fpga_properties` object. FPGA_EXCEPTION if an exception + * happend while initializing the `fpga_properties` object. + */ +__FPGA_API__ fpga_result fpgaGetProperties(fpga_token token, fpga_properties *prop); + +/** + * Update a fpga_properties object + * + * Populates the properties object 'prop' with properties of the resource + * referred to by 'token'. Unlike fpgaGetProperties(), this call will not create + * a new properties object or allocate memory for it, but use a previously + * created properties object. + * + * @param[in] token Token to retrieve properties for + * @param[in] prop fpga_properties object to update + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if `token` or `prop` are not + * valid objects. FPGA_NOT_FOUND if the resource referred to by `token` was + * not found. FPGA_NO_DRIVER if not driver is loaded. FPGA_EXCEPTION if an + * internal exception occured when trying to update `prop`. + */ +__FPGA_API__ fpga_result fpgaUpdateProperties(fpga_token token, fpga_properties prop); + +/** + * Clear a fpga_properties object + * + * Sets all fields of the properties object pointed at by 'prop' to 'don't + * care', which implies that the fpga_properties object would match all FPGA + * resources if used for an fpgaEnumerate() query. The matching criteria can be + * further refined by using fpgaSet* functions on the properties object. + * + * Instead of creating a new fpga_properties object every time, this function + * can be used to re-use fpga_properties objects from previous queries. + * + * @param[in] prop fpga_properties object to clear + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if `prop` is not a valid + * object. FPGA_EXCEPTION if an * internal exception occured when trying to + * access `prop`. + */ +__FPGA_API__ fpga_result fpgaClearProperties(fpga_properties prop); + +/** + * Clone a fpga_properties object + * + * Creates a copy of an fpga_properties object. + * + * @Note This call creates a new properties object and allocates memory for it. + * Both the 'src' and the newly created 'dst' objects will eventually need to be + * destroyed using fpgaDestroyProperties(). + * + * @param[in] src fpga_properties object to copy + * @param[out] dst New fpga_properties object cloned from 'src' + * @returns FPGA_OK on success. FPGA_INVALID_PARAM if `src` is not a valid + * object, or if `dst` is NULL. FPGA_NO_MEMORY if there was not enough memory + * to allocate an `fpga_properties` object for `dst`. FPGA_EXCEPTION if an + * internal exception occurred either accessing `src` or updating `dst`. + */ +__FPGA_API__ fpga_result fpgaCloneProperties(fpga_properties src, fpga_properties *dst); + +/** + * Destroy a fpga_properties object + * + * Destroys an existing fpga_properties object that the caller has previously + * created using fpgaGetProperties() or fpgaCloneProperties(). + * + * @param[inout] prop Pointer to the fpga_properties object to destroy + * @returns FPGA_OK on success. FPGA_INVALID_PARAM is `prop` is not a valid + * object. FPGA_EXCEPTION if an internal exception occurrred while trying to + * access `prop`. + */ +__FPGA_API__ fpga_result fpgaDestroyProperties(fpga_properties *prop); + +/** + * Get the token of the parent object + * + * Returns the token of the parent of the queried resource in '*parent'. + * + * @param[in] prop Properties object to query + * @param[out] parent Pointer to a token variable of the resource 'prop' is + * associated with + * @returns FPGA_NOT_FOUND if resource does not have a + * parent (e.g. an FPGA_DEVICE resource does not have parents). Also see + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetParent(const fpga_properties prop, + fpga_token *parent); + +/** + * Set the token of the parent object + * + * @param[in] prop Properties object to modify + * @param[out] parent Pointer to a token variable of the resource 'prop' is + * associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetParent(fpga_properties prop, + fpga_token parent); + +/** + * Get the object type of a resource + * + * Returns the object type of the queried resource. + * + * @param[in] prop Properties object to query + * @param[out] objtype Pointer to an object type variable of the resource + * 'prop' is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetObjectType(const fpga_properties prop, + fpga_objtype *objtype); + +/** + * Set the object type of a resource + * + * Sets the object type of the resource. * Currently supported object types are + * FPGA_DEVICE and FPGA_ACCELERATOR. + * + * @param[in] prop Properties object to modify + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetObjectType(fpga_properties prop, + fpga_objtype objtype); + +/** + * Get the PCI bus number of a resource + * + * Returns the bus number the queried resource. + * + * @param[in] prop Properties object to query + * @param[out] bus Pointer to a PCI bus variable of the resource 'prop' + * is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetBus(const fpga_properties prop, uint8_t *bus); + +/** + * Set the PCI bus number of a resource + * + * @param[in] prop Properties object to modify + * @param[in] bus PCI bus number of the resource 'prop' is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetBus(fpga_properties prop, uint8_t bus); + +/** + * Get the PCI device number of a resource + * + * Returns the device number the queried resource. + * + * @param[in] prop Properties object to query + * @param[out] device Pointer to a PCI device variable of the resource 'prop' + * is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetDevice(const fpga_properties prop, + uint8_t *device); + +/** + * Set the PCI device number of a resource + * + * Enforces the limitation on the number of devices as specified in the + * PCI spec. + * + * @param[in] prop Properties object to modify + * @param[in] device PCI device number of the resource 'prop' is associated + * with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetDevice(fpga_properties prop, + uint8_t device); + +/** + * Get the PCI function number of a resource + * + * Returns the function number the queried resource. + * + * @param[in] prop Properties object to query + * @param[out] function Pointer to PCI function variable of the + * resource 'prop' is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetFunction(const fpga_properties prop, + uint8_t *function); + +/** + * Set the PCI function number of a resource + * + * Enforces the limitation on the number of functions as specified in the + * PCI spec. + * + * @param[in] prop Properties object to modify + * @param[in] function PCI function number of the resource 'prop' is + * associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetFunction(fpga_properties prop, + uint8_t function); + +/** + * Get the socket id of a resource + * + * Returns the socket id of the queried resource. + * + * @param[in] prop Properties object to query + * @param[out] socket_id Pointer to a socket id variable of the + * resource 'prop' + * is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + * See also "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetSocketID(const fpga_properties prop, + uint8_t *socket_id); + +/** + * Set the socket id of the resource + * + * @param[in] prop Properties object to modify + * @param[in] socket_id Socket id of the resource 'prop' is + * associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetSocketID(fpga_properties prop, + uint8_t socket_id); + +/** + * Get the device id of the resource + * + * @param[in] prop Properties object to query + * @param[out] device_id Pointer to a device id variable of the + * resource 'prop' is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetDeviceID(const fpga_properties prop, + uint32_t *device_id); + +/** + * Set the device id of the resource + * + * @param[in] prop Properties object to modify + * @param[in] device_id Device id of the resource 'prop' is associated with + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetDeviceID(fpga_properties prop, + uint32_t device_id); + +/** + * Get the number of slots of an FPGA resource property + * + * Returns the number of slots present in an FPGA. + * + * @param[in] prop Properties object to query - must be of type FPGA_DEVICE + * @param[out] num_slots Pointer to number of slots variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetNumSlots(const fpga_properties prop, + uint32_t *num_slots); + +/** + * Set the number of slots of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type + * FPGA_DEVICE + * @param[in] num_slots Number of slots of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetNumSlots(fpga_properties prop, + uint32_t num_slots); + +/** + * Get the BBS ID of an FPGA resource property + * + * Returns the blue bitstream id of an FPGA. + * + * @param[in] prop Properties object to query - must be of type FPGA_DEVICE + * @param[out] bbs_id Pointer to a bbs id variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetBBSID(const fpga_properties prop, + uint64_t *bbs_id); + +/** + * Set the BBS ID of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type + * FPGA_DEVICE + * @param[in] bbs_id Blue bitstream id of the FPGA resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetBBSID(fpga_properties prop, + uint64_t bbs_id); + +/** + * Get the BBS Version of an FPGA resource property + * + * Returns the blue bitstream version of an FPGA. + * + * @param[in] prop Properties object to query - must be of type + * FPGA_DEVICE + * @param[out] bbs_version Pointer to a bbs version variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetBBSVersion(const fpga_properties prop, + fpga_version *bbs_version); + +/** + * Set the BBS Version of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type + * FPGA_DEVICE + * @param[in] bbs_version Blue bitstream version of the FPGA resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetBBSVersion(fpga_properties prop, + fpga_version version); + +/** + * Get the vendor id of an FPGA resource property + * + * Returns the vendor id of an FPGA. + * + * @param[in] prop Properties object to query - must be of type FPGA_DEVICE + * @param[out] vendor_id Pointer to a vendor id variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesGetVendorID(const fpga_properties prop, + uint16_t *vendor_id); + +/** + * Set the vendor id of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type FPGA_DEVICE + * @param[in] vendor_id Vendor id of the FPGA resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesSetVendorID(fpga_properties prop, + uint16_t vendor_id); + +/** + * Get the model of an FPGA resource property + * + * Returns the model of an FPGA. + * + * @param[in] prop Properties object to query - must be of type FPGA_DEVICE + * @param[in] model Model of the FPGA resource (string of minimum + * FPGA_MODEL_LENGTH length + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesGetModel(const fpga_properties prop, + char *model); + +/** + * Set the model of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type FPGA_DEVICE + * @param[in] model Model of the FPGA resource (string of maximum + * FPGA_MODEL_LENGTH length + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesSetModel(fpga_properties prop, + char *model); + +/** + * Get the local memory size of an FPGA resource property + * + * Returns the local memory size of an FPGA. + * + * @param[in] prop Properties object to query - must be of type FPGA_DEVICE + * @param[out] lms Pointer to a memory size variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesGetLocalMemorySize(const fpga_properties prop, + uint64_t *lms); + +/** + * Set the local memory size of an FPGA resource property + * + * @param[in] prop Properties object to modify - must be of type FPGA_DEVICE + * @param[in] lms Local memory size of the FPGA resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesSetLocalMemorySize(fpga_properties prop, + uint64_t lms); + +/** + * Get the capabilities FPGA resource property + * + * Returns the capabilities of an FPGA. + * Capabilities is a bitfield value + * + * @param[in] prop Properties object to query - must be of type + * FPGA_DEVICE + * @param[out] capabilities Pointer to a capabilities variable of the FPGA + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesGetCapabilities(const fpga_properties prop, + uint64_t *capabilities); + +/** + * Set the capabilities of an FPGA resource property + * + * Capabilities is a bitfield value + * + * @param[in] prop Properties object to modify - must be of type + * FPGA_DEVICE + * @param[in] capabilities Capabilities of the FPGA resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_DEVICE. See also + * "Accessor Return Values" in [properties.h](#properties-h). + * + * @note This API is not currently supported. + */ +__FPGA_API__ fpga_result fpgaPropertiesSetCapabilities(fpga_properties prop, + uint64_t capabilities); + +/** + * Get the GUID of a resource + * + * Returns the GUID of an FPGA or accelerator object. + * + * For an accelerator, the GUID uniquely identifies a specific accelerator context type, + * i.e. different accelerators will have different GUIDs. For an FPGA, the GUID + * is used to identify a certain instance of an FPGA, e.g. to determine whether + * a given bitstream would be compatible. + * + * @param[in] prop Properties object to query + * @param[out] guid Pointer to a GUID of the slot variable + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetGUID(const fpga_properties prop, + fpga_guid *guid); + +/** + * Set the GUID of a resource + * + * Sets the GUID of an FPGA or accelerator object. + * + * For an accelerator, the GUID uniquely identifies a specific accelerator context type, + * i.e. different accelerators will have different GUIDs. For an FPGA, the GUID + * is used to identify a certain instance of an FPGA, e.g. to determine whether + * a given bitstream would be compatible. + * + * @param[in] prop Properties object to modify + * @param[out] guid Pointer to a GUID of the slot variable + * @returns See "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetGUID(fpga_properties prop, fpga_guid guid); + +/** + * Get the number of mmio spaces + * + * Returns the number of mmio spaces of an AFU properties structure. + * + * @param[in] prop Properties object to query - must be of type FPGA_ACCELERATOR + * @param[out] mmio_spaces Pointer to a variable for number of mmio spaces + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetNumMMIO(const fpga_properties prop, + uint32_t *mmio_spaces); + +/** + * Set the number of mmio spaces + * + * Sets the number of mmio spaces of an AFU properties structure. + * + * @param[in] prop Properties object to modify - must be of type FPGA_ACCELERATOR + * @param[in] mmio_spaces Number of MMIO spaces of the accelerator + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetNumMMIO(fpga_properties prop, + uint32_t mmio_spaces); + +/** + * Get the number of interrupts + * + * Returns the number of interrupts of an accelerator properties structure. + * + * @param[in] prop Properties object to query - must be of type FPGA_ACCELERATOR + * @param[out] mmio_spaces Pointer to a variable for number of interrupts + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetNumInterrupts(const fpga_properties prop, + uint32_t *num_interrupts); + +/** + * Set the number of mmio spaces + * + * Sets the number of interrupts of an accelerator properties structure. + * + * @param[in] prop Properties object to modify - must be of type FPGA_ACCELERATOR + * @param[in] mmio_spaces Number of interrupts of the accelerator + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetNumInterrupts(fpga_properties prop, + uint32_t num_interrupts); + +/** + * Get the state of a accelerator resource property + * + * Returns the accelerator state of a accelerator. + * + * @param[in] prop Properties object to query - must be of type FPGA_ACCELERATOR + * @param[out] status Pointer to a accelerator state variable of the accelerator + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesGetAcceleratorState(const fpga_properties prop, + fpga_accelerator_state *state); + + +/** + * Set the state of an accelerator resource property + * + * @param[in] prop Properties object to modify - must be of type FPGA_ACCELERATOR + * @param[in] status accelerator state of the accelerator resource + * @returns FPGA_INVALID_PARAM if object type is not FPGA_ACCELERATOR. See also + * "Accessor Return Values" in [properties.h](#properties-h). + */ +__FPGA_API__ fpga_result fpgaPropertiesSetAcceleratorState(fpga_properties prop, + fpga_accelerator_state state); + +/** +* Get the object ID of a resource +* +* Returns the object ID of a resource. The object ID is a 64 bit identifier +* that is unique within a single node or system. It represents a similar +* concept as the token, but can be used across processes (e.g. passed on the +* command line). +* +* @param[in] prop Properties object to query +* @param[out] object_id Pointer to a 64bit memory location to store the object +* ID in +* @returns See "Accessor Return Values" in [properties.h](#properties-h). +*/ +__FPGA_API__ fpga_result fpgaPropertiesGetObjectID(fpga_properties prop, + uint64_t *object_id); + + +/** +* Set the object ID of a resource +* +* Sets the object ID of a resource. The object ID is a 64 bit identifier +* that is unique within a single node or system. It represents a similar +* concept as the token, but can be used across processes (e.g. passed on the +* command line). +* +* @param[in] prop Properties object to query +* @param[in] object_id A 64bit value to use as the object ID +* @returns See "Accessor Return Values" in [properties.h](#properties-h). +*/ +__FPGA_API__ fpga_result fpgaPropertiesSetObjectID(fpga_properties prop, + uint64_t object_id); + +/** +* Create a fpga_properties object +* +* Initializes the memory pointed at by `prop` to represent a properties +* object, and populates it with the properties of the resource referred to by +* `handle`. Individual properties can then be queried using fpgaPropertiesGet*() +* accessor functions. +* +* @note fpgaGetPropertiesFromHandle() will allocate memory for the created properties +* object returned in `prop`. It is the responsibility of the caller +* to free this memory after use by calling fpgaDestroyProperties(). +* +* @param[in] handle Open handle to get properties for. +* @param[out] prop Pointer to a variable of type fpga_properties +* @returns FPGA_OK on success. FPGA_NO_MEMORY if no memory could be allocated +* to create the `fpga_properties` object. FPGA_EXCEPTION if an exception +* happend while initializing the `fpga_properties` object. +**/ +__FPGA_API__ +fpga_result +fpgaGetPropertiesFromHandle( + fpga_handle handle, + fpga_properties *prop + ); + +END_C_DECL + +#endif // __FPGA_PROPERTIES_H__ + diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types.h new file mode 100644 index 0000000..481e6ae --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types.h @@ -0,0 +1,173 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file types.h + * @brief Type definitions for FPGA API + * + * OPAE uses the three opaque types fpga_properties, fpga_token, and + * fpga_handle to create a hierarchy of objects that can be used to enumerate, + * reference, acquire, and query FPGA resources. This object model is designed + * to be extensible to account for different FPGA architectures and platforms. + * + * Initialization + * -------------- + * OPAEs management of the opaque types `fpga_properties`, + * `fpga_token`, and `fpga_handle` relies on the proper initialization of + * variables of these types. In other words, before doing anything with a + * variable of one of these opaque types, you need to first initialize them. + * + * The respective functions that initizalize opaque types are: + * + * * fpgaGetProperties() and fpgaCloneProperties() for `fpga_properties` + * * fpgaEnumerate() and fpgaCloneToken() for `fpga_token` + * * fpgaOpen() for `fpga_handle` + * + * This should intuitively make sense - fpgaGetProperties() creates + * `fpga_properties` objects, fpgaEnumerate() creates `fpga_token` objects, + * fpgaOpen() creates `fpga_handle` objects, and fpgaCloneProperties() and + * fpgaCloneToken() clone (create) `fpga_properties` and `fpga_token` objects, + * respectively. + * + * Since these opaque types are interpreted as pointers (they are typedef'd to + * a `void *`), passing an uninitialized opaque type into any function except + * the respective initailzation function will result in undefined behaviour, + * because OPAE will try to follow an invalid pointer. Undefined behaviour in + * this case may include an unexpected error code, or an application crash. + * + */ + +#ifndef __FPGA_TYPES_H__ +#define __FPGA_TYPES_H__ + +#include <stdint.h> +#include <stddef.h> +#include <opae/types_enum.h> + +/** + * Object for expressing FPGA resource properties + * + * `fpga_properties` objects encapsulate all enumerable information about an + * FPGA resources. They can be used for two purposes: selective enumeration + * (discovery) and querying information about existing resources. + * + * For selective enumeration, usually an empty `fpga_properties` object is + * created (using fpgaGetProperties()) and then populated with the desired + * criteria for enumeration. An array of `fpga_properties` can then be passed + * to fpgaEnumerate(), which will return a list of `fpga_token` objects + * matching these criteria. + * + * For querying properties of existing FPGA resources, fpgaGetProperties() can + * also take an `fpga_token` and will return an `fpga_properties` object + * populated with information about the resource referenced by that token. + * + * After use, `fpga_properties` objects should be destroyed using + * fpga_destroyProperties() to free backing memory used by the + * `fpga_properties` object. + */ +typedef void *fpga_properties; + +/** + * Token for referencing FPGA resources + * + * An `fpga_token` serves as a reference to a specific FPGA resource present in + * the system. Holding an `fpga_token` does not constitute ownership of the + * FPGA resource - it merely allows the user to query further information about + * a resource, or to use fpgaOpen() to acquire ownership. + * + * `fpga_token`s are usually returned by fpgaEnumerate() or + * fpgaPropertiesGetParent(), and used by fpgaOpen() to acquire ownership and + * yield a handle to the resource. Some API calls also take `fpga_token`s as + * arguments if they don't require ownership of the resource in question. + */ +typedef void *fpga_token; + +/** + * Handle to an FPGA resource + * + * A valid `fpga_handle` object, as populated by fpgaOpen(), denotes ownership + * of an FPGA resource. Note that ownership can be exclusive or shared, + * depending on the flags used in fpgaOpen(). Ownership can be released by + * calling fpgaClose(), which will render the underlying handle invalid. + * + * Many OPAE C API functions require a valid token (which is synonymous with + * ownership of the resource). + */ +typedef void *fpga_handle; + +/** + * Globally unique identifier (GUID) + * + * GUIDs are used widely within OPAE for helping identify FPGA resources. For + * example, every FPGA resource has a `guid` property, which can be (and in the + * case of FPGA_ACCELERATOR resource primarily is) used for enumerating a resource of a + * specific type. + * + * `fpga_guid` is compatible with libuuid's uuid_t, so users can use libuuid + * functions like uuid_parse() to create and work with GUIDs. + */ +typedef uint8_t fpga_guid[16]; + +/** + * Semantic version + * + * Data structure for expressing version identifiers following the semantic + * versioning scheme. Used in various properties for tracking component + * versions. + */ +typedef struct { + uint8_t major; /**< Major version */ + uint8_t minor; /**< Minor version */ + uint16_t patch; /**< Revision or patchlevel */ +} fpga_version; + +/* + * Scatter Gather list in userspace that will be populated during fpgaGetPhysicalAddress call + */ +typedef struct _sg_element { + uint64_t phys_addr; /**< Starting physical address of this scatter/gather region */ + uint32_t length; /**< length, in bytes, of a physically contiguous SG region */ +} sg_element, *psg_element; + +/** Handle to an event object + * + * OPAE provides an interface to asynchronous events that can be generated by + * different FPGA resources. The event API provides functions to register for + * these events; associated with every event a process has registered for is an + * `fpga_event_handle`, which encapsulates the OS-specific data structure for + * event objects. + * + * On Linux, an `fpga_event_handle` can be used as a file descriptor and passed + * to select(), poll(), epoll() and similar functions to wait for asynchronous + * events. + */ +#ifndef _WIN32 +typedef int fpga_event_handle; +#else +typedef HANDLE fpga_event_handle; +#endif + +#endif // __FPGA_TYPES_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types_enum.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types_enum.h new file mode 100644 index 0000000..6fc4de2 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/types_enum.h @@ -0,0 +1,196 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * @file types_enum.h + * @brief Definitions of enumerated types for the OPAE C API + * + * This file defines return and error codes, event and object types, states, + * and flags as used or reported by OPAE C API functions. + */ + +#ifndef __FPGA_TYPES_ENUM_H__ +#define __FPGA_TYPES_ENUM_H__ + +#ifdef _WIN32 +#ifdef FpgaLib_EXPORTS +#define __FPGA_API__ __declspec(dllexport) +#else +#define __FPGA_API__ __declspec(dllimport) +#endif +#else +#define __FPGA_API__ __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +#define BEGIN_C_DECL extern "C" { +#define END_C_DECL } +#else +#define BEGIN_C_DECL +#define END_C_DECL +#endif + +/** + * OPAE C API function return codes + * + * Every public API function exported by the OPAE C library will return one of + * these codes. Usually, FPGA_OK denotes successful completion of the requested + * operation, while any return code *other* than FPGA_OK indicates an error or + * other deviation from the expected behavior. Users of the OPAE C API should + * always check the return codes of the APIs they call, and not use output + * parameters of functions that did not execute successfully. + + * The fpgaErrStr() function converts error codes into printable messages. + * + * OPAE also has a logging mechanism that allows a developer to get more + * information about why a particular call failed with a specific message. If + * enabled, any function that returns an error code different from FPGA_OK will + * also print out a message with further details. This mechanism can be enabled + * by setting the environment variable `LIBOPAE_LOG` to 1 before running the + * respective application. + */ + +// +// Minimum alignment requirement for DMA BBB +// +#define FPGA_DMA_ALIGN_BYTES 64 + +// +// Maximum size (in bytes0 descriptor of each SGDMA +// block can transfer. For pre-alpha maximum transfer size is +// One Meg minus some bytes. + +#define FPGA_DMA_BUF_SIZE (1020*1024) + +// +// Number of DMA blocks supported by SGDMA. +// Currently only one is supported by pre-alpha +// bitstream +// +#define NDMA 1 + +typedef enum { + FPGA_OK = 0, /**< Operation completed successfully */ + FPGA_INVALID_PARAM, /**< Invalid parameter supplied */ + FPGA_BUSY, /**< Resource is busy */ + FPGA_EXCEPTION, /**< An exception occurred */ + FPGA_NOT_FOUND, /**< A required resource was not found */ + FPGA_NO_MEMORY, /**< Not enough memory to complete operation */ + FPGA_NOT_SUPPORTED, /**< Requested operation is not supported */ + FPGA_NO_DRIVER, /**< Driver is not loaded */ + FPGA_NO_DAEMON, /**< FPGA Daemon (fpgad) is not running */ + FPGA_NO_ACCESS, /**< Insufficient privileges or permissions */ + FPGA_RECONF_ERROR /**< Error while reconfiguring FPGA */ +} fpga_result; + + /* + * FPGA events + * + * OPAE currently defines the following event types that applications can + * register for.Note that not all FPGA resources and target platforms may + * support all event types. + */ +typedef enum +{ + FPGA_NO_EVENT = 0, + FPGA_EVENT_INTERRUPT, /**< Interrupt generated by an accelerator */ + FPGA_EVENT_ERROR, /**< Infrastructure error event */ + FPGA_EVENT_POWER_THERMAL, /**< Infrastructure thermal event */ + FPGA_EVENT_PORT_ERROR, + FPGA_EVENT_FME_ERROR, + FPGA_LIFECYCLE_APPEAR_EVENT, + FPGA_LIFECYCLE_DISAPPEAR_EVENT, + FPGA_EVENT_AFC_INTERRUPT, + FPGA_EVENT_TYPE_MAX, + FPGA_EVENT_AP_EVENT, + FPGA_MAX_EVENT +} fpga_event_type; + +/* TODO: consider adding lifecycle events in the future + * to help with orchestration. Need a complete specification + * before including them in the API. Proposed events: + * FPGA_EVENT_APPEAR + * FPGA_EVENT_DISAPPEAR + * FPGA_EVENT_CHANGE + */ + +/** accelerator state */ +typedef enum { + FPGA_ACCELERATOR_ASSIGNED = 0, /**< accelerator is opened exclusively by another process */ + FPGA_ACCELERATOR_UNASSIGNED, /**< accelerator is free to be opened */ + FPGA_ACCELERATOR_STATE_MAX +} fpga_accelerator_state; + +/** + * OPAE FPGA resources (objects) + * + * These are the FPGA resources currently supported by the OPAE object model. + */ +typedef enum { + /** FPGA_DEVICE objects represent FPGA devices and their management functionality. + * These objects can be opened (typically requires a certain privilege level or + * access permissions) and used for management functions like fpgaReconfigreSlot(). */ + FPGA_DEVICE = 0, + /** FPGA_ACCELERATOR objects represent allocatable units for accessing + * accelerated functions on the FPGA. They are frequently opened for + * interacting via control registers (MMIO), shared memory, or other, + * possibly platform-specific functions. */ + FPGA_ACCELERATOR, + FPGA_OBJTYPE_MAX +} fpga_objtype; + +/** + * Buffer flags + * + * These flags can be passed to the fpgaPrepareBuffer() function. + */ +enum fpga_buffer_flags { + FPGA_BUF_PREALLOCATED = (1u << 0), /**< Use existing buffer */ + FPGA_BUF_QUIET = (1u << 1), /**< Suppress error messages */ + FPGA_BUF_NOCACHE = (1u << 2), + FPGA_BUF_LARGE_PAGE = (1u << 4) /*< For 2MB page support in VTP */ +}; + +/** + * Open flags + * + * These flags can be passed to the fpgaOpen() function. + */ +enum fpga_open_flags { + FPGA_OPEN_SHARED = (1u << 0) /**< Open FPGA resource for shared access */ +}; + +/** + * Reconfiguration flags + * + * These flags can be passed to the fpgaReconfigure() function. + */ +enum fpga_reconf_flags { + /** Reconfigure the slot without checking if it is in use */ + FPGA_RECONF_FORCE = (1u << 0) +}; + +#endif // __FPGA_TYPES_ENUM_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/umsg.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/umsg.h new file mode 100644 index 0000000..6e073ee --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/umsg.h @@ -0,0 +1,112 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * \file umsg.h + * \brief FPGA UMsg API + */ + +#ifndef __FPGA_UMSG_H__ +#define __FPGA_UMSG_H__ + +#include <opae/types.h> + +BEGIN_C_DECL + +/** + * Get number of Umsgs + * + * Retuns number of umsg supported by AFU. + * + * + * @param[in] handle Handle to previously opened accelerator resource + * @prarm[out] value Returns number of UMsgs + * @returns FPGA_OK on success. + * FPGA_INVALID_PARAM if input parameter combination + * is not valid. + * FPGA_EXCEPTION if input parameter fpga handle is not + * valid. + */ +__FPGA_API__ fpga_result fpgaGetNumUmsg(fpga_handle handle, uint64_t *value); + +/** + * Sets Umsg hint + * + * Writes usmg hint bit. + * + * + * @param[in] handle Handle to previously opened accelerator resource + * @prarm[in] value Value to use for UMsg hint, Umsg hit is N wide bitvector + * where N = number of Umsgs. + * @returns FPGA_OK on success. + * FPGA_INVALID_PARAM if input parameter combination + * is not valid. + * FPGA_EXCEPTION if input parameter fpga handle is not + * valid. + */ +__FPGA_API__ fpga_result fpgaSetUmsgAttributes(fpga_handle handle, + uint64_t value); + +/** + * Trigger Umsg + * + * Writes a 64-bit value to trigger low-latency accelerator notification mechanism + * (UMsgs). + * + * @param[in] handle Handle to previously opened accelerator resource + * @prarm[in] value Value to use for UMsg + * @returns FPGA_OK on success. + * FPGA_INVALID_PARAM if input parameter combination + * is not valid. + * FPGA_EXCEPTION if input parameter fpga handle is not + * valid. + */ +__FPGA_API__ fpga_result fpgaTriggerUmsg(fpga_handle handle, uint64_t value); + +/** + * Access UMsg memory directly + * + * This function will return a pointer to the memory allocated for low latency + * accelerator notifications (UMsgs). + * @note This call is only supported by hardware targets, not by ASE + * simulation. Use fpgaTriggerUmsg() if you need ASE simulation capabilities. + * + * @param[in] handle Handle to previously opened accelerator resource + * @param[out] umsg_ptr Pointer to memory where a pointer to the virtual + * address space will be returned + * @returns FPGA_OK on success. + * FPGA_INVALID_PARAM if input parameter combination + * is not valid. + * FPGA_EXCEPTION if input parameter fpga handle is not + * valid. + * FPGA_NO_MEMORY if memory allocation fails or system + * doesn't configure huge pages. + */ +__FPGA_API__ fpga_result fpgaGetUmsgPtr(fpga_handle handle, uint64_t **umsg_ptr); + +END_C_DECL + +#endif // __FPGA_UMSG_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/utils.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/utils.h new file mode 100644 index 0000000..5b57cbd --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/utils.h @@ -0,0 +1,54 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +/** + * \file utils.h + * \brief Utility functions and macros for the FPGA API + */ + +#ifndef __FPGA_UTILS_H__ +#define __FPGA_UTILS_H__ + +#include <opae/types.h> +#include <stdio.h> + +BEGIN_C_DECL + +/** + * Return human-readable error message + * + * Returns a pointer to a human-readable error message corresponding to the + * provided fpga_error error code. + * + * @param[in] e Error code (as returned by another FPGA API function + * @returns Pointer to a descriptive error message string + */ +__FPGA_API__ const char *fpgaErrStr(fpga_result e); + +END_C_DECL + +#endif // __FPGA_UTILS_H__ + diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/version.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/version.h new file mode 100644 index 0000000..66bd18b --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/opae/version.h @@ -0,0 +1,79 @@ +// Copyright(c) 2017 - 2019, Intel Corporation +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * 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. +// * Neither the name of Intel Corporation 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 OWNER 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. + +#ifndef __FPGA_VERSION_H__ +#define __FPGA_VERSION_H__ + +#include <opae/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Get version information about the OPAE library + * + * Retrieve major version, minor version, and revision information about the + * OPAE library. + * + * @param[out] version FPGA version + * @returns FPGA_INVALID_PARAM if any of the output parameters is NULL, FPGA_OK + * otherwise. + */ +__FPGA_API__ fpga_result fpgaGetOPAECVersion(fpga_version *version); + +/** + * Get version information about the OPAE library as a string + * + * Retrieve major version, minor version, and revision information about the + * OPAE library, encoded in a human-readable string (e.g. "1.0.0"). + * + * @param[out] version_str String to copy version information into + * @param[in] len Length of `version_str` + * @returns FPGA_INVALID_PARAM if `version_str` is NULL, FPGA_EXCEPTION if the + * version string cannot be copied into `version_str`, FPGA_OK otherwise. + */ +__FPGA_API__ fpga_result fpgaGetOPAECVersionString(char *version_str, size_t len); +#define FPGA_VERSION_STR_MAX 10 + +/** + * Get build information about the OPAE library as a string + * + * Retrieve the build identifier of the OPAE library. + * + * @param[out] build_str String to copy build information into + * @param[in] len Length of `build_str` + * @returns FPGA_INVALID_PARAM if `build_str` is NULL, FPGA_EXCEPTION if the + * version string cannot be copied into `build_str`, FPGA_OK otherwise. + */ +__FPGA_API__ fpga_result fpgaGetOPAECBuildString(char *build_str, size_t len); +#define FPGA_BUILD_STR_MAX 41 + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // __FPGA_VERSION_H__ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/pkg_editor.h b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/pkg_editor.h new file mode 100644 index 0000000..27f4f1e --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/include/pkg_editor.h @@ -0,0 +1,170 @@ +/* Editor for Altera OpenCL package files + * + * Dmitry Denisenko, June 2012. + * + * This provides higher-level functions for ELF work. + * The idea is to put content into sections, one "piece" of content + * per section, and use section names to identify the content. + * The interface enforces unique section names (not true for generic ELFs) + * and hides all the ugly ELF interface calls and structures. + */ + +#ifndef PKG_FILE_EDITOR_H +#define PKG_FILE_EDITOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAX_STRING_LENGTH 100000 + +/* Modes for open_struct acl_pkg_file() call. + * Exactly one of ACL_PKG_READ, ACL_PKG_READ_WRITE must be supplied. + * Other flags may be bitwise OR'd into the mode. + * + * You can combine other modes with ACL_PKG_SHOW_* to control messages. + */ +#define ACL_PKG_READ (1<<0) /* Only reading the package */ +#define ACL_PKG_READ_WRITE (1<<1) /* Expect to read and write the binary. File must already exist. */ +#define ACL_PKG_CREATE (1<<2) /* Also creating. Can only be used with ACL_PKG_READ_WRITE */ + +#define ACL_PKG_SHOW_ERROR (1<<8) /*print errors to stderr*/ +#define ACL_PKG_SHOW_INFO (1<<9) /*print info messages to stdout*/ + +#define ACL_PKG_SECTION_ACL_VERSION ".acl.version" +#define ACL_PKG_SECTION_ACL_BUILD ".acl.build" +#define ACL_PKG_SECTION_QVERSION ".acl.qversion" +#define ACL_PKG_SECTION_HASH ".acl.hash" +#define ACL_PKG_SECTION_BOARD ".acl.board" +#define ACL_PKG_SECTION_COMPILEOPTIONS ".acl.compileoptions" +#define ACL_PKG_SECTION_SOURCE ".acl.source" +#define ACL_PKG_SECTION_LLVMIR ".acl.llvmir" +#define ACL_PKG_SECTION_VERILOG ".acl.verilog" +#define ACL_PKG_SECTION_PROFILE_BASE ".acl.profile_base" +#define ACL_PKG_SECTION_AUTODISCOVERY ".acl.autodiscovery" +#define ACL_PKG_SECTION_RBF ".acl.rbf" +#define ACL_PKG_SECTION_CORE_RBF ".acl.core.rbf" +#define ACL_PKG_SECTION_PERIPH_RBF ".acl.periph.rbf" +#define ACL_PKG_SECTION_BASE_RBF ".acl.base_revision.rbf" +#define ACL_PKG_SECTION_SOF ".acl.sof" +#define ACL_PKG_SECTION_VFABRIC ".acl.vfabric" +#define ACL_PKG_SECTION_PLL_CONFIG ".acl.pll_config" +#define ACL_PKG_SECTION_FPGA_BIN ".acl.fpga.bin" +#define ACL_PKG_SECTION_EMULATOR_OBJ_LINUX ".acl.emulator_object.linux" +#define ACL_PKG_SECTION_EMULATOR_OBJ_WINDOWS ".acl.emulator_object.windows" +#define ACL_PKG_SECTION_AUTODISCOVERY_XML ".acl.autodiscovery.xml" +#define ACL_PKG_SECTION_BOARDSPEC_XML ".acl.board_spec.xml" +#define ACL_PKG_SECTION_PERIPH_HASH ".acl.periph.hash" +#define ACL_PKG_SECTION_PROFILER_XML ".acl.profiler.xml" +#define ACL_PKG_SECTION_COMPILE_REV ".acl.compile_revision" +#define ACL_PKG_SECTION_PCIE_DEV_ID ".acl.pcie.dev_id" +#define ACL_PKG_SECTION_BASE_PERIPH_HASH ".acl.base_revision.periph.hash" +#define ACL_PKG_SECTION_ADJUST_PLLS_OUTPUT ".acl.quartus_report" +#define ACL_PKG_SECTION_KERNEL_ARG_INFO_XML ".acl.kernel_arg_info.xml" +#define ACL_PKG_SECTION_FAST_COMPILE ".acl.fast_compile" + +/* Minimum alignment in memory. */ +#define ACL_PKG_MIN_SECTION_ALIGNMENT 128 + +/* Open and close the pkg file */ +struct acl_pkg_file *acl_pkg_open_file (const char *fname, int mode); +/* You can call close on a NULL pointer: it will do nothing. + * Closing the package file will also free its memory, so you better lose + * the pointer reference. + */ +int acl_pkg_close_file (struct acl_pkg_file *pkg); + +/* Set message output mode: show_mode is some combination of the bits + * in ACL_PKG_SHOW_INFO and ACL_PKG_SHOW_ERROR + */ +void acl_pkg_set_show_mode( struct acl_pkg_file* pkg, int show_mode ); + +/* Open memory image of pkg file. Only good for reading! + * The show_mode argument is an OR combination of zero or more of + * ACL_PKG_SHOW_INFO, + * ACL_PKG_SHOW_ERROR. + */ +struct acl_pkg_file *acl_pkg_open_file_from_memory (char *pkg_image, size_t pkg_image_size, int show_mode); + + +/* Does the given named section exist? + * Returns 1 for yes, 0 for no. + * If the section exists, and size_ret is not-NULL, then the size (in bytes) of the + * section is stored into *size_ret. The size does NOT include NULL terminator, just like strlen(). + */ +int acl_pkg_section_exists (const struct acl_pkg_file *pkg, const char *sect_name, size_t* size_ret); + +/* Return list of ALL (useful) section names in the package. + * The buffer must be pre-allocated by the caller upto max_len bytes. + * Each section name is separated by '\n' + * Returns 1 on success, 0 on failure. + */ +int acl_pkg_section_names (const struct acl_pkg_file *pkg, char *buf, size_t max_len); + + +/* Add a new section with specified content. + * If a section with such name already exists, nothing is done. + * Returns 0 on failure, non-zero on success. + */ +int acl_pkg_add_data_section (struct acl_pkg_file *pkg, const char *sect_name, const void* content, size_t len); +int acl_pkg_add_data_section_from_file (struct acl_pkg_file *pkg, const char *sect_name, const char *in_file); + +/* Read content of an existing section. + * For read_section(), the buffer must be pre-allocated by caller to hold at least len bytes. + * This function will add '\0' at the end, therefore, the 'len' argument passed to this function + * must be one larger than the value returned by acl_pkg_section_exists. + * Returns 0 on failure, non-zero on success. + */ +int acl_pkg_read_section (const struct acl_pkg_file *pkg, const char *sect_name, char *buf, size_t len); +int acl_pkg_read_section_into_file (struct acl_pkg_file *pkg, const char *sect_name, const char *out_file); + +/* Get a transient pointer to a section's data, via buf_ptr. + * The pointer is transient: It might move if you update the package in any way. + * This is a "fast" path in comparison to acl_pkg_read_section, so you + * don't have to allocate space to copy into. + * Returns 0 on failure, non-zero on success. + */ +int acl_pkg_read_section_transient(const struct acl_pkg_file *pkg, const char *sect_name, char** buf_ptr); + +/* Update content of an existing section. + * Old content is discarded. The section must already exist. + * Returns 0 on failure, non-zero on success. + */ +int acl_pkg_update_section (struct acl_pkg_file *pkg, const char *sect_name, const void *new_content, size_t new_len); +int acl_pkg_update_section_from_file (struct acl_pkg_file *pkg, const char *sect_name, const char *in_file); + +/* List all pkg sections to stdout. + * Returns 0 on failure, non-zero on success. + */ +int acl_pkg_list_file_sections (struct acl_pkg_file *pkg); + +/* Read full content of file into a buffer. + * The buffer is allocated by this function but must be freed by the caller. + * File length is returned in the second argument */ +void *acl_pkg_read_file_into_buffer (const char *in_file, size_t *file_size_out); + +/* support for package/unpackage */ + +/* Package the input files and directory trees (NULL terminated list in input_files_dirs) + * and put them into the output file (out_file). + * Returns 0 on failure, non-zero on success + */ +int acl_pkg_pack (const char* out_file, const char** input_files_dirs); + +/* Unpack the input file (or stdin if filename is ACL_PKG_UNPACKAGE_STDIN) + * created by acl_pkg_pack into directory out_dir. + * Returns 0 on failure, non-zero on success + */ +#define ACL_PKG_UNPACKAGE_STDIN "-" +int acl_pkg_unpack (const char* in_file, const char* out_dir); + +/* Unpack the buffer created by acl_pkg_pack into directory out_dir. + * Returns 0 on failure, non-zero on success + */ +int acl_pkg_unpack_buffer (const char* buffer, size_t buffer_size, const char* out_dir); + +#ifdef __cplusplus +} +#endif + +#endif /* PKG_FILE_EDITOR_H */ diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/FpgaLib.lib b/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/FpgaLib.lib Binary files differnew file mode 100755 index 0000000..2f26b62 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/FpgaLib.lib diff --git a/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/acl_check_sys_cmd.lib b/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/acl_check_sys_cmd.lib Binary files differnew file mode 100755 index 0000000..6c7f423 --- /dev/null +++ b/python/openvino/runtime/coredla_device/mmd/de10_agilex/lib/win64/acl_check_sys_cmd.lib |
