From d9f80fc165df6a9bcaa2eeba5846452b45b4609c Mon Sep 17 00:00:00 2001 From: DrasLorus Date: Mon, 16 May 2022 19:02:23 +0200 Subject: [PATCH] Enable support of GNU v4.6 and (consequently) of Vivado HLS 2019.1 - Also disable TCL unit COSIM test. Can be enable by turning 'true' to false in the CMakeLists, - v4.6 doesn't support C++14, nor some C++11, so some hacky ways have been found. Unit test has been updated but still works and coverage is good (94%, the 6% beeing exeption handling, unrelevant in HLS). --- CMakeLists.txt | 125 ++++++++++++------- hls_files/cordicabs_16_4_6/script_16_4_6.tcl | 109 +++++++--------- sources/CCordicAbs/CCordicAbs.hpp | 34 +++-- sources/top_level/top_level_cordic.cpp | 54 +------- sources/top_level/top_level_cordic_tb.cpp | 20 +-- 5 files changed, 162 insertions(+), 180 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19f29f0..147ddbd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,15 +25,23 @@ project ( ) if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2)) - message ( - FATAL_ERROR - "This project require a GNU compiler version greater than 6.2." - "\nNote: If you tried to use g++-4.6.3 as provided by Xilinx Vivado v2019.1, use directly the TCL script " - "in the folder `hls_files/cordicabs_16_4_6`." - ) + # message ( FATAL_ERROR "This project require a GNU compiler version greater than 6.2." "\nNote: + # If you tried to use g++-4.6.3 as provided by Xilinx Vivado v2019.1, use directly the TCL script + # " "in the folder `hls_files/cordicabs_16_4_6`." ) + set (IS_GNU_LEGACY ON) +else () + set (IS_GNU_LEGACY OFF) endif () -set (CMAKE_CXX_STANDARD 14) +if (IS_GNU_LEGACY) + set (CMAKE_CXX_STANDARD 11) + message ( + WARNING + "An old GNU compiler is currently used, Catch2, C++14 and advanced C++11 features are disabled." + ) +else () + set (CMAKE_CXX_STANDARD 14) +endif () set (CMAKE_CXX_STANDARD_REQUIRED ON) set (CMAKE_CXX_EXTENSIONS OFF) @@ -63,6 +71,7 @@ option (ENABLE_SOFTWARE ) option (PEDANTIC "use -Wall and -pedantic." ON) +option (ENABLE_GCOV "use GCOV and LCOV to assess code coverage." OFF) # ################################################################################################## @@ -70,6 +79,16 @@ if (PEDANTIC) add_compile_options (-Wall -pedantic) endif () +if (ENABLE_GCOV) + add_compile_options ( + -O0 + -fprofile-arcs + -ftest-coverage + -fprofile-abs-path + ) + add_link_options (-lgcov --coverage) +endif () + if (EXPORT_COMMANDS) set (CMAKE_EXPORT_COMPILE_COMMANDS ON) endif () @@ -82,7 +101,7 @@ if (ENABLE_XILINX) ) set ( XILINX_VER - "2020.2" + "2019.1" CACHE STRING "Xilinx software version to use." ) @@ -96,9 +115,15 @@ if (ENABLE_XILINX) XILINX_HLS vitis_hls HINTS ${XILINX_HOME}/Vitis_HLS/${XILINX_VER}/bin NO_CACHE REQUIRED ) else () - message ( - FATAL_ERROR - "Xilinx versions less than 2020.1 are not supported due to the outdated compiler version in use" + # message ( FATAL_ERROR "Xilinx versions less than 2020.1 are not supported due to the outdated + # compiler version in use" ) + set ( + AP_INCLUDE_DIR + ${XILINX_HOME}/Vivado/${XILINX_VER}/include + CACHE INTERNAL "Path to Xilinx includes" FORCE + ) + find_program ( + XILINX_HLS vivado_hls HINTS ${XILINX_HOME}/Vivado/${XILINX_VER}/bin NO_CACHE REQUIRED ) endif () message (STATUS "AP headers must lie under ${AP_INCLUDE_DIR}") @@ -144,24 +169,30 @@ if (ENABLE_XILINX) endif () if (ENABLE_TESTING) - find_package (Catch2 REQUIRED) - - add_library (catch_common_${PROJECT_NAME} OBJECT sources/tb/main_catch2.cpp) - target_link_libraries (catch_common_${PROJECT_NAME} PUBLIC Catch2::Catch2) - - add_executable (cordicabs_tb sources/tb/cordicabs_tb.cpp) - target_link_libraries (cordicabs_tb catch_common_${PROJECT_NAME} cordicabs) - include (CTest) - include (Catch) - catch_discover_tests (cordicabs_tb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data) + + if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.19.0) + cmake_policy (SET CMP0110 NEW) + endif () + + if (NOT IS_GNU_LEGACY) + find_package (Catch2 REQUIRED) + + add_library (catch_common_${PROJECT_NAME} OBJECT sources/tb/main_catch2.cpp) + target_link_libraries (catch_common_${PROJECT_NAME} PUBLIC Catch2::Catch2) + + add_executable (cordicabs_tb sources/tb/cordicabs_tb.cpp) + target_link_libraries (cordicabs_tb catch_common_${PROJECT_NAME} cordicabs) + + include (Catch) + catch_discover_tests (cordicabs_tb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data) + endif () if (ENABLE_XILINX) find_file (INPUT_DAT_TB input.dat HINTS data NO_CACHE REQUIRED) if (CMAKE_VERSION VERSION_LESS 3.19.0) set (TEST_NAME "\"Xilinx C-Simulation Testbench CordicAbs\"") else () - cmake_policy (SET CMP0110 NEW) set (TEST_NAME "Xilinx C-Simulation Testbench CordicAbs") endif () add_test (NAME ${TEST_NAME} COMMAND cordicabs_xilinx_tb ${INPUT_DAT_TB}) @@ -169,7 +200,7 @@ if (ENABLE_TESTING) if (XILINX_COSIM) set ( XILINX_TESTLINES - "1000" + "10000" CACHE STRING "Number of co-simulation passes" ) @@ -179,38 +210,40 @@ if (ENABLE_TESTING) "1" CACHE INTERNAL "EXPORT_IP" ) - if (XILINX_IMPL) - set ( - RUN_IMPL - "1" - CACHE INTERNAL "RUN_IMPL" - ) - endif () + else () + unset (EXPORT_IP CACHE) + endif () + if (XILINX_IMPL) + set ( + RUN_IMPL + "1" + CACHE INTERNAL "RUN_IMPL" + ) + else () + unset (RUN_IMPL CACHE) endif () add_custom_target ( run_hls - COMMAND ${XILINX_HLS} ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6/script_16_4_6.tcl - ${XILINX_TESTLINES} ${EXPORT_IP} ${RUN_IMPL} - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6 + COMMAND + ${XILINX_HLS} ${CMAKE_CURRENT_SOURCE_DIR}/hls_files/cordicabs_16_4_6/script_16_4_6.tcl + ${XILINX_TESTLINES} ${EXPORT_IP} ${RUN_IMPL} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/hls_files/cordicabs_16_4_6 USES_TERMINAL ) if (CMAKE_VERSION VERSION_LESS 3.19.0) - add_test ( - NAME "\"Xilinx HLS TCL Flow\"" - COMMAND ${XILINX_HLS} ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6/script_16_4_6.tcl - ${XILINX_TESTLINES} 0 0 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6 - ) + set (TEST_NAME "\"Xilinx HLS TCL Flow\"") else () - cmake_policy (SET CMP0110 NEW) - add_test ( - NAME "Xilinx HLS TCL Flow" - COMMAND ${XILINX_HLS} ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6/script_16_4_6.tcl - ${XILINX_TESTLINES} 0 0 - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/hls_files/cordicabs_16_4_6 - ) + set (TEST_NAME "Xilinx HLS TCL Flow") endif () + add_test ( + ${TEST_NAME} + COMMAND + ${XILINX_HLS} ${CMAKE_CURRENT_SOURCE_DIR}/hls_files/cordicabs_16_4_6/script_16_4_6.tcl + ${XILINX_TESTLINES} 0 0 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/hls_files/cordicabs_16_4_6 + ) + set_property (TEST ${TEST_NAME} PROPERTY DISABLED TRUE) endif () endif () endif () diff --git a/hls_files/cordicabs_16_4_6/script_16_4_6.tcl b/hls_files/cordicabs_16_4_6/script_16_4_6.tcl index c54adbc..eb4bcba 100644 --- a/hls_files/cordicabs_16_4_6/script_16_4_6.tcl +++ b/hls_files/cordicabs_16_4_6/script_16_4_6.tcl @@ -16,7 +16,7 @@ if { [expr {$argc > 0}] } { if { [expr {$argc > 1}] } { set EXPORT_IP [lindex $argv 1] } else { - set EXPORT_IP 1 + set EXPORT_IP 0 } if { [expr {$argc > 2}] && $EXPORT_IP } { @@ -30,7 +30,7 @@ set XILINX_MAJOR [expr {int($VERSION)}] if {[expr {$VERSION < 2020.1}]} { set CFLAGS "-std=c++0x -DXILINX_MAJOR=${XILINX_MAJOR} -Wno-unknown-pragmas -Wno-unused-label -Wall -DNDEBUG -I${ROOT_DIR}/sources" } else { - set CFLAGS "-std=c++14 -Wno-unknown-pragmas -Wno-unused-label -Wall -DNDEBUG -I${ROOT_DIR}/sources" + set CFLAGS "-std=c++14 -DXILINX_MAJOR=${XILINX_MAJOR} -Wno-unknown-pragmas -Wno-unused-label -Wall -DNDEBUG -I${ROOT_DIR}/sources" } set PROJECT_NAME "cordicabs_16_4_6" @@ -43,75 +43,56 @@ add_files -cflags "$CFLAGS" "${ROOT_DIR}/sources/hls_abs/hls_abs.hpp" add_files -cflags "$CFLAGS" "${ROOT_DIR}/sources/top_level/top_level_cordic.cpp" add_files -cflags "$CFLAGS" -tb "${ROOT_DIR}/sources/top_level/top_level_cordic_tb.cpp" -if {[expr {$VERSION < 2020.1}]} { - open_solution -reset "solution_spartan7" -} else { - open_solution -reset "solution_spartan7" -flow_target vivado -} -set_part {xc7s100-fgga484-1} -create_clock -period 10 -name default -set_clock_uncertainty 2 -set_directive_pipeline cordic_abs_16_4_6 -set_directive_interface cordic_abs_16_4_6 -mode ap_ctrl_none -csim_design -argv "${ROOT_DIR}/data/input.dat" -clean -O -csynth_design -cosim_design -O -argv "${ROOT_DIR}/data/input.dat ${NLINES}" -if { $EXPORT_IP } { - config_export -format ip_catalog \ - -display_name "${PROJECT_NAME}" \ - -ipname "${PROJECT_NAME}_spartan7" \ - -version "0.1.${XILINX_MAJOR}" - if { [expr {$VERSION >= 2020.1}] } { - config_export -output "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_spartan7" +set targets [ dict create spartan7 {xc7s100-fgga484-1} genesys2 {xc7k325tffg900-2} ] + +foreach target { spartan7 genesys2 } { + if {[expr {$VERSION < 2020.1}]} { + open_solution -reset "solution_${target}" + } else { + open_solution -reset "solution_${target}" -flow_target vivado } + set_part [ dict get $targets $target ] + create_clock -period 10 -name default + set_clock_uncertainty 2 + set_directive_pipeline cordic_abs_16_4_6 + set_directive_interface cordic_abs_16_4_6 -mode ap_ctrl_none + + csim_design -argv "${ROOT_DIR}/data/input.dat" -clean -O + csynth_design + cosim_design -O -argv "${ROOT_DIR}/data/input.dat ${NLINES}" + + config_export -format ip_catalog \ + -display_name "${PROJECT_NAME}" \ + -ipname "${PROJECT_NAME}_${target}" \ + -version "0.1.${XILINX_MAJOR}" \ + -vivado_optimization_level 2 \ + -vivado_phys_opt route + + if { $EXPORT_IP } { + if [ expr {! [ file isdirectory "${ROOT_DIR}/ip" ] } ] { + if { [ file exists "${ROOT_DIR}/ip" ] } { + file remove "${ROOT_DIR}/ip" + } + file mkdir "${ROOT_DIR}/ip" + } + + if { [expr {$VERSION >= 2020.1}] } { + export_design -output "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_${target}" + } else { + export_design + set IP_FILE [glob -directory "${SCRIPT_DIR}/${PROJECT_NAME}/solution_${target}/impl/ip" *.zip] + file copy -force "${IP_FILE}" "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_${target}.zip" + } + } + if { $RUN_IMPL } { export_design -flow impl - } else { - export_design - } - if { [expr {$VERSION < 2020.1}] } { - set IP_FILE [glob -directory "${SCRIPT_DIR}/${PROJECT_NAME}/solution_spartan7/impl/ip" *.zip] - file copy -force "${IP_FILE}" "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_spartan7.zip" } + + close_solution } -close_solution - -if {[expr {$VERSION < 2020.1}]} { - open_solution -reset "solution_genesys2" -} else { - open_solution -reset "solution_genesys2" -flow_target vivado -} -set_part {xc7k325tffg900-2} -create_clock -period 10 -name default -set_clock_uncertainty 2 -set_directive_pipeline cordic_abs_16_4_6 -set_directive_interface cordic_abs_16_4_6 -mode ap_ctrl_none -csim_design -argv "${ROOT_DIR}/data/input.dat" -clean -O -csynth_design -cosim_design -O -argv "${ROOT_DIR}/data/input.dat ${NLINES}" - -if { $EXPORT_IP } { - config_export -format ip_catalog \ - -display_name "${PROJECT_NAME}" \ - -ipname "${PROJECT_NAME}_genesys2" \ - -version "0.1.${XILINX_MAJOR}" - if { [expr {$VERSION >= 2020.1}] } { - config_export -output "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_genesys2" - } - if { $RUN_IMPL } { - export_design -flow impl - } else { - export_design - } - if { [expr {$VERSION < 2020.1}] } { - set IP_FILE [glob -directory "${SCRIPT_DIR}/${PROJECT_NAME}/solution_genesys2/impl/ip" *.zip] - file copy -force "${IP_FILE}" "${ROOT_DIR}/hls_files/cordicabs_16_4_6/ip/${XILINX_MAJOR}_genesys2.zip" - } -} - -close_solution close_project exit 0 diff --git a/sources/CCordicAbs/CCordicAbs.hpp b/sources/CCordicAbs/CCordicAbs.hpp index fe415dc..c3bbf70 100644 --- a/sources/CCordicAbs/CCordicAbs.hpp +++ b/sources/CCordicAbs/CCordicAbs.hpp @@ -37,20 +37,36 @@ #define cst_abs(x) (x > 0 ? x : -x) +#if __cplusplus < 201402L && XILINX_MAJOR <= 2019 +#ifndef kn_values +const double kn_values_define[7] = { + 0.70710678118655, 0.632455532033680, 0.613571991077900, + 0.608833912517750, 0.607648256256170, 0.607351770141300, 0.607277644093530}; +#define kn_values kn_values_define +#define kn_i unsigned(kn_values[Tnb_stages - 1] * double(1U << 4)) +#endif + +#define OWN_CONSTEXPR +#else +#define OWN_CONSTEXPR constexpr +#endif + template class CCordicAbs { public: +#if __cplusplus >= 201402L || XILINX_MAJOR > 2019 static constexpr double kn_values[7] = { 0.70710678118655, 0.632455532033680, 0.613571991077900, 0.608833912517750, 0.607648256256170, 0.607351770141300, 0.607277644093530}; - static constexpr const unsigned In_W = TIn_W; - static constexpr const unsigned In_I = TIn_I; - static constexpr const unsigned Out_W = In_W + 2; - static constexpr const unsigned Out_I = In_I + 2; - static constexpr const unsigned nb_stages = Tnb_stages; + static constexpr unsigned kn_i = unsigned(kn_values[Tnb_stages - 1] * double(1U << 4)); // 4 bits are enough +#endif + static constexpr unsigned In_W = TIn_W; + static constexpr unsigned In_I = TIn_I; + static constexpr unsigned Out_W = In_W + 2; + static constexpr unsigned Out_I = In_I + 2; + static constexpr unsigned nb_stages = Tnb_stages; - static constexpr unsigned kn_i = unsigned(kn_values[nb_stages - 1] * double(1U << 4)); // 4 bits are enough static constexpr unsigned in_scale_factor = unsigned(1U << (In_W - In_I)); static constexpr unsigned out_scale_factor = unsigned(1U << (Out_W - Out_I)); @@ -66,8 +82,7 @@ public: return ap_uint(ap_uint(in * ap_uint<4>(kn_i)) >> 4U); } -#if !defined(XILINX_MAJOR) || XILINX_MAJOR >= 2020 - static constexpr ap_uint process(ap_int re_in, ap_int im_in) { + static OWN_CONSTEXPR ap_uint process(ap_int re_in, ap_int im_in) { ap_int A[nb_stages + 1]; ap_int B[nb_stages + 1]; @@ -93,9 +108,8 @@ public: return ap_uint(A[nb_stages]); } -#endif -#if !defined(__SYNTHESIS__) && defined(SOFTWARE) +#if !defined(__SYNTHESIS__) && defined(SOFTWARE) && __cplusplus >= 201402L static constexpr uint64_t process(int64_t re_in, int64_t im_in) { const int64_t re_x = re_in; diff --git a/sources/top_level/top_level_cordic.cpp b/sources/top_level/top_level_cordic.cpp index f4cbae7..1504856 100644 --- a/sources/top_level/top_level_cordic.cpp +++ b/sources/top_level/top_level_cordic.cpp @@ -19,67 +19,17 @@ #include "top_level_cordic.hpp" -#if !defined(XILINX_MAJOR) || XILINX_MAJOR >= 2020 - void cordic_abs_16_4_6( ap_int re_in, ap_int im_in, ap_uint & module_out) { - static constexpr const cordic_abs_t cordic_abs {}; + // static constexpr const cordic_abs_t cordic_abs {}; const ap_int x = re_in; const ap_int y = im_in; - const ap_uint m = cordic_abs.process(x, y); + const ap_uint m = cordic_abs_t::process(x, y); module_out = m; } - -#else - -constexpr unsigned In_W = cordic_abs_t::In_W; -constexpr unsigned Out_W = cordic_abs_t::Out_W; -constexpr unsigned nb_stages = cordic_abs_t::nb_stages; - -static ap_uint process(ap_int re_in, ap_int im_in) { - ap_int A[nb_stages + 1]; - ap_int B[nb_stages + 1]; - - A[0] = hls_abs::abs(re_in); - B[0] = hls_abs::abs(im_in); - - for (uint16_t u = 0; u < nb_stages; u++) { - const bool sign_B = B[u] > 0; - - const ap_int step_A = B[u] >> u; - const ap_int step_B = A[u] >> u; - - const ap_int tmp_B = sign_B - ? ap_int(B[u] - step_B) - : ap_int(B[u] + step_B); - const ap_int tmp_A = sign_B - ? ap_int(A[u] + step_A) - : ap_int(A[u] - step_A); - - A[u + 1] = tmp_A; - B[u + 1] = tmp_B; - } - - return ap_uint(A[nb_stages]); -} - -void cordic_abs_16_4_6( - ap_int re_in, - ap_int im_in, - ap_uint & module_out) { - - const ap_int x = re_in; - const ap_int y = im_in; - - const ap_uint m = process(x, y); - - module_out = m; -} - -#endif diff --git a/sources/top_level/top_level_cordic_tb.cpp b/sources/top_level/top_level_cordic_tb.cpp index 5b13a1e..01493e5 100644 --- a/sources/top_level/top_level_cordic_tb.cpp +++ b/sources/top_level/top_level_cordic_tb.cpp @@ -44,10 +44,10 @@ int main(int argc, char ** argv) { input_fn = string(argv[1]); } - constexpr unsigned max_lines = 10000; + constexpr unsigned max_lines = 100000; unsigned n_lines = max_lines; if (argc > 2) { - n_lines = unsigned(std::stoi(string(argv[2]))); + n_lines = std::min(unsigned(std::stoi(string(argv[2]))), max_lines); } ap_int re_values_in[max_lines]; @@ -57,6 +57,10 @@ int main(int argc, char ** argv) { double results[max_lines]; FILE * INPUT = fopen(input_fn.c_str(), "r"); + if (!bool(INPUT)) { + perror("Can't open input file"); + exit(EXIT_FAILURE); + } // Init test vector for (unsigned i = 0; i < n_lines; i++) { @@ -73,7 +77,7 @@ int main(int argc, char ** argv) { fclose(INPUT); // Save the results to a file - // ofstream outfile("results.dat"); + ofstream outfile("results.dat"); constexpr double abs_margin = double(1 << (cordic_abs_t::Out_I - 1)) * 2. / 100.; @@ -89,10 +93,10 @@ int main(int argc, char ** argv) { // cout << "Series " << iter; // cout << " Outcome: "; - // outfile << re_values_in[iter].to_double() / cordic_abs_t::in_scale_factor << " " - // << im_values_in[iter].to_double() / cordic_abs_t::in_scale_factor << " " - // << cordic_abs_t::scale_cordic(values_out[iter].to_double()) / cordic_abs_t::out_scale_factor << " " - // << results[iter] << std::endl; + outfile << re_values_in[iter].to_double() / cordic_abs_t::in_scale_factor << " " + << im_values_in[iter].to_double() / cordic_abs_t::in_scale_factor << " " + << cordic_abs_t::scale_cordic(values_out[iter].to_double()) / cordic_abs_t::out_scale_factor << " " + << results[iter] << std::endl; const double dbl_res = cordic_abs_t::scale_cordic(values_out[iter].to_double()) / cordic_abs_t::out_scale_factor; @@ -105,7 +109,7 @@ int main(int argc, char ** argv) { } } } - // outfile.close(); + outfile.close(); printf("RESULT: %s!\n", counted_errors == 0 ? "SUCCESS" : "FAILURE");