`include "uvm_pkg.sv"
import uvm_pkg :: *;
typedef enum {ADD,SUB,MUL,DIV} inst_t; 

class instruction extends uvm_sequence_item; 
  rand inst_t inst; 
  
  `uvm_object_utils_begin(instruction) 
  `uvm_field_enum(inst_t,inst, UVM_ALL_ON) 
  `uvm_object_utils_end 
  
  function new (string name = "instruction"); 
    super.new(name); 
  endfunction 
endclass : instruction

class producer extends uvm_component; 
  uvm_blocking_put_port#(instruction) put_port; 
  `uvm_component_utils(producer)
  function new(string name, uvm_component parent = null); 
    super.new(name,parent); 
  endfunction : new

  function void build_phase (uvm_phase phase);
    super.build_phase (phase);
    put_port = new("put_port", this); 
  endfunction : build_phase

  task run_phase (uvm_phase phase); 
    for(int i = 0; i < 5; i ++) begin 
      instruction ints; 
      #10; 
      ints = new(); 
      if(ints.randomize()) begin 
        `uvm_info("producer", $sformatf("sending %s",ints.inst.name()), UVM_LOW) 
        put_port.put(ints); 
      end
      else begin
        `uvm_fatal("producer", $sformatf("Randomization failed"))
      end
    end 
  endtask : run_phase
endclass : producer

class consumer extends uvm_component;
  uvm_blocking_get_port#(instruction) get_port;
  `uvm_component_utils(consumer)

  function new(string name, uvm_component parent = null);
    super.new(name,parent);
    get_port = new("get_port", this);
  endfunction
  
  task run_phase (uvm_phase phase);
    //for(int i = 0 ; i < 5; i ++ )begin
    forever begin
      instruction inst;
      get_port.get(inst);
      `uvm_info("consumer", $sformatf("receiving %s",inst.inst.name()), UVM_LOW) 
      //push the transaction into queue or array
      //or drive the transaction to next levelt
      //or drive to interface
    end
  endtask : run_phase
endclass : consumer

class my_test extends uvm_test;
  producer p; 
  consumer c; 
  uvm_tlm_fifo #(instruction) f;
  `uvm_component_utils(my_test)
  function new(string name = "my_test", uvm_component parent = null); 
    super.new(name, parent); 
  endfunction : new

  function void build_phase (uvm_phase phase);
    super.build_phase (phase);
    p = new("producer",  this); 
    c = new("consumer",  this); 
    f = new ("tlm_fifo", this);
  endfunction : build_phase
  
  function void connect_phase (uvm_phase phase); 
    super.connect_phase (phase);
    p.put_port.connect(f.put_export);
    c.get_port.connect(f.get_export);
  endfunction : connect_phase

  task run_phase (uvm_phase phase); 
    phase.raise_objection(this);
    #100; 
    phase.drop_objection(this);
  endtask : run_phase
endclass : my_test

module top();
  initial begin 
    run_test("my_test"); 
  end 
endmodule : top