The short answer: UVM components communicate through TLM (transaction-level modelling) connections instead of direct handles. A port calls a method, an imp implements it, and an export passes a connection up or down the hierarchy. Put, get and peek ports give one-to-one transfers; analysis ports give one-to-many broadcast with a non-blocking write(), which is how monitors feed scoreboards and coverage. Analysis FIFOs buffer broadcasts for task-based consumers, and `uvm_analysis_imp_decl lets one component accept several analysis streams.
TLM is the plumbing of a UVM testbench. It looks heavy at first, but it’s what lets a monitor feed three checkers without knowing any of them exist. This post combines my older posts on TLM and analysis ports.
- The pieces
- Analysis ports: the one you’ll use most
- Several inputs in one component
- Analysis FIFOs
- Ports, exports and imps
- TLM 1 vs TLM 2
Table of Contents
Part 1: The pieces

Part 2: Analysis ports: the one you’ll use most
// monitor
uvm_analysis_port #(bus_item) ap;
ap = new("ap", this);
...
ap.write(t); // broadcast to every subscriber
// coverage collector: the simplest subscriber
class bus_cov extends uvm_subscriber #(bus_item);
`uvm_component_utils(bus_cov)
covergroup cg with function sample(bus_item t); ... endgroup
function new(string name, uvm_component parent);
super.new(name, parent); cg = new();
endfunction
function void write(bus_item t); cg.sample(t); endfunction
endclass
// env connect_phase
agt.mon.ap.connect(cov.analysis_export);
write() is a function, so subscribers must not block. If a subscriber needs to wait (for example, for a matching item), put an analysis FIFO in between.
Part 3: Several inputs in one component
`uvm_analysis_imp_decl(_exp)
`uvm_analysis_imp_decl(_act)
class my_sb extends uvm_scoreboard;
`uvm_component_utils(my_sb)
uvm_analysis_imp_exp #(bus_item, my_sb) exp_imp;
uvm_analysis_imp_act #(bus_item, my_sb) act_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
exp_imp = new("exp_imp", this);
act_imp = new("act_imp", this);
endfunction
function void write_exp(bus_item t); ... endfunction
function void write_act(bus_item t); ... endfunction
endclass
Part 4: Analysis FIFOs
uvm_tlm_analysis_fifo #(bus_item) exp_fifo, act_fifo;
...
mon_in.ap.connect(exp_fifo.analysis_export);
mon_out.ap.connect(act_fifo.analysis_export);
...
task run_phase(uvm_phase phase);
forever begin
bus_item e, a;
exp_fifo.get(e);
act_fifo.get(a);
if (!a.compare(e)) `uvm_error("SB", {"mismatch: ", a.convert2string()})
end
endtask
This is the simplest in-order scoreboard: no imp declarations, and the comparison runs as a task.
Part 5: Ports, exports and imps
| Object | Role | Example |
|---|---|---|
| port | Requires an interface: it calls put/get/write | uvm_blocking_put_port #(T) |
| imp | Provides the implementation | uvm_blocking_put_imp #(T, my_comp) |
| export | Forwards a connection through a parent | agent exposes its monitor’s port |
| connect() | Always called on the initiator side: port.connect(export or imp) | drv.seq_item_port.connect(sqr.seq_item_export) |
Connections are made in connect_phase. Unconnected ports that require a connection produce an error at the end of elaboration, which is helpful.
Part 6: TLM 1 vs TLM 2
UVM also includes TLM 2.0 sockets with generic payloads and timing annotation, mainly for connecting to SystemC models. Most block- and chip-level testbenches only use the TLM 1 ports above.
Practise this on VLSI Forge
I built VLSI Forge so you can write RTL in your browser, run it on a real simulator and check every signal in the waveform. Free, nothing to install.
Previous in my UVM series: Configuration and uvm_config_db. Next: Scoreboards.
FAQ
What is TLM in UVM?
Transaction-level modelling: a standard way for components to pass transactions through ports, exports and imps instead of direct handles.
What is the difference between an analysis port and a put port?
An analysis port broadcasts to any number of subscribers with a non-blocking write; a put port is one-to-one and can block.
Why use an analysis FIFO?
Because analysis write() can’t block. A FIFO stores items so a consumer task can get() them when it’s ready.
What does `uvm_analysis_imp_decl do?
It creates a new imp class with a differently named write method, so one component can receive several analysis streams.
