Skip to content

UVM

TLM Issues

Applying the debugging methodology to transaction-level connections — why transactions sent through a TLM port never arrive or a connect-phase fatal appears: a required port left unconnected (loud), an analysis port left unconnected (silent — it permits zero subscribers), a connection to the wrong export, a missing implementation of the required method, or multiple analysis imps without the decl macro; the asymmetry where a scoreboard silently receives nothing and runs no checks while the test passes green; how to observe the topology and connections and fix each so transactions flow end to end.

UVM Debugging · Module 27 · Page 27.4

The Engineering Problem

You wired your testbench's transaction flow — the monitor broadcasts observed transactions, the scoreboard receives and checks them, the coverage collector samples them — and transactions vanish. The scoreboard runs no comparisons; the coverage stays empty; the test passesgreen, but having checked nothing. Or, differently, the testbench won't even start: a connect-phase fatal announces an unconnected port. These are the TLM's characteristic failures (from 27.1's map: transactions never arrive, or a connection error → TLM), and they come in two flavors with a crucial asymmetry. A required port — a blocking put/get — that's left unconnected is caught loudly: UVM checks at end-of-elaboration that required ports have their minimum connections, and fatals if not, naming the port. But an analysis port — the broadcast kind the monitor uses — permits zero subscribers: an analysis port with nothing connected is not an error, so a write() to it succeeds silently into the void, and the consumer that should have received the transaction gets nothingwith no warning. This asymmetry is the trap: the loud failure tells you; the silent one lets a scoreboard receive nothing, run no checks, and pass greenfalse confidence, the worst kind of failure. The problem this chapter solves is TLM issues: applying the methodology to TLM connectionsreading the symptom (transactions vanish, or a connect-phase fatal), observing with the topology and connections, and confirming and fixing each root cause so transactions flow end to end.

TLM issues are transactions failing to flow through a TLM connectionvanishing silently or erroring at connect-phase — because a port isn't connected, is connected wrong, lacks an implementation, or (for analysis ports) has no subscriber. The root causes: a required port left unconnected (a blocking put/get with no target — a connect-phase fatal, loud); an analysis port left unconnected (the broadcast kind — permits zero subscribers, so the write() goes nowhere silently and the consumer receives nothing); a connection to the wrong export (transactions flow to the wrong place); a missing or mis-signatured implementation of the required method (a uvm_*_imp whose write()/put()/get() isn't implemented or matches the wrong signature); or multiple analysis imps without the uvm_analysis_imp_decl macro (two analysis_imps in one component both need write(), so they collide without the decl and suffixed write_* methods). The crucial asymmetry: required ports fail loudly (connect-phase fatal) but analysis ports fail silently (zero subscribers is legal), so the most insidious TLM failure is an unconnected analysis port where the producer writes into the void and the scoreboard or coverage silently receives nothing and runs no checks while the test passes green. The methodology applied: the symptom is transactions vanish (silent) or a connect-phase fatal (loud); localize to TLM; observe with uvm_top.print_topology() (the connections — an analysis port with zero targets is the silent case) and the connect-phase error (which names the port); confirm the cause; and fix (connect the port, connect to the right export, implement the method, use the decl macro). This chapter is TLM issues: the symptom, the asymmetry, the observation, and the fixes.

Why do transactions sent through a TLM port never arrive — or a connect-phase fatal appear — and why does an unconnected analysis port fail silently (the scoreboard receives nothing and passes green) while an unconnected required port fails loudly, and how do you observe the topology to find which and fix it?

Motivation — why TLM failures split into loud and silent

TLM's two port familiesrequired (point-to-point put/get) and analysis (broadcast write) — fail differently, and the silent family is why TLM bugs are dangerous. The reasons:

  • Required ports must be connected — so UVM checks. A blocking put/get is a point-to-point contract: the producer needs a consumer to complete the transfer. UVM knows this and checks the minimum connection count at end-of-elaboration, fataling if a required port is unconnected. So this failure is loud and named.
  • Analysis ports tolerate zero subscribers — by design. An analysis port is a broadcast: the producer write()s and doesn't care who (if anyone) listens. Zero subscribers is a legal, intended state (a monitor whose transactions nobody currently consumes). So UVM can't flag an unconnected analysis port as an error — it's indistinguishable from a deliberately unsubscribed one.
  • The silent failure produces false confidence. Because an unconnected analysis port is not an error, a scoreboard whose input was never connected receives nothing, compares nothing, and reports no mismatch — so the test passes. A green test that checked nothing is worse than a red one: it ships the illusion of verification.
  • The implementation is a separate failure. A uvm_*_imp requires the component to implement the method (write/put/get); a missing or mis-signatured one won't work. And two analysis imps in one component collide on write() without the decl macro — a common, confusing compile/wiring issue.
  • Connections are invisible without the topology. Whether a port is connected, to what, and how many subscribers it has is invisible from the port declaration — you must print the topology to see the actual connections.

The motivation, in one line: TLM splits into required ports (UVM checks them, so unconnected fatals loudly) and analysis ports (zero subscribers is legal, so unconnected vanishes silently) — and the silent failure is dangerous because an unconnected analysis port lets a scoreboard receive nothing and pass green (false confidence) — so you must observe the topology to see the connections and catch the silent case.

Mental Model

Hold TLM as a broadcast versus a required delivery — a radio station that may transmit to nobody, versus a courier that must hand the package to a named recipient:

There are two ways to send something, and they fail in opposite ways. The first is a broadcast — a radio station transmitting a show. The station puts the signal out on its frequency, and whoever has tuned in receives it. Crucially, the station does not need anyone to be listening; broadcasting to an empty audience is completely normal and legal — the show airs into the air whether one person or nobody is tuned in. So if you meant to listen but never tuned your radio to the right frequency, you simply hear nothing, and nothing is wrong from the station's side — it transmitted exactly as intended, into a void you happened to be standing outside of. The failure is silent: no error, no bounce, just an empty room where you expected a broadcast. The second way is a courier delivery to a named recipient — a package that must be handed to a specific person who signs for it. This is a contract: the package needs a recipient. If you try to send it and no recipient is registered, the courier won't even accept it — it's rejected at the depot, loudly, before it goes anywhere, because a delivery with no recipient is a setup error. So the two modes fail oppositely: the broadcast tolerates zero listeners and fails silently if you forgot to tune in, while the required delivery demands a recipient and fails loudly at setup if there isn't one. And the dangerous one is the broadcast, because silence looks exactly like a quiet, working system — you don't find out you missed the show until you go looking for what you should have heard. There are two ways to send something, and they fail in opposite ways. The first is a broadcast — a radio station transmitting a show. The station puts the signal out, and whoever tuned in receives it. Crucially, the station does not need anyone listening; broadcasting to an empty audience is normal and legal — the show airs whether one person or nobody is tuned in. So if you meant to listen but never tuned in, you hear nothing, and nothing is wrong from the station's side — it transmitted exactly as intended, into a void you stood outside of. The failure is silent: no error, no bounce, just an empty room where you expected a broadcast. The second is a courier delivery to a named recipient — a package that must be handed to a specific person. This is a contract: the package needs a recipient. If no recipient is registered, the courier won't even accept itrejected at the depot, loudly, because a delivery with no recipient is a setup error. So the two fail oppositely: the broadcast tolerates zero listeners and fails silently; the required delivery demands a recipient and fails loudly at setup. And the dangerous one is the broadcast, because silence looks exactly like a quiet, working system — you don't find out you missed the show until you go looking for what you should have heard.

So TLM is a broadcast versus a required delivery: an analysis port is the radio broadcast (the monitor write()s every transaction; subscribers who connected receive it; zero subscribers is legal, so an unconnected one transmits into the void silently — the scoreboard hears nothing, no error); a blocking put/get is the courier delivery (a point-to-point contract; no connected recipientconnect-phase fatal, loud, at setup). The asymmetry is the whole point: the required port tells you (UVM checks the recipient), the analysis port doesn't (zero listeners is intended), so the silent failure — an unconnected analysis port — lets a scoreboard pass green having received nothing, which looks like a quiet, working system until you go looking for the checks that never ran. Diagnose TLM by the port family: a connect-phase fatal names an unconnected required port (loud); a scoreboard receiving nothing is an unconnected analysis port (silent) — print the topology to see the connections, because a broadcast into the void leaves no error, only an empty room. The broadcast tolerates silence; go count the listeners.

Visual Explanation — the TLM failure causes and the asymmetry

The defining picture is the causes split by the loud/silent asymmetry: required-port unconnected (loud) versus analysis-port unconnected (silent), plus wrong target and missing implementation.

TLM failure causes split by loud/silentRequired port unconnected → LOUDa blocking put/get with no target — UVM fatals at connect-phase, naming the porta blocking put/get with no target — UVM fatals at connect-phase, naming the portAnalysis port unconnected → SILENTzero subscribers is legal — the write goes into the void, the consumer receives nothing, no errorzero subscribers is legal — the write goes into the void, the consumer receives nothing, no errorConnected to the wrong exporttransactions flow to the wrong place — the intended consumer still receives nothingtransactions flow to the wrong place — the intended consumer still receives nothingMissing impl / multiple imps without declthe imp's write/put/get isn't implemented or matches the wrong signature; two analysis imps collide without uvm_analysis_imp_declthe imp's write/put/get isn't implemented or matches the wrong signature; two analysis imps collide without uvm_analysis_imp_decl
Figure 1 — the TLM failure causes, split by the loud/silent asymmetry. A required port (blocking put/get) left unconnected is a connect-phase fatal that names the port — loud. An analysis port left unconnected permits zero subscribers, so the write goes nowhere silently and the consumer receives nothing — silent, the dangerous one. A connection to the wrong export sends transactions to the wrong place. A missing or mis-signatured implementation of the required method, or two analysis imps without the decl macro, breaks the connection at compile or wiring time. The loud failures announce themselves; the silent analysis-port failure is found only by observing the topology, because zero subscribers is legal and produces no error.

The figure shows the TLM failure causes split by loud/silent. Required port unconnected → LOUD (the brand-colored): a blocking put/get with no targetUVM fatals at connect-phase, naming the port. Analysis port unconnected → SILENT (the warning-colored — the dangerous one): zero subscribers is legal — the write goes into the void, the consumer receives nothing, no error. Connected to the wrong export (default-colored): transactions flow to the wrong place — the intended consumer still receives nothing. Missing impl / multiple imps without decl (success-colored): the imp's write/put/get isn't implemented or matches the wrong signature; two analysis imps collide without uvm_analysis_imp_decl. The crucial reading is the loud/silent split: the brand-colored required failure announces itself (a connect-phase fatal you can't miss, with the port named), while the warning-colored analysis failure is the one to fear — it produces no error at all, because zero subscribers is a legal, intended state, so it's found only by observing the topology. This is why the analysis-port case is highlighted: with the loud failures, UVM does the localizing for you (it tells you the port); with the silent analysis case, you must go lookprint the topology and count the subscribers on the producer's analysis port. The other two — wrong export and missing implementation — are also causes (transactions flow wrong, or the method isn't there), and the missing-impl one often errors at compile (so it's loud in its own way), while the multiple-imps-without-decl is a classic confusing compile failure. The diagram is the TLM failure map: required-unconnected (loud) vs analysis-unconnected (silent) vs wrong-target vs missing-impl — and the silent analysis case is the one you must hunt for. TLM failures split into loud (required port unconnected, fatals at connect-phase) and silent (analysis port unconnected, the write vanishes) — the silent analysis-port case is the dangerous one, found only by observing the topology.

RTL / Simulation Perspective — the connections, the asymmetry, and the fixes

In code, the loud and silent failures are concrete, and the fix is the connection. The example shows both, plus the implementation causes.

TLM connections: the loud and silent failures, and their fixes
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// === SILENT: analysis port unconnected — the monitor broadcasts into the void ===
class my_monitor extends uvm_monitor;
  uvm_analysis_port#(my_txn) ap;          // broadcast port
  task run_phase(uvm_phase phase);
    forever begin /* observe */ ap.write(txn); end   // write SUCCEEDS even with zero subscribers
  endtask
endclass
class my_scoreboard extends uvm_scoreboard;
  uvm_analysis_imp#(my_txn, my_scoreboard) analysis_export;
  function void write(my_txn t); /* compare */ endfunction   // never called if not connected
endclass
// ✗ in the env's connect_phase: the connection is MISSING
function void connect_phase(uvm_phase phase);
  // (forgot)  mon.ap.connect(sb.analysis_export);
endfunction
// → mon.ap.write() goes nowhere, sb.write() never runs, sb compares NOTHING, test passes GREEN (DebugLab)
// ✓ FIX: connect them
function void connect_phase(uvm_phase phase);
  mon.ap.connect(sb.analysis_export);     // now every write reaches the scoreboard
endfunction
 
// === LOUD: required (blocking) port unconnected — connect-phase fatal ===
class producer extends uvm_component;
  uvm_blocking_put_port#(my_txn) put_port; // point-to-point, REQUIRES a connection
endclass
// if put_port is never connected → UVM end_of_elaboration FATAL: "port not connected to sufficient ..."
// ✓ FIX: connect it to the consumer's export/imp in connect_phase
 
// === IMPLEMENTATION causes ===
// missing write(): uvm_analysis_imp#(T, C) requires C to implement function void write(T t);  → else compile error
// MULTIPLE analysis imps in one component need the decl macro + suffixed methods:
`uvm_analysis_imp_decl(_before)
`uvm_analysis_imp_decl(_after)
class sb2 extends uvm_scoreboard;
  uvm_analysis_imp_before#(my_txn, sb2) before_export;
  uvm_analysis_imp_after #(my_txn, sb2) after_export;
  function void write_before(my_txn t); /* ... */ endfunction   // suffixed methods, no collision
  function void write_after (my_txn t); /* ... */ endfunction
endclass
 
// === OBSERVE: the topology shows the connections (and unconnected analysis ports) ===
function void end_of_elaboration_phase(uvm_phase phase);
  uvm_top.print_topology();   // shows each port and what it connects to — ap with 0 subscribers = silent case
endfunction

The code shows the loud and silent failures and the fixes. Silent: a my_monitor with an analysis_port ap that write()s, and a my_scoreboard with an analysis_imp and a write() method — but the env's connect_phase forgot mon.ap.connect(sb.analysis_export), so ap.write() goes nowhere (zero subscribers is legal), sb.write() never runs, the scoreboard compares nothing, and the test passes green (the DebugLab); the fix is the connection. Loud: a producer with a blocking_put_port never connectedconnect-phase fatal (UVM names the unconnected port); the fix is to connect it. Implementation causes: a missing write() on an analysis_imp is a compile error; and two analysis imps in one component need uvm_analysis_imp_decl(_before)/_decl(_after) and suffixed write_before/write_after methods to avoid collision. Observe: uvm_top.print_topology() shows each port and what it connects to — an ap with zero subscribers is the silent case, visible in the topology. The shape to carry: the connection is made in connect_phase (producer.port.connect(consumer.export)), required ports fatal loudly if unconnected, analysis ports vanish silently if unconnected, and the topology print is how you see the actual connections — especially the silent analysis case. Connect ports in connect_phase, implement the imp's method, use the decl macro for multiple analysis imps, and print the topology to confirm the connections — especially that analysis ports have the subscribers you expect.

Verification Perspective — silence is false confidence

The defining danger is that the silent analysis failure produces false confidence — a green test that checked nothing. Seeing how a scoreboard passes while receiving nothing clarifies why you must verify the connection, not trust the green.

Silent analysis failure produces false confidencewrite() succeeds (0 subscribers legal)write() succeeds(0 subscribers…no connection → into the voidno connection→ into the…write() never called0 comparisons → no mismatch → green0 comparisons →no mismatch →…Monitorbroadcasts everytransactionAnalysis portwrite succeeds regardlessThe voidunconnected — no subscriberScoreboardexport never connected0 received, 0 comparedchecks nothingTest passes greenfalse confidence12
Figure 2 — the silent analysis failure produces false confidence. The monitor broadcasts every observed transaction on its analysis port, and from the producer's side everything is normal — the write succeeds, because an analysis port permits zero subscribers. But the scoreboard's analysis export was never connected, so its write method is never called: it receives zero transactions, runs zero comparisons, and detects zero mismatches. The test ends green — no mismatch reported — but the scoreboard verified nothing. A passing test that checked nothing is false confidence, worse than a failure, because it ships the illusion of verification. The tell is the comparison count: a scoreboard that reports zero comparisons received nothing.

The figure shows the silent analysis failure producing false confidence. The monitor broadcasts every observed transaction on its analysis port, and from the producer's side everything is normal — the write succeeds, because an analysis port permits zero subscribers. But the scoreboard's analysis export was never connected, so its write method is never called: it receives zero transactions, runs zero comparisons, and detects zero mismatches. The test ends greenno mismatch reported — but the scoreboard verified nothing. The verification insight is that silence and success look identical: a scoreboard that finds no mismatches because the DUT is correct and a scoreboard that finds no mismatches because it received nothing both report "no mismatch" and both make the test pass green. From the test result alone, you cannot distinguish them. This is why the silent analysis failure is worse than a loud one: a loud failure stops you (you can't ignore a connect-phase fatal), but a silent one passes and ships the illusion of verification — the scoreboard looks present, the test is green, and nobody knows it checked nothing. The brand monitor → default analysis-port → warning void path (write into the void) and the brand scoreboard → warning zero → warning green path (receives nothing → green) converge on false confidence. The crucial defense is the tell: a scoreboard that reports zero comparisons received nothing — so scoreboards should report their comparison count, and a count of zero (when transactions should have flowed) is a red flag that the connection is broken, regardless of the green result. The crucial point is that you cannot trust a green result from a component you haven't confirmed received dataverify the connection (topology print, comparison count), don't trust the absence of mismatches. The diagram is the false-confidence mechanism: unconnected analysis port → scoreboard receives nothing → 0 comparisons → green → false confidence. An unconnected analysis port lets a scoreboard pass green having checked nothing — silence and success look identical, so verify the connection and the comparison count, never trust the absence of mismatches alone.

Runtime / Execution Flow — diagnosing a TLM failure

At run time, diagnosing a TLM failure follows the methodology, branching on loud versus silent. The flow shows the TLM-specific diagnostic.

Diagnosing a TLM failure: loud vs silent branchessymptom: connect-phase fatal (loud) OR transactions vanish / 0 comparisons (silent) → loud: the fatal names the port → silent: print topology, check the analysis port's subscriber count → confirm received count → fix the connectionsymptom: connect-phase fatal (loud) OR transactions vanish / 0 comparisons (silent) → loud: the fatal names the port → silent: print topology, check the analysis port's subscriber count → confirm received count → fix the connection1Symptom: fatal (loud) or vanish (silent)a connect-phase fatal naming a port, or a scoreboard/coveragereceiving nothing while the test passes.2Loud: the fatal names the portthe connect-phase error identifies the unconnected required portdirectly — connect it.3Silent: print the topologysee the connections; the producer's analysis port with zerosubscribers is the silent failure.4Confirm received count → fixa zero received count confirms it; connect in connect_phase, to theright export, with the method implemented.
Figure 3 — diagnosing a TLM failure step by step. The symptom is either a connect-phase fatal naming a port, or transactions vanishing — a scoreboard or coverage receiving nothing while the test passes. For the loud case, the fatal names the unconnected required port directly; connect it. For the silent case, print the topology to see the connections, and look at the producer's analysis port: zero subscribers is the silent failure. Confirm by checking the consumer's received count — zero means it received nothing. Then fix: make the connection in connect_phase, connect to the right export, or implement the required method. The topology print is what makes the silent case visible, since UVM raises no error for an unconnected analysis port.

The flow shows the TLM-specific diagnostic, branching on loud versus silent. Symptom (step 1): a connect-phase fatal naming a port, or a scoreboard/coverage receiving nothing while the test passes. Loud (step 2): the connect-phase error identifies the unconnected required port directlyconnect it. Silent (step 3): print the topology to see the connections; the producer's analysis port with zero subscribers is the silent failure. Confirm → fix (step 4): a zero received count confirms it; connect in connect_phase, to the right export, with the method implemented. The runtime insight is that the two branches need different effort: the loud case is easy — UVM did the localizing, naming the port in the fatal, so you connect it. The silent case is where the work is — there's no error, so you must suspect it (from the symptom: a scoreboard receiving nothing, a coverage model staying empty, a test that passes suspiciously), then print the topology to see whether the producer's analysis port has the subscribers you expect (zero = the failure). The confirmation is the received count: instrument the consumer to report how many transactions it received, and a count of zero (when it should have received many) confirms the broken connection. This is why the methodology's "observe" step is essential for TLM: the silent failure is invisible without the topology print, and trusting the green result would miss it entirely. The brand symptom → success (loud) / warning (silent) → warning fix flow shows the branch and the greater care the silent path demands. The crucial point is that for the silent case, you must go lookingUVM won't tell you — so make checking analysis-port connections (and consumer received counts) a routine part of bring-up, not a reaction to a symptom (because the symptom is no symptom). The flow is the TLM diagnostic: loud → the fatal names the port; silent → print the topology and check subscriber/received counts. Diagnose a TLM failure by branching on loud versus silent — the connect-phase fatal names an unconnected required port, but the silent analysis case needs the topology print and a received count, because UVM raises no error for an unconnected analysis port.

Waveform Perspective — the producer broadcasts, the consumer never receives

The silent failure is observable as producer activity with no consumer reception: the monitor writes transactions, but the scoreboard's write is never called. The waveform shows it.

The producer broadcasts transactions but the consumer never receives — an unconnected analysis port

12 cycles
The producer broadcasts transactions but the consumer never receives — an unconnected analysis portmonitor observes a transaction and writes its analysis port (mon_write) — producer activemonitor observes a tra…another transaction broadcast (mon_write) — the producer side works normallyanother transaction br…but sb_write never fires — the scoreboard's write is never calledbut sb_write never fir…producer active, consumer inert = unconnected analysis port (writes into the void)producer active, consu…clkmon_writesb_writet0t1t2t3t4t5t6t7t8t9t10t11
Figure 4 — the silent analysis failure observed. The interface is active and the monitor observes transactions, so the monitor writes to its analysis port (mon_write pulses on each observed transaction) — the producer side is working normally. But the scoreboard's write is never called (sb_write stays flat): the analysis port has no subscriber, so the writes go into the void and the scoreboard receives nothing. The producer is active while the consumer is inert — that gap is the signature of an unconnected analysis port, distinct from a sequencer failure (where nothing would be produced at all) and from a correct flow (where every mon_write would be followed by an sb_write). Reading it points to the TLM connection, confirmed by the topology print.

The waveform shows the silent analysis failure. The interface is active and the monitor observes transactions, so the monitor writes to its analysis port (mon_write pulses on each observed transaction) — the producer side is working normally. But the scoreboard's write is never called (sb_write stays flat): the analysis port has no subscriber, so the writes go into the void and the scoreboard receives nothing. The crucial reading is the gap between producer and consumer: mon_write pulses (the producer broadcasts) but sb_write never fires (the consumer never receives). That gapproducer active, consumer inert — is the signature of an unconnected analysis port. It's distinct from a sequencer failure (27.1, where nothing would be produced at all — mon_write itself would be flat, because there'd be no transactions to observe) and from a correct flow (where every mon_write would be followed by an sb_write, the transaction reaching the consumer). So the three cases are distinguishable on the waveform: both flatno stimulus (sequencer); both active, pairedcorrect; producer active, consumer flatbroken TLM connection. Reading the symptom this way — is the producer writing? is the consumer receiving? do they pair up?localizes precisely: here, the producer writes but the consumer doesn't receive, so the transaction is lost between them — the TLM connection. The picture to carry is that the silent failure has a visible signature if you look at both ends: the producer's writes happen, the consumer's receptions don't, and the gap is the unconnected port. Once you've read this, you print the topology to confirm the analysis port has no subscriber. The producer broadcasting while the consumer stays inert is the waveform signature of the *silent TLM failure. An unconnected analysis port shows as producer activity (mon_write pulses) with no consumer reception (sb_write flat) — the gap between a broadcasting producer and an inert consumer is the signature, distinct from no-stimulus (both flat) and correct flow (paired).

DebugLab — the scoreboard that checked nothing and passed green

An unconnected analysis port that left the scoreboard receiving nothing while the test passed green

Symptom

A team added a scoreboard to an existing environment to check a DUT's outputs against expected. They wrote the scoreboard — an analysis_imp, a write() method that queued and compared transactions, mismatch reporting — and added it to the env. They ran the regression: all green. The scoreboard reported no mismatches; the test passed. Confident, they moved on. Some time later, during an unrelated review, someone noticed the scoreboard's end-of-test summary said "0 transactions compared." That was strange — the test had driven hundreds of transactions. Looking closer, the scoreboard's write() method had never been called, not once, across the entire regression. The scoreboard had received nothing, compared nothing, and — because it found no mismatches in the nothing it comparedreported success. The "all green" regression had been running with a scoreboard that verified nothing, for however long it had been there. The DUT might have had bugs the scoreboard was supposed to catch — and nobody would have known, because the test passed.

Root cause

The monitor's analysis port was never connected to the scoreboard's analysis export in connect_phase, and because an unconnected analysis port permits zero subscribers (it's not an error), the monitor's writes went silently into the void, the scoreboard's write() was never called, and the test passed green having compared nothing:

why an unconnected analysis port makes a scoreboard pass green checking nothing
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
✗ THE CONNECTION WAS NEVER MADE — and an unconnected analysis port is NOT an error:
  class my_env extends uvm_env;
    my_monitor    mon;
    my_scoreboard sb;
    function void connect_phase(uvm_phase phase);
      // (forgot)  mon.ap.connect(sb.analysis_export);   ← the connection is MISSING
    endfunction
  endclass
  // mon.ap.write(txn) SUCCEEDS (analysis ports allow 0 subscribers) → goes into the void
  // sb.write() is NEVER called → sb compares 0 transactions → 0 mismatches → test passes GREEN
  // NO connect-phase fatal (unlike a required port), because 0 subscribers is legal → SILENT
 
  // the topology print shows it:
  //   uvm_test_top.env.mon.ap   (no connections)        ← the smoking gun
  //   uvm_test_top.env.sb.analysis_export  (no source)
  // and the scoreboard's summary: "0 transactions compared"  ← the tell
 
✓ FIX — connect them in connect_phase:
  function void connect_phase(uvm_phase phase);
    mon.ap.connect(sb.analysis_export);   // now every write reaches the scoreboard
  endfunction
  // → sb.write() runs per transaction, comparisons happen, real mismatches are caught

This is the unconnected-analysis-port bug — the most insidious TLM failure, because it produces false confidence. The team built the scoreboard correctly — the write(), the comparison, the mismatch reporting were all fine — but the env's connect_phase never connected mon.ap to sb.analysis_export. And here is the trap: an unconnected analysis port is not an error, because analysis ports permit zero subscribers by design (a monitor whose output nobody consumes is a legal configuration). So no connect-phase fatal fired (unlike a required port, which would have fataled and told them). The monitor's write() succeeded (into the void), the scoreboard's write() was never called, the scoreboard compared zero transactions, found zero mismatches, and the test passed green. The deepest danger is that a scoreboard that checks nothing and a scoreboard that checks correctly both report "no mismatch" and both pass — so the green result was indistinguishable from a working one, and the team shipped the illusion of verification. The tellvisible in plain sight — was the scoreboard's "0 transactions compared" summary, and the cause was visible in the topology print: env.mon.ap with no connections. The fix is the one missing line: mon.ap.connect(sb.analysis_export) in connect_phase, after which sb.write() runs per transaction and real mismatches are caught. The general lesson, and the chapter's thesis: an unconnected analysis port fails silently — the producer writes into the void and the consumer receives nothing — because analysis ports permit zero subscribers, so unlike a required port (which fatals loudly at connect-phase) there is no error; a scoreboard whose input is unconnected receives nothing, compares nothing, and passes green, which is false confidence worse than a failure; verify TLM connections by printing the topology and checking consumer received counts (a scoreboard reporting zero comparisons received nothing), because silence and success look identical, and you cannot trust a green result from a component you have not confirmed received data. An unconnected analysis port makes a scoreboard pass green having checked nothing — count the comparisons and print the topology, because zero subscribers is legal and raises no error.

Diagnosis

The tell is a scoreboard or coverage component that passes while reporting zero activity. Diagnose an unconnected analysis port:

  1. Check the consumer's received count. A scoreboard reporting zero comparisons, or a coverage model with zero samples, received nothing — its input connection is broken.
  2. Print the topology. Look at the producer's analysis port; zero subscribers, or a missing source on the consumer's export, is the unconnected connection.
  3. Don't trust a green result from an unconfirmed consumer. Absence of mismatches can mean correct, or can mean nothing was checked; confirm the consumer received data.
  4. Remember analysis ports raise no error. Unlike a required port, an unconnected analysis port produces no connect-phase fatal, so you must look for it actively.
Prevention

Confirm transactions flow:

  1. Make and verify the connection in connect_phase. Connect every analysis port to its subscribers, and print the topology to confirm.
  2. Instrument consumers with received counts. Have scoreboards and coverage report how many transactions they received, so zero is an immediate red flag.
  3. Treat a passing scoreboard with zero comparisons as failed. A scoreboard that compared nothing did not verify; gate sign-off on non-zero, expected comparison counts.
  4. Make analysis-port connection checks routine in bring-up. Because the failure is silent, look for it proactively rather than waiting for a symptom that never comes.

The one-sentence lesson: an unconnected analysis port fails silently because analysis ports permit zero subscribers, so the producer writes into the void and a consuming scoreboard receives nothing, compares nothing, and passes green — false confidence worse than a failure — so verify TLM connections by printing the topology and checking consumer received counts, because silence and success look identical and a scoreboard reporting zero comparisons verified nothing.

Common Mistakes

  • Leaving an analysis port unconnected. It raises no error (zero subscribers is legal), so the producer writes into the void and the consumer receives nothing; connect it and confirm with the topology.
  • Trusting a green result from an unconfirmed consumer. A scoreboard that received nothing passes green; confirm it received data and ran comparisons, don't trust the absence of mismatches.
  • Not reporting comparison counts. Without a received or comparison count, a scoreboard checking nothing is invisible; instrument consumers so zero activity is a red flag.
  • Connecting to the wrong export. Transactions flow to the wrong place and the intended consumer still receives nothing; verify the connection target in the topology.
  • Forgetting the decl macro for multiple analysis imps. Two analysis imps in one component collide on write() without uvm_analysis_imp_decl and suffixed methods.
  • Connecting in the wrong phase. Connections belong in connect_phase, after build has created the components; connecting earlier finds nothing to connect.

Senior Design Review Notes

Interview Insights

Because the two port types have opposite connection requirements: a required port like a blocking put or get is a point-to-point contract that needs a connected peer, so UVM checks for it and fatals loudly if it's missing, whereas an analysis port is a broadcast that permits zero subscribers as a legal, intended state, so UVM cannot flag an unconnected one as an error and it fails silently. Take the required port first. A blocking put or get is a point-to-point transfer where the producer needs a consumer to complete the operation — a put blocks until the peer takes the item. UVM knows this kind of port requires a connection to function, so it enforces a minimum connection count, checked at end of elaboration, and if a required port is unconnected, it issues a connect-phase fatal that names the port. So the failure announces itself loudly and tells you exactly which port. Now the analysis port. An analysis port is a broadcast — the producer calls write and the transaction is delivered to every subscriber, of which there can be any number including zero. Zero subscribers is not an error; it's a perfectly valid configuration, like a monitor whose output nobody currently consumes. Because zero subscribers is legal and intended, UVM cannot distinguish a deliberately-unsubscribed analysis port from one you forgot to connect — both have zero subscribers — so it raises no error for either. The consequence is that an analysis port you meant to connect but didn't behaves exactly like one you meant to leave unconnected: the write succeeds, the transaction goes into the void, and nothing complains. This asymmetry is why the analysis-port failure is the dangerous one. The loud required-port failure stops you and points at the port; the silent analysis-port failure lets the testbench run, lets a scoreboard whose input was never connected receive nothing and pass green, and ships the illusion of verification. So with required ports UVM does the localizing for you, while with analysis ports you have to go looking — print the topology and check subscriber counts — because the absence of an error is not evidence of a connection.

A scoreboard passes green while verifying nothing when its input analysis port was never connected, so it receives zero transactions, runs zero comparisons, finds zero mismatches, and reports success — because finding no mismatches in nothing looks identical to finding no mismatches in correct behavior. Walk through the mechanism. The scoreboard has an analysis export and a write method that queues and compares transactions and reports mismatches. For it to work, the monitor's analysis port must be connected to that export in connect_phase. If that connection is missing, the monitor's write goes into the void — legal, because analysis ports allow zero subscribers — and the scoreboard's write method is never called. So the scoreboard's compare logic never runs. At end of test, it has compared zero transactions and found zero mismatches, and since it found no mismatches, it reports no failure. The test passes green. The crucial and dangerous point is that a scoreboard that checks nothing and a scoreboard that checks correctly both report no mismatch and both pass. From the test result alone you cannot tell them apart — silence and success look identical. So a green result is not evidence that the scoreboard verified anything; it's only evidence that the scoreboard reported no mismatch, which could mean correct behavior or could mean it received nothing. This is false confidence, and it's worse than a failing test, because a failure stops you and gets fixed, while this passes and ships the illusion of verification — the DUT might have had exactly the bugs the scoreboard was meant to catch, and nobody would know. The defenses are concrete. First, instrument scoreboards to report how many transactions they compared, and treat a count of zero — when transactions should have flowed — as a failure regardless of the green result. Second, print the topology and confirm the analysis port is connected to the scoreboard's export. Third, as a principle, never trust a green result from a component you haven't confirmed received data. The tell in the classic case is a scoreboard summary that says zero transactions compared while the test drove hundreds — that mismatch between expected and actual activity is the red flag that the connection is broken.

You diagnose vanishing transactions by localizing to TLM, then printing the topology to inspect the connections and checking the consumer's received count, branching on whether the failure is loud or silent. Start from the symptom. If there's a connect-phase fatal naming a port, that's the loud case — a required port left unconnected — and UVM has already localized it for you by naming the port; you connect it and you're done. If instead transactions are silently vanishing — a scoreboard receiving nothing, a coverage model staying empty, a test passing suspiciously — that's the silent case, an analysis port issue, and you have to investigate, because there's no error. For the silent case, the key observation is the topology print, uvm_top.print_topology, which shows each port and what it connects to. Look at the producer's analysis port: if it shows zero subscribers, or the consumer's analysis export shows no source, that's the unconnected connection — the smoking gun. Also check whether the port is connected to the wrong export, which sends transactions somewhere other than the intended consumer. Confirm the diagnosis with the consumer's received count: instrument the scoreboard or coverage to report how many transactions it received, and a count of zero when many should have arrived confirms the broken connection. The reason the topology print is essential here is that the silent failure is invisible otherwise — UVM raises no error for an unconnected analysis port, and the test result is green, so without looking at the actual connections you'd never see it. This is the general debugging methodology specialized to TLM: the symptom localizes to TLM, the topology print is the introspection that makes the invisible connections visible, and the received count confirms the cause before you fix it. Then the fix is to make the connection in connect_phase — producer.ap.connect(consumer.export) — or correct a wrong target, or implement a missing method. And because the silent failure has no symptom of its own, the discipline is to check analysis-port connections and consumer received counts proactively during bring-up, rather than waiting for a symptom that, for the silent case, never comes.

uvm_analysis_imp_decl is for when a single component needs more than one analysis imp — more than one analysis input — and you need it because each analysis imp requires the component to implement a write method, and two plain analysis imps would both require a method named write, which collide, so the decl macro creates suffixed variants with distinct write method names. Here's the situation. A uvm_analysis_imp parameterized on a transaction type and the component type requires that component to implement function void write(T t). That's fine for one analysis input. But a scoreboard often needs two or more analysis inputs — for example, one stream of transactions from the input-side monitor and another from the output-side monitor, to compare them. If you declare two plain uvm_analysis_imp members, both require a write method on the component, and you can't have two methods both named write with the same signature — they collide. The solution is the uvm_analysis_imp_decl macro. You invoke it with a suffix, like uvm_analysis_imp_decl with _before and again with _after. Each invocation generates a specialized imp type — uvm_analysis_imp_before and uvm_analysis_imp_after — that requires a correspondingly suffixed write method: write_before and write_after. So you declare your two ports as the suffixed imp types, implement write_before and write_after as distinct methods, and they no longer collide — each port routes to its own method. So you need the decl macro precisely when a component has multiple analysis imps. With a single analysis imp, a plain uvm_analysis_imp and a single write method are enough. With two or more, you reach for uvm_analysis_imp_decl per input, with a distinct suffix each, and implement the matching suffixed write methods. Forgetting this is a common, confusing failure when building scoreboards with multiple inputs — the symptoms are compile errors about write, or, if mis-wired, transactions arriving at the wrong handler. The fix is to use the decl macro and the suffixed methods so each analysis input has its own clearly-named write.

TLM connections are made in the connect_phase, by calling connect from a port to an export or imp — producer.port.connect(consumer.export) — and phasing goes wrong if you try to connect before the components exist or in the wrong phase relative to build. The lifecycle is: in build_phase, components are constructed top-down, so after build the full hierarchy of components and their ports exists but nothing is wired. Then connect_phase runs, bottom-up, and this is where you wire ports to exports — the producer's port to the consumer's export or imp. The direction matters: you call connect on the port (the initiating side) and pass the export or imp (the receiving side). For an analysis broadcast, the monitor's analysis_port connects to the subscriber's analysis_export or analysis_imp. For a blocking put, the producer's put_port connects to the consumer's put_export or put_imp. What goes wrong with phasing: First, connecting in build_phase instead of connect_phase. In build, the components you're trying to connect may not be constructed yet — if you connect before the child is created, there's nothing to connect to, and you get a null handle or a missing connection. Connections belong in connect_phase precisely because build has finished creating everything by then. Second, connecting in the wrong direction or to the wrong object — calling connect on the export instead of the port, or passing the wrong target — which fails to wire correctly. Third, forgetting to call super.connect_phase if your base class does connection work, though that's less common. The way to verify connections are correct is the topology print at end_of_elaboration, after connect_phase has run, which shows each port and what it's connected to. For required ports, UVM also checks connection counts at end of elaboration and fatals on unconnected ones, so a phasing mistake that leaves a required port unconnected gets caught loudly. For analysis ports, as discussed, there's no such check, so you confirm those connections yourself via the topology. So the rule is: make connections in connect_phase, call connect on the port passing the export or imp, and verify with the topology print — and remember that build creates the components while connect wires them, so connection code that runs too early finds nothing to wire.

Exercises

  1. Loud or silent. For each — an unconnected blocking put port, an unconnected monitor analysis port, a scoreboard reporting zero comparisons — say whether the failure is loud or silent and how you'd find it.
  2. Connect the scoreboard. Given a monitor with an analysis port and a scoreboard with an analysis export that were never connected, write the connect_phase line and explain how you'd confirm it.
  3. Two inputs. Given a scoreboard needing input-side and output-side transaction streams, declare the two analysis imps correctly and name their write methods.
  4. Catch the false green. Describe the instrumentation and the check that would have caught a scoreboard passing green with zero comparisons before sign-off.

Summary

  • TLM issues are transactions failing to flowvanishing silently or erroring at connect-phase — because a port isn't connected, is connected wrong, lacks an implementation, or (for analysis ports) has no subscriber.
  • The crucial asymmetry: required ports fail loudly (a blocking put/get unconnected → connect-phase fatal naming the port) but analysis ports fail silently (zero subscribers is legal, so an unconnected one lets the write go into the void and the consumer receive nothingno error).
  • The most insidious failure is the unconnected analysis port: a scoreboard whose input was never connected receives nothing, compares nothing, and passes greenfalse confidence worse than a failure, because silence and success look identical.
  • Observe with uvm_top.print_topology() (an analysis port with zero subscribers is the silent case) and the consumer's received count (a scoreboard reporting zero comparisons received nothing); don't trust a green result from an unconfirmed consumer.
  • The durable rule of thumb: a TLM failure is loud or silent by port type — a required port unconnected fatals at connect-phase naming the port, but an analysis port unconnected vanishes silently because zero subscribers is legal, letting a scoreboard receive nothing and pass green (false confidence); so connect ports in connect_phase, print the topology to confirm analysis ports reach their subscribers, instrument consumers with received counts, and treat a passing scoreboard that compared nothing as a failure — never trust the absence of mismatches from a component you haven't confirmed received data.

Next — Sequencer Problems: the methodology turns to stimulus generation. Why nothing drives the interface, or sequences stall or collide — a sequence never started on the sequencer, a sequence started on the wrong sequencer, the driver's get/item_done handshake broken, arbitration starving a sequence, or objection/finish_item mishandled — how to read the symptom (idle interface, hung sequence), how to observe it (sequencer and driver verbosity), and how to fix each so stimulus flows.