UVM
Debugging Objections
The tools and method for finding a stuck or mistimed objection — +UVM_OBJECTION_TRACE, display_objections, the objection count API and callbacks, and a systematic process from symptom to the exact objection holding a phase open or ending it too soon.
Objections · Module 17 · Page 17.6
The Engineering Problem
The catalog (Module 17.5) tells you which bug a symptom belongs to — a hang is a missing drop, a premature end is a timing problem. But knowing the class is not finding the culprit: which objection, raised by which component, on which line, is the unmatched raise holding the phase open? In a large testbench with dozens of components raising and dropping objections constantly, the stuck objection is a needle in a haystack — and the simulation gives you nothing on its own except a hang (the run never completes) or a silent premature end (the test passes having skipped work). You cannot find the objection by reading the code alone — there are too many raise/drop sites, and the failing path may be non-obvious. The problem this chapter solves is locating the specific objection — the tools that make the invisible objection activity visible, and the systematic method that turns a symptom into the exact raise/drop site.
The primary tool is +UVM_OBJECTION_TRACE — a command-line plusarg that makes UVM print every raise and drop, with the component, the count, the description, and the time. With it, the objection activity becomes a transcript you read to find the unmatched raise (a hang) or the premature drop (an early end). The complementary tools: display_objections() prints a snapshot of who is currently objecting (the hierarchical count per component) — invaluable when a timeout fires on a hang; the count API (get_objection_count, get_objection_total) and callbacks (raised, dropped, all_dropped) give programmatic access. The method: recognize the pole (17.5), enable the trace, re-run, and read the transcript — for a hang, find the outstanding objection (its component and description); for a premature end, find the drop that fired too early. And the description (Module 17.2) finally pays off: the trace shows it, so a meaningful description names the objection — without it, the objection is anonymous and hard to locate. This chapter is the debugging toolkit: the trace, the display, the count API, and the symptom-to-site method.
How do you find the specific stuck or mistimed objection — using
+UVM_OBJECTION_TRACE,display_objections, and the count API — and what is the systematic method from a symptom (hang or premature end) to the exact raise/drop site?
Motivation — why you need tools, not just code reading
Finding a stuck or mistimed objection requires tools because the activity is invisible and voluminous. The reasons:
- Objection activity is invisible by default. A raise or drop changes a count — it produces no output. So a hang or premature end gives you nothing to read except the symptom. You can't see who raised what when without a tool.
- There are too many raise/drop sites to inspect. A large testbench has dozens of components raising and dropping objections thousands of times. Reading the code to find the one unmatched raise is infeasible — you need the tool to point at it.
- The failing path may be non-obvious. A skipped drop (Module 17.5) hides on an error or exception path that only triggers on certain stimulus. The code looks correct on the normal path; the trace shows the actual (failing) path.
- A snapshot at the moment of failure is decisive. When a hang is caught by a timeout, a display of who is currently objecting points directly at the stuck component(s) — no searching.
- The description is the handle the tools give you. The trace and display show the description you passed at raise time. A meaningful description names the objection in the output; without it, you see an anonymous objection and must guess — so descriptions are for the tools.
The motivation, in one line: objection activity is invisible (a raise/drop just changes a count) and voluminous (dozens of components, thousands of raises/drops), with the failing path often non-obvious — so finding a stuck or mistimed objection requires tools (+UVM_OBJECTION_TRACE for the transcript, display_objections for the snapshot) that make the activity visible and point at the culprit, labeled by the description you passed for exactly this purpose.
Mental Model
Hold debugging objections as auditing the meeting's hand-raise log to find the hand that was never lowered:
Debugging objections is auditing the hand-raise log: the trace is a transcript that records every hand raised and lowered, with who, when, and why; you read it to find the hand that was raised but never lowered (a hang) or lowered too soon (an early end). The roll call (display) is a snapshot of whose hand is up right now. The reasons (descriptions) are what make the log readable. Picture the meeting (the phase) gone wrong — it never adjourns (a hang). You can't tell whose hand is stuck up just by looking at the room (the count is invisible). So you turn to the log:
+UVM_OBJECTION_TRACEis a transcript that recorded every hand-raise and hand-lower — who (the component), when (the time), why (the description), and the running tally (the count). You read the transcript and look for the raise with no matching lower — the hand that went up and never came down. The who and why in that entry tell you which component and which of its objections is stuck — and that points you to the line (the raise site) and the path that skipped the lower. For a premature adjournment, you read for the lower that happened too soon — before the business was done — and the time shows the mistiming. There's also a roll call:display_objectionsprints, at a moment, whose hand is currently up — so when a timeout fires on a hang, the roll call names the stuck participant(s) directly. And the crucial enabler: the log is only readable if the hands were raised with a reason — an anonymous raise (no description) shows up as "someone raised a hand" with no why, and you're back to guessing; a described raise shows "the scoreboard raised a hand for outstanding transactions" — exactly what you need. So auditing the log (trace) and taking a roll call (display), with reasons attached (descriptions), is how you find the stuck or premature hand.
So debugging objections is auditing the hand-raise log: the trace (+UVM_OBJECTION_TRACE) is the transcript of every raise/drop (who, when, why, tally); you read it for the unmatched raise (hang) or premature drop (early end). The display is a roll-call snapshot of current objectors (decisive on timeout). And the descriptions are what make the log readable — they name the objections in the output. Read the transcript for the hand never lowered; the description tells you whose.
Visual Explanation — the debugging tools
The defining picture is the toolkit: the trace (transcript), the display (snapshot), and the count API/callbacks (programmatic) — each making objection activity visible a different way.
The figure shows the three ways to make objection activity visible — the toolkit. +UVM_OBJECTION_TRACE (a plusarg) produces a transcript: every raise and drop, with the component, count, description, and time — read it to find the unmatched raise (hang) or premature drop (early end). display_objections() (a method call) produces a snapshot: who is currently objecting, per component in the hierarchy — decisive when a timeout fires (it names the stuck component(s) directly). The count API (get_objection_count, get_objection_total) and callbacks (raised, dropped, all_dropped) give programmatic access — for custom checks in code. The crucial reading is that each tool makes the invisible objection activity visible a different way: the trace shows it over time (the full history — every event), the display shows it at a moment (a snapshot — current state), and the API/callbacks expose it in code (for automated checks). The brand-colored tools produce the success-colored visibility. Which tool you reach for depends on the situation: a hang you can reproduce → the trace (read the history to the unmatched raise); a hang caught by a timeout → the display (snapshot who's stuck at the moment of timeout); a want to assert "no objections outstanding at point X" → the count API. The diagram is the debugging arsenal: the trace (transcript over time), the display (snapshot at a moment), and the API/callbacks (programmatic) — together, they turn the invisible count-changing activity into readable, locatable events, labeled by the descriptions you passed.
RTL / Simulation Perspective — reading the trace and display
In code (and command line), you enable the trace, read its output to find the culprit, and display the snapshot on a timeout. The example shows the tools and a sample trace.
// === ENABLE THE TRACE (command line) ===
// simv +UVM_OBJECTION_TRACE ← prints every raise/drop: component, count, description, time
// === SAMPLE TRACE OUTPUT for a HANG (the unmatched raise is the culprit) ===
// @ 10: uvm_test_top.env.agt raised 1 objection(s): "stimulus" count -> 1
// @ 10: uvm_test_top.env.sb raised 1 objection(s): "outstanding txns" count -> 2
// @ 90: uvm_test_top.env.agt dropped 1 objection(s): "stimulus" count -> 1
// ... (no drop for env.sb "outstanding txns") ... ← UNMATCHED RAISE → count stuck at 1 → HANG
// the trace NAMES the culprit: env.sb's "outstanding txns" objection was never dropped
// === DISPLAY THE SNAPSHOT when a TIMEOUT fires (who is objecting right now) ===
function void my_test::phase_ready_to_end(uvm_phase phase);
// or in a timeout handler: print who is currently holding the phase open
phase.phase_done.display_objections(); // prints the hierarchical count: env.sb = 1, ...
endfunction
// === SET A TIMEOUT so a hang is CAUGHT (not just an infinite run) ===
// simv +UVM_TIMEOUT=1ms,YES OR uvm_top.set_timeout(1ms); ← then display on timeout
// === PROGRAMMATIC: assert no objections outstanding at a checkpoint ===
if (phase.phase_done.get_objection_total() != 0)
`uvm_error("OBJ", $sformatf("unexpected outstanding objections: %0d", phase.phase_done.get_objection_total()))The example shows the tools in use. Enable the trace: simv +UVM_OBJECTION_TRACE makes UVM print every raise/drop — component, count, description, time. Read the trace (the sample for a hang): each line shows a raise or drop with its details; you scan for the raise with no matching drop — here, env.sb raised "outstanding txns" (count → 2) but never dropped it, so the count stuck at 1 → hang. The trace names the culprit: env.sb's "outstanding txns" objection — exactly the component and the reason, so you go straight to that component's phase_ready_to_end / drop logic and find the path that skipped the drop. Display the snapshot: phase.phase_done.display_objections() prints the hierarchical count per component — invaluable in a timeout handler (when a hang is caught, who is objecting right now). Set a timeout: +UVM_TIMEOUT=1ms,YES or uvm_top.set_timeout(1ms) makes a hang be caught (not an infinite run) so you can display on it. Programmatic: get_objection_total() lets you assert "no objections outstanding" at a checkpoint. The shape to carry: enable +UVM_OBJECTION_TRACE and read its transcript to find the unmatched raise (hang) or early drop (premature end) — labeled by the description; display_objections for a snapshot (decisive on timeout); a timeout to catch a hang; and the count API for programmatic checks. The trace's "outstanding txns" description is what makes the culprit obvious — without it, the line would read anonymously and the hunt would be harder.
Verification Perspective — reading the trace for each pole
The trace is read differently for the two poles: for a hang, find the unmatched raise; for a premature end, find the premature drop. Knowing what to look for is how you use the trace.
The figure shows how to read the trace for each pole. For a hang (never ends): scan the trace for a raise with no matching drop — the last outstanding objection. Its component and description name the stuck objection, pointing you to the path that skipped the drop (the fix of Module 17.5). For a premature end (ends too early): find the drop that fired before the work completed — its time, compared to when the work finished, shows the mistiming (the drop came too early, the fix being to drop after the drain). The verification insight is that the trace contains both the events and the times, so each pole's culprit is a specific entry you scan for — you don't read the whole trace blindly; you know (from the pole, Module 17.5) what to look for. For a hang: the trace will show raises and drops netting to a positive count, with one (or more) raise that has no drop — that's the culprit (and the display confirms it by snapshot). For a premature end: the trace will show a drop whose time is earlier than when the work actually finished (which you know from the waveform or the scoreboard's last check) — that's the mistimed drop. The warning-colored symptoms direct the scan; the brand-colored entries are what you find; the success-colored details (component + description, or time) point at the fix. The figure is the reading guide: the trace is not a wall of text to parse whole — it's a transcript you scan, guided by the pole, for a specific kind of entry (an unmatched raise or a too-early drop), whose details name the culprit. Know the pole, scan for its entry, read the details — the trace hands you the site.
Runtime / Execution Flow — the systematic method
The method is a fixed procedure: recognize the pole, enable the trace, re-run, read for the pole's entry, fix. The flow shows it.
The flow shows the systematic method — symptom to fix. Recognize the pole (step 1): hang (never ends) or premature end (ends too early) — from the symptom (Module 17.5). Enable the trace (step 2): +UVM_OBJECTION_TRACE prints every raise/drop; also set a timeout (+UVM_TIMEOUT / set_timeout) so a hang is caught (not an infinite run) and you can display on it; re-run. Read the trace (step 3): scan for the pole's entry — a raise with no drop (hang) or a too-early drop (premature end) — using display_objections for a snapshot if a timeout fired. Locate and fix (step 4): the entry's component and description point to the raise/drop site; apply the fix per the catalog (Module 17.5 — drop on every path for a hang, drop after drain for a premature end). The runtime insight is that this is a repeatable process that turns any objection symptom into the exact site — no guessing, no reading all the code. Each step narrows: the pole (step 1) tells you what to look for; the trace (step 2) makes it visible; the scan (step 3) finds the entry; the details (step 4) give the site. This is why the tools and the catalog work together: the catalog (17.5) classifies the symptom, and the tools (17.6) locate the instance — classification + location = fix. The flow is the complete debugging loop for objections: recognize (pole), instrument (trace + timeout), read (scan for the entry), locate (component + description), fix (catalog) — a deterministic path from a hung or prematurely-ended simulation to the one raise or drop that caused it. Apply the method, and the objection that took the haystack becomes a named line.
Waveform Perspective — the trace pinpointing the stuck objection
The trace's power is visible as the event history: a stream of raise/drop events, with one raise that never gets its drop — the count stuck — and the trace labels exactly which. The waveform shows the trace pinpointing it.
The trace event history: one raise never gets its drop — the stuck objection the count reveals
12 cyclesThe waveform shows the trace as an event history pinpointing the stuck objection. Reading the trace as events: raise_A and raise_B both fire (obj_count goes 0 → 1 → 2), and drop_A fires (count → 1), but drop_B never fires — so obj_count stays stuck at 1. The crucial reading is the asymmetry: every raise should have a matching drop, and the trace (and the count) reveal the one that doesn't — raise_B has no drop_B, so its objection is outstanding forever. The trace labels each event with its component and description, so the unmatched raise_B is named (e.g., env.sb "outstanding txns") — exactly the culprit. The picture to carry is that the count staying at 1 with no further drop is the hang, and the trace entry for the undropped raise_B is the culprit: reading the event history finds the raise with no matching drop. This is what +UVM_OBJECTION_TRACE gives you — the event history made readable, so the count 2 → 1 → (stuck) is not a mystery but a traceable sequence where the missing drop_B stands out. Reading an objection trace this way — which raise has no matching drop? what component and description does that raise carry? — is the core debugging move. The stuck count with the labeled, undropped raise is the signature of a located hang: the trace turned the invisible count into a named event you can point at — env.sb's "outstanding txns" raise that never dropped — and that name leads you to the skipped-drop path. The trace makes the stuck hand visible and tells you whose.
DebugLab — the hang that was hard to find until the objections were named
A hang located only after objections were given meaningful descriptions
A test hung, caught by a timeout. The engineer recognized the pole (a hang → a never-dropped objection) and enabled +UVM_OBJECTION_TRACE. But the trace was hard to read: the outstanding objection showed up as an anonymous raise from a component that had several objections — uvm_test_top.env.sb raised 1 objection(s): "" (an empty description) — so the engineer couldn't tell which of the scoreboard's several raise sites was the stuck one. The trace named the component but not which objection — and the component had multiple, all anonymous.
The objections had been raised without descriptions (just raise_objection(this)), so the trace and the display showed them as anonymous — indistinguishable from each other. The tool could only name the component, not which objection or why:
✗ raised WITHOUT a description → anonymous in the trace (which objection? which site?):
phase.raise_objection(this); // and elsewhere: phase.raise_objection(this); (several sites)
// trace shows: env.sb raised 1 objection(s): "" ← which of env.sb's several raises is this?
// the component is named, but the objection is anonymous → hard to locate the stuck site
✓ raised WITH a meaningful description → the trace NAMES it (component + reason → the exact site):
phase.raise_objection(this, "outstanding write responses"); // site 1
phase.raise_objection(this, "pending coverage sample"); // site 2
// trace shows: env.sb raised 1 objection(s): "outstanding write responses" ← the exact site
// re-run: the stuck objection is named → go straight to its raise site and find the skipped dropThis is the anonymous-objection debugging obstacle — the payoff (and cost) of the description argument (Module 17.2), realized in debugging. The engineer did everything right in method: recognized the pole, enabled the trace, read it for the unmatched raise. But the trace could only name the component (env.sb), not which of its several objections was stuck — because the objections were raised without descriptions (just raise_objection(this)), so they appeared as anonymous, empty-description entries, indistinguishable from one another. The tool did its job — made the activity visible — but the output was unreadable because the objections were unnamed. The fix is to give every objection a meaningful description: raise_objection(this, "outstanding write responses") at one site, raise_objection(this, "pending coverage sample") at another — so the trace names each objection by its reason, and the stuck one is immediately identifiable (the component AND the reason → the exact raise site → the skipped-drop path). After adding descriptions and re-running, the trace showed the stuck objection as env.sb raised 1 objection(s): "outstanding write responses" — naming the exact site, which led straight to the error path that skipped its drop. The general lesson, and the chapter's thesis: the objection debugging tools (+UVM_OBJECTION_TRACE, display_objections) show what you labeled — so a meaningful description on every raise is what makes the tools usable; an undescribed objection is anonymous in the output, naming only the component (not which objection or why), making a stuck objection hard to locate when a component has several. Describe every objection — the description is the handle the debug tools give you to find it. This is why Module 17.2 insisted on the description: it pays off here, in debugging, where it's the difference between a named culprit and an anonymous one.
The tell is a trace that names the component but not the objection. Diagnose the anonymous-objection obstacle:
- Check whether the trace shows descriptions. Empty-description entries mean objections were raised without descriptions and are hard to tell apart.
- Count the component's raise sites. If a component has several objections all anonymous, the trace can't distinguish the stuck one.
- Add descriptions and re-run. With meaningful descriptions, the trace names the stuck objection by its reason.
- Use display_objections for the snapshot. On a timeout, the hierarchical display confirms which component is stuck; the description narrows it to the site.
Name every objection so the tools can locate it:
- Pass a meaningful description on every raise. The description appears in the trace and display; it names the objection and points to the site.
- Make descriptions distinct per site. Different raise sites in one component should have different descriptions, so the trace distinguishes them.
- Set a timeout so hangs are caught. A timeout turns an infinite run into a catchable event where you can display the objections.
- Enable the trace early in debug. +UVM_OBJECTION_TRACE is the first tool for any objection symptom; descriptions make its output actionable.
The one-sentence lesson: the objection debug tools (+UVM_OBJECTION_TRACE, display_objections) show what you labeled, so give every raise a meaningful, distinct description — an undescribed objection is anonymous in the trace, naming only the component and not which objection or why, which makes a stuck objection hard to locate when a component has several.
Common Mistakes
- Debugging objections by reading code alone. The activity is invisible and voluminous; enable +UVM_OBJECTION_TRACE to see who raised/dropped what when.
- Raising objections without descriptions. Anonymous objections are hard to locate in the trace; pass a meaningful, distinct description on every raise.
- No timeout, so a hang runs forever. Set +UVM_TIMEOUT or set_timeout so a hang is caught and you can display the outstanding objections.
- Not using display_objections on a timeout. The hierarchical snapshot names the stuck component(s) directly; print it when a timeout fires.
- Reading the whole trace blindly. Know the pole first (Module 17.5), then scan for its specific entry — an unmatched raise or a too-early drop.
- Ignoring the count API for assertions. get_objection_total lets you assert no outstanding objections at a checkpoint, catching imbalances early.
Senior Design Review Notes
Interview Insights
You use +UVM_OBJECTION_TRACE as the primary tool, following a systematic method from symptom to site. First, recognize the pole from the symptom: a hang means a never-dropped objection, a premature end means a timing problem. Then enable +UVM_OBJECTION_TRACE, a command-line plusarg that makes UVM print every raise and drop with the component, count, description, and time — turning the invisible objection activity into a readable transcript. Also set a timeout, with +UVM_TIMEOUT or set_timeout, so a hang is caught rather than running forever, letting you display the objections. Re-run, then read the transcript for the pole's specific entry. For a hang, scan for a raise with no matching drop — the outstanding objection; its component and description name the stuck objection and point you to the path that skipped the drop. For a premature end, find the drop that fired before the work completed; its time, compared to when the work actually finished, shows the mistiming. The complementary tool is display_objections, which prints a snapshot of who is currently objecting, per component in the hierarchy — decisive when a timeout fires, because it names the stuck component directly. There's also the count API, get_objection_count and get_objection_total, and callbacks like raised and dropped, for programmatic checks, such as asserting no objections are outstanding at a checkpoint. The key enabler is the description: the trace and display show what you labeled, so a meaningful description names the objection in the output, while an anonymous one leaves you guessing. So the method is: recognize the pole, enable the trace and a timeout, re-run, scan the trace for the pole's entry, and use the component and description to locate the exact raise or drop site, then apply the fix. The mental model is auditing a hand-raise log to find the hand never lowered.
+UVM_OBJECTION_TRACE makes UVM print a line for every objection raise and drop, showing the component that raised or dropped, whether it was a raise or drop, the count change, the description, and the simulation time. So it produces a chronological transcript of all objection activity, which is otherwise invisible because a raise or drop just changes a count with no output. You read it differently depending on the pole. For a hang, you scan for a raise that has no matching drop — the running count will rise and partially fall but settle at a positive value, and somewhere there's a raise whose corresponding drop never appears. That unmatched raise is the culprit; its component and description tell you exactly which objection is stuck and why, so you go to that component's drop logic and find the path — often an error or early-return path — that skipped the drop. For a premature end, you look for a drop whose timestamp is earlier than when the work actually finished, which you know from the waveform or the scoreboard's last check; that early drop is the mistiming, and the fix is to drop after the work drains. The trace contains both the events and their times, so each pole's culprit is a specific kind of entry you scan for, guided by knowing the pole first — you don't read the whole thing blindly. The descriptions are what make the trace readable: with meaningful descriptions, each entry names its objection by reason, so the stuck or early one is identifiable; without them, entries are anonymous and you can only see the component, not which of its objections. So reading the trace is a targeted scan: know the pole, look for the unmatched raise or the too-early drop, and read the component and description on that entry to locate the site. It's the core tool because it turns a hang or premature end into a named, locatable event.
Because the debugging tools, +UVM_OBJECTION_TRACE and display_objections, show what you labeled, so a meaningful description is what names the objection in the output and lets you locate it, while an anonymous objection leaves you with only the component name. When you raise an objection, you can pass a description string. That string appears in the trace for every raise and drop of that objection, and in the display snapshot. If you pass a meaningful, distinct description like "outstanding write responses", then when that objection is stuck, the trace shows the component plus that reason, which points you straight to the exact raise site and the path that should have dropped it. But if you raise objections without descriptions, just raise_objection(this), they appear in the trace as anonymous entries with empty descriptions. If a component has several objections, all anonymous, the trace can name the component but can't tell you which of its objections is stuck — they're indistinguishable. So even though you followed the right method — recognized the pole, enabled the trace, looked for the unmatched raise — the output is unreadable because the objections are unnamed, and you're back to guessing among the component's several raise sites. The fix is to give every objection a meaningful, distinct description, so the trace names each by its reason and the stuck one is immediately identifiable. This is the payoff of the description argument: it exists for exactly this purpose, debugging. It's why you should always pass a description, with different sites in one component getting different descriptions. The description is the handle the debug tools give you to find the objection. So descriptions matter because they're the difference between a named culprit you can go straight to and an anonymous objection you have to hunt for — and you only appreciate that when you're debugging a hang and the trace shows empty descriptions.
display_objections prints a snapshot of the current objection state — the hierarchical count of objections per component at the moment it's called — and you use it to see who is objecting right now, especially when a timeout fires on a hang. You call it on the phase's objection, like phase.phase_done.display_objections(), and it walks the component hierarchy printing which components currently have outstanding objections and how many. Whereas the trace shows the full history of raises and drops over time, the display shows the state at a single moment. This makes it ideal for a hang caught by a timeout: when the timeout handler runs, the phase is stuck with some objections outstanding, and display_objections prints exactly which components are holding it open, naming the stuck objectors directly without you having to scan a long trace. So a common setup is to set a timeout, with +UVM_TIMEOUT or set_timeout, so a hang becomes a catchable event rather than an infinite run, and then in the timeout handling, call display_objections to snapshot the stuck objections. It's the fastest way to identify the stuck component when a hang is reproducible and timed out. You can also call it at any checkpoint where you want to see the current objection state, for example to confirm objections are clearing as expected during a complex test. It complements the trace: the display answers who is objecting now, while the trace answers who raised and dropped what over time. For a hang, the display gives you the stuck component immediately, and the trace, read with descriptions, gives you which objection and the history leading to it. Together with the count API for programmatic assertions, these make the objection state observable in the ways you need — at a moment, over time, and in code. So you reach for display_objections when you want a snapshot of current objectors, most importantly on a timeout.
The catalog classifies the symptom into a bug class, and the tools locate the specific instance, so classification plus location gives the fix. The catalog, the common-objection-bugs view, organizes objection bugs into two poles — phase ends too early and phase never ends — plus the over-drop error, and maps each symptom to a bug and its fix. So when you see a hang, the catalog immediately tells you it's the never-ends pole, a missing drop somewhere; when you see a zero-time test, it's never raised; an intermittent premature end is raised too late; a pass with final transactions unchecked is dropped too early. That classification narrows what you're looking for, but it doesn't tell you which objection, in which component, on which line. That's where the tools come in. Knowing it's a hang from a missing drop, you enable +UVM_OBJECTION_TRACE and scan for the raise with no matching drop, or use display_objections on a timeout to snapshot the stuck component; the component and description name the exact objection, and you go to its drop logic to find the skipped path. Knowing it's a premature end, you scan the trace for the drop that fired too early relative to when the work finished. So the catalog gives you what to look for, and the tools make it visible and point at the instance. The method ties them together: recognize the pole from the symptom using the catalog, enable the trace and a timeout, read the trace for the pole's entry, locate by component and description, and apply the catalog's fix for that bug. This is a deterministic loop from a hung or prematurely-ended simulation to the one raise or drop that caused it. Neither alone is enough — the catalog without the tools leaves you knowing the class but not the line, and the tools without the catalog leave you reading a trace without knowing what to look for. Together, classification and location, they turn an objection haystack into a named site with a known fix.
Exercises
- Enable and read. Describe what +UVM_OBJECTION_TRACE prints and how you scan it for a hang.
- Snapshot a hang. Explain how a timeout plus display_objections locates a stuck objection.
- Name the objections. Given a component with three undescribed objections, explain the debugging problem and the fix.
- Run the method. List the steps from a premature-end symptom to the mistimed drop site.
Summary
- The primary tool is
+UVM_OBJECTION_TRACE— a plusarg that prints every raise and drop (component, count, description, time), turning objection activity into a transcript you read for the unmatched raise (hang) or premature drop (early end). display_objections()prints a snapshot of who is currently objecting (hierarchical count) — decisive when a timeout fires; the count API (get_objection_count,get_objection_total) and callbacks give programmatic access.- Read the trace by pole: for a hang, scan for a raise with no matching drop (its component and description name the stuck objection → the skipped-drop path); for a premature end, find the drop that fired before the work completed (its time shows the mistiming).
- The systematic method: recognize the pole (Module 17.5) → enable the trace and a timeout → re-run → scan for the pole's entry → locate by component and description → apply the catalog's fix — classification (catalog) + location (tools) = fix.
- The durable rule of thumb: debug objections with
+UVM_OBJECTION_TRACE(and a timeout so hangs are caught), reading the transcript for the pole's entry — an unmatched raise (hang) or a too-early drop (premature end) — and give every objection a meaningful, distinct description, because the trace and display show what you labeled: an anonymous objection names only the component, not which objection, making a stuck one hard to find.
Next — Why Scoreboards Exist: the next module turns to checking correctness. A testbench that drives stimulus and observes responses still has to decide whether the DUT is right — and that is the scoreboard's job. The module opens with why scoreboards exist: the problem of independently determining the expected result and comparing it against what the DUT actually did, the foundation of automated checking.