Skip to content
VLSI Mentor

Perl Part II · Module 21 · Simulation and UVM Log Analysis

Extracting UVM_WARNING

Warnings are the severity nobody reads, and they are read all at once on the day a failure turns out to have been announcing itself for a month. The reason they go unread is not laziness: inside a single log a warning carries no information about whether it is routine, and a per-run count cannot distinguish a check that fires in every run from one that fires in two. This chapter measures forty real runs and finds that the useful ordering is the exact reverse of the one a count gives you.

Intermediate11 min readPerlPart IIUVM Logs

Part II · Module 21 · Chapter 21.4 · Extracting UVM_WARNING

1. Forty Logs, Four Warning IDs

Azvya Education Pvt. Ltd.VLSI Mentor
warnings.pl
use strict;
use warnings;

# logs/batch holds 40 REAL Icarus runs of tb/uvm_style_tb.v in batch mode,
# seeds 1..40. Which warnings appear depends on the seed, which is how a
# regression behaves: most runs look alike and a few do not.
my @FILES = sort glob("logs/batch/*.log");
my $SUMMARY_COUNT = qr/^(?:#\s*)?UVM_\w+\s*:\s*\d+\s*$/;
my $MSG = qr/^(?:\#\s*)?(UVM_\w+)\s+(?:(\S+?)\((\d+)\)\s+)?\@\s*(\d+)\s*:\s*(\S*)\s*\[(\w+)\]\s*(.*)$/;

my (%occurrences, %logs_with, %first_text, $total_warn);
for my $f (@FILES) {
    open my $fh, "<", $f or die "$f: $!";
    my %seen_here;
    while (my $l = <$fh>) {
        next if $l =~ $SUMMARY_COUNT;
        next unless $l =~ $MSG;
        next unless $1 eq "UVM_WARNING";
        my ($id, $text) = ($6, $7);
        $occurrences{$id}++;
        $total_warn++;
        $first_text{$id} //= $text;
        $seen_here{$id} = 1;
    }
    close $fh;
    $logs_with{$_}++ for keys %seen_here;
}

printf "%d logs, %d UVM_WARNING messages in total\n\n", scalar @FILES, $total_warn;

printf "%-10s %-13s %-14s %s\n", "ID", "OCCURRENCES", "LOGS (of 40)", "first text seen";
for my $id (sort { $logs_with{$b} <=> $logs_with{$a} || $a cmp $b } keys %logs_with) {
    printf "%-10s %-13d %-14s %s\n", $id, $occurrences{$id},
        sprintf("%d (%.0f%%)", $logs_with{$id}, 100 * $logs_with{$id} / scalar @FILES),
        $first_text{$id};
}

print "\nthe two numbers answer different questions:\n";
printf "  LATE: %d occurrences but %d logs -- it fires %.1f times in EVERY run,\n",
    $occurrences{LATE}, $logs_with{LATE}, $occurrences{LATE} / $logs_with{LATE};
print "        so it is background, and a per-run count tells you nothing.\n";
printf "  ARBSTALL: %d occurrences in %d logs -- rare, and a single log would\n",
    $occurrences{ARBSTALL}, $logs_with{ARBSTALL};
print "        show it as one warning among several and look like nothing.\n";

# ---- what a single log shows, for comparison
my $one = "logs/batch/test_05.log";
my $n = 0;
open my $fh, "<", $one or die $!;
while (my $l = <$fh>) { next if $l =~ $SUMMARY_COUNT; $n++ if $l =~ /^(?:#\s*)?UVM_WARNING\b/ }
close $fh;
printf "\nseen from inside ONE log (%s): %d warnings. No way to tell which is\n",
    ($one =~ m{([^/]+)$})[0], $n;
print "routine and which is the one that matters.\n";

# ---- rarity is the signal
print "\nwarnings ranked by RARITY (the triage order):\n";
for my $id (sort { $logs_with{$a} <=> $logs_with{$b} || $a cmp $b } keys %logs_with) {
    my $pct = 100 * $logs_with{$id} / scalar @FILES;
    printf "  %-10s in %2d of %d logs (%3.0f%%)  %s\n", $id, $logs_with{$id},
        scalar @FILES, $pct,
        ($pct <= 10 ? "<-- investigate first" : $pct >= 90 ? "background noise" : "");
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
40 logs, 100 UVM_WARNING messages in total

ID         OCCURRENCES   LOGS (of 40)   first text seen
LATE       80            40 (100%)      response arrived 2 cycles late
RETRY      13            13 (32%)       transaction retried once
CLKJIT     5             5 (12%)        clock jitter exceeded budget by 17 ps
ARBSTALL   2             2 (5%)         arbiter stalled for 57 cycles

the two numbers answer different questions:
  LATE: 80 occurrences but 40 logs -- it fires 2.0 times in EVERY run,
        so it is background, and a per-run count tells you nothing.
  ARBSTALL: 2 occurrences in 2 logs -- rare, and a single log would
        show it as one warning among several and look like nothing.

seen from inside ONE log (test_05.log): 2 warnings. No way to tell which is
routine and which is the one that matters.

warnings ranked by RARITY (the triage order):
  ARBSTALL   in  2 of 40 logs (  5%)  <-- investigate first
  CLKJIT     in  5 of 40 logs ( 12%)  
  RETRY      in 13 of 40 logs ( 32%)  
  LATE       in 40 of 40 logs (100%)  background noise

One hundred warnings across forty runs, from four IDs — and the four behave completely differently.

2. Occurrences Or Logs: Two Numbers

The table has two count columns and they answer different questions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
LATE       80 occurrences   40 of 40 logs (100%)
ARBSTALL    2 occurrences    2 of 40 logs (5%)

LATE fires 2.0 times in every single run. It is background: a property of the testbench or the design that has been true since somebody wrote the check. Its occurrence count tells you how long the run was, not whether anything is wrong.

ARBSTALL appears in two runs out of forty. Two occurrences total, one per affected run.

The distinction is worth the extra line of code because the two numbers fail in opposite directions. A per-run count makes LATE look like the problem. A total count makes ARBSTALL look negligible. Only in how many runs separates a universal condition from a rare one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
my %seen_here;                       # per log
...
$seen_here{$id} = 1;                 # a set (6.9), so repeats collapse
...
$logs_with{$_}++ for keys %seen_here;   # once per log, after the file closes

The %seen_here set reset per file is the whole mechanism, and the for keys after the file closes rather than inside the loop is what makes it one count per log instead of one per occurrence.

3. Rarity Is The Signal

Now the finding. Ranked by rarity:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ARBSTALL   in  2 of 40 logs (  5%)  <-- investigate first
CLKJIT     in  5 of 40 logs ( 12%)
RETRY      in 13 of 40 logs ( 32%)
LATE       in 40 of 40 logs (100%)  background noise

That is the exact inverse of the occurrence ranking. The warning with the most occurrences is the one to ignore; the one with the fewest is the one to look at.

Which means the default report — sorted by count, descending, as every warning summary ever written — puts the least interesting warning at the top and buries the interesting one at the bottom. It is not merely unhelpful; it is actively misleading, because the reader's attention goes to the first row.

The reasoning behind rarity being the signal is worth making explicit:

A warning in 100% of runs is a property of the setup. It fires whether the design is good or bad, so it carries no information about this run. It should be either fixed or waived explicitly — and a waiver list is the right place for it, not a report.

A warning in 5% of runs is correlated with something. A seed, a configuration, a piece of traffic, a time of day. That correlation is a lead, and the two affected runs are a small enough set to read.

The middle is where real degradation hides. A check at 32% that was at 5% last month is the "slow-creeping problem" this chapter's purpose names — and seeing it requires comparing against the previous regression, which is the same baseline comparison Chapter 20.7 built for cell counts.

4. One Log Cannot Tell You Any Of This

The measurement's middle section is the honest limit:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
seen from inside ONE log (test_05.log): 2 warnings. No way to tell which is
routine and which is the one that matters.

Two warnings. Both look identical in structure — same severity, same layout, a plausible-sounding message each. Nothing in the log says one has fired in every run for a year and the other has fired twice ever.

So warning analysis is inherently a cross-log operation, and that has two practical consequences:

A per-test report should list warnings, not judge them. Chapter 21.11's per-test summary carries a warning count precisely because a count is all a single log supports.

The grouping belongs at the regression level, run once over the whole log directory, with the previous run's numbers beside it. That is the only place the information exists.

Reading the whole directory is glob (Chapter 17.3), and the sort on it is not cosmetic: Chapter 17.8's rule means two runs of this script on the same logs produce byte-identical output, which is what makes the result diffable against last week's.

5. What To Remember

  • Warnings are read all at once, on the day a failure turns out to have been announcing itself.
  • Measured: 40 real runs, 100 warnings, 4 IDs — behaving completely differently.
  • Count occurrences AND the number of logs containing each ID — two numbers, two questions.
  • LATE: 80 occurrences, 40 of 40 logs — 2.0 per run, every run. Background.
  • ARBSTALL: 2 occurrences, 2 of 40 logs — rare, and correlated with something.
  • A per-run count makes the universal warning look like the problem; a total count makes the rare one look negligible.
  • Use a per-log set (6.9) reset for each file, and increment the log count after the file closes.
  • Measured: the rarity ranking is the exact inverse of the occurrence ranking.
  • So the default count-descending report puts the least interesting warning first and buries the interesting one — misleading, not merely unhelpful.
  • 100% means a property of the setup — fix it or waive it explicitly, do not report it every day.
  • 5% means correlated with something, and two runs is a readable set.
  • The middle band is where degradation hides — 32% today that was 5% last month.
  • Rank by rarity; do not threshold on it. A universal warning can be a serious bug and a rare one can be known behaviour.
  • Automate the CHANGE, not the judgement: "0 of 40 last week, 2 of 40 today" is a fact a script can own.
  • One log cannot distinguish routine from rare — nothing in it records how often the check fires elsewhere.
  • A per-test report lists warnings; the regression-level pass groups them (21.11).
  • glob the directory and sort it (17.3, 17.8), so the output is diffable against last week's.

Chapter 21.5 turns to the severity that decides the verdict. The useful output for an error is not the count on its own — it is the count plus the first one, with its ID and simulation time, which between them are usually enough to start work without opening the log.

Where this fits

Part of the Perl curriculum.