[UVM foundation] UVM transaction level communication: uvm_analysis_port and uvm_blocking_get_port

In UVM, TLM (Transaction Level Modeling) is usually used to realize the transaction level communication between component s.

This section takes the UVM tree structure in the following figure as an example to demonstrate uvm_analysis_port and uvm_blocking_get_port of UVM transaction level communication.

The data flow direction is: my_model is from my_ My of agent_ Get my from monitor_ Transaction and put my_transaction passed to my_scoreboard.

1. uvm_analysis_port

In the transaction level communication of UVM, there are many ways to send data, one of which is to use uvm_analysis_port. In my_ The following variables are defined in monitor:

uvm_analysis_port #(my_transaction)  ap;

uvm_analysis_port is a parameterized class, and its parameter is this analysis_ The type of data to be transferred by port is my in this section_ transaction.

After the ap is declared, it needs to be in the build of monitor_ Instantiate it in phase:

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

In main_ In phase, after collecting a transaction, you need to write it into ap:

task my_monitor::main_phase(uvm_phase phase);
	my_transaction tr;
	while(1) begin
		tr = new("tr");
		collect_one_pkt(tr);
		ap.write(tr); //Write ap
	end
endtask

write is UVM_ analysis_ A built-in function of port. Here, at my_ All preparations for transaction communication in monitor have been completed.

2. Data receiving (uvm_blocking_get_port)

There are also many data receiving methods for transaction level communication of UVM, one of which is to use uvm_blocking_get_port. This is also a parameterized class. Its parameters are the type of transaction to be passed in it.

In my_ In model, a port is defined

uvm_blocking_get_port #(my_transaction)  port;

And in build_ Instantiate it in phase:

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

In main_phase, through port Get task to get from I_ transaction issued in the monitor of AGT.

task my_model::main_phase(uvm_phase phase);
   my_transaction tr;
   my_transaction new_tr;
   super.main_phase(phase);
   while(1) begin
      port.get(tr); //Receive transaction from monitor
      ...
   end
endtask

3. Connect send and receive ports

In my_monitor and my_ After each port is defined and implemented in the model, the communication function has not been realized, and it needs to be implemented in my_ In Env, fifo is used to connect the two ports. In my_ Define a fifo in Env and build_ Instantiate it in phase:

class my_env extends uvm_env;
   ...
   uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
   
   virtual function void build_phase(uvm_phase phase);
      super.build_phase(phase);
	  ...
      agt_mdl_fifo = new("agt_mdl_fifo", this);
   endfunction
   ...
endclass

The type of fifo is uvm_tlm_analysis_fifo itself is also a parameterized class. Its parameters are the type of transaction stored in it, here is my_transaction.

After that, click Connect_ fifo and my in phase_ Analysis in monitor_ Port and my_ Blocking in model_ get_ Port connection:

function void my_env::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   i_agt.ap.connect(agt_mdl_fifo.analysis_export);
   mdl.port.connect(agt_mdl_fifo.blocking_get_export);
endfunction

Why do we need a fifo here? You can't just put my_ Analysis in monitor_ Port and my_ Blocking in model_ get_ Are you connected? Due to analysis_port is non blocking, AP The write function returns immediately after the call is completed and will not wait for the data to be received. Suppose that when the write function is called, blocking_ get_ When port is busy with other things and is not ready to receive new data, it is written by the write function at this time_ Transaction needs a temporary storage location, which is fifo.

In the above connection, I is used_ A member variable ap of AGT, whose definition is the same as my_ The definition of ap in monitor is exactly the same:

uvm_analysis_port #(my_transaction)  ap;

And my_ The difference between ap in monitor is that you don't need to modify my_ The ap in the agent is instantiated, but only in my_agent connect_ Assign the value of monitor to it in phase, in other words, it is equivalent to a point to my_ Pointer to ap of monitor:

function void my_agent::connect_phase(uvm_phase phase);
   super.connect_phase(phase);
   ap = mon.ap;
endfunction

According to the previously described connect_ Execution order of phase, my_agent connect_phase is executed earlier than my_env connect_ The execution order of phase, which can ensure the execution to i_agt.ap. When the connect statement, i_agt.ap is not a null pointer.

Keywords: uvm

Added by tsg on Wed, 16 Feb 2022 09:59:31 +0200