From 7707d12e9861f81ecc2293cb4cd5c0ab998b54f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camille=20Moni=C3=A8re?= Date: Mon, 14 Mar 2022 17:55:16 +0100 Subject: [PATCH] Add a synthesis-friendly option and yet another formatting - If ENABLE_SOFTWARE is true, the behavior is the same than before. All tests and functions relying on std::complex are obfuscated otherwise. This is required for Xilinx tools. --- CMakeLists.txt | 7 +++ .../RomGeneratorConst/RomGeneratorConst.hpp | 21 +++++--- .../sources/RomGeneratorML/RomGeneratorML.hpp | 20 +++++--- RomGenerators/sources/definitions.hpp | 50 +++++++++++++++++++ .../CCordicRotateConstexpr.hpp | 19 +++---- .../CCordicRotateRom/CCordicRotateRom.hpp.in | 8 +-- sources/tb/cordic_rom_tb.cpp.in | 7 ++- sources/tb/cordic_tb.cpp | 4 ++ 8 files changed, 108 insertions(+), 28 deletions(-) create mode 100644 RomGenerators/sources/definitions.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d9c6e89..ab5f75f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,9 @@ option (EXPORT_COMMANDS "export compile commands, for use with clangd for exampl option (ENABLE_XILINX "use Xilinx provided proprietary headers." OFF) option (ENABLE_TESTING "use Catch2 in conjunction with CTest as a test suite." ON) +option (ENABLE_SOFTWARE + "use C++ standard library types (like std::complex). Unsuitable for synthesis." ON +) option (PEDANTIC "use -Wall and -pedantic." ON) @@ -94,6 +97,10 @@ if ((NOT EXISTS ${AP_INCLUDE_DIR}/ap_int.h) OR (NOT EXISTS ${AP_INCLUDE_DIR}/ap_ ) endif () +if (ENABLE_SOFTWARE) + add_compile_definitions (SOFTWARE=1) +endif () + set ( ROM_TYPE "ml" diff --git a/RomGenerators/sources/RomGeneratorConst/RomGeneratorConst.hpp b/RomGenerators/sources/RomGeneratorConst/RomGeneratorConst.hpp index 3834777..50cd8ef 100644 --- a/RomGenerators/sources/RomGeneratorConst/RomGeneratorConst.hpp +++ b/RomGenerators/sources/RomGeneratorConst/RomGeneratorConst.hpp @@ -28,20 +28,21 @@ #include +#include "definitions.hpp" + template class CRomGeneratorConst { static_assert(In_W > 0, "Inputs can't be on zero bits."); static_assert(NStages < 8, "7 stages of CORDIC is the maximum supported."); static_assert(NStages > 1, "2 stages of CORDIC is the minimum."); - static_assert(((divider - 1) & divider) == 0, "divider must be a power of 2."); + static_assert(is_pow_2(divider), "divider must be a power of 2."); public: - static constexpr double pi = 3.14159265358979323846; - static constexpr double two_pi = 2 * pi; - static constexpr double half_pi = pi * 0.5; - static constexpr double rotation = pi / divider; - static constexpr double q = Tq; - static constexpr uint32_t max_length = 2 * divider * Tq; // 2pi / (pi / divider) * q + static constexpr double rotation = pi / divider; + static constexpr double q = Tq; + + static constexpr unsigned max_length = 2 * divider * Tq; // 2pi / (pi / divider) * q + static constexpr unsigned addr_length = needed_bits(max_length - 1); static constexpr int64_t scale_factor = int64_t(1U << (In_W - 1)); static constexpr double atanDbl[28] { @@ -140,9 +141,13 @@ void generate_rom_header_cst(const char * filename) { char rom_name[64]; snprintf(rom_name, 64, "cst_%u_%u_%u_%u", In_W, NStages, Tq, divider); + fprintf(rom_file, "/** @file %s\n * THIS FILE IS GENERATED AUTOMATICALY, DO NOT EDIT IT!\n */\n", filename); + fprintf(rom_file, "#ifndef %s\n#define %s\n\n", upper_file_def, upper_file_def); fprintf(rom_file, "#include \n\n"); - fprintf(rom_file, "namespace cordic_roms {\n"); + fprintf(rom_file, "namespace cordic_roms {\n\n"); + + fprintf(rom_file, "constexpr uint64_t %s_size = %d;\n\n", rom_name, rom.max_length); fprintf(rom_file, "constexpr uint8_t %s[%d] = {\n ", rom_name, rom.max_length); for (uint16_t u = 0; u < rom.max_length - 1; u++) { diff --git a/RomGenerators/sources/RomGeneratorML/RomGeneratorML.hpp b/RomGenerators/sources/RomGeneratorML/RomGeneratorML.hpp index f4f832c..8d48594 100644 --- a/RomGenerators/sources/RomGeneratorML/RomGeneratorML.hpp +++ b/RomGenerators/sources/RomGeneratorML/RomGeneratorML.hpp @@ -25,18 +25,22 @@ #include #include +#include "definitions.hpp" + template class CRomGeneratorML { static_assert(In_W > 0, "Inputs can't be on zero bits."); static_assert(NStages < 8, "7 stages of CORDIC is the maximum supported."); static_assert(NStages > 1, "2 stages of CORDIC is the minimum."); static_assert(NStages > 1, "2 stages of CORDIC is the minimum."); - static_assert(((divider - 1) & divider) == 0, "divider must be a power of 2."); + static_assert(is_pow_2(divider), "divider must be a power of 2."); public: - static constexpr double rotation = M_PI / divider; - static constexpr double q = Tq; - static constexpr uint32_t max_length = 2 * divider * Tq; // 2pi / (pi / divider) * q + static constexpr double rotation = pi / divider; + static constexpr double q = Tq; + + static constexpr unsigned max_length = 2 * divider * Tq; // 2pi / (pi / divider) * q + static constexpr unsigned addr_length = needed_bits(max_length - 1); static constexpr int64_t scale_factor = int64_t(1U << (In_W - 1)); private: @@ -115,11 +119,15 @@ void generate_rom_header_ml(const char * filename) { char rom_name[64]; snprintf(rom_name, 64, "ml_%u_%u_%u_%u", In_W, NStages, Tq, divider); + fprintf(rom_file, "/** @file %s\n * THIS FILE IS GENERATED AUTOMATICALY, DO NOT EDIT IT!\n */\n", filename); + fprintf(rom_file, "#ifndef %s\n#define %s\n\n", upper_file_def, upper_file_def); fprintf(rom_file, "#include \n\n"); - fprintf(rom_file, "namespace cordic_roms {\n"); + fprintf(rom_file, "namespace cordic_roms {\n\n"); - fprintf(rom_file, "constexpr uint8_t %s[%d] = {\n ", rom_name, rom.max_length); + fprintf(rom_file, "constexpr uint64_t %s_size = %d;\n\n", rom_name, rom.max_length); + + fprintf(rom_file, "constexpr uint8_t %s[%d] = {\n ", rom_name, rom.max_length); for (uint16_t u = 0; u < rom.max_length - 1; u++) { if (((u & 7) == 0) && u != 0) { fprintf(rom_file, "\n "); diff --git a/RomGenerators/sources/definitions.hpp b/RomGenerators/sources/definitions.hpp new file mode 100644 index 0000000..cbf429b --- /dev/null +++ b/RomGenerators/sources/definitions.hpp @@ -0,0 +1,50 @@ +/* + * + * Copyright 2022 Camille "DrasLorus" Monière. + * + * This file is part of CORDIC_Rotate_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 . + * + */ + +#ifndef _DEFINITIONS_HPP_ +#define _DEFINITIONS_HPP_ + +#include +#include + +#ifdef M_PI +constexpr double pi = M_PI; +#else +constexpr double pi = 3.14159265358979323846; +#endif + +constexpr double half_pi = pi / 2; +constexpr double inv_pi = 1 / pi; +constexpr double two_pi = 2 * pi; +constexpr double inv_2pi = 0.5 * inv_pi; + +constexpr uint32_t needed_bits(uint32_t value) { + uint32_t result = 0; + while (value > 0) { + result++; + value >>= 1; + } + return result; +} + +constexpr bool is_pow_2(uint32_t value) { + return (1U << (needed_bits(value) - 1)) == value; +} + +#endif // _DEFINITIONS_HPP_ diff --git a/sources/CCordicRotateConstexpr/CCordicRotateConstexpr.hpp b/sources/CCordicRotateConstexpr/CCordicRotateConstexpr.hpp index e21f911..02ab6c1 100644 --- a/sources/CCordicRotateConstexpr/CCordicRotateConstexpr.hpp +++ b/sources/CCordicRotateConstexpr/CCordicRotateConstexpr.hpp @@ -37,7 +37,7 @@ class CCordicRotateConstexpr { static_assert(TIn_W > 0, "Inputs can't be on zero bits."); static_assert(Tnb_stages < 8, "7 stages of CORDIC is the maximum supported."); static_assert(Tnb_stages > 1, "2 stages of CORDIC is the minimum."); - static_assert(((divider - 1) & divider) == 0, "divider must be a power of 2."); + static_assert(is_pow_2(divider), "divider must be a power of 2."); public: // ``` GNU Octave @@ -54,18 +54,20 @@ public: static constexpr unsigned Out_I = In_I + 2; static constexpr unsigned nb_stages = Tnb_stages; - static constexpr uint64_t kn_i = uint64_t(kn_values[nb_stages - 1] * double(1U << 3)); // 3 bits are enough - static constexpr uint64_t in_scale_factor = uint64_t(1U << (In_W - In_I)); - static constexpr uint64_t out_scale_factor = uint64_t(1U << (Out_W - Out_I)); + static constexpr unsigned kn_i = unsigned(kn_values[nb_stages - 1] * double(1U << 3)); // 3 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)); - static constexpr double rotation = CRomGeneratorConst::rotation; + static constexpr double rotation = CRomGeneratorConst::rotation; + static constexpr unsigned addr_length = CRomGeneratorConst::addr_length; static constexpr int64_t scale_cordic(int64_t in) { return in * kn_i / 8U; } +#if !defined(__SYNTHESIS__) && defined(SOFTWARE) static constexpr std::complex cordic(std::complex x_in, - uint8_t counter) { + uint64_t counter) { int64_t A = x_in.real(); int64_t B = x_in.imag(); @@ -90,13 +92,12 @@ public: return {(A), (B)}; } -#ifndef __SYNTHESIS__ static constexpr double scale_cordic(double in) { return in * kn_values[nb_stages - 1]; } static constexpr std::complex cordic(std::complex x_in, - uint8_t counter) { + uint64_t counter) { const std::complex fx_x_in(int64_t(x_in.real() * double(in_scale_factor)), int64_t(x_in.imag() * double(in_scale_factor))); @@ -112,7 +113,7 @@ public: } static void cordic(const ap_int & re_in, const ap_int & im_in, - const ap_uint<8> & counter, + const ap_uint & counter, ap_int & re_out, ap_int & im_out) { const ap_uint R = rom_cordic.rom[counter]; diff --git a/sources/CCordicRotateRom/CCordicRotateRom.hpp.in b/sources/CCordicRotateRom/CCordicRotateRom.hpp.in index d7e79d9..b1a4dc0 100644 --- a/sources/CCordicRotateRom/CCordicRotateRom.hpp.in +++ b/sources/CCordicRotateRom/CCordicRotateRom.hpp.in @@ -32,6 +32,7 @@ #include "CCordicRotateRomTemplate.hpp" #include "CordicRoms/cordic_rom_@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@_@CORDIC_DIVIDER@.hpp" +#include "definitions.hpp" #ifndef KN_STATIC_TABLE_DEFINED #define KN_STATIC_TABLE_DEFINED 1 @@ -48,7 +49,7 @@ class CCordicRotateRom 0, "Inputs can't be on zero bits."); static_assert(@CORDIC_STAGES@ < 8, "7 stages of CORDIC is the maximum supported."); static_assert(@CORDIC_STAGES@ > 1, "2 stages of CORDIC is the minimum."); - static_assert(((@CORDIC_DIVIDER@ - 1) & @CORDIC_DIVIDER@) == 0, "divider must be a power of 2."); + static_assert(is_pow_2(@CORDIC_DIVIDER@), "divider must be a power of 2."); public: static constexpr unsigned In_W = @CORDIC_W@; @@ -62,12 +63,14 @@ public: static constexpr uint64_t in_scale_factor = uint64_t(1U << (In_W - In_I)); static constexpr uint64_t out_scale_factor = uint64_t(1U << (Out_W - Out_I)); - static constexpr double rotation = M_PI / @CORDIC_DIVIDER@; + static constexpr double rotation = pi / @CORDIC_DIVIDER@; + static constexpr unsigned addr_length = cordic_roms::@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@_@CORDIC_DIVIDER@_size; static constexpr int64_t scale_cordic(int64_t in) { return in * kn_i / 8U; } +#if !defined(__SYNTHESIS__) && defined(SOFTWARE) static constexpr std::complex cordic(std::complex x_in, uint8_t counter) { @@ -94,7 +97,6 @@ public: return {(A), (B)}; } -#ifndef __SYNTHESIS__ static constexpr double scale_cordic(double in) { return in * kn_values[nb_stages - 1]; } diff --git a/sources/tb/cordic_rom_tb.cpp.in b/sources/tb/cordic_rom_tb.cpp.in index da8843f..5adb4c4 100644 --- a/sources/tb/cordic_rom_tb.cpp.in +++ b/sources/tb/cordic_rom_tb.cpp.in @@ -27,14 +27,14 @@ using namespace std; using Catch::Matchers::Floating::WithinAbsMatcher; - typedef CCordicRotateRom<4, @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@> cordic_rom; +#if defined(SOFTWARE) TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@) works with C-Types", "[CORDIC]") { SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - div:@CORDIC_DIVIDER@") { static constexpr cordic_rom cordic {}; - string input_fn = "../data/input.dat"; + string input_fn = "../data/input.dat"; constexpr unsigned n_lines = 100000; @@ -75,6 +75,7 @@ TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDI } } } +#endif TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@) works with AP-Types", "[CORDIC]") { constexpr unsigned n_lines = 100000; @@ -195,6 +196,7 @@ TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDI } } +#if defined(SOFTWARE) TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@) constexpr are evaluated during compilation.", "[CORDIC]") { SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - div:@CORDIC_DIVIDER@ - C-Types") { @@ -208,3 +210,4 @@ TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDI REQUIRE(res1 == cordic_rom::cordic(value_in, angle)); } } +#endif diff --git a/sources/tb/cordic_tb.cpp b/sources/tb/cordic_tb.cpp index 759fc1b..af35883 100644 --- a/sources/tb/cordic_tb.cpp +++ b/sources/tb/cordic_tb.cpp @@ -92,6 +92,7 @@ TEST_CASE("Adaptive CORDIC work as intended", "[!hide][WIP]") { // Return 0 if the test passed } +#if defined(SOFTWARE) TEST_CASE("ROM-based Cordic works with C-Types", "[CORDIC]") { SECTION("W:16 - I:4 - Stages:6 - q:64") { typedef CCordicRotateConstexpr<16, 4, 6, 64> cordic_rom; @@ -156,6 +157,7 @@ TEST_CASE("ROM-based Cordic works with C-Types", "[CORDIC]") { // Return 0 if the test passed } } +#endif TEST_CASE("ROM-based Cordic works with AP-Types", "[CORDIC]") { constexpr unsigned n_lines = 100000; @@ -480,6 +482,7 @@ TEST_CASE("ROM-based Cordic works with AP-Types", "[CORDIC]") { } } +#if defined(SOFTWARE) TEST_CASE("ROM-based Cordic constexpr are evaluated during compilation.", "[CORDIC]") { SECTION("W:16 - I:4 - Stages:6 - q:64 - C-Types") { typedef CCordicRotateConstexpr<16, 4, 6, 64> cordic_rom; @@ -507,3 +510,4 @@ TEST_CASE("ROM-based Cordic constexpr are evaluated during compilation.", "[CORD REQUIRE(res1 == cordic_rom::cordic(value_in, angle)); } } +#endif \ No newline at end of file