Perl Part II · Module 21 · Simulation and UVM Log Analysis
Simulator Log Formats: VCS, Xcelium, Questa
Before a single UVM message can be read, a log parser has to cope with the fact that the three major simulators wrap the same UVM output in three different shells: different banners, different end-of-run lines, and in one case a two-character prefix on every line that silently defeats every anchored pattern you have written. This chapter measures that, states plainly which of its inputs are real simulator output and which are samples of a documented format, and identifies the small set of lines that are genuinely the same everywhere.
Intermediate14 min readPerlPart IIUVM Logs
Part II · Module 21 · Chapter 21.1 · Simulator Log Formats: VCS, Xcelium, Questa
1. Four Log Shapes, One Parser
use strict;
use warnings;
# These three files are REPRESENTATIVE SAMPLES of each simulator's line shape,
# written by hand from the documented formats. They are NOT captured output --
# none of these three tools exists on this machine, and inventing tool output
# is the one thing this track never does. What is being measured is how a
# PARSER behaves against each shape, which does not require the tool.
my @LOGS = (
["VCS-style", "logs/sample_vcs.log"],
["Xcelium-style", "logs/sample_xcelium.log"],
["Questa-style", "logs/sample_questa.log"],
["this machine", "logs/errors.log"], # REAL Icarus output
);
# ---- A: the pattern almost everybody writes first
my $A_ERR = qr/^UVM_ERROR/;
my $A_FINISH = qr/^\$finish/;
# ---- B: tolerate a leading prefix, and accept each tool's finish line
my $B_ERR = qr/^(?:#\s*)?UVM_ERROR\b/;
my @B_FINISH = (
[ "VCS/plain", qr/^(?:#\s*)?\$finish\b/ ],
[ "VCS report", qr/^\$finish at simulation time/ ],
[ "Xcelium", qr/^Simulation complete via \$finish/ ],
[ "Questa", qr/^#\s*\*\* Note: \$finish/ ],
);
sub scan {
my ($file) = @_;
open my $fh, "<", $file or die "$file: $!";
my %r = (a_err => 0, b_err => 0, a_fin => 0, b_fin => "");
while (my $l = <$fh>) {
next if $l =~ /^(?:#\s*)?UVM_\w+\s*:\s*\d+\s*$/; # 18.4: the summary's own count
$r{a_err}++ if $l =~ $A_ERR;
$r{b_err}++ if $l =~ $B_ERR;
$r{a_fin}++ if $l =~ $A_FINISH;
for my $f (@B_FINISH) { $r{b_fin} = $f->[0] if !$r{b_fin} && $l =~ $f->[1] }
}
close $fh;
return %r;
}
printf "%-16s %-11s %-11s %-11s %s\n",
"LOG SHAPE", "A errors", "B errors", "A finish?", "B finish (which form)";
for my $L (@LOGS) {
my ($label, $file) = @$L;
my %r = scan($file);
printf "%-16s %-11d %-11d %-11s %s\n", $label, $r{a_err}, $r{b_err},
($r{a_fin} ? "yes" : "NO"), ($r{b_fin} || "NONE RECOGNISED");
}
print "\nevery one of these logs contains exactly ONE UVM_ERROR message.\n";
print "pattern A finds it in three of four, and misses the Questa-style log\n";
print "entirely, because Questa prefixes every line with '# '.\n";
print "A also fails to find a finish marker in two of the four, because each\n";
print "tool spells the end of a run differently.\n";LOG SHAPE A errors B errors A finish? B finish (which form)
VCS-style 1 1 yes VCS/plain
Xcelium-style 1 1 NO Xcelium
Questa-style 0 1 NO Questa
this machine 3 3 yes VCS/plain
every one of these logs contains exactly ONE UVM_ERROR message.
pattern A finds it in three of four, and misses the Questa-style log
entirely, because Questa prefixes every line with '# '.
A also fails to find a finish marker in two of the four, because each
tool spells the end of a run differently.Every sample log contains exactly one UVM_ERROR message. Pattern A finds it in three of the four shapes.
2. The Two Characters That Break Everything
Questa-style A errors: 0 B errors: 1Questa prefixes every line of its transcript with # . So a log that plainly contains
# UVM_ERROR ../tb/scb.sv(88) @ 41000: uvm_test_top.env.scb [SCBD] data mismatchcontains no line starting with UVM_ERROR. An anchored pattern — which Chapter 11.8 and Chapter 18.4 both insist on, correctly — finds nothing at all.
This is, in practice, the single most common reason a working log parser produces an empty report on somebody else's log. The script does not crash. It does not warn. It reports zero errors, and zero errors reads as a pass.
The fix is to allow the prefix rather than to drop the anchor:
my $B_ERR = qr/^(?:#\s*)?UVM_ERROR\b/;(?:#\s*)? — an optional hash and optional whitespace. Two things to notice about that pattern:
The anchor is kept. ^ still applies, so a UVM_ERROR mentioned mid-sentence in an informational message still does not match. Loosening the anchor entirely would trade this bug for Chapter 18.4's false positives.
It is a non-capturing group ((?:...)), so it does not shift $1 for any capture that follows — the mistake that breaks every downstream extraction when somebody adds the prefix support later.
3. The End Of The Run, Spelled Four Ways
Pattern A also fails to find a finish marker in two of the four logs, and this is the more dangerous of the two problems because Chapter 21.2 is entirely about why the finish marker is the first thing a parser should look for.
The four spellings, from the samples:
$finish called from file "../tb/top.sv", line 92. (plain, and this machine)
$finish at simulation time 70000 (VCS's report block)
Simulation complete via $finish(1) at time 70 NS + 0 (Xcelium)
# ** Note: $finish : ../tb/top.sv(92) (Questa)The parser carries them as a table, exactly as Chapter 18.8 carried several spellings of one field across tool versions:
my @B_FINISH = (
[ "VCS/plain", qr/^(?:#\s*)?\$finish\b/ ],
[ "VCS report", qr/^\$finish at simulation time/ ],
[ "Xcelium", qr/^Simulation complete via \$finish/ ],
[ "Questa", qr/^#\s*\*\* Note: \$finish/ ],
);And it reports which spelling matched — VCS/plain, Xcelium, Questa in the measured output. That label is worth keeping for the same reason Chapter 18.8 kept it: when a parser later finds nothing, knowing which forms it tried and which it has matched before is most of the diagnosis.
Note \$finish is escaped in every one of those patterns. In Perl a bare $f inside a regex is a variable interpolation, so an unescaped $finish silently becomes the empty string — a pattern that matches far too much rather than too little.
4. What Is Actually The Same Everywhere
The useful half of this chapter. Despite the shells, UVM's own output is identical across all three tools, because it is produced by the UVM library rather than by the simulator:
The message line layout. SEVERITY file(line) @ time: path [ID] text — same fields, same order, everywhere.
The four severities. UVM_INFO, UVM_WARNING, UVM_ERROR, UVM_FATAL, and no others. Chapters 21.3 to 21.6 take them one at a time.
The report summary block. --- UVM Report Summary --- followed by per-severity counts. Chapter 21.7 parses it and cross-checks it.
The [ID] field. A short string the testbench chose, which is what makes Chapter 21.9's signature classification possible at all.
So the division of labour for a parser is clean, and it is worth structuring the program this way:
| layer | varies by | handled in |
|---|---|---|
| line prefix, banner, finish line | the simulator | one normalisation step |
| severity, ID, time, path, text | nobody — UVM owns it | the rest of the module |
Get the shell right once, at the input, and everything after it is tool-independent. That is why this chapter comes first, and why the rest of Module 21 can be written without naming a vendor again.
5. What To Remember
- The three major simulators wrap identical UVM output in three different shells.
- Say which of your inputs are real and which are format samples. No invented tool output (16.1).
- Measured: one error per log, and an anchored
/^UVM_ERROR/found it in 3 of 4 shapes. - Questa prefixes every transcript line with
#, so the anchored pattern finds zero — the most common reason a working parser returns an empty report on someone else's log. - It fails silently. No crash, no warning, and zero errors reads as a pass.
- Allow the prefix, keep the anchor:
qr/^(?:#\s*)?UVM_ERROR\b/. - Use a non-capturing group
(?:...)or every later$1shifts. - Or strip the prefix once at the input (18.3's normalisation) — but then you lose the fact that a line lacked it, which is how you spot stray
$displayoutput in a Questa transcript. Pick one and write it down. - Measured: no finish marker found in 2 of 4 logs — each tool spells the end of a run differently.
- Carry the finish spellings as a table and report which one matched (18.8).
- Escape
\$finish— a bare$finishin a Perl regex interpolates to the empty string. - UVM's own output is identical everywhere: the message layout, the four severities, the report summary block, and the
[ID]field. - Structure the parser in two layers: a tool-specific normalisation at the input, and tool-independent logic after it.
- Skip the summary's own count lines first (18.4), or every log reports one error too many.
- Measured, on one real log: 6, 5 and 4 are all defensible counts of
UVM_INFO— a naive grep, the real message lines, and the summary's own figure.
Chapter 21.2 takes the first question any log parser should ask, and the one most parsers ask last: did the test actually finish? A log with zero errors and no finish line is a crashed run, and it is indistinguishable from a pass to anything that only counts severities.
Where this fits
Part of the Perl curriculum.
