While Put/Get ports are 1-to-1, Analysis Ports allow a single component
(like a Monitor) to broadcast a transaction to an unlimited number of subscribers
(Scoreboard, Coverage Collector, Loggers).
The "Void" Rule
Analysis write() calls are always void functions. They
cannot consume simulation time. This ensures that the monitor never waits for
the scoreboard to finish its math—it just drops the packet and keeps sampling.
The Three Analysis Components
1. uvm_analysis_port (The Broadcaster)
This is what the Monitor uses to send transactions. It can connect to multiple subscribers.
// Inside the Monitor
class my_monitor extends uvm_monitor;
uvm_analysis_port #(my_pkt) mon_ap;
function new(string name, uvm_component parent);
super.new(name, parent);
mon_ap = new("mon_ap", this);
endfunction
task run_phase(uvm_phase phase);
forever begin
sample_bus(pkt);
mon_ap.write(pkt); // Broadcast to all subscribers
end
endtask
endclass
2. uvm_analysis_imp (The Subscriber)
This is what the Scoreboard or Coverage Collector uses to receive transactions.
It implements the write() function.
class my_scoreboard extends uvm_scoreboard;
// Analysis implementation port
uvm_analysis_imp #(my_pkt, my_scoreboard) mon_export;
function new(string name, uvm_component parent);
super.new(name, parent);
mon_export = new("mon_export", this);
endfunction
// This function is called when the monitor writes
function void write(my_pkt pkt);
// Check the packet
if (pkt.data != expected_data)
`uvm_error("SCB", "Mismatch detected!")
endfunction
endclass
3. uvm_analysis_export (The Pass-Through)
This is used for hierarchical connections. It allows a parent component (like an
Environment)
to expose a child's analysis port to the outside world.
class my_env extends uvm_env;
my_agent agent;
uvm_analysis_export #(my_pkt) mon_export;
function new(string name, uvm_component parent);
super.new(name, parent);
mon_export = new("mon_export", this);
endfunction
function void connect_phase(uvm_phase phase);
// Pass through: env's export connects to agent's monitor port
mon_export.connect(agent.monitor.mon_ap);
endfunction
endclass
Multiple Subscribers Example
The real power of analysis ports is broadcasting to multiple components simultaneously.
class my_env extends uvm_env;
my_agent agent;
my_scoreboard sb;
my_coverage_collector cov;
my_logger logger;
function void connect_phase(uvm_phase phase);
// Connect monitor to scoreboard
agent.monitor.mon_ap.connect(sb.mon_export);
// Connect monitor to coverage collector
agent.monitor.mon_ap.connect(cov.mon_export);
// Connect monitor to logger
agent.monitor.mon_ap.connect(logger.mon_export);
// Now when the monitor calls mon_ap.write(pkt),
// all three components receive the packet!
endfunction
endclass
Using uvm_tlm_analysis_fifo for Decoupling
Sometimes you want to decouple the monitor from the scoreboard.
The uvm_tlm_analysis_fifo acts as a buffer between them.
class my_env extends uvm_env;
my_agent agent;
my_scoreboard sb;
uvm_tlm_analysis_fifo #(my_pkt) fifo;
function void build_phase(uvm_phase phase);
agent = my_agent::type_id::create("agent", this);
sb = my_scoreboard::type_id::create("sb", this);
fifo = new("fifo", this);
endfunction
function void connect_phase(uvm_phase phase);
// Monitor writes to FIFO
agent.monitor.mon_ap.connect(fifo.analysis_export);
// Scoreboard reads from FIFO (blocking get)
// This happens in the scoreboard's run_phase:
// fifo.get(pkt);
endfunction
endclass
When to Use Each
- uvm_analysis_imp: Direct, immediate processing (e.g., protocol
checking, coverage collection)
- uvm_tlm_analysis_fifo: Buffered, decoupled processing (e.g.,
scoreboarding with complex comparisons)
- uvm_analysis_export: Hierarchical pass-through (exposing child
ports to parent level)