//---------------------------------------------------------------------------
#pragma hdrstop

#include "generate_hdl_sources.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
//---------------------------------------------------------------------------

#pragma package(smart_init)

#define FILE_COUNT 4
#define MAX_BIT_WIDTH 32
#define MAX_STAGE_CNT 16
#define TEMP_HDL_SOURCES_ADDR "temp_rtl/"
#define HDL_SOURCES_ADDR "rtl/"
#define HDL_FILE_NAMES {"driving_logic",       \
                        "fft_module",          \
                        "loading_logic",    \
                        "top_fft"};


 int fce_gen_hdl_sources (int stage_count,      // number of stages, log2(N)
                          int bit_size,         // bit width of input data
                          int arith_synch,      // use one clk delay in arith part
                          int not_use_load_reg, // generate only fft_module
                          FILE *report)
  {
   int file_ind;
   int _error = 0;
   
  // generate constant bank file and definition package
   _error = _error + fce_gen_const_bank_file(pow(2, stage_count-1),bit_size,report);
   _error = _error + fce_gen_def_pkg_file(stage_count, bit_size, arith_synch, report);

  // copy templates file into hdl directory 
   for (file_ind = 0; file_ind < (FILE_COUNT - 2 * not_use_load_reg); file_ind ++)
    {
     _error = _error + fce_copy_files(file_ind,report);
    }
   return _error;
  }

  int fce_gen_const_bank_file(int const_count, int bit_size, FILE *report)
  {
   FILE *const_bank;
   int   const_addr;
   int   const_int;
   int   conv;
   int   __error = 0;
   float const_float;
   char  _string [MAX_BIT_WIDTH];

   const_bank = fopen(HDL_SOURCES_ADDR"/const_bank.vhd", "w");
   if (const_bank == NULL)
    {
     __error = __error + 1;
     fprintf(report, "Error during opening const_bank.vhd file!\n");
    }
   else
  {
    // print header and libraries
   fprintf(const_bank, "-----------------------------------------------------------------------------\n");
   fprintf(const_bank, "------------------------------- Constant bank -------------------------------\n");
   fprintf(const_bank, "-----------------------------------------------------------------------------\n\n");

   fprintf(const_bank, "library IEEE; \n");
   fprintf(const_bank, "use IEEE.STD_LOGIC_1164.all; \n");
   fprintf(const_bank, "use IEEE.NUMERIC_STD.all; \n\n");

   fprintf(const_bank, "use work.fft_module_pkg.all; \n\n");

   fprintf(const_bank, "package const_bank_pkg is \n\n");

   fprintf(const_bank, "constant c_CONST_RE  : t_CONST_BANK := ( \n");  

  // ------------------ generate constant values --------------------
   for (const_addr = 0; const_addr < const_count; const_addr++)  
    {
    // generate constants (real part) in real numbers and transform into integer
     const_float = (cos(3.14159/const_count*const_addr))*pow(2,bit_size-1-1);
     const_int = (int)const_float;

    // convert integer to binary values
     fce_int2bin(const_int, bit_size, _string);

     fprintf(const_bank, "             \"%s\"",_string); 
     if (const_addr < const_count -1)
      {
       fprintf(const_bank, ", ");}
       fprintf(const_bank, "\n");
      }
   fprintf(const_bank, "             ); \n\n\n");

   fprintf(const_bank, "constant c_CONST_IM  : t_CONST_BANK := ( \n");

   for (const_addr = 0; const_addr < const_count; const_addr++)
    {
    // generate constants (imag part) in real numbers and transform into integer
     const_float = -(sin(3.14159/const_count*const_addr))*pow(2,bit_size-1 -1);
     const_int = (int)const_float;

    // convert integer to binary values
     fce_int2bin(const_int, bit_size, _string);

     fprintf(const_bank, "             \"%s\"",_string); 
     if (const_addr < const_count -1)
       fprintf(const_bank, ", ");

     fprintf(const_bank, "\n");                     
    }
     fprintf(const_bank, "             ); \n\n");

     fprintf(const_bank, "end const_bank_pkg; \n\n");

    // report succesful constant bank generating
     fprintf(report, "Constant bank generated for FFT size %d, data width is %d.\n",
                      2 * const_count, bit_size);

     fclose(const_bank);
   }
   return __error;
  }


 // convert integer to binary numbers
 void fce_int2bin(int const_int, int bit_size, char* _string)
  {
    int i;
    _string[bit_size] = '\0';
    for (i = bit_size - 1; i >= 0; --i, const_int >>= 1)
     {
      _string[i] = (const_int & 1) + '0';
     }
  }

 // generate definition package
 int fce_gen_def_pkg_file(int stage_count,
                           int bit_size,
                           int arith_sync,
                           FILE *report)
  {
   FILE *def_pkg;
   FILE *def_pkg_template;
   char line [85];
   char _string [MAX_STAGE_CNT];
   int  __error = 0;
   int  index;
   int  stage_cnt_size; // FFT maximal size is here limited by 2**16

         if (stage_count < 2)    stage_cnt_size = 0;
    else if (stage_count < 3)    stage_cnt_size = 1;
    else if (stage_count < 5)    stage_cnt_size = 2;
    else if (stage_count < 9)    stage_cnt_size = 3;
    else                         stage_cnt_size = 4;

   // open template and target file
    def_pkg = fopen(HDL_SOURCES_ADDR"fft_module_pkg.vhd", "w");
    def_pkg_template = fopen (TEMP_HDL_SOURCES_ADDR"fft_module_pkg_temp.vhd", "r");

      if (def_pkg_template == NULL)
    {
     fprintf(report, "Error during opening fft_module_pkg_temp.vhd file!\n");
     __error = __error + 1;
    }
     else if (def_pkg == NULL)
    {
     fprintf(report, "Error during opening fft_module_pkg.vhd file!\n");
     __error = __error + 1;
    }
     else
   {
    while (!feof(def_pkg_template))
     {
      fgets(line,sizeof(line) - 1,def_pkg_template);
      if (strstr(line,"--insert design parameters--"))
       {
        fprintf(def_pkg, " -- stage count - derived from size of FFT - stage count = log2(N)\n");
        fprintf(def_pkg, " constant c_STAGE_COUNT      : integer    := %d;\n\n", stage_count);
 
        fprintf(def_pkg, " -- accuracy of input data - bit size\n");
        fprintf(def_pkg, " constant c_DATA_WIDTH       : integer    := %d;\n\n",bit_size);

        fprintf(def_pkg, "  -- switch for implementation of delay in aritmetic block\n");
        fprintf(def_pkg, " --  1 - latch is inserted between multiplier and adders, higher clk frequency\n");
        fprintf(def_pkg, " --  0 - no latch inserted, result is load in data reg with next clk rising edge\n");
        fprintf(def_pkg, " constant c_SYNC_ASYNC_ARITHMETIC : integer := %d;\n\n", arith_sync);
  
        fprintf(def_pkg, " -- bit size of stage counter - derived from c_STAGE_COUNT\n");
        fprintf(def_pkg, " constant c_STAGE_COUNTER_SIZE : integer := %d;\n\n", stage_cnt_size);
       }
      else
      if (strstr(line,"-- insert const addr case --"))
      {
        fprintf(def_pkg, "    case stage is\n");
         fce_int2bin (0, stage_cnt_size, _string);
         fprintf(def_pkg, "     when \"%s\"  =>", _string);
         fce_int2bin (0, stage_count-1, _string);
         fprintf(def_pkg, " const_addr := \"%s\";\n", _string);

        for (index = 1; index < stage_count-1; index ++)
        {
         fce_int2bin (index, stage_cnt_size, _string);
         fprintf(def_pkg, "     when \"%s\"  =>", _string);
         fce_int2bin (0, stage_count - index - 1, _string);
         fprintf(def_pkg, " const_addr := block_step(%2d downto 0) & \"%s\";\n",index-1, _string);
        }
         fprintf(def_pkg, "     when others =>");
         fprintf(def_pkg, " const_addr := block_step;\n");
         fprintf(def_pkg, "    end case;\n");
      }
      else
       fprintf(def_pkg, line);
     }

     fprintf(report, "FFT design definition package generated.\n");
     fclose(def_pkg);
     fclose(def_pkg_template);
   }
  return __error;
}

 int fce_copy_files ( int file_ind, FILE *report)
  {
   FILE *file;
   FILE *file_out;
   char temp_file_name[50];
   char hdl_file_name[50];
   char line [85];
   char file_list[FILE_COUNT] [20] = HDL_FILE_NAMES
   int  __error = 0;

   strcpy(temp_file_name,TEMP_HDL_SOURCES_ADDR);
   strcat(temp_file_name,file_list[file_ind]);
   strcat(temp_file_name,".vhd"); 

   strcpy(hdl_file_name,HDL_SOURCES_ADDR);   
   strcat(hdl_file_name,file_list[file_ind]);
   strcat(hdl_file_name,".vhd");

   file = fopen(temp_file_name,"r");
   file_out = fopen(hdl_file_name,"w");

   if (file == NULL || file_out == NULL)
    {
     __error = __error + 1;
     fprintf(report, "Error during copying source file %s into rtl/ directory.\n \
                      Check if template file and rtl/ directory exist.\n",
             hdl_file_name);
    }
   else
    {
     fprintf(report, "Source file %s was copied into rtl/ directory.\n",
             temp_file_name);
     while (!feof(file))
      {
       fgets(line,sizeof(line),file);
       fprintf(file_out, line);     
      }

     fclose(file);
     fclose(file_out);
    }
  return __error;
 }
