Verification

UVM Analysis Port

By CourseTron Team ·  8 min read ·  Updated Oct 2026 💬 𝕏 in f
Table of Contents
    ? Print / Save as PDF

    In short: A UVM monitor uses a single uvm_analysis_port to broadcast every reconstructed transaction to any number of subscribers (scoreboard, coverage collector, loggers) through a one-to-many write() call, and a subscriber that needs to buffer those transactions pulls them from a uvm_tlm_analysis_fifo instead of acting on them immediately.

    Of all the TLM building blocks in UVM, the analysis port is the one that separates a tangled testbench from a clean, reusable one. It is how passive components observe the DUT without ever being wired to each other. If you have interviewed for a verification role, you have almost certainly been asked to "explain how a monitor talks to the scoreboard and the coverage collector at the same time" — and the honest answer is: it doesn't talk to them, it broadcasts, and they choose to listen.

    Why a normal TLM port isn't enough

    A standard UVM blocking port such as uvm_blocking_put_port is a strict 1-to-1 connection: exactly one port, one export, one implementation. That is perfect for a driver pulling from a sequencer, where there is a single consumer. But a monitor sits in the middle of the testbench and typically feeds three or four independent consumers who must not know about each other. Connecting a 1-to-1 port to two exports throws a build-time error.

    The analysis port solves this with a different contract. An uvm_analysis_port can be connected to zero, one, or many uvm_analysis_imp (or analysis FIFO) targets. When the producer calls ap.write(txn), UVM iterates its subscriber list and invokes the write() method of every connected subscriber in turn. The call is non-blocking and void — it never consumes simulation time and never returns a value. This is the architectural key: the monitor fires and forgets, so adding a tenth coverage collector cannot slow down or stall the monitor.

    Analysis write() is a void function, not a task. It cannot block, cannot #delay, and must complete in zero time. If a subscriber needs to wait, it must queue the transaction — that is precisely the job of the analysis FIFO.

    The broadcast picture

    One analysis port, three subscribers Monitor ap (port) write(txn) Scoreboardanalysis_fifo → compareCoverage Collectoranalysis_imp → sample()Logger / Predictoranalysis_imp → write()CourseTron · coursetron.com
    One analysis port, three independent subscribers. The monitor never names any of them — connect() in the env wires them up.

    Declaring and writing from the monitor

    Inside the monitor you parameterize the port with the transaction type and call write() once the item is reconstructed from pin wiggles:

    class apb_monitor extends uvm_monitor;
      uvm_analysis_port #(apb_txn) ap;
      function void build_phase(uvm_phase phase);
        ap = new("ap", this);
      endfunction
      task run_phase(uvm_phase phase);
        forever begin
          apb_txn t = collect_transaction();  // samples the interface
          ap.write(t);                        // broadcast to all listeners
        end
      endtask
    endclass

    Note there is only one port even though three components will receive the data. The monitor is completely decoupled: you can drop it into a block-level or SoC-level environment unchanged.

    The coverage collector: subscribe directly

    A coverage collector does not need to buffer anything — it samples a covergroup the instant a transaction arrives. The idiomatic choice is uvm_subscriber, which already contains an analysis_export and asks you to implement write():

    class apb_cov extends uvm_subscriber #(apb_txn);
      apb_txn tr;
      covergroup cg; coverpoint tr.addr; coverpoint tr.cmd; endgroup
      function new(string n, uvm_component p); super.new(n,p); cg=new(); endfunction
      function void write(apb_txn t); tr=t; cg.sample(); endfunction
    endclass

    The scoreboard: buffer with an analysis FIFO

    The scoreboard is different. It must compare a DUT output against a reference, and the two streams rarely arrive on the same clock. If the scoreboard tried to act inside write() it would be forced to do all its work in zero time — impossible when it needs to wait for the matching expected item. The clean solution is uvm_tlm_analysis_fifo, an unbounded FIFO that already provides an analysis_export on its input side and a blocking get() on its output side.

    class apb_sb extends uvm_scoreboard;
      uvm_tlm_analysis_fifo #(apb_txn) act_fifo, exp_fifo;
      task run_phase(uvm_phase phase);
        apb_txn a, e;
        forever begin
          exp_fifo.get(e);   // blocks until predictor pushes
          act_fifo.get(a);   // blocks until monitor pushes
          if (!a.compare(e)) `uvm_error("SB", "mismatch")
        end
      endtask
    endclass

    An analysis FIFO is the adapter between the non-blocking analysis world and the blocking get() world. Its write() side accepts broadcasts instantly; its get() side lets a consuming thread wait. It is unbounded, so write() can never stall the monitor.

    Analysis FIFO: non-blocking in, blocking out Monitor ap.write(t) write() uvm_tlm_analysis_fifo t1t2t3... unbounded queue get() Scoreboard run_phase write() is a void function, zero time, never stalls the monitor CourseTron · coursetron.com
    The analysis FIFO decouples the zero-time write() from the scoreboard thread that waits on get().

    Port, imp, or FIFO — which do I use?

    NeedUseWhy
    Act immediately, no waiting (coverage)uvm_subscriber / analysis_impYou implement write(); runs in zero time
    Buffer and consume at your own pace (scoreboard)uvm_tlm_analysis_fifoNon-blocking in, blocking get() out
    Fan out to many targetsuvm_analysis_portOne write() reaches all subscribers
    A component has two independent inbound streams`uvm_analysis_imp_decl macroCreates write_a(), write_b() suffixed methods

    Watching the broadcast in motion

    One write(), three copies of the handle Monitor ap.write(t) Scoreboard FIFOCoverageLogger Same delta cycle, zero simulation time CourseTron · coursetron.com
    Each ap.write(t) delivers the same transaction handle to every subscriber in the same delta cycle.

    Gotchas that cost real debug time

    Handle vs. copy. write() passes the object handle, not a deep copy. If the monitor reuses or modifies that object on the next loop, every subscriber sees the change. Always allocate a fresh transaction each iteration, or clone() before writing. This is the single most common cause of phantom scoreboard mismatches in Synopsys VCS and Cadence Xcelium runs alike.

    Forgotten connect. If you never call mon.ap.connect(sb.act_fifo.analysis_export) in the env's connect_phase, there is no error — the transaction simply goes nowhere, and coverage silently reads 0%. Siemens Questa's UVM resource reporting can help, but the habit of checking fan-out counts saves hours.

    For teams building a full reusable UVC, our partner Avecas design-services team treats analysis-port fan-out as the contract that keeps block- and SoC-level reuse honest.

    FAQ

    Is uvm_analysis_port blocking?

    No. Its write() is a void function that executes in zero simulation time. If a consumer needs to wait, put a uvm_tlm_analysis_fifo between them.

    What is the difference between an analysis port and an analysis export?

    A port is the producer side that initiates write(); an export (or imp) is the consumer side that provides the write() implementation. You connect a port to one or more exports.

    Why use an analysis FIFO instead of implementing write() directly in the scoreboard?

    Because the scoreboard often must wait for a matching reference transaction before comparing, and write() cannot block. The FIFO buffers incoming items so a separate thread can get() them when ready.

    Can one component receive two analysis streams?

    Yes. Use the `uvm_analysis_imp_decl(_suffix) macro to generate distinct write_suffix() methods, so a single component can host multiple independent analysis imps.

    Want to wire monitors, scoreboards and coverage into a production-grade UVC with guided labs? Explore the Verification track on our CourseTron courses page and build a full APB/AXI environment from scratch.

    Keep going: understand the consumer side in our deep dive on the UVM scoreboard, then see how items originate in UVM sequences and the sequencer. For live cohort-based training, our sister brand ChipXpert VLSI training runs the same flow with mentor support.

    Advertisement