// -----------------------------------------------------------------------------
// (C) Bibix
// The content below includes confidential, proprietary information of
// Bibix. All use, disclosure, and/or reproduction is prohibited
// unless authorized in writing. All rights reserved.
// -----------------------------------------------------------------------------
// File         : tb_fft_r8_64p.cpp
// Description  : Testbench for 64-point (radix 2^3) FFT pipeline
// Author       : Sabih Gerez, Bibix
//                based on work by Rene Moll, DSE
// Creation date: November 18, 2011
// -----------------------------------------------------------------------------
// $Rev: 182 $
// $Author: sabih $
// $Date: 2011-11-22 20:27:04 +0100 (Tue, 22 Nov 2011) $
// $Log$
// -----------------------------------------------------------------------------



#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "fft_r8_64p.h"

#define WORD_LENGTH 16
#define IWORD_LENGTH 8
#define POINTS 64

/*
 * bit_reverse64
 * 
 * Fast bit reversal function for 6 bits numbers (0-63)
 */
unsigned char bit_reverse64(unsigned char value)
{
   unsigned char result = 0;

   if ( value & 0x20 ) result |= 0x01;
   if ( value & 0x10 ) result |= 0x02;
   if ( value & 0x08 ) result |= 0x04;
   if ( value & 0x04 ) result |= 0x08;
   if ( value & 0x02 ) result |= 0x10;
   if ( value & 0x01 ) result |= 0x20;

   return result;
}

/*
 * bit_reverse8
 * 
 * Fast bit reversal function for 3 bits numbers (0-7)
 */

unsigned char bit_reverse8(unsigned char value)
{
   unsigned char result = 0;

   if ( value & 0x04 ) result |= 0x01;
   if ( value & 0x02 ) result |= 0x02;
   if ( value & 0x01 ) result |= 0x04;

   return result;
}

/*
 * generate_twiddle
 * 
 * Fills a given vector with twiddle factors for a 64 point R2^3 FFT
 */
void generate_twiddle(double tw_real[], double tw_imag[])
{
  int nFactor    = 0;
  int nRemainder = 0;
  
  for (int i = 0; i < 64; i++) {
    nFactor = bit_reverse8(i/8);
    nRemainder = i % 8;

    tw_real[i] = cos(-2.0*3.1415926*double(nRemainder)*double(nFactor)/64.0);
    tw_imag[i] = sin(-2.0*3.1415926*double(nRemainder)*double(nFactor)/64.0);
  }
}

int main(int argc, char* argv[])
{
  FILE *pFileDec;		// input file for decimal input
  FILE *pFileTw;    // output file for the twiddle factors
  FILE *pFileIn;		// output file with input stream
  FILE *pFileOut;		// output file with output stream

  pFileDec = fopen("fft_r8_64p.dec", "r");

  if (pFileDec == NULL) {
    printf("Cannot open fft_r8_64p.dec for reading!\n");
    return 1;
  }

  pFileTw = fopen("fft_r8_64p.tw", "w");

  if (pFileTw == NULL) {
    printf("Cannot open fft_r8_64p.tw for writing!\n");
    return 1;
  }

  pFileIn = fopen("fft_r8_64p.in", "w");

  if (pFileIn == NULL) {
    printf("Cannot open fft_r8_64p.in for writing!\n");

    fclose(pFileDec);
    return 1;
  }

  pFileOut = fopen("fft_r8_64p.ref", "w");

  if (pFileOut == NULL) {
    printf("Cannot open fft_r8_64p.ref for writing!\n");

    fclose(pFileIn);
    fclose(pFileDec);
    return 1;
  }

  // Create an intance of the DUV
  fft_r8_64p oDUV;

  // Initialize DUV
  oDUV.reset();

  // Input reading
  char cBuffer[80];
  int nXreal, nXimag;
  double dXreal, dXimag;

  // Output variables
  i32    nYreal, nYimag;
  i32    nYbufReal[POINTS], nYbufImag[POINTS];
  double dYbufReal[POINTS], dYbufImag[POINTS];

  // Twiddles
  double dTwReal[64], dTwImag[64];
  int nTwReal, nTwImag;
  // total delay in first stage = (32+1) + (16+1) + (8+1) = 59
  // so, offset for twiddle counter: 64-59 = 5
  int nTwiddleCounter = 5;

  generate_twiddle(dTwReal, dTwImag);

  // Dump twiddles to file for the VHDL testbench
  for (int i = 0; i < 64; i++) {
    to_signed(dTwReal[i], nTwReal, WORD_LENGTH, IWORD_LENGTH);
    to_signed(dTwImag[i], nTwImag, WORD_LENGTH, IWORD_LENGTH);

    fprintf(pFileTw,  "%d %d\n", nTwReal, nTwImag);
  }

  // control
  int i = 0;
  bool prologue = 1;

  // Iterate for each test vector
  while (fgets(cBuffer, 80, pFileDec) != NULL)  {
    // Read input
    sscanf(cBuffer, "%lf %lf", &dXreal, &dXimag);

    // Convert to signed
    to_signed(dXreal, nXreal, WORD_LENGTH, IWORD_LENGTH);
    to_signed(dXimag, nXimag, WORD_LENGTH, IWORD_LENGTH);

    to_signed(dTwReal[nTwiddleCounter], nTwReal, WORD_LENGTH, IWORD_LENGTH);
    to_signed(dTwImag[nTwiddleCounter], nTwImag, WORD_LENGTH, IWORD_LENGTH);

    // run one clock cycle of hardware
    oDUV.run(i32(nXreal), i32(nXimag), i32(nTwReal), i32(nTwImag), nYreal, nYimag);

    // Update twiddle selector
    if (nTwiddleCounter >= 63) {
      nTwiddleCounter = 0;
    } else {
      nTwiddleCounter++;
    }

    // dump inputs and outputs as integers on behalf of VHDL simulations
    // no bitreversal of output order here!
    fprintf(pFileIn,  "%d %d\n", nXreal, nXimag);
    fprintf(pFileOut, "%d %d\n", nYreal, nYimag);

    // control
    // skip the first POINTS+5 samples and then process in blocks of
    // POINTS
    if (prologue) {
       i = i+1;
       if (i >= (POINTS+5)) {
          prologue = false;
          i = 0;
       }
    }
    else {
       nYbufReal[i] = nYreal;
       nYbufImag[i] = nYimag;

       // convert to double
       dYbufReal[i] = signed_to_double(nYbufReal[i], WORD_LENGTH, IWORD_LENGTH);
       dYbufImag[i] = signed_to_double(nYbufImag[i], WORD_LENGTH, IWORD_LENGTH);

       // check for end of block
       if (i >= (POINTS-1)) {
          // Perform bit reversal
          double dYrealRev[POINTS], dYimagRev[POINTS];
          for (int j = 0; j< POINTS; j++) {
            int nIndex = bit_reverse64(j);

            dYrealRev[nIndex] = dYbufReal[j];
            dYimagRev[nIndex] = dYbufImag[j];
          }

          memcpy(dYbufReal, dYrealRev, sizeof(dYbufReal));
          memcpy(dYbufImag, dYimagRev, sizeof(dYbufImag));

          // Write output (floating point to screen)
          printf("\nNew output block:\n");
          for (int j = 0; j < POINTS; j++) 
            printf ("Bin[%2d]: %+10.6f %+10.6fi; abs_val = %9.6f\n", j,
                    dYbufReal[j], 
                    dYbufImag[j],
                    sqrt(dYbufReal[j]*dYbufReal[j] + 
                         dYbufImag[j]*dYbufImag[j]));
          
         i = 0;
       }
       else {
         i = i + 1;
       }
    }
          
  }

  // close files
  fclose(pFileDec);
  fclose(pFileIn);
  fclose(pFileOut);

  printf("OK! End of input file reached. Stopping simulation.\n");
}
