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. TLM 2.0 Generic Payload: Extensions and Status

Part 4 · TLM & Analysis · Intermediate

TLM 2.0 Generic Payload: Extensions and Status

uvm_tlm_generic_payload fields and accessors, response status codes, byte enables and streaming width, uvm_tlm_extension with set_extension and get_extension, and what UVM-SV TLM2 leaves out compared with SystemC.

On this page

    The DMA's TLM model must refuse non-secure accesses to its KEY register, the way the RTL does when the AXI PROT[1] bit is set. But the generic payload has a command, an address, data and byte enables. It has no AXI ID, no PROT, no QoS. Someone suggests encoding PROT in the top address bit. Someone else suggests a custom payload class. Both break interoperability with every other model on the platform. Where does protocol sideband go, and how does a target say no in a way every initiator understands?

    The idea: a standard envelope with a pocket for extras

    The generic payload (uvm_tlm_generic_payload, typedef uvm_tlm_gp) is TLM 2.0's single memory-mapped transaction: command, address, data, lengths, byte enables, streaming width and response status. Every compliant model reads and writes those fields the same way, which is why a UVM initiator can drive a SystemC target it has never seen. For anything else, the payload carries extensions: small objects attached by type, which models that know about them read and models that do not simply ignore.

    Think of a standard shipping envelope. Every post office knows where the address and the stamp go. A sender who needs fragile, keep upright attaches a sticker; offices that understand it handle the parcel accordingly, and offices that do not still deliver it.

    The picture: what a target checks, in order

    The initiator builds the payload and attaches an extension; the target checks the extension, the address and the streaming width, setting COMMAND_ERROR, ADDRESS_ERROR or BURST_ERROR, or executes and sets OK; the initiator checks is_response_ok.
    The flow of the lesson's TLM2 test. The initiator always starts with INCOMPLETE; the target sets exactly one final status, and the initiator checks it before trusting any data.

    Build it

    Fields and accessors

    Field

    Accessors

    Default after new()

    Notes

    Command

    set_command, get_command, set_read, set_write, is_read, is_write

    UVM_TLM_IGNORE_COMMAND

    IGNORE asks the target to do nothing (useful for probing extensions)

    Address

    set_address, get_address

    0

    64 bits, the first byte of the transfer

    Data

    set_data(ref byte unsigned p[]), get_data(output ...)

    empty

    A byte array; the initiator owns and sizes it

    Data length

    set_data_length, get_data_length

    0

    Bytes to transfer; must match the data array

    Byte enables

    set_byte_enable, get_byte_enable, set_byte_enable_length, get_byte_enable_length

    length 0

    0xFF enables a byte, 0x00 disables it; length 0 means all enabled

    Streaming width

    set_streaming_width, get_streaming_width

    0

    Set it equal to the data length for a normal transfer

    Response status

    set_response_status, get_response_status, is_response_ok, is_response_error, get_response_string

    UVM_TLM_INCOMPLETE_RESPONSE

    Set by the target

    DMI hint

    set_dmi_allowed, is_dmi_allowed

    0

    A flag only: UVM-SV has no DMI interface

    Use the accessors, not the m_* members. The members are public for historical reasons, but accessors are what SystemC models and adapters expect, and they keep your code portable. Never randomize() a whole generic payload: the data array, lengths and byte enables must agree, and random values will not.

    Response status codes

    Status

    Value

    A target returns it when

    UVM_TLM_OK_RESPONSE

    1

    The transfer was executed

    UVM_TLM_INCOMPLETE_RESPONSE

    0

    Never as a result: the initiator sets it before the call, so a target that forgot to set a status is detectable

    UVM_TLM_GENERIC_ERROR_RESPONSE

    -1

    Any other failure

    UVM_TLM_ADDRESS_ERROR_RESPONSE

    -2

    The address or range is not supported

    UVM_TLM_COMMAND_ERROR_RESPONSE

    -3

    The command is not supported here (or refused, as for a non-secure access)

    UVM_TLM_BURST_ERROR_RESPONSE

    -4

    The data length or streaming width is not supported

    UVM_TLM_BYTE_ENABLE_ERROR_RESPONSE

    -5

    Byte enables are not supported, or the pattern is not

    is_response_ok() is true only for OK, the one positive value, and is_response_error() is simply its negation. So INCOMPLETE counts as an error: a target that never set a status looks like a failure, not a success, which is exactly why the initiator sets it before the call.

    Byte enables and streaming width

    Byte enables select which bytes of the data array take part. The array may be shorter than the data: it is applied repeatedly, so byte i uses be[i % byte_enable_length]. Streaming width models a FIFO-like target: with a data length of 16 and a streaming width of 4, the target sees four 4-byte beats to the same 4-byte address window. A plain memory sets streaming width equal to data length; a target that cannot stream returns BURST_ERROR for anything else.

    An extension for AXI sideband

    An extension is a class derived from uvm_tlm_extension #(T) with itself as T. The parameter gives each extension type a unique static handle, T::ID(), which is the key the payload stores it under. Register it with ` uvm_object_utils ` and give it a do_copy`, because copying a payload clones its extensions.

    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
    34
    35
    36
    37
    // snippet: from tlm2_gp_test in tlm_tb.sv
    class axi_attr_ext extends uvm_tlm_extension #(axi_attr_ext);
      bit [3:0] id;
      bit [2:0] prot;                                   // prot[1] = non-secure
      `uvm_object_utils(axi_attr_ext)
      function new(string name = "axi_attr_ext");
        super.new(name);
      endfunction
      function void do_copy(uvm_object rhs);
        axi_attr_ext r;
        super.do_copy(rhs);
        $cast(r, rhs);
        id = r.id; prot = r.prot;
      endfunction
    endclass
    
    // initiator: build the payload, attach the extension, call
    gp = uvm_tlm_generic_payload::type_id::create("gp");
    gp.set_command(UVM_TLM_WRITE_COMMAND);
    gp.set_address(64'h20);
    gp.set_data(data);
    gp.set_data_length(data.size());
    gp.set_byte_enable(be);                             // empty array: all bytes enabled
    gp.set_byte_enable_length(be.size());
    gp.set_streaming_width(data.size());
    gp.set_response_status(UVM_TLM_INCOMPLETE_RESPONSE);
    attr.id = 4'h3; attr.prot = 3'b010;
    void'(gp.set_extension(attr));                      // returns the previous one of this type, or null
    isock.b_transport(gp, delay);
    
    // target (uvm_tlm_extension_base xb; declared with the task's other variables):
    // look it up by type; null if the initiator attached none
    xb = gp.get_extension(axi_attr_ext::ID());
    if (xb != null && $cast(attr, xb) && attr.prot[1]) begin
      gp.set_response_status(UVM_TLM_COMMAND_ERROR_RESPONSE);  // non-secure access refused
      return;
    end
    • A payload holds at most one extension of each type; set_extension with a second one replaces the first and returns it.

    • get_extension(T::ID()) returns a base-class handle, or null when no extension of that type is attached. Test for null before you $cast and use it, as the target above does: an initiator that knows nothing about your extension is legal.

    • clear_extension(T::ID()), clear_extensions() and get_num_extensions() complete the set.

    • The register layer's uvm_reg_tlm_adapter converts register operations to and from uvm_tlm_gp, so the same fields and statuses appear in RAL.

    Watch it run

    The lesson's TLM2 test, after the writes and read shown in Blocking transport. The second write had byte enables FF FF 00 00; the read-back shows only the first two bytes changed. Then a read past the end of the 256-byte model, a non-secure write, and a clone:

    Diagram
    UVM_INFO tlm_tb.sv(502) @ 20000: uvm_test_top.ini [GP]  UVM_TLM_WRITE_COMMAND [0x0000000000000010] = aa bb -- -- (status=OK) -> OK
    UVM_INFO tlm_tb.sv(514) @ 30000: uvm_test_top.ini [GP] read back aa bb 33 44
    UVM_INFO tlm_tb.sv(502) @ 30000: uvm_test_top.ini [GP]  UVM_TLM_READ_COMMAND [0x00000000000000fe] = aa bb 33 44 (status=ADDRESS_ERROR) -> ADDRESS_ERROR
    UVM_INFO tlm_tb.sv(502) @ 30000: uvm_test_top.ini [GP]  UVM_TLM_WRITE_COMMAND [0x0000000000000020] = aa bb cc dd (status=COMMAND_ERROR) -> COMMAND_ERROR
    UVM_INFO tlm_tb.sv(523) @ 30000: uvm_test_top.ini [GP] clone has 1 extension(s), prot=10, same object=0
    • convert2string() prints disabled bytes as --, and get_response_string() turns the status into OK, ADDRESS_ERROR and so on.

    • The ADDRESS_ERROR read still shows data: the initiator's stale buffer. Status first, data second.

    • The clone carries its own copy of the extension (same object=0) with the same contents (prot is 3'b010, printed in binary as 10).

    What breaks: the extension that vanished on copy

    A team's scoreboard cloned every generic payload it received from a passthrough monitor, then looked up the AXI attributes extension to decide which checks applied. Every cloned payload came back with no extension, so every transfer was treated as secure, and the security checks never ran. The extension class had no ` uvm_object_utils ` macro. uvm_tlm_extension itself returns null from create(), so clone() on the unregistered extension failed, with a CRFLD warning (*the create method failed ... object cannot be cloned*) that nobody read, and the copied payload got a null in place of the extension. Registering the extension with the factory and adding a do_copy fixed it, and a check that get_extension is not null` when the sideband is mandatory would have caught it on day one.

    What UVM-SV TLM 2.0 does not provide

    UVM's SystemVerilog TLM 2.0 is a subset of the IEEE 1666 SystemC standard. Interviewers who know SystemC ask about the gaps:

    SystemC TLM 2.0 has

    UVM-SV has

    Direct memory interface: get_direct_mem_ptr, invalidate_direct_mem_ptr

    Only the dmi_allowed hint flag

    Debug transport: transport_dbg

    Nothing

    tlm_global_quantum and tlm_quantumkeeper

    Nothing: initiators keep their own offset (temporal decoupling)

    Payload event queues (peq_with_get, peq_with_cb_and_phase)

    Nothing: use your own queues and threads

    Convenience sockets (simple_target_socket with registered callbacks, multi-passthrough sockets)

    The fixed socket classes, one target per initiator socket

    Memory management: tlm_mm_interface, acquire/release, sticky and automatic extensions

    Garbage-collected objects; extensions stay until cleared

    None of this matters inside a pure UVM bench. It matters at the SystemC boundary, where a co-simulation layer such as UVM Connect translates between the two, and where a SystemC model that relies on DMI or debug transport needs a different path.

    Interview

    "How would you pass protocol sideband, such as an AXI ID or PROT, through a TLM 2.0 generic payload?"

    A weak answer: "put it in unused address bits" or "extend the generic payload class." Both break every model that expects the standard payload.

    A strong answer: "With an extension: a class derived from uvm_tlm_extension of itself, registered with the factory and with a do_copy. The initiator attaches it with set_extension; a target that understands it looks it up with get_extension(T::ID()) and casts, and a target that does not ignores it. One extension per type per payload, and clones copy it. The target reports refusals with the standard status codes, here COMMAND_ERROR, and the initiator sets INCOMPLETE before the call so a silent target is visible."

    Expect the follow-ups: What does a byte-enable array shorter than the data mean? (it repeats) and What does UVM-SV leave out of TLM 2.0? (DMI, debug transport, the quantum keeper, PEQs, memory management).

    Check yourself After b_transport returns, is_response_error() is 1 but get_response_string() says INCOMPLETE. What happened, and why is this the behaviour you want?

    The target never set a status, so the value the initiator set before the call is still there. is_response_error() is the negation of is_response_ok(), so INCOMPLETE counts as an error. Had the initiator not set INCOMPLETE first, a stale OK from a reused payload could have made a target that did nothing look successful.

    Check yourself A 16-byte write carries a 4-byte byte-enable array FF 00 FF 00. Which data bytes are written?

    The pattern repeats every 4 bytes, so bytes 0, 2, 4, 6, 8, 10, 12 and 14 are written and the odd bytes are left unchanged. A target that cannot handle byte enables should return UVM_TLM_BYTE_ENABLE_ERROR_RESPONSE rather than ignore them.

    Check yourself Two different components each attach an axi_attr_ext to the same payload. What does the payload hold afterwards?

    One extension of that type: the second set_extension replaced the first and returned the old one. If both pieces of information are needed, they belong in one extension, or in two different extension classes.

    Next: with TLM 1 plumbing and TLM 2.0 sockets covered, the course turns to the stimulus side. UVM sequences is where the transactions flowing through all these connections are created.

    Related topics

    • Initiator and target sockets

    • Blocking transport

    • Temporal decoupling

    • Register layer overview

    Previous

    72 / 280

    Next: UVM Sequences & Stimulus

    Up next

    Up nextUVM Sequences & Stimulus

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Initiator and Target Sockets
    • Blocking Transport
    • Non-Blocking Transport