UVM TLM 1
Before going into the TLM interface concepts, lets see why we need TLM interface
Port Based Data Transfer:
Following is a simple verification environment.
Components generator and driver are implemented as modules. These modules are connected using module ports or SV interfaces. The advantage of this methodology is, the two above mentioned components are independent. Instead of consumer module, any other component which can understand producer interface can be connected, which gives a great reusability.
The disadvantage of this methodology is , data transfer is done at lower lever abstraction.
Task Based Data Transfer:
In the above environment, methods are used to transfer the data between components. So, this gives a better control and data transfer is done at high level. The disadvantage is, components are using hierarchal paths which do not allow the reusability.
(S)TLM interfaces:
UVM has TLM interfaces which provide the advantages which we saw in the above two data transfer styles.
Data is transferred at high level. Transactions which are developed by extending the uvm_sequence_item can be transferred between components using method calls. These methods are not hierarchal fixed, so that components can be reused.
The advantages of TLM interfaces are
1) Higher level abstraction
2) Reusable. Plug and play connections.
3) Maintainability
4) Less code.
5) Easy to implement.
6) Faster simulation.
7) Connect to Systemc.
8) Can be used for reference model development.
Operation Supported By Tlm Interface:
Putting:
Producer transfers a value to Consumer.
Getting:
Consumer requires a data value from producer.
Peeking:
Copies data from a producer without consuming the data.
Broadcasting:
Transaction is broadcasted to none or one or multiple consumers.
Methods
(S)BLOCKING:
virtual task put(input T1 t
)
virtual task get(output T2 t
)
virtual task peek(output T2 t
)
(S)NON-BLOCKIN:
virtual function bit try_put(input T1 t
)
virtual function bit can_put
()
virtual function bit try_get(output T2 t
)
virtual function bit can_get
()
virtual function bit try_peek(output T2 t
)
virtual function bit can_peek
()
(S)BLOCKING TRANSPORT:
virtual task transport
(input T1 req
,output T2 rsp
)
(S)NON-BLOCKING TRANSPORT:
virtual function bit nb_transport
(input T1 req
,output T2 rsp
)
(S)ANALYSIS:
virtual function void write
(input T1 t
)
Tlm Terminology :
Producer:
A component which generates a transaction.
Consumer:
A component which consumes the transaction.
Initiator:
A component which initiates process.
Target:
A component which responded to initiator.
Tlm Interface Compilation Models:
Blocking:
A blocking interface conveys transactions in blocking fashion; its methods do not return until the transaction has been successfully sent or retrieved. Its methods are defined as tasks.
Non-blocking:
A non-blocking interface attempts to convey a transaction without consuming simulation time. Its methods are declared as functions. Because delivery may fail (e.g. the target component is busy and can not accept the request), the methods may return with failed status.
Combined:
A combination interface contains both the blocking and non-blocking variants.
Interfaces:
The UVM provides ports, exports and implementation and analysis ports for connecting your components via the TLM interfaces. Port, Export, implementation terminology applies to control flow not to data flow.
Port:
Interface that requires an implementation is port.
Import:
Interface that provides an implementation is import ot implementation port.
Export:
Interface used to route transaction interfaces to other layers of the hierarchy.
Analysis:
Interface used to distribute transactions to passive components.
Direction:
Unidirectional:
Data transfer is done in a single direction and flow of control is in either or both direction.
Bidirectional:
Data transfer is done in both directions and flow of control is in either or both directions.
Examples:
A read operation is a bidirectional.
A write operation is unidirectional.
Lets look at a example:
In this example, we will use *_put_* interface.
There are 2 components, producer and consumer.
Producer generates the transaction and Consumers consumes it.
In this example, producer calls the put() method to send transaction to consumer i.e producer is initiator and consumer is target.
When the put() method in the producer is called, it actually executes the put() method which is defined in consumer component.
(S)Transaction
We will use the below transaction in this example.
class instruction
extends uvm_sequence_item
;
typedef enum {PUSH_A,PUSH_B,ADD,SUB,MUL,DIV,POP_C} inst_t
;
rand inst_t inst
;
`uvm_object_utils_begin(instruction
)
`uvm_field_enum(inst_t
,inst
, UVM_ALL_ON)
`uvm_object_utils_end
function new (string name = "instruction");
super.new(name);
endfunction
endclass
(S)Producer:
1) Define producer component by extending uvm_component.
class producer
extends uvm_component
;
endclass : producer
2) Declare uvm_blocking_put_port port.
uvm_blocking_put_port
#(instruction
) put_port
;
3) In the constructor, construct the port.
function new(string name, uvm_component p
= null);
super.new(name,p
);
put_port
= new("put_port", this);
endfunction
4) Define the run() method. In this method, randomize the transaction.
Then call the put() of the put_port and pass the randomized transaction.
task run
;
for(int i
= 0; i
< 10; i
++)
begin
instruction ints
;
#10;
ints
= new();
if(ints
.randomize
()) begin
`uvm_info("producer", $sformatf("sending %s",ints
.inst
.name()), UVM_MEDIUM)
put_port
.put(ints
);
end
end
endtask
(S)Producer source code
class producer
extends uvm_component
;
uvm_blocking_put_port
#(instruction
) put_port
;
function new(string name, uvm_component p
= null);
super.new(name,p
);
put_port
= new("put_port", this);
endfunction
task run
;
for(int i
= 0; i
< 10; i
++)
begin
instruction ints
;
#10;
ints
= new();
if(ints
.randomize
()) begin
`uvm_info("producer", $sformatf("sending %s",ints
.inst
.name()), UVM_MEDIUM)
put_port
.put(ints
);
end
end
endtask
endclass : producer
(S)Consumer:
1) Define a consumer component by extending uvm_component.
class consumer
extends uvm_component
;
endclass : consumer
2) Declare uvm_blocking_put_imp import. The parameters to this port are transaction and the consumer component itself.
uvm_blocking_put_imp
#(instruction
,consumer
) put_port
;
3) In the construct , construct the port.
function new(string name, uvm_component p
= null);
super.new(name,p
);
put_port
= new("put_port", this);
endfunction
4) Define put() method. When the producer calls "put_port.put(ints);", then this method will be called. Arguments to this method is transaction type "instruction".
In this method, we will just print the transaction.
task put(instruction t
);
`uvm_info("consumer", $sformatf("receiving %s",t
.inst
.name()), UVM_MEDIUM)
endtask
(S)Consumer source code
class consumer
extends uvm_component
;
uvm_blocking_put_imp
#(instruction
,consumer
) put_port
;
function new(string name, uvm_component p
= null);
super.new(name,p
);
put_port
= new("put_port", this);
endfunction
task put(instruction t
);
`uvm_info("consumer", $sformatf("receiving %s",t
.inst
.name()), UVM_MEDIUM)
//push the transaction into queue or array
//or drive the transaction to next level
//or drive to interface
endtask
endclass : consumer
(S)Connecting producer and consumer
In the env class, take the instance of producer and consumer components.
In the connect method, connect the producer put_port to consumer put_port using
p.put_port.connect(c.put_port);
(S)Env Source code
class env
extends uvm_env
;
producer p
;
consumer c
;
function new(string name = "env");
super.new(name);
p
= new("producer", this);
c
= new("consumer", this);
endfunction
function void connect
();
p
.put_port
.connect
(c
.put_port
);
endfunction
task run
();
#1000;
global_stop_request
();
endtask
endclass
(S)Testcase
module test
;
env e
;
initial begin
e
= new();
run_test
();
end
endmodule
(S)Download the example
uvm_tlm_1.tar
Browse the code in uvm_tlm_1.tar
(S)Command to sun the simulation
VCS Users : make vcs
Questa Users: make questa
(S)Log
UVM_INFO producer.sv(26) @ 10:
env.producer [producer] sending PUSH_A
UVM_INFO consumer.sv(20) @ 10:
env.consumer [consumer] receiving PUSH_A
UVM_INFO producer.sv(26) @ 20:
env.producer [producer] sending PUSH_B
UVM_INFO consumer.sv(20) @ 20:
env.consumer [consumer] receiving PUSH_B
One more example using *_get_* interface as per the below topology.
(S)Download the example
uvm_tlm_2.tar
Browse the code in uvm_tlm_2.tar
(S)Command to sun the simulation
VCS Users : make vcs
Questa Users: make questa
All Interfaces In Uvm:
uvm_blocking_put_port #(T)
uvm_nonblocking_put_port #(T)
uvm_put_port #(T)
uvm_blocking_get_port #(T)
uvm_nonblocking_get_port #(T)
uvm_get_port #(T)
uvm_blocking_peek_port #(T)
uvm_nonblocking_peek_port #(T)
uvm_peek_port #(T)
uvm_blocking_get_peek_port #(T)
uvm_nonblocking_get_peek_port #(T)
uvm_get_peek_port #(T)
uvm_analysis_port #(T)
uvm_transport_port #(REQ,RSP)
uvm_blocking_transport_port #(REQ,RSP)
uvm_nonblocking_transport_port #(REQ,RSP)
uvm_master_port #(REQ,RSP)
uvm_blocking_master_port #(REQ,RSP)
uvm_nonblocking_master_port #(REQ,RSP)
uvm_slave_port #(REQ,RSP)
uvm_blocking_slave_port #(REQ,RSP)
uvm_nonblocking_slave_port #(REQ,RSP)
uvm_put_export #(T)
uvm_blocking_put_export #(T)
uvm_nonblocking_put_export #(T)
uvm_get_export #(T)
uvm_blocking_get_export #(T)
uvm_nonblocking_get_export #(T)
uvm_peek_export #(T)
uvm_blocking_peek_export #(T)
uvm_nonblocking_peek_export #(T)
uvm_get_peek_export #(T)
uvm_blocking_get_peek_export #(T)
uvm_nonblocking_get_peek_export #(T)
uvm_analysis_export #(T)
uvm_transport_export #(REQ,RSP)
uvm_nonblocking_transport_export #(REQ,RSP)
uvm_master_export #(REQ,RSP)
uvm_blocking_master_export #(REQ,RSP)
uvm_nonblocking_master_export #(REQ,RSP)
uvm_slave_export #(REQ,RSP)
uvm_blocking_slave_export #(REQ,RSP)
uvm_nonblocking_slave_export #(REQ,RSP)
uvm_put_imp #(T,IMP)
uvm_blocking_put_imp #(T,IMP)
uvm_nonblocking_put_imp #(T,IMP)
uvm_get_imp #(T,IMP)
uvm_blocking_get_imp #(T,IMP)
uvm_nonblocking_get_imp #(T,IMP)
uvm_peek_imp #(T,IMP)
uvm_blocking_peek_imp #(T,IMP)
uvm_nonblocking_peek_imp #(T,IMP)
uvm_get_peek_imp #(T,IMP)
uvm_blocking_get_peek_imp #(T,IMP)
uvm_nonblocking_get_peek_imp #(T,IMP)
uvm_analysis_imp #(T,IMP)
uvm_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_transport_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_master_imp #(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_master_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_master_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_blocking_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)
uvm_nonblocking_slave_imp#(REQ,RSP,IMP,REQ_IMP,RSP_IMP)