Part 4 · TLM & Analysis · Intermediate

uvm_tlm_analysis_fifo Pattern: Monitor Broadcast to Pull-Based Checker

How to use uvm_tlm_analysis_fifo as a monitor sink with analysis_export, and why scoreboards often consume via get() thread instead of heavy write().

The core pattern

Monitors publish transactions through analysis_port.write(). A scoreboard can receive those directly via write(), but complex checks often need a dedicated run_phase thread. uvm_tlm_analysis_fifo bridges this gap by providing an analysis_export for input and get()/try_get() for pull-based consumption.

systemverilog
class bus_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(bus_scoreboard)

  uvm_tlm_analysis_fifo #(bus_txn) act_fifo;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    act_fifo = new("act_fifo", this);
  endfunction

  task run_phase(uvm_phase phase);
    bus_txn t;
    forever begin
      act_fifo.get(t);
      compare_against_model(t);
    end
  endtask
endclass

// env connect_phase:
// mon.ap.connect(scb.act_fifo.analysis_export);
diagram
[MON]->[TLM]->[SCB] analysis FIFO wiring

 monitor.ap.write(txn)
        │
        ▼
 scb.act_fifo.analysis_export
        │
        ▼
 internal queue
        │
        ▼
 scb.run_phase: act_fifo.get(txn) -> compare()

Why not do everything in write()

A scoreboard write() must be a function and should return quickly. Heavy matching logic, model updates, logging bursts, or database operations can bloat write() and distort monitor throughput.

  • write() should capture or enqueue, not run expensive multi-step comparisons.

  • analysis FIFO allows deterministic pull order in a single checker thread.

  • run_phase consumer can block, wait for companions, and coordinate multiple streams.

  • this pattern centralizes timeout and liveness checks in one place.

diagram
[SCB] direct write() vs analysis FIFO

DIRECT WRITE:
  monitor -> scb.write(txn) -> heavy compare now
  risk: monitor path slows when checker heavy

ANALYSIS FIFO:
  monitor -> fifo.enqueue(txn) -> write returns quickly
  scoreboard thread pulls at controlled pace
  benefit: better decoupling and easier liveness instrumentation
systemverilog
function void write(bus_txn t);
  // Keep this tiny if direct analysis_imp is used:
  // queue.push_back(t.clone());
  //
  // Better: avoid this method entirely by wiring monitor to analysis_fifo.
endfunction

Dual-stream scoreboards

Many scoreboards compare expected and actual streams. Two analysis FIFOs provide clear ownership boundaries and easier ordering logic.

systemverilog
class compare_scoreboard extends uvm_scoreboard;
  `uvm_component_utils(compare_scoreboard)

  uvm_tlm_analysis_fifo #(bus_txn) exp_fifo;
  uvm_tlm_analysis_fifo #(bus_txn) act_fifo;

  function new(string name, uvm_component parent);
    super.new(name, parent);
    exp_fifo = new("exp_fifo", this);
    act_fifo = new("act_fifo", this);
  endfunction

  task run_phase(uvm_phase phase);
    bus_txn exp_t, act_t;
    forever begin
      exp_fifo.get(exp_t);
      act_fifo.get(act_t);
      if (!exp_t.compare(act_t))
        `uvm_error("MISMATCH", $sformatf("exp=%s act=%s",
          exp_t.convert2string(), act_t.convert2string()))
    end
  endtask
endclass

// env connect examples:
// pred.ap.connect(scb.exp_fifo.analysis_export);
// mon.ap.connect(scb.act_fifo.analysis_export);
diagram
[TLM] dual FIFO synchronization sketch

expected stream ---> exp_fifo --->                                   > pair + compare
actual stream -----> act_fifo --->/

Both queues provide explicit buffering and visible occupancy.

Design notes for dual FIFOs

  • Define pairing policy early: strict in-order, keyed by ID, or windowed matching.

  • Track occupancy asymmetry (exp used vs act used) to catch model drift quickly.

  • Add timeout watchdog if one stream can pause legitimately.


Operational checklist

  1. Construct analysis FIFO objects in new() or build_phase.

  2. Connect monitor analysis_port to fifo.analysis_export in connect_phase.

  3. Consume via get() in run_phase with clear objection/lifecycle policy.

  4. Instrument used() periodically in long tests to detect unhealthy lag.

  5. Document whether FIFO depth is bounded or intentionally unbounded.

Key takeaways

  • analysis FIFO is the canonical monitor-to-scoreboard decoupling pattern.

  • It keeps producer callback paths lightweight and checker logic thread-based.

  • Dual FIFOs simplify many expected-vs-actual matching architectures.

Common pitfalls

  • Connecting monitor directly to heavy write() and then debugging phantom monitor stalls.

  • Forgetting to connect analysis_export; consumer blocks forever with no obvious errors.

  • Ignoring queue growth metrics until regressions start timing out.