Working AP template cordic

This commit is contained in:
Camille Monière 2022-02-11 18:17:13 +01:00
parent d46fbc40b6
commit f2a7c0c886
Signed by: moniere
GPG key ID: 188DD5B072181C0F
13 changed files with 950 additions and 200011 deletions

View file

@ -59,9 +59,12 @@ if ((NOT EXISTS ${AP_INCLUDE_DIR}/ap_int.h) OR (NOT EXISTS
)
endif ()
add_library (cordic STATIC sources/CCordicRotate/CCordicRotate.cpp)
add_subdirectory(RomGenerators)
add_library (cordic STATIC sources/CCordicRotate/CCordicRotate.cpp sources/CCordicRotateHalfPiRom/CCordicRotateHalfPiRom.cpp)
target_include_directories (cordic PUBLIC sources)
target_include_directories (cordic SYSTEM PUBLIC ${AP_INCLUDE_DIR})
target_link_libraries(cordic PUBLIC romgen)
find_package(Catch2 REQUIRED)
@ -70,3 +73,7 @@ target_link_libraries(catch_common PUBLIC Catch2::Catch2)
add_executable(cordic_tb sources/tb/cordic_tb.cpp)
target_link_libraries(cordic_tb PUBLIC cordic catch_common)
include(CTest)
include(Catch)
catch_discover_tests(cordic_tb)

View file

@ -0,0 +1,28 @@
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_EXPORT_COMPILE_COMMANDS true)
project(
CordicRomGenerator
LANGUAGES CXX
VERSION 0.1)
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()
add_executable(rom_generator sources/main.cpp)
target_link_libraries(rom_generator PUBLIC romgen)

View file

@ -0,0 +1 @@
#include "RomGeneratorConst.hpp"

View file

@ -0,0 +1,148 @@
#ifndef _ROM_GENERATOR_CONST_
#define _ROM_GENERATOR_CONST_
#include <array>
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
#include <cassert>
template <unsigned In_W, unsigned NStages, unsigned Tq>
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.");
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 = half_pi;
static constexpr double q = Tq;
static constexpr uint32_t max_length = 4 * Tq; // 2pi / (pi / 2) * q
static constexpr int64_t scale_factor = int64_t(1U << (In_W - 1)); // 2pi / (pi / 2) * q
static constexpr double atanDbl[28] {
0.78539816339745, 0.46364760900081, 0.24497866312686, 0.12435499454676,
0.06241880999596, 0.03123983343027, 0.01562372862048, 0.00781234106010,
0.00390623013197, 0.00195312251648, 0.00097656218956, 0.00048828121119,
0.00024414062015, 0.00012207031189, 0.00006103515617, 0.00003051757812,
0.00001525878906, 0.00000762939453, 0.00000381469727, 0.00000190734863,
0.00000095367432, 0.00000047683716, 0.00000023841858, 0.00000011920929,
0.00000005960464, 0.00000002980232, 0.00000001490116, 0.00000000745058};
private:
constexpr uint8_t cordic_rom_gen(double rot_in) const {
double A = scale_factor - 1;
double B = 0;
uint8_t R = 0;
uint8_t mask = 0x80;
double beta = rot_in;
#if 0
printf("Step 0 - %03u : %02x : %8lf\n", R, R, beta);
#endif
if ((beta < -two_pi) || (two_pi <= beta)) {
fprintf(stderr, "rotation must be inside ] -2*pi; 2*pi ]");
exit(EXIT_FAILURE);
}
if ((beta <= -pi) || (beta > pi)) {
beta = beta < 0. ? beta + two_pi : beta - two_pi;
}
if ((beta < -half_pi) || (beta > half_pi)) {
R = R | mask;
beta = beta < 0 ? beta + pi : beta - pi;
// A = -A;
// B = -B;
} else {
R = R & (~mask);
}
for (uint8_t u = 1; u < NStages + 1; u++) {
#if 0
printf("Step %d - %03u : %02x : %8lf\n", u, R, R, beta);
#endif
const uint8_t mask = (1U << (7 - u));
const uint8_t nmask = ~mask;
assert((mask & nmask) == 0x00);
assert((mask | nmask) == 0xFF);
const double sigma = beta < 0 ? -1. : 1;
R = beta < 0 ? R | mask : R & ~mask;
const double factor = sigma / double(1U << (u - 1));
const double I = A + B * factor;
B = B - A * factor;
A = I;
beta = beta - sigma * atanDbl[u - 1];
}
#if 0
printf("\nFINAL - %03u : %02x : %8lf\n\n", R, R, beta);
#endif
return R;
}
public:
uint8_t rom[max_length];
constexpr CRomGeneratorConst() {
for (unsigned n = 0; n < max_length; n++) {
const double chip_rotation = rotation / double(q) * double(n);
rom[n] = cordic_rom_gen(chip_rotation);
}
}
};
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;
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
perror("Can't open the rom file for writing.");
exit(EXIT_FAILURE);
}
// fprintf(rom_file, "#ifndef ROM_CORDIC\n#define ROM_CORDIC\n\n");
fprintf(rom_file, "constexpr uint8_t rom_cordic[%d] = {\n ", rom.max_length);
for (uint16_t u = 0; u < rom.max_length - 1; u++) {
if (((u & 7) == 0) && u != 0) {
fprintf(rom_file, "\n ");
}
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");
}
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;
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
perror("Can't open the rom file for writing.");
exit(EXIT_FAILURE);
}
for (uint16_t u = 0; u < rom.max_length - 1; u++) {
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

@ -0,0 +1 @@
#include "RomGeneratorMCHalfPi.hpp"

View file

@ -0,0 +1,123 @@
#ifndef _ROM_GENERATOR_MC_HALF_PI
#define _ROM_GENERATOR_MC_HALF_PI
#include <climits>
#include <cmath>
#include <complex>
#include <cstdint>
template <unsigned In_W, unsigned NStages, unsigned Tq>
class CRomGeneratorMCHalfPi {
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.");
public:
static constexpr double rotation = M_PI_2;
static constexpr double q = Tq;
static constexpr uint32_t max_length = 4 * Tq; // 2pi / (pi / 2) * q
static constexpr int64_t scale_factor = int64_t(1U << (In_W - 1)); // 2pi / (pi / 2) * q
private:
constexpr std::complex<int64_t> cordic_MC(const 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 = counter;
uint8_t mask = 0x80;
if ((R & mask) == mask) {
A = -A;
B = -B;
}
for (uint16_t u = 1; u < NStages + 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};
}
public:
uint8_t rom[max_length];
CRomGeneratorMCHalfPi() {
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)));
const std::complex<int64_t> x {re_x, im_x};
double error = 1000.;
uint8_t rom_v = 0x0;
std::complex<double> res;
for (uint32_t v = 0; v < max_length; v++) {
const std::complex<int64_t> res_int = cordic_MC(x, v);
const std::complex<double> res_dbl(double(res_int.real()) / double(scale_factor - 1),
double(res_int.imag()) / double(scale_factor - 1));
const double curr_error = std::abs(std::arg(res_dbl));
if (curr_error < error) {
error = curr_error;
rom_v = uint8_t(v);
res = res_dbl;
}
}
rom[n] = rom_v;
}
}
};
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_mc(const char * filename = "rom_cordic.h") {
const CRomGeneratorMCHalfPi<In_W, NStages, Tq> rom;
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
perror("Can't open the rom file for writing.");
exit(EXIT_FAILURE);
}
// fprintf(rom_file, "#ifndef ROM_CORDIC\n#define ROM_CORDIC\n\n");
fprintf(rom_file, "constexpr uint8_t rom_cordic[%d] = {\n ", rom.max_length);
for (uint16_t u = 0; u < rom.max_length - 1; u++) {
if (((u & 7) == 0) && u != 0) {
fprintf(rom_file, "\n ");
}
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");
}
template <unsigned In_W, unsigned NStages, unsigned Tq>
void generate_rom_header_mc_raw(const char * filename = "rom_cordic.txt") {
const CRomGeneratorMCHalfPi<In_W, NStages, Tq> rom;
FILE * rom_file = fopen(filename, "w");
if (!bool(rom_file)) {
perror("Can't open the rom file for writing.");
exit(EXIT_FAILURE);
}
for (uint16_t u = 0; u < rom.max_length - 1; u++) {
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_MC_HALF_PI

View file

@ -0,0 +1,223 @@
#include "RomGeneratorConst/RomGeneratorConst.hpp"
#include "RomGeneratorMCHalfPi/RomGeneratorMCHalfPi.hpp"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
template <unsigned NStages>
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();
const uint8_t R = rom_cordic[counter];
uint8_t mask = 0x80;
if ((R & mask) == mask) {
A = -A;
B = -B;
}
for (uint8_t u = 1; u < NStages + 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};
}
template <unsigned NStages>
void checkMC() {
const CRomGeneratorMCHalfPi<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;
}
}
template <unsigned NStages>
void checkConst() {
constexpr CRomGeneratorConst<16, NStages, 64> rom;
string fn = "result_const_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;
}
}
int main(int argc, char * argv[]) {
uint8_t stages;
bool use_mc;
bool use_txt;
switch (argc) {
case 1:
stages = 6;
use_mc = false;
use_txt = false;
break;
case 2:
stages = stoi(string(argv[1]));
use_mc = false;
use_txt = false;
break;
case 3:
stages = stoi(string(argv[1]));
use_mc = stoi(string(argv[2])) > 0;
use_txt = false;
break;
case 4:
stages = stoi(string(argv[1]));
use_mc = stoi(string(argv[2])) > 0;
use_txt = stoi(string(argv[3])) > 0;
break;
default:
cerr << "Error, too many arguments. Expected at most 3." << endl;
exit(EXIT_FAILURE);
}
if (use_txt) {
if (use_mc) {
switch (stages) {
case 2:
generate_rom_header_mc_raw<16, 2, 64>("rom_cordic_mc_W16_S2_Q64.txt");
checkMC<2>();
break;
case 3:
generate_rom_header_mc_raw<16, 3, 64>("rom_cordic_mc_W16_S3_Q64.txt");
checkMC<3>();
break;
case 4:
generate_rom_header_mc_raw<16, 4, 64>("rom_cordic_mc_W16_S4_Q64.txt");
checkMC<4>();
break;
case 5:
generate_rom_header_mc_raw<16, 5, 64>("rom_cordic_mc_W16_S5_Q64.txt");
checkMC<5>();
break;
case 6:
generate_rom_header_mc_raw<16, 6, 64>("rom_cordic_mc_W16_S6_Q64.txt");
checkMC<6>();
break;
case 7:
generate_rom_header_mc_raw<16, 7, 64>("rom_cordic_mc_W16_S7_Q64.txt");
checkMC<7>();
break;
default:
cerr << "Error, no less than 2 stages, no more than 7." << endl;
exit(EXIT_FAILURE);
}
} else {
switch (stages) {
case 2:
generate_rom_header_const_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");
checkConst<3>();
break;
case 4:
generate_rom_header_const_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");
checkConst<5>();
break;
case 6:
generate_rom_header_const_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");
checkConst<7>();
break;
default:
cerr << "Error, no less than 2 stages, no more than 7." << endl;
exit(EXIT_FAILURE);
}
}
} else {
if (use_mc) {
switch (stages) {
case 2:
generate_rom_header_mc<16, 2, 64>("rom_cordic_mc_W16_S2_Q64.hpp");
checkMC<2>();
break;
case 3:
generate_rom_header_mc<16, 3, 64>("rom_cordic_mc_W16_S3_Q64.hpp");
checkMC<3>();
break;
case 4:
generate_rom_header_mc<16, 4, 64>("rom_cordic_mc_W16_S4_Q64.hpp");
checkMC<4>();
break;
case 5:
generate_rom_header_mc<16, 5, 64>("rom_cordic_mc_W16_S5_Q64.hpp");
checkMC<5>();
break;
case 6:
generate_rom_header_mc<16, 6, 64>("rom_cordic_mc_W16_S6_Q64.hpp");
checkMC<6>();
break;
case 7:
generate_rom_header_mc<16, 7, 64>("rom_cordic_mc_W16_S7_Q64.hpp");
checkMC<7>();
break;
default:
cerr << "Error, no less than 2 stages, no more than 7." << endl;
exit(EXIT_FAILURE);
}
} else {
switch (stages) {
case 2:
generate_rom_header_const<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");
checkConst<3>();
break;
case 4:
generate_rom_header_const<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");
checkConst<5>();
break;
case 6:
generate_rom_header_const<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");
checkConst<7>();
break;
default:
cerr << "Error, no less than 2 stages, no more than 7." << endl;
exit(EXIT_FAILURE);
}
}
}
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
#include "CCordicRotate.hpp"
#define uint2int(sz, in) ((in & (1U << sz)) == (1U << sz) \
? static_cast<short>(~in + 1) \
: static_cast<short>(in))
@ -8,14 +7,14 @@
template <>
void CCordicRotate<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,
const ap_fixed<17, 5> & fx_re_in,
const ap_fixed<17, 5> & fx_im_in,
ap_fixed<19, 7> & fx_re_out,
ap_fixed<19, 7> & fx_im_out) {
constexpr uint64_t sign_mask_14 = 0x2000; // 0bxxx xx10 0000 0000 0000
constexpr uint64_t sign_mask_17 = 0x10000; // 0bxx1 0000 0000 0000 0000
constexpr uint64_t sign_mask_19 = 0x10000; // 0b100 0000 0000 0000 0000
// constexpr uint64_t sign_mask_14 = 0x2000; // 0bxxx xx10 0000 0000 0000
// constexpr uint64_t sign_mask_17 = 0x10000; // 0bxx1 0000 0000 0000 0000
// constexpr uint64_t sign_mask_19 = 0x10000; // 0b100 0000 0000 0000 0000
const uint16_t angle_bits = fx_angle.bits_to_uint64();

View file

@ -0,0 +1,2 @@
#include "CCordicRotateHalfPiRom.hpp"

View file

@ -0,0 +1,176 @@
#ifndef C_CORDIC_ROTATE_ROM_HALF_PI_HPP
#define C_CORDIC_ROTATE_ROM_HALF_PI_HPP
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <complex>
#include <ap_fixed.h>
#include <ap_int.h>
#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 {
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.");
public:
static constexpr CRomGeneratorConst<TIn_W, TNStages, 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 uint64_t kn_i = uint64_t(kn_values[NStages - 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;
}
constexpr std::complex<int64_t> cordic(std::complex<int64_t> x_in,
uint8_t counter) const {
int64_t A = x_in.real();
int64_t B = x_in.imag();
const uint8_t R = rom_cordic.rom[counter];
uint8_t mask = 0x80;
if ((R & mask) == mask) {
A = -A;
B = -B;
}
for (uint8_t u = 1; u < NStages + 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[NStages - 1];
}
constexpr std::complex<double> cordic(std::complex<double> x_in,
uint8_t counter) const {
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);
}
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 {
const ap_uint<6 + 1> R = (rom_cordic.rom[counter] >> (7 - NStages));
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;
for (uint8_t u = 1; u < 6 + 1; u++) { // 6 stages
const bool Ri = bool(R[NStages - 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;
}
};
#if 0
template <>
inline void CCordicRotateRomHalfPi<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 {
const ap_uint<6 + 1> R = (rom_cordic.rom[counter.to_uint()] >> (7 - 6));
ap_int<Out_W> A = bool(R[6]) ? ap_int<16>(-re_in) : re_in;
ap_int<Out_W> B = bool(R[6]) ? ap_int<16>(-im_in) : im_in;
for (uint8_t u = 1; u < 6 + 1; u++) { // 6 stages
const bool Ri = bool(R[6 - 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 auto I = A + arc_step_B;
B = B + arc_step_A;
A = I;
}
re_out = A;
im_out = B;
}
#endif
#endif // C_CORDIC_ROTATE_ROM_HALF_PI_HPP

View file

@ -1,4 +1,5 @@
#include "CCordicRotate/CCordicRotate.hpp"
#include "CCordicRotateHalfPiRom/CCordicRotateHalfPiRom.hpp"
#include <fstream>
#include <iostream>
@ -6,11 +7,10 @@
using namespace std;
typedef CCordicRotate<8, 14, 4, 17, 5, 19, 7, 12> cordic_t;
using Catch::Matchers::Floating::WithinAbsMatcher;
TEST_CASE("_8_14_10_17_5_19_12_12") {
TEST_CASE("Adaptive CORDIC work as intended", "[!hide][WIP]") {
typedef CCordicRotate<8, 14, 4, 17, 5, 19, 7, 12> cordic_legacy;
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
@ -52,7 +52,9 @@ TEST_CASE("_8_14_10_17_5_19_12_12") {
for (unsigned iter = 0; iter < n_lines; iter++) {
// Execute
cordic_t::process(angles_in[iter], values_re_in[iter], values_im_in[iter], values_re_out[iter], values_im_out[iter]);
cordic_legacy::process(angles_in[iter],
values_re_in[iter], values_im_in[iter],
values_re_out[iter], values_im_out[iter]);
// Display the results
// cout << "Series " << iter;
@ -69,3 +71,232 @@ TEST_CASE("_8_14_10_17_5_19_12_12") {
// int retval = 0;
// Return 0 if the test passed
}
TEST_CASE("ROM-based Cordic works with C-Types", "[CORDIC]") {
SECTION("W:16 - I:4 - Stages:6 - q:64") {
typedef CCordicRotateRomHalfPi<16, 4, 6, 64> cordic_rom;
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 = cordic_rom::rom_cordic.rotation;
constexpr double q = cordic_rom::rom_cordic.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 cordic_rom cordic {};
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.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 works with AP-Types", "[CORDIC]") {
constexpr unsigned n_lines = 100000;
SECTION("W:16 - I:4 - Stages:6 - q:64") {
typedef CCordicRotateRomHalfPi<16, 4, 6, 64> cordic_rom;
string input_fn = "../data/input.dat";
constexpr double rotation = cordic_rom::rom_cordic.rotation;
constexpr double q = cordic_rom::rom_cordic.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 cordic_rom cordic {};
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.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:16 - I:4 - Stages:6 - q:64 - internal scaling") {
typedef CCordicRotateRomHalfPi<16, 4, 6, 64> cordic_rom;
string input_fn = "../data/input.dat";
constexpr double rotation = cordic_rom::rom_cordic.rotation;
constexpr double q = cordic_rom::rom_cordic.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 cordic_rom cordic {};
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.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
}
}