From c68e3e937212ca3111bb0ce2ee4e635f9abdf83d Mon Sep 17 00:00:00 2001 From: DrasLorus Date: Fri, 15 Apr 2022 18:59:42 +0200 Subject: [PATCH] Finalize a top level IP - Must be tested on hardware, cosimulation testbench is buggy. --- .gitignore | 5 +- CMakeLists.txt | 16 ++- hls_files/cordicabs_16_4_6/script_16_4_6.tcl | 22 ++++ sources/CCordicAbs/CCordicAbs.hpp | 66 ++++++----- sources/tb/cordicabs_tb.cpp | 71 ++++++++++-- sources/top_level/top_level_cordic.cpp | 37 +++++++ sources/top_level/top_level_cordic.hpp | 27 +++++ sources/top_level/top_level_cordic_tb.cpp | 111 +++++++++++++++++++ 8 files changed, 314 insertions(+), 41 deletions(-) create mode 100644 hls_files/cordicabs_16_4_6/script_16_4_6.tcl create mode 100644 sources/top_level/top_level_cordic.cpp create mode 100644 sources/top_level/top_level_cordic.hpp create mode 100644 sources/top_level/top_level_cordic_tb.cpp diff --git a/.gitignore b/.gitignore index a2e344a..5d89477 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,7 @@ compile_commands.json RomGenerators/sources/main_generator_??*_*.cpp sources/CCordicRotateRom/CCordicRotateRom_*.?pp sources/CordicRoms/cordic_rom_*.?pp -sources/tb/cordic_rom_tb_??*_*.?pp \ No newline at end of file +sources/tb/cordic_rom_tb_??*_*.?pp + +hls_files/cordicabs_16_4_6/* +!hls_files/cordicabs_16_4_6/*.tcl diff --git a/CMakeLists.txt b/CMakeLists.txt index 28daba3..ba0cde0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,12 +101,19 @@ if (ENABLE_SOFTWARE) add_compile_definitions (SOFTWARE=1) endif () - # ################################################################################################## -add_library(cordicabs sources/CCordicAbs/CCordicAbs.cpp) -target_include_directories(cordicabs PUBLIC sources) -target_include_directories(cordicabs SYSTEM PUBLIC ${AP_INCLUDE_DIR}) +add_library (cordicabs sources/CCordicAbs/CCordicAbs.cpp) +target_include_directories (cordicabs PUBLIC sources) +target_include_directories (cordicabs SYSTEM PUBLIC ${AP_INCLUDE_DIR}) + +if (ENABLE_XILINX) + add_executable ( + cordicabs_xilinx_tb sources/top_level/top_level_cordic_tb.cpp + sources/top_level/top_level_cordic.cpp + ) + target_link_libraries (cordicabs_xilinx_tb cordicabs) +endif () if (ENABLE_TESTING) find_package (Catch2 REQUIRED) @@ -121,4 +128,3 @@ if (ENABLE_TESTING) include (Catch) catch_discover_tests (cordicabs_tb) 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 new file mode 100644 index 0000000..3cd1ce0 --- /dev/null +++ b/hls_files/cordicabs_16_4_6/script_16_4_6.tcl @@ -0,0 +1,22 @@ +set SCRIPT_DIR [file normalize [file dirname [info script]]] +set ROOT_DIR "${SCRIPT_DIR}/../.." + +set CFLAGS "-std=c++14 -Wno-unknown-pragmas -Wno-unused-label -Wall -DNDEBUG -I${ROOT_DIR}/sources" + +open_project -reset cordicabs_16_4_6 +set_top cordic_abs_16_4_6 +add_files -cflags "$CFLAGS" "${ROOT_DIR}/sources/CCordicAbs/CCordicAbs.cpp" +add_files -cflags "$CFLAGS" "${ROOT_DIR}/sources/CCordicAbs/CCordicAbs.hpp" +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" + +open_solution "solution_spartan7" -flow_target vivado +set_part {xc7s100fgga484-1} +create_clock -period 10 -name default +set_clock_uncertainty 2 +#source "./cordicabs_16_4_6/solution/directives.tcl" +csim_design -argv "${ROOT_DIR}/data/input.dat" -clean -O +csynth_design +# cosim_design -O -argv "${ROOT_DIR}/data/input.dat" +export_design -format ip_catalog diff --git a/sources/CCordicAbs/CCordicAbs.hpp b/sources/CCordicAbs/CCordicAbs.hpp index 0aa0b90..e24b072 100644 --- a/sources/CCordicAbs/CCordicAbs.hpp +++ b/sources/CCordicAbs/CCordicAbs.hpp @@ -21,11 +21,14 @@ #define C_CORDIC_ABS_HPP #include -#include #include +#include #include +#if !defined(__SYNTHESIS__) && defined(SOFTWARE) +#include #include +#endif #include #include @@ -55,7 +58,39 @@ public: return in * kn_i / 8U; } - static constexpr int64_t process(int64_t re_in, int64_t im_in) { + static constexpr double scale_cordic(double in) { + return in * kn_values[nb_stages - 1]; + } + + static constexpr 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]); + } + +#if !defined(__SYNTHESIS__) && defined(SOFTWARE) + static constexpr uint64_t process(int64_t re_in, int64_t im_in) { const int64_t re_x = re_in; const int64_t im_x = im_in; @@ -77,28 +112,7 @@ public: return A; } - static constexpr ap_int process(ap_int re_in, ap_int im_in) { - - ap_int A = hls_abs::abs(re_in); - ap_int B = hls_abs::abs(im_in); - - for (uint16_t u = 1; u < nb_stages + 1; u++) { - - const bool sign_B = B > 0; - - const int64_t step_A = (+B) >> (u - 1); - const int64_t step_B = (-A) >> (u - 1); - - B = sign_B ? B + step_B : B - step_B; - A = sign_B ? A + step_A : A - step_A; - } - - return A; - } - -#if !defined(__SYNTHESIS__) && defined(SOFTWARE) - static constexpr int64_t - process(const std::complex & x_in) { + static constexpr uint64_t process(const std::complex & x_in) { const int64_t re_x = x_in.real(); const int64_t im_x = x_in.imag(); @@ -120,10 +134,6 @@ public: return A; } - static constexpr double scale_cordic(double in) { - return in * kn_values[nb_stages - 1]; - } - static constexpr double process(std::complex x_in) { const std::complex fx_x_in(int64_t(x_in.real() * double(in_scale_factor)), int64_t(x_in.imag() * double(in_scale_factor))); diff --git a/sources/tb/cordicabs_tb.cpp b/sources/tb/cordicabs_tb.cpp index 7664e4b..58e84c2 100644 --- a/sources/tb/cordicabs_tb.cpp +++ b/sources/tb/cordicabs_tb.cpp @@ -18,9 +18,13 @@ */ #include "CCordicAbs/CCordicAbs.hpp" +#include #include #include +#include +#include + #include using namespace std; @@ -29,7 +33,7 @@ using Catch::Matchers::Floating::WithinAbsMatcher; #if defined(SOFTWARE) TEST_CASE("Constexpr CordicAbs works with C-Types", "[CORDICABS]") { - SECTION("W:16 - I:4 - Stages:6") { + SECTION("W:16 - I:4 - Stages:6 - double") { typedef CCordicAbs<16, 4, 6> cordic_abs; string input_fn = "../data/input.dat"; // _8_14_4_17_5_19_7_12 @@ -79,6 +83,59 @@ TEST_CASE("Constexpr CordicAbs works with C-Types", "[CORDICABS]") { } // outfile.close(); } + + SECTION("W:16 - I:4 - Stages:6 - int64") { + typedef CCordicAbs<16, 4, 6> cordic_abs; + + string input_fn = "../data/input.dat"; // _8_14_4_17_5_19_7_12 + string output_fn = "../data/output.dat"; // _8_14_4_17_5_19_7_12 + + constexpr unsigned n_lines = 100000; + + complex values_in[n_lines]; + int64_t values_out[n_lines]; + + double results[n_lines]; + + FILE * INPUT = fopen(input_fn.c_str(), "r"); + + // Init test vector + for (unsigned i = 0; i < n_lines; i++) { + double a, b, r; + fscanf(INPUT, "%lf,%lf,%lf\n", &a, &b, &r); + + const complex c {a, b}; + const complex ic {(int64_t) floor(c.real() * cordic_abs::in_scale_factor), + (int64_t) floor(c.imag() * cordic_abs::in_scale_factor)}; + values_in[i] = ic; + + const double ac = std::abs(c); + results[i] = ac; + } + + fclose(INPUT); + + // Save the results to a file + // ofstream outfile("results.dat"); + + constexpr double abs_margin = double(1 << (cordic_abs::Out_I - 1)) * 2. / 100.; + + // Executing the encoder + for (unsigned iter = 0; iter < n_lines; iter++) { + // Execute + + values_out[iter] = cordic_abs::process(values_in[iter]); + + // Display the results + // cout << "Series " << iter; + // cout << " Outcome: "; + + // outfile << values_in[iter].real() << " " << values_in[iter].imag() << " " << values_out[iter] << " " << results[iter] << endl; + + REQUIRE_THAT(cordic_abs::scale_cordic(double(values_out[iter])) / cordic_abs::out_scale_factor , WithinAbsMatcher(results[iter], abs_margin)); + } + // outfile.close(); + } } #endif @@ -114,7 +171,7 @@ TEST_CASE("Constexpr CordicAbs works with AP-Types", "[CORDICABS]") { fclose(INPUT); // Save the results to a file - ofstream outfile("results.dat"); + // ofstream outfile("results.dat"); constexpr double abs_margin = double(1 << (cordic_abs::Out_I - 1)) * 2. / 100.; @@ -128,16 +185,16 @@ TEST_CASE("Constexpr CordicAbs works with AP-Types", "[CORDICABS]") { // cout << "Series " << iter; // cout << " Outcome: "; - outfile << re_values_in[iter].to_double() / cordic_abs::in_scale_factor << " " - << im_values_in[iter].to_double() / cordic_abs::in_scale_factor << " " - << cordic_abs::scale_cordic(values_out[iter].to_double()) / cordic_abs::out_scale_factor << " " - << results[iter] << endl; + // outfile << re_values_in[iter].to_double() / cordic_abs::in_scale_factor << " " + // << im_values_in[iter].to_double() / cordic_abs::in_scale_factor << " " + // << cordic_abs::scale_cordic(values_out[iter].to_double()) / cordic_abs::out_scale_factor << " " + // << results[iter] << endl; const double dbl_res = cordic_abs::scale_cordic(values_out[iter].to_double()) / cordic_abs::out_scale_factor; REQUIRE_THAT(dbl_res, WithinAbsMatcher(results[iter], abs_margin)); } - outfile.close(); + // outfile.close(); // Compare the results file with the golden results // int retval = 0; diff --git a/sources/top_level/top_level_cordic.cpp b/sources/top_level/top_level_cordic.cpp new file mode 100644 index 0000000..c6bdd2e --- /dev/null +++ b/sources/top_level/top_level_cordic.cpp @@ -0,0 +1,37 @@ +/* + * + * Copyright 2022 Camille "DrasLorus" Monière. + * + * This file is part of CORDIC_Abs_APFX. + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this program. + * If not, see . + * + */ + +#include "top_level_cordic.hpp" + +#include "hls_stream.h" + +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 {}; + + const ap_int x = re_in; + const ap_int y = im_in; + + const ap_uint m = cordic_abs.process(x, y); + + module_out = m; +} diff --git a/sources/top_level/top_level_cordic.hpp b/sources/top_level/top_level_cordic.hpp new file mode 100644 index 0000000..e18666e --- /dev/null +++ b/sources/top_level/top_level_cordic.hpp @@ -0,0 +1,27 @@ +/* + * + * Copyright 2022 Camille "DrasLorus" Monière. + * + * This file is part of CORDIC_Abs_APFX. + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this program. + * If not, see . + * + */ + +#include "CCordicAbs/CCordicAbs.hpp" + +typedef CCordicAbs<16, 4, 6> cordic_abs_t; + +void cordic_abs_16_4_6( + ap_int re_in, + ap_int im_in, + ap_uint & module_out); \ No newline at end of file diff --git a/sources/top_level/top_level_cordic_tb.cpp b/sources/top_level/top_level_cordic_tb.cpp new file mode 100644 index 0000000..e4ecb77 --- /dev/null +++ b/sources/top_level/top_level_cordic_tb.cpp @@ -0,0 +1,111 @@ + +/* + * + * Copyright 2022 Camille "DrasLorus" Monière. + * + * This file is part of CORDIC_Abs_APFX. + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU + * Lesser General Public License as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without + * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with this program. + * If not, see . + * + */ + +#include "top_level_cordic.hpp" + +#include +#include +#include +#include + +using std::complex; +using std::ofstream; +using std::string; + +int main(int argc, char ** argv) { + const string testname = "Constexpr CordicAbs works with AP-Types and Xilinx synthesis"; + const string testsection = "W:16 - I:4 - Stages:6"; + + printf("# TEST: %s\n", testname.c_str()); + printf("# SETTINGS: %s\n", testsection.c_str()); + printf("## Compile date: %s\n", __DATE__); + printf("## time: %s\n", __TIME__); + + string input_fn = "../data/input.dat"; // _8_14_4_17_5_19_7_12 + + if (argc == 2) { + input_fn = string(argv[1]); + } + + constexpr unsigned n_lines = 100; + + ap_int re_values_in[n_lines]; + ap_int im_values_in[n_lines]; + ap_uint values_out[n_lines]; + + double results[n_lines]; + + FILE * INPUT = fopen(input_fn.c_str(), "r"); + + // Init test vector + for (unsigned i = 0; i < n_lines; i++) { + double a, b, r; + fscanf(INPUT, "%lf,%lf,%lf\n", &a, &b, &r); + + re_values_in[i] = int64_t(floor(a * cordic_abs_t::in_scale_factor)); + im_values_in[i] = int64_t(floor(b * cordic_abs_t::in_scale_factor)); + + const double ac = std::abs(complex {a, b}); + results[i] = ac; + } + + fclose(INPUT); + + // Save the results to a file + // ofstream outfile("results.dat"); + + constexpr double abs_margin = double(1 << (cordic_abs_t::Out_I - 1)) * 2. / 100.; + + int counted_errors = 0; + + // Executing the encoder + for (int iter = 0; iter < int(n_lines); iter++) { + // Execute + + cordic_abs_16_4_6(re_values_in[iter], im_values_in[iter], values_out[iter]); + + + // Display the results + // 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; + + const double dbl_res = cordic_abs_t::scale_cordic(values_out[iter].to_double() ) / cordic_abs_t::out_scale_factor; + + if (std::abs(dbl_res - results[iter]) > abs_margin) { + counted_errors++; + if (counted_errors < 100) { + std::cerr << dbl_res << " is not in the margin of " << abs_margin << " from " << results[iter] << std::endl; + } else if (counted_errors == 100) { + std::cerr << "Too much errors to be reported on terminal." << std::endl; + } + } + } + // outfile.close(); + + printf("RESULT: %s!\n", counted_errors == 0 ? "SUCCESS" : "FAILURE"); + + // Return 0 if the test passed + return counted_errors; +}