Big update, that compiles with Xilinx GCC 6.2

- Commit title implies possible Xilinx Vivado HLS 2019.1 support, thus support
  for e.g. all USRP Series 3 from Ettus.
- Add a new CORDIC version, Rom based and meta-programmed via CMake
  features, to be even compiled with earlier GCC (not quite tested, but
  by changing constexpr to const and using gcc 4.6 -sdt=c++0x, it
  worked).
- Class name modified, to be more explicit.
This commit is contained in:
Camille Monière 2022-02-18 21:33:59 +01:00
parent 9b83926968
commit 5bc9c3eeb1
Signed by: moniere
GPG key ID: 188DD5B072181C0F
17 changed files with 751 additions and 86 deletions

6
.gitignore vendored
View file

@ -4,4 +4,8 @@ build
lib
bin
compile_commands.json
*octave-workspace
*octave-workspace
sources/CCordicRotateRom/CCordicRotateRom_*.?pp
sources/CordicRoms/cordic_rom_*.?pp
sources/tb/cordic_rom_tb_??*_*.?pp

View file

@ -19,6 +19,7 @@ cmake_minimum_required (VERSION 3.16.0 FATAL_ERROR)
# setting this is required
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
@ -37,7 +38,7 @@ project (
# ##################################################################################################
option (EXPORT_COMMANDS "export compile commands, for use with clangd for example." ON)
option (ENABLE_XILINX "use Xilinx provided and proprietary headers." OFF)
option (ENABLE_XILINX "use Xilinx provided proprietary headers." OFF)
option (ENABLE_TESTING "use Catch2 in conjunction with CTest as a test suite." ON)
@ -72,7 +73,16 @@ if (ENABLE_XILINX)
endif ()
message (STATUS "AP headers must lie under ${AP_INCLUDE_DIR}")
else ()
find_file (AP_FIXED ap_fixed.h PATH_SUFFIXES ap_types hls_ap_types/include REQUIRED)
set (
AP_TYPES_HINT
/usr/include
CACHE PATH "location of ap_types include directory."
)
find_file (
AP_FIXED ap_fixed.h
HINTS ${AP_TYPES_HINT}
PATH_SUFFIXES ap_types hls_ap_types/include REQUIRED
)
get_filename_component (AP_INCLUDE_DIR ${AP_FIXED} DIRECTORY)
endif ()
@ -83,23 +93,88 @@ if ((NOT EXISTS ${AP_INCLUDE_DIR}/ap_int.h) OR (NOT EXISTS ${AP_INCLUDE_DIR}/ap_
)
endif ()
set (
ROM_TYPE
"mc"
CACHE STRING "RomGenerator to use, either 'mc' or 'cst'."
)
set (
CORDIC_W
"16"
CACHE STRING "bit length of the CORDIC input."
)
set (
CORDIC_STAGES
"6"
CACHE STRING "number of CORDIC stages."
)
set (
CORDIC_Q
"64"
CACHE STRING "number of rotation divisions."
)
add_subdirectory (RomGenerators)
set (ROM_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sources/CordicRoms)
string (CONFIGURE ${ROM_DIRECTORY}/cordic_rom_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}.hpp ROM_HEADER
)
add_custom_command (
OUTPUT ${ROM_HEADER}
COMMAND rom_generator
WORKING_DIRECTORY ${ROM_DIRECTORY}
)
set (CORDIC_ROM_HEADER CCordicRotateRom_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}.hpp)
set (CORDIC_ROM_SOURCE CCordicRotateRom_${ROM_TYPE}_${CORDIC_W}_${CORDIC_STAGES}_${CORDIC_Q}.cpp)
configure_file (
sources/CCordicRotateRom/CCordicRotateRom.hpp.in
${CMAKE_CURRENT_SOURCE_DIR}/sources/CCordicRotateRom/${CORDIC_ROM_HEADER} @ONLY
)
configure_file (
sources/CCordicRotateRom/CCordicRotateRom.cpp.in
${CMAKE_CURRENT_SOURCE_DIR}/sources/CCordicRotateRom/${CORDIC_ROM_SOURCE} @ONLY
)
add_library (
cordic STATIC sources/CCordicRotate/CCordicRotate.cpp
sources/CCordicRotateHalfPiRom/CCordicRotateHalfPiRom.cpp
cordic_rom_gen OBJECT sources/CCordicRotateRom/${CORDIC_ROM_HEADER}
sources/CCordicRotateRom/${CORDIC_ROM_SOURCE} ${ROM_HEADER}
)
target_include_directories (cordic_rom_gen PUBLIC sources)
target_include_directories (cordic_rom_gen SYSTEM PUBLIC ${AP_INCLUDE_DIR})
target_link_libraries (cordic_rom_gen PUBLIC romgen)
file (GLOB ALL_CORDIC_ROM_HEADERS sources/CCordicRotateRom/*.hpp)
file (GLOB ALL_CORDIC_ROM_SOURCES sources/CCordicRotateRom/*.cpp)
file (GLOB ALL_ROM_HEADERS sources/CordicRoms/*.hpp)
add_library (
cordic STATIC sources/CCordicRotateSmart/CCordicRotateSmart.cpp
sources/CCordicRotateConstexpr/CCordicRotateConstexpr.cpp
${ALL_CORDIC_ROM_SOURCES}
)
target_include_directories (cordic PUBLIC sources)
target_include_directories (cordic SYSTEM PUBLIC ${AP_INCLUDE_DIR})
target_link_libraries (cordic PUBLIC romgen)
# ##################################################################################################
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}.cpp
TB_SOURCE)
configure_file (
sources/tb/cordic_rom_tb.cpp.in
${TB_SOURCE} @ONLY
)
add_library (catch_common OBJECT sources/tb/main_catch2.cpp)
target_link_libraries (catch_common PUBLIC Catch2::Catch2)
add_executable (cordic_tb sources/tb/cordic_tb.cpp)
add_executable (cordic_tb sources/tb/cordic_tb.cpp ${TB_SOURCE})
target_link_libraries (cordic_tb PUBLIC cordic catch_common)
include (CTest)

View file

@ -15,31 +15,62 @@
# If not, see <https://www.gnu.org/licenses/>.
#
cmake_minimum_required(VERSION 3.16.0 FATAL_ERROR)
cmake_minimum_required (VERSION 3.16.0 FATAL_ERROR)
# setting this is required
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../lib)
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/../bin)
set(CMAKE_EXPORT_COMPILE_COMMANDS true)
set (CMAKE_EXPORT_COMPILE_COMMANDS true)
project(
project (
CordicRomGenerator
LANGUAGES CXX
VERSION 0.1)
VERSION 0.1
)
add_library(romgen sources/RomGeneratorMCHalfPi/RomGeneratorMCHalfPi.cpp
sources/RomGeneratorConst/RomGeneratorConst.cpp)
target_include_directories(romgen PUBLIC sources)
add_library (
romgen sources/RomGeneratorMCHalfPi/RomGeneratorMCHalfPi.cpp
sources/RomGeneratorConst/RomGeneratorConst.cpp
)
target_include_directories (romgen PUBLIC sources)
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL
"RelWithDebInfo")
target_compile_definitions(romgen PRIVATE DEBUG=1)
else()
target_compile_definitions(romgen PRIVATE NDEBUG=1)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
target_compile_definitions (romgen PRIVATE DEBUG=1)
else ()
target_compile_definitions (romgen PRIVATE NDEBUG=1)
endif ()
add_executable(rom_generator sources/main.cpp)
target_link_libraries(rom_generator PUBLIC romgen)
add_executable (rom_generator_legacy sources/main_legacy.cpp)
target_link_libraries (rom_generator_legacy PUBLIC romgen)
set (
ROM_TYPE
"mc"
CACHE STRING "RomGenerator to use, either 'mc' or 'const'."
)
set (
CORDIC_W
"16"
CACHE STRING "bit length of the CORDIC input."
)
set (
CORDIC_STAGES
"6"
CACHE STRING "number of CORDIC stages."
)
set (
CORDIC_Q
"64"
CACHE STRING "number of rotation divisions."
)
configure_file (
sources/main_generator.cpp.in ${CMAKE_CURRENT_SOURCE_DIR}/sources/main_generator.cpp @ONLY
)
add_executable (rom_generator sources/main_generator.cpp)
target_link_libraries (rom_generator PUBLIC romgen)

View file

@ -58,7 +58,7 @@ private:
double A = scale_factor - 1;
double B = 0;
uint8_t R = 0;
uint8_t R = 0;
const uint8_t sig_mask = 0x80;
double beta = rot_in;
@ -124,8 +124,8 @@ public:
};
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_const(const char * filename = "rom_cordic.h") {
constexpr CRomGeneratorConst<In_W, NStages, Tq> rom{};
void generate_rom_header_cst(const char * filename) {
constexpr CRomGeneratorConst<In_W, NStages, Tq> rom {};
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
@ -133,9 +133,17 @@ void generate_rom_header_const(const char * filename = "rom_cordic.h") {
exit(EXIT_FAILURE);
}
// fprintf(rom_file, "#ifndef ROM_CORDIC\n#define ROM_CORDIC\n\n");
char upper_file_def[64];
snprintf(upper_file_def, 64, "CORDIC_ROMS_CST_%u_%u_%u", In_W, NStages, Tq);
fprintf(rom_file, "constexpr uint8_t rom_cordic[%d] = {\n ", rom.max_length);
char rom_name[64];
snprintf(rom_name, 64, "cst_%u_%u_%u", In_W, NStages, Tq);
fprintf(rom_file, "#ifndef %s\n#define %s\n\n", upper_file_def, upper_file_def);
fprintf(rom_file, "#include <cstdint>\n\n");
fprintf(rom_file, "namespace cordic_roms {\n");
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 ");
@ -143,13 +151,14 @@ void generate_rom_header_const(const char * filename = "rom_cordic.h") {
fprintf(rom_file, "%3d, ", uint16_t(rom.rom[u]));
}
fprintf(rom_file, "%3d};\n", uint16_t(rom.rom[rom.max_length - 1]));
// fprintf(rom_file, "#endif // ROM_CORDIC");
fprintf(rom_file, "\n} // namespace cordic_roms\n\n");
fprintf(rom_file, "#endif // %s\n\n", upper_file_def);
}
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_const_raw(const char * filename = "rom_cordic.txt") {
constexpr CRomGeneratorConst<In_W, NStages, Tq> rom{};
void generate_rom_header_cst_raw(const char * filename = "rom_cordic.txt") {
constexpr CRomGeneratorConst<In_W, NStages, Tq> rom {};
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
@ -161,7 +170,6 @@ void generate_rom_header_const_raw(const char * filename = "rom_cordic.txt") {
fprintf(rom_file, "%03d\n", uint16_t(rom.rom[u]));
}
fprintf(rom_file, "%03d\n\n", uint16_t(rom.rom[rom.max_length - 1]));
}
#endif // _ROM_GENERATOR_CONST_

View file

@ -99,7 +99,7 @@ public:
};
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_mc(const char * filename = "rom_cordic.h") {
void generate_rom_header_mc(const char * filename) {
const CRomGeneratorMCHalfPi<In_W, NStages, Tq> rom;
FILE * rom_file = fopen(filename, "w");
@ -108,9 +108,17 @@ void generate_rom_header_mc(const char * filename = "rom_cordic.h") {
exit(EXIT_FAILURE);
}
// fprintf(rom_file, "#ifndef ROM_CORDIC\n#define ROM_CORDIC\n\n");
char upper_file_def[64];
snprintf(upper_file_def, 64, "CORDIC_ROMS_MC_%u_%u_%u", In_W, NStages, Tq);
fprintf(rom_file, "constexpr uint8_t rom_cordic[%d] = {\n ", rom.max_length);
char rom_name[64];
snprintf(rom_name, 64, "mc_%u_%u_%u", In_W, NStages, Tq);
fprintf(rom_file, "#ifndef %s\n#define %s\n\n", upper_file_def, upper_file_def);
fprintf(rom_file, "#include <cstdint>\n\n");
fprintf(rom_file, "namespace cordic_roms {\n");
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 ");
@ -119,11 +127,12 @@ void generate_rom_header_mc(const char * filename = "rom_cordic.h") {
}
fprintf(rom_file, "%3d};\n", uint16_t(rom.rom[rom.max_length - 1]));
// fprintf(rom_file, "#endif // ROM_CORDIC");
fprintf(rom_file, "\n} // namespace cordic_roms\n\n");
fprintf(rom_file, "#endif // %s\n\n", upper_file_def);
}
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_mc_raw(const char * filename = "rom_cordic.txt") {
void generate_rom_header_mc_raw(const char * filename) {
const CRomGeneratorMCHalfPi<In_W, NStages, Tq> rom;
FILE * rom_file = fopen(filename, "w");

View file

@ -0,0 +1,36 @@
/*
*
* 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 "RomGeneratorConst/RomGeneratorConst.hpp"
#include "RomGeneratorMCHalfPi/RomGeneratorMCHalfPi.hpp"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(int, char **) {
const char filename[] = "cordic_rom_mc_16_6_64.hpp";
generate_rom_header_mc<16, 6, 64>(filename);
return EXIT_SUCCESS;
}

View file

@ -0,0 +1,36 @@
/*
*
* 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 "RomGeneratorConst/RomGeneratorConst.hpp"
#include "RomGeneratorMCHalfPi/RomGeneratorMCHalfPi.hpp"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(int, char **) {
const char filename[] = "cordic_rom_@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@.hpp";
generate_rom_header_@ROM_TYPE@<@CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@>(filename);
return EXIT_SUCCESS;
}

View file

@ -145,27 +145,27 @@ int main(int argc, char * argv[]) {
} else {
switch (stages) {
case 2:
generate_rom_header_const_raw<16, 2, 64>("rom_cordic_const_W16_S2_Q64.txt");
generate_rom_header_cst_raw<16, 2, 64>("rom_cordic_const_W16_S2_Q64.txt");
checkConst<2>();
break;
case 3:
generate_rom_header_const_raw<16, 3, 64>("rom_cordic_const_W16_S3_Q64.txt");
generate_rom_header_cst_raw<16, 3, 64>("rom_cordic_const_W16_S3_Q64.txt");
checkConst<3>();
break;
case 4:
generate_rom_header_const_raw<16, 4, 64>("rom_cordic_const_W16_S4_Q64.txt");
generate_rom_header_cst_raw<16, 4, 64>("rom_cordic_const_W16_S4_Q64.txt");
checkConst<4>();
break;
case 5:
generate_rom_header_const_raw<16, 5, 64>("rom_cordic_const_W16_S5_Q64.txt");
generate_rom_header_cst_raw<16, 5, 64>("rom_cordic_const_W16_S5_Q64.txt");
checkConst<5>();
break;
case 6:
generate_rom_header_const_raw<16, 6, 64>("rom_cordic_const_W16_S6_Q64.txt");
generate_rom_header_cst_raw<16, 6, 64>("rom_cordic_const_W16_S6_Q64.txt");
checkConst<6>();
break;
case 7:
generate_rom_header_const_raw<16, 7, 64>("rom_cordic_const_W16_S7_Q64.txt");
generate_rom_header_cst_raw<16, 7, 64>("rom_cordic_const_W16_S7_Q64.txt");
checkConst<7>();
break;
default:
@ -208,27 +208,27 @@ int main(int argc, char * argv[]) {
} else {
switch (stages) {
case 2:
generate_rom_header_const<16, 2, 64>("rom_cordic_const_W16_S2_Q64.hpp");
generate_rom_header_cst<16, 2, 64>("rom_cordic_const_W16_S2_Q64.hpp");
checkConst<2>();
break;
case 3:
generate_rom_header_const<16, 3, 64>("rom_cordic_const_W16_S3_Q64.hpp");
generate_rom_header_cst<16, 3, 64>("rom_cordic_const_W16_S3_Q64.hpp");
checkConst<3>();
break;
case 4:
generate_rom_header_const<16, 4, 64>("rom_cordic_const_W16_S4_Q64.hpp");
generate_rom_header_cst<16, 4, 64>("rom_cordic_const_W16_S4_Q64.hpp");
checkConst<4>();
break;
case 5:
generate_rom_header_const<16, 5, 64>("rom_cordic_const_W16_S5_Q64.hpp");
generate_rom_header_cst<16, 5, 64>("rom_cordic_const_W16_S5_Q64.hpp");
checkConst<5>();
break;
case 6:
generate_rom_header_const<16, 6, 64>("rom_cordic_const_W16_S6_Q64.hpp");
generate_rom_header_cst<16, 6, 64>("rom_cordic_const_W16_S6_Q64.hpp");
checkConst<6>();
break;
case 7:
generate_rom_header_const<16, 7, 64>("rom_cordic_const_W16_S7_Q64.hpp");
generate_rom_header_cst<16, 7, 64>("rom_cordic_const_W16_S7_Q64.hpp");
checkConst<7>();
break;
default:

View file

@ -17,4 +17,4 @@
*
*/
#include "CCordicRotateHalfPiRom.hpp"
#include "CCordicRotateConstexpr.hpp"

View file

@ -32,27 +32,28 @@
#include "RomGeneratorConst/RomGeneratorConst.hpp"
// ``` GNU Octave
// kn_values(X) = prod(1 ./ abs(1 + 1j * 2.^ (-(0:X))))
// ```
static constexpr double kn_values[7] = {0.70710678118655, 0.632455532033680, 0.613571991077900, 0.608833912517750, 0.607648256256170, 0.607351770141300, 0.607277644093530};
template <unsigned TIn_W, unsigned TIn_I, unsigned TNStages, unsigned Tq>
class CCordicRotateRomHalfPi {
template <unsigned TIn_W, unsigned TIn_I, unsigned Tnb_stages, unsigned Tq>
class CCordicRotateConstexpr {
static_assert(TIn_W > 0, "Inputs can't be on zero bits.");
static_assert(TNStages < 8, "7 stages of CORDIC is the maximum supported.");
static_assert(TNStages > 1, "2 stages of CORDIC is the minimum.");
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.");
public:
static constexpr const CRomGeneratorConst<TIn_W, TNStages, Tq> & rom_cordic {};
// ``` GNU Octave
// kn_values(X) = prod(1 ./ abs(1 + 1j * 2.^ (-(0:X))))
// ```
static constexpr double kn_values[7] = {
0.70710678118655, 0.632455532033680, 0.613571991077900,
0.608833912517750, 0.607648256256170, 0.607351770141300, 0.607277644093530};
static constexpr const CRomGeneratorConst<TIn_W, Tnb_stages, Tq> & rom_cordic {};
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 NStages = TNStages;
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 uint64_t kn_i = uint64_t(kn_values[NStages - 1] * double(1U << 3)); // 3 bits are enough
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));
@ -73,7 +74,7 @@ public:
B = -B;
}
for (uint8_t u = 1; u < NStages + 1; u++) {
for (uint8_t u = 1; u < nb_stages + 1; u++) {
mask = mask >> 1;
const int64_t Ri = (R & mask) == mask ? 1 : -1;
@ -88,7 +89,7 @@ public:
#ifndef __SYNTHESIS__
static constexpr double scale_cordic(double in) {
return in * kn_values[NStages - 1];
return in * kn_values[nb_stages - 1];
}
static constexpr std::complex<double> cordic(std::complex<double> x_in,
@ -112,14 +113,14 @@ public:
const ap_uint<8> & counter,
ap_int<Out_W> & re_out, ap_int<Out_W> & im_out) {
const ap_uint<6 + 1> R = (rom_cordic.rom[counter] >> (7 - NStages));
const ap_uint<nb_stages + 1> R = (rom_cordic.rom[counter] >> (7 - nb_stages));
ap_int<Out_W> A = bool(R[NStages]) ? ap_int<In_W>(-re_in) : re_in;
ap_int<Out_W> B = bool(R[NStages]) ? ap_int<In_W>(-im_in) : im_in;
ap_int<Out_W> A = bool(R[nb_stages]) ? ap_int<In_W>(-re_in) : re_in;
ap_int<Out_W> B = bool(R[nb_stages]) ? ap_int<In_W>(-im_in) : im_in;
for (uint8_t u = 1; u < 6 + 1; u++) { // 6 stages
for (uint8_t u = 1; u < nb_stages + 1; u++) { // nb_stages stages
const bool Ri = bool(R[NStages - u]);
const bool Ri = bool(R[nb_stages - u]);
// Results in (X / 2^(u - 1)), meaning only the
// Out_W - u LSBs are meaninfull in shifted_X
@ -146,11 +147,13 @@ public:
re_out = A;
im_out = B;
}
constexpr CCordicRotateConstexpr() = default;
};
#if 0
template <>
inline void CCordicRotateRomHalfPi<16, 4, 6, 64>::cordic(
inline void CCordicRotateConstexpr<16, 4, 6, 64>::cordic(
const ap_int<16> & re_in, const ap_int<16> & im_in,
const ap_uint<8> & counter,
ap_int<Out_W> & re_out, ap_int<Out_W> & im_out) const {

View file

@ -0,0 +1 @@
#include "CCordicRotateRom/CCordicRotateRom_@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@.hpp"

View file

@ -0,0 +1,158 @@
/*
*
* 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/>.
*
*/
#ifndef C_CORDIC_ROTATE_ROM_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@
#define C_CORDIC_ROTATE_ROM_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <complex>
#include <ap_fixed.h>
#include <ap_int.h>
#include "CCordicRotateRomTemplate.hpp"
#include "CordicRoms/cordic_rom_@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@.hpp"
#ifndef KN_STATIC_TABLE_DEFINED
#define KN_STATIC_TABLE_DEFINED 1
// ``` GNU Octave
// kn_values(X) = prod(1 ./ abs(1 + 1j * 2.^ (-(0:X))))
// ```
static constexpr double kn_values[7] = {
0.70710678118655, 0.632455532033680, 0.613571991077900,
0.608833912517750, 0.607648256256170, 0.607351770141300, 0.607277644093530};
#endif // KN_STATIC_TABLE_DEFINED
template <unsigned TIn_I>
class CCordicRotateRom<TIn_I, @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@> {
static_assert(@CORDIC_W@ > 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.");
public:
static constexpr unsigned In_W = @CORDIC_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 = @CORDIC_STAGES@;
static constexpr unsigned q = @CORDIC_Q@;
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 int64_t scale_cordic(int64_t in) {
return in * kn_i / 8U;
}
static constexpr std::complex<int64_t> cordic(std::complex<int64_t> x_in,
uint8_t counter) {
int64_t A = x_in.real();
int64_t B = x_in.imag();
const uint8_t R = cordic_roms::@ROM_TYPE@_@CORDIC_W@_@CORDIC_STAGES@_@CORDIC_Q@[counter];
uint8_t mask = 0x80;
if ((R & mask) == mask) {
A = -A;
B = -B;
}
for (uint8_t u = 1; u < nb_stages + 1; u++) {
mask = mask >> 1;
const int64_t Ri = (R & mask) == mask ? 1 : -1;
const int64_t I = A + Ri * (B / int64_t(1U << (u - 1)));
B = B - Ri * (A / int64_t(1U << (u - 1)));
A = I;
}
return {(A), (B)};
}
#ifndef __SYNTHESIS__
static constexpr double scale_cordic(double in) {
return in * kn_values[nb_stages - 1];
}
static constexpr std::complex<double> cordic(std::complex<double> x_in,
uint8_t counter) {
const std::complex<int64_t> fx_x_in(int64_t(x_in.real() * double(in_scale_factor)),
int64_t(x_in.imag() * double(in_scale_factor)));
const std::complex<int64_t> fx_out = cordic(fx_x_in, counter);
return {scale_cordic(double(fx_out.real())) / double(out_scale_factor), scale_cordic(double(fx_out.imag())) / double(out_scale_factor)};
}
#endif
template <unsigned ap_W>
static ap_int<ap_W> scale_cordic(const ap_int<ap_W> & in) {
const ap_int<ap_W + 3> tmp = in * ap_uint<3>(kn_i);
return ap_int<ap_W>(tmp >> 3);
}
static void cordic(const ap_int<In_W> & re_in, const ap_int<In_W> & im_in,
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@[counter] >> (7 - nb_stages));
ap_int<Out_W> A = bool(R[nb_stages]) ? ap_int<In_W>(-re_in) : re_in;
ap_int<Out_W> B = bool(R[nb_stages]) ? ap_int<In_W>(-im_in) : im_in;
for (uint8_t u = 1; u < nb_stages + 1; u++) { // nb_stages stages
const bool Ri = bool(R[nb_stages - u]);
// Results in (X / 2^(u - 1)), meaning only the
// Out_W - u LSBs are meaninfull in shifted_X
// Can't use range access since 11111111 (-1) would become 00001111 (15).
// Would be possible if the loop is manually unrolled, to predict bitsize,
// thus directly put 1111 into 4 bits (so still -1).
const ap_int<Out_W> shifted_A = A >> (u - 1); // A(Out_W - 1, u - 1);
const ap_int<Out_W> shifted_B = B >> (u - 1); // B(Out_W - 1, u - 1);
const ap_int<Out_W> arc_step_A
= Ri
? ap_int<Out_W>(-shifted_A)
: shifted_A;
const ap_int<Out_W> arc_step_B
= Ri
? shifted_B
: ap_int<Out_W>(-shifted_B);
const ap_int<Out_W + 1> I = A + arc_step_B;
B = B + arc_step_A;
A = I;
}
re_out = A;
im_out = B;
}
constexpr CCordicRotateRom() = default;
};
#endif // C_CORDIC_ROTATE_ROM_W_STAGES_Q

View file

@ -0,0 +1,35 @@
/*
*
* 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/>.
*
*/
#ifndef C_CORDIC_ROTATE_ROM_TEMPLATE
#define C_CORDIC_ROTATE_ROM_TEMPLATE
enum rom_types {
mc,
cst
};
template <unsigned TIn_I, rom_types type, unsigned TIn_W, unsigned Tnb_stages, unsigned Tq>
class CCordicRotateRom {
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.");
};
#endif // C_CORDIC_ROTATE_ROM_TEMPLATE

View file

@ -17,14 +17,14 @@
*
*/
#include "CCordicRotate.hpp"
#include "CCordicRotateSmart.hpp"
#define uint2int(sz, in) ((in & (1U << sz)) == (1U << sz) \
? static_cast<short>(~in + 1) \
: static_cast<short>(in))
template <>
void CCordicRotate<8, 14, 4, 17, 5, 19, 7, 12>::process(
void CCordicRotateSmart<8, 14, 4, 17, 5, 19, 7, 12>::process(
const ap_fixed<14, 4> & fx_angle,
const ap_fixed<17, 5> & fx_re_in,
const ap_fixed<17, 5> & fx_im_in,

View file

@ -63,7 +63,7 @@ template <uint8_t N_STAGES,
uint8_t OUT_W,
uint8_t OUT_I,
uint8_t ATAN_I>
class CCordicRotate {
class CCordicRotateSmart {
public:
static constexpr const CAtanLUT<N_STAGES, uint64_t, ATAN_I> & atanLUT = CAtanLUT<N_STAGES, uint64_t, ATAN_I>();
@ -74,8 +74,8 @@ public:
ap_fixed<OUT_W, OUT_I> & fx_re_out,
ap_fixed<OUT_W, OUT_I> & fx_im_out);
CCordicRotate() {}
virtual ~CCordicRotate() {};
CCordicRotateSmart() {}
virtual ~CCordicRotateSmart() {};
};
#endif

View file

@ -0,0 +1,269 @@
/*
*
* 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@.hpp"
#include <fstream>
#include <iostream>
#include <catch2/catch.hpp>
using namespace std;
using Catch::Matchers::Floating::WithinAbsMatcher;
typedef CCordicRotateRom<4, @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@> cordic_rom;
TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@) works with C-Types", "[CORDIC]") {
SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@") {
static constexpr cordic_rom cordic {};
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<double> values_in[n_lines];
complex<double> values_out[n_lines];
complex<double> results[n_lines];
ofstream FILE;
ifstream INPUT(input_fn);
// Init test vector
for (unsigned i = 0; i < n_lines; i++) {
double a, b, r;
INPUT >> a >> b >> r;
const complex<double> c {a, b};
values_in[i] = c;
constexpr double rotation = M_PI / 2;
constexpr double q = cordic_rom::q;
const complex<double> e = exp(complex<double>(0., rotation / q * (i & 255)));
results[i] = c * e;
}
INPUT.close();
// Save the results to a file
FILE.open("results.dat");
constexpr double abs_margin = double(1 << cordic.Out_I) * 2. / 100.;
// Executing the encoder
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
values_out[iter] = cordic_rom::cordic(values_in[iter], (iter & 255));
// Display the results
// cout << "Series " << iter;
// cout << " Outcome: ";
FILE << values_out[iter].real() << " " << values_out[iter].imag() << " " << results[iter].real() << " " << results[iter].imag() << endl;
REQUIRE_THAT(values_out[iter].real(), WithinAbsMatcher(results[iter].real(), abs_margin));
REQUIRE_THAT(values_out[iter].imag(), WithinAbsMatcher(results[iter].imag(), abs_margin));
}
FILE.close();
// Compare the results file with the golden results
// int retval = 0;
// Return 0 if the test passed
}
}
TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@) works with AP-Types", "[CORDIC]") {
constexpr unsigned n_lines = 100000;
SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@") {
static constexpr cordic_rom cordic {};
string input_fn = "../data/input.dat";
constexpr double rotation = M_PI / 2;
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];
ofstream out_stream;
ifstream INPUT(input_fn);
// Init test vector
for (unsigned i = 0; i < n_lines; i++) {
double a, b, r;
INPUT >> 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();
}
INPUT.close();
// Save the results to a file
out_stream.open("results_ap.dat");
// FILE * romf = fopen("rom.dat", "w");
constexpr double abs_margin = double(1 << cordic.Out_I) * 2. / 100.;
// Executing the encoder
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
const uint8_t counter = uint8_t(iter & cnt_mask);
// if (iter < cnt_mask + 1)
// fprintf(romf, "%03d\n", (uint16_t) cordic.rom_cordic.rom[counter]);
cordic_rom::cordic(
values_re_in[iter], values_im_in[iter],
counter,
values_re_out[iter], values_im_out[iter]);
// Display the results
// cout << "Series " << iter;
// cout << " Outcome: ";
out_stream << values_re_out[iter].to_int64() << " " << values_im_out[iter].to_int64() << " " << results_re[iter] << " " << results_im[iter] << endl;
REQUIRE_THAT(values_re_out[iter].to_double() * 5. / 8. / cordic_rom::out_scale_factor, WithinAbsMatcher(results_re[iter], abs_margin));
REQUIRE_THAT(values_im_out[iter].to_double() * 5. / 8. / cordic_rom::out_scale_factor, WithinAbsMatcher(results_im[iter], abs_margin));
}
out_stream.close();
// fclose(romf);
// Compare the results file with the golden results
// int retval = 0;
// Return 0 if the test passed
}
SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - internal scaling") {
static constexpr cordic_rom cordic {};
string input_fn = "../data/input.dat";
constexpr double rotation = M_PI / 2;
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];
ofstream out_stream;
ifstream INPUT(input_fn);
// Init test vector
for (unsigned i = 0; i < n_lines; i++) {
double a, b, r;
INPUT >> 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();
}
INPUT.close();
// Save the results to a file
out_stream.open("results_ap.dat");
// FILE * romf = fopen("rom.dat", "w");
constexpr double abs_margin = double(1 << cordic.Out_I) * 2. / 100.;
// Executing the encoder
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
const uint8_t counter = uint8_t(iter & cnt_mask);
// if (iter < cnt_mask + 1)
// fprintf(romf, "%03d\n", (uint16_t) cordic.rom_cordic.rom[counter]);
cordic_rom::cordic(
values_re_in[iter], values_im_in[iter],
counter,
values_re_out[iter], values_im_out[iter]);
// Display the results
// cout << "Series " << iter;
// cout << " Outcome: ";
out_stream << values_re_out[iter].to_int64() << " " << values_im_out[iter].to_int64() << " " << results_re[iter] << " " << results_im[iter] << endl;
REQUIRE_THAT(cordic_rom::scale_cordic<Out_W>(values_re_out[iter]).to_double() / cordic_rom::out_scale_factor,
WithinAbsMatcher(results_re[iter],
abs_margin));
REQUIRE_THAT(cordic_rom::scale_cordic<Out_W>(values_im_out[iter]).to_double() / cordic_rom::out_scale_factor,
WithinAbsMatcher(results_im[iter],
abs_margin));
}
out_stream.close();
// fclose(romf);
// Compare the results file with the golden results
// int retval = 0;
// Return 0 if the test passed
}
}
TEST_CASE("ROM-based Cordic (TPL @ROM_TYPE@, @CORDIC_W@, @CORDIC_STAGES@, @CORDIC_Q@) constexpr are evaluated during compilation.", "[CORDIC]") {
SECTION("W:@CORDIC_W@ - I:4 - Stages:@CORDIC_STAGES@ - q:@CORDIC_Q@ - C-Types") {
constexpr complex<int64_t> value_in = (1U << 12) * 97;
constexpr uint8_t angle = 169;
constexpr complex<int64_t> res1 = cordic_rom::cordic(value_in, angle);
constexpr complex<int64_t> res2 = cordic_rom::cordic(value_in, angle);
static_assert(res1 == res2, "Test");
REQUIRE_FALSE(res1 == cordic_rom::cordic(complex<int64_t>(1, 0), angle));
REQUIRE(res1 == cordic_rom::cordic(value_in, angle));
}
}

View file

@ -17,8 +17,8 @@
*
*/
#include "CCordicRotate/CCordicRotate.hpp"
#include "CCordicRotateHalfPiRom/CCordicRotateHalfPiRom.hpp"
#include "CCordicRotateSmart/CCordicRotateSmart.hpp"
#include "CCordicRotateConstexpr/CCordicRotateConstexpr.hpp"
#include <fstream>
#include <iostream>
@ -28,7 +28,7 @@ using namespace std;
using Catch::Matchers::Floating::WithinAbsMatcher;
typedef CCordicRotate<8, 14, 4, 17, 5, 19, 7, 12> cordic_legacy;
typedef CCordicRotateSmart<8, 14, 4, 17, 5, 19, 7, 12> cordic_legacy;
TEST_CASE("Adaptive CORDIC work as intended", "[!hide][WIP]") {
@ -92,7 +92,7 @@ TEST_CASE("Adaptive CORDIC work as intended", "[!hide][WIP]") {
// Return 0 if the test passed
}
typedef CCordicRotateRomHalfPi<16, 4, 6, 64> cordic_rom;
typedef CCordicRotateConstexpr<16, 4, 6, 64> cordic_rom;
TEST_CASE("ROM-based Cordic works with C-Types", "[CORDIC]") {
SECTION("W:16 - I:4 - Stages:6 - q:64") {