diff --git a/README.md b/README.md index f13a9cf..321b6eb 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,8 @@ There are a few ways: #### How do I use it? -Just `#include` the header(s) you are interested in. You can see an example [here](https://github.com/spcl/gemm_hls)! - -When a Xilinx hlslib header is included, compilation must allow C++11 features, and the macro `HLSLIB_SYNTHESIS` must be set whenever HLS is run. Set `-cflags "-std=c++11 -DHLSLIB_SYNTHESIS"` in your synthesis script, and `--advanced.prop kernel..kernel_flags="-std=c++11 -DHLSLIB_SYNTHESIS"` when building Xilinx kernels. See the included `xilinx_test/CMakeLists.txt` for reference. +Just `#include` the header(s) you are interested in, and/or put the CMake files somewhere in your project directory and +add their path to your `${CMAKE_MODULE_PATH}`. You can see an example [here](https://github.com/spcl/gemm_hls)! Officially supported versions of Vitis currently include 2021.1, 2020.2, 2020.1, and 2019.2. Older versions (including SDx and SDAccel) _might_ work, but should be used at your own discretion. @@ -29,7 +28,7 @@ A brief overview of hlslib features is given below. #### CMake integration -For integrating the Xilinx or Intel HLS tools in your project, the `FindVitis.cmake` and `FindIntelFPGAOpenCL.cmake` are provided in the `cmake` subdirectory. The scripts will set all necessary variables required to build both host and device code. It also provides the `add_vitis_kernel` function, which will produce targets for building hardware emulation, hardware, and synthesis. +For integrating the Xilinx or Intel HLS tools in your project, the `FindVitis.cmake` and `FindIntelFPGAOpenCL.cmake` are provided in the `cmake` subdirectory. The scripts will set all necessary variables required to build both host and device code. It also provides the `add_vitis_kernel` and `add_vitis_program` functions, which will produce targets for building hardware emulation, hardware, and high-level synthesis. Example `CMakeLists.txt`: ```cmake @@ -41,7 +40,8 @@ include_directories(${Vitis_INCLUDE_DIRS}) target_link_libraries(MyHostExecutable ${Vitis_LIBRARIES}) # Will populate the "hw", "hw_emu", and "synthesis" targets -add_vitis_kernel(MyKernel xilinx_vcu1525_dynamic_5_1 FILES src/MyKernel.cpp) +add_vitis_kernel(MyKernel FILES src/MyKernel.cpp) +add_vitis_program(MyKernel xilinx_u250_gen3x16_xdma_3_1_202020_1) ``` Kernels can then be built with: @@ -50,23 +50,34 @@ Kernels can then be built with: make hw ``` -The `add_vitis_kernel` takes a number of optional arguments that can be used to configure the hardware targets: +The `add_vitis_kernel` and `add_vitis_program` functions takes a number of optional arguments that can be used to configure the hardware targets: ```cmake -add_vitis_kernel(MyKernel xilinx_vcu1525_dynamic_5_1 +add_vitis_kernel(MyKernel FILES src/MyKernel.cpp src/MyKernelHelper.cpp - # All flags below this are optional keywords, and any combination of them - # can be specified/not specified - CLOCK 400 # Target a higher clock frequency - KERNEL NameOfKernelFunction # If different from target name - CONFIG scripts/my_config.cfg # Given as --config to Vitis - SAVE_TEMPS ON # Forwards --save-temps to Vitis + # All flags below this are optional keywords, and any + # combination of them can be specified/not specified. + KERNEL MyKernelName # If different from target name HLS_FLAGS "-DMY_IMPORTANT_DEFINITION -O2" - BUILD_FLAGS "-Os --export_script" - DEBUGGING ON # Enables Chipscope debugging on all interfaces - PROFILING ON # Enables profiling for stalls, data transfers, and execution DEPENDS include/MyHeader.h include/OtherDependency.h - INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include hlslib/include) + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include hlslib/include + PORT_MAPPING "ddr0:DDR[0]" "ddr1:DDR[1]") +add_vitis_kernel(MyOtherKernel + FILES src/MyOtherKernel.cpp) +add_vitis_program(MyProgram + xilinx_u250_gen3x16_xdma_3_1_202020_1 # Name of Vitis platform + # All flags below this are optional keywords, and any + # combination of them can be specified/not specified. + KERNELS MyKernel # If KERNELS is not specified, the function checks for a kernel + MyOtherKernel # with the same name as specified for the program + # Connect multiple linked kernels using streaming interfaces + CONNECTIVITY "MyKernel_1.stream_out:MyOtherKernel_1.stream_in" + CLOCK 400 # Target a different clock frequency than the default + CONFIG scripts/my_config.cfg # Given as --config to Vitis + SAVE_TEMPS ON # Forwards --save-temps to Vitis + BUILD_FLAGS "-Os --export_script" + DEBUGGING ON # Enables Chipscope debugging on all interfaces + PROFILING ON) # Enables profiling for stalls, data transfers, and execution ``` #### DataPack diff --git a/cmake/FindVitis.cmake b/cmake/FindVitis.cmake index be25277..e2b5bc2 100644 --- a/cmake/FindVitis.cmake +++ b/cmake/FindVitis.cmake @@ -275,28 +275,17 @@ function(hlslib_make_paths_absolute OUTPUT_FILES) set(${OUTPUT_FILES} ${_OUTPUT_FILES} PARENT_SCOPE) endfunction() -# Registers targets to compile and link hardware and hardware emulation kernels -# for a kernel with the given name, targeting the given platform. -# -# For a target "foo", the build targets will be called: -# make foo_hw -# make foo_hw_emu -# Or for each step individually: -# make compile_foo_hw -# make link_foo_hw -# # The name of the kernel is expected to match the target name. If it does not, # the kernel name can be passed separately with the KERNEL keyword. function(add_vitis_kernel - KERNEL_TARGET_NAME - KERNEL_PLATFORM) + KERNEL_TARGET) # Keyword arguments cmake_parse_arguments( KERNEL "" - "CLOCK;KERNEL;CONFIG;SAVE_TEMPS;DEBUGGING;PROFILING" - "FILES;HLS_FLAGS;BUILD_FLAGS;COMPILE_FLAGS;LINK_FLAGS;DEPENDS;INCLUDE_DIRS;PORT_MAPPING" + "KERNEL" + "FILES;DEPENDS;INCLUDE_DIRS;HLS_FLAGS;COMPILE_FLAGS;PORT_MAPPING" ${ARGN}) # Verify that input is sane @@ -316,72 +305,15 @@ function(add_vitis_kernel endforeach() set(KERNEL_DEPENDS ${KERNEL_FILES} ${_KERNEL_DEPENDS}) - # Recover the part name used by the given platform - if(NOT "${${KERNEL_TARGET_NAME}_PLATFORM}" STREQUAL "${KERNEL_PLATFORM}") - set(${KERNEL_TARGET_NAME}_PLATFORM "" CACHE INTERNAL "") - message(STATUS "Querying Vitis platform for ${KERNEL_TARGET_NAME}.") - execute_process(COMMAND ${Vitis_PLATFORMINFO} --platform ${KERNEL_PLATFORM} -jhardwarePlatform.board.part - OUTPUT_VARIABLE KERNEL_PLATFORM_PART - RESULT_VARIABLE PLATFORM_FOUND) - string(STRIP ${KERNEL_PLATFORM_PART} KERNEL_PLATFORM_PART) - set(KERNEL_PLATFORM_PART ${KERNEL_PLATFORM_PART} CACHE INTERNAL "") - endif() - if(NOT KERNEL_PLATFORM_PART) - message(WARNING "Xilinx platform ${KERNEL_PLATFORM} was not found. Please consult \"${Vitis_PLATFORMINFO} -l\" for a list of installed platforms.") - else() - # Cache this so we don't have to rerun platform info if the platform didn't change - set(${KERNEL_TARGET_NAME}_PLATFORM ${KERNEL_PLATFORM} CACHE INTERNAL "") - endif() - - # Augment with the platform specification - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --platform ${KERNEL_PLATFORM}") - - # Augment with frequency flag is specified - if(KERNEL_CLOCK) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --kernel_frequency ${KERNEL_CLOCK}") - endif() + # Create the target that will carry the properties and dependencies + add_custom_target(${KERNEL_TARGET} DEPENDS ${KERNEL_DEPENDS}) # Use the target name as the kernel name if the kernel name hasn't been # explicitly passed if(DEFINED KERNEL_KERNEL) set(KERNEL_NAME ${KERNEL_KERNEL}) else() - set(KERNEL_NAME ${KERNEL_TARGET_NAME}) - endif() - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --kernel ${KERNEL_NAME}") - - # Pass config file if specified - if(KERNEL_CONFIG) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --config ${KERNEL_CONFIG}") - endif() - - # Save temporaries if instructed to do so - if(DEFINED KERNEL_SAVE_TEMPS) - if(${KERNEL_SAVE_TEMPS}) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --save-temps") - endif() - endif() - - # Default to -O3 if no other optimization flag is passed - string(FIND "${KERNEL_BUILD_FLAGS}" "-O" FOUND_SHORT) - string(FIND "${KERNEL_BUILD_FLAGS}" "--optimize" FOUND_LONG) - if(FOUND_SHORT EQUAL -1_SHORT AND FOUND_LONG EQUAL -1) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} -O3") - endif() - - # Optional profiling flags - if(DEFINED KERNEL_PROFILING) - if(${KERNEL_PROFILING}) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --profile.data all:all:all --profile.exec all:all --profile.stall all:all") - endif() - endif() - - # Optional debugging flags - if(DEFINED KERNEL_DEBUGGING) - if(${KERNEL_DEBUGGING}) - # Append _1 to match the Vitis convention (only supports single compute unit kernels) - set(KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS} --debug --debug.chipscope ${KERNEL_NAME}_1") - endif() + set(KERNEL_NAME ${KERNEL_TARGET}) endif() # Specify port mapping @@ -391,7 +323,7 @@ function(add_vitis_kernel if(IS_MEMORY_BANK) set(KERNEL_LINK_FLAGS "${KERNEL_LINK_FLAGS} --connectivity.sp ${KERNEL_NAME}_1.${MAPPING}") else() - message(FATAL_ERROR "Unrecognized port mapping ${MAPPING}.") + message(FATAL_ERROR "Unrecognized port mapping \"${MAPPING}\".") endif() endforeach() @@ -423,7 +355,7 @@ function(add_vitis_kernel set(KERNEL_HLS_FLAGS "${KERNEL_HLS_FLAGS} -DVITIS_VERSION=${Vitis_VERSION}") endif() - # Tell hlslib if we're using Vitis HLS or Vivado HLS + # Tell hlslib whether we're using Vitis HLS or Vivado HLS if(NOT Vitis_USE_VITIS_HLS) set(KERNEL_HLS_FLAGS "${KERNEL_HLS_FLAGS} -D__VIVADO_HLS__") else() @@ -442,138 +374,349 @@ function(add_vitis_kernel string(REGEX REPLACE ";|[ \t\r\n][ \t\r\n]+" " " KERNEL_HLS_FLAGS "${KERNEL_HLS_FLAGS}") string(STRIP "${KERNEL_HLS_FLAGS}" KERNEL_HLS_FLAGS) - # Pass HLS flags in compilation stage - set(KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS} --advanced.prop kernel.${KERNEL_NAME}.kernel_flags=\"${KERNEL_HLS_FLAGS}\"") + # Pass variables the program target through properties + set_target_properties(${KERNEL_TARGET} PROPERTIES KERNEL_FILES "${KERNEL_FILES}") + set_target_properties(${KERNEL_TARGET} PROPERTIES KERNEL_NAME "${KERNEL_NAME}") + set_target_properties(${KERNEL_TARGET} PROPERTIES HLS_FLAGS "${KERNEL_HLS_FLAGS}") + set_target_properties(${KERNEL_TARGET} PROPERTIES COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") + set_target_properties(${KERNEL_TARGET} PROPERTIES LINK_FLAGS "${KERNEL_LINK_FLAGS}") + +endfunction() + +function(add_vitis_program + PROGRAM_TARGET + PROGRAM_PLATFORM) + + # Keyword arguments + cmake_parse_arguments( + PROGRAM + "" + "CLOCK;CONFIG;SAVE_TEMPS;DEBUGGING;PROFILING" + "KERNELS;BUILD_FLAGS;LINK_FLAGS;CONNECTIVITY;DEPENDS" + ${ARGN}) + + # Verify that input is sane + if(NOT PROGRAM_KERNELS) + if(TARGET ${PROGRAM_TARGET}) + # If no kernels were specified, try using the same target name + set(PROGRAM_KERNELS ${PROGRAM_TARGET}) + else() + # Otherwise we have to give up + message(FATAL_ERROR "Must pass kernel targets created with add_vitis_kernel to add_vitis_program using the KERNELS keyword.") + endif() + endif() + foreach(KERNEL ${PROGRAM_KERNELS}) + if(NOT TARGET ${KERNEL}) + message(FATAL_ERROR "${KERNEL} is not a valid Vitis kernel. Kernels must have been created with the add_vitis_kernel function.") + endif() + endforeach() + + # Convert non-target dependencies to absolute paths + string(REPLACE " " ";" PROGRAM_DEPENDS "${PROGRAM_DEPENDS}") + unset(_PROGRAM_DEPENDS) + foreach(DEP ${PROGRAM_DEPENDS}) + if(NOT TARGET ${DEP}) + hlslib_make_paths_absolute(DEP ${DEP}) + endif() + set(_PROGRAM_DEPENDS ${_PROGRAM_DEPENDS} ${DEP}) + endforeach() + set(PROGRAM_DEPENDS ${PROGRAM_DEPENDS} ${_PROGRAM_DEPENDS}) + + # Recover the part name used by the given platform + if(NOT PROGRAM_PLATFORM_PART OR NOT "${${PROGRAM_TARGET}_PLATFORM}" STREQUAL "${PROGRAM_PLATFORM}") + message(STATUS "Querying Vitis platform for ${PROGRAM_TARGET}.") + execute_process(COMMAND ${Vitis_PLATFORMINFO} --platform ${PROGRAM_PLATFORM} -jhardwarePlatform.board.part + OUTPUT_VARIABLE PLATFORM_PART) + string(STRIP "${PLATFORM_PART}" PLATFORM_PART) + if(PLATFORM_PART) + set(PROGRAM_PLATFORM_PART ${PLATFORM_PART} CACHE INTERNAL "") + endif() + endif() + if(NOT PROGRAM_PLATFORM_PART) + message(WARNING "Xilinx platform ${PROGRAM_PLATFORM} was not found. Please consult \"${Vitis_PLATFORMINFO} -l\" for a list of installed platforms.") + else() + # Cache this so we don't have to rerun platforminfo if the platform didn't change + set(${PROGRAM_TARGET}_PLATFORM ${PROGRAM_PLATFORM} CACHE INTERNAL "") + endif() + + # Specify the platform + set(PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS} --platform ${PROGRAM_PLATFORM}") + + # Augment with frequency flag if specified + if(PROGRAM_CLOCK) + set(PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS} --kernel_frequency ${PROGRAM_CLOCK}") + endif() + + # Pass config file if specified + if(PROGRAM_CONFIG) + set(PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS} --config ${PROGRAM_CONFIG}") + endif() + + # Save temporaries if instructed to do so + if(PROGRAM_SAVE_TEMPS) + set(PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS} --save-temps") + endif() + + # Default to -O3 if no other optimization flag is passed + string(FIND "${PROGRAM_BUILD_FLAGS} ${PROGRAM_LINK_FLAGS}" "-O" FOUND_SHORT) + string(FIND "${PROGRAM_BUILD_FLAGS} ${PROGRAM_LINK_FLAGS}" "--optimize" FOUND_LONG) + if(FOUND_SHORT EQUAL -1 AND FOUND_LONG EQUAL -1) + set(PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS} -O3") + endif() + + # Optional profiling flags + if(PROGRAM_PROFILING) + set(PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS} --profile.data all:all:all --profile.exec all:all --profile.stall all:all") + endif() + + # Optional debugging flags + if(PROGRAM_DEBUGGING) + foreach(KERNEL ${PROGRAM_KERNELS}) + # Append _1 to match the Vitis convention (only supports single compute unit kernels) + get_target_property(KERNEL_NAME ${KERNEL} KERNEL_NAME) + set(PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS} --debug --debug.chipscope ${KERNEL_NAME}_1") + endforeach() + endif() + + # Mapping between streaming interfaces on kernels + string(REPLACE " " ";" PROGRAM_CONNECTIVITY "${PROGRAM_CONNECTIVITY}") + foreach(MAPPING ${PROGRAM_CONNECTIVITY}) + string(REGEX MATCH "[^\\.: \t\n]+\\.[^\\.: \t\n]+:[^\\.: \t\n]+\\.[^\\.: \t\n]+" IS_MAPPING ${MAPPING}) + if(IS_MAPPING) + set(PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS} --connectivity.sc ${MAPPING}") + else() + message(FATAL_ERROR "Unrecognized kernel mapping \"${MAPPING}\".") + endif() + endforeach() # Clean up build flags by removing superfluous whitespace and convert from # string syntax to list syntax, - string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") - string(STRIP "${KERNEL_COMPILE_FLAGS}" KERNEL_COMPILE_FLAGS) - string(REGEX REPLACE " " ";" KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") - string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") - string(STRIP "${KERNEL_LINK_FLAGS}" KERNEL_LINK_FLAGS) - string(REGEX REPLACE " " ";" KERNEL_LINK_FLAGS "${KERNEL_LINK_FLAGS}") - string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS}") - string(STRIP "${KERNEL_BUILD_FLAGS}" KERNEL_BUILD_FLAGS) - string(REGEX REPLACE " " ";" KERNEL_BUILD_FLAGS "${KERNEL_BUILD_FLAGS}") + string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS}") + string(STRIP "${PROGRAM_BUILD_FLAGS}" PROGRAM_BUILD_FLAGS) + string(REGEX REPLACE " " ";" PROGRAM_BUILD_FLAGS "${PROGRAM_BUILD_FLAGS}") + + unset(PROGRAM_XO_FILES_SW_EMU) + unset(PROGRAM_XO_FILES_HW_EMU) + unset(PROGRAM_XO_FILES_HW) + + foreach(KERNEL ${PROGRAM_KERNELS}) + + get_target_property(KERNEL_NAME ${KERNEL} KERNEL_NAME) + get_target_property(KERNEL_FILES ${KERNEL} KERNEL_FILES) + get_target_property(KERNEL_HLS_FLAGS ${KERNEL} HLS_FLAGS) + get_target_property(KERNEL_COMPILE_FLAGS ${KERNEL} COMPILE_FLAGS) + get_target_property(KERNEL_LINK_FLAGS ${KERNEL} LINK_FLAGS) + + set(KERNEL_COMPILE_FLAGS "${PROGRAM_COMPILE_FLAGS} ${KERNEL_COMPILE_FLAGS} --advanced.prop kernel.${KERNEL_NAME}.kernel_flags=\"${KERNEL_HLS_FLAGS}\"") + set(PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS} ${KERNEL_LINK_FLAGS}") + + # Canonicalize flags + string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") + string(STRIP "${KERNEL_COMPILE_FLAGS}" KERNEL_COMPILE_FLAGS) + string(REGEX REPLACE " " ";" KERNEL_COMPILE_FLAGS "${KERNEL_COMPILE_FLAGS}") + + # Software emulation target + set(KERNEL_XO_FILE_SW_EMU ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_NAME}_sw_emu.xo) + set(PROGRAM_XO_FILES_SW_EMU ${PROGRAM_XO_FILES_SW_EMU} ${KERNEL_XO_FILE_SW_EMU}) + add_custom_command( + OUTPUT ${KERNEL_XO_FILE_SW_EMU} + COMMENT "Compiling ${KERNEL} for software emulation." + COMMAND ${CMAKE_COMMAND} -E env + XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} + ${Vitis_COMPILER} --compile --target sw_emu + --kernel ${KERNEL_NAME} + ${KERNEL_COMPILE_FLAGS} + ${PROGRAM_BUILD_FLAGS} + ${KERNEL_FILES} + --output ${KERNEL_XO_FILE_SW_EMU} + DEPENDS ${KERNEL}) + add_custom_target(compile_${KERNEL}_sw_emu DEPENDS + ${KERNEL_XO_FILE_SW_EMU}) + if(NOT TARGET compile_sw_emu) + add_custom_target(compile_sw_emu COMMENT "Compiling software emulation targets." + DEPENDS compile_${KERNEL}_sw_emu) + else() + add_dependencies(compile_sw_emu compile_${KERNEL}_sw_emu) + endif() + + # Hardware emulation target + set(KERNEL_XO_FILE_HW_EMU ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_NAME}_hw_emu.xo) + set(PROGRAM_XO_FILES_HW_EMU ${PROGRAM_XO_FILES_HW_EMU} ${KERNEL_XO_FILE_HW_EMU}) + add_custom_command( + OUTPUT ${KERNEL_XO_FILE_HW_EMU} + COMMENT "Compiling ${KERNEL} for hardware emulation." + COMMAND ${CMAKE_COMMAND} -E env + XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} + ${Vitis_COMPILER} --compile --target hw_emu + --kernel ${KERNEL_NAME} + ${KERNEL_COMPILE_FLAGS} + ${PROGRAM_BUILD_FLAGS} + ${KERNEL_FILES} + --output ${KERNEL_XO_FILE_HW_EMU} + DEPENDS ${KERNEL}) + add_custom_target(compile_${KERNEL}_hw_emu DEPENDS + ${KERNEL_XO_FILE_HW_EMU}) + if(NOT TARGET compile_hw_emu) + add_custom_target(compile_hw_emu COMMENT "Compiling hardware emulation targets." + DEPENDS compile_${KERNEL}_hw_emu) + else() + add_dependencies(compile_hw_emu compile_${KERNEL}_hw_emu) + endif() + + # Hardware target + set(KERNEL_XO_FILE_HW ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_NAME}_hw.xo) + set(PROGRAM_XO_FILES_HW ${PROGRAM_XO_FILES_HW} ${KERNEL_XO_FILE_HW}) + add_custom_command( + OUTPUT ${KERNEL_XO_FILE_HW} + COMMENT "Compiling ${KERNEL_NAME} for hardware." + COMMAND ${CMAKE_COMMAND} -E env + XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} + ${Vitis_COMPILER} --compile --target hw + --kernel ${KERNEL_NAME} + ${KERNEL_COMPILE_FLAGS} + ${PROGRAM_BUILD_FLAGS} + ${KERNEL_FILES} + --output ${KERNEL_XO_FILE_HW} + DEPENDS ${KERNEL}) + add_custom_target(compile_${KERNEL}_hw DEPENDS + ${KERNEL_XO_FILE_HW}) + if(NOT TARGET compile_hw) + add_custom_target(compile_hw COMMENT "Compiling hardware targets." + DEPENDS compile_${KERNEL}_hw) + else() + add_dependencies(compile_hw compile_${KERNEL}_hw) + endif() + + if(PROGRAM_PLATFORM_PART) + # Make separate synthesis target, which is faster to run than Vitis compile + if(PROGRAM_CLOCK) + set(KERNEL_HLS_TCL_CLOCK "create_clock -period ${PROGRAM_CLOCK}MHz -name default\n") + endif() + string(REPLACE ";" " " KERNEL_FILES "${KERNEL_FILES}") + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL}_synthesis.tcl + "\ +open_project ${KERNEL} \ +open_solution ${PROGRAM_PLATFORM_PART} \ +set_part ${PROGRAM_PLATFORM_PART} \ +add_files -cflags \"${KERNEL_HLS_FLAGS}\" \"${KERNEL_FILES}\" \ +set_top ${KERNEL_NAME} \ +${KERNEL_HLS_TCL_CLOCK}\ +config_interface -m_axi_addr64 \ +config_compile -name_max_length 256 \ +csynth_design \ +exit") + add_custom_command(OUTPUT ${KERNEL}/${KERNEL_PLATFORM_PART}/${KERNEL_PLATFORM_PART}.log + COMMENT "Running high-level synthesis for ${KERNEL}." + COMMAND ${Vitis_HLS} -f ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL}_synthesis.tcl + DEPENDS ${KERNEL}) + add_custom_target(synthesize_${KERNEL} DEPENDS + ${KERNEL}/${KERNEL_PLATFORM_PART}/${KERNEL_PLATFORM_PART}.log) + endif() + + endforeach() - # Hardware emulation target - add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xo - COMMENT "Compiling ${KERNEL_TARGET_NAME} for hardware emulation." - COMMAND ${CMAKE_COMMAND} -E env - XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} - ${Vitis_COMPILER} --compile --target hw_emu - ${KERNEL_BUILD_FLAGS} - ${KERNEL_COMPILE_FLAGS} - ${KERNEL_FILES} - --output ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xo - DEPENDS ${KERNEL_DEPENDS} ${KERNEL_DEPENDS_DEBUGGING}) - add_custom_target(compile_${KERNEL_TARGET_NAME}_hw_emu DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xo) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/emconfig.json COMMENT "Generating emconfig.json file for hardware emulation." - COMMAND ${VITIS_ROOT}/bin/emconfigutil --platform ${KERNEL_PLATFORM}) - if(NOT TARGET ${KERNEL_PLATFORM}_emconfig) - add_custom_target(${KERNEL_PLATFORM}_emconfig DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/emconfig.json) + COMMAND ${VITIS_ROOT}/bin/emconfigutil --platform ${PROGRAM_PLATFORM}) + if(NOT TARGET ${PROGRAM_PLATFORM}_emconfig) + add_custom_target(${PROGRAM_PLATFORM}_emconfig DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/emconfig.json) endif() + + # Canonicalize link flags + string(REGEX REPLACE "[ \t\r\n][ \t\r\n]+" " " PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS}") + string(STRIP "${PROGRAM_LINK_FLAGS}" PROGRAM_LINK_FLAGS) + string(REGEX REPLACE " " ";" PROGRAM_LINK_FLAGS "${PROGRAM_LINK_FLAGS}") + + # Software emulation target + set(PROGRAM_XCLBIN_SW_EMU ${CMAKE_CURRENT_BINARY_DIR}/${PROGRAM_TARGET}_sw_emu.xclbin) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xclbin - COMMENT "Linking ${KERNEL_TARGET_NAME} for hardware emulation." + OUTPUT ${PROGRAM_XCLBIN_SW_EMU} + COMMENT "Linking ${PROGRAM_TARGET} for software emulation." COMMAND ${CMAKE_COMMAND} -E env XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} - ${Vitis_COMPILER} --link --target hw_emu - ${KERNEL_BUILD_FLAGS} - ${KERNEL_LINK_FLAGS} - ${KERNEL_TARGET_NAME}_hw_emu.xo - --output ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xclbin - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xo - ${KERNEL_PLATFORM}_emconfig) - add_custom_target(link_${KERNEL_TARGET_NAME}_hw_emu DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xclbin) - add_custom_target(${KERNEL_TARGET_NAME}_hw_emu DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw_emu.xclbin) + ${Vitis_COMPILER} --link --target sw_emu + ${PROGRAM_BUILD_FLAGS} + ${PROGRAM_LINK_FLAGS} + ${PROGRAM_XO_FILES_SW_EMU} + --output ${PROGRAM_XCLBIN_SW_EMU} + DEPENDS ${PROGRAM_XO_FILES_SW_EMU} + ${PROGRAM_PLATFORM}_emconfig + ${PROGRAM_DEPENDS}) + add_custom_target(link_${PROGRAM_TARGET}_sw_emu DEPENDS ${PROGRAM_XCLBIN_SW_EMU}) + add_custom_target(${PROGRAM_TARGET}_sw_emu DEPENDS ${PROGRAM_XCLBIN_SW_EMU}) - # Hardware target + # Hardware emulation target + set(PROGRAM_XCLBIN_HW_EMU ${CMAKE_CURRENT_BINARY_DIR}/${PROGRAM_TARGET}_hw_emu.xclbin) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xo - COMMENT "Compiling ${KERNEL_TARGET_NAME} for hardware." + OUTPUT ${PROGRAM_XCLBIN_HW_EMU} + COMMENT "Linking ${PROGRAM_TARGET} for hardware emulation." COMMAND ${CMAKE_COMMAND} -E env XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} - ${Vitis_COMPILER} --compile --target hw - ${KERNEL_BUILD_FLAGS} - ${KERNEL_COMPILE_FLAGS} - ${KERNEL_FILES} - --output ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xo - DEPENDS ${KERNEL_DEPENDS} ${KERNEL_DEPENDS_DEBUGGING}) - add_custom_target(compile_${KERNEL_TARGET_NAME}_hw DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xo) + ${Vitis_COMPILER} --link --target hw_emu + ${PROGRAM_BUILD_FLAGS} + ${PROGRAM_LINK_FLAGS} + ${PROGRAM_XO_FILES_HW_EMU} + --output ${PROGRAM_XCLBIN_HW_EMU} + DEPENDS ${PROGRAM_XO_FILES_HW_EMU} + ${PROGRAM_PLATFORM}_emconfig + ${PROGRAM_DEPENDS}) + add_custom_target(link_${PROGRAM_TARGET}_hw_emu DEPENDS ${PROGRAM_XCLBIN_HW_EMU}) + add_custom_target(${PROGRAM_TARGET}_hw_emu DEPENDS ${PROGRAM_XCLBIN_HW_EMU}) + + # Hardware target + set(PROGRAM_XCLBIN_HW ${CMAKE_CURRENT_BINARY_DIR}/${PROGRAM_TARGET}_hw.xclbin) add_custom_command( - OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xclbin - COMMENT "Linking ${KERNEL_TARGET_NAME} for hardware." + OUTPUT ${PROGRAM_XCLBIN_HW} + COMMENT "Linking ${PROGRAM_TARGET} for hardware." COMMAND ${CMAKE_COMMAND} -E env XILINX_PATH=${CMAKE_CURRENT_BINARY_DIR} ${Vitis_COMPILER} --link --target hw - ${KERNEL_BUILD_FLAGS} - ${KERNEL_LINK_FLAGS} - ${KERNEL_TARGET_NAME}_hw.xo - --output ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xclbin - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xo) - add_custom_target(link_${KERNEL_TARGET_NAME}_hw DEPENDS - ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_hw.xclbin) - add_custom_target(${KERNEL_TARGET_NAME}_hw DEPENDS link_${KERNEL_TARGET_NAME}_hw) + ${PROGRAM_BUILD_FLAGS} + ${PROGRAM_LINK_FLAGS} + ${PROGRAM_XO_FILES_HW} + --output ${PROGRAM_XCLBIN_HW} + DEPENDS ${PROGRAM_XO_FILES_HW} + ${PROGRAM_DEPENDS}) + add_custom_target(link_${PROGRAM_TARGET}_hw DEPENDS ${PROGRAM_XCLBIN_HW}) + add_custom_target(${PROGRAM_TARGET}_hw DEPENDS link_${KERNEL_TARGET_NAME}_hw) # Shorthand to compile kernels, so user can just run "make hw" or "make hw_emu" - if(NOT TARGET hw_emu) - add_custom_target(hw_emu COMMENT "Building hardware emulation targets." - DEPENDS ${KERNEL_TARGET_NAME}_hw_emu) + if(NOT TARGET link_sw_emu) + add_custom_target(link_sw_emu COMMENT "Linking software emulation targets." + DEPENDS link_${PROGRAM_TARGET}_sw_emu) else() - add_dependencies(hw_emu ${KERNEL_TARGET_NAME}_hw_emu) + add_dependencies(link_sw_emu link_${PROGRAM_TARGET}_sw_emu) endif() - if(NOT TARGET compile_hw_emu) - add_custom_target(compile_hw_emu COMMENT "Compiling hardware emulation targets." - DEPENDS compile_${KERNEL_TARGET_NAME}_hw_emu) + if(NOT TARGET link_hw_emu) + add_custom_target(link_hw_emu COMMENT "Linking hardware emulation targets." + DEPENDS link_${PROGRAM_TARGET}_hw_emu) else() - add_dependencies(compile_hw_emu compile_${KERNEL_TARGET_NAME}_hw_emu) + add_dependencies(link_hw_emu link_${PROGRAM_TARGET}_hw_emu) endif() - if(NOT TARGET hw) - add_custom_target(hw COMMENT "Building hardware targets." - DEPENDS ${KERNEL_TARGET_NAME}_hw) + if(NOT TARGET link_hw) + add_custom_target(link_hw COMMENT "Linking hardware targets." + DEPENDS link_${PROGRAM_TARGET}_hw) else() - add_dependencies(hw ${KERNEL_TARGET_NAME}_hw) + add_dependencies(link_hw link_${PROGRAM_TARGET}_hw) endif() - if(NOT TARGET link_hw_emu) - add_custom_target(link_hw_emu COMMENT "Linking hardware emulation targets." - DEPENDS link_${KERNEL_TARGET_NAME}_hw_emu) + if(NOT TARGET sw_emu) + add_custom_target(sw_emu COMMENT "Building software emulation targets." + DEPENDS link_sw_emu) else() - add_dependencies(link_hw_emu link_${KERNEL_TARGET_NAME}_hw_emu) + add_dependencies(sw_emu ${PROGRAM_TARGET}_sw_emu) endif() - - if(KERNEL_PLATFORM_PART) - # Make separate synthesis target, which is faster to run than Vitis compile - if(KERNEL_CLOCK) - set(KERNEL_HLS_TCL_CLOCK "create_clock -period ${KERNEL_CLOCK}MHz -name default\n") - endif() - string(REPLACE ";" " " KERNEL_FILES_STRING "${KERNEL_FILES}") - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_synthesis.tcl - "\ -open_project ${KERNEL_TARGET_NAME} \ -open_solution ${KERNEL_PLATFORM_PART} \ -set_part ${KERNEL_PLATFORM_PART} \ -add_files -cflags \"${KERNEL_HLS_FLAGS}\" \"${KERNEL_FILES_STRING}\" \ -set_top ${KERNEL_NAME} \ -${KERNEL_HLS_TCL_CLOCK}\ -config_interface -m_axi_addr64 \ -config_compile -name_max_length 256 \ -csynth_design \ -exit") - add_custom_command(OUTPUT ${KERNEL_TARGET_NAME}/${KERNEL_PLATFORM_PART}/${KERNEL_PLATFORM_PART}.log - COMMENT "Running high-level synthesis for ${KERNEL_TARGET_NAME}." - COMMAND ${Vitis_HLS} -f ${CMAKE_CURRENT_BINARY_DIR}/${KERNEL_TARGET_NAME}_synthesis.tcl - DEPENDS ${KERNEL_DEPENDS}) - add_custom_target(synthesize_${KERNEL_TARGET_NAME} DEPENDS - ${KERNEL_TARGET_NAME}/${KERNEL_PLATFORM_PART}/${KERNEL_PLATFORM_PART}.log) + if(NOT TARGET hw_emu) + add_custom_target(hw_emu COMMENT "Building hardware emulation targets." + DEPENDS link_hw_emu) + else() + add_dependencies(hw_emu ${PROGRAM_TARGET}_hw_emu) + endif() + if(NOT TARGET hw) + add_custom_target(hw COMMENT "Building hardware targets." + DEPENDS link_hw) + else() + add_dependencies(hw ${PROGRAM_TARGET}_hw) endif() # Add Xilinx build directory to clean target diff --git a/include/hlslib/common/OpenCL.h b/include/hlslib/common/OpenCL.h index 8a9398e..ee62ee0 100644 --- a/include/hlslib/common/OpenCL.h +++ b/include/hlslib/common/OpenCL.h @@ -34,7 +34,7 @@ namespace hlslib { namespace ocl { //############################################################################# -// Enumerations +// Enumerations and types //############################################################################# /// Enum for type of memory access to device buffers. Will cause the @@ -47,6 +47,11 @@ enum class MemoryBank { unspecified, bank0, bank1, bank2, bank3 }; /// Enum for storage types on the FPGA enum class StorageType { DDR, HBM }; +/// Used to signal that an argument is a streaming connection between kernels +/// and should not be assigned from the host as an OpenCL argument +struct _Stream {}; +inline constexpr _Stream Stream; + //############################################################################# // OpenCL exceptions //############################################################################# @@ -1236,6 +1241,10 @@ class Kernel { } } + void SetKernelArguments(size_t index, _Stream const &) { + // Ignore argument, as this is set internally during linking + } + template void SetKernelArguments(size_t index, T &&arg) { auto errorCode = kernel_.setArg(index, arg); diff --git a/intel_test/CMakeLists.txt b/intel_test/CMakeLists.txt index ce15e37..3e2b110 100644 --- a/intel_test/CMakeLists.txt +++ b/intel_test/CMakeLists.txt @@ -6,7 +6,7 @@ include_directories(SYSTEM ${IntelFPGAOpenCL_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -DHLSLIB_INTEL") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -DHLSLIB_INTEL") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--disable-new-dtags") function(opencl_target KERNEL_NAME) @@ -48,4 +48,4 @@ add_custom_target(run_Jacobi2D_emulator_oldapi_copy COMMAND CL_CONTEXT_EMULATOR_ add_custom_target(run_Jacobi2D_hardware_newapi_copy COMMAND ${CMAKE_CURRENT_BINARY_DIR}/RunJacobi2D.exe hardware newapi_copy) add_custom_target(run_Jacobi2D_emulator_newapi_copy COMMAND CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=1 ${CMAKE_CURRENT_BINARY_DIR}/RunJacobi2D.exe emulator newapi_copy) add_custom_target(run_Jacobi2D_hardware_newapi_notransfer COMMAND ${CMAKE_CURRENT_BINARY_DIR}/RunJacobi2D.exe hardware newapi_notransfer) -add_custom_target(run_Jacobi2D_emulator_newapi_notransfer COMMAND CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=1 ${CMAKE_CURRENT_BINARY_DIR}/RunJacobi2D.exe emulator newapi_notransfer) \ No newline at end of file +add_custom_target(run_Jacobi2D_emulator_newapi_notransfer COMMAND CL_CONTEXT_EMULATOR_DEVICE_INTELFPGA=1 ${CMAKE_CURRENT_BINARY_DIR}/RunJacobi2D.exe emulator newapi_notransfer) diff --git a/xilinx_test/CMakeLists.txt b/xilinx_test/CMakeLists.txt index e3ba5ad..8139d6a 100644 --- a/xilinx_test/CMakeLists.txt +++ b/xilinx_test/CMakeLists.txt @@ -4,7 +4,7 @@ set(HLSLIB_DSA_NAME "xilinx_u250_gen3x16_xdma_3_1_202020_1" CACHE STRING "DSA st include_directories(${Vitis_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -DHLSLIB_XILINX") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -DHLSLIB_XILINX") if(((${Vitis_MAJOR_VERSION} LESS 2018) AND (${Vitis_MINOR_VERSION} LESS 3)) OR ${Vitis_MAJOR_VERSION} LESS 2017) message(STATUS "Targeting legacy SDx.") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHLSLIB_LEGACY_SDX=1") @@ -27,6 +27,60 @@ add_executable(TestHBMandBlockCopySimulation test/TestHBMandBlockCopySimulation. target_link_libraries(TestHBMandBlockCopySimulation ${Vitis_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} catch) add_test(TestHBMandBlockCopySimulation TestHBMandBlockCopySimulation) +function(add_hls_test KERNEL_NAME) + cmake_parse_arguments( + KERNEL + "" + "CONFIG" + "HLS_FLAGS" + ${ARGN}) + # Abuse add_vitis_kernel to generate synthesis target, even if not all of the + # tests would actually be valid OpenCL kernels + add_vitis_kernel(${KERNEL_NAME} + FILES kernels/${KERNEL_NAME}.cpp + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include + HLS_FLAGS "${KERNEL_HLS_FLAGS}") + add_vitis_program(${KERNEL_NAME} ${HLSLIB_DSA_NAME} + CONFIG "${KERNEL_CONFIG}") + add_test(NAME Test${KERNEL_NAME}Synthesis COMMAND ${CMAKE_COMMAND} + --build ${CMAKE_CURRENT_BINARY_DIR} --target synthesize_${KERNEL_NAME}) +endfunction() + +add_hls_test("MultiStageAdd") +add_hls_test("Reduce") +add_hls_test("AccumulateFloat" HLS_FLAGS "-DHLSLIB_COMPILE_ACCUMULATE_FLOAT") +add_hls_test("AccumulateInt" HLS_FLAGS "-DHLSLIB_COMPILE_ACCUMULATE_INT") +add_hls_test("ShiftRegister") +add_hls_test("StreamAPI") +add_hls_test("Subflow") +add_hls_test("HBMandBlockCopy" CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/configs/hbmkernel.cfg) + +# Test mapping of interfaces to DRAM banks +add_vitis_kernel(DDRMappingKernel + FILES kernels/DDRExplicit.cpp + KERNEL DDRExplicit + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include + PORT_MAPPING "ddr0:DDR[0]" "ddr1:DDR[1]") +add_vitis_program(DDRMapping ${HLSLIB_DSA_NAME} + KERNELS DDRMappingKernel) +add_test(NAME TestDDRMapping COMMAND ${CMAKE_COMMAND} + --build ${CMAKE_CURRENT_BINARY_DIR} --target DDRMapping_hw_emu) + +# Test linking multiple kernels +add_vitis_kernel(FirstKernel + FILES kernels/MultipleKernels.cpp + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include) +add_vitis_kernel(SecondKernel + FILES kernels/MultipleKernels.cpp + INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include) +add_vitis_program(MultipleKernels ${HLSLIB_DSA_NAME} + KERNELS FirstKernel SecondKernel + CONNECTIVITY "FirstKernel_1.stream:SecondKernel_1.stream") + # Test kernels in software find_package(Threads) if(Threads_FOUND) @@ -52,36 +106,13 @@ if(Threads_FOUND) add_executable(TestSubflow test/TestSubflow.cpp kernels/Subflow.cpp) target_link_libraries(TestSubflow ${CMAKE_THREAD_LIBS_INIT} catch) add_test(TestSubflow TestSubflow) + add_executable(TestMultipleKernels test/TestMultipleKernels.cpp) + add_dependencies(TestMultipleKernels MultipleKernels_hw_emu) + target_link_libraries(TestMultipleKernels ${Vitis_LIBRARIES} catch ${CMAKE_THREAD_LIBS_INIT}) + add_test(NAME TestMultipleKernels + COMMAND ${CMAKE_COMMAND} -E env + XCL_EMULATION_MODE="hw_emu" + ${CMAKE_CURRENT_BINARY_DIR}/TestMultipleKernels) else() message(WARNING "Threads not found. Disabling multi-PE kernel tests.") endif() - -function(add_hls_test KERNEL_NAME) - cmake_parse_arguments( - KERNEL - "" - "CONFIG" - "HLS_FLAGS;PORT_MAPPING" - ${ARGN}) - # Abuse add_vitis_kernel to generate synthesis target, even if not all of the - # tests would actually be valid OpenCL kernels - add_vitis_kernel(${KERNEL_NAME} ${HLSLIB_DSA_NAME} - FILES kernels/${KERNEL_NAME}.cpp - INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/include - HLS_FLAGS "${KERNEL_HLS_FLAGS}" - CONFIG "${KERNEL_CONFIG}" - PORT_MAPPING "${KERNEL_PORT_MAPPING}") - add_test(NAME Test${KERNEL_NAME}Synthesis COMMAND ${CMAKE_COMMAND} - --build ${CMAKE_CURRENT_BINARY_DIR} --target synthesize_${KERNEL_NAME}) -endfunction() - -add_hls_test("MultiStageAdd") -add_hls_test("Reduce") -add_hls_test("AccumulateFloat" HLS_FLAGS "-DHLSLIB_COMPILE_ACCUMULATE_FLOAT") -add_hls_test("AccumulateInt" HLS_FLAGS "-DHLSLIB_COMPILE_ACCUMULATE_INT") -add_hls_test("ShiftRegister") -add_hls_test("StreamAPI") -add_hls_test("Subflow") -add_hls_test("HBMandBlockCopy" CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/configs/hbmkernel.cfg) -add_hls_test("DDRExplicit" PORT_MAPPING "ddr0:DDR[0] ddr1:DDR[1]") diff --git a/xilinx_test/include/MultipleKernels.h b/xilinx_test/include/MultipleKernels.h new file mode 100644 index 0000000..7b4c6a4 --- /dev/null +++ b/xilinx_test/include/MultipleKernels.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "hlslib/xilinx/Stream.h" + +extern "C" void FirstKernel(uint64_t const *memory, + hlslib::Stream &stream, int n); + +extern "C" void SecondKernel(hlslib::Stream &stream, uint64_t *memory, + int n); diff --git a/xilinx_test/kernels/MultipleKernels.cpp b/xilinx_test/kernels/MultipleKernels.cpp new file mode 100644 index 0000000..4506022 --- /dev/null +++ b/xilinx_test/kernels/MultipleKernels.cpp @@ -0,0 +1,21 @@ +#include "MultipleKernels.h" + +#include "hlslib/xilinx/Stream.h" + +void FirstKernel(uint64_t const *memory, hlslib::Stream &stream, int n) { + #pragma HLS INTERFACE m_axi port=memory offset=slave bundle=gmem0 + #pragma HLS INTERFACE axis port=stream + for (int i = 0; i < n; ++i) { + #pragma HLS PIPELINE II=1 + stream.Push(memory[i] + 1); + } +} + +void SecondKernel(hlslib::Stream &stream, uint64_t *memory, int n) { + #pragma HLS INTERFACE axis port=stream + #pragma HLS INTERFACE m_axi port=memory offset=slave bundle=gmem1 + for (int i = 0; i < n; ++i) { + #pragma HLS PIPELINE II=1 + memory[i] = 2 * stream.Pop(); + } +} diff --git a/xilinx_test/test/TestMultipleKernels.cpp b/xilinx_test/test/TestMultipleKernels.cpp new file mode 100644 index 0000000..0dd05ee --- /dev/null +++ b/xilinx_test/test/TestMultipleKernels.cpp @@ -0,0 +1,26 @@ +#include + +#include "MultipleKernels.h" +#include "catch.hpp" +#include "hlslib/xilinx/OpenCL.h" + +TEST_CASE("MultipleKernels") { + setenv("XCL_EMULATION_MODE", "hw_emu", true); + using namespace hlslib::ocl; + Context context; + auto program = context.MakeProgram("MultipleKernels_hw_emu.xclbin"); + std::vector memory_host(128, 1); + auto memory = context.MakeBuffer( + memory_host.cbegin(), memory_host.cend()); + hlslib::Stream pipe; + auto first_kernel = program.MakeKernel("FirstKernel", memory, Stream, 128); + auto second_kernel = program.MakeKernel("SecondKernel", Stream, memory, 128); + auto first_future = first_kernel.ExecuteTaskAsync(); + auto second_future = second_kernel.ExecuteTaskAsync(); + first_future.wait(); + second_future.wait(); + memory.CopyToHost(memory_host.begin()); + for (int i = 0; i < 128; ++i) { + REQUIRE(memory_host[i] == 4); + } +}