GNU 4.6 (consequently Xilinx 2019.1) is finally supported

- if GNU version is less than 6.2:
    * Catch2 is disabled, a custom unit test is used instead,
    * Constexpr keyword is largely removed and less used (performance
      loss),
    * Only ML Cordic/Roms are compiled and tested.
- Perfect compilation on g++ v4.6.4, should work on Vivado HLS (g++
  v4.6.3).
This commit is contained in:
anonymous 2022-05-13 15:53:14 +02:00
parent 25255802f1
commit 6afa99d7a1
Signed by: moniere
GPG key ID: 188DD5B072181C0F
12 changed files with 355 additions and 69 deletions

3
.gitignore vendored
View file

@ -10,4 +10,5 @@ compile_commands.json
RomGenerators/sources/main_generator_??*_*.cpp
sources/CCordicRotateRom/CCordicRotateRom_*.?pp
sources/CordicRoms/cordic_rom_*.?pp
sources/tb/cordic_rom_tb_??*_*.?pp
sources/tb/catchy/cordic_rom_tb_??*_*.?pp
sources/tb/catch_less/cordic_rom_aptypes_tb_??*_*.?pp

View file

@ -15,10 +15,20 @@
# If not, see <https://www.gnu.org/licenses/>.
#
cmake_minimum_required (VERSION 3.16.0 FATAL_ERROR)
cmake_minimum_required (VERSION 3.18.0 FATAL_ERROR)
# setting this is required
set (CMAKE_CXX_STANDARD 14)
project (
CordicRotate
LANGUAGES CXX
VERSION 0.1
)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2))
set (CMAKE_CXX_STANDARD 11)
else ()
set (CMAKE_CXX_STANDARD 14)
endif ()
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
@ -27,20 +37,13 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
set (CMAKE_EXPORT_COMPILE_COMMANDS true)
project (
CordicRotate
LANGUAGES CXX
VERSION 0.1
)
# ##################################################################################################
# Options
# ##################################################################################################
option (EXPORT_COMMANDS "export compile commands, for use with clangd for example." ON)
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_TESTING "use Catch2 (if possible) in conjunction with CTest as a test suite." ON)
option (ENABLE_SOFTWARE
"use C++ standard library types (like std::complex). Unsuitable for synthesis." ON
)
@ -65,7 +68,7 @@ if (ENABLE_XILINX)
)
set (
XILINX_VER
"2020.2"
"2019.1"
CACHE STRING "Xilinx software version to use."
)
@ -169,10 +172,15 @@ target_link_libraries (cordic_rom_gen PUBLIC romgen)
file (GLOB ALL_CORDIC_ROM_SOURCES sources/CCordicRotateRom/*.cpp)
list (REMOVE_ITEM ALL_CORDIC_ROM_SOURCES sources/CCordicRotateRom/${CORDIC_ROM_SOURCE})
add_library (
cordic STATIC sources/CCordicRotateSmart/CCordicRotateSmart.cpp
sources/CCordicRotateConstexpr/CCordicRotateConstexpr.cpp ${ALL_CORDIC_ROM_SOURCES}
add_library (cordic STATIC ${ALL_CORDIC_ROM_SOURCES})
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.2
)
)
target_sources (
cordic PRIVATE sources/CCordicRotateSmart/CCordicRotateSmart.cpp
sources/CCordicRotateConstexpr/CCordicRotateConstexpr.cpp
)
endif ()
target_include_directories (cordic PUBLIC sources)
target_include_directories (cordic SYSTEM PUBLIC ${AP_INCLUDE_DIR})
target_link_libraries (cordic PUBLIC romgen cordic_rom_gen)
@ -180,27 +188,63 @@ target_link_libraries (cordic PUBLIC romgen cordic_rom_gen)
# ##################################################################################################
if (ENABLE_TESTING)
find_package (Catch2 REQUIRED)
string (
CONFIGURE
${CMAKE_CURRENT_SOURCE_DIR}/sources/tb/cordic_rom_tb_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}_${CORDIC_DIVIDER}.cpp
TB_SOURCE
)
configure_file (sources/tb/cordic_rom_tb.cpp.in ${TB_SOURCE} @ONLY)
add_library (catch_common_${PROJECT_NAME} OBJECT sources/tb/main_catch2.cpp)
target_link_libraries (catch_common_${PROJECT_NAME} PUBLIC Catch2::Catch2)
file (GLOB ALL_ROM_TB_SOURCES sources/tb/cordic_rom_*.cpp)
list (REMOVE_ITEM ALL_ROM_TB_SOURCES ${TB_SOURCE})
add_executable (cordic_tb sources/tb/cordic_tb.cpp ${TB_SOURCE} ${ALL_ROM_TB_SOURCES})
target_link_libraries (cordic_tb PRIVATE cordic catch_common_${PROJECT_NAME})
include (CTest)
include (Catch)
catch_discover_tests (cordic_tb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2))
string (
CONFIGURE
${CMAKE_CURRENT_SOURCE_DIR}/sources/tb/catch_less/cordic_rom_aptypes_tb_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}_${CORDIC_DIVIDER}.cpp
TB_SOURCE
)
configure_file (sources/tb/catch_less/cordic_rom_aptypes_tb.cpp.in ${TB_SOURCE} @ONLY)
file (GLOB ALL_ROM_TB_SOURCES sources/tb/catch_less/cordic_rom_aptypes*.cpp)
list (REMOVE_ITEM ALL_ROM_TB_SOURCES ${TB_SOURCE})
get_filename_component (FILE_RADIX ${TB_SOURCE} NAME_WE)
add_executable (${FILE_RADIX} ${TB_SOURCE})
target_link_libraries (${FILE_RADIX} PRIVATE cordic)
add_test (
NAME ${FILE_RADIX}
COMMAND ${FILE_RADIX}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data
)
foreach (TB_FILE ${ALL_ROM_TB_SOURCES})
get_filename_component (FILE_RADIX ${TB_FILE} NAME_WE)
add_executable (${FILE_RADIX} ${TB_FILE})
target_link_libraries (${FILE_RADIX} PRIVATE cordic)
add_test (
NAME ${FILE_RADIX}
COMMAND ${FILE_RADIX}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data
)
endforeach ()
else ()
find_package (Catch2 REQUIRED)
string (
CONFIGURE
${CMAKE_CURRENT_SOURCE_DIR}/sources/tb/catchy/cordic_rom_tb_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}_${CORDIC_DIVIDER}.cpp
TB_SOURCE
)
configure_file (sources/tb/catchy/cordic_rom_tb.cpp.in ${TB_SOURCE} @ONLY)
add_library (catch_common_${PROJECT_NAME} OBJECT sources/tb/catchy/main_catch2.cpp)
target_link_libraries (catch_common_${PROJECT_NAME} PUBLIC Catch2::Catch2)
file (GLOB ALL_ROM_TB_SOURCES sources/tb/catchy/cordic_rom_*.cpp)
list (REMOVE_ITEM ALL_ROM_TB_SOURCES ${TB_SOURCE})
add_executable (cordic_tb sources/tb/catchy/cordic_tb.cpp ${TB_SOURCE} ${ALL_ROM_TB_SOURCES})
target_link_libraries (cordic_tb PRIVATE cordic catch_common_${PROJECT_NAME})
include (Catch)
catch_discover_tests (cordic_tb WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data)
endif ()
endif ()
file (GLOB ALL_ROM_HEADERS sources/CordicRoms/cordic_rom_*.hpp)

View file

@ -15,10 +15,20 @@
# If not, see <https://www.gnu.org/licenses/>.
#
cmake_minimum_required (VERSION 3.16.0 FATAL_ERROR)
# setting this is required
cmake_minimum_required (VERSION 3.18.0 FATAL_ERROR)
# setting this is
set (CMAKE_CXX_STANDARD 14)
project (
CordicRomGenerator
LANGUAGES CXX
VERSION 0.1
)
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2))
set (CMAKE_CXX_STANDARD 11)
else ()
set (CMAKE_CXX_STANDARD 14)
endif ()
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
@ -27,15 +37,14 @@ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
set (CMAKE_EXPORT_COMPILE_COMMANDS true)
project (
CordicRomGenerator
LANGUAGES CXX
VERSION 0.1
)
set (IS_GNU (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"))
set (IS_GNU_LEGACY ${IS_GNU} AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.2))
add_library (romgen sources/RomGeneratorML/RomGeneratorML.cpp)
if (NOT IS_GNU_LEGACY)
target_sources (romgen PUBLIC sources/RomGeneratorConst/RomGeneratorConst.cpp)
endif ()
add_library (
romgen sources/RomGeneratorML/RomGeneratorML.cpp sources/RomGeneratorConst/RomGeneratorConst.cpp
)
target_include_directories (romgen PUBLIC sources)
if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")

View file

@ -20,6 +20,8 @@
#ifndef _ROM_GENERATOR_CONST_
#define _ROM_GENERATOR_CONST_
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
#include <array>
#include <climits>
#include <cmath>
@ -180,4 +182,5 @@ void generate_rom_header_cst_raw(const char * filename = "rom_cordic.txt") {
fprintf(rom_file, "%03d\n\n", uint16_t(rom.rom[rom.max_length - 1]));
}
#endif // STANDARD GUARD
#endif // _ROM_GENERATOR_CONST_

View file

@ -29,6 +29,12 @@
namespace rcr = rom_cordic_rotate;
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
#define OWN_CONSTEXPR constexpr
#else
#define OWN_CONSTEXPR
#endif
template <unsigned In_W, unsigned NStages, unsigned Tq, unsigned divider = 2>
class CRomGeneratorML {
static_assert(In_W > 0, "Inputs can't be on zero bits.");
@ -38,16 +44,23 @@ class CRomGeneratorML {
static_assert(rcr::is_pow_2<divider>(), "divider must be a power of 2.");
public:
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
static constexpr double rotation = rcr::pi / divider;
static constexpr double q = Tq;
static constexpr unsigned max_length = 2 * divider * Tq; // 2pi / (pi / divider) * q
static constexpr unsigned addr_length = rcr::needed_bits<max_length - 1>();
static constexpr int64_t scale_factor = int64_t(1U << (In_W - 1));
#else
const double rotation;
const double q;
const unsigned max_length;
const unsigned addr_length;
const int64_t scale_factor;
#endif
private:
constexpr std::complex<int64_t> cordic_ML(const std::complex<int64_t> & x_in,
uint8_t counter) {
OWN_CONSTEXPR std::complex<int64_t> cordic_ML(const std::complex<int64_t> & x_in,
uint8_t counter) {
int64_t A = x_in.real();
int64_t B = x_in.imag();
@ -73,9 +86,21 @@ private:
}
public:
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
uint8_t rom[max_length];
#else
uint8_t rom[2 * Tq * divider];
#endif
CRomGeneratorML() {
CRomGeneratorML()
#if __cplusplus < 201402L
: rotation(rcr::pi / divider),
q(Tq),
max_length(2 * Tq * divider),
addr_length(rcr::needed_bits<2 * Tq * divider - 1>()),
scale_factor(int64_t(1U << (In_W - 1)))
#endif
{
for (unsigned n = 0; n < max_length; n++) {
const double re_x = floor(double(scale_factor - 1) * cos(-rotation / double(q) * double(n)));
const double im_x = floor(double(scale_factor - 1) * sin(-rotation / double(q) * double(n)));
@ -158,4 +183,6 @@ void generate_rom_header_ml_raw(const char * filename) {
fprintf(rom_file, "%03d\n\n", uint16_t(rom.rom[rom.max_length - 1]));
}
#undef OWN_CONSTEXPR
#endif // _ROM_GENERATOR_ML

View file

@ -36,7 +36,7 @@ constexpr double inv_pi = 1 / pi;
constexpr double two_pi = 2 * pi;
constexpr double inv_2pi = 0.5 * inv_pi;
#if XILINX_MAJOR > 2019 || !defined (XILINX_MAJOR)
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
constexpr uint32_t needed_bits(uint32_t value) {
uint32_t result = 0;

View file

@ -26,10 +26,17 @@
using namespace std;
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
#define OWN_CONSTEXPR constexpr
#else
#define OWN_CONSTEXPR
#endif
template <unsigned NStages>
constexpr complex<int64_t> cordic(complex<int64_t> x_in,
uint8_t counter,
const uint8_t * rom_cordic) {
OWN_CONSTEXPR complex<int64_t> cordic(
complex<int64_t> x_in,
uint8_t counter,
const uint8_t * rom_cordic) {
int64_t A = x_in.real();
int64_t B = x_in.imag();
@ -54,18 +61,7 @@ constexpr complex<int64_t> cordic(complex<int64_t> x_in,
return {A, B};
}
template <unsigned NStages>
void checkMC() {
const CRomGeneratorML<16, NStages, 64> rom;
string fn = "result_MC_W16_S" + to_string(NStages) + "_Q64.dat";
ofstream res_file(fn);
for (unsigned u = 0; u < rom.max_length; u++) {
auto res = cordic<NStages>(4096, u, rom.rom);
res_file << double(res.real() * 155) / 1048576. << ", " << double(res.imag() * 155) / 1048576. << endl;
}
}
#if __cplusplus >= 201402L || XILINX_MAJOR > 2019
template <unsigned NStages>
void checkConst() {
@ -80,6 +76,34 @@ void checkConst() {
}
}
#else
template <unsigned,unsigned,unsigned>
void generate_rom_header_cst_raw(const string &) {}
template <unsigned,unsigned,unsigned>
void generate_rom_header_cst(const string &) {}
template <unsigned>
void checkConst() {}
#endif
template <unsigned NStages>
void checkMC() {
const CRomGeneratorML<16, NStages, 64, 2> rom;
string fn = "result_MC_W16_S" + to_string(NStages) + "_Q64.dat";
ofstream res_file(fn);
for (unsigned u = 0; u < rom.max_length; u++) {
complex<int64_t> res = cordic<NStages>(4096, u, rom.rom);
res_file << double(res.real() * 155) / 1048576. << ", " << double(res.imag() * 155) / 1048576. << endl;
}
}
int main(int argc, char * argv[]) {
uint8_t stages;

View file

@ -73,7 +73,7 @@ public:
return in * kn_i / 16U;
}
#if !defined(__SYNTHESIS__) && defined(SOFTWARE)
#if !defined(__SYNTHESIS__) && defined(SOFTWARE) && __cplusplus >= 201402L
static constexpr std::complex<int64_t> cordic(std::complex<int64_t> x_in,
uint8_t counter) {
@ -124,7 +124,7 @@ public:
const ap_uint<8> & counter,
ap_int<Out_W> & re_out, ap_int<Out_W> & im_out) {
const ap_uint<nb_stages + 1> R = cordic_roms::@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@_@CORDIC_DIVIDER@[counter];
const ap_uint<nb_stages + 1> R = *(cordic_roms::@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@_@CORDIC_DIVIDER@ + counter);
ap_int<Out_W> A = bool(R[0]) ? ap_int<In_W>(-re_in) : re_in;
ap_int<Out_W> B = bool(R[0]) ? ap_int<In_W>(-im_in) : im_in;

View file

@ -0,0 +1,179 @@
/*
*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#include "CCordicRotateRom/CCordicRotateRom_@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@_@CORDIC_DIVIDER@.hpp"
#include <fstream>
#include <iostream>
using namespace std;
typedef CCordicRotateRom<4, @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@> cordic_rom;
template <unsigned n_lines>
int section_1() {
// SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - div:@CORDIC_DIVIDER@") {}
int counted_errors = 0;
static constexpr cordic_rom cordic {};
string input_fn = "../data/input.dat";
constexpr double rotation = cordic_rom::rotation;
constexpr double q = cordic_rom::q;
constexpr uint64_t cnt_mask = 0xFF; // Value dependant of the way the ROM is initialized
constexpr unsigned Out_W = cordic_rom::Out_W;
constexpr unsigned In_W = cordic_rom::In_W;
ap_int<In_W> values_re_in[n_lines];
ap_int<In_W> values_im_in[n_lines];
ap_int<Out_W> values_re_out[n_lines];
ap_int<Out_W> values_im_out[n_lines];
double results_re[n_lines];
double results_im[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<double> c {a, b};
values_re_in[i] = int64_t(a * double(cordic_rom::in_scale_factor));
values_im_in[i] = int64_t(b * double(cordic_rom::in_scale_factor));
const complex<double> e = c * exp(complex<double>(0., rotation / q * (i & cnt_mask)));
results_re[i] = e.real();
results_im[i] = e.imag();
}
fclose(INPUT);
constexpr double abs_margin = double(1 << cordic.Out_I) * 2. / 100.;
// Executing the CORDIC
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
const uint8_t counter = uint8_t(iter & cnt_mask);
cordic_rom::cordic(
values_re_in[iter], values_im_in[iter],
counter,
values_re_out[iter], values_im_out[iter]);
const double res_re = values_re_out[iter].to_double() * 5. / 8. / cordic_rom::out_scale_factor;
const double res_im = values_im_out[iter].to_double() * 5. / 8. / cordic_rom::out_scale_factor;
if (abs(res_re - results_re[iter]) > abs_margin) {
counted_errors++;
}
if (abs(res_im - results_im[iter]) > abs_margin) {
counted_errors++;
}
}
return counted_errors;
}
template <unsigned n_lines>
int section_2() {
// SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - div:@CORDIC_DIVIDER@ - internal scaling")
int counted_errors = 0;
static constexpr cordic_rom cordic {};
string input_fn = "../data/input.dat";
constexpr double rotation = cordic_rom::rotation;
constexpr double q = cordic_rom::q;
constexpr uint64_t cnt_mask = 0xFF; // Value dependant of the way the ROM is initialized
constexpr unsigned Out_W = cordic_rom::Out_W;
constexpr unsigned In_W = cordic_rom::In_W;
ap_int<In_W> values_re_in[n_lines];
ap_int<In_W> values_im_in[n_lines];
ap_int<Out_W> values_re_out[n_lines];
ap_int<Out_W> values_im_out[n_lines];
double results_re[n_lines];
double results_im[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<double> c {a, b};
values_re_in[i] = int64_t(a * double(cordic_rom::in_scale_factor));
values_im_in[i] = int64_t(b * double(cordic_rom::in_scale_factor));
const complex<double> e = c * exp(complex<double>(0., rotation / q * (i & cnt_mask)));
results_re[i] = e.real();
results_im[i] = e.imag();
}
fclose(INPUT);
constexpr double abs_margin = double(1 << cordic.Out_I) * 2. / 100.;
// Executing the CORDIC
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
const uint8_t counter = uint8_t(iter & cnt_mask);
cordic_rom::cordic(
values_re_in[iter], values_im_in[iter],
counter,
values_re_out[iter], values_im_out[iter]);
const double res_re = cordic_rom::scale_cordic(values_re_out[iter]).to_double() / cordic_rom::out_scale_factor;
const double res_im = cordic_rom::scale_cordic(values_im_out[iter]).to_double() / cordic_rom::out_scale_factor;
if (abs(res_re - results_re[iter]) > abs_margin) {
counted_errors++;
}
if (abs(res_im - results_im[iter]) > abs_margin) {
counted_errors++;
}
}
return counted_errors;
}
int main(int, char **) {
int counted_errors = 0;
const string test_case = "ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@, @CORDIC_DIVIDER@) works with AP-Types";
constexpr unsigned n_lines = 100000;
cout << "Test case: " << test_case << endl;
counted_errors += section_1<n_lines>();
counted_errors += section_2<n_lines>();
cout << (counted_errors == 0 ? "SUCCESS" : "FAILURE") << "." << endl;
return counted_errors;
}

View file

@ -63,7 +63,6 @@ TEST_CASE("Adaptive CORDIC work as intended", "[!mayfail][!hide][WIP]") {
fclose(INPUT);
fclose(RESULTS);
constexpr double abs_margin = double(1 << 6) * 3. / 100.;
// Save the results to a file