Skip to content
VLSI Mentor

Perl Part II · Module 23 · Static Timing Analysis Report Parsing

Extracting Slack

Slack is the number the whole report exists to deliver, and capturing it looks like one pattern against one line. It is five lines in practice: the two annotated forms, a value that is not a number, a version with no annotation, and a path whose slack line is simply absent. This chapter measures what the obvious pattern does with each, and establishes why the non-numeric case is the dangerous one — for a reason that turned out to be the opposite of what I expected.

Intermediate12 min readPerlPart IISTA Reports

Part II · Module 23 · Chapter 23.2 · Extracting Slack

1. Five Slack Lines, Two Patterns

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

# annotations.pt.rpt carries five endpoints whose slack lines take the five
# forms a real report produces: MET, VIOLATED, INFINITY, no annotation at
# all, and no slack line. The numbers come from the generated corner reports.
my $RPT = "reports/annotations.pt.rpt";
open my $fh, "<", $RPT or die "$RPT: $!";
local $/;
my $text = <$fh>;
close $fh;
my @blocks = split /(?=^[ \t]*Endpoint:)/m, $text;
shift @blocks while @blocks && $blocks[0] !~ /Endpoint:/;

# ---- A: the pattern almost everybody writes first
my $A = qr/^[ \t]*slack[ \t]*\((MET|VIOLATED)\)[ \t]*(-?[\d.]+)/m;

# ---- B: annotation optional, value may be INFINITY, and ABSENCE is a state
my $B = qr/^[ \t]*slack[ \t]*(?:\((\w+)\))?[ \t]*(-?[\d.]+|INFINITY)[ \t]*$/m;

printf "%-14s %-22s %s\n", "ENDPOINT", "A (strict)", "B (tolerant)";
my (@a_ok, @b_ok);
for my $blk (@blocks) {
    my ($ep) = $blk =~ /^[ \t]*Endpoint:[ \t]*(\S+)/m;
    next unless defined $ep;
    my ($aa, $av) = $blk =~ $A;
    my ($ba, $bv) = $blk =~ $B;
    push @a_ok, $ep if defined $av;
    push @b_ok, $ep if defined $bv;
    printf "%-14s %-22s %s\n", $ep,
        (defined $av ? "$av ($aa)" : "NOTHING"),
        (defined $bv ? ($bv . " (" . ($ba // "no annotation") . ")") : "NOTHING");
}
printf "\nA read %d of %d endpoints; B read %d of %d\n",
    scalar @a_ok, scalar @blocks, scalar @b_ok, scalar @blocks;

# ---- the three outcomes, named (18.8)
print "\nclassifying each endpoint into THREE states, not two:\n";
printf "%-14s %-12s %s\n", "ENDPOINT", "STATE", "why";
for my $blk (@blocks) {
    my ($ep) = $blk =~ /^[ \t]*Endpoint:[ \t]*(\S+)/m;
    next unless defined $ep;
    my ($ann, $val) = $blk =~ $B;
    my ($state, $why);
    if (!defined $val)            { ($state, $why) = ("UNKNOWN", "no slack line in the block") }
    elsif ($val eq "INFINITY")    { ($state, $why) = ("UNCONSTRAINED", "no capture clock reaches it") }
    elsif (defined $ann && $ann eq "VIOLATED") { ($state, $why) = ("VIOLATED", "the tool says so") }
    elsif (defined $ann && $ann eq "MET")      { ($state, $why) = ("MET", "the tool says so") }
    elsif ($val < 0)              { ($state, $why) = ("VIOLATED", "no annotation; sign is negative") }
    else                          { ($state, $why) = ("MET", "no annotation; sign is non-negative") }
    printf "%-14s %-12s %s\n", $ep, $state, $why;
}

# ---- why INFINITY must not become a number
print "\nwhat happens if INFINITY is treated as a number:\n";
my $inf = "INFINITY";
{
    no warnings 'numeric';
    printf "  'INFINITY' + 0            = %s\n", $inf + 0;
    printf "  is 'INFINITY' < 0 ?       %s\n", ($inf < 0 ? "yes" : "no");
    printf "  does it sort as the worst? %s\n", (($inf + 0) < -0.09 ? "yes" : "no");
}
printf "  does it sort as the BEST?   %s\n", (($inf + 0) > 0.465 ? "yes" : "no");
print "  so Perl recognises the WORD: 'INFINITY' numifies to Inf, not to 0.\n";
print "  That is worse than a zero would be. An unconstrained path becomes\n";
print "  the path with the MOST margin, so it sorts to the bottom of a\n";
print "  worst-slack list and vanishes from every top-N report (23.10).\n";
print "  Perl also numifies 'Inf', 'Infinity' and 'NaN' this way, so a\n";
print "  string field is not safely numeric just because it is not empty.\n";

# ---- and the sign-only rule, measured against the annotation
print "\nis the SIGN a reliable substitute for the annotation?\n";
my ($agree, $total) = (0, 0);
for my $blk (@blocks) {
    my ($ann, $val) = $blk =~ $B;
    next unless defined $val && defined $ann && $val ne "INFINITY";
    $total++;
    my $by_sign = ($val < 0) ? "VIOLATED" : "MET";
    $agree++ if $by_sign eq $ann;
}
printf "  on the %d endpoints that have BOTH, sign and annotation agree %d times\n",
    $total, $agree;
print "  -- so the sign is usually right, and 23.3 is about the cases where\n";
print "  trusting it instead of the annotation changes the answer.\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
ENDPOINT       A (strict)             B (tolerant)
ep_met/D       0.4650 (MET)           0.4650 (MET)
ep_viol/D      -0.0900 (VIOLATED)     -0.0900 (VIOLATED)
ep_infinite/D  NOTHING                INFINITY (MET)
ep_noann/D     NOTHING                0.1234 (no annotation)
ep_missing/D   NOTHING                NOTHING

A read 2 of 5 endpoints; B read 4 of 5

classifying each endpoint into THREE states, not two:
ENDPOINT       STATE        why
ep_met/D       MET          the tool says so
ep_viol/D      VIOLATED     the tool says so
ep_infinite/D  UNCONSTRAINED no capture clock reaches it
ep_noann/D     MET          no annotation; sign is non-negative
ep_missing/D   UNKNOWN      no slack line in the block

what happens if INFINITY is treated as a number:
  'INFINITY' + 0            = Inf
  is 'INFINITY' < 0 ?       no
  does it sort as the worst? no
  does it sort as the BEST?   yes
  so Perl recognises the WORD: 'INFINITY' numifies to Inf, not to 0.
  That is worse than a zero would be. An unconstrained path becomes
  the path with the MOST margin, so it sorts to the bottom of a
  worst-slack list and vanishes from every top-N report (23.10).
  Perl also numifies 'Inf', 'Infinity' and 'NaN' this way, so a
  string field is not safely numeric just because it is not empty.

is the SIGN a reliable substitute for the annotation?
  on the 2 endpoints that have BOTH, sign and annotation agree 2 times
  -- so the sign is usually right, and 23.3 is about the cases where
  trusting it instead of the annotation changes the answer.

The strict pattern reads 2 of 5 endpoints. The tolerant one reads 4, and the fifth has no slack line at all — which is the correct answer for it.

The difference between the two patterns is three decisions:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
my $A = qr/^[ \t]*slack[ \t]*\((MET|VIOLATED)\)[ \t]*(-?[\d.]+)/m;
my $B = qr/^[ \t]*slack[ \t]*(?:\((\w+)\))?[ \t]*(-?[\d.]+|INFINITY)[ \t]*$/m;

The annotation is optional — (?:\((\w+)\))?. Some tool versions and some options print the bare word slack followed by a value.

The annotation is captured as \w+, not as an alternation of the two words you have seen. A tool that prints (UNCONSTRAINED) or a localised word still parses, and the value is still recovered.

The value may not be a number. (-?[\d.]+|INFINITY) — and section 3 is about why that alternative matters more than it looks.

Both use [ \t]* rather than \s*, for the reason Chapter 23.1 measured: \s matches newlines and turns a /m-anchored pattern into one that matches in places you did not intend.

2. Three States, Not Two

A slack value is not a number with two labels. It is one of four situations, and the script classifies them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ep_met/D        MET            the tool says so
ep_viol/D       VIOLATED       the tool says so
ep_infinite/D   UNCONSTRAINED  no capture clock reaches it
ep_noann/D      MET            no annotation; sign is non-negative
ep_missing/D    UNKNOWN        no slack line in the block

UNCONSTRAINED is not a pass. An endpoint with infinite slack has no capture clock reaching it — which usually means a missing constraint, not a comfortable path. It is one of the most useful things a timing report can tell you, and treating it as a very good MET discards it.

UNKNOWN is not a pass either. A path block with no slack line means the parse is incomplete or the report is truncated, and Chapter 18.9's argument applies unchanged: the absence of a value is a distinct fact, and mapping it onto zero or onto MET manufactures a result.

So the classification has four outcomes and only two of them are verdicts about timing. Chapter 18.8's rule once more: unrecognised must be a named outcome.

3. INFINITY Numifies To Inf, Not To Zero

Here is the measurement that corrected my own expectation. I assumed "INFINITY" in numeric context would behave like any other non-numeric string and become 0. It does not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
'INFINITY' + 0            = Inf
is 'INFINITY' < 0 ?       no
does it sort as the worst? no
does it sort as the BEST? yes

Perl recognises the word. INFINITY, Inf, Infinity and NaN all numify to real infinities and NaN, case-insensitively — unlike abc, which becomes 0, or 12abc, which becomes 12.

And that is worse than a zero would be:

A zero would put the path in the middle of a sorted slack list — wrong, but visible.

An Inf puts it at the far end as the best path in the design. So an unconstrained endpoint — one of the highest-value findings in a timing report — sorts to the bottom of a worst-slack list and never appears in a top-N report at all (Chapter 23.10). The report is not wrong about any number it shows; it silently omits the one that mattered.

4. When The Annotation Is Absent

ep_noann/D has a value and no (MET) or (VIOLATED), and the classifier falls back to the sign:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
elsif ($val < 0) { ("VIOLATED", "no annotation; sign is negative") }
else             { ("MET",      "no annotation; sign is non-negative") }

The measurement checks that fallback against the annotation where both exist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
on the 2 endpoints that have BOTH, sign and annotation agree 2 times

The sign is usually right. Which is exactly why it is a trap, and why Chapter 23.3 is a separate chapter: a rule that agrees with the authority on every case you have tested is not a verified rule, it is an untested one — the same shape as Chapter 21.2's error-counting verdict that was correct on every healthy log.

Two reasons to prefer the annotation when it is present:

The tool knows about things the sign does not encode — a path that is unconstrained, a check that was waived, a hold path where the sign convention is the same but the meaning differs.

Zero is ambiguous. A slack of exactly 0.0000 is MET by the annotation and MET by >= 0, but a slack of -0.0000 — which a tool can print — is VIOLATED by annotation and MET by >= 0 in floating point. Rare, and the kind of rare that appears once and costs a day.

So: use the annotation when present, the sign only as a fallback, and record which one you used. That last part is Chapter 18.8's label again — a verdict whose provenance is in the output is one somebody can check.

5. What To Remember

  • Slack is the number the report exists to deliver, and it arrives in five forms.
  • Measured: the strict pattern read 2 of 5 endpoints; the tolerant one read 4, with the fifth correctly unreadable.
  • Make the annotation optional — (?:\((\w+)\))? — and capture it as \w+, not as a two-word alternation.
  • Allow a non-numeric value in the pattern: (-?[\d.]+|INFINITY).
  • Use [ \t]*, never \s*, in a /m-anchored pattern (23.1).
  • Four outcomes, not two: MET, VIOLATED, UNCONSTRAINED, UNKNOWN.
  • UNCONSTRAINED is not a pass — it usually means a missing constraint, and it is one of the most valuable findings in a report.
  • UNKNOWN is not a pass — a missing slack line means an incomplete parse or a truncated report (18.9).
  • Measured, correcting my own expectation: 'INFINITY' + 0 is Inf, not 0. Perl recognises the word.
  • Inf, Infinity, NaN all numify, case-insensitively; abc gives 0 and 12abc gives 12.
  • An Inf slack sorts as the BEST path in the design, so an unconstrained endpoint vanishes from every top-N report (23.10).
  • "Not empty" is not "safely numeric." Test the string's shape: /^-?\d+(?:\.\d+)?$/.
  • looks_like_number returns TRUE for Inf and NaN — correct, and useless here.
  • Related: '3.5ns' + 1 is 4.5 — Perl takes the leading number and drops the unit (14.7). Both failures are silent.
  • Measured: where both exist, sign and annotation agreed 2 of 2 — which is why the sign is a trap, not a rule (23.3).
  • Zero is ambiguous: a printed -0.0000 is VIOLATED by annotation and MET by >= 0.
  • Prefer the annotation, fall back to the sign, and record which you used (18.8).

Chapter 23.3 takes that fallback seriously. Finding violated endpoints from the annotation rather than from arithmetic sounds like a detail, and it decides whether a timing signoff report can be trusted.

Where this fits

Part of the Perl curriculum.