// ***************************************************************************/
//Actel Corporation Proprietary and Confidential
//Copyright 2009 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:  CoreFIR
//              Fully enumerated FIR filter RTL modules
//
//Revision Information:
//Date         Description
//08Jan2009    Initial Release
//
//SVN Revision Information:
//SVN $Revision: 14801 $
//SVN $Data: $
//
//Resolved SARs
//SAR     Date    Who         Description
//
//Notes:

`timescale 1 ns/100 ps

module LMS_FIR_TOP_COREFIR_0_fir_enum_g4 (nGrst, rstn, clk, clkEn,
              datai,
              datai_valid,
              coefi,
              coefi_valid,
              firo,
              datao_valid,
              coef_on,              // load coef_set in coef register
              coef_sel  );          // select coef_set
  parameter TAPS          = 100;
  parameter COEF_TYPE     = 0;      // 0-const, 1-reloadable
  parameter COEF_SETS     = 1;      // Num of coef sets; 1-single, etc
  parameter COEF_SYMM     = 0;      // 0-no symm, 1-symm, 2-antisymm
  parameter COEF_BITS     = 8;
  parameter COEF_UNSIGN   = 0;      // 0-signed, 1-unsigned
  parameter DATA_BITS     = 8;
  parameter DATA_UNSIGN   = 0;      // 0-signed, 1-unsigned
  parameter ACC_WIDTH     = 44;     // Hard output width
  parameter SYSTOLIC      = 0;
  parameter VALID_O       = 0;
  parameter COLUMN        = 16; // Mathblock column size: 2000D-16; 4000D-24
  parameter XREG_COEF     = 3;
  parameter PERFORMANCE   = 0;

  // Number of symm taps.  If odd symmetry, there is one more special tap
  localparam TAPS_PHY  = (COEF_SYMM == 0) ? TAPS : TAPS>>1;
  // Identify odd symmetry
  localparam ODD_SYMM  = ((COEF_SYMM!=0) && ((TAPS & 'b1)==1)) ? 1 : 0;
  localparam LATENCY1  = (COEF_SYMM == 0) ? 2 : 3;

  // If symmetical, enforce systolic mode, as it supports symmetry
  localparam SYST      = (COEF_SYMM == 0) ? SYSTOLIC : 1;

//sar57820  localparam XREGS=((TAPS_PHY-1)/COLUMN)*XREG_COEF; // number of extra reg
  localparam XREGS=((TAPS_PHY+ODD_SYMM-1)/COLUMN)*XREG_COEF; // number of extra reg   //sar57820

  // Transposed structure imposes fill-in latency; systolic adds up another latency
  localparam LATENC2 = SYSTOLIC ? 2*TAPS-2 : TAPS-1;      //jan
  localparam LATENC3 = TAPS + (TAPS>>1) + ODD_SYMM - 2;   //jan
  localparam LATENCY = (COEF_SYMM ? LATENC3 : LATENC2) + XREGS;

  // Normally turn_data can be obtained from syst_dly_line but in an odd
  // case it may be too short.  Then prolong syst_dly_line to MAXDLY.
  localparam MAXDLY = (TAPS-1 > 2*(TAPS_PHY-1)+XREGS) ?
                              TAPS-1 : 2*(TAPS_PHY-1)+XREGS;

  input nGrst, rstn, clk, clkEn;
  input datai_valid;
  input [DATA_BITS-1:0] datai;
  input [COEF_BITS-1:0] coefi;
  output [ACC_WIDTH-1:0]  firo;
  output datao_valid;
  input coef_on;
  input coefi_valid;
  input [3:0] coef_sel;
  
  integer index;
//6/16/2011  wire tap0;
  reg  [DATA_BITS-1:0] data_zero = 0;
  wire [ACC_WIDTH-1:0] odd_firo;
//6/16/2011  wire [DATA_BITS-1:0] data2odd;

  wire [ACC_WIDTH-1:0] cd_w    [0: TAPS_PHY];
  wire [ACC_WIDTH-1:0] pout_w  [0: TAPS_PHY];
  wire [ACC_WIDTH-1:0] carryin [0: TAPS_PHY];

  wire [DATA_BITS-1:0] data_w  [0: TAPS_PHY-1+ODD_SYMM];
  wire [DATA_BITS-1:0] data_symm_w  [0: TAPS_PHY-1+ODD_SYMM];
  reg  [DATA_BITS-1:0] syst_dly_line[0: MAXDLY-1];
  reg  [DATA_BITS-1:0] short_dly_line[0:(TAPS_PHY/COLUMN)*XREG_COEF-1];
  wire [DATA_BITS-1:0] short_dly_line_w[0:TAPS_PHY/COLUMN];
  wire [DATA_BITS-1:0] short_dly_inp, turn_data;

  wire [COEF_BITS-1:0] coef_w  [0: TAPS_PHY];

  wire cdsel_odd;
  wire[ACC_WIDTH-1:0] carryin_odd;

  wire filled_w, coef_on_const, datao_valid_i;
  wire rst_latency_dly, synced_ngrst;                                   //rt
  
  reg  filled;
  reg  add_valid_r  /* synthesis syn_maxfan=1 */;
  reg  add_valid_tick, add_valid_tick2;

  wire[(TAPS_PHY+ODD_SYMM)*COEF_BITS-1:0] flat_coef_w;

  assign data_w[0]  = datai;
  assign cd_w[0]    = {ACC_WIDTH{1'b0}};
  assign pout_w[0]  = {ACC_WIDTH{1'b0}};

  assign firo = ODD_SYMM ? odd_firo : pout_w[TAPS_PHY];

  // For a single const coef set, reg B always accepts B input data
  assign coef_on_const = ((COEF_TYPE==0) && (COEF_SETS==1))? 1'b1 : coef_on;

//  ------------------------------------------------------------------------
//                     +-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+
//                     |F|a|b|r|i|c| |D|e|l|a|y| |L|i|n|e|
//                     +-+-+-+-+-+-+ +-+-+-+-+-+ +-+-+-+-+
//  ------------------------------------------------------------------------

  // The short delay line short_dly_line is used in
  // 1. Transposed structure to compensate for extra intercolumn/row regs 
  //    inferred in the cascade chain.  Equal number of regs are then inserted 
  //    in the data bus and those are called the short_dly_line
  // 2. Symmetric structure to implement turn_data delay line that serves the 
  //    same purpose as the one of the transposed structure
  // Note: Systolic delay line incorporates the regs of the short delay line 
  // The short delay line length = number of cascade breaks * XREG_COEF
  assign short_dly_inp = SYST ? turn_data : datai;

  generate
    begin: fill_short_dly
    if(TAPS_PHY>COLUMN)   // Only compensate for extra column regs
      always @ (posedge clk)
        if((clkEn==1'b1) && (datai_valid==1'b1)) begin
          short_dly_line[0]  <= short_dly_inp;
          for(index=1; index<(TAPS_PHY/COLUMN)*XREG_COEF; index=index+1)
            short_dly_line[index] <= short_dly_line[index-1];
        end
    end
  endgenerate

  // The systolic delay line syst_dly_line implements systolic data delay
  // The delay line has TAPS_PHY-1 double regs (the last regular tap does not
  // need the regs; neither the last odd symmetry tap needs these) plus extra
  // intercolumn regs.  It may have more regs to produce turn_data
  generate
    begin: fill_syst_dly
      if(SYST) begin
        always @ (posedge clk)
          if((clkEn==1'b1) && (datai_valid==1'b1)) begin
            syst_dly_line[0]  <= datai;
            for(index=0; index<MAXDLY-1; index=index+1)
              syst_dly_line[index+1] <= syst_dly_line[index];
          end
        assign turn_data = syst_dly_line[TAPS-2];
      end
    end
  endgenerate

  // Create short_dly_line taps
  assign short_dly_line_w[0] = short_dly_inp;
  generate
    begin: short_dly
    genvar l;
      for(l=1; l<=TAPS_PHY/COLUMN; l=l+1)
        begin: short_dly_tap
          assign short_dly_line_w[l] = short_dly_line[l*XREG_COEF-1];
        end
    end
  endgenerate

  // Create systolic delay line taps
  assign data_w[0] = datai;
  assign data_symm_w[0] = turn_data;
  generate
    begin: syst_dly
    genvar k;
    for(k=1; k<TAPS_PHY+ODD_SYMM; k=k+1)
      begin: syst_dly_tap
        assign data_w[k] = SYST ?
              syst_dly_line[2*k-1+(k/COLUMN)*XREG_COEF] :
                                short_dly_line_w[k/COLUMN];
        assign data_symm_w[k] = COEF_SYMM ? short_dly_line_w[k/COLUMN] : 'b0;
      end
    end
  endgenerate

  generate  // instantiate filter taps
    begin: taps
    genvar i;
    // array is a way to connect different constants to the first tap
    wire cdsel [0: TAPS_PHY-1];

    for(i=0; i<TAPS_PHY; i=i+1)
      begin: tap
        // pick CIN at taps 0, and after column break: i==COLUMN, 2*COLUMN, etc;
        // pick CDOUT otherwise
        assign cdsel[i]   = ((i%COLUMN)==0)? 1'b0 : 1'b1;
        assign carryin[i] = (i==0)? 'b0 : ((i%COLUMN)==0)? pout_w[i] : cd_w[i];

        LMS_FIR_TOP_COREFIR_0_tap_enum_g4 # (
          .COEF_SYMM(COEF_SYMM),  // 0-no symm, 1-symm, 2-antisymm
          .COEF_BITS(COEF_BITS),
          .COEF_UNSIGN(COEF_UNSIGN),
          .DATA_BITS(DATA_BITS),
          .DATA_UNSIGN(DATA_UNSIGN),
          .ACC_WIDTH(ACC_WIDTH),
          .CHAIN_BREAK(clmn_break(i+1)),
          .XREG_COEF(XREG_COEF)  ) a_tap (
        .nGrst(nGrst),
        .rstn (rstn),
        .clk  (clk),
        .clkEn(clkEn),
        .datai(data_w[i]),
        .en_p(add_valid_r & clkEn),
        .symm_datai(data_symm_w[i]),
        .coefi(coef_w[i]),
        .cascade_in (carryin[i]),
        .cdout(cd_w[i+1]),
        .pout(pout_w[i+1]),
        .cdsel(cdsel[i]),
        .coef_on(coef_on_const)     );
    end
    end
  endgenerate

  generate
    begin: odd_tap
    if(ODD_SYMM)   // Last special tap # TAPS_PHY for the odd symmetry case
      // pick CIN if after column break; pick CDOUT otherwise
      assign cdsel_odd    = ((TAPS_PHY%COLUMN)==0)? 1'b0 : 1'b1;
      assign carryin_odd  = ((TAPS_PHY%COLUMN)==0)? pout_w[TAPS_PHY] : cd_w[TAPS_PHY];

      LMS_FIR_TOP_COREFIR_0_odd_symmetry_tap_enum_g4 # (
          .COEF_BITS(COEF_BITS),
          .COEF_UNSIGN(COEF_UNSIGN),
          .DATA_BITS(DATA_BITS),
          .DATA_UNSIGN(DATA_UNSIGN),
          .ACC_WIDTH(ACC_WIDTH),
          .XREG_COEF(XREG_COEF)   ) last_tap (
        .nGrst(nGrst), .rstn (rstn), .clk  (clk), .clkEn(clkEn),
        .datai(short_dly_line_w[(TAPS_PHY-1)/COLUMN]),
        .coefi(coef_w[TAPS_PHY]),
        .en_p(add_valid_r & clkEn),
        .cascade_in (carryin_odd),
        .pout(odd_firo),
        .cdsel(cdsel_odd),      //const
        .coef_on(coef_on_const)     );
    end
  endgenerate

//  ---------------------------------------------------------------------------
//                              +-+-+-+-+-+-+-+
//                              |L|a|t|e|n|c|y|
//                              +-+-+-+-+-+-+-+
//  ---------------------------------------------------------------------------
  always @ (posedge clk)
      if(clkEn==1'b1)
        begin
          // Control hard adder chain with datai_valid delayed by 1 clk to
          // account for regA delay
          if(PERFORMANCE==0)                
            add_valid_r     <= datai_valid; 
          else                              
            add_valid_r     <= 1'b1;        
        end

  // Pipeline delay btw datai_valid and datao_valid.    
//rt start  assign rst_latency_dly = ~rstn | coef_on; 
  // Generate a sync reset at the rear end of NGRST
  enum_kitSync_ngrst # (.PULSE_WIDTH(1)) sync_ngrst_0 (
    .nGrst(nGrst), .clk(clk), .pulse(), .synced_ngrst(synced_ngrst)  ); //rt end

  assign rst_latency_dly = ~rstn | coef_on | ~synced_ngrst; 
  enum_kitDelay_bit_reg # (.DELAY(LATENCY1)) pipe_dly_0 (
    .nGrst(nGrst),
    .rst  (rst_latency_dly),
    .clk  (clk),
    .clkEn(clkEn),
    .inp(datai_valid),
    .outp(datao_valid_i) );

  // Valid filtered data signal marks the first clock where the valid output is
  // present.  It optionally accounts for the fill-in and systolic delays
//rt  generate
//rt    begin: valid_o_circuit
//rt      if(VALID_O)   begin     // add systolic delay
        enum_kitDelay_bit_reg # (.DELAY(LATENCY) ) fill_in_dly (
          .nGrst(nGrst),
          .rst  (rst_latency_dly),
          .clk  (clk),
          .clkEn(datao_valid_i),
          .inp(1'b1),
          .outp(filled_w) );
          
        assign datao_valid = datao_valid_i & filled_w;  
//rt      end
//rt      else begin
//rt        assign datao_valid = datao_valid_i;
//rt      end
//rt    end
//rt  endgenerate
//  -----------------------------   Latency ends  -----------------------------

  // Supply flattened coef array
  LMS_FIR_TOP_COREFIR_0_wide_coef # ( .TAPS (TAPS_PHY+ODD_SYMM),
                .COEF_BITS (COEF_BITS),
                .COEF_SETS (COEF_SETS),
                .COEF_TYPE (COEF_TYPE)  ) wide_coef_0 (
        .nGrst(nGrst),
        .rstn(rstn),
        .clk(clk),
        .clkEn(clkEn),
        .coefi(coefi),
        .coefi_valid(coefi_valid),
        .set(coef_sel),
        .flat_coefo(flat_coef_w)  );

  // Convert the flattened array back to a normal one
  generate
    genvar j;
    begin: flat2arr
      if(SYST==0)
        for(j=0; j<TAPS_PHY+ODD_SYMM; j=j+1) begin
          assign coef_w[j] = flat_coef_w[COEF_BITS*j +: COEF_BITS];
        end
      else
        for(j=0; j<TAPS_PHY+ODD_SYMM; j=j+1) begin
          assign coef_w[TAPS_PHY+ODD_SYMM-1-j] = flat_coef_w[COEF_BITS*j +: COEF_BITS];
        end
    end
  endgenerate

  // ------------------------ FUNCTION
  function integer clmn_break;
      input integer i;
    begin
      // don't do column break (fabric preg) if it is the last tap. Just use p
      if ((i%COLUMN==0) && (i!=TAPS_PHY)) clmn_break = 1;
      else                                clmn_break = 0;
    end
  endfunction

endmodule
