In Data and Driver, we added a data packet and driver, but that model has the following drawbacks:

  • Limited verification components
  • Data packet is generated inside the Driver
  • Protocol used to send data is hard coded in the driver class
  • Data packet had a custom display function
Now, we'll add a sequencer and a monitor to the environment. The sequencer will generate, randomize data packets and send it to the driver. The driver will extract necessary information from the data packet and toggle DUT ports via the virtual interface handle. The monitor simply observes the transactions happening across the interface signals and converts it back into data packet format which can be sent to other verification components, if any.


GitHub

You can download/clone the uvm-301 lab from our repository at github.


Testbench

sequencer and monitor sequencer-monitor

Data

Previously we used to register a new object class with the factory using `uvm_object_utils. If we register it as shown below, we'll be able to utilize the automation features of UVM. That means, we don't have to explicitly write a display function as we did before, or write copy/clone/compare functions for each uvm_object inherited class.

  
  
`uvm_object_utils_begin (my_data)
	`uvm_field_int (data, UVM_ALL_ON)
	`uvm_field_int (addr, UVM_ALL_ON)
`uvm_object_utils_end

  
So, if we want to print out the contents in the class, all we need to do is call the print () method. We can specify the format in which data should be printed - table, line, tree, etc


  
  
data_obj.print ();
data_obj.print (uvm_default_line_printer);
data_obj.print (uvm_default_tree_printer);
data_obj.print (uvm_default_table_printer);

  

Driver

Since we extended the driver from uvm_driver class, it will have handles to the TLM interface. TLM stands for Transaction Level Modeling. For now, all you need to know is that it is a port that can be connected with another TLM interface. You may also refer to TLM tutorial. Usually we connect this with the sequencer, because that's where the data packets are generated. get_next_item () is a task in the TLM object seq_item_port which will get an item from the interface. drive_item is a user defined, protocol specific task that will do the pin wiggling of the DUT. After data is driven out, the driver will indicate back to the sequencer that it has finished the process by calling the function item_done.


Sequence

We need a sequence for the sequencer to operate on, and a base_sequence can be coded as

  
  
class base_sequence extends uvm_sequence;
	`uvm_object_utils (base_sequence)

	my_data  data_obj;
	int unsigned      n_times;

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

	// Raise an objection if started as the root sequence
	virtual task pre_body ();
		if (starting_phase != null)
			starting_phase.raise_objection (this);
	endtask

  

uvm_sequence is indirectly a derivative of uvm_object and hence we have registered it with the factory using `uvm_object_utils. Every sequence has body () task which will execute patterns and consume simulation time. You guessed it right, this is where we put our code for a sequence. The pre_body () task will automatically be called (unless it is configured to be skipped) before the body() task and it's a good place to raise an objection to let the testbench know that the sequence has items still pending for execution.


  
  
	virtual task body ();
		`uvm_info ("BASE_SEQ", $sformatf ("Starting body of %s", this.get_name()), UVM_MEDIUM)
		data_obj = my_data::type_id::create ("data_obj");

		repeat (n_times) begin
			start_item (data_obj);
			assert (data_obj.randomize ());
			finish_item (data_obj);
		end
		`uvm_info (get_type_name (), $sformatf ("Sequence %s is over", this.get_name()), UVM_MEDIUM)
	endtask
      
	// Drop objection if started as the root sequence
	virtual task post_body ();
		if (starting_phase != null) 
			starting_phase.drop_objection (this);
	endtask
endclass
  

Within the body() task, we can use start_item () and finish_item() methods to send the data object to the driver. Note that finish_item() will be over only after the driver calls item_done. After the body() method is over, objection is dropped and simulation proceeds to the next phase.


Sequencer

The sequencer is NOT required to be derived from uvm_sequencer, but instead can be instantiated directly as an object of uvm_sequencer parameterized to handle a specific data type. This is done in env class, where driver and monitor are instantiated.

  
  
uvm_sequencer #(my_data) m_seqr0;

  

The important part now is to connect the sequencer with the driver in the connect_phase () method.

  
  
m_drv0.seq_item_port.connect (m_seqr0.seq_item_export);

  

Simulation Output

----------------------------------------------------------------
CDNS-UVM-1.1d (14.10-s004)
(C) 2007-2013 Mentor Graphics Corporation
(C) 2007-2013 Cadence Design Systems, Inc.
(C) 2006-2013 Synopsys, Inc.
(C) 2011-2013 Cypress Semiconductor Corp.
----------------------------------------------------------------
UVM_INFO @ 0: reporter [RNTST] Running test base_test...
UVM_INFO @ 0: reporter [UVMTOP] UVM testbench topology:
------------------------------------------------------------
Name                     Type                    Size  Value
------------------------------------------------------------
uvm_test_top             base_test               -     @2629
  m_top_env              my_env                  -     @208
    m_drv0               my_driver               -     @202
      rsp_port           uvm_analysis_port       -     @2828
      seq_item_port      uvm_seq_item_pull_port  -     @2777
    m_mon0               my_monitor              -     @2728
    m_seqr0              uvm_sequencer           -     @2859
      rsp_export         uvm_analysis_export     -     @2949
      seq_item_export    uvm_seq_item_pull_imp   -     @3497
      arbitration_queue  array                   0     -
      lock_queue         array                   0     -
      num_last_reqs      integral                32    'd1
      num_last_rsps      integral                32    'd1
------------------------------------------------------------

UVM_INFO ./tb/my_pkg.sv(88) @ 0: uvm_test_top.m_top_env.m_drv0 [my_driver] Applying initial reset
UVM_INFO ./tb/my_pkg.sv(92) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] DUT is now out of reset
UVM_INFO ./tb/my_pkg.sv(99) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Waiting for data from sequencer
UVM_INFO ./tb/my_pkg.sv(186) @ 390000: uvm_test_top.m_top_env.m_seqr0@@base_sequence [BASE_SEQ] Starting body of base_sequence
UVM_INFO ./tb/my_pkg.sv(194) @ 390000: uvm_test_top.m_top_env.m_seqr0@@base_sequence [base_sequence] Sequence base_sequence is over
UVM_INFO ./tb/my_pkg.sv(118) @ 390000: uvm_test_top.m_top_env.m_drv0 [my_driver] Finished DUT simulation

--- UVM Report catcher Summary ---


Number of demoted UVM_FATAL reports  :    0
Number of demoted UVM_ERROR reports  :    0
Number of demoted UVM_WARNING reports:    0
Number of caught UVM_FATAL reports   :    0
Number of caught UVM_ERROR reports   :    0
Number of caught UVM_WARNING reports :    0

--- UVM Report Summary ---

** Report counts by severity
UVM_INFO :    8
UVM_WARNING :    0
UVM_ERROR :    0
UVM_FATAL :    0
** Report counts by id
[BASE_SEQ]     1
[RNTST]     1
[UVMTOP]     1
[base_sequence]     1
[my_driver]     4
Simulation complete via $finish(1) at time 390 NS + 139

Go to the final step Agent and Scoreboard