Skip to content
VLSI Mentor

awk Part III · Module 10 · Timing and STA Report Analysis

Slack Extraction

A slack line carries two statements of the same fact: a word — MET or VIOLATED — and a number whose sign ought to say the same thing. Almost every parser trusts the number, because a number is what you want to sort and compare. This chapter shows two real report shapes where the number and the word disagree, in opposite directions, and one where a sign-based count produces exactly the right total while being wrong about which paths failed. The report's own rounding is what destroys the sign, and the word is what survives it.

Advanced18 min readawkPart IIISTASlackNumeric Comparison

Part III · Module 10 · Chapter 10.3 · Slack Extraction

1. Why This Matters

The slack is the answer. Everything upstream — the path, the clock, the delay table — is context for one number and one word.

Chapter 10.2 made paths into records. This chapter takes the number, and it is marked Critical for a reason that is not obvious: the obvious extraction is wrong on report shapes that occur routinely, and it is wrong in a way that survives a sanity check on the totals. A timing signoff script that counts violations by the sign of the slack can report the right count of failures and the wrong set of failures.

2. The Slack Line

This chapter continues on timing.rpt, the three-path synthetic report listed in full in Chapter 10.1 section 2 — the whole module shares one fixture so that a result in a later chapter can be compared against an earlier one. Section 3 adds a second fixture for the shapes timing.rpt does not contain.

Three fields on a well-formed line:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { printf "NF=%d |$1=%s| |$2=%s| |$3=%s| |$NF=%s|\n", NF, $1, $2, $3, $NF }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
NF=3 |$1=slack| |$2=(VIOLATED)| |$3=-0.018| |$NF=-0.018|
NF=3 |$1=slack| |$2=(MET)| |$3=0.067| |$NF=0.067|
NF=3 |$1=slack| |$2=(VIOLATED)| |$3=-0.023| |$NF=-0.023|

$1 is the marker, $2 is the verdict in parentheses, $3 is the value. On these lines $3 and $NF are the same field, which is exactly the coincidence that section 4 breaks.

The extraction every later chapter in this module uses:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ && NF >= 3 { printf "%-12s %8.3f\n", $2, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
(VIOLATED)     -0.018
(MET)           0.067
(VIOLATED)     -0.023

Note the NF >= 3 guard in the pattern. Section 4 is the argument for it.

3. The Six Shapes a Slack Line Takes

A real report does not only emit the well-formed case. This fixture collects every shape:

Azvya Education Pvt. Ltd.VLSI Mentor
odd.rpt — synthetic teaching data. Six slack lines, every shape a real report emits.
  slack (MET)                                         0.067
  slack (VIOLATED)                                   -0.018
  slack (MET)                                        1e+30
  slack (INFEASIBLE)
  slack (MET)                                         0.000
  slack (VIOLATED)                                   -0.000
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ printf "NF=%d  verdict=%-14s value=%s\n", NF, $2, (NF >= 3 ? $3 : "<none on the line>") }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
NF=3  verdict=(MET)          value=0.067
NF=3  verdict=(VIOLATED)     value=-0.018
NF=3  verdict=(MET)          value=1e+30
NF=2  verdict=(INFEASIBLE)   value=<none on the line>
NF=3  verdict=(MET)          value=0.000
NF=3  verdict=(VIOLATED)     value=-0.000

What each one is:

  • 0.067 / -0.018 — the ordinary cases.
  • 1e+30 — an unconstrained path. The timing tool had no constraint to check against, so it reports a sentinel. The path is not passing; it is unmeasured.
  • (INFEASIBLE) with no number — the tool could not compute a slack at all. NF is 2.
  • 0.000 — exactly met, or met by less than the display precision.
  • -0.000 — a violation whose magnitude is smaller than the display precision. Section 5 is about this line.

Everything in this chapter follows from rows 3, 4 and 6 — the three that the obvious code gets wrong.

4. $NF on a Line With No Value

The guard first, because it is cheap and it explains itself:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { printf "%-14s NF=%d -> %s\n", $2, NF, (NF >= 3 ? "has a value: " $3 : "NO VALUE -- $NF is the verdict, not a number") }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
(MET)          NF=3 -> has a value: 0.067
(VIOLATED)     NF=3 -> has a value: -0.018
(MET)          NF=3 -> has a value: 1e+30
(INFEASIBLE)   NF=2 -> NO VALUE -- $NF is the verdict, not a number
(MET)          NF=3 -> has a value: 0.000
(VIOLATED)     NF=3 -> has a value: -0.000

On the (INFEASIBLE) line, $NF is $2. The field that usually holds the value now holds the verdict word, and nothing about the code notices.

Then the comparison. $NF < 0 on all six:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '{ printf "NF=%d $NF=%-14s | $NF<0 -> %-5s | $NF+0<0 -> %-5s | ($NF+0)=%s\n", NF, $NF, ($NF < 0 ? "TRUE" : "false"), ($NF + 0 < 0 ? "TRUE" : "false"), $NF + 0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
NF=3 $NF=0.067          | $NF<0 -> false | $NF+0<0 -> false | ($NF+0)=0.067
NF=3 $NF=-0.018         | $NF<0 -> TRUE  | $NF+0<0 -> TRUE  | ($NF+0)=-0.018
NF=3 $NF=1e+30          | $NF<0 -> false | $NF+0<0 -> false | ($NF+0)=1.00000000000000001988462483866e+30
NF=2 $NF=(INFEASIBLE)   | $NF<0 -> TRUE  | $NF+0<0 -> false | ($NF+0)=0
NF=3 $NF=0.000          | $NF<0 -> false | $NF+0<0 -> false | ($NF+0)=0
NF=3 $NF=-0.000         | $NF<0 -> false | $NF+0<0 -> false | ($NF+0)=0

$NF < 0 is TRUE for (INFEASIBLE). An unconstrained path is counted as a timing violation.

Why: (INFEASIBLE) is not a numeric string, so awk cannot apply the numeric-comparison rule from Chapter 5.2 section 7 and falls back to comparing it as a string against "0". ( is character 40 and 0 is character 48, so ( sorts first and the comparison is true.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { print "(INFEASIBLE) < 0  ->", ("(INFEASIBLE)" < 0 ? "TRUE" : "false") }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
(INFEASIBLE) < 0  -> TRUE

Adding + 0 fixes this particular line — $NF + 0 < 0 is false — because it forces the numeric interpretation and a non-numeric string converts to 0. That is Chapter 8.5's rule, and it is necessary but not sufficient, because the next section breaks the numeric version too.

5. The Report Rounds Away the Sign

Row 6 of the fixture is slack (VIOLATED) -0.000. The word says the path fails. The number does not:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { s = sprintf("%.3f", -0.0004); printf "re-read as text: [%s]  and [%s]+0 < 0 -> %s\n", s, s, (s + 0 < 0 ? "TRUE" : "false") }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
re-read as text: [-0.000]  and [-0.000]+0 < 0 -> false

This is not a contrived line. It is what a report prints when the slack is a genuine violation smaller than the display precision:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { for (i = 1; i <= 4; i++) { v = -0.0004 / i; printf "true slack %-12.8f prints as %.3f   sign of printed value: %s\n", v, v, (v + 0 < 0 ? "negative" : "NOT negative") } }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
true slack -0.00040000  prints as -0.000   sign of printed value: negative
true slack -0.00020000  prints as -0.000   sign of printed value: negative
true slack -0.00013333  prints as -0.000   sign of printed value: negative
true slack -0.00010000  prints as -0.000   sign of printed value: negative

Four genuinely negative slacks. All four print as -0.000. Inside the tool the sign is intact — that is why every one of those rows reads negative. The loss happens at the %.3f, in the report, before awk ever sees the line. By the time the text reaches a parser the sign is a minus character in front of a zero, and reading it as a number discards it.

The verdict word did not go through that rounding. The tool wrote VIOLATED from the value it still had.

6. Three Methods, Three Counts

Count the violations in odd.rpt three ways:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '%-26s ' 'naive $NF < 0'; awk '/^ *slack/ { if ($NF < 0) v++ } END { printf "%d violating\n", v+0 }' odd.rpt
printf '%-26s ' 'numeric $3 + 0 < 0'; awk '/^ *slack/ { if ($3 + 0 < 0) v++ } END { printf "%d violating\n", v+0 }' odd.rpt
printf '%-26s ' 'verdict word'; awk '/^ *slack/ { if ($2 == "(VIOLATED)") v++ } END { printf "%d violating\n", v+0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
naive $NF < 0              2 violating
numeric $3 + 0 < 0         1 violating
verdict word               2 violating

Read that carefully, because it is the chapter's sharpest result.

The numeric method — the one that looked like the fix in section 4 — reports 1. It misses the -0.000 violation entirely.

The naive method reports 2, which is the correct total. It is also wrong about which paths those are:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { n++; a = ($NF < 0); b = ($3 + 0 < 0); c = ($2 == "(VIOLATED)"); printf "row %d  %-14s naive=%-3s numeric=%-3s word=%-3s [%s]\n", n, $2, (a?"YES":"no"), (b?"YES":"no"), (c?"YES":"no"), (a == c && b == c ? "all agree" : "A METHOD IS WRONG HERE") }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
row 1  (MET)          naive=no  numeric=no  word=no  [all agree]
row 2  (VIOLATED)     naive=YES numeric=YES word=YES [all agree]
row 3  (MET)          naive=no  numeric=no  word=no  [all agree]
row 4  (INFEASIBLE)   naive=YES numeric=no  word=no  [A METHOD IS WRONG HERE]
row 5  (MET)          naive=no  numeric=no  word=no  [all agree]
row 6  (VIOLATED)     naive=no  numeric=no  word=YES [A METHOD IS WRONG HERE]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { n++; a = ($NF < 0); c = ($2 == "(VIOLATED)"); if (a != c) { d++; printf "row %d disagrees: naive says %s, the tool says %s\n", n, (a ? "VIOLATED" : "MET"), (c ? "VIOLATED" : "not VIOLATED") } } END { printf "%d row(s) disagree, yet both totals are 2\n", d+0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
row 4 disagrees: naive says VIOLATED, the tool says not VIOLATED
row 6 disagrees: naive says MET, the tool says VIOLATED
2 row(s) disagree, yet both totals are 2

Two errors in opposite directions, cancelling to the right total. The naive method invents a violation on row 4 and loses one on row 6. Any check that compares counts — against the tool's own summary, against last night's run, against a reviewer's expectation — passes.

And on the well-formed report all three methods agree, which is why this is never found during development:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '%-26s ' 'naive $NF < 0'; awk '/^ *slack/ { if ($NF < 0) v++ } END { printf "%d violating\n", v+0 }' timing.rpt
printf '%-26s ' 'numeric $3 + 0 < 0'; awk '/^ *slack/ { if ($3 + 0 < 0) v++ } END { printf "%d violating\n", v+0 }' timing.rpt
printf '%-26s ' 'verdict word'; awk '/^ *slack/ { if ($2 == "(VIOLATED)") v++ } END { printf "%d violating\n", v+0 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
naive $NF < 0              2 violating
numeric $3 + 0 < 0         2 violating
verdict word               2 violating

Three methods, one answer, on the input you test against. The divergence needs a report containing an unconstrained path or a sub-precision violation — which is to say, a real one.

7. Printing the Number Back Out

Two formatting hazards, both from the unconstrained sentinel:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { printf "%%s->[%s]  %%.3f->[%.3f]  %%g->[%g]\n", $NF + 0, $NF + 0, $NF + 0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
%s->[0.067]  %.3f->[0.067]  %g->[0.067]
%s->[-0.018]  %.3f->[-0.018]  %g->[-0.018]
%s->[1.00000000000000001988462483866e+30]  %.3f->[1000000000000000019884624838656.000]  %g->[1e+30]
%s->[0]  %.3f->[0.000]  %g->[0]
%s->[0]  %.3f->[0.000]  %g->[0]
%s->[0]  %.3f->[0.000]  %g->[0]

%s of 1e+30 is 31 significant digits, and %.3f of it is 34 characters. Either one destroys a column in a report.

What makes that surprising is that awk's output formats are set to six significant digits:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { x = 1e+30; print "print:", x; printf "concat: %s\n", x ""; print "CONVFMT=" CONVFMT, "OFMT=" OFMT }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print: 1.00000000000000001988462483866e+30
concat: 1.00000000000000001988462483866e+30
CONVFMT=%.6g OFMT=%.6g

OFMT and CONVFMT are both %.6g and neither applied. The reason is the rule Chapter 5.2 section 9 introduced: those formats govern non-integral values only. 1e+30 has no fractional part, so awk prints it as an integer — and at that magnitude an integer needs 31 digits.

The second hazard is quieter. %s of a 0.000 slack prints 0, so a report that showed three decimals comes out with none. %.3f preserves what the input showed.

Both are solved by formatting explicitly and special-casing the sentinel, which section 8 does.

8. The Robust Extractor

Azvya Education Pvt. Ltd.VLSI Mentor
slack.awk — synthetic teaching script.
# slack.awk — classify every slack line, word-first.
#
# Usage:  awk -f slack.awk timing.rpt
# Exit:   0 clean   1 a word/sign disagreement   2 a line shape it cannot read

BEGIN { UNCONSTRAINED = 1e29 }

/^ *slack/ {
    n++
    word = $2
    gsub(/[()]/, "", word)

    if (NF < 3) { shape++; printf "%-4d %-14s %-10s [no value on the line]\n", n, word, "-"; next }

    val = $3 + 0
    if (word == "VIOLATED")   cls = "VIOLATED"
    else if (word == "MET")   cls = (val > UNCONSTRAINED ? "UNCONSTRAINED" : "MET")
    else                      { shape++; cls = "UNKNOWN" }

    disagree = (word == "VIOLATED") != (val < 0)
    if (disagree && cls != "UNCONSTRAINED") bad++

    printf "%-4d %-14s %-10s %s\n", n, cls, (val > UNCONSTRAINED ? "1e+30" : sprintf("%.3f", val)),
           (disagree && cls != "UNCONSTRAINED" ? "[word and sign disagree -- trust the word]" : "[ok]")
}

END {
    printf "# %d slack line(s), %d disagreement(s), %d unreadable shape(s)\n", n + 0, bad + 0, shape + 0
    if (shape) exit 2
    if (bad) exit 1
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f slack.awk timing.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1    VIOLATED       -0.018     [ok]
2    MET            0.067      [ok]
3    VIOLATED       -0.023     [ok]
# 3 slack line(s), 0 disagreement(s), 0 unreadable shape(s)
status: 0

Three clean paths, and nothing in the way on a well-formed report. The same script on the awkward fixture:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f slack.awk odd.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1    MET            0.067      [ok]
2    VIOLATED       -0.018     [ok]
3    UNCONSTRAINED  1e+30      [ok]
4    INFEASIBLE     -          [no value on the line]
5    MET            0.000      [ok]
6    VIOLATED       0.000      [word and sign disagree -- trust the word]
# 6 slack line(s), 1 disagreement(s), 1 unreadable shape(s)
status: 2

Every one of the three awkward rows is named rather than silently miscounted:

  • Row 3 is UNCONSTRAINED, not a passing path — the sentinel is recognised by magnitude, and its value is printed as 1e+30 rather than 31 digits.
  • Row 4 has no value, and the NF < 3 branch reports it instead of comparing a word against zero.
  • Row 6 is classified VIOLATED from the word, prints 0.000 because that is honestly what the rounded value is, and flags the disagreement.

The exit status distinguishes the two problems: 1 means the numbers and the words disagree somewhere, so the magnitudes are suspect; 2 means a line shape the script does not understand, so nothing it produced should be trusted.

9. Common Mistakes

$NF for the value. On a line with no number, $NF is the verdict word — section 4.

$NF < 0 without + 0. A non-numeric field compares as a string, and (INFEASIBLE) sorts below "0", so it reads as a violation.

Trusting the sign instead of the word. -0.000 is a violation the report rounded — section 5.

Checking that the totals agree. The naive method's two errors cancel — section 6.

Treating 1e+30 as a huge pass. It is an unconstrained path; it was never checked.

%s on a slack. 0.000 prints as 0 and 1e+30 prints with 31 digits — section 7.

Assuming OFMT limits the width. It governs non-integral values only, and the sentinel is integral.

Dropping the other bucket when counting verdicts. A report with INFEASIBLE lines silently loses them unless something counts what is neither MET nor VIOLATED — Chapter 6.2 section 8's point about a total that must reconcile.

10. Exercises

Exercise 1 — Extract the verdict and the value

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ && NF >= 3 { printf "%-12s %8.3f\n", $2, $3 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
(VIOLATED)     -0.018
(MET)           0.067
(VIOLATED)     -0.023

Exercise 2 — Count by the word, with an other bucket

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { w = $2; gsub(/[()]/, "", w); c[w]++ } END { printf "MET=%d VIOLATED=%d INFEASIBLE=%d\n", c["MET"]+0, c["VIOLATED"]+0, c["INFEASIBLE"]+0 }' timing.rpt
awk '/^ *slack/ { w = $2; gsub(/[()]/, "", w); c[w]++ } END { printf "MET=%d VIOLATED=%d INFEASIBLE=%d\n", c["MET"]+0, c["VIOLATED"]+0, c["INFEASIBLE"]+0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
MET=1 VIOLATED=2 INFEASIBLE=0
MET=3 VIOLATED=2 INFEASIBLE=1

gsub(/[()]/, "", w) strips the parentheses so the key is a plain word — Chapter 4.4's substitution, used on a copy so $2 itself is untouched.

Exercise 3 — Debug a violation reported as a pass

A signoff script reports zero violations on a report containing one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '  slack (VIOLATED)                                   -0.000\n' > tiny.rpt
awk '/^ *slack/ { if ($3 + 0 < 0) v++ } END { printf "violations found: %d\n", v+0 }' tiny.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
violations found: 0

The sign-based test returns 0 on a line whose own verdict reads VIOLATED. The report printed the slack at three decimals, and the true value was smaller than that, so -0.000 is all the text has left.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($2 == "(VIOLATED)") v++ } END { printf "violations found: %d\n", v+0 }' tiny.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
violations found: 1

Exercise 4 — Debug an unconstrained path reported as a violation

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '  slack (INFEASIBLE)\n' > inf.rpt
awk '/^ *slack/ { printf "$NF is [%s], and $NF < 0 -> %s\n", $NF, ($NF < 0 ? "TRUE" : "false") }' inf.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$NF is [(INFEASIBLE)], and $NF < 0 -> TRUE

$NF is the verdict word because the line has only two fields, and a non-numeric string compares lexically against "0" — ( sorts before 0.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { printf "NF=%d -> %s\n", NF, (NF < 3 ? "no value on this line, nothing to compare" : ($3 + 0 < 0 ? "violation" : "pass")) }' inf.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
NF=2 -> no value on this line, nothing to compare

Exercise 5 — Engineering application

Produce the slack table a signoff review actually reads: one row per path, the verdict from the word, the magnitude from the number, and a refusal if the two ever disagree.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Group:/ { g = $3 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { if (NF < 3) { printf "%-24s %-9s %-4s %10s  [NO VALUE]\n", s, g, ty, "-"; s = ""; next } w = $2; gsub(/[()]/, "", w); printf "%-24s %-9s %-4s %10.3f  [%s]\n", s, g, ty, $3, w; s = "" }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_regfile/dout_reg[3]    clk_core  max      -0.018  [VIOLATED]
u_ctrl/state_reg[1]      clk_core  max       0.067  [MET]
u_io/pad_reg[0]          clk_io    min      -0.023  [VIOLATED]

That is Chapter 10.2's state-carrying pattern with this chapter's extraction dropped into the slack rule — the identity from the header lines, the verdict from the word, the magnitude from the number, and the s = "" that keeps a missing header from mispairing.

The same command run through slack.awk first, to get the verdict counts and the exit status a gate needs:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f slack.awk timing.rpt | tail -1
awk -f slack.awk odd.rpt | tail -1
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# 3 slack line(s), 0 disagreement(s), 0 unreadable shape(s)
# 6 slack line(s), 1 disagreement(s), 1 unreadable shape(s)

One report is safe to summarise and the other is not, and the difference is stated rather than averaged away.

Exercise 6 — Challenge: show that a count check cannot find the bug

A reviewer proposes validating the naive parser by comparing its violation count against the verdict words. Show that the check passes on odd.rpt while the parser is wrong about two paths.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($NF < 0) naive++; if ($2 == "(VIOLATED)") word++ } END { printf "naive=%d word=%d -> %s\n", naive+0, word+0, (naive == word ? "CHECK PASSES" : "MISMATCH") }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
naive=2 word=2 -> CHECK PASSES

The check passes. Now the same comparison made per row rather than in aggregate:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { n++; if (($NF < 0) != ($2 == "(VIOLATED)")) { d++; printf "row %d: naive and word disagree\n", n } } END { printf "rows checked %d, rows disagreeing %d\n", n+0, d+0 }' odd.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
row 4: naive and word disagree
row 6: naive and word disagree
rows checked 6, rows disagreeing 2

Two of six rows are wrong and the aggregate check saw nothing. The errors are in opposite directions, so they cancel in the sum.

The general lesson, and the reason this chapter is marked Critical: a check on a total can only detect errors that do not cancel. Comparing two methods row by row costs one extra condition and detects every disagreement, including the ones that happen to balance.

This is the same argument as Chapter 9.2 section 7, where comparing a parser's record count against UVM's own summary caught a pattern error only because the error was one-directional; and Chapter 8.7 section 9, where a per-case assertion found what a pass-rate could not. Aggregate first, verify per row.

11. Summary

  • A slack line is slack + verdict + value, and on a well-formed line $3 and $NF are the same field.
  • Guard NF >= 3. A line with no value makes $NF the verdict word.
  • $NF < 0 is TRUE for (INFEASIBLE) — a lexical comparison, because ( sorts before "0". An unconstrained path becomes a violation.
  • + 0 forces the numeric reading, which fixes that line and not the next one.
  • -0.000 is a violation the report rounded. The sign was intact inside the tool and lost at the %.3f.
  • The verdict word is authoritative; the number is evidence. Classify by the word, measure with the number, report disagreements.
  • 1e+30 is an unconstrained path, not a large pass. It was never checked.
  • Three counting methods gave 2, 1 and 2 on the same six lines — and the naive 2 was right in total while wrong on two rows.
  • %s of a slack loses precision and %s of the sentinel prints 31 digits, because OFMT governs non-integral values only.
  • A check on a total cannot find errors that cancel. Compare per row.

The number is now trustworthy. Chapter 10.4 — Setup and Hold Violations uses it, and shows why classifying a violation by the sign of its slack puts setup and hold failures in the same bucket when they have different causes, different owners and different fixes.

Where this fits

Part of the awk curriculum.