Part 4 · TLM & Analysis · Intermediate

Hierarchical Pass-Through: Parent Export to Child Endpoint Chains

How to forward interfaces through hierarchy so parent components expose child connectivity cleanly using exports and compatible child endpoints.

Why pass-through exists

Reusable environments avoid exposing deep child internals directly. Instead, a parent component publishes an interface endpoint and forwards it to child endpoints. This creates stable external APIs with internal flexibility.

Exports are the usual bridge for this pattern. In many designs, parent exports connect into child ports/exports according to interface direction and family compatibility, then ultimately terminate at an imp.

diagram
[UVM][TLM] hierarchical pass-through concept

top requester port
   │
   ▼
parent export  (public API at parent boundary)
   │
   ▼
child endpoint chain (port/export as design requires)
   │
   ▼
terminal imp (actual implementation)
diagram
[TLM] encapsulation benefit

without pass-through:
  top directly references deep_child.driver.req_port

with pass-through:
  top references parent.req_export only
  internal child structure stays private
  • Pass-through makes hierarchy refactors safer because external bind points remain stable.

  • Parent-level exports form explicit interface contracts for composite components.

  • Terminal imp ownership still defines final behavior regardless of hierarchy depth.


Pattern A: export forwarding into child chain

This pattern exposes a parent export and forwards to child connectivity so upper layers can bind once at parent boundary.

systemverilog
class packet_agent extends uvm_component;
  `uvm_component_utils(packet_agent)
  packet_driver drv;
  packet_router rt;

  uvm_blocking_put_export #(pkt_t) req_export;

  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    drv = packet_driver::type_id::create("drv", this);
    rt  = packet_router::type_id::create("rt", this);
    req_export = new("req_export", this);
  endfunction

  function void connect_phase(uvm_phase phase);
    super.connect_phase(phase);
    req_export.connect(rt.upstream_port);
    rt.downstream_export.connect(drv.req_imp);
  endfunction
endclass
diagram
[UVM][TLM] parent export wiring map

external caller -> agent.req_export
agent.req_export -> router.upstream_port
router.downstream_export -> driver.req_imp

call path resolves across hierarchy without exposing driver handle
diagram
[TLM] adaptation note

exact intermediate endpoint kinds depend on interface family and component design.
key requirement:
  every hop remains type-compatible and directionally legal.
terminal endpoint:
  imp with concrete method implementation.
  • Keep parent export names domain-specific (req_export, rsp_export, cfg_export).

  • Avoid generic endpoint names that hide intent across large env hierarchies.

  • Add unit tests that validate pass-through topology with one deterministic call.


Pattern B: multi-level pass-through chain

Large benches may route through env -> subsystem -> agent -> driver. Multi-level pass-through is maintainable if each layer owns one clear forwarding responsibility.

diagram
[UVM][TLM] multi-level chain

test_seq.port
   -> env.req_export
   -> ss.req_export
   -> agt.req_export
   -> drv.req_imp

Each layer:
  exposes one boundary endpoint
  forwards in connect_phase
  hides internal child details
systemverilog
function void top_env::connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  test_adapter.req_port.connect(req_export);
  req_export.connect(ss.req_export);
endfunction

function void subsystem::connect_phase(uvm_phase phase);
  super.connect_phase(phase);
  req_export.connect(agent.req_export);
endfunction
diagram
[TLM] chain governance rules

1) one owner per forwarding edge
2) no bypass connections around published boundary endpoints
3) preserve naming consistency at each hierarchy layer
4) keep chain depth reasonable; add diagrams when depth > 3
  • Multi-level pass-through scales when each layer has explicit forwarding contracts.

  • Boundary endpoints should be treated as public APIs with documentation.

  • Bespoke bypass binds create hidden coupling and brittle integration.


Debugging hierarchical pass-through

Pass-through bugs often look like missing traffic because calls never reach terminal imp. Instrument each boundary during bring-up to confirm chain continuity.

diagram
[UVM][TLM] chain-debug playbook

for each boundary layer:
  - print connect confirmation at UVM_LOW
  - optionally add temporary probe callback/log in provider method

if call missing at terminal:
  inspect nearest upstream forwarding edge first
diagram
[TLM] common pass-through failures

failure: parent export declared but never connected in connect_phase
failure: one mid-layer connect reversed or incompatible
failure: endpoint type changed in child, parent forwarding not updated
failure: chain ends at export with no imp terminal
systemverilog
virtual task put(pkt_t p);
  `uvm_info("TERM_IMP",
            $sformatf("terminal imp reached: id=%0d", p.id),
            UVM_MEDIUM)
endtask

Key takeaways

  • Hierarchical pass-through keeps external APIs stable while internal structure evolves.

  • Parent exports are effective boundary endpoints when forwarding is explicit and tested.

  • Every pass-through chain must terminate at an imp implementation.

  • Boundary-by-boundary tracing localizes pass-through failures quickly.

Common pitfalls

  • Publishing parent exports without maintaining forwarding connections after refactors.

  • Bypassing parent boundary APIs and binding directly to deep children.

  • Allowing chains to terminate in export-only topology with no provider implementation.

  • No topology smoke test for multi-level pass-through paths.