// *************************************************************************** //
// file     : HBF2L_tb.v
// desp     : testbench of HBF2L
// author   : duweitao@cuc.edu.cn
// date     : July.20th.2007
// version  : 0.1
// modified : 
// *************************************************************************** // 
// testbench of module HBF2L

`include "stim_def.v"
`include "timescale.v"
// `define RTLSIM 1

module testcase1();
  parameter period_CLK          = 14;
  parameter time_SETUP          = 10;
  parameter time_RESET          = 0;
  parameter num_period_RESET    = 4;

  parameter num_cycle_in        = 32;   // cycle number of one input data 


  parameter time_TESTCASE1_ON       = (num_period_RESET+1)*period_CLK;


  harness_filter U_hflt();

  defparam  U_hflt.period_CLK       = period_CLK;
  defparam  U_hflt.time_SETUP       = time_SETUP;
  defparam  U_hflt.num_cycle_in     = num_cycle_in;

  // init port and generate clock
  initial    
  begin
    U_hflt.U_stim.PortInit();       // init the stim port
    U_hflt.U_stim.GenClk();         // task GenClk will never return (forever clock)
  end

  // reset and generate the clock
  initial
  begin
    U_hflt.U_stim.CodeRamInit();    // init code ram
    U_hflt.U_cmpOut.CodeRamInit();    // init code ram

    #100 U_hflt.U_stim.Reset(num_period_RESET);
    #100 Testcase1Run(time_TESTCASE1_ON);
  end

// -------------------------------------------------------------------------------
// testcase1, run this task at time 0 
task Testcase1Run;
 
  input   [31:0] startTime; 
  reg     [31:0] errNum;
  begin
    #startTime    ;
    $display("testcase1 run at time %0d\n", $time);
    U_hflt.U_stim.SendData();
    $display("testcase1 off at time %0d\n", $time);
    errNum = U_hflt.U_cmpOut.errNum;
    $display("-------------------------------------------------------------------------");
    $display("TOTAL RTL MISMATCH RESULTS IS %d", errNum);
    $display("--------------------------------------------------------------------------");
    $stop         ;
  end
endtask

endmodule // module testcase1


// #########################################################################################################
module harness_filter();

// ---------------------------------------------------
// data width definition
// these data width MUST accroding with RTL defination
// ---------------------------------------------------
    parameter  STIMLEN       = `STIMLEN; 
    parameter  INPUTWL       = `INPUTWL;
    parameter  OUTPUTWL      = `OUTPUTWL;
    parameter  COEFWL        = `COEFWL; 
   
    parameter  num_cycle_in  = 0;
    
    parameter  time_SETUP    = 0;
    parameter  period_CLK    = 0;


wire clkW, rstW, dinenW, doutenW;
wire [INPUTWL-1:0]  diniW, dinqW;
wire [OUTPUTWL-1:0] doutiW, doutqW;
wire [1:0] outidxW;
wire       clkoutW;

reg CLK;

stim U_stim (
  .CLK  (clkW  ),       // clock
  .RST  (rstW  ),       // reset, high reset
  .DINI (diniW ),       // data input I
  .DINQ (dinqW ),       // data input Q
  .DINEN(dinenW));      // data input enable      

defparam U_stim.INPUTWL         = INPUTWL;
defparam U_stim.OUTPUTWL        = OUTPUTWL;
defparam U_stim.STIMLEN         = STIMLEN;
defparam U_stim.time_SETUP      = time_SETUP;
defparam U_stim.period_CLK      = period_CLK;
defparam U_stim.num_cycle_in    = num_cycle_in;

HBF2L U_hbf(
  .CLK          (clkW   ),   // clock , posedge 
  .RST          (rstW   ),   // high level reset asyn
  .DINI         (diniW  ),   // data input I
  .DINQ         (dinqW  ),   // data input Q
  .DINEN        (dinenW ),   // data input enable
  .DOUTI        (doutiW ),   // data output I
  .DOUTQ        (doutqW ),   // data output Q
  .DOUTEN       (doutenW),   // data output enable
  .OUTIDX       (outidxW),   // data output index
  .CLKOUT       (clkoutW));  // clock output only for simulation

// global param
`ifdef RTLSIM
defparam U_hbf.INPUTWL   = INPUTWL;
defparam U_hbf.OUTPUTWL  = INPUTWL;
`endif

compareOut U_cmpOut(
  .CLK   (clkoutW   ),   // clock , posedge 
  .RST   (rstW      ),   // high level reset asyn
  .DOUT  (doutiW    ),   // data output I
  .DOUTEN(doutenW   ),   // data output enable
  .OUTIDX(outidxW   ));  // data output index
defparam U_cmpOut.OUTPUTWL      = OUTPUTWL;
defparam U_cmpOut.STIMLEN       = STIMLEN ;

`ifdef RTLSIM

dumprtl U_dumprtl();
 
parameter K1        = U_hbf.K1      ;
parameter K1LOG2    = U_hbf.K1LOG2  ;
parameter K2        = U_hbf.K2      ;
parameter K2LOG2    = U_hbf.K2LOG2  ;
parameter C2Q       = U_hbf.C2Q     ;
parameter PCYWL     = U_hbf.U_pecnt.PCYWL;
parameter D_ADDRWL  = U_hbf.U_dch_I.U_dpath.U_dwrctl.ADDRWL;


defparam U_dumprtl.INPUTWL   = INPUTWL ;
defparam U_dumprtl.OUTPUTWL  = OUTPUTWL;
defparam U_dumprtl.COEFWL    = COEFWL  ;
defparam U_dumprtl.K1        = K1      ;
defparam U_dumprtl.K1LOG2    = K1LOG2  ;
defparam U_dumprtl.K2        = K2      ;
defparam U_dumprtl.K2LOG2    = K2LOG2  ;
defparam U_dumprtl.C2Q       = C2Q     ; 
defparam U_dumprtl.D_ADDRWL  = D_ADDRWL; 
defparam U_dumprtl.PCYWL     = PCYWL;

assign   U_dumprtl.CLK      = U_hbf.CLK ;
assign   U_dumprtl.RST      = U_hbf.RST ;
assign   U_dumprtl.DINEN    = U_hbf.DINEN;
assign   U_dumprtl.DINI     = U_hbf.DINI;
assign   U_dumprtl.DINQ     = U_hbf.DINQ;

assign   U_dumprtl.PECY     = U_hbf.U_pecnt.PECY;

assign   U_dumprtl.WA0      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WA0;
assign   U_dumprtl.WA1      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WA1;
assign   U_dumprtl.WE0      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WE0;
assign   U_dumprtl.WE1      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WE1;
assign   U_dumprtl.WD0      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WD0;
assign   U_dumprtl.WD1      = U_hbf.U_dch_I.U_dpath.U_dwrctl.WD1;

assign   U_dumprtl.DATA0   = U_hbf.U_dch_I.U_dpath.U_pe.DATA0; 
assign   U_dumprtl.DATA1   = U_hbf.U_dch_I.U_dpath.U_pe.DATA1;
assign   U_dumprtl.COEF    = U_hbf.U_dch_I.U_dpath.U_pe.COEF ;
assign   U_dumprtl.PEOUT   = U_hbf.U_dch_I.U_dpath.U_pe.PEOUT;
assign   U_dumprtl.DOUT    = U_hbf.U_dch_I.DOUT; 
assign   U_dumprtl.OUTIDX  = U_hbf.U_dch_I.OUTIDX;
assign   U_dumprtl.DOUTEN  = U_hbf.U_dch_I.DOUTEN;



`endif

endmodule   // module harness_ACS

// #########################################################################################################
// module compareOut(), compare the RTL result and the MATLAB result
module compareOut(
  CLK   ,   // clock , posedge 
  RST   ,   // high level reset asyn
  DOUT  ,   // data output I
  DOUTEN,   // data output enable
  OUTIDX);  // data output index
parameter OUTPUTWL      = 0;
parameter STIMLEN       = 0;
parameter OUTLEN        = STIMLEN*4;

input                   CLK, RST;
input                   DOUTEN;
input   [OUTPUTWL-1:0]  DOUT;
input   [1:0]           OUTIDX;

reg [OUTPUTWL-1:0] codeRam[OUTLEN - 1:0];   // code block ram
reg [31:0]  inNum;
reg signed [OUTPUTWL-1:0]  doutRTL, doutMat;
reg [31:0]  errNum;

integer fidOutDump;
initial begin
  fidOutDump    = $fopen("RTLOutDump.m");
  $display("compareOut: open out dump file @%0d, fidOutDump = %d", $time, fidOutDump);
  errNum = 0;
end

always@(posedge CLK or posedge RST) begin
  if(RST) begin
  end
  else begin
    if(DOUTEN) begin
      if(OUTIDX == 0) begin
        inNum = inNum + 1;
      end
      doutRTL = DOUT;
      doutMat = codeRam[inNum*4+OUTIDX];
      if(doutRTL!=doutMat) begin
        $write("inNum = %5d, OUTIDX=%2d, ", inNum, OUTIDX);
        $write("RTL = %5x, MAT=%5x, ", doutRTL, doutMat);
        $write("@time%8d\n", $time);
        errNum = errNum + 1;
      end
      $fwrite(fidOutDump, "%6d,\n", doutRTL);
    end
  end
end

task CodeRamInit;
  reg[254*8:0] RESULTFILENAME;
begin
  inNum = -1;
  RESULTFILENAME = "HBF2LResultData.dat";
  $display("compareOut.CodeRamInit() Called @ %0d", $time);
  $readmemb(RESULTFILENAME, codeRam);
end
endtask

endmodule // module compareOut();

// #########################################################################################################
`ifdef RTLSIM
module dumprtl();

parameter INPUTWL   = 0;
parameter OUTPUTWL  = 0;
parameter COEFWL    = 0;

parameter K1        = 0;
parameter K1LOG2    = 0;
parameter K2        = 0;
parameter K2LOG2    = 0;
parameter C2Q       = 0; 
parameter D_ADDRWL  = 0;

parameter PCYWL     = 0;

wire CLK, RST;
wire DINEN;
wire signed [INPUTWL-1:0] DINI, DINQ;

wire            [PCYWL-1:0]     PECY; 
wire            [D_ADDRWL-1:0]  WA0, WA1;
wire                            WE0, WE1;
wire    signed  [INPUTWL-1:0] WD0, WD1;
wire    signed  [INPUTWL-1:0] DATA0, DATA1;
wire    signed  [INPUTWL-1:0] COEF;
wire    signed  [INPUTWL-1:0] PEOUT;


wire    [OUTPUTWL-1:0]  DOUT;
wire    [1:0]           OUTIDX;
wire                    DOUTEN;

reg [31:0]  dataInNum;
reg             [PCYWL-1:0]     pecyD1R, pecyD2R, pecyD3R, pecyD4R; 
integer fidRtlDump;
initial begin
  dataInNum = 0;
  fidRtlDump    = $fopen("rtldump.dat");
  $display("dumprtl: open acs dump file @%0d, fidRtlDump = %d", $time, fidRtlDump);
end
always @ (posedge CLK) begin
  pecyD1R <= PECY   ;
  pecyD2R <= pecyD1R; 
  pecyD3R <= pecyD2R; 
  pecyD4R <= pecyD3R; 
end
always @ (posedge CLK) begin
  DumpRTL();
end

// ------------------------------------------------------------------------------
// task DumpRTL()
task DumpRTL; begin
    // DumpIn();
    // DumpPe();
    // DumpWrRam();
    DumpOut();
end
endtask // task DumpRTL;
// ------------------------------------------------------------------------------
// task DumpIn()
task DumpIn;
  if(DINEN) begin
    $fwrite(fidRtlDump, "DATAIN %3d @%8d", dataInNum, $time);
    $fwrite(fidRtlDump, "  DINI = 0x%-6x, DINQ = 0x%-6x", DINI, DINQ);
    dataInNum = dataInNum + 1;
  end
endtask // task DumpIn()
// ------------------------------------------------------------------------------
// task DumpWrRam()
task DumpWrRam;
  if(((PECY>=0)&&(PECY<=(K1+2*K2)))||DINEN==1) begin
    if((WE0 == 1)||(WE1 == 1)) begin
      $fwrite(fidRtlDump, "  PECY = %2d", PECY);
      $fwrite(fidRtlDump, "  WE0 = %d, WA0 = %d, WD0 = 0x%-0x,", WE0, WA0, WD0);
      $fwrite(fidRtlDump, "  WE1 = %d, WA1 = %d, WD1 = 0x%-0x ", WE1, WA1, WD1);
      $fwrite(fidRtlDump, "  @%0d\n", $time);
    end
  end
endtask // task DumpWrRam()
/// ------------------------------------------------------------------------------
// task DumpPe()
task DumpPe;
begin
  if(PECY == 19)    begin// catch y1n0 
    $fwrite(fidRtlDump, "  PECY  = %2d", PECY);
    $fwrite(fidRtlDump, "  PEOUT = %5x", PEOUT);
    $fwrite(fidRtlDump, "  @%8d  ", $time);
  end
  if(PECY == 17)    begin// catch y2n1 
    $fwrite(fidRtlDump, "  PECY  = %2d", PECY);
    $fwrite(fidRtlDump, "  PEOUT = %5x", PEOUT);
    $fwrite(fidRtlDump, "  @%8d", $time);
  end
  if(PECY == 27)    begin// catch y2n0 
    $fwrite(fidRtlDump, "  PECY  = %2d", PECY);
    $fwrite(fidRtlDump, "  PEOUT = %5x", PEOUT);
    $fwrite(fidRtlDump, "  @%8d\n", $time);
  end
end // task
endtask // task DumpPe()
/// ------------------------------------------------------------------------------
// task DumpOut()
task DumpOut;
begin
  if(DOUTEN) begin
    $fwrite(fidRtlDump, "OUTIDX=%2d, DOUT=%5x(%-6d,),  @%-8d", OUTIDX, DOUT, DOUT, $time);
    if(OUTIDX == 3) begin
      $fwrite(fidRtlDump, "\n");
    end
  end
end // task
endtask // task DumpOut()
//------------------------------------------------------------------------------
  
endmodule   // module dumprtl
`endif
// #########################################################################################################

module stim (
  CLK       ,       // clock
  RST       ,       // reset, high reset
  DINI      ,       // data input I
  DINQ      ,       // data input Q
  DINEN     );      // data input enable      

parameter INPUTWL       = 0;
parameter OUTPUTWL      = 0;
parameter STIMLEN       = 0;
parameter num_cycle_in  = 0;

parameter time_SETUP    = 0;
parameter period_CLK    = 0;
parameter time_HOLD     = period_CLK - time_SETUP;

// num_BlkIv should NOT less than 3*TBLEN, for flush of TB RAM

output CLK, RST, DINEN; 
output [INPUTWL-1:0]    DINI; 
output [OUTPUTWL-1:0]   DINQ;

reg CLK, RST, DINEN; 
reg [INPUTWL-1:0]    DINI; 
reg [OUTPUTWL-1:0]   DINQ;

reg [INPUTWL-1:0] codeRam[STIMLEN - 1:0];   // code block ram
// -------------------------------------------------------------------------------------------
// task PendOnClk, wait for 'ticks' number of CLK 
task PendOnClk;
input [31:0] ticks, residua; 
begin
  repeat (ticks) @ (posedge CLK) ;
  # residua;
end
endtask
// -------------------------------------------------------------------------------------------
// task GenClk
task GenClk;
  reg   [31:0] halfPeriod;
  begin
    halfPeriod = period_CLK/2;
    forever
    begin
      #halfPeriod CLK = ~CLK;
    end
  end
endtask     // task GenClk
// -------------------------------------------------------------------------------------------
// task PortInit, init the port
task PortInit;
fork
  CLK       = 0;
  RST       = 0;
  DINI      = 0; 
  DINQ      = 0; 
  DINEN     = 0; 
join
endtask
// -------------------------------------------------------------------------------------------
// task PortInit, init the port
task CodeRamInit;
  reg[254*8:0] STIMFILENAME;
begin
  STIMFILENAME = "HBF2LStimData.dat";
  $display("stim.CodeRamInit() Called @ %0d", $time);
  $readmemb(STIMFILENAME, codeRam);
end
endtask
// -------------------------------------------------------------------------------------------
// generate reset signal 
task Reset;
  input [31:0] numCLK;
  begin
    $display("stim.Reset() called @ time %0d, reset DUT %0d clock period", $time, numCLK);
    RST = 1;
    repeat (numCLK) @ (posedge CLK)
    # time_HOLD;
    RST = 0;
  end
endtask     // task Reset
// -------------------------------------------------------------------------------------------
// SendData, send the I/Q data, each data send at the first cycle of num_cycle_in clock cycles
//
task SendData;
  reg [31:0] codeIdx;
  reg [31:0] pendClkNum;
  reg [INPUTWL-1:0]     dataIn;
  begin
    pendClkNum = num_cycle_in ; 
    for(codeIdx = 0; codeIdx < STIMLEN; codeIdx = codeIdx + 1) begin
      dataIn = codeRam[codeIdx];
      DINI = dataIn;
      DINQ = dataIn;
      DINEN = 1;
      PendOnClk(1, time_HOLD);
      DINEN = 0;
      DINI  = DINI;
      DINQ  = DINQ;
      PendOnClk(pendClkNum - 1, time_HOLD);
    end
    PendOnClk(pendClkNum - 1, time_HOLD);
  end       // task end
endtask     // task SendData
// -------------------------------------------------------------------------------------------
endmodule // module : stim
// ###########################################################################################

