SV -> SC -> SV loopback example with dual ports | |
Example of dual TLM target ports | This example is a slight modification of the single port SV -> SC -> SV exmaple and the handling of configuration extensions is identical so no need to repeat that here. |
class producer TLM port containers | The two port container classes below instantiate TLM target blocking transport sockets (class uvm_tlm_b_target_socket). |
class producer - SV initiator and target | The class producer is a uvm_component that defines both initiator and target TLM-2 sockets since this is a loopback and the same producer plays the role of both initiator and eventual target. |
This example is a slight modification of the single port SV -> SC -> SV exmaple and the handling of configuration extensions is identical so no need to repeat that here.
However, what this example does demonstrate is the case of having dual TLM initiator + target socket couplings for a single parent UVM component. One is for WRITE operations and the other is for READ operations. This might be desirable in the case where a modeler would want to have a dedicated ::b_transport() function for each type of operation, especially if time consuming WRITE and READ ops might overlap.
In SystemVerilog UVM, there is a dilema of how a TLM target model that is some derivation of uvm_component can define 2 TLM target sockets. The reason is that these sockets make the assumption that their parent class is expected to define the required target ::b_transport() method implementation which is always assumed to have that fixed name.
In SystemC we can use convenience sockets for this where each convenience socket allows you to register the separately named transport callbacks for each target socket.
But, as mentioned UVM TLM does not allow this. So what this example demonstrates is to have 2 uvm_component derived child members of the parent class producer and each of those child components define a single target UVM TLM blocking transport socket (class uvm_tlm_b_target_socket). One of the containers handles WRITE operations and the other handles READ operations.
The diagram below illustrates the configuration showing two child uvm_component’s acting as TLM port containers inside the parent SV producer which is also a uvm_component.
The two port container classes below instantiate TLM target blocking transport sockets (class uvm_tlm_b_target_socket).
The first one, class producer_write_port_container handles WRITE operations exclusively.
The second one, class producer_read_port_container handles READ operations exclusively.
Both do so by calling the ::b_transport_write() and ::b_transport_read() methods of their parent class respectively.
typedef class producer; class producer_write_port_container extends uvm_component; // { `uvm_component_utils(producer_write_port_container) // All ports default to TLM GP as transaction kind. uvm_tlm_b_target_socket #( producer_write_port_container ) in; producer parent = producer'(get_parent()); function new(string name, uvm_component parent=null); super.new(name,parent); in = new("in", this); endfunction virtual task b_transport( uvm_tlm_gp t, uvm_tlm_time delay ); parent.b_transport_write( t, delay ); endtask endclass // } class producer_read_port_container extends uvm_component; // { `uvm_component_utils(producer_read_port_container) // All ports default to TLM GP as transaction kind. uvm_tlm_b_target_socket #( producer_read_port_container ) in; producer parent = producer'(get_parent()); function new(string name, uvm_component parent=null); super.new(name,parent); in = new("in", this); endfunction virtual task b_transport( uvm_tlm_gp t, uvm_tlm_time delay ); parent.b_transport_read( t, delay ); endtask endclass // }
The class producer is a uvm_component that defines both initiator and target TLM-2 sockets since this is a loopback and the same producer plays the role of both initiator and eventual target.
However in this example we want to have separate target ports for WRITE operations and READ operations.
Notice in this case however, that rather than directly instantiating two UVM TLM target sockets, rather, the parent class producer instantiates the containers for those sockets as described above.
class producer extends uvm_component; // { `uvm_component_utils(producer) // All ports default to TLM GP as transaction kind. uvm_tlm_b_initiator_socket #() out; // "mainstream" port uvm_tlm_nb_initiator_socket #( producer ) out_config; // static config port uvm_tlm_nb_target_socket #( producer ) in_config; producer_write_port_container in_write; producer_read_port_container in_read;