// -----------------------------------------------------------------------------
// (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_r2_16p.cpp
// Description  : Testbench for 16-point parallel FFT 
// Author       : Sabih Gerez, Bibix
//                based on work by Rene Moll, DSE
// Creation date: September 1, 2011
// -----------------------------------------------------------------------------
// $Rev: 163 $
// $Author: sabih $
// $Date: 2011-09-11 00:49:35 +0200 (Sun, 11 Sep 2011) $
// $Log$
// -----------------------------------------------------------------------------

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

#include "fft_r2_16p.h"

#define WORD_LENGTH 10
#define IWORD_LENGTH 6
#define POINTS 16

/*
 * bit_reverse16
 * 
 * Fast bit reversal function for 4 bits numbers (0-15)
 */
unsigned char bit_reverse16(unsigned char value)
{
   unsigned char result = 0;

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

   return result;
}

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

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

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

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

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

    fclose(pFileDec);
    return 1;
  }

  pFileOut = fopen("fft_r2_16p.out", "w");

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

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

  // Create an intance of the DUV
  fft_r2_16p oDUV;

  // Initialize DUV
  oDUV.reset();

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

  // Output variables
  i32 nYreal[POINTS],    nYimag[POINTS];
  double dYreal[POINTS], dYimag[POINTS];

  // Iterate for each test vector
  while (fgets(cBuffer, 80, pFileDec) != NULL)  {
    // Read input
    for (int i = 0; i < POINTS; i++) {
      sscanf(cBuffer, "%lf %lf", &dXreal[i], &dXimag[i]);
      // read one more line, but leave one line to be read by while 
      if (i < POINTS - 1) {
         if (fgets(cBuffer, 80, pFileDec) == NULL) {
            printf ("Unexpected end of input file, aborting ...");
            return 1;
         }
      }
    }

    // Convert to signed
    for (int i = 0; i < POINTS; i++) {
      to_signed(dXreal[i], nXreal[i], WORD_LENGTH, IWORD_LENGTH);
      to_signed(dXimag[i], nXimag[i], WORD_LENGTH, IWORD_LENGTH);
    }

    // Apply input to DUV
    oDUV.run(
      i32(nXreal[0]), i32(nXimag[0]),
      i32(nXreal[1]), i32(nXimag[1]),
      i32(nXreal[2]), i32(nXimag[2]),
      i32(nXreal[3]), i32(nXimag[3]),
      i32(nXreal[4]), i32(nXimag[4]),
      i32(nXreal[5]), i32(nXimag[5]),
      i32(nXreal[6]), i32(nXimag[6]),
      i32(nXreal[7]), i32(nXimag[7]),
      i32(nXreal[8]), i32(nXimag[8]),
      i32(nXreal[9]), i32(nXimag[9]),
      i32(nXreal[10]), i32(nXimag[10]),
      i32(nXreal[11]), i32(nXimag[11]),
      i32(nXreal[12]), i32(nXimag[12]),
      i32(nXreal[13]), i32(nXimag[13]),
      i32(nXreal[14]), i32(nXimag[14]),
      i32(nXreal[15]), i32(nXimag[15]),
      nYreal[0], nYimag[0], nYreal[1], nYimag[1],
      nYreal[2], nYimag[2], nYreal[3], nYimag[3],
      nYreal[4], nYimag[4], nYreal[5], nYimag[5],
      nYreal[6], nYimag[6], nYreal[7], nYimag[7],
      nYreal[8], nYimag[8], nYreal[9], nYimag[9],
      nYreal[10], nYimag[10], nYreal[11], nYimag[11],
      nYreal[12], nYimag[12], nYreal[13], nYimag[13],
      nYreal[14], nYimag[14], nYreal[15], nYimag[15]
    );

    // dump inputs and outputs as integers on behalf of VHDL simulations
    // no bitreversal of output order here!
    for (int i = 0; i< POINTS; i++) {
        fprintf(pFileIn, "%d %d\n", nXreal[i], nXimag[i]);
        fprintf(pFileOut, "%d %d\n", nYreal[i], nYimag[i]);
    }

    // Perform output conversion
    for (int i = 0; i < POINTS; i++) {
      dYreal[i] = signed_to_double(nYreal[i], WORD_LENGTH, IWORD_LENGTH);
      dYimag[i] = signed_to_double(nYimag[i], WORD_LENGTH, IWORD_LENGTH);
    }

    /*
    * Perform bit reversal
    */
    double dYrealRev[POINTS], dYimagRev[POINTS];

    for (int i = 0; i< POINTS; i++) {
      int nIndex = bit_reverse16(i);

      dYrealRev[nIndex] = dYreal[i];
      dYimagRev[nIndex] = dYimag[i];
    }

    memcpy(dYreal, dYrealRev, sizeof(dYrealRev));
    memcpy(dYimag, dYimagRev, sizeof(dYimagRev));

    // Write output (floating point to screen)

    printf("Input:\n");
    for (int i = 0; i < POINTS; i++) 
      printf ("%+10.6f %+10.6fi\n",  dXreal[i], dXimag[i]);

    printf("Output:\n");
    for (int i = 0; i < POINTS; i++) 
      printf ("%+10.6f %+10.6fi; abs_val = %9.6f\n",  
              dYreal[i], 
              dYimag[i],
              sqrt(dYreal[i]*dYreal[i] + dYimag[i]*dYimag[i]));
    
  }

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

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