UVM Tutorial for Candy Lovers – 20. TLM 1

UVM supports ports (TLM 1) and sockets (TLM 2) as transaction-level interfaces. This post will explain TLM 1. TLM 1 seems daunting as it has many ports, exports, and “imp”s, but once you understand the basics, TLM 1 is not too difficult.

Ports

Ports define which access methods to use. There are twenty-three port classes in TLM 1. Each port is a subclass of the uvm_port_base class, which in turn is a subclass of the uvm_tlm_if_base class (or a subclass of the uvm_sqr_if_base class in case of the uvm_seq_item_pull_port). See the class diagram below.

TLM 1 Port Classes
TLM 1 Port Classes
The uvm_tlm_if_base class and the uvm_sqr_if_base class define the access methods of a port. By default, each method issues an error message if it is called. Each port overrides a subset of the access methods as listed below. For example, the uvm_blocking_put_port overrides the put method (only). This means if you call a method other than the put, you will get an error message defined in the uvm_tlm_if_base class.
Class \ Method put try_ put can_ put get try_ get can_ get peek try_ peek can_ peek transport nb_ transport write
uvm_blocking_ put_port X
uvm_nonblocking_ put_port X X
uvm_put_port X X X
uvm_blocking_ get_port X
uvm_nonblocking_ get_port X X
uvm_get_port X X X
uvm_blocking_ peek_port X
uvm_nonblocking_ peek_port X X
uvm_peek_port X X X
uvm_blocking_ get_peek_port X X
uvm_nonblocking_ get_peek_port X X X X
uvm_get_peek_port X X X X X X
uvm_blocking_ master_port X X X
uvm_nonblocking_ master_port X X X X X X
uvm_master_port X X X X X X X X X
uvm_blocking_ slave_port X X X
uvm_nonblocking_ slave_port X X X X X X
uvm_slave_port X X X X X X X X X
uvm_blocking_ transport_port X
uvm_nonblocking_ transport_port X
uvm_transport_port X X
uvm_analysis_port X

Exports

Exports are used when you promote imps (see the next section) to a parent component. Similar to the ports, each export is a subclass of the uvm_port_base class, which in turn is a subclass of the uvm_tlm_if_base class (or a subclass of the uvm_sqr_if_base class in case of the uvm_seq_item_pull_export). See the class diagram below.

TLM 1 Export Classes
TLM 1 Export Classes
Similar to a port class, each export class supports a subset of the access methods. See the corresponding port in the table above to figure out which methods are supported.

Imps

Imps provide the implementation of access methods. To be precise, the imps delegate the implementation to the component that actually implements the access methods. The type of the component is passed as a parameter when an imp is instantiated. Similar to the ports and exports, each imp is a subclass of the uvm_port_base class, which in turn is a subclass of uvm_tlm_if_base class (or a subclass of the uvm_sqr_if_base class in case of the uvm_seq_item_pull_imp). See the class diagram below.

TLM 1 Imp Classes
TLM 1 Imp Classes
Similar to a port class, each imp class supports a subset of the access methods. See the corresponding port in the table above to figure out which methods are supported.

FIFOs

TLM 1 defines two FIFOs; the uvm_tlm_fifo and the uvm_tlm_analysis_fifo. See the component figure and the class diagram below.

TLM 1 FIFO Components
TLM 1 FIFO Components
TLM 1 FIFO Class Diagram
TLM 1 FIFO Class Diagram

Channels

TLM 1 defines two channels; the uvm_tlm_req_rsp_channel and the uvm_tlm_transport_channel. See the component figure and the class diagram below.

TLM 1 Channel Components
TLM 1 Channel Components
TLM 1 Channel Class Diagram
TLM 1 Channel Class Diagram
The next post will give you an example of how to use the TLM 1 classes.

25 thoughts on “UVM Tutorial for Candy Lovers – 20. TLM 1”

    1. A component instantiates a port when the component uses a TLM interface. For example, the uvm_driver has the seq_item_port, which is an object of the uvm_seq_item_pull_port class. Since the uvm_seq_item_pull_port class defines the TLM access methods such as get_next_item() and item_done(), the driver can use these TLM methods to get a next item by calling seq_item_port.get_next_item(), for example.

      A component instantiates an export when the component provides the methods of a TLM interface. For example, the uvm_sequencer has the seq_item_export, which is an object of the uvm_seq_item_pull_imp class. The seq_item_export calls the TLM access methods provided by the uvm_sequencer. In other words, the uvm_sequencer must implement the get_next_item() and item_done(), etc.

      By connecting a port to an export, calling seq_item_port.get_next_item() in a driver actually executes the get_next_item() task provided by a sequencer.

      1. Thank you for the quick reply.
        So essentially the imports (in the example uvm_seq_item_pull_imp) are the one’s that actually implement the functions.
        And Exports are just for connectivity purposes or in other words promoting imports to parent components.
        Is my understanding correct?

        1. Yes, that is correct. Strictly speaking, the imp itself does not implement the functions. The actual implementations are in the component that instantiates the imp. The imp just calls the functions defined in the component. Also note that there is no “import” in UVM. The imp stands for “implementation.”

          1. Can you explain this concept with respect to a scoreboard and a monitor.
            According to this, a scoreboard component (which has the seq_item_export) should “provide” the methods of a TLM interface, and a monitor should “use” a TLM interface, and thus have a uvm_seq_item_pull_port.

            Visually the monitor does not seem like having a uvm_seq_item_pull_port.

          2. Usually a scoreboard has one or more uvm_analysis_imp and a monitor has one or more uvm_analysis_port. The uvm_seq_item_pull_port is used in the uvm_driver and the uvm_seq_item_pull_imp is used in the uvm_sequencer.

          3. It depends. For example, in our tutorial, the type of analysis_export of the jelly_bean_sb_subscriber is uvm_analysis_imp, whereas the type of jb_analysis_export of the jelly_bean_scoreboard is uvm_analysis_export. However, this difference is usually transparent from the user point of view. You can connect uvm_analysis_port to either uvm_analysis_export or uvm_analysis_imp without knowing the type.
            analysis_export

      2. Hi Shimize,

        I’m not clear about the context which uvm_*_export classes are used. Could you give me a sample code?

        Thank you
        Cuong Tran

        1. The uvm_*_export classes are used to connect the uvm_*_imp of enclosed component to the enclosing component. In the jelly beans example, the jelly_bean_scoreboard encloses the jelly_bean_sb_subscriber (see Verification Components). The jelly_bean_sb_subscriber has a uvm_analysis_imp (called analysis_export; yes, the name is confusing but this is an imp type not an export type). To promote this imp to the enclosing jelly_bean_scoreboard, it instantiated a uvm_analysis_export.

          class jelly_bean_scoreboard extends uvm_scoreboard;
          // …
             uvm_analysis_export#( jelly_bean_transaction ) jb_analysis_export;

          The connect_phase of the jelly_bean_scoreboard connects this export to the enclosed imp.

          function void connect_phase( uvm_phase phase );
             super.connect_phase( phase );
             // connect imp to export
             jb_analysis_export.connect( jb_sb_sub.analysis_export );
          endfunction: connect_phase

          In summary, you can think the uvm_*_export as the connector to export the internal imp. You can see the full listing of the jelly_bean_scoreboard in Environment.

  1. uvm_driver uses seq_item_pull_port interface uvm_seq_item_pull_port #(REQ, RSP) seq_item_port; and uses the interfaces get_next_item e.t.c provided by the uvm_sqr_if.I see the the implementation of the methods in uvm_sequencer.I see any export instantiated in uvm_sequencer but I dont see any implementation port instantiated

    1. You can specify the size of the FIFO, when you create a uvm_tlm_fifo.

      function new( string name, uvm_component parent = null, int size = 1 );

      The default size is 1. If the size is zero, there is no upper bound of the size.

    1. The uvm_put_port can be used if you want to call put, try_put, or can_put. Which port to use really depends on which API (functions/tasks) you want to use. For example, the jelly_bean_put_driver in TLM 1 Example uses the uvm_put_port in order to call the put task.

  2. Hi Keisuke,

    I am seeing a peculiar issue with the “Seq_item_port” functions. I am trying to develop a 2-stage pipelined driver where the address phase is followed by the data phase on the successive clock. On the same clock the driver must also drive the address of the next transfer. Data phase can extend for multiple clocks. The address of next transfer must be driven on the same clock when the last data byte of the current transfer is being driven

    The driver can’t trigger “item.done” until it has driven all data, but it must fetch the “next” seq_item on the clock of the last data byte to know what the address of the “next” seq_item is.
    I designed the data phase to first drive the last data byte on the clock, then immediately do a “get_next_item” to fetch the address and drive that on the interface on the same clock.

    The problem is that during the last data byte of the last seq_item in the queue, when it tries to “get_next_item”, it sees that there are no more seq_items available, and so the loop exits and the sequence drops the objection in the same sim cycle and the sim terminates. DUT to this the last data byte is never seen on the interface.

    Is there a way to make the simulator wait for some fixed time (like one clock edge) when it sees that the seq_item queue is empty, before exiting?

    Or is there a way to “check” if the queue is empty before actually doing a “get”, without triggering the exit.

    Thank you.

    1. Based on your description, I assume your sequence raises/drops an objection. Probably the easiest way to control the end of run_phase is to let your test raise/drop an objection. Let me know if that is not the case.

        1. I see. Then you can set the drain time to wait like this:

          // your test
          virtual task run_phase( uvm_phase phase );
            uvm_objection objection;
           
            phase.raise_objection( .obj( this ) );
            objection = phase.get_objection();
            objection.set_drain_time( .obj( this ), .time( 10ns ) ); // or whatever drain time
          // ...
          endtask: run_phase
  3. Hi Keisuke,
    What is the difference between uvm_tlm_fifo and uvm_tlm_analysis_fifo ? when to use what ?

    Thanks,
    Madhu

    1. The only difference is that uvm_tlm_analysis_fifo has an analysis export. If you want to connect an analysis port to the FIFO, then use uvm_tlm_analysis_fifo, otherwise use uvm_tlm_fifo.

Leave a Reply