UVM: How TLM ports work? (The straightforward way)

Continuing from my previous post about “UVM: How the TLM ports work?“, here I am presenting an easy way to understand the mystery. Today we will develop a simple infrastructure in SystemVerilog for the same put port. One can implement a get port in a similar fashion just by changing the direction of argument to the tasks.

Ports are nothing but the lightweight intermediate classes that calls specific tasks from one class to another. We will implement following infrastructure in this example:

ports

Example port infrastructure

Basically there is a base class that contains a virtual put method. This class is parameterized with the transaction type that it has to transfer. Note that this class can have other methods like get, peek as well. But I have added only one method since we are implementing put port over here. Here the provider is the handle of target class whose put method is to be called.

// Lightweight base class
class my_base #(type transaction=int);
my_base #(transaction) provider;
virtual task put(transaction pkt);
endtask
endclass

Thereafter, there is an extended class which has the actual put implementation. In UVM, we define the port handle pameterized with the packet type.

In connect method, we pass the implementation port’s handle as an input argument. This handle is stored as the target class. Later, the target class’s put API is invoked using this handle.

// Put port
class my_put_port #(type transaction=int) extends my_base #(transaction);

function new(string name="my_put_port");
endfunction

function void connect(my_base #(transaction) provider);
this.provider = provider;
endfunction

task put(transaction pkt);
provider.put(pkt); // Invoke implementation ports put task
endtask

endclass

Now, while declaring the implementation port handle, we provide the packet type and the target class that has actual implementation. The connect function is redundant over here since we are never going to do: implementation_port.connect(). The put method of this class is invoked by the put port class. This class simply forwards the packet to target class by calling the target class’s put task.

// Put port
class my_put_imp #(type transaction=int, target=int) extends my_base #(transaction);

target implement;
function new(string name="my_put_port", target implement);
this.implement = implement;
endfunction

function void connect(my_base #(transaction) provider);
this.provider = provider; // Redundant over here
endfunction

task put(transaction pkt);
implement.put(pkt); // Invoke target class's put task
endtask

endclass

We have all the library functions/tasks that we need. Lets develop the producer-consumer and environment infrastructure for using this communication.

The producer puts a packet five times to the consumer. The consumer delays the packet by a single timestamp and then unblocks the producer to put next packet.

// user defined driver
class producer;
my_put_port #(int) p;
function new();
p = new("p");
endfunction

task run();
for(int i=0;i<5;i++) begin
$display($time,"\tCalling put for i=%d",i);
p.put(i);
#1;
end
endtask
endclass

// user defined consumer
class consumer;
my_put_imp #(int,consumer) p_imp;
function new();
p_imp = new("p_imp",this);
endfunction

task put(int i);
$display($time,"\tReceived i=%d",i);
#1; // some delay for processing
endtask

task run();
$display($time,"\tRun for consumer");
endtask
endclass

The top level components instantiates producer and consumer and calls the run tasks.

// user defined environment
class env;
producer p1;
consumer c1;
function new();
p1 = new();
c1 = new();
endfunction

function void connect();
p1.p.connect(c1.p_imp);
endfunction

task run();
fork
p1.run();
c1.run();
join
endtask

endclass

// top
module top();
env e;

initial begin
e = new();
e.connect();
e.run();
end

endmodule

So this is a small illustration about how the ports work in UVM. Note that we have skipped matching the port connection rules and handling multiple connections at a time. You can try implementing a get/peek port similar way by making minor changes in the code. Hope that this gives you a head-start about understanding the ports.

Here is a working example of the above infrastructure: EDAPlayground Link.

 -Sharvil

profile for sharvil111 at Stack Overflow, Q&A for professional and enthusiast programmers

3 responses

  1. […] to my next post for an easy way to grasp this […]

    Like

  2. Awesome Sharvil Desai, you have deep dive in UVM!!!, this is really great article.

    Liked by 1 person

    1. Thanks Tahir. Glad to hear that 🙂

      Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: