`define ADDR_WIDTH 16
`define DATA_WIDTH 8

module ram_tb(); 

  reg clk,rst;
 
 //write signals
  reg wr_enb;
  reg [(`ADDR_WIDTH/4)-1:0] wr_addr;
  reg [`DATA_WIDTH-1:0] wr_data;

 //read signals
  reg rd_enb;
  reg [(`ADDR_WIDTH/4)-1:0] rd_addr;
  wire [`DATA_WIDTH-1:0] rd_data;
  
  //DUT Intantiation
  ram DUT (clk,
       rst,
       wr_enb,
       wr_addr,
       wr_data,
       rd_enb,
       rd_addr,
       rd_data);
  
//reference model
  reg [`DATA_WIDTH-1:0] rm [0:`ADDR_WIDTH-1];
  
  reg [`DATA_WIDTH-1:0] exp_data;
  
  
  parameter CYCLE = 10;
  
 //clock generation 
  initial begin
    clk = 1'b0;
    forever
    #(CYCLE/2) clk = ~clk;
   end
  
 //reset task 
  task reset();
   begin
    @(negedge clk);
    rst = 1'b1;
    @(negedge clk);
    rst = 1'b0;
   end
  endtask
  
 //task for write in ram
  task write(input [(`ADDR_WIDTH/4)-1:0] write_addr, input [`DATA_WIDTH-1:0] write_data);
   begin
    @(negedge clk);
    wr_enb = 1'b1;
    wr_addr = write_addr;
    wr_data = write_data;
   end                                                            
   endtask
 
 //task for reading from ram
  task read(input [(`ADDR_WIDTH/4)-1:0] read_addr);
   begin
     @(negedge clk);
    rd_enb = 1'b1;
    rd_addr = read_addr;
   end
  endtask
  
 //reset check
  task reset_check();
    begin
      @(negedge clk);
      rst     = 1'b1;
      wr_enb  = 1'b1;
      wr_addr = 4'd10;
      wr_data = 8'd23;     
      @(negedge clk);
      rd_enb  = 1'b1;
      rd_addr = 4'd10;
      @(posedge clk);
      if (rd_data == 0)
          $display("RESET IS WORKING FINE !");
      else
          $display("oops ! RESET IS NOT WORKING FINE !");
     end
  endtask


 //reference model logic   
  initial begin
    forever  
     @(posedge clk) begin
     if (rd_enb)
        exp_data = rm[rd_addr];
     if (wr_enb)
        rm[wr_addr] = wr_data;
    end
   end
   
   
 always@(posedge clk)
   if (rd_data !== 0 && rd_data !== 8'dx) begin
      if (rd_data === exp_data)
          $display(" SUCCESS ! RD_data = %d : %d = exp_data",rd_data,exp_data);
      else
          $display(" DATA MISMATCH ! RD_data = %d : %d = exp_data",rd_data,exp_data);
   end

  initial begin
    reset_check();
    reset();
  //continuous write
    repeat(20)
    write({$random},{$random});
    wr_enb = 1'b0; 
  //continuous read
    repeat(20)
    read({$random});
    rd_enb = 1'b0;
  //simultaneous read write
  fork 
     begin
      repeat(20)
       write({$random},{$random});
     end
     begin
      repeat(20)
       read({$random});
     end
  join

    wr_enb = 1'b0;
    rd_enb = 1'b0;
    #100 $finish;
  end
    
endmodule