### Kernel Configuration: HBM Bank Allocation (krnl_vadd.cfg) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/hbm_simple_xrt/details.rst This configuration file specifies how kernel interfaces should be connected to HBM banks. It uses the 'sp' keyword to associate kernel ports with specific HBM bank ranges. This setup is crucial for the system linker to build the design with the desired memory mapping. ```cfg [connectivity] sp=krnl_vadd_1.in1:HBM[0:3] sp=krnl_vadd_1.in2:HBM[0:3] sp=krnl_vadd_1.out_r:HBM[0:3] ``` -------------------------------- ### Command Line Execution Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst Command line to execute the compiled kernel binary. It requires the path to the accelerator binary file (xclbin) as an argument. ```shell ./kernel_global_bandwidth ``` -------------------------------- ### Link Vitis Kernels with Configuration File Source: https://github.com/xilinx/vitis_accel_examples/blob/main/rtl_kernels/rtl_streaming_free_running_k2k/details.rst This command demonstrates how to invoke the v++ linker to build a Vitis executable, incorporating a specific configuration file. The '--config' option is used to provide the linker with the kernel connectivity and other build settings defined in the specified .cfg file, such as the stream connections detailed in the previous example. This is crucial for correctly integrating and connecting RTL kernels within the Vitis acceleration flow. ```bash v++ --config myadder.cfg ``` -------------------------------- ### Load Binary, Create Program and Kernel for Each FPGA (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/multiple_devices/details.rst Loads the FPGA binary file, creates an OpenCL program, and instantiates a kernel for each FPGA device. This step is crucial for executing custom acceleration functions on the hardware. ```cpp fileBuf[d] = xcl::read_binary_file(binaryFile, fileBufSize); bins[d].push_back({fileBuf[d], fileBufSize}); programs[d] = load_cl2_binary(bins[d], devices[d], contexts[d]); kernels[d] = cl::Kernel(programs[d], "vadd", &err); ``` -------------------------------- ### Kernel Configuration File Example Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst Example configuration file for specifying memory bank connections. This file is used in the Vitis linker to map kernel ports to specific DDR banks, enabling multi-DDR access. ```ini [connectivity] sp=bandwidth_1.m_axi_gmem0:DDR[0] sp=bandwidth_1.m_axi_gmem1:DDR[1] ``` -------------------------------- ### Example Kernel Execution Log Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/details.rst This log output demonstrates a successful run of the kernel global bandwidth test on the U200 platform. It shows the platform detection, kernel loading, execution duration, and the calculated concurrent read and write throughput, indicating the achieved memory bandwidth. ```text ./kernel_global ./build_dir.hw.xilinx_u200_xdma_201830_2/krnl_kernel_global.xclbin Found Platform Platform Name: Xilinx Found Device=xilinx_u200_xdma_201830_2 INFO: Reading ./build_dir.hw.xilinx_u200_xdma_201830_2/krnl_kernel_global.xclbin Loading: './build_dir.hw.xilinx_u200_xdma_201830_2/krnl_kernel_global.xclbin' Starting kernel to read/write 1024 MB bytes from/to global memory... Kernel Duration...66662680 ns Kernel completed read/write 1024 MB bytes from/to global memory. Execution time = 0.066663 (sec) Concurrent Read and Write Throughput = 30.72 (GB/sec) TEST PASSED ``` -------------------------------- ### XRT Buffer Allocation and Data Transfer (C++) Source: https://context7.com/xilinx/vitis_accel_examples/llms.txt Demonstrates allocating buffer objects in device memory and performing synchronous data transfers between the host and the FPGA using XRT. Buffer objects are essential for moving data to and from global memory. This example requires 'xrt_bo.h', 'xrt_device.h', and 'xrt_kernel.h'. ```cpp #include "xrt/xrt_bo.h" #include "xrt/xrt_device.h" #include "xrt/xrt_kernel.h" #include #define DATA_SIZE 256 int main() { auto device = xrt::device(0); auto uuid = device.load_xclbin("kernel.xclbin"); auto krnl = xrt::kernel(device, uuid, "dummy_kernel"); size_t size_in_bytes = sizeof(int) * DATA_SIZE; // Allocate buffer objects std::cout << "Allocate Buffer in Global Memory\n"; auto input_buffer = xrt::bo(device, size_in_bytes, krnl.group_id(0)); auto output_buffer = xrt::bo(device, size_in_bytes, krnl.group_id(1)); // Prepare input data int buff_in_data[DATA_SIZE], buff_out_data[DATA_SIZE]; for (auto i = 0; i < DATA_SIZE; ++i) { buff_in_data[i] = i; buff_out_data[i] = 0; } // Write to buffer and transfer to device std::cout << "Write the input data\n"; input_buffer.write(buff_in_data); std::cout << "Synchronize input buffer to device\n"; input_buffer.sync(XCL_BO_SYNC_BO_TO_DEVICE); // Execute kernel std::cout << "Execution of the kernel\n"; auto run = krnl(input_buffer, output_buffer, DATA_SIZE); run.wait(); // Transfer results from device std::cout << "Synchronize output buffer from device\n"; output_buffer.sync(XCL_BO_SYNC_BO_FROM_DEVICE); std::cout << "Read the output data\n"; output_buffer.read(buff_out_data); // Validate results if (std::memcmp(buff_in_data, buff_out_data, DATA_SIZE)) { throw std::runtime_error("Value read back does not match reference"); } std::cout << "TEST PASSED\n"; return 0; } ``` -------------------------------- ### Initialize OpenCL Context and Queues for Multiple FPGAs (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/multiple_devices/details.rst Initializes OpenCL contexts and command queues for each detected FPGA device. This is a prerequisite for interacting with the FPGA hardware and launching kernels. ```cpp contexts[d] =cl::Context(devices[d], props, nullptr, nullptr, &err); queues[d] = cl::CommandQueue(contexts[d], devices[d], CL_QUEUE_PROFILING_ENABLE, &err); ``` -------------------------------- ### Create Data Buffers for Each FPGA Device (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/multiple_devices/details.rst Allocates and creates OpenCL buffers on each FPGA device to hold input and output data for the kernel. This example uses `CL_MEM_USE_HOST_PTR` for efficient data transfer. ```cpp buffer_a[d] = cl::Buffer(contexts[d], CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, size_per_device, &A[offset], &err); ``` -------------------------------- ### Vector Addition Host Code (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/hbm_simple_xrt/README.rst The host code for the vector addition example. It initializes data, manages memory on the device, executes the kernel, and retrieves results. It uses XRT native APIs for device and buffer management. Dependencies include the XRT library. ```cpp #include #include #include #include #include #include // Function to perform vector addition on the host (for comparison/verification) void vadd_host(const std::vector &a, const std::vector &b, std::vector &c, int size) { for (int i = 0; i < size; ++i) { c[i] = a[i] + b[i]; } } int main(int argc, char **argv) { if (argc != 2) { std::cout << "Usage: " << argv[0] << " " << std::endl; return EXIT_FAILURE; } std::string binaryFile = argv[1]; int dataSize = 1024 * 1024; // Example data size int bank0 = 0; int bank1 = 1; int bank2 = 2; int bank3 = 3; // --- Case 1: Single HBM bank for all buffers --- std::cout << "Running CASE 1 : Single HBM for all three Buffers" << std::endl; // Open device and load xclbin auto dev = xrt::device(0); auto xclbin_uuid = dev.load_xclbin(binaryFile); auto krnl = xrt::kernel(dev, xclbin_uuid, "krnl_vadd"); // Allocate buffers in Global Memory (single bank) std::cout << "Allocate Buffer in Global Memory" << std::endl; xrt::buffer bank0_buffer(dev, dataSize * sizeof(int), xrt::buffer::normal, bank0); xrt::buffer bank0_buffer2(dev, dataSize * sizeof(int), xrt::buffer::normal, bank0); xrt::buffer bank0_buffer3(dev, dataSize * sizeof(int), xrt::buffer::normal, bank0); // Prepare input data std::vector input1(dataSize); std::vector input2(dataSize); std::vector output(dataSize); std::iota(input1.begin(), input1.end(), 1); std::iota(input2.begin(), input2.end(), 1); // Synchronize input buffer data to device global memory std::cout << "synchronize input buffer data to device global memory" << std::endl; bank0_buffer.copy_to(input1.data()); bank0_buffer2.copy_to(input2.data()); // Execution of the kernel std::cout << "Execution of the kernel" << std::endl; auto drv_run = krnl(bank0_buffer, bank0_buffer2, bank0_buffer3); drv_run.wait(); // Get the output data from the device std::cout << "Get the output data from the device" << std::endl; bank0_buffer3.copy_from(output.data()); // Verify results (optional) std::vector expected_output(dataSize); vadd_host(input1, input2, expected_output, dataSize); if (output == expected_output) { std::cout << "Verification successful for CASE 1." << std::endl; } else { std::cout << "Verification FAILED for CASE 1." << std::endl; } // Calculate and report throughput auto exec_time_ms = drv_run.get_times().count(); double throughput_gbps = (2.0 * dataSize * sizeof(int)) / (exec_time_ms / 1000.0) / (1024.0 * 1024.0 * 1024.0) * 1000.0; std::cout << "[CASE 1] THROUGHPUT = " << throughput_gbps << " GB/s" << std::endl; // --- Case 2: Three Separate Banks for Three Buffers --- std::cout << "Running CASE 2: Three Separate Banks for Three Buffers" << std::endl; std::cout << "input 1 -> bank 1" << std::endl; std::cout << "input 2 -> bank 2" << std::endl; std::cout << "output -> bank 3" << std::endl; // Allocate buffers in different banks std::cout << "Allocate Buffer in Global Memory" << std::endl; xrt::buffer bank1_buffer(dev, dataSize * sizeof(int), xrt::buffer::normal, bank1); xrt::buffer bank2_buffer(dev, dataSize * sizeof(int), xrt::buffer::normal, bank2); xrt::buffer bank3_buffer(dev, dataSize * sizeof(int), xrt::buffer::normal, bank3); // Synchronize input buffer data to device global memory std::cout << "synchronize input buffer data to device global memory" << std::endl; bank1_buffer.copy_to(input1.data()); bank2_buffer.copy_to(input2.data()); // Execution of the kernel std::cout << "Execution of the kernel" << std::endl; auto drv_run2 = krnl(bank1_buffer, bank2_buffer, bank3_buffer); drv_run2.wait(); // Get the output data from the device std::cout << "Get the output data from the device" << std::endl; bank3_buffer.copy_from(output.data()); // Verify results (optional) if (output == expected_output) { std::cout << "Verification successful for CASE 2." << std::endl; } else { std::cout << "Verification FAILED for CASE 2." << std::endl; } // Calculate and report throughput auto exec_time_ms2 = drv_run2.get_times().count(); double throughput_gbps2 = (2.0 * dataSize * sizeof(int)) / (exec_time_ms2 / 1000.0) / (1024.0 * 1024.0 * 1024.0) * 1000.0; std::cout << "[CASE 2] THROUGHPUT = " << throughput_gbps2 << " GB/s" << std::endl; std::cout << "TEST PASSED" << std::endl; return EXIT_SUCCESS; } ``` -------------------------------- ### Host Execution Command Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/streaming_free_running_k2k_xrt/README.rst This command demonstrates how to execute the compiled accelerator application. It specifies the path to the generated XCLBIN file, which contains the hardware description of the kernels. The `-x` flag indicates the kernel binary to be loaded and run on the target FPGA. ```bash ./streaming_free_running_k2k_xrt -x ``` -------------------------------- ### XRT Device Initialization and XCLBIN Loading (C++) Source: https://context7.com/xilinx/vitis_accel_examples/llms.txt Initializes an XRT device and loads a compiled FPGA binary (xclbin) using the XRT Native API. This is a prerequisite for any XRT-based FPGA acceleration workflow, preparing the device for kernel execution. It requires the 'xrt_device.h' and 'xrt_kernel.h' headers. ```cpp #include "xrt/xrt_device.h" #include "xrt/xrt_kernel.h" #include #include int main(int argc, char** argv) { // Device initialization int device_index = 0; std::string binaryFile = "kernel.xclbin"; std::cout << "Open the device " << device_index << std::endl; auto device = xrt::device(device_index); std::cout << "Load the xclbin " << binaryFile << std::endl; auto uuid = device.load_xclbin(binaryFile); // Create kernel object auto krnl = xrt::kernel(device, uuid, "krnl_vadd"); std::cout << "Device initialized successfully" << std::endl; return 0; } ``` -------------------------------- ### Vitis Build Settings for Multi-DDR Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/details.rst These are example command-line flags and configuration options for the Vitis compiler and linker to enable multi-DDR access. The '-max_memory_ports all' flag is used during kernel compilation, and the '--config' flag points to the DDR bank configuration file during linking. These settings are applied through the project's build properties. ```bash Vitis V++ Kernel Compiler -> Miscellaneous -> Other flags: –max_memory_ports all Vitis V++ Kernel Linker -> Miscellaneous -> Other flags: –config ../src/ ``` -------------------------------- ### Open Device and Load XCLBIN using PyXRT Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_py/hello_world_py/details.rst This snippet shows how to open a Xilinx device and load an XCLBIN file using the PyXRT library. It requires a device index and the path to the bitstream file. The function returns a device handle and the UUID of the loaded xclbin. ```python d = pyxrt.device(index) xbin = pyxrt.xclbin(bitstreamFile) uuid = d.load_xclbin(xbin) ``` -------------------------------- ### C: Forking Child Processes for Kernel Execution Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/multiple_process/README.rst This C code snippet demonstrates how to create multiple child processes using the fork() system call. Each child process is intended to execute a specific kernel on the FPGA. The code prints the Process ID (PID) of the child and the Parent Process ID (PPID) for identification. It utilizes a loop to create a specified number of child processes. ```c for(int i=0; i< num_of_child_process; i++) { if(fork() == 0) { printf("[CHILD] PID %d from [PARENT] PPID %d",getpid(),getppid()); result = run_kernel(krnl_id); } } ``` -------------------------------- ### Enable P2P Feature using xbutil Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/p2p_fpga2fpga_bandwidth/details.rst This snippet shows how to enable the PCIe peer-to-peer (P2P) feature on an FPGA device using the XRT xbutil command-line tool. Enabling P2P requires root privileges and may necessitate a warm reboot if system resources are not immediately available. ```bash # xbutil p2p --enable # xbutil query ... PCIe DMA chan(bidir) MIG Calibrated P2P Enabled GEN 3x16 2 true true ... ``` -------------------------------- ### Open Device and Load XCLBIN using XRT Native API Source: https://github.com/xilinx/vitis_accel_examples/blob/main/hello_world/details.rst Demonstrates how to open a device by its index and load an XCLBIN file using XRT Native APIs. This is a foundational step for interacting with hardware accelerators. It returns a device handle and the UUID of the loaded XCLBIN. ```cpp xrt::device(unsigned int didx) load_xclbin(binaryFile) ``` -------------------------------- ### Vitis Linker Configuration for Multi-DDR Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst These are the Vitis linker flags used to enable multi-DDR configurations. The `--config` option points to a configuration file that specifies the memory bank mappings. ```shell -config ../src/ ``` -------------------------------- ### Runtime Log Analysis for Throughput Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/hbm_bandwidth_pseudo_random/README.rst This snippet shows a typical runtime log output from a Vitis accelerated application. It details kernel loading, CU creation, and critically, reports the overall and channel throughput achieved by the design. This information is essential for performance tuning and validation. ```log Loading: './build_dir.hw.xilinx_u50_xdma_201920_1/krnl_vaddmul.xclbin' Creating a kernel [krnl_vaddmul:{krnl_vaddmul_1}] for CU(1) Creating a kernel [krnl_vaddmul:{krnl_vaddmul_2}] for CU(2) Creating a kernel [krnl_vaddmul:{krnl_vaddmul_3}] for CU(3) OVERALL THROUGHPUT = 138.022 GB/s CHANNEL THROUGHPUT = 11.501 GB/s TEST PASSED ``` -------------------------------- ### Kernel C++ Source File Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst The main C++ source file for the kernel. It is responsible for orchestrating the global memory bandwidth test. This file typically includes logic to initiate read/write operations and interact with the hardware. ```c++ // src/kernel.cpp // This file contains the kernel logic for the global bandwidth test. // It is responsible for reading from and writing to global memory. // Specific implementation details would be here. ``` -------------------------------- ### Buffer Object Management with XRT Native API Source: https://github.com/xilinx/vitis_accel_examples/blob/main/hello_world/details.rst Illustrates the creation and synchronization of buffer objects using XRT Native APIs. It covers allocating host and device buffers, synchronizing data between them, and mapping buffer contents to host memory for direct access. ```cpp xrt::bo(xclDeviceHandle dhdl, size_t size, memory_group grp) void sync(xclBOSyncDirection dir, size_t sz, size_t offset) void *map() ``` -------------------------------- ### Allocate and Synchronize Buffer using PyXRT Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_py/hello_world_py/details.rst Demonstrates allocating a buffer object on the device and synchronizing its contents between the host and the device. It uses PyXRT's buffer APIs, requiring the device handle, data size, and buffer type. The buffer is then mapped into host memory for access. ```python boHandle1 = pyxrt.bo(d, DATA_SIZE, pyxrt.bo.normal, kHandle.group_id(0)) boHandle1.sync(pyxrt.bo.SYNC_BO_TO_DEVICE, DATA_SIZE, 0) bo1 = boHandle1.map() ``` -------------------------------- ### Execute Kernel and Wait for Completion using PyXRT Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_py/hello_world_py/details.rst This snippet illustrates how to create a kernel object and execute it with specified buffer arguments. It then waits for the kernel's execution to complete. This requires kernel handle, buffer handles, and a count parameter. The state of the kernel execution is returned. ```python run = kHandle(boHandle1, boHandle2, boHandle3, COUNT) state = run.wait() ``` -------------------------------- ### HLS C/C++ Kernel for Vector Addition Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_py/hello_world_py/README.rst This C++ code defines a hardware kernel for vector addition. It utilizes HLS pragmas to guide the synthesis process. The function takes pointers to input arrays and an output array, along with the size, performing element-wise addition. This is the core computation executed on the FPGA. ```cpp #include "vadd.h" extern "C" { void vadd(const int *a, const int *b, int *c, const int n) { #pragma HLS INTERFACE m_axi port=a offset=slave bundle=gmem #pragma HLS INTERFACE m_axi port=b offset=slave bundle=gmem #pragma HLS INTERFACE m_axi port=c offset=slave bundle=gmem #pragma HLS INTERFACE s_axilite port=n bundle=control #pragma HLS INTERFACE s_axilite port=return bundle=control for (int i = 0; i < n; ++i) { #pragma HLS PIPELINE II=1 c[i] = a[i] + b[i]; } } } ``` -------------------------------- ### Program Initialization with New Binary in C++ Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/kernel_swap/README.rst This C++ code snippet demonstrates how to load and initialize a new OpenCL program on the device using a different binary container. This is a key step in kernel swapping, allowing the host to reconfigure the FPGA with a new set of kernels. The `vadd_bins` variable would contain the compiled binary for the new kernel. ```cpp cl::Program program(context, devices, vadd_bins); ``` -------------------------------- ### Integrate RTL Kernel with Streaming Interfaces using C++ Source: https://context7.com/xilinx/vitis_accel_examples/llms.txt This C++ code demonstrates how to set up an OpenCL environment, program an FPGA with RTL kernels, and launch a streaming pipeline for data processing. It involves allocating host and device memory, setting kernel arguments, migrating data, and validating results. Ensure the 'rtl_kernel.xclbin' binary is available. ```cpp #include "xcl2.hpp" #include #define DATA_SIZE 4096 #define INCR_VALUE 10 int main(int argc, char** argv) { std::string binaryFile = "rtl_kernel.xclbin"; // Allocate host memory auto vector_size_bytes = sizeof(int) * DATA_SIZE; std::vector> source_input(DATA_SIZE); std::vector> source_hw_results(DATA_SIZE); std::vector> source_sw_results(DATA_SIZE); // Create test data for (int i = 0; i < DATA_SIZE; i++) { source_input[i] = i; source_sw_results[i] = i + INCR_VALUE; source_hw_results[i] = 0; } // Setup OpenCL environment cl_int err; cl::CommandQueue q; cl::Context context; cl::Program program; auto devices = xcl::get_xil_devices(); auto fileBuf = xcl::read_binary_file(binaryFile); cl::Program::Binaries bins{{fileBuf.data(), fileBuf.size()}}; bool valid_device = false; for (unsigned int i = 0; i < devices.size(); i++) { auto device = devices[i]; OCL_CHECK(err, context = cl::Context(device, nullptr, nullptr, nullptr, &err)); OCL_CHECK(err, q = cl::CommandQueue(context, device, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_PROFILING_ENABLE, &err)); std::cout << "Programming device[" << i << "]: " << device.getInfo() << std::endl; program = cl::Program(context, {device}, bins, nullptr, &err); if (err == CL_SUCCESS) { std::cout << "Device[" << i << "]: program successful!\n"; valid_device = true; break; } } if (!valid_device) { std::cout << "Failed to program any device, exit!\n"; exit(EXIT_FAILURE); } // Create RTL kernels - streaming pipeline OCL_CHECK(err, cl::Kernel krnl_input_stage(program, "krnl_input_stage_rtl", &err)); OCL_CHECK(err, cl::Kernel krnl_adder_stage(program, "krnl_adder_stage_rtl", &err)); OCL_CHECK(err, cl::Kernel krnl_output_stage(program, "krnl_output_stage_rtl", &err)); // Allocate buffers OCL_CHECK(err, cl::Buffer buffer_input(context, CL_MEM_USE_HOST_PTR | CL_MEM_READ_ONLY, vector_size_bytes, source_input.data(), &err)); OCL_CHECK(err, cl::Buffer buffer_output(context, CL_MEM_USE_HOST_PTR | CL_MEM_WRITE_ONLY, vector_size_bytes, source_hw_results.data(), &err)); auto inc = INCR_VALUE; auto size = DATA_SIZE; // Set kernel arguments OCL_CHECK(err, err = krnl_input_stage.setArg(0, buffer_input)); OCL_CHECK(err, err = krnl_input_stage.setArg(1, size)); OCL_CHECK(err, err = krnl_adder_stage.setArg(0, inc)); OCL_CHECK(err, err = krnl_adder_stage.setArg(1, size)); OCL_CHECK(err, err = krnl_output_stage.setArg(0, buffer_output)); OCL_CHECK(err, err = krnl_output_stage.setArg(1, size)); // Migrate input data to device OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_input}, 0 /* to device */)); // Launch streaming kernel pipeline OCL_CHECK(err, err = q.enqueueTask(krnl_input_stage)); OCL_CHECK(err, err = q.enqueueTask(krnl_adder_stage)); OCL_CHECK(err, err = q.enqueueTask(krnl_output_stage)); // Wait for completion OCL_CHECK(err, err = q.finish()); // Read results OCL_CHECK(err, err = q.enqueueMigrateMemObjects({buffer_output}, CL_MIGRATE_MEM_OBJECT_HOST)); OCL_CHECK(err, err = q.finish()); // Validate results bool match = true; for (int i = 0; i < DATA_SIZE; i++) { if (source_hw_results[i] != source_sw_results[i]) { std::cout << "Error: Result mismatch at index " << i << std::endl; std::cout << "Expected: " << source_sw_results[i] << ", Got: " << source_hw_results[i] << std::endl; match = false; break; } } std::cout << (match ? "TEST PASSED" : "TEST FAILED") << std::endl; return match ? EXIT_SUCCESS : EXIT_FAILURE; } ``` -------------------------------- ### Vitis Kernel Compiler Flags for Multi-DDR Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst These are the Vitis kernel compiler flags used to enable multi-DDR configurations. The `-max_memory_ports all` flag ensures that all available memory ports are considered. ```shell -max_memory_ports all ``` -------------------------------- ### Configure Kernel Arguments in Host Code (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/streaming_k2k_mm_xrt/details.rst Sets kernel arguments for two kernels, 'vadd' and 'vmult'. Note that stream interfaces do not require explicit argument setup in the host code. ```cpp run.set_arg(0,bo0); run.set_arg(1,bo1); run.set_arg(3,DATA_SIZE); run1.set_arg(0,bo2); run1.set_arg(2,bo_out); run1.set_arg(3,DATA_SIZE); ``` -------------------------------- ### Configure Compute Units and Memory Banks in vadd.cfg Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/multiple_cus_asymmetrical_xrt/details.rst This configuration file specifies the instantiation of multiple compute units for the 'vadd' kernel and their respective connections to different memory banks like DDR and PLRAM. It uses 'nk' to define the number and naming of compute units and 'sp' to map their ports to specific memory interfaces. ```text [connectivity] nk=vadd:4:vadd_1.vadd_2.vadd_3.vadd_4 sp=vadd_1.in1:DDR[0] sp=vadd_1.in2:DDR[0] sp=vadd_1.out:DDR[0] sp=vadd_2.in1:DDR[1] sp=vadd_2.in2:DDR[1] sp=vadd_2.out:DDR[1] sp=vadd_3.in1:PLRAM[0] sp=vadd_3.in2:PLRAM[0] sp=vadd_3.out:PLRAM[0] sp=vadd_4.in1:PLRAM[1] sp=vadd_4.in2:PLRAM[1] sp=vadd_4.out:PLRAM[1] ``` -------------------------------- ### Kernel Global Bandwidth C++ Source File Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst The C++ source file specifically implementing the global bandwidth test functionality. This file contains the core logic for performing memory transfers and measuring bandwidth. ```c++ // src/kernel_global_bandwidth.cpp // This file implements the kernel_global_bandwidth function. // It performs read and write operations to global memory to measure bandwidth. // It utilizes OpenCL or Vitis kernel constructs. ``` -------------------------------- ### Migrate Memory Objects for Bandwidth Test (C++) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/host_global_bandwidth/details.rst This C++ code snippet demonstrates how to use the `clenqueueMigrateMemObjects` API to test host-to-device and device-to-host memory transfer bandwidth. It is crucial for optimizing data movement in PCIe-based FPGA applications. The second argument to the function specifies the direction of migration (0 for host to device, `CL_MIGRATE_MEM_OBJECT_HOST` for device to host). ```cpp err = commands.enqueueMigrateMemObjects(mems1, 0/* 0 means from host*/); err = commands.enqueueMigrateMemObjects(mems2, CL_MIGRATE_MEM_OBJECT_HOST); ``` -------------------------------- ### Migrating Buffer to Device Memory in C++ Source: https://github.com/xilinx/vitis_accel_examples/blob/main/sys_opt/kernel_swap/README.rst This C++ code snippet illustrates the process of migrating data from host memory to device memory for the second kernel's execution. After reprogramming the device and creating the new buffer (`d_temp`), this command ensures that the data residing in `h_temp` is transferred to the device. A migration direction of `0` explicitly signifies a transfer from host to device. ```cpp q.enqueueMigrateMemObjects({d_temp}, 0/* 0 means from host*/); ``` -------------------------------- ### Kernel Source Code with DDR Bank Definition Source: https://github.com/xilinx/vitis_accel_examples/blob/main/performance/kernel_global_bandwidth/README.rst This snippet shows how to define the number of DDR banks used by the kernel. The NDDR_BANKS macro is set at the top of the kernel source file to inform the compiler about the memory configuration. ```c // #define NDDR_BANKS 3 // Define the number of DDR banks to be used by the kernel. // This macro should be set according to the XSA and configuration. ``` -------------------------------- ### Control FPGA Acceleration with Python XRT Bindings Source: https://context7.com/xilinx/vitis_accel_examples/llms.txt This Python script demonstrates how to use the pyxrt module to control FPGA acceleration. It initializes a device, loads an xclbin, allocates buffers, transfers data, executes a kernel, and validates the results. Dependencies include the pyxrt library and a compiled kernel (kernel.xclbin). The function takes device index, bitstream file path, and data size as input, returning 0 on success or exiting with an error code. ```python #!/usr/bin/python3 import os import sys import re import pyxrt def runKernel(device_index, bitstreamFile, DATA_SIZE): # Initialize device and load xclbin d = pyxrt.device(device_index) xbin = pyxrt.xclbin(bitstreamFile) uuid = d.load_xclbin(xbin) # Find and create kernel kernellist = xbin.get_kernels() rule = re.compile("vadd*") kernel = list(filter(lambda val: rule.match(val.get_name()), kernellist))[0] kHandle = pyxrt.kernel(d, uuid, kernel.get_name(), pyxrt.kernel.shared) # Allocate and initialize buffers print("Allocate and initialize buffers") zeros = bytearray(DATA_SIZE) boHandle1 = pyxrt.bo(d, DATA_SIZE, pyxrt.bo.normal, kHandle.group_id(0)) boHandle1.write(zeros, 0) bo1 = boHandle1.map() boHandle2 = pyxrt.bo(d, DATA_SIZE, pyxrt.bo.normal, kHandle.group_id(1)) boHandle2.write(zeros, 0) bo2 = boHandle2.map() boHandle3 = pyxrt.bo(d, DATA_SIZE, pyxrt.bo.normal, kHandle.group_id(2)) boHandle3.write(zeros, 0) bo3 = boHandle3.map() # Initialize input data for i in range(DATA_SIZE): bo1[i] = 1 bo2[i] = 2 bufReference = [3 for i in range(DATA_SIZE)] # Transfer to device boHandle1.sync(pyxrt.xclBOSyncDirection.XCL_BO_SYNC_BO_TO_DEVICE, DATA_SIZE, 0) boHandle2.sync(pyxrt.xclBOSyncDirection.XCL_BO_SYNC_BO_TO_DEVICE, DATA_SIZE, 0) # Execute kernel print("Start the kernel") run = kHandle(boHandle1, boHandle2, boHandle3, DATA_SIZE) print("Wait for kernel to finish") state = run.wait() # Get results and validate print("Get output data and validate") boHandle3.sync(pyxrt.xclBOSyncDirection.XCL_BO_SYNC_BO_FROM_DEVICE, DATA_SIZE, 0) for i in range(DATA_SIZE): if bufReference[i] != bo3[i]: print("Computed value does not match reference") raise AssertionError("Validation failed") print("TEST PASSED") return 0 if __name__ == "__main__": os.environ["Runtime.xrt_bo"] = "false" try: result = runKernel(0, "kernel.xclbin", 256) sys.exit(result) except Exception as e: print(f"TEST FAILED: {e}") sys.exit(-1) ``` -------------------------------- ### Vector Addition Kernel Code (Verilog) Source: https://github.com/xilinx/vitis_accel_examples/blob/main/host_xrt/hbm_simple_xrt/README.rst The Verilog kernel code for vector addition. It performs element-wise addition of two input vectors and stores the result in an output vector. This kernel is designed to be used with HLS and XRT, interfacing with memory banks. ```systemverilog module krnl_vadd ( input wire clk, input wire reset, // Input 1 input wire [31:0] in1_r_TDATA, input wire [0:0] in1_r_TVALID, output wire [0:0] in1_r_TREADY, // Input 2 input wire [31:0] in2_r_TDATA, input wire [0:0] in2_r_TVALID, output wire [0:0] in2_r_TREADY, // Output output wire [31:0] out_r_TDATA, output wire [0:0] out_r_TVALID, input wire [0:0] out_r_TREADY ); reg [31:0] data_a; reg [31:0] data_b; reg [31:0] data_c; // Input 1 handshake assign in1_r_TREADY = 1'b1; always @(posedge clk) begin if (in1_r_TVALID) data_a <= in1_r_TDATA; end // Input 2 handshake assign in2_r_TREADY = 1'b1; always @(posedge clk) begin if (in2_r_TVALID) data_b <= in2_r_TDATA; end // Perform addition always @(posedge clk) begin data_c <= data_a + data_b; end // Output handshake and data assign out_r_TDATA = data_c; assign out_r_TVALID = 1'b1; // Assuming data is always ready endmodule ```