OOPs based testbench of combinational adder

//The code uses the OOPs based layered techniq for architecting the testbench.The result generated by the DUT are passed to the acoreboard through monitor. But the result generated by the reference model differs due to synchronization. Whats the issue?
Any suggestion will be appreciated.

//code link : fullcode link

//================ design ===============//
module adder(a,b,c);
  input [3:0] a,b;
  output [4:0] c;
  assign c=a+b;
endmodule

interface intr;
  logic [3:0] a,b;
  logic [4:0] c;
endinterface
//=============== test =================//
class transaction;
  rand logic [3:0] a,b;
  logic [4:0] c;
  constraint c_a {a inside {[10:15]};}
  constraint c_b {b inside {[0:9]};}
  function dis(string name);
    $display("----------%0s----------",name);
  endfunction
endclass

class generator;
  int test_vector;
  mailbox gen2drive;
  mailbox gen2scr;
  transaction txn=new();
  function new(mailbox gen2drive,mailbox gen2scr);
    this.gen2drive=gen2drive;
    this.gen2scr=gen2scr;
  endfunction
  task run(int test_vector);
    for(int i=0;i<test_vector;i++)begin
      if(!txn.randomize())
        $display("transaction is not done");
      else begin
        gen2drive.put(txn);
        gen2scr.put(txn);
        txn.dis("[   Packet send   ]");
      end
      #2;
    end
  endtask
endclass

class driver;
  mailbox gen2drive;
  virtual intr v_intr;
  transaction trans;
  int test_vector;
  function new(virtual intr v_intr,mailbox gen2drive);
    this.v_intr=v_intr;
    this.gen2drive=gen2drive;
    trans=new();
  endfunction
  task run(int test_vector);
    for(int i=0;i<test_vector;i++)begin
      gen2drive.get(trans);
      v_intr.a=trans.a;
      v_intr.b=trans.b;
      #2;
      trans.c=v_intr.c;
      trans.dis("[ Packet received ]");
      $display("%g a=%0d b=%0d sum=%0d",$time,v_intr.a,v_intr.b,v_intr.c);
    end
  endtask
endclass

class monitor;
  virtual intr v_intr;
  mailbox mon2scr;
  function new(virtual intr v_intr,mailbox mon2scr);
    this.v_intr=v_intr;
    this.mon2scr=mon2scr;
  endfunction
  task run(int test_vector);
    logic [4:0] c;
    for(int i=0;i<test_vector;i++)begin 
    	c=v_intr.c;
    	mon2scr.put(c);
      	#2;
    end 
  endtask
endclass


class agent;//it contain monitor,generator,driver
  mailbox mon2scr;
  mailbox gen2scr;
  mailbox gen2drive;
  virtual intr v_intr;
  generator gen;
  driver drive;
  monitor mon;
  function new(virtual intr v_intr,mailbox gen2scr,mailbox mon2scr);
    this.mon2scr=mon2scr;
    this.gen2scr=gen2scr;
    gen2drive=new();//no upper level access
    drive=new(v_intr,gen2drive);
    mon=new(v_intr,mon2scr);
    gen=new(gen2drive,gen2scr);
  endfunction
  task run(int test_vector);
    fork
      gen.run(test_vector);
      drive.run(test_vector);
      mon.run(test_vector);
    join
  endtask
endclass

class score_board;
  mailbox mon2scr;
  mailbox gen2scr;
  generator gen;
  transaction txn;
  function new(mailbox gen2scr,mailbox mon2scr);
    this.mon2scr=mon2scr;
    this.gen2scr=gen2scr;
    txn=new();
  endfunction
  task check(int test_vector);
    logic [4:0] exp_sum;
    logic [4:0] calculated_sum;
    for(int i=0;i<test_vector;i++)begin 
    gen2scr.get(txn);
    mon2scr.get(calculated_sum);   
    exp_sum=txn.a+txn.b;
      $display("calculated_sum=%0d,exp_sum=%0d",calculated_sum,exp_sum);
    if(exp_sum==calculated_sum)
      $display("PASS");
    else
      $display("FAIL");
      #2;
    end 
  endtask

endclass

class environment;
  mailbox gen2scr;
  mailbox mon2scr;
  virtual intr v_intr;
  agent age;
  score_board scr;
  function new(virtual intr v_intr);
    gen2scr=new();
    mon2scr=new();
    this.v_intr=v_intr;
    age=new(v_intr,gen2scr,mon2scr);
    scr=new(gen2scr,mon2scr);
  endfunction

  task run(int test_vector);
    fork
    age.run(test_vector);
    scr.check(test_vector);
    join
  endtask
endclass

program test(intr i_intr,int test_vector);
  environment env;
  initial begin
    env=new(i_intr);
    env.run(test_vector);
  end
endprogram

module tb();
  intr i_intr();
  test ts(i_intr,5);
  adder dut(.a(i_intr.a),.b(i_intr.b),.c(i_intr.c));
endmodule

In reply to designMaster:

Your methodology has a race condition. You start driving and monitoring at time 0, and there is not guarantee which happens first. Non-blocking assignments would help in your driver, as well as have a clock.

In reply to dave_59:

You mean clock at testbench not in design as I have combinational ADDER.

In reply to designMaster:

Yes.