Global training solutions for engineers creating the world's electronics
Menu

UVM Golden Reference Guide - Sample Code Fragments

The following code fragments may be downloaded for use by readers of the Doulos UVM Golden Reference Guide. You may freely use them in your projects subject to the Apache 2.0 License both privately and commercially. Please do not publish or re-distribute to others except by recommending they get a copy of our UVM Golden Reference Guide. If you wish to use some fragment in a presentation or article you are writing, please request permission from Doulos.

Click here to download the examples below as a zip file, or use the links below to jump to a particular example.

Click on the links under each section to run prepared examples in a live simulation environment using EDA Playground.  

Click here to use EDA Playground to run your own sample code.

uvm_agent

Try out an example using this feature in EDA Playground

class example_agent_t extends uvm_agent;
  `uvm_component_utils(example_agent_t)

  example_sequencer_t #(example_transaction_t) m_sequencer;
  example_driver_t  m_driver;
  example_monitor_t m_monitor;
  uvm_analysis_port #(example_transaction_t) monitor_ap;
  virtual dut_if v_dut_if;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_monitor  = example_monitor_t::type_id::create("m_monitor", this);
    monitor_ap = new("monitor_ap", this);
    if ( get_is_active() == UVM_ACTIVE ) begin
      m_sequencer = example_sequencer_t::type_id::create(
                                    "m_sequencer", this);
      m_driver    = example_driver_t   ::type_id::create(
                                    "m_driver", this);
    end
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    m_monitor.monitor_ap.connect(monitor_ap);
    // code to connect monitor's virtual interface
    //...

    if ( get_is_active() == UVM_ACTIVE ) begin
        m_driver.seq_item_port.connect(m_sequencer.seq_item_export);
        // code to connect driver's virtual interface
        //...
    end
  endfunction: connect_phase
  //...
endclass: example_agent_t

uvm_algorithmic_comparator

Try out an example using this feature in EDA Playground

class cpu_scoreboard_t extends uvm_scoreboard;
  `uvm_component_utils(cpu_scoreboard_t)
  // Fetched instructions are observed here:
  uvm_analysis_export #(fetch_xact_t) af_fetch_export;
  // Execution results are observed here:
  uvm_analysis_export #(exec_xact_t) af_exec_export;
  Instr_Set_Simulator m_iss;
  uvm_algorithmic_comparator
    #(.BEFORE(fetch_xact_t), .AFTER(exec_xact_t),
      .TRANSFORMER(Instr_Set_Simulator) ) m_comp;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // Create the transformer object
    m_iss  = Instr_Set_Simulator::type_id::create("m_iss", this);
    // Create analysis exports
    af_fetch_export = new("af_fetch_export", this);
    af_exec_export  = new("af_exec_export",  this);
    // Supply the transformer object to the comparator
    m_comp          = new("comp", this, m_iss);
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    af_fetch_export.connect( m_comp.before_export );
    af_cpu_export.connect( m_comp.after_export );
  endfunction: connect_phase

  integer m_logfile;
  virtual function void start_of_simulation_phase(uvm_phase phase);
    m_logfile = $fopen("cpu_comparator_log.txt");
    set_report_id_action_hier("Comparator Match",UVM_LOG);
    set_report_id_file_hier ("Comparator Match",m_logfile);
    set_report_id_action_hier("Comparator Mismatch",UVM_LOG);
    set_report_id_file_hier("Comparator Mismatch",m_logfile);
  endfunction: start_of_simulation_phase

endclass: cpu_scoreboard_t

uvm_analysis_export

Try out an example using this feature in EDA Playground

class custom_subscr_1_t extends uvm_subscriber #(example_transaction_t);
  // code for first custom subscriber
  //...

class custom_subscr_2_t extends uvm_subscriber #(example_transaction_t);
  // code for second custom subscriber
  //...

class example_double_subscriber_t extends uvm_component;
  `uvm_component_utils(example_double_subscriber_t)

  custom_subscr_1_t subscr1;
  custom_subscr_2_t subscr2;
  uvm_analysis_export #(example_transaction_t) analysis_export;

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

  function void build_phase(uvm_phase phase);
    subscr1 = custom_subscr_1_t::type_id::create("subscr1", this );
    subscr2 = custom_subscr_2_t::type_id::create("subscr2", this );
    analysis_export = new ( "analysis_export", this );
  endfunction

  function void connect_phase(uvm_phase phase);
    // Connect the analysis export to both internal components
    analysis_export.connect(subscr1.analysis_export);
    analysis_export.connect(subscr2.analysis_export);
  endfunction

endclass

uvm_barrier

Try out an example using this feature in EDA Playground

class usb_driver_t extends uvm_driver#(usb_trans_t);
  `uvm_component_utils(usb_driver_t)
  uvm_barrier m_barrier;
  //...
  task post_reset_phase(uvm_phase phase);
     // Wait for the barrier before proceeding
     m_barrier.wait_for();
  endtask
endclass

class spi_driver_t extends uvm_driver#(spi_trans_t);
  `uvm_component_utils(spi_driver_t)
  uvm_barrier m_barrier;
  //...
  task pre_main_phase(uvm_phase phase);
     // Wait for the barrier before proceeding
     m_barrier.wait_for();
  endtask
endclass

class top_env extends uvm_env;
  `uvm_component_utils(top_env)
  uvm_barrier   m_barrier;
  usb_driver_t  m_usb_drv;
  spi_driver_t  m_spi_drv;
  //...
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);

    // Create a barrier for 2 processes
    m_barrier = new("m_barrier", 2);

    m_usb_drv = usb_driver_t::type_id::create("m_usb_drv",this);
    m_spi_drv = spi_driver_t::type_id::create("m_spi_drv",this);

    // Pass the barrier object to the drivers
    m_usb_drv.m_barrier = m_barrier;
    m_spi_drv.m_barrier = m_barrier;
  endfunction
/...
endclass

uvm_callback_iter

uvm_callback_iter#(mycomp_t,mycb_t) iter = new(this);
for (mycb_t cb = iter.first();cb != null;cb = iter.next())
  cb.dosomething();

Callbacks

Try out an example using this feature in EDA Playground

typedef class my_driver;
virtual class driver_cbs_t extends uvm_callback;
  `uvm_object_utils(driver_cbs_t)

  // Constructor
  function new(string name = "driver_cbs_t");
    super.new(name);
  endfunction

  // Callback method
  virtual task trans_received(my_driver_t drv, trans_t tr);
    drv.uvm_report_info("callback", "In trans_received()");
  endtask

  // Callback method
  virtual task trans_executed(my_driver_t drv, trans_t tr);
  endtask
endclass : driver_cbs_t
class my_driver_t extends uvm_driver#(trans_t);
  `uvm_register_cb(my_driver_t,driver_cbs_t)//Register callbacks
  `uvm_component_utils(my_driver_t)
  //...

  task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(tr);
      // Call callback task
      `uvm_do_callbacks(my_driver, driver_cbs, trans_received(this,tr))

      // Drive the transaction
      //...

      // Call callback task
      `uvm_do_callbacks(my_driver, driver_cbs, trans_executed(this,tr))

      seq_item_port.item_done(tr);
    end
  endtask : run_phase
endclass : my_driver_t
class error_cb_t extends driver_cbs_t;
  `uvm_object_utils(error_cb_t)

  // Constructor
  function new(string name = "error_cb_t");
    super.new(name);
  endfunction

  task trans_received(my_driver_t drv, trans_t tr);
    bit [2:0] i;
    // Twiddle up to 8 bits
    repeat ($urandom_range(8))
      assert(std::randomize(i)) begin
        tr.data[i] = ~tr.data[i]; // Corrupt the data
      end
      else
        drv.uvm_report_warning("Error Callback",
                      "Unable to select bit to twiddle!" );
  endtask

  task trans_executed(my_driver_t drv, trans_t tr);
    // Implement the trans_executed callback
  endtask
endclass : error_cb_t
class error_injector_test_t extends uvm_test;
  `uvm_component_utils(error_injector_test_t)
  //...
  function void start_of_simulation_phase(uvm_phase phase);
    my_driver_t drv;       // Driver in the environment
    error_cb_t e_cb;       // User defined callbacks class
    e_cb = new( "e_cb" );  // Create the error injecting callbacks
    // Find the driver where the callbacks will be installed
    $cast(drv, uvm_top.find( "*.m_drv" ));
    // Install the callbacks in the driver
    uvm_callbacks #(my_driver, driver_cbs)::add(drv, e_cb);
  endfunction : start_of_simulation_phase
endclass : error_injector_test_t

uvm_cmdline_processor

string all_cmdline_args[$];
string all_uvm_cmdline_args[$];

uvm_cmdline_proc.get_args(all_cmdline_args);
uvm_cmdline_proc.get_uvm_args(all_uvm_cmdline_args);

`uvm_info("", $sformatf(
          {"There were %0d command-line arguments, ",
           "including %0d UVM ones"},
           all_cmdline_args.size(),
           all_uvm_cmdline_args.size()),
          UVM_NONE)
% simulator \
 +uvm_set_verbosity=uvm_test_top.m_env.m_driver,_ALL_,UVM_FULL,build ...
% simulator \
 +uvm_set_verbosity=uvm_test_top.*,_ALL_,UVM_FULL,time,200 ...

uvm_comparer

Try out an example using this feature in EDA Playground

function bit My_transaction_t::do_compare(uvm_object rhs,
                                          uvm_comparer comparer);
  My_transaction_t t;
  bit result = 1;
  $cast(t,rhs);
  result &= comparer.compare_field("op", op, t.op, $bits(op));
  if (op != NOP)
    result &= comparer.compare_field("dst", dst, t.dst, $bits(dst));
     //...
     return result;
 endfunction : do_compare

UVM Compilation directives

% compiler +define+UVM_NO_DEPRECATED ...
% compiler +define+UVM_NO_DPI ...
% compiler +define+UVM_CB_TRACE_ON ...

uvm_component

class lookup_table_t #(WIDTH=10) extends uvm_component;
  uvm_blocking_get_imp #(int,lookup_table_t#(WIDTH)) get_export;

  int lut [WIDTH];
  int index = 0;

  // when using field automation - must be after definitions
  `uvm_component_param_utils_begin(lookup_table_t#(WIDTH))
    `uvm_field_sarray_int(lut,UVM_ALL_ON + UVM_DEC)
  `uvm_component_utils_end

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    foreach (lut[i]) lut[i] = i * 10;
    get_export = new("get_export",this);
  endfunction: build_phase

  task get (output int val);
    #10 val = lut[index++];
    if (index > WIDTH-1) index = 0;
  endtask: get

endclass: lookup_table_t

uvm_config_db

uvm_config_db #(int)::set(this,"*driver", "iterations", 50);
uvm_config_db #(int)::set(this,"/.*driver/", "iterations", 50);
if ( !uvm_config_db #(int)::get(this,"","iterations", n_iterations) )
  // In the absence of a config setting, use a default value:
  n_iterations = 20;
uvm_config_db#(uvm_sequence_base)::set( null,
  "/.*m_env.m_sequencer.run_phase/", // Regular expression
  "default_sequence",
  my_sequence::type_id::get());

Configuration

class verif_env_t extends uvm_env;
  int m_n_cycles;
  string m_lookup;
  instruction_t m_template;
  typedef enum {IDLE,FETCH,WRITE,READ} bus_state_t;
  bus_state_t m_bus_state;

  // remember when using field automation - must be after definitions
  `uvm_component_utils_begin(verif_env_t)
    `uvm_field_string(m_lookup,UVM_DEFAULT)
    `uvm_field_object(m_template,UVM_DEFAULT)
    `uvm_field_enum(bus_state_t,m_bus_state,UVM_DEFAULT)
  `uvm_component_utils_end
  //...
endclass: verif_env_t

class test2 extends uvm_test;
  `uvm_component_utils(test2)

  register_instruction_t inst = new();
  string str_lookup;
  //...
  function void build_phase(uvm_phase phase);
    //...
    uvm_config_db #(bus_state_t)::set(
      null,"*env1.*","m_bus_state",verif_env::IDLE);
    uvm_config_db #(string)::set(
      null,"*","m_lookup",str_lookup);
    uvm_config_db #(register_instruction_t)(
      null,"*","m_template",inst);
    //...
  endfunction : build_phase
  //...
endclass : test2
// In a test, create an entry "count" in the global configuration
// settings table ...

uvm_config_db#(int)::set(this,"*","count",1000);

// ... and retrieve the value of "count"

if (!uvm_config_db#(int)::get(this,"","count",m_n_cycles))
  m_n_cycles = 1500; // use default value

uvm_delegate_factory

class my_factory_t extends uvm_delegate_factory;
  virtual function void set_inst_override_by_type(
    uvm_object_wrapper original_type,
    uvm_object_wrapper override_type,
    string full_inst_path
  );
    `uvm_info("FACTORY","",UVM_DEBUG)
    delegate.set_inst_override_by_type(original_type,
      override_type, full_inst_path);
  endfunction
endclass
//...
function my_test::new(...);
  super.new(...);
  uvm_coreservice_t cs = uvm_coreservice_t::get();
  my_factory_t f = new();
  f.delegate = uvm_factory::get();
  cs.set_factory(f);
endfunction

uvm_driver

Try out an example using this feature in EDA Playground

class example_driver_t extends uvm_driver#(my_transaction_t);
  `uvm_component_utils(example_driver_t)
//...
  virtual task run_phase(uvm_phase phase);
    forever begin
      seq_item_port.get_next_item(req);
      phase.raise_objection(this);

      // Code to generate physical signal activity
      // as specified by transaction data in req

      seq_item_port.item_done();
      phase.drop_objection(this);
    end
  endtask

endclass: example_driver_t
class example_push_driver_t extends uvm_push_driver #(my transaction_t);
  `uvm_component_utils(example_push_driver_t)
  //...
  // Called by push_sequencer through TLM export
  task put (my_transaction_t item);
    // code to generate physical signal activity
    // as specified by transaction data in item
  endtask : put

  //...
endclass: example_push_driver_t

End of test

class my_sequence extends uvm_sequence#(my_transaction);
  `uvm_object_utils(my_sequence)

  // pre_start() is called before start()
  task pre_start();
    // raise objection if started as a root sequence
    starting_phase = get_starting_phase();  // UVM 1.2
    if ( starting_phase != null )
      starting_phase.raise_objection(this);
  endtask

  // start() handshakes the sequence's main activity - body()
  task body();
    // do interesting activity
    //...
  endtask

  // post_start() is called after start() completes
  task post_start();
    // drop objection if started as a root sequence
    if ( starting_phase != null )
      starting_phase.drop_objection(this);
  endtask
endclass
class my_test_t extends uvm_test;
  `uvm_component_utils(my_test_t)

  my_env_t       m_env;
  my_sequence_t  seq;

  //...
  task main_phase(uvm_phase phase);
    seq.starting_phase = phase;    // UVM-1.1
    seq.set_starting_phase(phase); // UVM-1.2
    seq.start(m_env.m_sequencer);
  endtask
  //...
endclass
class my_test_t extends uvm_test;
  `uvm_component_utils(my_test_t)

  my_env_t m_env;
  my_sequence_t seq;

  //...
  task main_phase(uvm_phase phase);
    phase.raise_objection(this);
    // blocks until the sequence has completed on the sequencer
    seq.start(m_env.m_sequencer);
    // Set a 'drain time' for the test
    uvm_objection obj = phase.get_objection();
    obj.set_drain_time( this, 100ns );
    phase.drop_objection(this);
  endtask
  //...
endclass

uvm_env

class verif_env_t extends uvm_env;
  `uvm_component_utils(verif_env_t)

  // Testbench methodology components
  //...

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

  function void build_phase(uvm_phase phase);
    // Instantiate top-level components using "new" or
    // the factory, as appropriate
    //...
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    // Connect ports-to-exports
    //...
  endfunction: connect_phase

  virtual task run_phase(uvm_phase phase);
    // Prevent simulation from ending - do this before the first wait.
    // Not needed here if it is in the test class
    phase.raise_objection(this);
    // Control stimulus generation
    //...
    // Allow simulation to end
    phase.drop_objection(this);
  endtask: run_phase

endclass: verif_env_t

uvm_event

Try out an example using this feature in EDA Playground

typedef uvm_event basic_tr_event_t;
class C extends uvm_component;
  `uvm_component_utils(C)
  basic_tr_event_t e1;
  function new (string name, uvm_component parent);
   super.new(name,parent);
  endfunction : new

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    e1 = new ("e1");
  endfunction: build_phase

  task run_phase(uvm_phase phase);
    basic_transaction_t tx,rx;
    tx = new();
    fork
      begin
        tx.data = 10;
        tx.addr = 1;
        #10 e1.trigger(tx);
      end
      begin
        e1.wait_ptrigger();
        $cast(rx, e1.get_trigger_data());
        rx.print();
      end
    join
  endtask: run_phase

endclass: C
class my_e_callback_t extends uvm_event_callback;
  function new (string name="");
    super.new(name);
  endfunction : new

  function void post_trigger(uvm_event e, uvm_object data=null);
    basic_transaction_t rx;
    if (data) begin
      $cast(rx,data);
      `uvm_info("CBACK",$sformatf("Received %s",
                        rx.convert2string()),UVM_NONE)
    end
  endfunction: post_trigger
endclass: my_e_callback_t
my_e_callback_t cb1;
//...
cb1 = new ("cb1");  //in build_phase
//...
e1.add_callback(cb1);  //in run_phase

uvm_factory

class verif_env_t extends uvm_env;
  // Register the environment with the factory
  `uvm_component_utils(verif_env_t)
  //...
  instruction_t m_template;
  //...
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    //...
    // Use the factory to create the m_template object
    m_template = instruction_t::type_id::create("m_template", this);
  endfunction : build_phase
endclass: verif_env_t

class test1 extends uvm_test;
  `uvm_component_utils(test1)
  verif_env_t env1;
  //...
  function void build_phase(uvm_phase phase);
    uvm_factory factory = uvm_factory::get();
    //...
    // Change type of m_template from instruction to
    // register_instruction using factory method
    factory.set_inst_override_by_name("instruction",
           "register_instruction","*env?.m_template");

     // Type overrides have lower precedence than inst overrides
     factory.set_type_override_by_type(
                      instruction_t::get_type(),
                      same_regs_instruction_t::get_type() );

     // Print all factory overrides and registered classes
     factory.print();

     // Call factory method to create top-level environment
     // (requires cast so type_id::create is generally preferred)
     $cast(env1,factory.create_component_by_type(
           verif_env_t::get_type(),"","env1",null) );
  endfunction : build_phase

  //...
endclass : test1

Field Macros

class basic_transaction extends uvm_sequence_item;
  rand bit[7:0] addr, data;
  //...
  `uvm_object_utils_begin(basic_transaction)
    `uvm_field_int(addr,UVM_ALL_ON)
    `uvm_field_int(data,UVM_ALL_ON | UVM_BIN)
  `uvm_object_utils_end
endclass : basic_transaction

uvm_get_to_lock_dap#(T)

uvm_get_to_lock_dap#(string) version;
version.set("Initial version");
version.set("Second version"); // Overwrite initial value
`uvm_info("DAP example",version.get(),UVM_MEDIUM);
assert(version.try_get(text) == 1);
assert(version.try_set(text) == 0);
version.set("cause an error");

uvm_heartbeat

Try out an example using this feature in EDA Playground

// Create an objection for heartbeat monitoring
uvm_callbacks_objection hb_objection = new("hb_objection");

// An instance of this class will be monitored
class my_driver_t extends uvm_driver #(trans_t);
  //...

  task run_phase(uvm_phase phase);
    //...
    // This must happen between every two event triggers in my_env
    // This in effect says "I'm alive"
    hb_objection.raise_objection(this);
    //...
  endtask

  //...
endclass : my_driver_t

class my_env_t extends uvm_env;
  //...
  uvm_event#(uvm_object) hb_event;
  my_driver_t m_driver;

  uvm_heartbeat hb;

  function new (string name, uvm_component parent);
    super.new(name,parent);
    // Create a heartbeat for my_env using hb_objection.
    hb = new("hb", this, hb_objection);
    hb.add(m_driver);
  endfunction

  //...

  task run_phase(uvm_phase phase);
    hb_event = new ("hb_event");
    //...

    hb.set_mode(UVM_ALL_ACTIVE);
    hb.start(hb_event);

    fork
      forever
        // At least one raise or drop must occur
        // between successive triggers
        #10 hb_event.trigger(this);
      //...
    join_any
    hb.stop();
    hb.remove(m_driver);
  endtask
  //...
endclass : my_env_t

HDL backdoor access

class fullchip_sb_t extends uvm_scoreboard;
  //...

  // Analysis port method that compares the returned value from the
  // design's interface with the RTL's current value

  function void write(input AXItran_t t);
    uvm_hdl_data_t value;

    // Probe into the design
    assert (uvm_hdl_read("top_tb.dut.csr", value)) else
         `uvm_error("FULLCHIPSB", "Cannot read top_tb.dut.csr");

    assert ( t.data === value )
       `uvm_error("FULLCHIPSB",
                  $sformatf("Returned value (%h) != RTL value (%h)",
                            t.data, value ))

  endfunction

endclass : fullchip_sb_t

uvm_in_order_comparator

Try out an example using this feature in EDA Playground

class cpu_scoreboard_t extends uvm_scoreboard;
  `uvm_component_utils(cpu_scoreboard_t)

  uvm_analysis_export #(exec_xact) af_iss_export;
  uvm_analysis_export #(exec_xact) af_cpu_export;
  uvm_in_order_class_comparator #(exec_xact) m_comp;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    af_iss_export = new("af_iss_export", this);
    af_cpu_export = new("af_cpu_export", this);
    m_comp        = new("comp",          this);
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    af_iss_export.connect( m_comp.before_export );
    af_cpu_export.connect( m_comp.after_export  );
  endfunction: connect_phase

  integer m_log_file;
  virtual function void
  start_of_simulation_phase(uvm_phase phase);
    m_log_file = $fopen("cpu_comparator_log.txt");
    set_report_id_action_hier("Comparator Match",UVM_LOG);
    set_report_id_file_hier  ("Comparator Match",
                               m_log_file);
    set_report_id_action_hier("Comparator Mismatch",
                               UVM_LOG);
    set_report_id_file_hier  ("Comparator Mismatch",
                               m_log_file);
  endfunction: start_of_simulation_phase

  virtual function void report_phase(uvm_phase phase);
    string txt;
    $sformat(txt, "#matches = %d, #mismatches = %d",
             m_comp.m_matches, m_comp.m_mismatches);
    `uvm_info("", txt,UVM_NONE)
  endfunction: report_phase

endclass: cpu_scoreboard_t

uvm_monitor

class example_monitor_t extends uvm_monitor;
  `uvm_component_utils (example_monitor_t)
  uvm_analysis_port #(example_transaction_t) monitor_ap;
  virtual example_virtual_if vif;

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    monitor_ap = new("monitor_ap", this);
  endfunction: build_phase
  //...
  virtual task run_phase(uvm_phase phase);
    example_transaction_t tr;
    forever begin
      // Start with a new, clean transaction so that
      // already-monitored transactions are unaffected
      tr = new;
      // code to observe physical signal activity
      // and assemble transaction data in tr
      monitor_ap.write(tr);
    end
  endtask

endclass: example_monitor_t

uvm_objection

Try out an example using this feature in EDA Playground

task run_phase(uvm_phase phase);
  phase.raise_objection(this);
  forever
  begin
    my_transaction_t tx;
    #10 phase.drop_objection(this);
    seq_item_port.get_next_item(tx);
    phase.raise_objection(this);
    seq_item_port.item_done();
  end
endtask: run_phase

uvm_phase

  class mytest extends uvm_test;
    //...
    virtual task run_phase(uvm_phase phase);
      uvm_objection obj;
      obj = phase.get_objection();
      obj.set_drain_time(10ns);
      phase.raise_objection(this);
      `uvm_do_on(seq, seqr);
      phase.drop_objection(this);
    endtask
  endclass

uvm_pool

// Create a global pool somewhere like in a package
package my_pkg;
  uvm_pool #(string,axi_cfg_t) m_global_pool
    = uvm_pool#(string,axi_cfg_t)::get_global_pool();
endpackage
// Add config objects to the global pool in the testcase
begin
  axi_cfg_t  axi_cfg1, axi_cfg2, axi_cfg3;

  axi_cfg1 = new(...);
  axi_cfg2 = new(...);
  axi_cfg3 = new(...);

  axi_cfg1.randomize with { data < 8'h7fff; ... };
  axi_cfg2.randomize with { ... };
  axi_cfg3.randomize with { ... };

  my_pkg::m_global_pool.add("axi_cfg1", axi_cfg1);
  my_pkg::m_global_pool.add("axi_cfg2", axi_cfg2);
  my_pkg::m_global_pool.add("axi_cfg2", axi_cfg3);
end
// Retrieve the config object in a test sequence
task body();
  axi_cfg_t  cfg;
  cfg = my_pkg::m_global_pool.get("axi_cfg1");

  `uvm_do_with(req, { req.data == cfg.data, ... })
endtask

uvm_printer

Try out an example using this feature in EDA Playground

class my_object_t extends uvm_object;
  int addr = 198;
  int data = 89291;
  string name = "This is my test string";
  `uvm_object_utils_begin( my_object_t )
    `uvm_field_int( addr, UVM_ALL_ON )
    `uvm_field_int( data, UVM_ALL_ON )
    `uvm_field_string( name, UVM_ALL_ON )
  `uvm_object_utils_end
  //...
endclass : my_object_t

module top;
  my_object_t my_obj = new("my_obj");
  initial begin
    // Print using the table printer
    uvm_default_printer = uvm_default_table_printer;
    $display("# This is from the table printer\n");
    my_obj.print();

    // Print using the tree printer
    uvm_default_printer = uvm_default_tree_printer;
    $display("# This is from the tree printer\n");
    my_obj.print();

    // Print using the line printer
    $display("# This is from the line printer\n");
    my_obj.print(uvm_default_line_printer);
  end
endmodule : top
class my_printer_t extends uvm_table_printer;

  // Print out the time and name before printing an object
  function void print_object( string name,
    uvm_object value, byte scope_separator=".");

    // Header information to print out (use write_steam())
    write_stream( $sformatf(
      "Printing object %s at time %0t:\n",name, $time) );

    // Call the parent function to print out object
    super.print_object(name, value, scope_separator );
  endfunction : print_object

endclass : my_printer_t

my_printer_t my_special_printer = new();

module top;
  my_object_t my_obj = new( "my_obj" );
  initial begin
    #100;
    // Print using my_printer
    my_obj.print( my_special_printer );
  end
endmodule : top

uvm_printer_knobs

Try out an example using this feature in EDA Playground

  // Using default printer:
  uvm_default_printer.knobs.indent = 5; // Indent by 5 spaces
  uvm_default_printer.knobs.type_name = 0; // No type values
  // Using tree printer:
  uvm_default_printer = uvm_default_tree_printer;
  uvm_default_printer.knobs.hex_radix = "0x"; // Change radix
  uvm_default_printer.knobs.separator = "@@";
  // Turning off identifiers (not very useful in practice): 
  uvm_default_printer = uvm_default_line_printer;
  uvm_default_printer.knobs.identifier = 0;

uvm_queue

Try out an example using this feature in EDA Playground

`uvm_analysis_imp_decl(_pci)
`uvm_analysis_imp_decl(_usb)

class top_sb extends uvm_scoreboard;
  `uvm_component_utils(top_sb)

  uvm_analysis_imp_usb #(usb_trans_t,top_sb) usb_ap;
  uvm_analysis_imp_pci #(pci_trans_t,top_sb) pci_ap;

  uvm_queue #(usb_trans_t) expected_queue;

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    usb_ap = new("usb_ap",this);
    pci_ap = new("pci_ap",this);

    // Create the queue
    expected_queue = new();
  endfunction : build_phase

  function void write_pci(pci_trans_t t);
    // Save expected result in queue
    expected_queue.push_back( compute_result(t) );
  endfunction : write_pci

  function void write_usb(usb_trans_t t);
    // Compare the transaction with expected value from the queue
    assert ( t.compare( expected_queue.pop_front() )) else
    `uvm_error("TOP_SB", "Actual != Expected!")
  endfunction : write_usb
endclass

uvm_random_stimulus

Try out an example using this feature in EDA Playground

class verif_env_t extends uvm_env;
  `uvm_component_utils(verif_env_t)
  uvm_random_stimulus #(basic_transaction_t) m_stimulus;
  dut_driver_t m_driver;
  uvm_tlm_fifo #(basic_transaction_t) m_fifo;
  int test_length;
  //...

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    uvm_config_db#(int)::get(this,"","run_test_length",test_length);
    m_stimulus = new ("m_stimulus",this);
    m_fifo     = new("m_fifo",this);
    m_driver   = dut_driver_t::type_id::create("m_driver",this);
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    m_stimulus.blocking_put_port.connect(m_fifo.put_export);
    m_driver.tx_in_port.connect(m_fifo.get_export);
  endfunction: connect_phase

  virtual task run_phase(uvm_phase phase);
    m_stimulus.generate_stimulus(null,test_length);
  endtask: run_phase
endclass: verif_env_t

uvm_recorder

Try out an example using this feature in EDA Playground

class basic_trans_t extends uvm_sequence_item;
  `uvm_object_utils(basic_trans_t)
  //...
  function void do_record(uvm_recorder recorder);
    super.do_record(recorder);
    // recorder.tr_handle (UVM 1.1) replaced with
    // recorder.get_record_attribute_handle() in UVM 1.2
    `uvm_record_attribute(recorder.get_record_attribute_handle(),
                          "cmd",  cmd);
    `uvm_record_attribute(recorder.get_record_attribute_handle(),
                          "addr", addr);
    `uvm_record_attribute(recorder.get_record_attribute_handle(),
                          "data", data);
  // -- OR --
  //  recorder.record_field_int("cmd", cmd, 2, UVM_UNSIGNED);
  //  recorder.record_field_int("addr", addr, $bits(addr), UVM_UNSIGNED);
  //  recorder.record_field_int("data", data, $bits(data), UVM_UNSIGNED);
  endfunction
endclass: basic_trans_t

class my_driver_t extends uvm_driver #(basic_trans_t);
  `uvm_component_utils(my_driver_t)
  //...
  task run_phase(uvm_phase phase);
    //...
    // Start transaction recording
    void'( begin_tr(req, "basic_trans") );
    //...
    // End transaction recording
    end_tr(req);
    //...
  endtask: run_phase
endclass: my_driver_t

module top;
//...
  initial begin
  //...
  // Enable transaction recording.
  uvm_config_db#(int)::set(null, "*", "recording_detail", 1);
  //...
  end
endmodule : top

Register and Memory Sequences

uvm_config_db#(bit)::set( uvm_top????
  {"REG::",regmodel.blk.get_full_name(),".*"},
  "NO_REG_TESTS", 1, this);
  class smoke_test extends uvm_test;
    my_env_t m_env;
    //...
    task main_phase(uvm_phase phase);
      uvm_reg_hw_reset_test test_seq = new();
      phase.raise_objection(this, "Running hw_reset test");
      test_seq.model = m_env.regmodel;
      test_seq.start(null);
      phase.drop_objection(this, "End of hw_reset test");
    endtask : main_phase
  endclass : smoke_test

Register Layer

class my_model_t extends uvm_reg_block;
  `uvm_object_utils(my_model_t)
  //...
endclass : my_model_t
class reg_to_bus_adapter_t extends uvm_reg_adapter;
  `uvm_object_utils(reg_to_bus_adapter_t)
  function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    bus_trans_t tx =    bus_trans_t::type_id::create("bus_item");
    tx.addr = rw.addr[7:0];
    //...
    return tx;
  endfunction : reg2bus

  function void bus2reg(uvm_sequence_item bus_item,
                        ref uvm_reg_bus_op rw);
    //...
  endfunction : bus2reg
endclass : reg_to_bus_adapter_t
class my_env_t extends uvm_env;
  my_model_t regmodel;
  bus_agent_t agt;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    agt = bus_agent_t::type_id::create(...);
    if (regmodel == null) begin
      regmodel = my_model_t::type_id::create(...);
      regmodel.build();
      // Lock the register model to prevent further structural changes
      // and build the address map(s)
      regmodel.lock_model();
    end
  endfunction : build_phase

  function void connect_phase(uvm_phase phase);
    if (regmodel.get_parent() == null) begin
      // Create an instance of the register-to-bus adapter
      reg_to_bus_adapter_t adapter
        = reg_to_bus_adapter_t::type_id::create(...);
      // Set the sequencer for the register model's default map to be
      // the sequencer in the bus agent and connect it to the adapter
      regmodel.default_map.set_sequencer(
        agt.m_sequencer, adapter);
      // Turn on implicit prediction
      regmodel.set_auto_predict(1);
    end
    //...
  endfunction : connect_phase

endclass : my_env_t
class my_test extends uvm_test;
  my_env_t env;
  virtual function void run_phase(uvm_phase phase);
    // Create and start a user-defined or built-in sequence
    my_reg_sequence_t seq =
      my_reg_sequence_t::type_id::create("seq",this);
    seq.start(env.agt.m_sequencer);
  endfunction : run_phase
endclass : my_test

uvm_reg_transaction_order_policy

class high_addr_first_t
  extends uvm_reg_transaction_order_policy;
  virtual function void order(ref uvm_reg_bus_op q[$]);
    q.rsort with (item.addr);
  endfunction
  //...
endclass

high_addr_first_t p = new("high_addr_first_policy");
someregmap.set_transaction_order_policy(p);

uvm_report_catcher

Try out an example using this feature in EDA Playground

class my_error_demoter extends uvm_report_catcher;
  function new(string name="my_error_demoter_t");
    super.new(name);
  endfunction

  // This example demotes "MY_ID" errors to an info message
  function action_e catch();
    if (get_severity() == UVM_ERROR && get_id() == "MY_ID")
      set_severity(UVM_INFO);
    return THROW;
  endfunction
endclass
  // Instantiate the new report catcher
  my_error_demoter demoter = new;

  initial begin
    // Catch messages for all reporters
    uvm_report_cb::add(null, demoter);

    // To affect some specific object use the specific reporter
    uvm_report_cb::add(mytest.myenv.myagent.mydriver, demoter);

    // To affect some set of components using the component name
    uvm_report_cb::add_by_name("*.*driver", demoter, this);

    `uvm_error("MY_ID", "This message will be demoted to UVM_INFO")
    // Disable demotion
    void'(demoter.callback_mode(0));
    `uvm_error("MY_ID", "This message will NOT be demoted")
  end

uvm_report_message

uvm_report_message msg
    = uvm_report_message::type_id::create("msg");
`uvm_info_begin("MY_ID2", "My info message", UVM_LOW, msg)
  `uvm_message_add_tag("my_color", "red") //uses add string
  `uvm_message_add_int(my_int, UVM_HEX)
  `uvm_message_add_object(object)
`uvm_info_end

uvm_text_tr_database db = new("my_msg_log.txt");
uvm_tr_stream stream = db.open_stream("my_stream");
uvm_recorder rec = stream.open_recorder("my_recorder");
msg.record(rec);
uvm_process_report_message(msg);

uvm_report_object

class my_test extends uvm_test;
  //...
  // Turn off messages tagged with the id = "debug" for the my_env
  // component only
  my_env.set_report_id_action ("debug", UVM_NO_ACTION);

  // Turn all UVM_INFO messages off -
  // (Use set_report_[]_hier version [from uvm_component]
  // to recursively traverse the hierarchy and set the action)
  set_report_severity_action_hier(UVM_INFO, UVM_NO_ACTION);

  // Turn all messages back on
  set_report_severity_action_hier( UVM_INFO,
                    UVM_DISPLAY | UVM_LOG );
  set_report_severity_action_hier( UVM_WARNING,
                    UVM_DISPLAY | UVM_LOG );
  set_report_severity_action_hier( UVM_ERROR,
                    UVM_DISPLAY | UVM_COUNT | UVM_LOG );
  set_report_severity_action_hier( UVM_FATAL,
                    UVM_DISPLAY | UVM_EXIT | UVM_LOG );

  // Setup the global reporting for messages that use uvm_top as a
  // global report handler (like the sequences machinery)
  uvm_top.set_report_verbosity_level( UVM_ERROR );
  uvm_top.dump_report_state();  // Print out state

  // Configure the environment to quit/die after one UVM_ERROR message
  set_report_max_quit_count( 1 );
endclass : my_test
// Example with user-definable report hooks
class my_env_t extends uvm_env;
  `uvm_component_utils(my_env_t)

  bit under_reset = 0;  // Indicates device under reset
  UVM_FILE f;
  //...
  // Override the report_hook function
  function bit report_hook(input id, string message, int verbosity,
                           string filename, int line);

    // Turn off all reporting during the boot-up,
    // initialization, and reset period
    if (!under_reset && ( $time > 100ns ))
      return 1;
    else
      return 0;  // Either under_reset or time less than
                 // 100ns so do not issue report messages
  endfunction : report_hook

  function void start_of_simulation_phase(uvm_phase phase);
    // Duplicate report messages to a file
    f = $fopen( "sim.log", "w" );
    set_report_default_file_hier( f );

    // Setup the environment to not print INFO, WARNING,
    // and ERROR message during reset or initialization by
    // adding the UVM_CALL_HOOK reporting action
    set_report_severity_action_hier( UVM_INFO,
              UVM_DISPLAY | UVM_CALL_HOOK );
    set_report_severity_action_hier( UVM_WARNING,
              UVM_DISPLAY | UVM_CALL_HOOK );
    set_report_severity_action_hier( UVM_ERROR,
              UVM_DISPLAY | UVM_COUNT | UVM_CALL_HOOK );
  endfunction : start_of_simulation_phase
endclass : my_env_t

uvm_resource_db

  uvm_resource_db #(int)::set("shared_resources",
                          "max_count", 100, null);
max_count =
  uvm_resource_db #(int)::get_by_name("shared_resources", "max_count");
max_count =
  uvm_resource_db #(int)::get_by_type("shared_resources");
module top;

  import uvm_pkg::*;
  import my_pkg::*;

  // Instance of the interface that connects the DUT and TB
  dut_if dut_if1 ();

  // Instance the DUT
  dut    dut1 ( .i_f(dut_if1) );

  initial
  begin
    // Set virtual interface of driver to point to actual interface
    uvm_resource_db #(virtual dut_if)::set("DUT", "m_dut_if", dut_if1);
    run_test();
  end

endmodule: top

class my_driver_t extends uvm_driver #(my_transaction_t);
  //...
  virtual dut_if m_dut_if;

  function void connect_phase(uvm_phase phase);
    if ( ! uvm_resource_db #(virtual dut_if)::read_by_name(
                                  "DUT",
                                  "m_dut_if",
                                  m_dut_if) )
      `uvm_fatal("NOVIF", {"No virtual interface set for",
                             get_full_name(),".m_dut_if"})
  endfunction : connect_phase
  //...
endclass : my_driver_t

uvm_root

module top;
//...
initial
  begin
    uvm_top.enable_print_topology = 1;
    uvm_top.finish_on_completion = 0;
    run_test("test1");
  end
endmodule: top
class test1 extends uvm_test;
  `uvm_component_utils(test1)
  verif_env_t env1;
  uvm_component c;
  uvm_component cq[$];

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    env1 = verif_env_t::type_id::create("env1",this);
  endfunction : build_phase

  function void end_of_elaboration_phase(uvm_phase phase);
    c = uvm_top.find("env1.m_driver");
    uvm_top.find_all("*",cq,c);
    foreach(cq[i])
      `uvm_info("FIND_ALL",
            $sformatf("Found %s of type %s",
            cq[i].get_full_name(),cq[i].get_type_name()),
            UVM_NONE)
  endfunction: end_of_elaboration_phase
endclass: test1

Sequence

Try out an example using this feature in EDA Playground

class my_seq_item_t extends uvm_sequence_item;
  rand int data;
  rand bit [4:0] addr;
  //...
  `uvm_object_utils_begin ( my_seq_item_t )
    `uvm_field_int ( data, UVM_ALL_ON + UVM_HEX )
    `uvm_field_int ( addr, UVM_ALL_ON + UVM_HEX )
    //...
  `uvm_object_utils_end
  //...
endclass : my_seq_item_t
typedef uvm_sequencer #(my_seq_item_t) my_sequencer_t;
class my_sequence_t extends uvm_sequence #(my_seq_item_t);
  `uvm_object_utils ( my_sequence_t )
  my_seq_item_t    m_seq_item;

  function new ( string name = "my_sequence" );
    super.new ( name );
  endfunction : new

  virtual task body();
    // Create a sequence item using a macro
    `uvm_do ( m_seq_item )

    // Manually create another sequence item
    m_seq_item = my_seq_item::type_id::create();
    start_item(m_seq_item);
    m_seq_item.randomize with { ... };
    finish_item(m_seq_item);
  endtask : body
endclass : my_sequence_t

uvm_sequence

Try out an example using this feature in EDA Playground

typedef enum { read, write } dir_t;
class my_seq_item extends uvm_sequence_item;

  rand bit [31:0] data;
  rand bit [9:0]  addr;
  rand dir_t      dir;

  function new (string name = "");
    super.new(name);
  endfunction

  // Register sequence item with the factory and add the
  // field automation macros
  `uvm_object_utils_begin( my_seq_item_t )
    `uvm_field_int( data, UVM_ALL_ON )
    `uvm_field_int( addr, UVM_ALL_ON )
    `uvm_field_enum( dir_t, dir, UVM_ALL_ON )
  `uvm_object_utils_end
endclass : my_seq_item
class my_seq extends uvm_sequence #(my_seq_item);
  `uvm_object_utils (my_seq)
  // my_seq_item  req;          // built-in sequence item
  my_other_seq_t  subseq;       // A nested subsequence

  // Define a constructor
  function new ( string name = "my_seq");
    super.new (name);
  endfunction : new

  // Define the sequence functionality in the body() using macros
  virtual task body();
    `uvm_info ( get_name(), "Starting the sequence ...", UVM_NONE )

    // Use the do action macros on the sequence item
    `uvm_do_with( req,{ addr > 10'hfff; dir == read; } )

    // Invoke a nested subsequence
    `uvm_do_with( subseq, { ctrl_flag == `TRUE; } )
    //...
  endtask : body
endclass : my_seq
class my_seq_alt extends uvm_sequence #(my_seq_item);
  `uvm_object_utils (my_seq_alt)
  `uvm_declare_p_sequencer(uvm_sequencer #(my_seq_item))
  //...
  virtual task body();

    // Generate a sequence item
    req = my_seq_item::type_id::create("req");
    start_item(req);
    randomize(req) with { addr > 10'hfff; dir == read; };
    finish_item(req);

    // Invoke a nested subsequence
    subseq = my_other_seq_t::type_id::create("subseq");
    subseq.start(p_sequencer);
    //...
  endtask : body
endclass : my_seq_alt

Sequence Action Macros

// Examples of `uvm_do and `uvm_do_with
class example_sequence extends uvm_sequence #(example_sequence_item_t);
  //...
  task body();

    // Send the sequence item to the driver
    `uvm_do( req )

    // Send item again, but add constraints
    `uvm_do_with( req, { addr > 0 && addr < 'hffff; })
  endtask : body
endclass : example_sequence
// Examples of `uvm_create and `uvm_rand_send
class fixed_size_sequence extends uvm_sequence #(example_sequence_item_t);
  //...
  task body();
    // Allocate the sequence item
    `uvm_create ( req )

    // Modify the sequence item before sending
    req.size = 128;
    req.size.rand_mode(0);  // No randomization

    // Now send the sequence item
    `uvm_rand_send ( req )
  endtask : body
endclass : fixed_size_sequence
// Example of `uvm_do_on in a virtual sequence
class virtual_sequence extends uvm_sequence;
  `uvm_object_utils(virtual_sequence_t)
  `uvm_declare_p_sequencer(my_sequencer_t)
  write_sequence_t   wr_seq;
  read_sequence_t    rd_seq;
  //...
  task body();
    fork // Launch the virtual sequences in parallel
      `uvm_do_on_with ( wr_seq,
                        p_sequencer.seqr_a,
                        { parity == 1; addr > 48; })

      `uvm_do_on_with ( rd_seq,
                        p_sequencer.seqr_b,
                        { width == 32; type == LONG; })
    join
  endtask : body
endclass : virtual_sequence

uvm_sequence_item

typedef enum { RX, TX } kind_t;
class my_seq_item extends uvm_sequence_item;
  rand bit [4:0]  addr;
  rand bit [31:0] data;
  rand kind_t kind;
  // Constructor
  function new ( string name = "my_seq_item" );
    super.new ( name );
  endfunction : new

  // Register with UVM factory and use field automation
  `uvm_object_utils_begin( my_seq_item_t )
    `uvm_field_int  ( addr,  UVM_ALL_ON + UVM_DEC)
    `uvm_field_int  ( data,  UVM_ALL_ON + UVM_DEC)
    `uvm_field_enum  ( kind_t, kind, UVM_ALL_ON )
  `uvm_object_utils_end
endclass : my_seq_item

uvm_sequence_library

class my_seq_lib_t extends uvm_sequence_library #(my_item_t);
  `uvm_object_utils(my_seq_lib_t)
  `uvm_sequence_library_utils(my_seq_lib_t)

  function new(string name="");
    super.new(name);
    init_sequence_library();
  endfunction
  //...
endclass
class my_seq_t extends uvm_sequence #(my_item_t);
  `uvm_object_utils(my_seq_t)
  `uvm_add_to_seq_lib (my_seq_t, my_seq_lib_t)
  //...
endclass
class my_test extends uvm_test;
  `uvm_component_utils (my_test)
  //...
  function void build_phase(uvm_phase phase);
    uvm_sequence_library_cfg cfg;
    super.build_phase(phase);

    // Create an instance of the configuration convenience class
    cfg = new("seqlib_cfg", UVM_SEQ_LIB_RANDC, 1000, 2000);
    uvm_config_db #(uvm_sequence_library_cfg)::set(
                    null,
                    "*.env.agent.sequencer.main_phase",
                    "default_sequence.config",
                    cfg);
    // Configure the sequence library and cause it to be run on a
    // sequencer - it is a sequence and is run in exactly the same
    // way, either by setting the sequencer's default_sequence or
    // starting it manually.
    uvm_config_db #(uvm_sequencer_base)::set(
                    this,"*.my_sequencer.main_phase",
                    "default_sequence",
                    my_seq_lib_t::type_id::get());

  endfunction
  //...
endclass : my_test

uvm_sequencer

Try out an example using this feature in EDA Playground

//  Define a uvm_sequence_item
typedef enum { read, write } dir_t;
class my_seq_item_t extends uvm_sequence_item;

  rand bit [31:0] data;
  rand bit [9:0]  addr;
  rand dir_t      dir;

  function new (string name = "");
    super.new(name);
  endfunction

  // Register sequence item with the factory and add the
  // field automation macros
  `uvm_object_utils_begin( my_seq_item_t )
    `uvm_field_int( data, UVM_ALL_ON )
    `uvm_field_int( addr, UVM_ALL_ON )
    `uvm_field_enum( dir_t, dir, UVM_ALL_ON )
  `uvm_object_utils_end
endclass : my_seq_item_t

//  Define a sequence - generates between 1 and 20 transactions
class my_sequence_t extends uvm_sequence #(my_seq_item_t);
  `uvm_object_utils ( my_sequence_t )

  rand int num_items = 5;
  constraint c_num_items { num_items > 0; num_items <= 20;}

  function new ( string name = "my_sequence_t");
    super.new (name);
  endfunction : new

  task body;
    uvm_phase starting_phase = get_starting_phase(); // UVM 1.2
    if ( starting_phase != null )
      starting_phase.raise_objection(this);
    repeat (num_items)
    begin
      my_seq_item tx;
      `uvm_do(tx)
    end
    if ( starting_phase != null )
      starting_phase.drop_objection(this);
  endtask : body
endclass : my_sequence_t

//  Create a typedef for the sequencer
typedef uvm_sequencer #(my_seq_item) my_sequencer_t;

//  Connect the sequencer to the driver
class my_env_t extends uvm_env;
  `uvm_component_utils (my_env_t)
  //...
  my_sequencer_t  m_seqr;
  my_driver_t     m_drv;

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

  function void connect_phase(uvm_phase phase);
    // Hook up the sequencer to the driver
    m_drv.seq_item_port.connect(m_seqr.seq_item_export);
  endfunction : connect_phase
  //...
endclass : my_env_t

//  Create a test
class my_test extends uvm_test;
  `uvm_component_utils (my_test)

  my_env_t m_env;

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // Use the factory to create the environment
    m_env = my_env_t::type_id::create("m_env", this);
    // Set the sequencer's default sequence
    uvm_config_db #(uvm_object_wrapper)::set(
      null,
      "/.*m_seqr.main_phase/",
      "default_sequence",
      my_sequence_t::get_type() );
  endfunction : build_phase

endclass : my_test

Try out another example using this feature in EDA Playground

class my_transaction_t extends uvm_sequence_item;
  //...

class my_program_t extends uvm_sequence_item;
  `uvm_object_utils(my_program_t)

  // my_program_t contains a dynamic array of my_transactions
  my_transaction_t tx_h[];

  //...
  // When a my_program object is randomized, a random number (n) of
  // randomized my_transactions are created and stored in the dynamic
  // array
  function void pre_randomize;
    int n = $urandom_range(3, 6);
    tx_h = new[n];
    for (int i = 0; i < n; i++)
    begin
      tx_h[i] = new;
      assert( tx_h[i].randomize() );
    end
  endfunction
endclass : my_program_t

class my_program_seq_t extends uvm_sequence#(my_program_t);
  `uvm_object_utils(my_program_seq_t)

  function new (string name = "");
    super.new(name);
  endfunction: new

  // Create one my_program item, which contains n my_transaction items
  task body;
    my_program_t tx;
    tx = my_program_t::type_id::create("tx");
    start_item(tx);
    assert( tx.randomize() );
    finish_item(tx);
  endtask: body

endclass: my_program_seq_t

//  The sequencer for my_transactions contains a uvm_seq_item_pull_port;
//  the my_program sequencer will pull my_program items through this
class my_tx_sqr_t extends uvm_sequencer#(my_transaction_t);
  `uvm_component_utils(my_tx_sqr_t)

  uvm_seq_item_pull_port #(my_program_t) seq_item_port;

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

  function void build_phase(uvm_phase phase);
    seq_item_port = new("seq_item_port", this);
  endfunction: build_phase
endclass: my_tx_sqr_t

class my_tx_seq_t extends uvm_sequence#(my_transaction_t);
  `uvm_object_utils(my_tx_seq_t)
  // Includes the p_sequencer member - points to this
  // sequence's sequencer
  `uvm_declare_p_sequencer(my_tx_sqr_t)

  my_program_t my_program_h;

  function new (string name = "");
    super.new(name);
  endfunction: new

  task body;
  fork
    forever
    begin
      int n;
      // Get a my_program_t item from the my_program_t sequencer using
      // the interface that connects it to the my_transaction_t sequencer
      p_sequencer.seq_item_port.get(my_program_h);
      // Find out how many my_transaction items there are
      n = my_program_h.tx_h.size();
      // Create the same number here, copy across and generate
      for (int i = 0; i < n; i++)
      begin
        my_transaction tx;
        tx = my_transaction_t::type_id::create("tx");
        start_item(tx);
        tx.cmd  = my_program_h.tx_h[i].cmd;
        tx.addr = my_program_h.tx_h[i].addr;
        tx.data = my_program_h.tx_h[i].data;
        finish_item(tx);
      end
    end
  join_none
  endtask: body

endclass: my_tx_seq_t

class my_env_t extends uvm_env;
  `uvm_component_utils(my_env_t)
  uvm_sequencer #(my_program_t) my_program_sqr_h;
  my_tx_sqr_t my_tx_sqr_h;

  //...

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    // Create the sequencers (and other components)
    my_program_sqr_h = uvm_sequencer #(my_program_t)::type_id::create(
                                               "my_program_sqr_h", this);
    my_tx_sqr_h      = my_tx_sqr_t::type_id::create("my_tx_sqr_h", this);
    //...
  endfunction: build_phase

  function void connect_phase(uvm_phase phase);
    // Connect the sequencers' port and export
    my_tx_sqr_h.seq_item_port.connect(my_program_sqr_h.seq_item_export );
    // Other component connections
    //...
  endfunction: connect_phase
endclass: my_env_t

class my_test extends uvm_test;
  `uvm_component_utils(my_test)

  my_env_t my_env_h;

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

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    my_env_h = my_env_t::type_id::create("my_env_h", this);
  endfunction: build_phase

  task run_phase(uvm_phase phase);
    my_program_seq_t program_seq;
    my_tx_seq_t      tx_seq;

    // Create and start a my_tx_seq
    tx_seq = my_tx_seq_t::type_id::create("tx_seq");
    tx_seq.start( my_env_h.my_tx_sqr_h );

    // Create and start a my_program_seq
    program_seq = my_program_seq_t::type_id::create("program_seq");
    assert( program_seq.randomize() );
    program_seq.start( my_env_h.my_program_sqr_h );
    phase.phase_done.set_drain_time( this, 30ns );
  endtask: run_phase

endclass: my_test

Sequencer Interface and Ports

class my_driver_t extends uvm_driver #(my_trans_t);
  //...

  task run_phase(uvm_phase phase);
    forever begin
      // Pull a sequence item from the interface. We could use get
      // instead of get_next_item, in which case we don't need to call
      // to call item_done.
      seq_item_port.get_next_item( req );

      // Apply transaction to DUT interface
      //...

      // Set the response id and indicate that item is done
      rsp = new("rsp");
      rsp.set_id_info(req);
      seq_item_port.item_done( rsp );
    end
  endtask : run_phase
endclass : my_driver_t

//  Connect the sequencer and driver together
class my_env_t extend uvm_env;
  //...

  function void connect_phase(uvm_phase phase);
    my_drv.seq_item_port.connect( my_seqr.seq_item_export );
  endfunction : connect_phase
endclass : my_env_t

uvm_subscriber

//  Define a specialized subscriber class. This class does nothing except to
//  report all transactions it receives.
class example_subscriber_t extends
  uvm_subscriber #(example_transaction_t);
  `uvm_component_utils(example_subscriber_t)

  int transaction_count;

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

  function void write (example_transaction_t t);
    `uvm_info("WRITE", $sformatf(
       "Received transaction number %0d:\n%s",
       transaction_count++, t.sprint() ), UVM_NONE )
  endfunction
endclass : example_subscriber_t

//  In the enclosing environment class:
class subscriber_test_env_t extends uvm_env;
  `uvm_component_utils(subscriber_test_env_t)

  example_subscriber_t  m_subscriber;
  example_monitor_t     m_monitor;  // see uvm_monitor
  //...
  function void build_phase(uvm_phase phase);
    //...
    m_monitor    = example_monitor_t::type_id::create(
                                      "m_monitor",this);
    m_subscriber = example_subscriber_t::type_id::create(
                                      "m_ subscriber",this);
    //...
  endfunction

  function void connect_phase(uvm_phase phase);
    // Connect monitor's analysis port (requires)
    // to subscriber's export (provides)
    m_monitor.monitor_ap.connect ( m_subscriber.analysis_export );
  endfunction
  //...
endclass : subscriber_test_env_t

uvm_test

module top;

class test1 extends uvm_test;

  // Register test with factory
  `uvm_component_utils(test1)

  //...

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

  function void build_phase(uvm_phase phase);
    // Create environment
  endfunction: build_phase

  function void connect_phase(uvm_phase phase);
    // Connect test environment's virtual interface to the
    // DUT's interface
    m_env.m_mon.m_bus_if = tf.cpu_if.mon;
  endfunction: connect_phase

  task run_phase(uvm_phase phase);
    // Call methods in environment to control test (optional)
  endtask: run_phase
endclass: test1

initial begin
  run_test("test1"); // Use test1 by default
                     // Can override using +UVM_TESTNAME
end

// Contains DUT, DUT interface, clock/reset, ancillary modules etc.
test_harness tf ();

endmodule

TLM Implementation Declaration Macros

 `uvm_analysis_imp_decl(_ahb)
 `uvm_analysis_imp_decl(_pci)

class fullchip_sb_t extends uvm_scoreboard;
  `uvm_component_utils(fullchip_sb_t)

  uvm_analysis_imp_ahb #(ahb_trans_t, fullchip_sb) ahb_ap;
  uvm_analysis_imp_pci #(pci_trans_t, fullchip_sb) pci_ap;

  //...
  function void write_ahb(ahb_trans_t t) {
    // AHB implementation
    expected_queue.push_back( compute_result(t) );
  }

  function void write_pci(pci_trans_t t) {
    // PCI implementation
    assert ( t.compare( expected_queue.pop_front() )) else
        `uvm_error("FULLCHIP_SB", "Actual != Expected!")
  }

endclass

TLM-1 Interfaces

uvm_master_port #(my_req_t,my_rsp_t) m_master;
m_master.put(req1);
m_master.get(req1);
class uvm_tlm_req_rsp_channel #( type REQ = int , type RSP = REQ )
  extends uvm_component;

  typedef uvm_tlm_req_rsp_channel #( REQ , RSP ) this_type_t;

  protected uvm_tlm_fifo #( REQ ) m_request_fifo;
  protected uvm_tlm_fifo #( RSP ) m_response_fifo;

  //...
  uvm_master_imp #( REQ , RSP , this_type_t , uvm_tlm_fifo #( REQ ) ,
     uvm_tlm_fifo #( RSP ) ) master_export;

  uvm_slave_imp  #( REQ , RSP ,this_type_t , uvm_tlm_fifo #( REQ ) ,
    uvm_tlm_fifo #( RSP ) ) slave_export;
  //...
endclass
master_export = new(  "master_export" , this ,
              m_request_fifo, m_response_fifo );

slave_export = new(   "slave_export" , this ,
              m_request_fifo , m_response_fifo );

TLM-1.0 Ports, Exports and Imps

class compA_t extends uvm_component;
  `uvm_component_utils(compA_t)
  `uvm_blocking_put_port #(int) p0;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    p0 = new("p0",this,1,UVM_UNBOUNDED_CONNECTIONS);
  endfunction: build_phase

  task run_phase(uvm_phase phase);
    p0.debug_connected_to();
    for (int i=1; i<= p0.size(); i++) begin
     p0.put(i);
     p0.set_if(i); // next port index
    end
  endtask: run_phase
endclass: compA_t
class compB_t extends uvm_component;
  `uvm_component_utils(compB_t)
  uvm_blocking_put_imp #(int,compB_t) put_export;

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

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

  task put(int val);  // interface method
    `uvm_info("compB_t ",$sformatf("Received %0d",val), UVM_NONE)
  endtask: put
endclass: compB_t
class compC_t extends uvm_component;
  `uvm_component_utils(compC_t)

  compA_t A;
  compB_t B00,B01;
  uvm_blocking_put_port #(int) put_port;

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

  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    put_port = new("put_port", this, 1, UVM_UNBOUNDED_CONNECTIONS);
    A   = compA_t::type_id::create("A",  this);
    B00 = compB_t::type_id::create("B00",this);
    B01 = compB_t::type_id::create("B01",this);
  endfunction: build_phase

  function void connect_phase(uvm_phase phase);
    // The order of these statements does not matter
    A.p0.connect(B00.put_export);
    A.p0.connect(B01.put_export);
    A.p0.connect(put_port);
  endfunction: connect_phase
endclass: compC_t
class sve_t extends uvm_env;
  `uvm_component_utils(sve_t)

  compC_t C;
  compB_t ZB1,B2,B03;
  //...
  function void connect_phase(uvm_phase phase);
    C.put_port.connect(ZB1.put_export);
    C.put_port.connect(B03.put_export);
    C.put_port.connect(B2.put_export);
  endfunction: connect_phase
endclass: sve_t

TLM-2.0

class initiator_t extends uvm_component;
  `uvm_component_utils(initiator_t)

  // Blocking initiator socket
  uvm_tlm_b_initiator_socket#(my_seq_item_t) tlm2_sock;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    // Create the socket
    tlm2_sock = new("tlm2_sock", this);
  endfunction : new

  // During simulation, use the socket to pass transactions to the target
  virtual task run_phase(uvm_phase phase);
    my_seq_item_t rw;
    uvm_tlm_time delay = new;

    phase.raise_objection(this);

    rw = my_seq_item_t::type_id::create("rw",, get_full_name());
    //...
    // Pass a transaction through the socket
    tlm2_sock.b_transport(rw, delay);
    //...

    phase.drop_objection(this);
  endtask : run_phase

endclass : initiator_t
// Target component
class target_t extends uvm_component;
  `uvm_component_utils(target_t)

  uvm_tlm_b_target_socket#(target_t, my_seq_item_t) tlm2_sock;

  function new(string name = "target", uvm_component parent = null);
    super.new(name, parent);
    tlm2_sock = new("tlm2_sock", this);
  endfunction : new

  // Implementation of the blocking transport method.
  // The initiator calls this through the socket.
  task b_transport(my_seq_item_t rw, uvm_tlm_time delay);
    // Process the transaction
    // ...
  endtask : b_transport

endclass : target_t
// Environment that instantiates the initiator and target
class tb_env_t extends uvm_component;
  `uvm_component_utils(tb_env_t)

  initiator_t m_initiator;
  target_t    m_target;

  // Construct the environment
  function new(string name, uvm_component parent);
    super.new(name, parent);
  endfunction : new

  // Build the initiator and target components
  function void build_phase(uvm_phase phase);
    m_initiator = initiator_t::type_id::create("m_initiator", this);
    m_target    = target_t::type_id::create("m_target", this);
  endfunction : build_phase

  // Connect the target and the initiator sockets
  function void connect_phase(uvm_phase phase);
    m_initiator.tlm2_sock.connect(m_target.tlm2_sock);
  endfunction : connect_phase

endclass : tb_env_t

TLM-2.0 Ports, Exports and Imps

class master_t extends uvm_component;
  `uvm_component_utils(master_t)

  // Non-blocking initiator socket - is a forward port and has a
  // backward imp
  uvm_tlm_nb_initiator_socket
    #(master_t, trans_t, uvm_tlm_phase_e) initiator_socket;

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

  function void build_phase(uvm_phase phase);
    // Construct the socket, hence the port and imp
    // the first 'this' is the 'parent' object, the second 'this' is a
    // reference to the object where the implementation for the *_bw_*
    // functions are
    initiator_socket = new("initiator_socket", this, this);
  endfunction

  // Required implementation
  function uvm_tlm_sync_e nb_transport_bw(
        trans t,
        ref uvm_tlm_phase_e p,
        input uvm_tlm_time delay);
    transaction = t;
    state = p;
    return UVM_TLM_ACCEPTED;
  endfunction
  //...
endclass

uvm_tlm_analysis_fifo

class example_env_t extends uvm_env;
  `uvm_component_utils(example_env)

  uvm_tlm_analysis_fifo #(example_transaction_t) an_fifo;
  example_monitor_t  m_monitor;   // has analysis port
  example_analyzer_t m_analyzer;  // has "get" port
  //...
  virtual function void build_phase(uvm_phase phase);
    m_monitor   = ...; // Create
    m_analyzer  = ...; // Create
    an_fifo     = new("an_fifo", this);
  endfunction

  virtual function void connect_phase(uvm_phase phase);
    m_monitor.monitor_ap.connect(an_fifo.analysis_export);
    m_analyzer.get_port.connect(an_fifo.get_export);
  endfunction
  //...
endclass

uvm_tlm_extension

// Extension class
class my_extension_t extends uvm_tlm_extension#(my_extension_t);
  `uvm_object_utils(my_extension_t)
  int when;
endclass

// Add an extension to a payload
uvm_tlm_gp gp = new;
my_extension ext = new;
ext.when= $time;
gp.set_extension( ext );

// Retrieve the extension value
my_extension_t ext;
if ( $cast( ext, gp.get_extension( my_extension_t::ID()) )
  // Extension present
  //...
else
  `uvm_error( ... )

uvm_tlm_fifo

uvm_get_port #(basic_transaction_t) m_trans_in;
m_trans_in.get(tx);
class verif_env_t extends uvm_env;
  'uvm_component_utils(verif_env_t)

  uvm_random_stimulus #(basic_transaction_t) m_stimulus;
  dut_driver_t m_driver;
  uvm_tlm_fifo #(basic_transaction_t) m_fifo;
  //...
  virtual function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_stimulus = ... // Create
    m_fifo = ...     // Create
    m_driver = dut_driver_t::type_id::create("m_driver",this);
  endfunction: build_phase

  virtual function void connect_phase(uvm_phase phase);
    m_stimulus.blocking_put_port.connect(m_fifo.put_export);
    m_driver.m_trans_in.connect(m_fifo.get_export);
  endfunction: connect_phase
  //...
endclass: verif_env_t

uvm_tlm_generic_payload

// Create a generic payload transaction using the factory:
m_tlm_gptx = uvm_tlm_gp::type_id::create("m_tlm_gptx");
// Randomize the transaction
assert( m_tlm_gptx.randomize() with {
  m_address >= 0 && m_address < 256;
  m_length== 4 || m_length == 8;
  m_data.size == m_length;
  m_byte_enable_length <= m_length;
  (m_byte_enable_length % 4) == 0;
  m_byte_enable.size == m_byte_enable_length;
  m_streaming_width == m_length;
  m_response_status == UVM_TLM_INCOMPLETE_RESPONSE;
} )
else
  `uvm_error("Initiator", "m_tlm_gptx.randomize() failed")
// Create a copy of the transaction
tx.copy(m_tlm_gptx);

// Use the blocking transport, which is implemented in the target
initiator_socket.b_transport(tx, delay);

// Was the bus transaction successful?
assert( tx.is_response_ok() );

uvm_tlm_if

class Target_t extends uvm_component;
  `uvm_component_utils(Target_t)

   local byte unsigned m_data[4];

  // Declaration of target socket
  uvm_tlm_b_target_socket #(Target_t, my_transaction_t)
    t_socket;

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

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

  // Called by initiator through the socket
  task b_transport(my_transaction_t t, uvm_tlm_time delay);
    if ( t.get_address() == 64'HC000)
      if ( t.get_command() == UVM_TLM_READ_COMMAND )
        t.set_data(m_data);
      else
        t.get_data(m_data);
    #(delay.get_realtime(1ns));
  endtask : b_transport

endclass : Target_t

uvm_tlm_time

`timescale 100ps/1ps
//...
// Create a new uvm_tlm_time value
uvm_tlm_time delay = new("delay"); // Value will be 0ns
delay.set_abstime(10.5,1.0e-9);    // Set to 10.5ns
delay.incr(25,1ns);    // Increment by 25 x 100ps (timescale) = 2.5ns
delay.incr(25ns,1ns);  // Increment by 25ns

// Display value in ns and ps
`uvm_info("",$sformatf("delay is %.3f ns, %.3 ps",
                      delay.get_abstime(1.0e-9)
                      delay.get_abstime(1.0e-12)),UVM_NONE)
// Display value using the current timescale
`uvm_info("",$sformatf("delay is %.3f x100ps",
                      delay.get_realtime(1ns)),UVM_NONE)

uvm_text_tr_database

uvm_text_tr_database db = new("my_msg_db");
uvm_tr_stream stream;
db.set_filename("my_msg_db.txt");
void'(db.open_db());
assert(db.is_open())
  else `uvm_error("","open_db failed!");
stream = db.open_stream("my_stream");
//...
assert(db.close_db())
  else `uvm_error("","close_db failed");

uvm_text_tr_stream

uvm_text_stream stream;
stream = db.open_stream("my_stream");

uvm_transaction

class basic_trans_t extends uvm_sequence_item;
  rand bit[7:0] m_var1, m_var2;
  static int tx_count = 0;

  `uvm_object_utils_begin(basic_trans_t)
    `uvm_field_int(m_var1,UVM_ALL_ON | UVM_DEC)
    `uvm_field_int(m_var2,UVM_ALL_ON | UVM_DEC)
  `uvm_object_utils_end

  function new (string name = "",
                uvm_component initiator=null);
    super.new(name,initiator);
    tx_count++;
  endfunction: new

  virtual protected function int do_begin_tr();
    `uvm_info("TRX",$sformatf(
                    "Transaction %0d beginning",tx_count),
              UVM_NONE)
  endfunction: do_begin_tr

  virtual protected function void do_end_tr();
    `uvm_info("TRX",$sformatf(
                    "Transaction %0d ended",tx_count),
              UVM_NONE)
  endfunction: do_end_tr
endclass : basic_trans_t
// Generating constrained random transactions:
virtual task generate_stimulus(
  basic_trans_t t = null, input int max_count = 30 );
  basic_trans_t temp;
  uvm_event_pool tx_epool;
  uvm_event tx_end;
  if( t == null ) t = new("trans",this);
  for( int i = 0; (max_count == 0 || i < max_count-1);i++ )
  begin
    assert( t.randomize() );
    $cast( temp , t.clone() );
    `uvm_info("stimulus generation", temp.convert2string(), UVM_NONE )
    tx_epool = temp.get_event_pool();
    blocking_put_port.put( temp );
    tx_end = tx_epool.get("end");
    tx_end.wait_trigger();
  end
endtask: generate_stimulus

Utility macros

class basic_trans_t extends uvm_sequence_item;
  rand bit[7:0] addr, data;
  `uvm_object_utils_begin(basic_trans_t)
    `uvm_field_int(addr,UVM_ALL_ON)
    `uvm_field_int(data,UVM_ALL_ON)
  `uvm_object_utils_end
//...

endclass : basic_trans_t
class dut_driver_t extends uvm_driver #(basic_trans_t);
  uvm_get_port #(basic_trans_t) m_trans_in;
  vif_wrapper vif;
  `uvm_component_utils_begin(dut_driver_t)
    `uvm_field_object(vif,UVM_ALL_ON)
  `uvm_component_utils_end
  //...

endclass: dut_driver_t

UVM Versions

`uvm_info($sformatf("","Using %s",`UVM_VERSION_STRING),UVM_NONE)
// To enforce use of UVM 1.2:

// Require at least UVM 1.2
`ifndef UVM_POST_VERSION_1_1
  // UVM 1.1 and earlier here (or possibly forgot UVM altogether)
  assert("This code requires UVM 1.2 or later."==0);
`endif
// Backward compatibility (requires testing under each version):
`ifdef UVM_POST_VERSION_1_1
    // UVM 1.1 stuff goes here
`else
    // UVM 1.2 stuff goes here
`endif

Virtual sequences

Try out an example using this feature in EDA Playground

// Create a virtual sequencer
class my_virtual_sequencer_t extends uvm_sequencer;
  // Variables to reference the sequencers we will control
  uvm_sequencer #(seq_item_t) m_write_sqr;
  uvm_sequencer #(seq_item_t) m_read_sqr;

  // Register this sequencer with the factory
  `uvm_component_utils ( my_virtual_sequencer_t )

  function new ( string name, uvm_component parent = null );
      super.new ( name, parent );
  endfunction : new
endclass : my_virtual_sequencer_t
// Create a virtual sequence
class read_write_seq_t extends uvm_sequence;
  `uvm_object_utils(read_write_seq_t)
  `uvm_declare_p_sequencer(my_virtual_sequencer_t)

  my_read_seq_t    read_seq;  // Sequences on sequencers
  my_write_seq_t   write_seq;

  function new ( string name = "read_write_seq_t" );
      super.new ( name );
  endfunction : new

  // Define the virtual sequence functionality.
  // Note the use of `uvm_do_on() instead of `uvm_do() !!
  virtual task body();
    // Write to a bunch of register locations
    for ( int i = 0; i < 32; i += 4 ) begin
      `uvm_do_on_with ( write_seq,
           p_sequencer.m_write_sqr, { addr == i; })
    end
    // Now read the results on another interface
    `uvm_do_on ( read_seq, p_sequencer.m_read_sqr )
    //...
  endtask : body
endclass : read_write_seq_t
// Connect the sequencer to the driver
class my_env_t extends uvm_env;
  `uvm_component_utils(my_env_t)
  //...
  my_virtual_sequencer_t       m_vseqr;
  uvm_sequencer #(seq_item_t)  m_seqr_w;
  uvm_sequencer #(seq_item_t)  m_seqr_r;

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

  // Build the components
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    m_vseqr  = my_virtual_sequencer_t::type_id::create("m_vseqr", this);
    m_seqr_w =
      uvm_sequencer_t#(seq_item_t)::type_id::create("m_seqr_w", this);
    m_seqr_r =
      uvm_sequencer_t#(seq_item_t)::type_id::create("m_seqr_r", this);
  endfunction : build_phase

  // Connect up the sequencers to the virtual sequencer
  function void connect_phase(uvm_phase phase);
    m_vseqr.m_write_sqr = m_seqr_w;
    m_vseqr.m_read_sqr  = m_seqr_r;
  endfunction : connect_phase
  //...
endclass : my_env_t