// ***************************************************************************/
//Actel Corporation Proprietary and Confidential
//Copyright 2011 Actel Corporation. All rights reserved.
//
//ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN
//ACCORDANCE WITH THE ACTEL LICENSE AGREEMENT AND MUST BE APPROVED
//IN ADVANCE IN WRITING.
//
//Description:  CoreFFT (Streaming) RTL Component Library
//              Various Standard RTL Modules
//
//Revision Information:
//Date         Description
//08Apr2011    Initial Release
//
//SVN Revision Information:
//SVN $Revision: $
//SVN $Data: $
//
//Resolved SARs
//SAR     Date    Who         Description
//
//Notes:
//

`timescale 1 ns/100 ps
`define STANDALONE


// ----------- Simple one-DFF Edge Detector ------------
module kitEdge_fft_strm (nGrst, rst, clk, inp, outp);
  parameter REDGE = 1;   // 1 - rising edge, or 0 - falling edge

  input  nGrst, clk, rst;
  input inp;
  output outp;

  reg inp_tick;
  wire rise_edge, fall_edge;

  assign rise_edge = inp & ~inp_tick;
  assign fall_edge = ~inp & inp_tick;
  assign    outp      = (REDGE) ? rise_edge : fall_edge;

  always @(posedge clk or negedge nGrst) begin
    if(!nGrst)    inp_tick <= 1'b0;
    else
      if (rst)    inp_tick <= 1'b0;
      else        inp_tick <= inp;

  end
endmodule
/*  -- usage
 kitEdge # (.REDGE(1)) edge_detect_0 (
     .nGrst(nGrst), .rst(rst), .clk(clk),
     .inp(in), .outp(out) );
*/
// -----------------------------------------------------------------------------
//    ########    ########   ##           ###     ##    ##
//    ##     ##   ##         ##          ## ##     ##  ##
//    ##     ##   ##         ##         ##   ##     ####
//    ##     ##   ######     ##        ##     ##     ##
//    ##     ##   ##         ##        #########     ##
//    ##     ##   ##         ##        ##     ##     ##
//    ########    ########   ########  ##     ##     ##


//----------- Register-based 1-bit Delay has only input and output ---------
module kitDelay_bit_reg_fft_strm(nGrst, rst, clk, clkEn, inp, outp);
  parameter
    DELAY = 2;

  input  nGrst, rst, clk, clkEn;
  input  inp;
  output outp;
  // extra register to avoid error if DELAY==0
  reg delayLine [0:DELAY];
  integer i;

  generate
    if(DELAY==0)
      assign outp = inp;
    else begin
      assign outp = delayLine[DELAY-1];

      always @(posedge clk or negedge nGrst)
        if(!nGrst)
          for(i=0; i<DELAY; i=i+1)         delayLine[i] <= 1'b0;
        else
          if (rst)
            for (i = 0; i<DELAY; i=i+1)  delayLine[i] <= 1'b0;
          else
            if (clkEn) begin
              for(i=DELAY-1; i>=1; i=i-1)  delayLine[i] <= delayLine[i-1];
              delayLine[0] <= inp;
            end  // clkEn
    end
  endgenerate
endmodule
/* Use
  kitDelay_bit_reg # (.DELAY(2)) bit_dly_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
    .inp(input), .outp(outp) ) ;
*/

module kitDelay_reg_fft_strm(nGrst, rst, clk, clkEn, inp, outp);
  parameter
    BITWIDTH = 16,
    DELAY = 2;

  input  nGrst, rst, clk, clkEn;
  input  [BITWIDTH-1:0] inp;
  output   [BITWIDTH-1:0] outp;
  // extra register to avoid error if DELAY==0
  reg [BITWIDTH-1:0] delayLine [0:DELAY];
  integer i;

  generate
    if(DELAY==0)
      assign outp = inp;
    else begin
      assign outp = delayLine[DELAY-1];

      always @(posedge clk or negedge nGrst)
        if(!nGrst)
          for(i=0; i<DELAY; i=i+1)         delayLine[i] <= 'b0;
        else
          if (clkEn)
            if (rst)
              for (i = 0; i<DELAY; i=i+1)  delayLine[i] <= 'b0;
            else begin
              for(i=DELAY-1; i>=1; i=i-1)  delayLine[i] <= delayLine[i-1];
              delayLine[0] <= inp;
            end  // clkEn
    end
  endgenerate
endmodule
/* Use
  kitDelay_reg_fft_strm # (.BITWIDTH(WIDTH), .DELAY(1) ) dly_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
    .inp(datain), .outp(dataout)  );
*/


//     #####
//    #     #   ####   #    #  #    #  #####  ######  #####
//    #        #    #  #    #  ##   #    #    #       #    #
//    #        #    #  #    #  # #  #    #    #####   #    #
//    #        #    #  #    #  #  # #    #    #       #####
//    #     #  #    #  #    #  #   ##    #    #       #   #
//     #####    ####    ####   #    #    #    ######  #    #

// simple incremental counter
module kitCountS_fft_strm(nGrst, rst, clk, clkEn, cntEn, Q, dc);
  parameter WIDTH = 16;
  parameter DCVALUE = (1 << WIDTH) - 1; // state to decode
  parameter BUILD_DC = 1;

  input nGrst, rst, clk, clkEn, cntEn;
  output [WIDTH-1:0] Q;
  output dc;  // decoder output
  reg [WIDTH-1:0] Q;

  assign dc = (BUILD_DC==1)? (Q == DCVALUE) : 1'bx;

  always@(negedge nGrst or posedge clk)
    if(!nGrst) Q <= {WIDTH{1'b0}};
    else
      if(clkEn)
        if(rst)       Q <= {WIDTH{1'b0}};
        else
          if(cntEn)   Q <= Q + 1'b1;
endmodule
/* instance
  kitCountS # ( .WIDTH(WIDTH), .DCVALUE({WIDTH{1'bx}}),
                .BUILD_DC(0) ) counter_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
    .cntEn(cntEn), .Q(outp), .dc() );
*/

//complex counter
module kitCountC_fft_strm(nGrst, rst, clk, clkEn, cntEn, up, ld, din, Q, dc);
  parameter WIDTH = 16;     // in bits
  parameter UPDOWN = 2;  // 0 - up, 1-down, 2 - up/down
  parameter DCVALUE = (1 << WIDTH) - 1; // state to decode
  parameter BUILD_CLKEN = 1;
  parameter BUILD_RST = 1;
  parameter BUILD_LD = 1;
  parameter BUILD_DC = 1;

  input nGrst, rst, clk, clkEn, cntEn, up, ld;
  input [WIDTH-1:0] din;
  output [WIDTH-1:0] Q;
  output dc;  // decoder output
  reg [WIDTH-1:0] Q;

  assign dc = (BUILD_DC==1)? (Q == DCVALUE) : 1'bx;

  always@(negedge nGrst or posedge clk) begin
    if(!nGrst) Q <= {WIDTH{1'b0}};
    else
      if(clkEn || (BUILD_CLKEN==0) ) begin // if clkEn, or no clkEn input at all
        if(rst && BUILD_RST) Q <= {WIDTH{1'b0}};
        else begin
          if(ld && BUILD_LD)  Q <= din; // sync load
          else
            if(cntEn) begin  // regular count
              if(UPDOWN==0)           Q <= Q + 1'b1;
              if(UPDOWN==1)           Q <= Q - 1'b1;
              if((UPDOWN==2) && up)   Q <= Q + 1'b1;
              if((UPDOWN==2) && !up)  Q <= Q - 1'b1;
            end // count
        end
      end
  end // always
endmodule

/* instance
  kitCountC # ( .WIDTH(WIDTH), .UPDOWN(0), .DCVALUE({WIDTH{1'bx}}),
                .BUILD_CLKEN(1), .BUILD_RST(1), .BUILD_LD(0),
                .BUILD_DC(0) ) counter_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn), .cntEn(cntEn),
    .up(1'bx), .ld(1'bx), .din({WIDTH{1'bx}}), .Q(outp), .dc() );
*/

// -----------------------  Simple Round Up: 1-clk delay  ----------------------
// ---------- Use it when it is known INBITWIDTH > OUTBITWIDTH -----------------
// Partially truncated input comes in as follows: LSB's are truncated but one 
// extra bit retained

module kitRndUp_fft_strm (nGrst, rst, clk, clkEn, inp, valInp, outp, valOutp);
  parameter WIDTH_OUT = 12;
  parameter RND_MODE  = 0;

  input nGrst, rst, clk, clkEn, valInp;
  input[WIDTH_OUT:0] inp;
  output valOutp;
  output [WIDTH_OUT-1:0] outp;
  reg valOutp;
  reg [WIDTH_OUT-1:0] outp;

  wire[WIDTH_OUT:0] outp_int;

  assign outp_int = inp[WIDTH_OUT:0] + 'd1;

  always @(posedge clk or negedge nGrst)
    if(!nGrst) begin
      outp    <= 'b0;
      valOutp <= 1'b0;
    end
    else
      if(clkEn)
        if (rst) begin
          outp    <= 'b0;
          valOutp <= 1'b0;
        end
        else begin
          if(RND_MODE) outp <= outp_int[WIDTH_OUT:1];
          else         outp <= inp[WIDTH_OUT:1];          //?
          valOutp <= valInp;
        end
endmodule

// Result: Resizes the vector inp to the specified size.
// To create a larger vector, the new [leftmost] bit positions are filled
// with the sign bit (if UNSIGNED==0) or 0's (if UNSIGNED==1).
// When truncating, the sign bit is retained along with the rightmost part
// if UNSIGNED==0.  Otherwise (UNSIGNED==1) the leftmost bits are all dropped.
module signExt_fft_strm (inp, outp);
  parameter INWIDTH = 16;
  parameter OUTWIDTH = 20;
  parameter UNSIGNED = 0;   // 0-signed conversion; 1-unsigned

  input [INWIDTH-1:0] inp;
  output[OUTWIDTH-1:0] outp;

  generate
    if(INWIDTH >= OUTWIDTH) begin
      assign outp[OUTWIDTH-1] = UNSIGNED ? inp[OUTWIDTH-1] : inp[INWIDTH-1];
      assign outp[OUTWIDTH-2:0] = inp[OUTWIDTH-2:0];
    end
    else
      assign outp = UNSIGNED ?  {{(OUTWIDTH-INWIDTH){1'b0}}, inp} :
                                {{(OUTWIDTH-INWIDTH){inp[INWIDTH-1]}}, inp};
  endgenerate
endmodule
/* usage:
  signExt # ( .INWIDTH(DATA_WIDTH),
              .OUTWIDTH(DATA_WIDTH_MAC),
              .UNSIGNED(UNSIGNED)) signExt_0 (
    .inp(data), .outp(data2mac)   );
*/


//                     ########      ###     ##     ##
//                     ##     ##    ## ##    ###   ###
//                     ##     ##   ##   ##   #### ####
//                     ########   ##     ##  ## ### ##
//                     ##   ##    #########  ##     ##
//                     ##    ##   ##     ##  ##     ##
//                     ##     ##  ##     ##  ##     ##
//
// --------- Two-port RAM simulation model ----------
// It has an extra reg simulating an output hard RAM register
module RAM_fabric_fft_strm (nGrst, RCLOCK, WCLOCK,
       WRB, RDB,
       DI,
       RADDR, WADDR,
       DO       );
  parameter
    BITWIDTH  = 16,
    RAM_DEPTH = 256,
    RAM_LOGDEPTH = 8;

  input  nGrst, WCLOCK, RCLOCK;
  input  WRB, RDB;
  input  [BITWIDTH-1:0] DI;
  input  [RAM_LOGDEPTH-1:0] RADDR, WADDR;
  output [BITWIDTH-1:0] DO;

  reg [BITWIDTH-1:0] ramArray [0:RAM_DEPTH-1];
  reg [BITWIDTH-1:0] arrOut;
  integer i;

  assign DO = arrOut;
  // write
  always @(posedge WCLOCK or negedge nGrst)
    if(!nGrst)
      for(i=0; i<RAM_DEPTH; i=i+1)
        ramArray[i] <= 'bx;
    else
      if (WRB) ramArray[WADDR] <= DI;

  // read
  always @(posedge RCLOCK)                // RAM block output register
    if (RDB) arrOut <= ramArray[RADDR];

endmodule

//            ____   _    _                    __  _                 _ 
//           / __ \ | |  | |                  / _|(_)               | |
//          | |  | || |_ | |__    ___  _ __  | |_  _ __  __ ___   __| |
//          | |  | || __|| '_ \  / _ \| '__| |  _|| |\ \/ // _ \ / _` |
//          | |__| || |_ | | | ||  __/| |    | |  | | >  <|  __/| (_| |
//           \____/  \__||_| |_| \___||_|    |_|  |_|/_/\_\\___| \__,_|
//                                        _         _            
//                                       | |       | |           
//                  _ __ ___    ___    __| | _   _ | |  ___  ___ 
//                 | '_ ` _ \  / _ \  / _` || | | || | / _ \/ __|
//                 | | | | | || (_) || (_| || |_| || ||  __/\__ \
//                 |_| |_| |_| \___/  \__,_| \__,_||_| \___||___/
//                                                               
// Subtractor for ACT_UNIQUE_radix2.  At any SCALE value applies 1-clk delay
module radix2_subtractor(inp_plus, inp_minus, outp, diff_virt, clkEn,
              clk, nGrst, rst);
  parameter WIDTH = 16;
  parameter SCALE = 1;

  input signed [WIDTH-1:0] inp_plus, inp_minus;
  output [WIDTH-1:0] outp;
  output [WIDTH:0] diff_virt;
  input nGrst, rst, clk, clkEn;

  wire signed [WIDTH-1:0] diff;
  wire signed [WIDTH:0] diff_round, diff_tick;
  wire signed [WIDTH:0] inp_plus_ext, inp_minus_ext, diff_virt;

  signExt_fft_strm # ( .INWIDTH(WIDTH),
              .OUTWIDTH(WIDTH+1),
              .UNSIGNED(0)) signExt_inpplus_0 (
    .inp(inp_plus), .outp(inp_plus_ext)   );

  signExt_fft_strm # ( .INWIDTH(WIDTH),
              .OUTWIDTH(WIDTH+1),
              .UNSIGNED(0)) signExt_inpminus_0 (
    .inp(inp_minus), .outp(inp_minus_ext)   );

  assign diff_virt = inp_plus_ext - inp_minus_ext;

  generate
    if (SCALE==0)  begin: simple_diff  // Discard MSB of a sum
      assign diff = diff_virt[WIDTH-1:0];

      kitDelay_reg_fft_strm # (.BITWIDTH(WIDTH), .DELAY(1) ) pipe_0 (
        .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
        .inp(diff), .outp(outp)  );
    end

    else begin: rounding_diff
      kitDelay_reg_fft_strm # (.BITWIDTH(WIDTH+1), .DELAY(1) ) pipe_0 (
        .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
        .inp(diff_virt), .outp(diff_tick)  );

      assign diff_round = diff_tick + 1;
      assign outp = diff_round[WIDTH:1];
    end
  endgenerate
    
endmodule  



// Overflow logic:
// - sum_re_virt is a bit wider than addends, thus no overflow occurs;
//   When rounding and truncating LSB, no overflow occurs either;
//   Only truncating MSB imposes the risk of overflow
// - Watch valid sums and diffs for overflow.  Not all sums/diffs are valid,
//   only those that coincide with active sel_tick

// Overflow timing:
// Q: How communicate an earlier stage OV to the last FFT stage when the FFT 
// results are ready?
// A: At every stage raise an OV flag and keep it (ov_next) until the stage  
//    ends.  At a next stage use previous stage flag to raise its own flag.
//    The trick here is for next stage to watch only relevant time window of 
//    the previous stage flag, read_lower_ov.  
//    This interval starts when the next stage begins generating its results
//    (a couple clk delay is tolerable) along with the potential OV signal, 
//    i.e. on start_next of the next stage.
//    The interval ends WHEN THE PREVIOUS STAGE ENDS generating its outputs
//    along with its potential OV, i.e. on pre_start of the next stage                
module bfly_ov_fft_strm( sum_im, sum_re, diff_im, diff_re, pre_start, start_next, 
                ov, ov_next, sel_tick, clkEn, clk, nGrst, rst );
  parameter WIDTH = 16;
  parameter SCALE = 1;

  input [WIDTH:0] sum_im, sum_re, diff_im, diff_re;       
  input nGrst, rst, clk, pre_start, start_next, clkEn, ov, sel_tick;
  output ov_next;
  reg ov_next;
  
  reg [1:0] sum_re_virt_bits, sum_im_virt_bits; 
  reg [1:0] diff_re_virt_bits, diff_im_virt_bits;
  reg read_lower_ov;
  wire sum_re_ov, sum_im_ov, diff_re_ov, diff_im_ov, local_ov,lower_ov,aggr_ov;

  generate 
    if (SCALE==0) begin: local_ovflow
      // separate necessary bits of the full sum and diff 
      always @(negedge nGrst or posedge clk)
        if(nGrst==1'b0) begin
          sum_re_virt_bits  <= 2'b0; 
          sum_im_virt_bits  <= 2'b0; 
          diff_re_virt_bits <= 2'b0; 
          diff_im_virt_bits <= 2'b0; 
        end
        else if (clkEn)
          if(rst) begin                                                 
            sum_re_virt_bits  <= 2'b0; 
            sum_im_virt_bits  <= 2'b0; 
            diff_re_virt_bits <= 2'b0; 
            diff_im_virt_bits <= 2'b0; 
          end
          else begin
            // To improve speed, pipeline bits necessary for OV detection
            sum_re_virt_bits   <= sum_re[WIDTH:WIDTH-1]; 
            sum_im_virt_bits   <= sum_im[WIDTH:WIDTH-1]; 
            diff_re_virt_bits  <= diff_re[WIDTH:WIDTH-1]; 
            diff_im_virt_bits  <= diff_im[WIDTH:WIDTH-1]; 
          end
      
      assign sum_re_ov  = sum_re_virt_bits[1] ^sum_re_virt_bits[0];
      assign sum_im_ov  = sum_im_virt_bits[1] ^sum_im_virt_bits[0];
      assign diff_re_ov = diff_re_virt_bits[1]^diff_re_virt_bits[0];
      assign diff_im_ov = diff_im_virt_bits[1]^diff_im_virt_bits[0];
      
      // Raise local ov flag on valid sum/diff ov that happen on positive sel_tick 
      assign local_ov = sel_tick & 
                        (sum_re_ov | sum_im_ov | diff_re_ov | diff_im_ov);
    end
    else begin: pass_ovflow
      assign local_ov = 1'b0;
    end 
  endgenerate
  
  // ov stuff that is always present to communicate lower stage ov to the output
  always @(negedge nGrst or posedge clk)
    if(nGrst==1'b0) begin
      ov_next          <= 1'b0;
      read_lower_ov     <= 1'b0;
    end
    else if (clkEn)
      if(rst) begin                                                 
        ov_next          <= 1'b0;
        read_lower_ov     <= 1'b0;
      end
      else begin
        if(start_next==1'b1)    ov_next <= 1'b0;
        else if(aggr_ov==1'b1)  ov_next <= 1'b1;
        
        // A window when previous stage' ov is valid  
        if(pre_start)       read_lower_ov <= 1'b0;
        else if(start_next) read_lower_ov <= 1'b1;        
      end

  assign lower_ov = read_lower_ov & ov;
  assign aggr_ov  = local_ov | lower_ov;
endmodule



module rotate22_fft_strm(
  inp_im, inp_re, 
  neg_inp_im, neg_inp_re,
  outp_im, outp_re, start, start_next,
                inverse, inverse_next, ov, ov_next, clk, clkEn, rst, nGrst);
  parameter WIDTH = 16;
  parameter PTS   = 256;   // FFT size
  parameter STAGE = 1;

  localparam MAXSTAGE = ceil_log2(PTS);
  localparam STAGE_REV = MAXSTAGE+1-STAGE;
  
  input  [WIDTH-1:0] inp_im, inp_re, neg_inp_im, neg_inp_re;          
  output  [WIDTH-1:0] outp_im, outp_re;           
  input clk, rst, nGrst, start, clkEn, inverse, ov;
  output start_next, inverse_next, ov_next;

  reg [WIDTH-1:0] outp_im, outp_re;           
  wire [WIDTH-1:0] rot_im_forw, rot_re_forw, rot_im_inv, rot_re_inv, rot_im, rot_re;
  wire [STAGE_REV-1:0] timer;
  reg run_timer;
  wire rotate;

  kitDelay_bit_reg_fft_strm # (.DELAY(1)) start_next_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
    .inp(start), .outp(start_next) ) ;

  // State machine
  kitCountS_fft_strm # ( .WIDTH(STAGE_REV), .DCVALUE(1),
                .BUILD_DC(0) ) timer_0 (
    .nGrst(nGrst), .clk(clk), .clkEn(clkEn), 
    .cntEn(1'b1),
    .rst(start_next),
    .Q(timer), .dc() );
    
  assign rotate = timer[STAGE_REV-1] & timer[STAGE_REV-2];  

  // Latch 'inverse' to line it up with the input data
  kitDelay_bit_reg_fft_strm # (.DELAY(1)) inverse_0 (
    .nGrst(nGrst), .rst(1'b0), .clk(clk), .clkEn(start_next),
    .inp(inverse), .outp(inverse_next) ) ;
    
  kitDelay_bit_reg_fft_strm # (.DELAY(1)) ov_next_0 (
    .nGrst(nGrst), .rst(rst), .clk(clk), .clkEn(clkEn),
    .inp(ov), .outp(ov_next) ) ;

  // State machine ends  

  always @(negedge nGrst or posedge clk)
    if(nGrst==1'b0) begin
      outp_im     <= 'b0;
      outp_re     <= 'b0;
    end
    else if (clkEn)
      if(rst==1'b1)  begin
        outp_im     <= 'b0;
        outp_re     <= 'b0;
      end
      else begin        
        outp_im <= rot_im;  
        outp_re <= rot_re;
      end
    
  assign rot_im_forw = rotate ? neg_inp_re : inp_im;
  assign rot_re_forw = rotate ?     inp_im : inp_re;
  
  assign rot_im_inv = rotate ?     inp_re : inp_im;
  assign rot_re_inv = rotate ? neg_inp_im : inp_re;

  assign rot_im = inverse_next ? rot_im_inv : rot_im_forw;
  assign rot_re = inverse_next ? rot_re_inv : rot_re_forw;


  // -----------------------------
  function [31:0] ceil_log2;
      input integer x;
      integer tmp, res;
    begin
      tmp = 1;
      res = 0;
      while (tmp < x) begin
        tmp = tmp * 2;
        res = res + 1;
      end
      ceil_log2 = res;
    end
  endfunction
        
endmodule

// Reverse bits                                                 
module bit_reverse_fft_strm(bit_direct, bit_reverse );          
  parameter WIDTH = 10;                                         
                                                                
  input  [WIDTH-1:0] bit_direct;                                
  output [WIDTH-1:0] bit_reverse;                               
                                                                
  genvar i;                                                     
                                                                
  generate                                                      
    for(i=0; i<WIDTH; i=i+1) begin                              
      assign bit_reverse[i] = bit_direct[WIDTH-1-i];            
    end                                                         
  endgenerate                                                   
endmodule                 
                                                                                     