Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
    • TLM 1: Ports, Exports and Analysis
    • TLM 2.0 Sockets
      • Initiator and Target Sockets
      • Blocking Transport
      • Non-Blocking Transport
      • Temporal Decoupling
      • TLM 2.0 Generic Payload: Extensions and Status
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹TLM 2.0 Sockets
  1. Topics
  2. UVM Verification
  3. Transaction-Level Modeling (TLM)
  4. TLM 2.0 Sockets
  5. Initiator and Target Sockets

Part 4 · TLM & Analysis · Intermediate

Initiator and Target Sockets

What a TLM 2.0 socket bundles, the blocking, non-blocking and passthrough socket classes, how they bind, and what connect() refuses.

On this page

    The virtual-platform team ships a TLM-2.0 model of the DMA's register block, and your job is to drive it from the UVM bench so the same register sequences run against the model and the RTL. Their model exposes a target socket. You add an initiator socket, connect it, and the first attempt fails with a Connection Error saying the provider does not provide the complete interface. You used a blocking socket; their target is non-blocking. What does a socket actually bundle, and which pairs fit together?

    The idea: one connector for both directions

    In TLM 1 a request-response link with callbacks needs a port one way and an export or imp the other way, and two connect() calls. A TLM 2.0 socket bundles them. An initiator socket is the end that starts transfers; a target socket is the end that services them. For blocking transport there is only a forward path, so the initiator socket is essentially a port and the target socket an imp. For non-blocking transport there is also a backward path, target to initiator, so each socket contains both: the initiator socket is a forward port plus a backward imp, the target socket a forward imp plus a backward port. One connect() binds both.

    Compare a two-way radio handset with separate microphone and speaker cables. The handset is one plug; you cannot plug the microphone into the wrong socket because there is no separate microphone plug.

    The picture

    An initiator socket connects through an interconnect's passthrough target and initiator sockets to a model's target socket; the non-blocking backward path returns from target to initiator.
    A forward call travels from the initiator socket to the target socket, through any passthrough sockets in between. Only non-blocking sockets carry a backward path.

    Build it

    The socket classes

    Class

    Owner

    Calls

    Implements

    uvm_tlm_b_initiator_socket #(T)

    Initiator

    b_transport on the target

    nothing

    uvm_tlm_b_target_socket #(IMP, T)

    Target

    nothing

    b_transport (in IMP)

    uvm_tlm_nb_initiator_socket #(IMP, T, P)

    Initiator

    nb_transport_fw

    nb_transport_bw (in IMP)

    uvm_tlm_nb_target_socket #(IMP, T, P)

    Target

    nb_transport_bw

    nb_transport_fw (in IMP)

    uvm_tlm_b_passthrough_initiator_socket, ..._target_socket, and the nb versions

    Interconnect or wrapper

    forward the same calls

    nothing

    T defaults to uvm_tlm_generic_payload and P to the phase enum uvm_tlm_phase_e, so uvm_tlm_b_initiator_socket #() is the common declaration. The IMP parameter plays the same role as in a TLM 1 imp: the class whose method the socket calls. If you omit the constructor's third argument, the socket casts its parent to IMP.

    A blocking initiator and target

    These are from a small TLM 2.0 test added to the bench: an initiator component that issues transfers and a memory model that services them.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // snippet: from tlm2_gp_test in tlm_tb.sv
    class gp_init extends uvm_component;
      `uvm_component_utils(gp_init)
      uvm_tlm_b_initiator_socket #() isock;         // T = uvm_tlm_generic_payload
      function new(string name, uvm_component parent);
        super.new(name, parent);
        isock = new("isock", this);
      endfunction
      // xfer() builds a payload and calls isock.b_transport(gp, delay)
    endclass
    
    class gp_mem extends uvm_component;
      `uvm_component_utils(gp_mem)
      uvm_tlm_b_target_socket #(gp_mem) tsock;      // IMP = gp_mem
      byte unsigned mem[bit [63:0]];
      function new(string name, uvm_component parent);
        super.new(name, parent);
        tsock = new("tsock", this);
      endfunction
      task b_transport(uvm_tlm_generic_payload gp, uvm_tlm_time delay);
        // decode, check, execute, set the response status
      endtask
    endclass
    
    // test connect_phase: the initiator side calls connect()
    ini.isock.connect(mem.tsock);

    What the target does inside b_transport, and why the initiator waits after the call, are the next lesson. The payload's fields are their own lesson.

    Binding rules

    • As in TLM 1, connect() is called on the initiator's end: initiator socket to passthrough or target socket, passthrough initiator up to a parent's passthrough initiator, passthrough target down to a child's target socket.

    • Calling connect() on a target socket is an error: You cannot call connect() on a target termination socket.

    • An initiator socket binds to exactly one target path (minimum and maximum 1), so an unconnected initiator socket is an error at end of elaboration, like any non-analysis port.

    • Blocking and non-blocking sockets do not mix. A blocking initiator into a non-blocking target fails at connect() with does not provide the complete interface, followed by type mismatch in connect.

    A driver whose bus is a socket

    To run the bench's existing register sequences against the model, you do not need new sequences. You need a driver that, instead of wiggling APB pins, turns each apb_item into a generic payload and calls b_transport. The sequencer, the sequences and the item_done() handshake stay exactly as they are; only the last hop changes.

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // snippet
    class apb2tlm_driver extends uvm_driver #(apb_item);
      `uvm_component_utils(apb2tlm_driver)
      uvm_tlm_b_initiator_socket #() isock;
      function new(string name, uvm_component parent);
        super.new(name, parent);
        isock = new("isock", this);
      endfunction
    
      task run_phase(uvm_phase phase);
        uvm_tlm_time delay = new("delay");
        forever begin
          uvm_tlm_generic_payload gp = uvm_tlm_generic_payload::type_id::create("gp");
          byte unsigned data[] = new[4];
          seq_item_port.get_next_item(req);
          {data[3], data[2], data[1], data[0]} = req.data;      // little-endian bytes
          gp.set_command(req.write ? UVM_TLM_WRITE_COMMAND : UVM_TLM_READ_COMMAND);
          gp.set_address(req.addr);
          gp.set_data(data);
          gp.set_data_length(4);
          gp.set_byte_enable_length(0);
          gp.set_streaming_width(4);
          gp.set_response_status(UVM_TLM_INCOMPLETE_RESPONSE);
          delay.reset();
          isock.b_transport(gp, delay);
          #(delay.get_realtime(1ns));
          gp.get_data(data);
          req.rdata  = {data[3], data[2], data[1], data[0]};
          req.slverr = !gp.is_response_ok();
          seq_item_port.item_done();                              // read data rides back in req
        end
      endtask
    endclass

    This is the same pattern the register layer uses in reverse: uvm_reg_tlm_adapter converts register operations into generic payloads. Swapping apb_driver for apb2tlm_driver with a factory override points every existing test at the model.

    Under the sockets, UVM also provides the plain TLM 2.0 ports, exports and imps (uvm_tlm_b_transport_port, uvm_tlm_nb_transport_fw_imp, and so on). Sockets are the recommended interface; the separate pieces exist mainly for building custom sockets.

    Watch it run

    With the sockets bound, the initiator wrote four bytes at address 0x10. The target added 10 ns of delay and the initiator waited it out before logging:

    Diagram
    UVM_INFO tlm_tb.sv(502) @ 10000: uvm_test_top.ini [GP]  UVM_TLM_WRITE_COMMAND [0x0000000000000010] = 11 22 33 44 (status=OK) -> OK

    The message is the payload's own convert2string(): command, 64-bit address, data bytes and response status. One call, one complete transfer.

    What breaks: the socket that matched on paper

    Back to the opening. Both sockets used the generic payload, so the types matched, and the code compiled. But the model's target socket was uvm_tlm_nb_target_socket, which implements nb_transport_fw and expects a backward path; the bench's initiator socket was blocking and could only call b_transport. connect() compares the interface masks, finds the target cannot answer a b_transport, and refuses the link at time 0. The fix was not to change the model: the team put a small adapter component between them, with a blocking target socket facing the bench and a non-blocking initiator socket facing the model, that converts each b_transport into the phase sequence of the non-blocking lesson. That adapter is also the place to bridge timing styles.

    Interview

    "What is a TLM 2.0 socket, and how is it different from a TLM 1 port?"

    A weak answer: "a socket is the TLM 2.0 name for a port."

    A strong answer: "A socket bundles the forward and backward paths of one link into one connector, so one connect() binds both. An initiator socket starts transfers; a target socket names the class that implements b_transport or nb_transport_fw. Sockets carry the generic payload by default and pass a timing annotation with every call. They bind one-to-one, blocking with blocking, and an unconnected initiator socket fails at end of elaboration. TLM 1 ports carry any type, have no timing argument, and the analysis family broadcasts."

    Expect the follow-ups: What is a passthrough socket for? (hierarchy: an interconnect or wrapper forwards calls without implementing them) and Why does a non-blocking target socket contain a port? (to call nb_transport_bw back on the initiator).

    Check yourself Which type parameter does a target socket take that a blocking initiator socket does not, and why?

    IMP, the class that implements the transport method. The target socket must call b_transport or nb_transport_fw on that class, exactly like a TLM 1 imp calls write() on its component. A blocking initiator socket implements nothing, so it needs no IMP.

    Check yourself A wrapper component contains the memory model and must expose its target socket to the env. What socket does the wrapper declare, and how is it connected?

    A passthrough target socket, uvm_tlm_b_passthrough_target_socket. The wrapper connects it down to the model's target socket in its connect_phase, and the env connects the initiator socket to the wrapper's passthrough socket, mirroring a TLM 1 export forwarding to a child imp.

    Check yourself A test that never uses the TLM model still builds the apb2tlm_driver, but nothing connects its socket. What happens, and how should the env avoid it?

    The run stops at time 0: an initiator socket needs exactly one connection, so resolve_bindings reports connection count of 0 does not meet required minimum of 1 and the root raises BUILDERR. The env should build the TLM path only when its configuration selects the model: read a knob such as cfg.use_tlm_model in build_phase and, only when it is set, create the model and apply the apb_driver to apb2tlm_driver override; connect the socket in connect_phase under the same condition. That is the same rule as for any optional TLM 1 port: build it only where you will connect it.

    Next: the socket carries a call, but who accounts for the time it takes? Blocking transport shows the delay argument and the two styles of using it.

    Related topics

    • Blocking transport

    • Generic payload: extensions and status

    • TLM 1 connect() rules

    Previous

    68 / 280

    Next: Blocking Transport

    Up next

    Up nextBlocking Transport

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Non-Blocking Transport
    • Temporal Decoupling
    • TLM 2.0 Generic Payload: Extensions and Status