/*******************************************************************************
 Copyright 2007 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.

 Revision 3.0 April 30, 2007 : v3.0 CoreFFT Release
*******************************************************************************/
`timescale 1 ns/100 ps


// if (sel) straight, else cross
module switch(clk, inP, inQ, sel, outP, outQ, validIn, validOut);
  parameter DWIDTH = 32;
  
  input clk, validIn;
  input [DWIDTH-1:0] inP, inQ;
  input sel;
  output [DWIDTH-1:0] outP, outQ;
  output validOut;
  reg[DWIDTH-1:0] outP, outQ;
  reg validOut;

  reg[DWIDTH-1:0] leftQ_r, rightP_r;
  reg pipe1;
  wire[DWIDTH-1:0] muxP_w, muxQ_w;

  assign muxP_w =  sel ? leftQ_r : inP;
  assign muxQ_w = ~sel ? leftQ_r : inP;

  always @ (posedge clk)
    begin
      outP <= #1 rightP_r;
      outQ <= #1 muxQ_w;
      leftQ_r <= #1 inQ;
      rightP_r <= #1 muxP_w;
      validOut <= #1 pipe1;
      pipe1 <= #1 validIn;
    end
endmodule


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

module wrapRam(clk, D, Q, wA, rA, wEn);
  parameter LOGPTS = 8;
  parameter DWIDTH = 32;

  input clk, wEn;
  input[DWIDTH-1:0] D;
  input[LOGPTS-2:0] rA, wA;
  output[DWIDTH-1:0] Q;

  actram wrapRam_0(.DO(Q), .RCLOCK(clk), .WCLOCK(clk),
                  .DI(D), .WRB(~wEn), .RDB(1'b0),
                  .WADDR(wA), .RADDR(rA) );
endmodule

module inBuffer (clk, clkEn, rA, wA_bfly, wA_load, ldData,
  pipo, wP_bfly, wQ_bfly, wEn_bfly, wEn_even, wEn_odd,
  outP, outQ );
  parameter LOGPTS = 8;
  parameter DWIDTH = 32;

  input clk, clkEn;
  // InBuf stores double complex words so that FFT engine can read two cmplx
  // words per clock.  Thus the depth of the buffer is LOGPTS-1
  input[LOGPTS-2:0] rA;
  // read Addr is used by FFT engine only
  input[LOGPTS-2:0] wA_bfly;
  // write Addr supplied by FFT engine
  input[LOGPTS-2:0] wA_load; // write Addr for loading new data
  input[DWIDTH-1:0] ldData;  // new data to load
  input[DWIDTH-1:0] wP_bfly, wQ_bfly;  // input data coming from FFT engine
  input wEn_bfly;  // wEn to store FFT engine data
  input wEn_even, wEn_odd;  // wEn to store new data in even/odd subbuffers
  input pipo;   // controls buffer input muxes.
                // pipo=pong for pong buffer, =/pong for ping buffer
  output[DWIDTH-1:0] outP, outQ;  // output data to FFT engine

  // internal wires, &-gates
  wire[LOGPTS-2:0] wA_w;
  wire[DWIDTH-1:0] wP_w, wQ_w;
  wire wEn_P, wEn_Q;
  wire wEnP_ce_w = wEn_P & clkEn;
  wire wEnQ_ce_w = wEn_Q & clkEn;

  // if(~pipo) LOAD, else - RUN BFLY.  Use MUX'es
  assign wEn_P = pipo ? wEn_bfly : wEn_even;
  assign wEn_Q = pipo ? wEn_bfly : wEn_odd;
  assign wA_w  = pipo ? wA_bfly : wA_load;
  assign wP_w  = pipo ? wP_bfly : ldData;
  assign wQ_w  = pipo ? wQ_bfly : ldData;

  // instantiate two mem blocks HALFPTS deep each
  wrapRam #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) memP
    (.D(wP_w),.Q(outP),.wA(wA_w),.rA(rA),.wEn(wEnP_ce_w),.clk(clk));
  wrapRam #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) memQ
    (.D(wQ_w),.Q(outQ),.wA(wA_w),.rA(rA),.wEn(wEnQ_ce_w),.clk(clk));
endmodule  //inBuffer


module pipoBuffer (clk, rA, wA_load, wA_bfly, ldData, wP_bfly, wQ_bfly,
  wEn_bfly, wEn_even, wEn_odd, pong, clkEn, outP, outQ);
  parameter LOGPTS = 8;
  parameter DWIDTH = 32;

  input clk, clkEn, pong;
  input[LOGPTS-2:0] rA;
  input[LOGPTS-2:0] wA_load;
  input[LOGPTS-2:0] wA_bfly;
  input[DWIDTH-1:0] ldData;
  input[DWIDTH-1:0] wP_bfly, wQ_bfly;
  input wEn_bfly, wEn_even, wEn_odd;
  output[(DWIDTH-1):0] outP, outQ;

  wire[DWIDTH-1:0] pi_outP, pi_outQ, po_outP, po_outQ;

  inBuffer #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) piBuf
    (.clk(clk), .clkEn(clkEn),
    // Addresses
    .rA(rA), .wA_bfly(wA_bfly), .wA_load(wA_load),
    // Data
    .ldData(ldData), .wP_bfly(wP_bfly), .wQ_bfly(wQ_bfly),
    // wEn
    .wEn_bfly(wEn_bfly), .wEn_even(wEn_even), .wEn_odd(wEn_odd),
    .pipo(~pong),
    .outP(pi_outP), .outQ(pi_outQ));

  inBuffer #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) poBuf
    (.clk(clk), .clkEn(clkEn),
    // Addresses
    .rA(rA), .wA_bfly(wA_bfly), .wA_load(wA_load),
    // Data
    .ldData(ldData), .wP_bfly(wP_bfly), .wQ_bfly(wQ_bfly),
    // wEn
    .wEn_bfly(wEn_bfly), .wEn_even(wEn_even), .wEn_odd(wEn_odd),
    .pipo(pong),
    .outP(po_outP), .outQ(po_outQ));

  assign outP = pong ? po_outP : pi_outP; 
  assign outQ = pong ? po_outQ : pi_outQ; 
endmodule

/******************************  outBuffer  ***********************************/
module outBuff (clk, clkEn, wEn, inP, inQ, wA, rA, outD );
  parameter LOGPTS = 8;
  parameter DWIDTH = 32;

  input clk, clkEn, wEn;
  input [DWIDTH-1:0] inP, inQ;
  input [LOGPTS-2:0] wA;
  input [LOGPTS-1:0] rA;
  output[DWIDTH-1:0] outD;
  reg[DWIDTH-1:0] outD;

  reg wEn_r;
  reg [DWIDTH-1:0] inP_r, inQ_r;
  reg [LOGPTS-2:0] wA_r;
  reg rAmsb_r1, rAmsb_r2;
  wire[DWIDTH-1:0] P_w, Q_w, outPQ;

  wrapRam #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) outBuf_0
    (.D(inP_r), .Q(P_w), .wA(wA_r), .rA(rA[LOGPTS-2:0]),
     .wEn(wEn_r), .clk(clk));
  wrapRam #(.LOGPTS(LOGPTS), .DWIDTH(DWIDTH)) outBuf_1
    (.D(inQ_r), .Q(Q_w), .wA(wA_r), .rA(rA[LOGPTS-2:0]),
     .wEn(wEn_r), .clk(clk));
  
  assign outPQ =  rAmsb_r2 ? Q_w : P_w;

  always @ (posedge clk) begin
    inP_r <= #1 inP;      inQ_r <= #1 inQ;        //pipes
    wEn_r <= #1 wEn;      wA_r  <= #1 wA;
    rAmsb_r2 <= #1 rAmsb_r1;    rAmsb_r1  <= #1 rA[LOGPTS-1];
    outD <= #1 outPQ;
  end
endmodule

/************************* T W I D D L E   L U T *******************************
RAM-block based twiddle LUT                                                   */
module twidLUT (clk, wA, wEn, rA, D, Q);
  parameter LOGPTS = 8;
  parameter TDWIDTH = 32;
  input clk, wEn;
  input [TDWIDTH-1:0] D;
  output[TDWIDTH-1:0] Q;
  input [LOGPTS-2:0] rA, wA;

  reg[LOGPTS-2:0] rA_r;

  wrapRam #(.LOGPTS(LOGPTS), .DWIDTH(TDWIDTH)) twidLUT_0
    (.D(D), .Q(Q), .wA(wA), .rA(rA_r), .wEn(wEn), .clk(clk));
  always @ (posedge clk)
      rA_r <= #1 rA;
endmodule

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

// -----------------------  Simple Round Up: 1-clk delay  ----------------------
// ---------- Use it when it is known INBITWIDTH > OUTBITWIDTH -----------------
// Partially truncated input comes in: LSB's are truncated but one extra bit
// retained
module kitRndUp (nGrst, rst, clk, clkEn, inp, valInp, outp, valOutp);
  parameter OUTBITWIDTH = 12;
  parameter RND_MODE    = 0;

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

  reg[OUTBITWIDTH:0] outp_int;

  assign outp = outp_int[OUTBITWIDTH:1];

  always @(posedge clk or negedge nGrst) begin

    if(!nGrst) begin
      outp_int    <= 'b0;
      valOutp <= 1'b0;
    end
    else
      if (rst) begin
        outp_int    <= #1 'b0;
        valOutp <= #1 1'b0;
      end
      else
        if(clkEn) begin
          if(valInp)
            if(RND_MODE) outp_int <= #1 inp + 1'b1;
            else         outp_int <= #1 inp;
          valOutp <= #1 valInp;
        end
  end
endmodule


module agen(clk, a, t, out) /* synthesis syn_preserve=1 */;
  parameter WIDTH    = 16;
  parameter TWIDTH   = 16;
  parameter RND_MODE = 0;
  input clk;
  input [WIDTH-1:0] a;
  input [TWIDTH-1:0] t;
  output[WIDTH-1:0] out;

  reg [WIDTH-1:0] a_r;
  reg [TWIDTH-1:0] t_r;
  wire [WIDTH+TWIDTH-1:0] out_w;
  reg[WIDTH:0] out1;
  reg[WIDTH-1:0] out2;

  actar actar_0(.DataA(a_r),.DataB(t_r),.Mult(out_w),.Clock(clk));

    kitRndUp #(.OUTBITWIDTH(WIDTH), .RND_MODE(RND_MODE) ) rndUp_0
      (.nGrst(1'b1), .rst(1'b0), .clk(clk), .clkEn(1'b1),
       .inp(out1), .valInp(1'b1), .outp(out), .valOutp() );

	always@(posedge clk)
    begin       //pipes
      a_r <= #1 a;
      t_r <= #1 t;
      out1 <= #1 out_w[WIDTH+TWIDTH-1:TWIDTH-1];
    end
endmodule

module bfly2 (clk, upScale, inP, inQ, T, outP, outQ,
              validIn, validOut, swCrossIn, swCrossOut);
  parameter WIDTH     = 16;
  parameter TWIDTH    = 16;
  parameter DWIDTH    = 32;
  parameter TDWIDTH   = 32;
  parameter RND_MODE  = 0;

  input clk; 
  //Signals need to be delayed by the bfly latency. That's why they are here
  input validIn, swCrossIn;  
  input upScale;     // don't do downscaling if upScale==1
  input [DWIDTH-1:0] inQ, inP;
  input [TDWIDTH-1:0] T;
  output [DWIDTH-1:0] outP, outQ;
  output validOut, swCrossOut;
  reg [DWIDTH-1:0] outP, outQ;
  reg validOut, swCrossOut;
  // CONVENTION: real - LSBs[WIDTH-1:0], imag - MSBs[DWIDTH-1:WIDTH]
  wire [WIDTH-1:0] inPr_w, inPi_w, inQr_w, inQi_w;
  wire [TWIDTH-1:0] Tr_w  = T[TWIDTH-1:0];
  wire [TWIDTH-1:0] Ti_w  = T[TDWIDTH-1:TWIDTH];
  wire [WIDTH-1:0] Hr_w, Hi_w;
  reg  [WIDTH-1:0] PrT1_r,PrT2_r,PrT3_r,PrT4_r,PrT5_r,PrT6_r;
  reg  [WIDTH-1:0] PiT1_r,PiT2_r,PiT3_r,PiT4_r,PiT5_r,PiT6_r; 
  reg  [WIDTH-1:0] Hr, Hi;
  wire [WIDTH-1:0] QrTr_w, QiTi_w, QiTr_w, QrTi_w;

  reg[1:0] pipe1, pipe2, pipe3, pipe4, pipe5, pipe6;

  // select either 16-bit value or sign-extended 15-bit value (downscaled one)
  assign #1 inPr_w =upScale ? 
          inP[WIDTH-1:0] : {inP[WIDTH-1], inP[WIDTH-1:1]};
  assign #1 inPi_w =upScale ? 
          inP[DWIDTH-1:WIDTH] : {inP[DWIDTH-1],inP[DWIDTH-1:WIDTH+1]};
  
  // select either 16-bit value or left-shifted value (upscaled one)
  assign #1 inQr_w =upScale ? {inQ[WIDTH-2:0], 1'b0} : inQ[WIDTH-1:0];
  assign #1 inQi_w =upScale ? {inQ[DWIDTH-2:WIDTH], 1'b0} : inQ[DWIDTH-1:WIDTH];

  agen #(.WIDTH(WIDTH), .TWIDTH(TWIDTH), .RND_MODE(RND_MODE)) am3QrTr
    (.clk(clk),.a(inQr_w),.t(Tr_w),.out(QrTr_w));
  agen #(.WIDTH(WIDTH), .TWIDTH(TWIDTH), .RND_MODE(RND_MODE)) am3QiTi
    (.clk(clk),.a(inQi_w),.t(Ti_w),.out(QiTi_w));
  agen #(.WIDTH(WIDTH), .TWIDTH(TWIDTH), .RND_MODE(RND_MODE)) am3QiTr
    (.clk(clk),.a(inQi_w),.t(Tr_w),.out(QiTr_w));
  agen #(.WIDTH(WIDTH), .TWIDTH(TWIDTH), .RND_MODE(RND_MODE)) am3QrTi
    (.clk(clk),.a(inQr_w),.t(Ti_w),.out(QrTi_w));
  
  assign #1 Hr_w = QrTr_w + QiTi_w;
  assign #1 Hi_w = QiTr_w - QrTi_w;

  always @ (posedge clk)
    begin
      outQ[DWIDTH-1:WIDTH]  <= #1 (PiT6_r - Hi);
      outQ[WIDTH-1:0]       <= #1 (PrT6_r - Hr);
      outP[DWIDTH-1:WIDTH]  <= #1 (PiT6_r + Hi);
      outP[WIDTH-1:0]       <= #1 (PrT6_r + Hr);

      // pipes  
      PrT6_r <= #1 PrT5_r;    PiT6_r <= #1 PiT5_r;
      PrT5_r <= #1 PrT4_r;    PiT5_r <= #1 PiT4_r;
      PrT4_r <= #1 PrT3_r;    PiT4_r <= #1 PiT3_r;
      PrT3_r <= #1 PrT2_r;    PiT3_r <= #1 PiT2_r;
      PrT2_r <= #1 PrT1_r;    PiT2_r <= #1 PiT1_r;
      PrT1_r <= #1 inPr_w;    PiT1_r <= #1 inPi_w;
      Hr <= #1 Hr_w;          Hi <= #1 Hi_w;
      
      validOut <= #1 pipe6[0];    swCrossOut <= #1 pipe6[1];
      pipe6 <= #1 pipe5; 
      pipe5 <= #1 pipe4; 
      pipe4 <= #1 pipe3; 
      pipe3 <= #1 pipe2; 
      pipe2 <= #1 pipe1;
      pipe1[0] <= #1 validIn;     pipe1[1] <= #1 swCrossIn; 
    end
endmodule


module autoScale (clk, clkEn, ldRiskOV, bflyRiskOV, startLoad, ifo_loadOn,
          startFFT, bflyOutValid, wLastStage, wEn_even, wEn_odd, upScale);
  parameter SCALE_MODE = 1;  // autoscaling ON
  input clk, clkEn;
  input ldRiskOV, bflyRiskOV;
  input startLoad, ifo_loadOn, startFFT, bflyOutValid, wLastStage;
  input wEn_even, wEn_odd;
  output upScale;
  reg upScale;

  reg ldMonitor, bflyMonitor;
  wire stageEnd_w;

  edgeDetect #(.INPIPE(0), .FEDGE(1)) fedge_0 (.clk(clk), .clkEn(clkEn),
                .in(bflyOutValid&(!wLastStage)), .out(stageEnd_w));

  always @ (posedge clk) begin
    // Initialize ldMonitor
    if(startLoad)   ldMonitor <= #1 SCALE_MODE;
    else // Monitor the data being loaded: turn down ldMonitor
         // if any valid input data violates the condition
      if(ldRiskOV & (wEn_even|wEn_odd)) ldMonitor <= #1 1'b0;

    // monitor the data being FFT'ed
    if(bflyRiskOV & bflyOutValid) bflyMonitor <= 1'b0;

    //check ldMonitor on startFFT (startFFT coinsides with the next startLoad)
    if(startFFT) begin
      upScale <= #1 ldMonitor;
      // initialize bflyMonitor
      bflyMonitor <= #1 SCALE_MODE;
    end
    else
    // Check the bflyMonitor at a stage end except the last stage, since the
    // end of the last stage may come on or even after the startFFT signal
    // when the upScale is supposed to check the ldMonitor only
      if(stageEnd_w) begin
        upScale <= #1 bflyMonitor;
        // initialize bflyMonitor at the beginning of every stage
        bflyMonitor <= #1 SCALE_MODE;
    end
  end
endmodule
