How I can synchronize multiple monitors with scoreboard's analysis export?

Hi, I’m working with a Round Robin scheduler, where I have variable number of inputs. I want to synchronize analysis ports from monitors with analysis export in scoreboard so I can tell which monitor is sending transactions to the scoreboard. Is there any way to do this. I’m new with UVM, every kind of help is welcome.

In reply to stole:

Unfortunately you do not show your implementation details. I assume you have 1 monitor sending data to the scoreboard. You can differentiate them with an ID in the transaction indicating where it comes from.

In reply to stole:

Hi, I’m working with a Round Robin scheduler, where I have variable number of inputs. I want to synchronize analysis ports from monitors with analysis export in scoreboard so I can tell which monitor is sending transactions to the scoreboard. Is there any way to do this. I’m new with UVM, every kind of help is welcome.

use write method diffrent suffix for diffrent monitor .

Regards
Shubham

Hi, sorry for the lack of the informations. I’ve been busy since I posted this.

Since there are variable number of the inputs, which is determined by the NUM_OF_INPUTS parameter, there are also NUM_OF_INPUTS agents. In my case I created two monitors in agent class. One monitor is watching ouptut data(packets), and the other one is watching input packets and collecting coverage, which can be ignored for now. Packets sent from input ports should appear on the output after one clock cycle.

Problem is that I can’t resolve which packet is sampled and compared with the packet on the output. For example, let’s say that packets are sent from the first port. When the end of the packet is reached(in my case when MSB of the input data is 1), next port will be serviced, and so on.

I want to avoid situation where I compare packets on the output, which are sent from the first port, with the packets from different port. I want to be sure that output packets are compared with packets from the first port. Same proccess is repeated with every other port.

I hope that I could explain this a little bit more. I’ll post here my codes for the both monitors, socreboard and seq_item.

// Scoreboard class
ifndef PRRS_SCBD_SV define PRRS_SCBD_SV

class prrs_scoreboard extends uvm_scoreboard;
`uvm_component_utils(prrs_scoreboard)

// Analysis export declarations 
uvm_analysis_export #(eth_pkt) exp_export;
uvm_analysis_export #(eth_pkt) act_export;

// Analysis TLM FIFO declarations
uvm_tlm_analysis_fifo #(eth_pkt) exp_fifo;
uvm_tlm_analysis_fifo #(eth_pkt) act_fifo;

function new(string name = "prrs_scoreboard", uvm_component parent = null);
	super.new(name, parent);
endfunction 

virtual function void build_phase(uvm_phase phase);
	super.build_phase(phase);
	exp_export = new("exp_export", this);
	act_export = new("act_export", this);
	exp_fifo = new("exp_fifo", this);
	act_fifo = new("act_fifo", this);
endfunction : build_phase

virtual function void connect_phase(uvm_phase phase); 
	super.connect_phase(phase);
	exp_export.connect(exp_fifo.analysis_export); 
	act_export.connect(act_fifo.analysis_export);
endfunction : connect_phase

virtual task main_phase(uvm_phase phase);
	eth_pkt exp_tx;
	eth_pkt act_tx;
	
	super.main_phase(phase);
	forever begin
	  
	  exp_fifo.get_peek_export.get(exp_tx);
		`uvm_info(get_full_name(), $sformatf("Pulled %0h from exp_fifo at %0t", exp_tx.payload_out, $time), UVM_LOW)
		
	  act_fifo.get_peek_export.get(act_tx);
	  `uvm_info(get_full_name(), $sformatf("Pulled %0h from act_fifo at %0t", act_tx.payload_out, $time), UVM_LOW)
		
		check_compare: assert(exp_tx.payload_out == act_tx.payload_out)
		   //exp_tx.packet_enable = 1; // ovo prati monitor koji kupi ulaz
			`uvm_info(get_full_name(), 
			          $sformatf("OK! act_pkg = %0h(monitor_%0d), exp_pkg = %0h(monitor_%0d)", 
			          act_tx.payload_out, act_tx.monitor_select(), exp_tx.payload_out, exp_tx.monitor_select()), UVM_MEDIUM)
		 else 
			`uvm_error(get_full_name(), $sformatf("Mismatch! act_pkg = %0h(monitor_%0d), exp_pkg = %0h(monitor_%0d)", 
			                            act_tx.payload_out, act_tx.monitor_select(), exp_tx.payload_out, exp_tx.monitor_select()))
			                           
		
	end
endtask : main_phase


virtual function void report_phase(uvm_phase phase);
	`uvm_info(get_full_name(), $psprintf("Scoreboard report \n", this.sprint()), UVM_LOW)
endfunction : report_phase

endclass : prrs_scoreboard

`endif

// Monitor class - for output packets
ifndef PRRS_MONITOR_SV define PRRS_MONITOR_SV

class prrs_monitor extends uvm_monitor;
`uvm_component_utils(prrs_monitor)

// Analysis port declaration
uvm_analysis_port #(eth_pkt) m_act_ap;

//Virtual interface handle
virtual prrs_if vif;

// Class contructor
//-------------------------------------------------
function new(string name = "prrs_monitor", uvm_component parent = null);
	super.new(name, parent);
//	eop = new();
endfunction

// Monitor build_phase
//-------------------------------------------------
virtual function void build_phase(uvm_phase phase);
	super.build_phase(phase);
	m_act_ap = new("m_act_ap", this);
endfunction : build_phase

// Monitor connect_phase
//-------------------------------------------------
virtual function void connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	if(!uvm_config_db #(virtual prrs_if)::get(this, "*", "prrs_if", vif))
		`uvm_fatal("Monitor: NOVIF",{"virtual interface must be set for: ",get_full_name(),".vif"})
endfunction : connect_phase


// Monitor run_phase/main_phase
//-------------------------------------------------
virtual task run_phase(uvm_phase phase);

	eth_pkt act_pkt, pkt_clone;	
        #1;
	forever begin
	    
	    act_pkt = eth_pkt::type_id::create("act_pkt", this);
	    
			@(posedge vif.pi_clk);
			@(posedge vif.pi_clk);
			act_pkt.payload_out = vif.po_dout;
			$cast(pkt_clone, act_pkt.clone());
			m_act_ap.write(pkt_clone); 
	end

endtask : run_phase

endclass : prrs_monitor

`endif

// cov_monitor class - for input packets
ifndef PRRS_COV_MONITOR_SV define PRRS_COV_MONITOR_SV

class prrs_coverage_monitor extends uvm_monitor;
`uvm_component_utils(prrs_coverage_monitor)

// Analysis port declaration
uvm_analysis_port #(eth_pkt) cm_exp_ap;

// Virtual interface handle
virtual prrs_if vif;

// Ethernet transaction handle
eth_pkt cg_pkt;

// Covergroup declaration
  //-------------------------------------------------
covergroup cg_mon_pkt;
  payload_cp:  coverpoint cg_pkt.payload_out{
      bins b1 = {0, 255};
	      bins b2 = {256, 511};
	      bins b3 = {512, 2047};
	      bins b4 = {2048, 3504};
	      bins b5 = {3505, 4095};
  }
  
  //out_data_cp: coverpoint cg_pkt.payload_out;
endgroup: cg_mon_pkt


// Class constructor
//-------------------------------------------------
function new(string name = "prrs_coverage_monitor", uvm_component parent = null);
    super.new(name, parent);
    cg_mon_pkt = new;
   // eop = new();
endfunction

// Coverage monitor build_phase
//-------------------------------------------------
function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    cm_exp_ap = new("cm_exp_ap", this);
endfunction: build_phase

// Coverage monitor connect_phase
  //-------------------------------------------------
  virtual function void connect_phase(uvm_phase phase);
	  super.connect_phase(phase);
	  if(!uvm_config_db #(virtual prrs_if)::get(this, "*", "prrs_if", vif))
   `uvm_fatal("NOVIF", {"virtual interface must be set for: ",get_full_name(),".vif"})
  endfunction : connect_phase

// Coverage monitor run_phase
//-------------------------------------------------
virtual task run_phase(uvm_phase phase);
	 
	 eth_pkt exp_tx_clone;                               
	 int mon_num;
   
   #1;
    forever begin

      cg_pkt = eth_pkt::type_id::create("cg_pkt", this);   
      
       mon_num = cg_pkt.monitor_select();  // This variable will contain number which is going to determine which monitor will be used

	      @(posedge vif.pi_clk);                          
	       		      
	      // Checking if there is any data on the current input port
	      if(vif.pi_data_valid[mon_num] == 1)begin
		       cg_pkt.payload_out = vif.pi_data[mon_num];   
		       cg_mon_pkt.sample();                         
		       $cast(exp_tx_clone, cg_pkt.clone());          
		       cm_exp_ap.write(exp_tx_clone);                
		    end
	    end
endtask: run_phase

endclass: prrs_coverage_monitor

`endif

// seq_item class
ifndef ETH_PKT_SV define ETH_PKT_SV

parameter NUMBER_OF_INPUTS = 4;
parameter DATA_WIDTH = 12;

class eth_pkt extends uvm_sequence_item;

rand bit[11:0] payload_out;

uvm_object_utils_begin(eth_pkt) uvm_field_int(payload_out, UVM_DEFAULT)
`uvm_object_utils_end

function new(string name = “eth_pkt”);
super.new(name);
endfunction

// Function used to check whis monitor should be used
virtual function int monitor_select();

   static int mon_sel = 0;
   
   // If MSB of the input data is 1, then start reading from the next input
   if(payload_out[DATA_WIDTH-1] == 1)begin
	    mon_sel++;
   end

   if(mon_sel == (NUMBER_OF_INPUTS-1)) mon_sel = 0;

   return mon_sel;

endfunction : monitor_select

function bit do_compare(uvm_object rhs, uvm_comparer comparer);
eth_pkt rhs_;

if(!$cast(rhs_, rhs))begin
  return 0;
end

return(super.do_compare(rhs, comparer) &&
   //    (payload_data == rhs_.payload_data) &&
       (payload_out == rhs_.payload_out));

endfunction : do_compare

endclass : eth_pkt

`endif

In reply to stole:

I believe you are making your problem more complicates as it is by using 2 monitors in an agent. Having only 1monitor which monitors both input and output data would simplify your problem.

Hi,

I would recommend having active agent and passive agent. You can place your input_monitor on active and output_monitor in passive agent.
Create two analysis_imps in scoreboard. Connect the input analysis imp to your input monitor and output analysis imp to output mointor.
Collect input packet in input tlm fifo and output in output tlm fifo. So you can have two separate structures

Following is snippet of codes in monitor, scoreboard and env class


//active agent : input_monitor
uvm_analysis_port #(pkt_item) mon_ip_ap;

//passive agent : output monitor
uvm_analysis_port #(pkt_item) mon_op_ap;

//scoreboard
`uvm_analysis_imp_decl (_ip) // remember to add _ip suffix for everything that deals with input
`uvm_analysis_imp_decl (_op)

class my_sb extends uvm_scoreboard;

uvm_analysis_imp_ip #(pkt_item, my_sb) sb_ap_ip;
uvm_analysis_imp_op #(pkt_item, my_sb) sb_ap_op;


uvm_tlm_fifo #(pkt_item) pkt_fifo_ip;
uvm_tlm_fifo #(pkt_item) pkt_fifo_op;

//build_phase
pkt_fifo_ip = new("pkt_fifo_ip", this, 100);
pkt_fifo_op = new("pkt_fifo_op", this, 100);

//ip write method
function void write_ip(pkt_item tr);
`void(pkt_fifo_ip.try_put(tr));
end

//op write method
function void write_op(pkt_item rcv);
`void(pkt_fifo_op.try_put(rcv));
end

///env class

//connect_phase

pkt_agent.pkt_mon.mon_ip_ap.connect(pkt_sb.sb_ap_ip);
pkt_agent.pkt_mon.mon_op_ap.connect(pkt_sb.sb_ap_op);


Please ignore syntax errors

In reply to UVM_SV_101:

You are violating a key rule in the UVM, i.e. ‘as-simple-as-possible’. Using 2 different agents for the same functional interface is exactly an overkill.

In reply to chr_sue:

I think this is a simple and reusable approach. It would be great if you can point to an example where monitor evaluates input and output signals and sends it to scoreboard.

Thanks

In reply to UVM_SV_101:

Look to the Verification Academy Code examples. There you’ll find this approach, having 1 agent for 1 functional/pinlevel (virtual) interface. Using an active and a passive agent for the same intertface I have never seen befor in an industrial project.

In reply to chr_sue:


I believe you are making your problem more complicates as it is by using 2 monitors in an agent. Having only 1monitor which monitors both input and output data would simplify your problem.

I’m sorry for the late answer. Using only one monitor simpified and solved my problems. Thanks!