
`timescale  1 ps / 1 ps


//
//   This code contains a template for how to call the BxBFFT,
//   when using its BxBIF interface.
//
//   Parameter values given below are common values that work for
//   most applications.  Detailed descriptions are in the User
//   Guide.
//


module top ();


   ///////////////////////////////////////////////////////////////////////////////
   // These parameters are constants.  They are not BxBFFT parameters,
   // but are values that BxBFFT parameters may be set to.

   localparam  TRUE                        = 1;
   localparam  FALSE                       = 0;
   localparam  ENABLED                     = 1;
   localparam  DISABLED                    = 0;

   // For setting input and output data order
   localparam  ORDER_FULLY_NATURAL         = 0;
   localparam  ORDER_PARTIALLY_NATURAL     = 1;
   localparam  ORDER_SCRAMBLED             = 2;

   // For setting pipelining.  There are global defaults that can be set.
   // There are also many specific settings that can either use the default
   // (by setting them to USE_PIPELINING_DEFAULT) or can be locally set to a
   // desired value.
   localparam  USE_PIPELINING_DEFAULT      = -1;
   localparam  PIPELINING_MINIMUM          = 0;
   localparam  PIPELINING_SMALL            = 1;
   localparam  PIPELINING_MEDIUM           = 2;
   localparam  PIPELINING_LARGE            = 3;
   localparam  PIPELINING_EXTRA_LARGE      = 4;

   // For setting BRAM and URAM desirability.  There are global defaults
   // that can be set to a given desirability percentage from 0 to 100.  There
   // are also many settings that can use the default by setting them to
   // USE_BRAM_DESIRABILITY_DEFAULT or to USE_URAM_DESIRABILITY_DEFAULT.
   // These many settings can also be set individually to a desired
   // percentage from 0 to 100.
   localparam  USE_BRAM_DESIRABILITY_DEFAULT = -1; // This value says to use global default
   localparam  USE_URAM_DESIRABILITY_DEFAULT = -1; // This value says to use global default


   ///////////////////////////////////////////////////////////////////////////////
   // These first parameters are for information only.  They are not user modifiable.

   localparam  FFT_SIZE                  = 4096;  // Complex data points, or twice this in real points
   localparam  REAL_FFT                  = FALSE;     // Are forward FFT data points real
   localparam  POINTS_PER_CLOCK          = 4;     // Complex data points, or twice this in real points
   localparam  NUM_STAGES                = 6;
   localparam  NUM_SHIFTS                = NUM_STAGES-1;
   localparam  CLOCKS_PER_FFT            = FFT_SIZE / POINTS_PER_CLOCK;
   localparam  RADICES                   = 72'h004004004004004004;


   ///////////////////////////////////////////////////////////////////////////////
   // Main Parameters of the BxBFFT.  These are user alterable.

   localparam  FORWARD                     = TRUE;    // True for forward FFT exponent of -1
   localparam  FULL_FORWARD_FLOW_CONTROL   = ENABLED; // Allow Flow Control at every input clock
   localparam  REAL_WIDTH                  = 27;      // FFT Data bit width; start large, narrow in
   localparam  NUM_SYNC                    = 1;       // Number of snc signals, 1 or more


   ///////////////////////////////////////////////////////////////////////////////
   // Input Parameters.

   localparam  INPUT_ORDER                 = ORDER_FULLY_NATURAL;
   localparam  SYNC_ADVANCEMENT_IN         = 0;      // Clocks sync precedes data
   localparam  VALID_ADVANCEMENT_IN        = 0;      // Clocks valid precedes data
   localparam  FOLD_IN                     = FALSE;  // False gives zero bin at data start


   ///////////////////////////////////////////////////////////////////////////////
   // Output Parameters.

   localparam  OUTPUT_ORDER                = ORDER_FULLY_NATURAL;
   localparam  SYNC_ADVANCEMENT_OUT        = 0;      // Clocks sync precedes data
   localparam  VALID_ADVANCEMENT_OUT       = 0;      // Clocks valid precedes data
   localparam  FOLD_OUT                    = FALSE; // False gives zero bin at data start


   ///////////////////////////////////////////////////////////////////////////////
   // Gain Control Parameters.

   // GAIN_CONTROL_STRATEGY is 0 (no scaling) up to 10 (scale maximum amount).
   // It can also be 11 for user gain control, or 12 for dynamic user gain control.
   localparam  GAIN_CONTROL_STRATEGY       = 10;    // Strategy 10 prevents overflow.
   localparam  GAIN_CONTROL_MARGIN_BITS    = 0;     // Extra margin not needed with strategy 10
   localparam  USER_STATIC_SHIFTS          = 0;     // Only for user-defined gain strategy 11 or 12

   localparam  ENABLE_MONITORING           = FALSE; // Disable amplitude monitoring


   ///////////////////////////////////////////////////////////////////////////////
   // Pipelining Parameters.

   localparam  PIPELINING_DEFAULT            = PIPELINING_MINIMUM;

   localparam  IO_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S0_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S1_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S2_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S3_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S4_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S5_REORDERING_PIPELINE_LEVEL  = USE_PIPELINING_DEFAULT;
   localparam  S1_TWIDDLE_PIPELINE_LEVEL     = USE_PIPELINING_DEFAULT;
   localparam  S2_TWIDDLE_PIPELINE_LEVEL     = USE_PIPELINING_DEFAULT;
   localparam  S3_TWIDDLE_PIPELINE_LEVEL     = USE_PIPELINING_DEFAULT;
   localparam  S4_TWIDDLE_PIPELINE_LEVEL     = USE_PIPELINING_DEFAULT;
   localparam  S5_TWIDDLE_PIPELINE_LEVEL     = USE_PIPELINING_DEFAULT;


   ///////////////////////////////////////////////////////////////////////////////
   // Memory Parameters.

   // The global defaults.  These are the percentages used for values set to
   // USE_BRAM_DESIRABILITY_DEFAULT or to USE_URAM_DESIRABILITY_DEFAULT.
   localparam  BRAM_DESIRABILITY_DEFAULT_PERCENT  = 50;
   localparam  URAM_DESIRABILITY_DEFAULT_PERCENT  = 1;

   // BRAM desirability controls.
   localparam  IO_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S0_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S1_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S2_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S3_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S4_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;
   localparam  S5_BRAM_DESIRABILITY_PERCENT  = USE_BRAM_DESIRABILITY_DEFAULT;

   // URAM desirability controls.
   localparam  IO_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S0_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S1_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S2_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S3_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S4_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;
   localparam  S5_URAM_DESIRABILITY_PERCENT  = USE_URAM_DESIRABILITY_DEFAULT;

   // Controls for splitting the IO memory between BRAM and URAM.  This allows
   // fine control of the BRAM/URAM utilization split.
   localparam  IO_FORCE_PERCENT_OF_MEM_INTO_BRAM = 0;
   localparam  IO_FORCE_PERCENT_OF_MEM_INTO_URAM = (FFT_SIZE>65536)?50:0;

   // Parameters to generate twiddles on-the-fly instead of
   // from a table at each stage.  This trades off memory usage
   // for logic/DSPs.
   localparam  S1_ON_THE_FLY_TWIDDLES        = DISABLED;
   localparam  S2_ON_THE_FLY_TWIDDLES        = DISABLED;
   localparam  S3_ON_THE_FLY_TWIDDLES        = DISABLED;
   localparam  S4_ON_THE_FLY_TWIDDLES        = DISABLED;
   localparam  S5_ON_THE_FLY_TWIDDLES        = DISABLED;


   ///////////////////////////////////////////////////////////////////////////////
   // Multi-Vector Parameters.

   // It's normal to enable all FFT stages, and to disable them being
   // enabled dynamically.
   localparam  STATIC_NUM_STAGES_ENABLED   = NUM_STAGES;
   localparam  ENABLE_DYNAMIC_MVFFT        = FALSE;



   ///////////////////////////////////////////////////////////////////////////////
   // Parameters that are derived but necessary to determine interface widths.

   localparam  REALS_PER_COMPLEX  = 2;
   localparam  ITEM_NUMBER        = REALS_PER_COMPLEX * POINTS_PER_CLOCK;
   localparam  STAGES_WIDTH       = $clog2(NUM_STAGES+1);
   localparam  CLOCK_BITS         = $clog2(CLOCKS_PER_FFT);


   ///////////////////////////////////////////////////////////////////////////////
   // BxBFFT Inputs and Outputs.  Signals ending in _i are inputs, _o are outputs.

   wire                         clk;
   wire                         resetn;

   wire [NUM_SYNC-1:0]          in_sync_i;
   wire                         in_data_valid_i;
   wire signed [REAL_WIDTH-1:0] in_data_i         [0:ITEM_NUMBER-1];

   wire [NUM_SYNC-1:0]          out_sync_o;
   wire                         out_data_valid_o;
   wire signed [REAL_WIDTH-1:0] out_data_o         [0:ITEM_NUMBER-1];

   wire [2*NUM_SHIFTS-1:0]      fft_shifts_i;            // Only used for GAIN_CONTROL_STRATEGY 12
   wire [2*NUM_SHIFTS-1:0]      overflow_detect_o;       // Only used for ENABLE_MONITORING=TRUE
   wire                         overflow_detect_reset_i; // Only used for ENABLE_MONITORING=TRUE
   wire [STAGES_WIDTH-1:0]      num_stages_enabled_i;    // Only used for ENABLE_DYNAMIC_MVFFT=TRUE


   wire [NUM_SYNC-1:0]          in_address_sync_i;  // To decode Scrambled input order.  Otherwise set to 1.
   wire [CLOCK_BITS-1:0]        in_address_o;       // To decode Scrambled input order.  Otherwise leave disconnected.
   wire [CLOCK_BITS-1:0]        out_address_o;      // To decode Scrambled output order.  Otherwise leave disconnected.



  BxBFFT_4096_4_complex_BxBIF
                               #( // Main Parameters
                                  .FORWARD                            ( FORWARD                           ),
                                  .FULL_FORWARD_FLOW_CONTROL          ( FULL_FORWARD_FLOW_CONTROL         ),
                                  .REAL_WIDTH                         ( REAL_WIDTH                        ),
                                  .NUM_SYNC                           ( NUM_SYNC                          ),

                                  // Input Parameters
                                  .INPUT_ORDER                        ( INPUT_ORDER                       ),
                                  .SYNC_ADVANCEMENT_IN                ( SYNC_ADVANCEMENT_IN               ),
                                  .VALID_ADVANCEMENT_IN               ( VALID_ADVANCEMENT_IN              ),
                                  .FOLD_IN                            ( FOLD_IN                           ),

                                  // Output Parameters
                                  .OUTPUT_ORDER                       ( OUTPUT_ORDER                      ),
                                  .SYNC_ADVANCEMENT_OUT               ( SYNC_ADVANCEMENT_OUT              ),
                                  .VALID_ADVANCEMENT_OUT              ( VALID_ADVANCEMENT_OUT             ),
                                  .FOLD_OUT                           ( FOLD_OUT                          ),

                                  // Gain Management Parameters
                                  .GAIN_CONTROL_STRATEGY              ( GAIN_CONTROL_STRATEGY             ),
                                  .GAIN_CONTROL_MARGIN_BITS           ( GAIN_CONTROL_MARGIN_BITS          ),
                                  .USER_STATIC_SHIFTS                 ( USER_STATIC_SHIFTS                ),
                                  .ENABLE_MONITORING                  ( ENABLE_MONITORING                 ),

                                  // Pipelining Parameters
                                  .PIPELINING_DEFAULT                 ( PIPELINING_DEFAULT                ),

                                  .IO_REORDERING_PIPELINE_LEVEL       ( IO_REORDERING_PIPELINE_LEVEL      ),
                                  .S0_REORDERING_PIPELINE_LEVEL       ( S0_REORDERING_PIPELINE_LEVEL      ),
                                  .S1_REORDERING_PIPELINE_LEVEL       ( S1_REORDERING_PIPELINE_LEVEL      ),
                                  .S2_REORDERING_PIPELINE_LEVEL       ( S2_REORDERING_PIPELINE_LEVEL      ),
                                  .S3_REORDERING_PIPELINE_LEVEL       ( S3_REORDERING_PIPELINE_LEVEL      ),
                                  .S4_REORDERING_PIPELINE_LEVEL       ( S4_REORDERING_PIPELINE_LEVEL      ),
                                  .S5_REORDERING_PIPELINE_LEVEL       ( S5_REORDERING_PIPELINE_LEVEL      ),

                                  .S1_TWIDDLE_PIPELINE_LEVEL          ( S1_TWIDDLE_PIPELINE_LEVEL         ),
                                  .S2_TWIDDLE_PIPELINE_LEVEL          ( S2_TWIDDLE_PIPELINE_LEVEL         ),
                                  .S3_TWIDDLE_PIPELINE_LEVEL          ( S3_TWIDDLE_PIPELINE_LEVEL         ),
                                  .S4_TWIDDLE_PIPELINE_LEVEL          ( S4_TWIDDLE_PIPELINE_LEVEL         ),
                                  .S5_TWIDDLE_PIPELINE_LEVEL          ( S5_TWIDDLE_PIPELINE_LEVEL         ),

                                  // Memory Parameters.
                                  .BRAM_DESIRABILITY_DEFAULT_PERCENT  ( BRAM_DESIRABILITY_DEFAULT_PERCENT ),
                                  .URAM_DESIRABILITY_DEFAULT_PERCENT  ( URAM_DESIRABILITY_DEFAULT_PERCENT ),

                                  .IO_BRAM_DESIRABILITY_PERCENT       ( IO_BRAM_DESIRABILITY_PERCENT      ),
                                  .IO_URAM_DESIRABILITY_PERCENT       ( IO_URAM_DESIRABILITY_PERCENT      ),

                                  .S0_BRAM_DESIRABILITY_PERCENT       ( S0_BRAM_DESIRABILITY_PERCENT      ),
                                  .S1_BRAM_DESIRABILITY_PERCENT       ( S1_BRAM_DESIRABILITY_PERCENT      ),
                                  .S2_BRAM_DESIRABILITY_PERCENT       ( S2_BRAM_DESIRABILITY_PERCENT      ),
                                  .S3_BRAM_DESIRABILITY_PERCENT       ( S3_BRAM_DESIRABILITY_PERCENT      ),
                                  .S4_BRAM_DESIRABILITY_PERCENT       ( S4_BRAM_DESIRABILITY_PERCENT      ),
                                  .S5_BRAM_DESIRABILITY_PERCENT       ( S5_BRAM_DESIRABILITY_PERCENT      ),

                                  .S0_URAM_DESIRABILITY_PERCENT       ( S0_URAM_DESIRABILITY_PERCENT      ),
                                  .S1_URAM_DESIRABILITY_PERCENT       ( S1_URAM_DESIRABILITY_PERCENT      ),
                                  .S2_URAM_DESIRABILITY_PERCENT       ( S2_URAM_DESIRABILITY_PERCENT      ),
                                  .S3_URAM_DESIRABILITY_PERCENT       ( S3_URAM_DESIRABILITY_PERCENT      ),
                                  .S4_URAM_DESIRABILITY_PERCENT       ( S4_URAM_DESIRABILITY_PERCENT      ),
                                  .S5_URAM_DESIRABILITY_PERCENT       ( S5_URAM_DESIRABILITY_PERCENT      ),

                                  .IO_FORCE_PERCENT_OF_MEM_INTO_BRAM  ( IO_FORCE_PERCENT_OF_MEM_INTO_BRAM ),
                                  .IO_FORCE_PERCENT_OF_MEM_INTO_URAM  ( IO_FORCE_PERCENT_OF_MEM_INTO_URAM ),

                                  .S1_ON_THE_FLY_TWIDDLES             ( S1_ON_THE_FLY_TWIDDLES            ),
                                  .S2_ON_THE_FLY_TWIDDLES             ( S2_ON_THE_FLY_TWIDDLES            ),
                                  .S3_ON_THE_FLY_TWIDDLES             ( S3_ON_THE_FLY_TWIDDLES            ),
                                  .S4_ON_THE_FLY_TWIDDLES             ( S4_ON_THE_FLY_TWIDDLES            ),
                                  .S5_ON_THE_FLY_TWIDDLES             ( S5_ON_THE_FLY_TWIDDLES            ),

                                  // Multi-Vector Parameters
                                  .STATIC_NUM_STAGES_ENABLED          ( STATIC_NUM_STAGES_ENABLED         ),
                                  .ENABLE_DYNAMIC_MVFFT               ( ENABLE_DYNAMIC_MVFFT              ))
              bxbfft_bxbif     (
                                  .clk                       ( clk                       ),
                                  .resetn                    ( resetn                    ),

                                  .top_in_sync_i             ( in_sync_i                 ),
                                  .top_in_data_valid_i       ( in_data_valid_i           ),
                                  .top_in_data_i             ( in_data_i                 ),

                                  .top_out_sync_o            ( out_sync_o                ),
                                  .top_out_data_valid_o      ( out_data_valid_o          ),
                                  .top_out_data_o            ( out_data_o                ),

                                  .fft_shifts_i              ( fft_shifts_i              ),
                                  .overflow_detect_o         ( overflow_detect_o         ),
                                  .overflow_detect_reset_i   ( overflow_detect_reset_i   ),
                                  .num_stages_enabled_i      ( num_stages_enabled_i      ),
                                  .top_in_address_sync_i     ( in_address_sync_i         ),
                                  .top_in_address_o          ( in_address_o              ),
                                  .top_out_address_o         ( out_address_o             ));

`ifdef TEMPLATE_TEST

`define r always@(posedge clk)
`define w assign

reg clk_r                    = 0;
reg resetn_r                 = 0;
integer clk_count_r          = 0;
integer fft_clock_count_in_r = 0;

`w in_address_sync_i = 1;

`w clk    = clk_r;
`w resetn = resetn_r;

initial begin

  for(;;)
    begin
      #100;
      clk_r = !clk_r;
    end
end

`r clk_count_r <= clk_count_r + 1;
`r resetn_r <= (clk_count_r<10) ? 0 : 1;

`w in_data_valid_i = (!resetn) ? 0 : $random;
`w in_sync_i  = (fft_clock_count_in_r==0) ? 1 : 0;

localparam BIN_NUMBER    = FFT_SIZE/8-1;
localparam MAX_AMPLITUDE = 1<<(REAL_WIDTH-2);
localparam PI            = 3.1415927;

`r fft_clock_count_in_r <= (!resetn)           ?  0                        :
                           (!in_data_valid_i)  ?  fft_clock_count_in_r     :
                                                  fft_clock_count_in_r + 1 ;


genvar i;

// Create a sine wave or complex exponential input that is centered in bin
// BIN_NUMBER.

generate

  for(i=0; i<REALS_PER_COMPLEX*POINTS_PER_CLOCK; i=i+1)
    begin : create_input_data
      integer sample;
      if(REAL_FFT)
        begin : real_case
          localparam REAL_FFT_SIZE = FFT_SIZE * 2;
          `w sample = i + fft_clock_count_in_r * REALS_PER_COMPLEX * POINTS_PER_CLOCK;
          `w in_data_i[i] = (!in_data_valid_i) ? 0 : MAX_AMPLITUDE * $sin(2*PI*sample*BIN_NUMBER/REAL_FFT_SIZE);
        end
      else
        begin : complex_case
          `w sample = (i/2) + fft_clock_count_in_r * POINTS_PER_CLOCK;
          if(i&1)
            begin : imag_part
              `w in_data_i[i] = (!in_data_valid_i) ? 0 : MAX_AMPLITUDE * $sin(2*PI*sample*BIN_NUMBER/FFT_SIZE);
            end
          else
            begin : real_part
              `w in_data_i[i] = (!in_data_valid_i) ? 0 : MAX_AMPLITUDE * $cos(2*PI*sample*BIN_NUMBER/FFT_SIZE);
            end
        end
    end


endgenerate


// Handle output data

integer fft_clock_count_out_r = 0;

`r fft_clock_count_out_r <= (!resetn)            ?  0                         :
                            (!out_data_valid_o)  ?  fft_clock_count_out_r     :
                                                    fft_clock_count_out_r + 1 ;

integer outfile;
integer outfile2;

initial begin
  outfile=$fopen(`OUTFILE, "w");
end

real dB_in_bin         = 0.0;
real max_dB_not_in_bin = 0.0;


`r
  if(resetn && out_data_valid_o)
    begin
      integer j;
      for(j=0; j<POINTS_PER_CLOCK; j=j+1)
        begin : unpacking
          real rrr;
          real iii;
          integer sample;
          real mag;
          real dB;
          sample = j + fft_clock_count_out_r * POINTS_PER_CLOCK;
          rrr = out_data_o[2*j];
          iii = out_data_o[2*j+1];
          mag = rrr*rrr + iii*iii;
          dB  = 10.0 * $log10(mag + 1);
          if(sample==BIN_NUMBER)
            begin
              dB_in_bin = dB;
            end
          else
            begin
              if(dB>max_dB_not_in_bin)
                max_dB_not_in_bin=dB;
            end
          $fwrite(outfile, "%5d %f\n", sample, dB);
          if(sample==FFT_SIZE-1)
            begin
              outfile2=$fopen(`OUTFILE2, "w");
              $fwrite(outfile2, "Dynamic Range is %.2fdB\n", dB_in_bin-max_dB_not_in_bin);
              $fclose(outfile);
              $fclose(outfile2);
              $finish;
            end
        end
    end


`w fft_shifts_i = 0;
`w overflow_detect_reset_i = 0;
`w num_stages_enabled_i = NUM_STAGES;


`ifdef DEBUG
  initial
    begin
      $dumpfile("BxBIF.vcd");
      $dumpvars(0, top);
    end
`endif

`endif


endmodule


