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. Non-Blocking Transport

Part 4 · TLM & Analysis · Intermediate

Non-Blocking Transport

The base protocol's four phases on the forward and backward paths, sync return values, and the legal 3-, 2- and 1-call shortcuts, with a runnable phase tracker.

On this page

    An interviewer asks you to draw a TLM 2.0 non-blocking transfer. You draw four arrows: BEGIN_REQ, END_REQ, BEGIN_RESP, END_RESP. Good. Now the model you are integrating answers BEGIN_REQ with COMPLETED. Is that a protocol violation? Many candidates say yes. It is legal, and so are two other shortcuts. Why does the protocol have four phases, and when may a transfer take fewer calls?

    The idea: split a transfer where a real bus can stall

    A real bus transfer has two places where either side may need to wait: the target may not be ready to accept the request, and the initiator may not be ready to accept the response. The TLM 2.0 base protocol models each with a begin and an end phase: BEGIN_REQ (request offered), END_REQ (request accepted), BEGIN_RESP (response offered), END_RESP (response accepted). That lets one model pipeline requests and another apply backpressure, which b_transport cannot show. It is like a restaurant with a separate order counter and pickup counter: ordering and collecting are different moments, and either queue can back up.

    Each phase is delivered by a function call that returns at once: nb_transport_fw on the forward path, initiator to target, carries BEGIN_REQ and END_RESP; nb_transport_bw on the backward path, target to initiator, carries END_REQ and BEGIN_RESP. The phase is passed by reference, so the callee can move it along in its return, and the return value says what happened:

    Return (uvm_tlm_sync_e)

    Meaning

    What happens next

    UVM_TLM_ACCEPTED

    Phase received; nothing changed

    The callee will call back later on the other path

    UVM_TLM_UPDATED

    The callee advanced the phase argument

    The caller continues from the new phase

    UVM_TLM_COMPLETED

    The transfer is finished

    No more calls; the payload is free

    The base protocol allows shorter paths, and all of them are legal: the target may return UVM_TLM_COMPLETED to BEGIN_REQ (early completion, one call); it may send BEGIN_RESP without a separate END_REQ, which implies `END_REQ`; and the initiator may return UVM_TLM_COMPLETED to BEGIN_RESP instead of sending END_RESP. A transfer therefore takes four, three, two or one call. What is illegal is a phase on the wrong path or out of order.

    Every call also carries a uvm_tlm_time delay, as in b_transport, but its meaning is narrower: it says how far after the current simulation time the phase being delivered takes effect. A target that accepts a request "in 5 ns" returns UPDATED with END_REQ and a 5 ns delay, and the initiator treats the request as accepted only once that time has passed.

    The picture: four calls and the shortcuts

    Sequence diagram with four alternatives: the full four-call transfer, a three-call form where BEGIN_RESP implies END_REQ, a two-call form where the initiator returns COMPLETED to BEGIN_RESP, and a one-call early completion.
    The four legal forms of the base protocol. Forward calls carry BEGIN_REQ and END_RESP; backward calls carry END_REQ and BEGIN_RESP. A return of UPDATED can also deliver the next phase without a separate call.

    Build it: the two callbacks

    A non-blocking target implements nb_transport_fw and calls nb_transport_bw through its socket; a non-blocking initiator implements nb_transport_bw and calls nb_transport_fw. Both are functions: they must return immediately, so any waiting happens in a separate thread that later makes the next call.

    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
    // snippet: target side, inside the class named as IMP of a uvm_tlm_nb_target_socket
    function uvm_tlm_sync_e nb_transport_fw(uvm_tlm_generic_payload gp,
                                            ref uvm_tlm_phase_e phase,
                                            input uvm_tlm_time delay);
      case (phase)
        UVM_TLM_BEGIN_REQ: begin
          pending.push_back(gp);          // a thread will send BEGIN_RESP on tsock.nb_transport_bw
          phase = UVM_TLM_END_REQ;        // accept the request in the return itself
          return UVM_TLM_UPDATED;         // or execute now and return UVM_TLM_COMPLETED
        end
        UVM_TLM_END_RESP: return UVM_TLM_COMPLETED;
        default: return UVM_TLM_ACCEPTED;
      endcase
    endfunction
    
    // initiator side, inside the class named as IMP of a uvm_tlm_nb_initiator_socket
    function uvm_tlm_sync_e nb_transport_bw(uvm_tlm_generic_payload gp,
                                            ref uvm_tlm_phase_e phase,
                                            input uvm_tlm_time delay);
      case (phase)
        UVM_TLM_END_REQ: return UVM_TLM_ACCEPTED;   // free to send the next request
        UVM_TLM_BEGIN_RESP: begin
          // sample the response here, then close the transfer in the return
          return UVM_TLM_COMPLETED;                  // implies END_RESP
        end
        default: return UVM_TLM_ACCEPTED;
      endcase
    endfunction

    The initiator's thread

    Because both callbacks return at once, the initiator's sequential logic lives in a task that sends BEGIN_REQ, interprets the return, and otherwise waits for the backward callback to signal it. Events tie the two together:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // snippet: initiator side; nb_transport_bw triggers req_accepted and resp_done
    event req_accepted, resp_done;
    
    task do_transfer(uvm_tlm_generic_payload gp);
      uvm_tlm_phase_e phase = UVM_TLM_BEGIN_REQ;
      uvm_tlm_time    delay = new("delay");
      uvm_tlm_sync_e  sync  = isock.nb_transport_fw(gp, phase, delay);
      case (sync)
        UVM_TLM_COMPLETED: return;                         // early completion: one call
        UVM_TLM_UPDATED:   if (phase == UVM_TLM_BEGIN_RESP) begin
                             // the target answered in the return: close it ourselves
                             phase = UVM_TLM_END_RESP;
                             void'(isock.nb_transport_fw(gp, phase, delay));
                             return;
                           end
        default: ;                                          // ACCEPTED, or UPDATED to END_REQ
      endcase
      @resp_done;                                           // set by nb_transport_bw on BEGIN_RESP
    endtask

    A real initiator also honours the delay argument at each step and waits for req_accepted before it sends the next BEGIN_REQ. The structure is the point: functions for the callbacks, a task for the sequence of steps, and events between them.

    Ownership follows the phases. The target may read the payload from BEGIN_REQ on. The initiator must not modify or reuse the payload until the transfer completes, by a COMPLETED return or the final END_RESP. The base protocol also allows only one request phase and one response phase in progress per socket at a time, which is how END_REQ acts as backpressure: an initiator may not send the next BEGIN_REQ until the previous request has been accepted.

    Watch it run: a phase tracker

    The plain-SystemVerilog model below is not the UVM library; it captures the base protocol's rules as a state machine. It accepts the four-, three-, two- and one-call forms and flags phases on the wrong path or out of order:

    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
    typedef enum {PH_BEGIN_REQ, PH_END_REQ, PH_BEGIN_RESP, PH_END_RESP} ph_e;
    typedef enum {FW, BW} path_e;
    
    // Tracks one transaction through the TLM-2.0 base protocol.
    // state 0: idle, 1: request open, 2: request accepted, 3: response open
    class phase_tracker;
      int state;
      bit [31:0] held;
      function new();
        state = 0;
        held = '0;
      endfunction
      // A legal call returns 1 and advances the state; an illegal call returns 0.
      function bit call(path_e p, ph_e ph, bit [31:0] wdata);
        bit ok;
        ok = 0;
        case (state)
          0: if (p == FW && ph == PH_BEGIN_REQ) begin state = 1; held = wdata; ok = 1; end
          1: begin
            if (p == BW && ph == PH_END_REQ) begin state = 2; ok = 1; end
            else if (p == BW && ph == PH_BEGIN_RESP) begin state = 3; ok = 1; end  // implies END_REQ
          end
          2: if (p == BW && ph == PH_BEGIN_RESP) begin state = 3; ok = 1; end
          3: if (p == FW && ph == PH_END_RESP) begin state = 0; ok = 1; end
          default: ok = 0;
        endcase
        return ok;
      endfunction
      // A UVM_TLM_COMPLETED return closes the transaction from any open state.
      function void completed();
        state = 0;
      endfunction
    endclass
    
    Diagram
    PASS: 4-, 3-, 2- and 1-call transfers accepted, 3 illegal phases flagged, payload intact

    What breaks: the checker that rejected a legal model

    A team wrote a protocol checker for their non-blocking adapter that insisted on all four phases, in order, for every transfer. It worked against their own target. Then they connected a vendor's SystemC model through co-simulation, and every single register read was flagged as a protocol violation: the vendor's model completed simple register accesses early, returning UVM_TLM_COMPLETED to BEGIN_REQ, as the base protocol allows. A week went into a bug report against the vendor before someone read the standard. A second, real bug was hiding in the same adapter: after receiving UPDATED with END_REQ, it also waited for a separate END_REQ call that never came. A checker for TLM 2.0 must accept every legal form and flag only wrong paths, wrong order and ownership violations, exactly what the tracker above does.

    Interview

    "Walk me through nb_transport. Must a transfer always use four phases?"

    A weak answer: "four phases in order, and skipping one is a protocol violation."

    A strong answer: "Up to four: BEGIN_REQ and END_RESP on the forward path, END_REQ and BEGIN_RESP on the backward path, so either side can stall the request or the response. Each call returns ACCEPTED, UPDATED with a new phase, or COMPLETED. The base protocol allows three shortcuts: COMPLETED on BEGIN_REQ, BEGIN_RESP implying END_REQ, and COMPLETED on BEGIN_RESP instead of END_RESP, so one to four calls. The target may read the payload from BEGIN_REQ; the initiator must not modify it until completion."

    Expect the follow-ups: Why are the callbacks functions? (they must return at once; waiting happens in threads that make the next call) and How does END_REQ give backpressure? (no new BEGIN_REQ until the previous request is accepted).

    Check yourself The forward call returns UVM_TLM_UPDATED with the phase now END_REQ. Who makes the next call, and with which phase?

    The target, on the backward path, with BEGIN_RESP once the response is ready. The UPDATED return already delivered END_REQ, so no separate END_REQ call follows.

    Check yourself The initiator reuses the payload object for a new request right after BEGIN_RESP arrives and it returns UVM_TLM_ACCEPTED. What is wrong?

    Returning ACCEPTED means the initiator will send END_RESP later, so the transfer is not complete and the payload still belongs to it. Reusing the object now overwrites the response. Either return COMPLETED after sampling the response, or wait until after END_RESP.

    Check yourself A target answers BEGIN_REQ with UVM_TLM_COMPLETED. Where is the read data, and when may the initiator trust it?

    In the payload's data array, and the response status is already set, at the moment the forward call returns. Early completion is the non-blocking equivalent of b_transport: the whole transfer happened inside one call, so the initiator checks the status and uses the data immediately, honouring the returned delay if it is not zero.

    A last practical point: a non-blocking interface is only worth its complexity when the model actually overlaps transfers, accepting a new request while an earlier response is still pending. A register block that serves one access at a time gains nothing from it, which is why many SystemC models offer both interfaces and complete simple accesses early.

    Next: phases model a bus accurately, at the cost of many calls. Temporal decoupling goes the other way, trading timing accuracy for speed.

    Related topics

    • Blocking transport

    • Temporal decoupling

    • Generic payload: extensions and status

    Previous

    70 / 280

    Next: Temporal Decoupling

    Up next

    Up nextTemporal Decoupling

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Initiator and Target Sockets
    • TLM 2.0 Generic Payload: Extensions and Status