// ***************************************************************************/
//Microsemi Corporation Proprietary and Confidential
//Copyright 2013 Microsemi Corporation. All rights reserved.
//
//ANY USE OR REDISTRIBUTION IN PART OR IN WHOLE MUST BE HANDLED IN
//ACCORDANCE WITH THE MICROSEMI LICENSE AGREEMENT AND MUST BE APPROVED
//IN ADVANCE IN WRITING.
//
//Description:  CoreFIR
//              Fully Enumerated filter test bench
//
//Revision Information:
//Date         Description
//16Jan2013    Initial Release
//
//SVN Revision Information:
//SVN $Revision: 14801 $
//SVN $Data: $
//
//Resolved SARs
//SAR     Date    Who         Description
//
//Notes:

`timescale 1 ns/100 ps


module testbench;
    `include "coreparameters_tgi.v"
  // Number of symm taps
  localparam ODD_SYMM  = ((COEF_SYMM!=0) && ((TAPS & 'b1)==1)) ? 1 : 0;
  localparam TAPS_SYMM = (TAPS>>1) + ODD_SYMM;
  localparam PHY_TAPS  = (COEF_SYMM == 0) ? TAPS : TAPS_SYMM;

  localparam WRAP_LAYERS = 2*INP_REG;

    localparam DATA_VALID_INTERVAL = 0;   // 0 - no interval
    localparam DATA_PATTERN        = 3;
    localparam NGRSTnotRST         = 1;   //rt

    localparam CD_WIDTH = DATA_WIDTH+COEF_WIDTH + ceil_log2(TAPS);
    wire                  firo_valid, clk;
    wire                  nGrst;
    wire                  data_valid, data_valid_gold, stop_samples;
    wire [DATA_WIDTH-1:0] datai, datai_gold;
    wire [CD_WIDTH-1:0]   firo, gold_firo;
    wire                  datai_valid, datai_valid_latency, match_datao_valid;
    reg                   swap_page, gold_rst;
    reg                   coef_ref;
    reg  [3:0]            coef_sel;
    reg                   coefi_valid;
    reg  [COEF_WIDTH-1:0] coefi;
    reg  [10:0]           coefi_count;
    reg                   start_samples;
    reg                   start_coefi;
    reg                   stop_coefi;
    wire [COEF_WIDTH-1:0] coef_arr [0:COEF_SETS-1][0:TAPS-1];
    reg  [COEF_WIDTH-1:0] coefi_arr [0:COEF_SETS-1][0:TAPS-1];
    reg signed [DATA_WIDTH-1:0] data_arr [0:TAPS-1];
    reg signed [CD_WIDTH  -1:0] firo_arr;
    reg signed [CD_WIDTH  -1:0] firo_arr_fifo [0:1023];
    integer shift_loop;
    integer coefi_loop;
    integer coef_sel_loop;
    integer convolve_loop;
    integer error_count, firo_count;
    integer wr, rd;
    wire firo_xbit;
    wire fifo_ready, fifo_rdy;
    wire ngrst2uut, rstn2uut;                   //rt

  LMS_FIR_TOP_COREFIR_0_COREFIR # (
    .CFG_ARCH     (1),    // Fully enum
    .TAPS         (TAPS),
    .COEF_TYPE    (COEF_TYPE),
    .COEF_SETS    (COEF_SETS),
    .COEF_SYMM    (COEF_SYMM),
    .COEF_WIDTH   (COEF_WIDTH),
    .COEF_UNSIGN  (COEF_UNSIGN),
    .DATA_WIDTH   (DATA_WIDTH),
    .DATA_UNSIGN  (DATA_UNSIGN),
    .SYSTOLIC   (SYSTOLIC),
//rt    .VALID_O    (1),
    .INP_REG    (INP_REG),
    .DIE_SIZE     (DIE_SIZE),
    .FPGA_FAMILY  (FPGA_FAMILY  ),
    // Unused params
    .URAM_MAXDEPTH(5),
    .COEF_RAM   (5),
    .DATA_RAM   (5),
    .SAMPLEID   (5),
    .ID_WIDTH   (5),
    .SAMPLE_RATE(5),
    .L (5),
    .M (5),
    .PERFORMANCE  (0 ),
    .RADIX        (10),
    .CLOCK_RATE (5)     ) uut_0 (
      .NGRST        (ngrst2uut),                                      //rt
      .RSTN         (rstn2uut),                                       //rt
      .CLK          (clk),
      .DATAI        (datai),
      .DATAI_VALID  (data_valid),
      .COEFI        (coefi),
      .COEFI_VALID  (coefi_valid),
      .COEF_ON      (swap_page),
      .COEF_SEL     (coef_sel),
      .COEF_REF     (1'b0),
      .COEF_REF_DONE(),
      .FIRO         (firo),
      .DATAO_VALID  (firo_valid),
      .SAMPLE_ID    (5'b0),
      .READY        (),
      .DATAO        (),
      .FIRO_ID      (),
      .RCLK         (),
      .COEF_ON_SLOT ()   );

  bhvClockGen # (.CLKPERIOD(10), .NGRSTLASTS(24)  ) clock_0 (
    .clk (clk),  .nGrst (nGrst) );
    
  // Create sync reset to test if nGrst and rst do the same   //rt start
  bhvDelay # (.DELAY(2), .WIDTH(1)) sync_ngrst_0
    (.nGrst(nGrst), .rst(1'b0), .clk(clk), .clkEn(1'b1),
     .inp(nGrst), .outp(ngrst_synced)   );
  bhvEdge # (.REDGE(1)) gen_sync_rst_0 (  
    .nGrst(nGrst), .rst(1'b0), .clk(clk), .inp(ngrst_synced), .outp(sync_rst) );    
    
  assign ngrst2uut = (NGRSTnotRST==1)? nGrst : 1'b1;
  assign  rstn2uut = (NGRSTnotRST==1)? 1'b1  : ~sync_rst;
  //rt end
    
  // TEMP TO VERIFY THE UUT LATENCY
  // Apply delay equal to the UUT latency
  bhvDelay # (  .DELAY(PHY_TAPS-1),
                .WIDTH(1) ) latency_match_0 (
    .nGrst(nGrst), .rst(1'b0), .clk(clk), .clkEn(datai_valid),
    .inp(datai_valid),
    .outp(datai_valid_latency) );

  bhvDelay # (  .DELAY(4),
                .WIDTH(1) ) latency_match_1 (
    .nGrst(nGrst), .rst(1'b0), .clk(clk), .clkEn(1'b1),
    .inp(datai_valid_latency),
    .outp(match_datao_valid) );
  // TEMP TO VERIFY THE UUT LATENCY ends

  // -------------------  CHECK THE RESULTS --------------------
  assign firo_xbit = ^firo;
  assign gold_firo = firo_arr_fifo[rd];
  always @ (negedge nGrst or posedge clk) begin
    if (!nGrst) begin
      rd = TAPS;
      error_count = 0;
      firo_count  = 0;
    end
    else
      if (swap_page==1'b1)
        rd = TAPS;
      else begin
        if(fifo_rdy==1'b1) begin
          $display("");
          if( (COEF_TYPE==0) && (COEF_SETS==1) )
            $display("Running Constant Coefficient filter test");
          if( (COEF_TYPE==0) && (COEF_SETS>1) )
            $display("Running Coefficient Set %d", coef_sel+1);
          if(COEF_TYPE==1)
            $display("Running Reloadable Coefficient Test");
        end

        if((firo_valid == 1'b1) && (start_samples==1'b1) ) begin

          if(firo_xbit===1'bx)  // detect x state
            $display("ERROR: Filter output is undefined (x) at time %t", $time);
          else begin
            if ($signed(firo) !== firo_arr_fifo[rd]) begin
              $display("ERROR: expected filter output = %d, Actual = %d",
                                              firo_arr_fifo[rd], $signed(firo));
              error_count = error_count + 1;
            end
            else
              $display("time:%t Match: Filter output = %d, Golden model = %d",
                                      $time, $signed(firo), firo_arr_fifo[rd]);
            firo_count = firo_count+1;
          end
          rd = rd + 1;
        end
      end
  end

  // coefi generator
  always @ (negedge nGrst or posedge clk) begin
    if (!nGrst) begin
      coefi <= 0;
      coefi_valid <= 1'b0;
      coefi_count <= 0;
      stop_coefi <= 1'b0;
	    // Copy external coef_arr[][] of sd_COREFIR_0_coef.v in
	    // internal coefi_arr[][]
	    for(coef_sel_loop=0; coef_sel_loop<COEF_SETS;
                                         coef_sel_loop=coef_sel_loop+1) begin
        if(COEF_SYMM == 0)
          for(coefi_loop=0; coefi_loop<TAPS; coefi_loop=coefi_loop+1)
            coefi_arr[coef_sel_loop][coefi_loop] <=
                                            coef_arr[coef_sel_loop][coefi_loop];
        if(COEF_SYMM == 1) begin  // Symmetric
          for(coefi_loop=0; coefi_loop<TAPS_SYMM; coefi_loop=coefi_loop+1)
            coefi_arr[coef_sel_loop][coefi_loop] <=
                                            coef_arr[coef_sel_loop][coefi_loop];
          for(coefi_loop=TAPS_SYMM; coefi_loop<TAPS; coefi_loop=coefi_loop+1)
            coefi_arr[coef_sel_loop][coefi_loop] <=
                                  coef_arr[coef_sel_loop][TAPS-1-coefi_loop];
        end
        if(COEF_SYMM == 2) begin  // Anti-symmetric
          for(coefi_loop=0; coefi_loop<TAPS_SYMM; coefi_loop=coefi_loop+1)
            coefi_arr[coef_sel_loop][coefi_loop] <=
                                            coef_arr[coef_sel_loop][coefi_loop];
          for(coefi_loop=TAPS_SYMM; coefi_loop<TAPS; coefi_loop=coefi_loop+1)
            coefi_arr[coef_sel_loop][coefi_loop] <=
                                  - coef_arr[coef_sel_loop][TAPS-1-coefi_loop];
        end
      end
    end
    // On start_coefi fill in coefficient_set_0 with $urandom values and output
    // these one per clock to the coefi reg.
    // start_coefi goes active in reload mode only
    else begin
      if(COEF_SYMM==0) begin
        if (start_coefi == 1'b1 && coefi_count<TAPS) begin
          if (COEF_UNSIGN==0)
            coefi_arr[0][coefi_count] = $urandom_range(2**(COEF_WIDTH-1)-1, 0) -
                                                              2**(COEF_WIDTH-2);
          else
            coefi_arr[0][coefi_count] = $urandom_range(2**(COEF_WIDTH-1)-1, 0);

          coefi_count <= coefi_count+1;
          coefi <= coefi_arr[0][coefi_count];
          coefi_valid <= 1'b1;
          stop_coefi <= 1'b0;
        end
        else if (coefi_count == TAPS) begin
          coefi <= 0;
          coefi_count <= coefi_count;
          coefi_valid <= 1'b0;
          stop_coefi <= 1'b1;
        end
      end   // non-symmetric

      else begin  // Symmetric and anti-symmetric
        if (start_coefi == 1'b1 && coefi_count<TAPS_SYMM) begin
          if (COEF_UNSIGN==0) begin
            coefi_arr[0][coefi_count] = $urandom_range(2**(COEF_WIDTH-1)-1, 0) -
                                                              2**(COEF_WIDTH-2);
            coefi_arr[0][TAPS-1-coefi_count] =
                                      (3-2*COEF_SYMM)*coefi_arr[0][coefi_count];

          end
          else  begin
            coefi_arr[0][coefi_count] = $urandom_range(2**(COEF_WIDTH-1)-1, 0);
            coefi_arr[0][TAPS-1-coefi_count] = coefi_arr[0][coefi_count];
          end
          coefi_count <= coefi_count+1;
          coefi <= coefi_arr[0][coefi_count];
          coefi_valid <= 1'b1;
          stop_coefi <= 1'b0;
        end
        else if (coefi_count == TAPS_SYMM) begin
          coefi <= 0;
          coefi_count <= coefi_count;
          coefi_valid <= 1'b0;
          stop_coefi <= 1'b1;
        end
      end   // symmetric
    end
  end

//      _   _   _   _     _   _   _   _   _     _   _   _
//     / \ / \ / \ / \   / \ / \ / \ / \ / \   / \ / \ / \
//    ( D | A | T | A ) ( V | A | L | I | D ) ( G | E | N )
//     \_/ \_/ \_/ \_/   \_/ \_/ \_/ \_/ \_/   \_/ \_/ \_/

  // generate data_valid
  gen_data_valid_bhv #      // 0-no interval, 1-one-clk interval, etc
    (.DATA_VALID_INTERVAL(DATA_VALID_INTERVAL)) data_valid_to_uut_0 (
      .nGrst(nGrst), .clk(clk),
      .start_samples(start_samples),
      .data_valid(data_valid) );

  // create datai_valid similar to the one used by UUT (data_valid delayed
  // by wrap pipes).  The testbench model will run on the datai_valid to
  // replicate timing of the UUT
  bhvDelay # (  .DELAY(WRAP_LAYERS),
                .WIDTH(1) ) datai_valid_local_0 (
    .nGrst(nGrst), .rst(1'b0), .clk(clk), .clkEn(1'b1),
    .inp(data_valid),
    .outp(datai_valid) );

  // ------------------------------------- Data generator
  data_gen_bhv # (
    .DATA_PATTERN(DATA_PATTERN),
    .TAPS        (TAPS        ),
    .DATA_UNSIGN (DATA_UNSIGN ),
    .DATA_WIDTH  (DATA_WIDTH  ) ) data_to_uut_0 (
      .nGrst(nGrst), .clk(clk),
      .swap_page    (swap_page    ),
      .start_samples(start_samples),
      .data_valid   (data_valid   ),
      .stop_samples (stop_samples ),
      .datai        (datai        )      );

  //    _____         _      _                    __  _  _  _
  //   / ____|       | |    | |                  / _|(_)| || |
  //  | |  __   ___  | |  __| |  ___  _ __      | |_  _ | || |_  ___  _ __
  //  | | |_ | / _ \ | | / _` | / _ \| '_ \     |  _|| || || __|/ _ \| '__|
  //  | |__| || (_) || || (_| ||  __/| | | |    | |  | || || |_|  __/| |
  //   \_____| \___/ |_| \__,_| \___||_| |_|    |_|  |_||_| \__|\___||_|

  // Create time-advanced copies of data_valid and datai to give the golden
  // model a headstart to put some golden samples in FIFO
  gen_data_valid_bhv #      // 0-no interval, 1-one-clk interval, etc
    (.DATA_VALID_INTERVAL(DATA_VALID_INTERVAL)) data_valid_to_gold_0 (
      .nGrst(nGrst), .clk(clk), 
      .start_samples(1'b1), 
      .data_valid(data_valid_gold) );

  data_gen_bhv # (
    .DATA_PATTERN(DATA_PATTERN),
    .TAPS        (TAPS        ),
    .DATA_UNSIGN (DATA_UNSIGN ),
    .DATA_WIDTH  (DATA_WIDTH  ) ) data_to_gold_0 (
      .nGrst(nGrst), .clk(clk), 
      .swap_page    (swap_page    ), 
      .start_samples(1'b1), 
      .data_valid   (data_valid_gold   ), 
      .stop_samples (), 
      .datai        (datai_gold        )      );

  always @ (negedge nGrst or posedge clk) begin
    if (nGrst==1'b0)  begin
      for (shift_loop=0; shift_loop<TAPS; shift_loop=shift_loop+1)
        data_arr[shift_loop] <= 0;
      
      gold_rst <= 1'b1;
    end
    
    else 
      if(swap_page==1'b1) begin         
        gold_rst <= 1'b0;               
        wr       <= 0;                  
      end

    if(gold_rst==1'b1)    wr <= 0;
    else begin
      if(data_valid_gold==1'b1)   begin  // shift the sample
        for (shift_loop=TAPS-1; shift_loop > 0; shift_loop=shift_loop-1)
          data_arr[shift_loop] <= data_arr[shift_loop-1];
        data_arr[0] <= datai_gold;

        // calculate output
        firo_arr = 0;
        for (convolve_loop = 0; convolve_loop < TAPS; convolve_loop = convolve_loop + 1) begin
          firo_arr = firo_arr +
                        $signed(coefi_arr[coef_sel][convolve_loop]) *
                        $signed(data_arr[convolve_loop]);
        end

        firo_arr_fifo[wr] = firo_arr;
        wr = wr + 1;
      end   //  data_valid_gold
    end   // not gold_rst
  end   //always

    `include "LMS_FIR_TOP_COREFIR_0_coef.v"
    assign fifo_ready = (wr==10);
    bhvEdge # (.REDGE(0)) fifo_ready_0 (
     .nGrst(nGrst), .rst(1'b0), .clk(clk),
     .inp(fifo_ready),
     .outp(fifo_rdy) );

    initial begin
      start_samples = 1'b0;
      swap_page     = 1'b0;
      coef_sel      = 4'b0;
      coef_ref      = 1'b0;
      start_coefi   = 1'b0;

      @ (posedge nGrst);    // wait for nGrst to deassert

      if (COEF_TYPE == 0 && COEF_SETS == 1) begin   // single coef set
        // pulse swap_page solely to fill in golden array
        repeat (2) @ (negedge clk);   // wait for 2 clks
        swap_page = 1'b1;
        repeat (1) @ (negedge clk);
        swap_page = 1'b0;
        repeat (2) @ (negedge clk);   // wait for 2 clks

        // wait for fifo_rdy to pulse
        @ (posedge fifo_rdy);
        @ (negedge fifo_rdy);

        repeat (3) @ (negedge clk);
        start_samples = 1'b1;
        @ (posedge stop_samples);
        start_samples = 1'b0;
        repeat (6*TAPS) @ (negedge clk);

      end
      else if (COEF_TYPE == 0 && COEF_SETS > 1) begin   // multiple coef sets
        repeat (3) @ (negedge clk);   // wait 3 clk
        coef_sel = 4'b0;

        repeat (COEF_SETS) begin
          repeat (2) @ (negedge clk);   // wait for 2 clks
          swap_page = 1'b1;
          repeat (1) @ (negedge clk);
          swap_page = 1'b0;

          // wait for fifo_rdy to pulse
          @ (posedge fifo_rdy);
          @ (negedge fifo_rdy);

          repeat (3) @ (negedge clk);
          start_samples = 1'b1;
          @ (posedge stop_samples);
          start_samples = 1'b0;
          repeat (4*TAPS) @ (negedge clk);
          coef_sel = coef_sel+1;
        end
      end

      else if (COEF_TYPE == 1) begin
        repeat (2) @ (negedge clk);
        start_coefi = 1'b1;
        @ (posedge stop_coefi);
        start_coefi = 1'b0;

        repeat (2) @ (negedge clk);
        swap_page = 1'b1;
        repeat (1) @ (negedge clk);
        swap_page = 1'b0;

        // wait for fifo_rdy to pulse
        @ (posedge fifo_rdy);
        @ (negedge fifo_rdy);

        repeat (3) @ (negedge clk);
        start_samples = 1'b1;
        @ (posedge stop_samples);
        start_samples = 1'b0;
        repeat (4*TAPS) @ (negedge clk);
      end

      if (error_count > 0)
        $display("TEST FAILED");
      else
        if (firo_count == 0)
          $display("TEST FAILED: CoreFIR has not generated any valid samples");
        else
          $display("TEST PASSED");
      $stop;
    end

// -------------------------
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


// start jan
module gen_data_valid_bhv (nGrst, clk, start_samples, data_valid);
  parameter DATA_VALID_INTERVAL = 0;  // 0-no interval, 1-one-clk interval, etc

  input nGrst, clk, start_samples;
  output data_valid;

  reg data_valid;
  integer data_valid_timer;

  always @ (negedge nGrst or posedge clk)
    if (!nGrst) begin
      data_valid <= 1'b0;
      data_valid_timer <= 0;
    end
    else  begin
      if (start_samples == 1'b1)
        if (data_valid_timer == DATA_VALID_INTERVAL) begin
          data_valid_timer <= 0;
          data_valid       <= 1'b1;
        end
        else  begin
          data_valid_timer <= data_valid_timer + 1;
          data_valid       <= 1'b0;
        end
      else
        data_valid <= 1'b0;
    end
endmodule

module data_gen_bhv (nGrst, clk, swap_page, start_samples, data_valid,
  stop_samples, datai);
  parameter DATA_PATTERN = 0;
  parameter TAPS = 16;
  parameter DATA_UNSIGN = 0;
  parameter DATA_WIDTH = 12;

  input nGrst, clk, swap_page, start_samples, data_valid;
  output stop_samples;
  output [DATA_WIDTH-1:0] datai;

  reg [DATA_WIDTH-1:0] datai;
  reg stop_samples;
  integer sample_count;

  always @ (negedge nGrst or posedge clk) begin
    if ((nGrst==1'b0) || (swap_page==1'b1)) begin
      if(DATA_PATTERN==0) datai <= 1;
      if(DATA_PATTERN==1) datai <= 2**(DATA_WIDTH-1)-1;
      if(DATA_PATTERN==2) datai <= 2**(DATA_WIDTH-1)-1;
      if(DATA_PATTERN==3) datai <= 0;
      if(DATA_PATTERN==4) datai <= 3;
      sample_count <= 0;
      stop_samples <= 0;
    end
    else begin
      if (start_samples == 1'b1) begin
        // generate samples going to the UUT
        if(data_valid==1'b1)  begin
          if(DATA_PATTERN==0) begin     // ascending from 1 up
            if(datai < {(DATA_WIDTH-1){1'b1}}) datai <= datai + 1;
            else datai <= 1;
          end

          if(DATA_PATTERN==1) begin     // descending from max positive
            if(datai > 0) datai <= datai - 1;
            else datai <= {DATA_WIDTH{1'b1}};
          end

          if(DATA_PATTERN==2) begin     // const max
            datai <= 2**(DATA_WIDTH-1)-1;
          end

          if( (DATA_PATTERN==3) && (DATA_UNSIGN==0) )
            datai <= (sample_count-2*TAPS) * 17*23*31;
          if( (DATA_PATTERN==3) && (DATA_UNSIGN==1) )
            datai <= $unsigned (sample_count * 17*23*31);


          sample_count = sample_count + 1;
          if (sample_count == TAPS * 4 )
            stop_samples <= 1'b1;
        end

      end
      else begin
        datai <= datai;
      end
    end
  end
endmodule
// end jan

