Parametrization

In reply to Digesh:

The uvm_driver class is just

class uvm_driver #(type REQ=uvm_sequence_item,
                   type RSP=REQ) extends uvm_component;
  uvm_seq_item_pull_port #(REQ, RSP) seq_item_port;

  uvm_analysis_port #(RSP) rsp_port;

  REQ req;
  RSP rsp;

  function new (string name, uvm_component parent);
    super.new(name, parent);
    seq_item_port    = new("seq_item_port", this);
    rsp_port         = new("rsp_port", this);
  endfunction

  const static string type_name = "uvm_driver #(REQ,RSP)";
  virtual function string get_type_name ();
    return type_name;
  endfunction

endclass

Let say you want to create a different kind of driver with two different seq_item_pull ports and no response port.

class my_driver extends uvm_component;
  `uvm_component_utils(my_driver)
  uvm_seq_item_pull_port #(my_req_type1, my_rsp_type1) seq_item1_port;
  uvm_seq_item_pull_port #(my_req_type2, my_rsp_type2) seq_item2_port;

  my_req_type1 req1;
  my_req_type2 req2;
  my_rsp_type1 rsp1;
  my_rsp_type2 rsp2;

  function new (string name, uvm_component parent);
    super.new(name, parent);

  endfunction

  function void build_phase(uvm_phase phase);
    seq_item1_port    = new("seq_item1_port", this);
    seq_item2_port    = new("seq_item2_port", this);
    ...
  endfunction

  task run_phase(uvm_phase phase);
    fork
       forever send1; // tasks that would get their respective items from each sequencer
       forever send2;
    join
  endtask
endclass

You could do something similar a monitor or coverage collector, etc. The key is the component is written with specific transaction classes in mind.