Part 4 · TLM & Analysis · Intermediate
Port, Export, and Imp Objects: What Each Type Does
Detailed object responsibilities for TLM endpoints and how each one participates in binding and execution.
Object semantics in one view
A clean mental model is: port expresses demand, imp provides capability, export forwards capability through hierarchy. These are not interchangeable declarations - they represent distinct intent.
[UVM][TLM] endpoint responsibilities
port:
- call site handle
- initiates interface methods
- may chain through hierarchy
export:
- relay endpoint
- forwards interface binding
- often used at subsystem boundaries
imp:
- terminal endpoint
- binds to concrete class method implementation[TLM] ownership pattern
testbench composition:
requester component owns port
intermediate container may own export
provider component owns imp
execution:
requester call travels through chain to impDeclare endpoints by role intent, not by what compiles fastest.
Imp is the only endpoint that actually executes interface behavior.
Export is best treated as interface plumbing through component layers.
Port deep dive
A port is a typed handle used by initiator code. For example, uvm_blocking_put_port#(txn_t) means this component wants to call blocking put on txn_t objects through some provider chain resolved in connect_phase.
class req_gen extends uvm_component;
`uvm_component_utils(req_gen)
uvm_nonblocking_put_port #(req_t) req_port;
function new(string name, uvm_component parent);
super.new(name, parent);
req_port = new("req_port", this);
endfunction
task run_phase(uvm_phase phase);
req_t r;
bit ok;
forever begin
r = req_t::type_id::create("r");
assert(r.randomize());
ok = req_port.try_put(r);
if (!ok) `uvm_warning("FLOW", "try_put failed due to flow control")
end
endtask
endclass[UVM][TLM] port behavior notes
port does:
- type checks interface signature
- initiates call during run-time
- reports bind/connect issues when unresolved
port does not:
- store business logic implementation
- decide provider semanticsUse blocking vs non-blocking ports to express flow-control expectations clearly.
Keep initiator behavior in the owner component, not in connection code.
Initialize ports in constructor/build and bind only in connect_phase.
Export deep dive
An export is a forwarding endpoint that re-exposes an interface. It lets parent components present interfaces owned by deeper child components, avoiding direct external references into child internals.
class stream_agent extends uvm_component;
`uvm_component_utils(stream_agent)
stream_driver drv;
uvm_blocking_put_export #(req_t) req_export;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = stream_driver::type_id::create("drv", this);
req_export = new("req_export", this);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
// Parent-level users connect to agent.req_export.
// Agent forwards to internal driver port/chain endpoint.
req_export.connect(drv.req_port);
endfunction
endclass[UVM][TLM] export as boundary contract
outside world sees:
agent.req_export
inside agent:
req_export forwards to child endpoint chain
benefit:
child structure can evolve while external interface remains stable[TLM] where export shines
- subsystem-to-agent boundary
- reusable VIP wrappers
- multi-layer env composition
- preserving encapsulation with explicit public TLM surfaceUse exports to publish stable TLM boundaries from composite components.
Document where each export forwards to reduce hidden coupling.
Avoid long anonymous export chains; name them by functional intent.
Imp deep dive and terminal behavior
Imp is the terminal endpoint that binds method calls to concrete class code. Its second type parameter is the implementation class, which is where interface methods are executed.
class req_sink extends uvm_component;
`uvm_component_utils(req_sink)
uvm_nonblocking_put_imp #(req_t, req_sink) req_imp;
int accepted_count;
function new(string name, uvm_component parent);
super.new(name, parent);
req_imp = new("req_imp", this);
endfunction
virtual function bit try_put(req_t r);
if (!can_accept(r)) return 0;
accepted_count++;
process_req(r);
return 1;
endfunction
protected function bit can_accept(req_t r);
return (r != null);
endfunction
protected function void process_req(req_t r);
`uvm_info("SINK", $sformatf("accepted req id=%0d", r.id), UVM_MEDIUM)
endfunction
endclass[UVM][TLM] imp terminal role
incoming call chain:
requester port -> ... -> req_imp.try_put(r)
terminal behavior:
class method executes
local state updates
status/return values producedKey takeaways
Port expresses call intent, export relays interface, and imp executes behavior.
Export helps maintain encapsulation by exposing stable public TLM boundaries.
Imp methods are where flow control and semantics become real behavior.
Design endpoint declarations as API contracts, not temporary wiring artifacts.
Common pitfalls
Declaring imps without implementing required interface methods correctly.
Hiding critical export forwarding in undocumented connect code.
Treating port declarations as passive fields instead of runtime call endpoints.
Using inconsistent transaction type parameters across endpoint chain.