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.
[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)[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 privatePass-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.
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[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[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.
[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 detailsfunction 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[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 > 3Multi-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.
[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[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 terminalvirtual task put(pkt_t p);
`uvm_info("TERM_IMP",
$sformatf("terminal imp reached: id=%0d", p.id),
UVM_MEDIUM)
endtaskKey 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.