Protected Course Page
Enter the password to view Day 13 of the UVM 30-Day plan.
Day 13: Debugging and Reporting — uvm_info Verbosity, ID Filters, Transaction Recording, Waveforms
A 30-day SystemVerilog/UVM verification series. The tools today do not add features. They make features debuggable.
You cannot fix what you cannot see. UVM gives you three layers of visibility: the text log (uvm_info and friends), the transaction database (uvm_tr_database), and the waveform viewer. Each answers a different kind of question. Today is about knowing which one to reach for, and how to configure it so the answer is right there instead of buried in a thousand INFO lines.
1. uvm_info Verbosity Levels
Every uvm_info call takes a verbosity as its last argument. The simulator suppresses any message whose verbosity is higher than the current global verbosity. The levels, in increasing order of detail:
| Level | Meaning | Use for |
|---|---|---|
UVM_NONE | Always printed, cannot be suppressed | Unrecoverable failure context; almost never correct to use |
UVM_LOW | Important, shown by default | Test start/end, major milestones |
UVM_MEDIUM | Default level | Per-transaction summaries, config reports |
UVM_HIGH | Debug only | Per-cycle details, per-field comparisons |
UVM_FULL, UVM_DEBUG | Extreme detail | Bring-up of the testbench itself |
The critical rule: a default run should show at most a few hundred lines. If your default run produces thousands of lines, nobody will read the log, and the day a failure is in there, nobody will find it.
`uvm_info("TEST", "starting", UVM_LOW)
`uvm_info("DRV", $sformatf("driving wdata=%0h", tr.wdata), UVM_HIGH)
`uvm_info("SB", $sformatf("mismatch: exp %0h got %0h", exp, got), UVM_NONE)
Notice the ID — the first argument. It is what filtering operates on.
2. ID Filtering
Each component can set its own default verbosity and its own ID filter. That means you can turn on HIGH for one component without drowning in HIGH from every other component:
// In a driver's build_phase:
set_report_verbosity_level(UVM_HIGH);
// or per-ID:
set_report_id_verbosity("DRV", UVM_HIGH);
// In the test, to disable a specific component's output entirely:
env.wr_agent.drv.set_report_id_action("DRV", UVM_NO_ACTION);
At the command line, without changing code:
+UVM_VERBOSITY=UVM_HIGH // global
+UVM_VERBOSITY=UVM_LOW // global, less verbose
// per-ID filtering is done via the report server or in code
3. Transaction Recording
The text log tells you what the testbench did. The waveform tells you what the pins did. Neither tells you what the transactions did — unless you record them into the transaction database.
UVM's uvm_tr_database and uvm_tr_stream record transaction begin/end and field values into a format that waveform viewers like Verdi or Questa can read. In modern UVM, this is mostly automatic if you call accept_tr() and begin_tr()/end_tr() on your sequence item:
function void do_record(uvm_recorder recorder);
super.do_record(recorder);
`uvm_record_field("wdata", wdata)
`uvm_record_field("rdata", rdata)
`uvm_record_field("wr_en", wr_en)
endfunction
Once recorded, transactions appear as a second timeline above the signal waveforms. When a scoreboard reports "ID 0x42 mismatch: exp 0x10 got 0x11", you can find the transaction in the viewer, see its begin/end time, and correlate with the exact pins.
uvm_tr_database::do_open gated on a plusarg) or use it only on the failing seed.
4. Waveform Debugging Strategies
When a failure occurs, the instinct is to open the waveform and search for the bug. That works, but slowly. A better workflow:
- Find the first mismatching transaction in the log. Note the transaction ID and the simulation time.
- Set a breakpoint or a "when" condition in the waveform viewer at that exact time.
- Add the scoreboard's expected queue to the waveform as a variable, if the viewer supports it.
- Look at the interface signals for one transaction before and one after the failure. The failure is usually the boundary.
- Look at the DUT's internal state at that time — full/empty, pointers, registers.
The failure mode to avoid: opening the waveform at time 0 and scrolling forward. On any real design that is hopeless. Start from the failure, work backwards.
5. Log Parsing
A UVM log for a 10-second simulation is enormous. Grep is your friend:
grep "UVM_ERROR\|UVM_FATAL" sim.log
grep -n "SB" sim.log | head -20
grep "TEST_PASS\|TEST_FAIL" sim.log
Teams that run large regressions typically wrap this in a Makefile target or a Python script. The two lines that matter most are the summary:
UVM_INFO @ 5000ns: reporter [TEST_DONE] Test finished
UVM_ERROR : 3
UVM_FATAL : 0
If UVM_ERROR is nonzero, the test failed. If UVM_FATAL is nonzero, the test crashed before finishing. Those two numbers are the pass/fail criterion.
6. What Day 14 Will Add
Tomorrow is the Week 2 Review + Advanced Mini-Project. You will take everything from Days 8–13 and build a FIFO environment with virtual sequences, an out-of-order scoreboard, coverage collection, and end-of-test discipline. The mini-project is the checkpoint before Week 3's RAL work.
7. Quiz
Ten questions on verbosity, filtering, transaction recording, and debugging.
Coming Up Next: Day 14 — Week 2 Review + Advanced Mini-Project
- Assemble Week 2's pieces — virtual sequence, two-import scoreboard, coverage collector, drain-time discipline.
- Run a full regression — multiple seeds, coverage database merged across runs, scoreboard clean.
- Close at least two coverage holes — write targeted sequences for what random regression did not hit.
- Document the environment — what each agent does, what the scoreboard checks, what the coverage model covers.
Series: 30 days of SystemVerilog/UVM verification. Day 13's debugging discipline.
No comments:
Post a Comment