Part 4 · TLM & Analysis · Intermediate
transport(): Combined Request + Response
Using transport interfaces when a caller needs a direct response to each request, with memory-model style examples and integration guidance.
Why transport exists
The transport family models request-response coupling in one API call. It is ideal when every request naturally yields a response object and the caller should not manually orchestrate separate put/get channels.
A classic case is memory/register abstraction: caller sends a read/write request and receives status/data response. The method encapsulates both direction and synchronization contract.
Legend: [TLM] [UVM]
separate channels approach:
put(req)
get(rsp)
(caller coordinates pairing)
transport approach:
transport(req, rsp)
(pairing guaranteed by interface contract)transport simplifies per-request response pairing.
Useful for functional models that behave like callable services.
Avoids accidental request/response stream desynchronization.
Blocking transport example: memory service
class mem_req extends uvm_sequence_item;
`uvm_object_utils(mem_req)
rand bit write;
rand bit [15:0] addr;
rand bit [31:0] data;
function new(string name = "mem_req");
super.new(name);
endfunction
endclass
class mem_rsp extends uvm_sequence_item;
`uvm_object_utils(mem_rsp)
bit ok;
bit [31:0] data;
function new(string name = "mem_rsp");
super.new(name);
endfunction
endclass
class mem_model extends uvm_component;
`uvm_component_utils(mem_model)
uvm_transport_imp #(mem_req, mem_rsp, mem_model) tr_imp;
bit [31:0] mem [bit[15:0]];
function new(string name, uvm_component parent);
super.new(name, parent);
tr_imp = new("tr_imp", this);
endfunction
task transport(mem_req req, output mem_rsp rsp);
rsp = mem_rsp::type_id::create("rsp");
rsp.ok = 1'b1;
if (req.write) begin
mem[req.addr] = req.data;
rsp.data = req.data;
end
else begin
rsp.data = mem.exists(req.addr) ? mem[req.addr] : '0;
end
#5ns; // service latency
endtask
endclass
class mem_client extends uvm_component;
`uvm_component_utils(mem_client)
uvm_transport_port #(mem_req, mem_rsp) tr_port;
function new(string name, uvm_component parent);
super.new(name, parent);
tr_port = new("tr_port", this);
endfunction
task run_phase(uvm_phase phase);
mem_req req;
mem_rsp rsp;
req = mem_req::type_id::create("wreq");
req.write = 1;
req.addr = 'h40;
req.data = 32'hCAFE_BABE;
tr_port.transport(req, rsp);
req = mem_req::type_id::create("rreq");
req.write = 0;
req.addr = 'h40;
req.data = '0;
tr_port.transport(req, rsp);
`uvm_info("MEM_CLIENT", $sformatf("readback=0x%08h", rsp.data), UVM_MEDIUM)
endtask
endclass[UVM] connect_phase
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
client.tr_port.connect(model.tr_imp);
endfunctionNon-blocking transport and concurrency
For poll-driven services, nb_transport variants allow immediate return. The caller can continue with other work and retry or check completion state later.
class service extends uvm_component;
`uvm_component_utils(service)
uvm_nonblocking_transport_imp #(mem_req, mem_rsp, service) nb_tr_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
nb_tr_imp = new("nb_tr_imp", this);
endfunction
function bit nb_transport(mem_req req, output mem_rsp rsp);
rsp = mem_rsp::type_id::create("rsp");
if (resource_busy(req.addr))
return 0;
rsp.ok = 1;
rsp.data = req.write ? req.data : do_read(req.addr);
return 1;
endfunction
endclass[TLM] selection heuristic
use blocking transport when:
caller naturally waits for response
latency is part of modeled behavior
use non-blocking transport when:
caller must stay reactive
service availability is opportunisticKeep request-response pairing explicit regardless of blocking style.
Log request IDs/addresses for easy pairing debug.
Avoid hybrid hidden side channels that bypass transport responses.
Debugging response mismatch issues
Attach an ID field in req/rsp for traceability in logs.
Log at call entry and return with addr/kind/data/status.
Check for stale response object reuse across calls.
Verify one target implementation per call path in connect topology.
[UVM][TLM] transport debug table
Symptom Likely cause
-------------------------------------------------------------
response appears from old req rsp object reused incorrectly
wrong read data address decode mismatch
always timeout/busy target never leaves busy state
sporadic mismatches shared mutable req object raceKey takeaways
transport is the cleanest abstraction for tightly coupled request-response calls.
Blocking and non-blocking variants address different scheduling needs.
Trace IDs and explicit response lifecycle handling make debug deterministic.
Common pitfalls
Reusing response objects across requests without reset.
Mixing transport with ad-hoc side-channel responses.
Ignoring non-blocking transport failure returns.