Skip to content
VLSI Mentor

Perl Part II · Module 22 · Synthesis Report Parsing

Extracting Constraint and Check Warnings

The synthesis log is the one file in this module that is not a table, and it is where the tool says what it was unhappy about. Most of what it says is the same thing it said last week, repeated several times. The job is therefore not to collect warnings but to collapse them to one entry per condition and then split that list in two — the conditions that are always there, and the ones that turned up today, which are the only ones anybody needs to read.

Intermediate14 min readPerlPart IISynthesis Reports

Part II · Module 22 · Chapter 22.6 · Extracting Constraint and Check Warnings

1. Two Logs, Deduplicated By Code

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

# Two synthesis logs generated by gen_log.pl from the same design, with
# different seeds, so some warnings appear in both and some only in one.
my $LINE = qr/^(Warning|Error|Information):\s*(.*?)\s*\((\w+-\d+)\)\s*$/;

sub read_log {
    my ($file) = @_;
    open my $fh, "<", $file or die "$file: $!";
    my (%by_code, %sev, $total);
    while (my $l = <$fh>) {
        chomp $l;
        next unless $l =~ $LINE;
        my ($s, $text, $code) = ($1, $2, $3);
        next if $s eq "Information";          # not a complaint
        $total++;
        $by_code{$code}{count}++;
        $by_code{$code}{text} //= $text;
        $sev{$code} = $s;
    }
    close $fh;
    return (\%by_code, \%sev, $total);
}

# NOTE: these are deliberately NOT called $a and $b. Perl's sort block
# uses the globals $a and $b, so a hash ref named $a is shadowed inside
# every sort -- which printed HASH(0x...) as a "code" in my first run.
my ($run1, $sev1, $tot1) = read_log("reports/counter_syn.syn.log");
my ($run2, $sev2, $tot2) = read_log("reports/counter_syn_v2.syn.log");

printf "run 1: %d warning/error lines, %d distinct codes\n", $tot1, scalar keys %$run1;
printf "run 2: %d warning/error lines, %d distinct codes\n\n", $tot2, scalar keys %$run2;

print "deduplicated by code, run 1:\n";
printf "%-10s %-6s %-7s %s\n", "CODE", "SEV", "COUNT", "text";
for my $c (sort { $run1->{$b}{count} <=> $run1->{$a}{count} || $a cmp $b } keys %$run1) {
    printf "%-10s %-6s %-7d %s\n", $c, $sev1->{$c}, $run1->{$c}{count}, $run1->{$c}{text};
}
printf "  %d lines collapse to %d codes (%.1fx)\n",
    $tot1, scalar keys %$run1, $tot1 / scalar keys %$run1;

# ---- always-present vs NEW
my %all = map { $_ => 1 } (keys %$run1, keys %$run2);
my (@both, @only_a, @only_b);
for my $c (sort keys %all) {
    if    ($run1->{$c} && $run2->{$c}) { push @both,  $c }
    elsif ($run1->{$c})             { push @only_a, $c }
    else                         { push @only_b, $c }
}
print "\ncomparing the two runs:\n";
printf "  in BOTH runs (background):  %s\n", join(", ", @both);
printf "  only in run 1 (went away):  %s\n", (@only_a ? join(", ", @only_a) : "none");
printf "  only in run 2 (NEW):        %s\n", (@only_b ? join(", ", @only_b) : "none");

print "\nthe NEW ones, with their text -- this is the actionable list:\n";
for my $c (@only_b) {
    printf "  %-10s %-6s %s\n", $c, $sev2->{$c}, $run2->{$c}{text};
}

# ---- severity is in the line, and it is not the same as importance
print "\nseverity as the tool reports it, vs what needs attention:\n";
for my $c (sort keys %$run2) {
    my $where = ($run1->{$c} ? "both runs" : "NEW");
    printf "  %-10s tool says %-8s appears in %-10s %s\n", $c, $sev2->{$c}, $where,
        ($sev2->{$c} eq "Error" ? "<- an Error, and NEW" : "");
}
print "  an Error that appears in every run is a waived condition somebody\n";
print "  already decided about; a Warning that is NEW is the one to read.\n";
print "  So severity alone does not order the list -- novelty does (21.4).\n";

# ---- the count hazard
print "\nwhy deduplicate by code at all?\n";
my ($top) = sort { $run1->{$b}{count} <=> $run1->{$a}{count} } keys %$run1;
printf "  %s appears %d times in run 1 out of %d lines (%.0f%% of the log's\n",
    $top, $run1->{$top}{count}, $tot1, 100 * $run1->{$top}{count} / $tot1;
print "  complaints). Without dedup it dominates any count-ordered report,\n";
print "  and it is one condition, not several.\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
run 1: 6 warning/error lines, 3 distinct codes
run 2: 9 warning/error lines, 6 distinct codes

deduplicated by code, run 1:
CODE       SEV    COUNT   text
OPT-170    Warning 3       Design 'counter_top' has unmapped cell 'FILL1'.
OPT-314    Warning 2       Disabling timing arc between pins 'E' and 'ECK' on cell 'clk_gate'.
TIM-134    Warning 1       The clock 'clk' has no defined uncertainty.
  6 lines collapse to 3 codes (2.0x)

comparing the two runs:
  in BOTH runs (background):  OPT-170, OPT-314, TIM-134
  only in run 1 (went away):  none
  only in run 2 (NEW):        ELAB-974, TIM-052, UID-95

the NEW ones, with their text -- this is the actionable list:
  ELAB-974   Warning Latch 'clk_gate/en' is inferred; check the enable.
  TIM-052    Warning Port 'enable' is unconstrained for max_delay.
  UID-95     Error  Cannot find port 'scan_en' on design 'counter_top'.

severity as the tool reports it, vs what needs attention:
  ELAB-974   tool says Warning  appears in NEW        
  OPT-170    tool says Warning  appears in both runs  
  OPT-314    tool says Warning  appears in both runs  
  TIM-052    tool says Warning  appears in NEW        
  TIM-134    tool says Warning  appears in both runs  
  UID-95     tool says Error    appears in NEW        <- an Error, and NEW
  an Error that appears in every run is a waived condition somebody
  already decided about; a Warning that is NEW is the one to read.
  So severity alone does not order the list -- novelty does (21.4).

why deduplicate by code at all?
  OPT-170 appears 3 times in run 1 out of 6 lines (50% of the log's
  complaints). Without dedup it dominates any count-ordered report,
  and it is one condition, not several.

Six complaint lines collapse to three conditions, and one of those three accounts for half the lines:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
OPT-170 appears 3 times in run 1 out of 6 lines (50% of the log's complaints)

Without deduplication, OPT-170 dominates any count-ordered report — while being one condition, not three. Chapter 21.4 measured the same shape for UVM warnings: an occurrence count answers a question nobody asked.

2. The Code Is The Identity

Every synthesis complaint carries a code — OPT-170, TIM-134, ELAB-974 — and that code, not the message text, is what identifies the condition:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
my $LINE = qr/^(Warning|Error|Information):\s*(.*?)\s*\((\w+-\d+)\)\s*$/;

Three reasons the code is the right key, and the third is the practical one:

The text varies, the code does not. has unmapped cell 'FILL1' and has unmapped cell 'FILL2' are two texts and one condition. Chapter 21.9 solved the same problem for UVM errors by normalising values out of the text; here the tool has already done it for you by assigning a code.

The code is stable across versions. Message wording gets rewritten between releases; codes are part of the tool's documented interface, which is exactly what Chapter 18.8's spelling tables were working around the absence of.

The code is searchable. OPT-170 pasted into a vendor's documentation gives the explanation and the recommended action, which a paraphrase of the text does not.

Information: lines are dropped at the input — they are not complaints, and keeping them means the "warning count" includes the tool announcing which library it loaded.

3. Always-Present Versus New

The split is a set comparison over the two runs' codes — Chapter 6.9's hash-as-set:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
in BOTH runs (background):  OPT-170, OPT-314, TIM-134
only in run 1 (went away):  none
only in run 2 (NEW):        ELAB-974, TIM-052, UID-95

Three codes are background and three are new, and the three new ones are the entire actionable list. The script prints their text, because for a new condition the text is what somebody needs; for a background condition it is noise they have read before.

The three categories each mean something different:

In both runs — a standing condition. Either waive it explicitly in a list somebody owns, or fix it. What it must not be is reported every day, because that is how the whole report becomes unread.

Only in the old run — it went away. Usually good, occasionally a sign that a check stopped running rather than stopped firing, which is worth one line of output.

Only in the new run — the actionable list. This is what a QoR review should open with.

That requires keeping the previous run's codes, which makes this chapter's output the same shape as Chapter 22.5's delta and Chapter 20.7's count comparison: the useful report is a difference, and a difference needs yesterday's data stored somewhere.

4. Severity Is Not Importance

The last measurement is the one that changes the ordering of the report:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ELAB-974   tool says Warning  appears in NEW
OPT-170    tool says Warning  appears in both runs
UID-95     tool says Error    appears in NEW        <- an Error, and NEW

Two facts sit side by side there, and they are independent:

An Error that appears in every run is a waived condition. Somebody already looked at it and decided the flow should continue. Reporting it at the top because the tool called it an Error wastes the top of the report.

A Warning that is new is the one to read. Nobody has seen it before. It may be nothing, and it is the only item on the page about which that is not already known.

So the sort key is novelty first, severity second — not severity alone. UID-95 above is both, which makes it unambiguously first; but ELAB-974, a new Warning about an inferred latch, outranks OPT-170, a standing Warning, by a wide margin.

This is Chapter 21.4's finding in a different file: there, a warning in 100% of runs was background and one in 5% was the lead, and ranking by count inverted the useful order. Here the tool hands you a severity that looks like a ranking and is not one.

5. What To Remember

  • The log is the one file here that is not a table, and it is where the tool says what it was unhappy about.
  • Measured: 6 complaint lines collapse to 3 conditions, and one condition is 50% of the lines.
  • Deduplicate by CODE, not by text — the text varies with the object named, the code does not.
  • The code is stable across tool versions and searchable in vendor documentation; a paraphrase is neither.
  • Drop Information: lines at the input, or the warning count includes the tool naming its library.
  • Never name a variable $a or $b — Perl's sort block owns those globals, and a hash ref so named is shadowed inside every sort (5.9).
  • That bug printed HASH(0x…) as a warning code and a count of 5 where 3 was right, with the evidence on stderr (14.2, 20.3).
  • Split the codes three ways: in both runs, only in the old, only in the new (6.9).
  • Measured: 3 background codes and 3 new ones — the new ones are the entire actionable list.
  • Print the text for new conditions only; for background ones it is noise already read.
  • A condition that went away can mean a check stopped running, not just stopped firing.
  • A useful report is a difference, so store yesterday's codes (22.5, 20.7).
  • Measured: severity and novelty are independent. An Error in every run is a waiver; a new Warning is the thing to read.
  • Sort by novelty first, severity second — the tool's severity looks like a ranking and is not one (21.4).
  • Keep a waiver list with code, reason and date, diff it against the log, and report the background set as a count rather than a list.

That closes Module 22. You can take a synthesis run and answer the questions a QoR review asks: pick the report whose subject is your question rather than scanning the log, read the area fields through a table of spellings that survives a tool upgrade, count cell types with a row pattern that copes with the names and wrapping a real report contains, reconstruct the hierarchy from its indentation and divide inclusive roll-ups into the exclusive areas that say where the area actually is, compare two runs and report only what moved under a significance policy that lives in one named place, and reduce the log's complaints to one entry per condition split into the standing ones and the ones that turned up today.

Where this fits

Part of the Perl curriculum.