Skip to content
VLSI Mentor

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

Setup and Hold Violations

A negative slack means a check failed. It does not say which check, and that distinction decides what anyone does next: a setup violation is a path that is too slow and a hold violation is a path that is too fast, so the two are fixed in opposite directions by different people. The report says which is which on a line four above the slack — and this chapter shows what happens when that line is missing and the parser, obeying Chapter 5.1's persistence rule, reports two hold violations as setup and a design with a hold problem as having no hold checks at all.

Advanced19 min readawkPart IIISTASetup and HoldClassification

Part III · Module 10 · Chapter 10.4 · Setup and Hold Violations

1. Why This Matters

Chapter 10.3 made the slack number trustworthy. A trustworthy number still answers only half the question.

Both of a chip's two fundamental timing checks report a violation as a negative slack. They are not variants of one problem:

  • A setup violation means data arrived too late for the capturing clock edge. The path is too slow. It is fixed by making the logic faster — resizing gates, restructuring the logic, shortening the route — or by slowing the clock.
  • A hold violation means data arrived too early and overwrote the value the capturing flop was still holding from the previous cycle. The path is too fast. It is fixed by making the logic slower, usually by inserting delay buffers.

Those are opposite fixes, and a hold violation is the more alarming of the two at signoff because slowing the clock does not help it — the race is between two paths, not against a clock period, so it persists at any frequency. A combined count of "4 violations" tells a reviewer nothing about whether the design needs a frequency drop or a buffer insertion pass.

This chapter is marked Critical because the obvious classification — the sign of the slack — cannot make this distinction at all, and the correct one has a failure mode that reports the alarming case as the routine one.

2. A Six-Path Report

Azvya Education Pvt. Ltd.VLSI Mentor
sh.rpt — synthetic teaching data. The header and slack lines of a six-path report; the delay tables are removed for length, and the parser reads only these lines.
  Startpoint: u_regfile/dout_reg[3]
  Endpoint: u_alu/sum_reg[7]
  Path Group: clk_core
  Path Type: max
  slack (VIOLATED)                                   -0.018

  Startpoint: u_ctrl/state_reg[1]
  Endpoint: u_alu/flag_reg[0]
  Path Group: clk_core
  Path Type: max
  slack (MET)                                         0.067

  Startpoint: u_alu/acc_reg[0]
  Endpoint: u_alu/acc_reg[1]
  Path Group: clk_core
  Path Type: min
  slack (VIOLATED)                                   -0.041

  Startpoint: u_io/pad_reg[0]
  Endpoint: u_io/out_reg[0]
  Path Group: clk_io
  Path Type: min
  slack (VIOLATED)                                   -0.023

  Startpoint: u_dma/len_reg[4]
  Endpoint: u_dma/cnt_reg[4]
  Path Group: clk_core
  Path Type: max
  slack (VIOLATED)                                   -0.112

  Startpoint: u_io/cfg_reg[2]
  Endpoint: u_io/shift_reg[0]
  Path Group: clk_io
  Path Type: max
  slack (MET)                                         0.031

Six paths: four max and two min by the line counts, four violating and two meeting by the verdict words. The two classifications cut the set differently, and that is the chapter.

3. The Count That Cannot Be Acted On

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 + 0 < 0) neg++; else nonneg++ } END { printf "%d violating, %d passing -- but WHICH CHECK FAILED?\n", neg+0, nonneg+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4 violating, 2 passing -- but WHICH CHECK FAILED?

Four violations. Everything a reviewer needs to decide what happens next is missing from that number: whether to ask for a frequency reduction, a buffer insertion pass, both, or neither.

Worse, the number is reassuring in the wrong direction. Four setup violations on a first timing run is routine and often closes with no intervention. Two hold violations is a different conversation. The combined figure reads like the first case.

4. Path Type Is the Classification

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { print "Path Type line:", $0; print "  $3 is [" $3 "]" }' sh.rpt | head -4
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
Path Type line:   Path Type: max
  $3 is [max]
Path Type line:   Path Type: max
  $3 is [max]

max and min are delay-analysis terms rather than check names — a setup check asks whether the maximum delay still arrives in time, and a hold check asks whether the minimum delay is slow enough not to race ahead. So:

Path TypeCheckMeansFixed by
maxsetupdata too slowfaster logic, or a slower clock
minholddata too fastslower logic — delay buffers

Splitting the report on it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)") { v[chk]++; if (w[chk] == "" || $3 + 0 < w[chk] + 0) w[chk] = $3 } else m[chk]++; ty = "" } END { printf "setup   %d VIOLATED  %d MET  worst %s\n", v["setup"]+0, m["setup"]+0, (w["setup"] == "" ? "n/a" : w["setup"]); printf "hold    %d VIOLATED  %d MET  worst %s\n", v["hold"]+0, m["hold"]+0, (w["hold"] == "" ? "n/a" : w["hold"]); printf "unknown %d VIOLATED  %d MET\n", v["unknown"]+0, m["unknown"]+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup   2 VIOLATED  2 MET  worst -0.112
hold    2 VIOLATED  0 MET  worst -0.041
unknown 0 VIOLATED  0 MET

The same four violations, now actionable: two setup failures with the worst at −0.112, and two hold failures with nothing passing in that category at all. The 0 MET on the hold row is itself informative — every hold check in the report failed, which points at a systematic problem rather than two unlucky paths.

Three details in that command are deliberate and carried from earlier chapters:

  • The verdict word classifies pass/fail, not the sign — Chapter 10.3 section 5.
  • ty = "" clears the carried type once it has been used — Chapter 10.2 section 7, and section 6 below is what it prevents.
  • An unknown bucket exists and is printed even when empty, so a path that is neither max nor min cannot vanish — Chapter 6.2 section 8's reconciling total.

5. The Flow

Six-step flow for classifying timing violations by check type: read the path type, translate it to a check name, clear the stored type, classify the slack by its verdict word, accumulate per check, and report per check.01READ PATH TYPEStore max or min when the line appears, four lines above the slack.02TRANSLATE TO A CHECKmax becomes setup, min becomes hold, anything else becomes unknown.03CLEAR THE STORED TYPESet it back to empty the moment it is used, so a path with no type linecannot inherit the previous one.04CLASSIFY BY THE VERDICT WORDVIOLATED or MET comes from the word, never from the sign of the slack.05ACCUMULATE PER CHECKA violation count and a worst slack for setup and for hold separately.06REPORT PER CHECKNever a combined total; setup and hold have different fixes anddifferent owners.
Figure 1 — Classifying a violation by check. The third step is the one whose omission produces section 6: the remembered path type must be cleared once it has been used, or a path with no type line inherits the previous path's.

Steps 1 and 3 are a pair. Every later chapter in this module repeats them, and the next section is the reason.

6. One Uncleared Variable Hides a Hold Problem

Remove the two Path Type: min lines — the shape a tool-version change produces when one header's wording shifts:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep -v "Path Type: min" sh.rpt > noty.rpt
grep -c "Path Type" noty.rpt
grep -c "slack" noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
4
6

Six paths, four type lines. Now the two versions of the classifier, differing only in whether ty is cleared:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++; ty = "" } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup=4 hold=0 unknown=2
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++ } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup=6 hold=0 unknown=0

Six setup paths, no hold paths, nothing unknown. The uncleared version produces a report that is internally consistent, fully populated, and states that this design has no hold checks — on a report containing two hold violations.

The cleared version puts the same two paths in unknown, which says the honest thing: there are two paths here whose check I could not determine.

7. And It Erases the Worst Hold Slack

The count is not the only casualty. The same two versions, tracking the worst violating slack per check:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)" && (w[chk] == "" || $3 + 0 < w[chk] + 0)) w[chk] = $3; ty = "" } END { printf "cleared  setup worst %-7s hold worst %-7s unknown worst %s\n", (w["setup"]==""?"n/a":w["setup"]), (w["hold"]==""?"n/a":w["hold"]), (w["unknown"]==""?"n/a":w["unknown"]) }' noty.rpt
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)" && (w[chk] == "" || $3 + 0 < w[chk] + 0)) w[chk] = $3 } END { printf "stale    setup worst %-7s hold worst %-7s unknown worst %s\n", (w["setup"]==""?"n/a":w["setup"]), (w["hold"]==""?"n/a":w["hold"]), (w["unknown"]==""?"n/a":w["unknown"]) }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cleared  setup worst -0.112  hold worst n/a     unknown worst -0.041
stale    setup worst -0.112  hold worst n/a     unknown worst n/a

In the stale version the −0.041 hold violation leaves no trace at all. It was filed under setup, where the worst was already −0.112, so the minimum did not change. The violation was not merely mislabelled — it was absorbed.

That is the general hazard with a per-group extremum: a misfiled value that is not the group's extremum disappears entirely. The count would have shown it, which is the argument for reporting a count alongside every worst-case figure rather than the worst case alone.

8. Explicit Keys, Not Iteration

Both commands above print setup and hold by name. The alternative is to iterate:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : "hold"); c[chk]++; ty = "" } END { for (k in c) printf "[%s=%d]", k, c[k]; print "" }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[setup=4][hold=2]

That is the right answer, and it is not a reliable way to get it. POSIX defines no order for for (k in array) — the implementation may return keys in any sequence, and nothing about this run's output being in a sensible order means the next one will be. Chapter 6.1 section 9 covers the rule; here the consequence is a signoff table whose rows move.

Iteration has a second problem that matters more. It can only print categories that occurred. A report with no hold paths prints no hold row, and a missing row reads as no information when it should read as hold 0 VIOLATED. The explicit form says it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++; ty = "" } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup=2 hold=1 unknown=0

Chapter 10.1's profile.awk named its array keys for the same reason, and so does every summary in the rest of this module.

9. Common Mistakes

Counting violations without splitting by check. Setup and hold need opposite fixes — section 3.

Classifying by the sign of the slack. Both checks report a violation as negative.

Reading max as "the worst case" and min as "the best case." They name which delay corner the check uses, not a ranking.

Not clearing ty. A path with no type line inherits the previous one, and hold violations become setup — section 6.

Reporting only a worst slack per check. A misfiled value that is not the extremum vanishes — section 7.

Omitting the unknown bucket. It is the only thing that distinguishes "no hold violations" from "I could not tell."

Using for (k in c) for the summary. No defined order, and absent categories print no row — section 8.

Expecting a frequency reduction to fix a hold violation. It is a race between two paths and does not depend on the period.

10. Exercises

Exercise 1 — Split the counts by check

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)") v[chk]++; else m[chk]++; ty = "" } END { printf "setup   %d VIOLATED  %d MET\nhold    %d VIOLATED  %d MET\nunknown %d VIOLATED  %d MET\n", v["setup"]+0, m["setup"]+0, v["hold"]+0, m["hold"]+0, v["unknown"]+0, m["unknown"]+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup   2 VIOLATED  2 MET
hold    2 VIOLATED  0 MET
unknown 0 VIOLATED  0 MET

Exercise 2 — Name every violating path with its check

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { if ($2 == "(VIOLATED)") printf "%-6s %-24s %8s\n", (ty == "max" ? "setup" : (ty == "min" ? "hold" : "?")), s, $3; s = ""; ty = "" }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup  u_regfile/dout_reg[3]      -0.018
hold   u_alu/acc_reg[0]           -0.041
hold   u_io/pad_reg[0]            -0.023
setup  u_dma/len_reg[4]           -0.112

Four violations, each with the check that failed and the register it starts from — the form a reviewer can hand to two different people.

Exercise 3 — Debug a report claiming no hold checks

A timing summary reports six setup paths and no hold paths. The design has two hold violations.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep -v "Path Type: min" sh.rpt > noty.rpt
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++ } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
grep -c "Path Type" noty.rpt
grep -c "slack" noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup=6 hold=0 unknown=0
4
6

Six slack lines and four Path Type lines. Two paths have no type, and the uncleared ty gave them the previous path's.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++; ty = "" } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup=4 hold=0 unknown=2

Two paths are now reported as unclassified rather than as setup. Note what the fix does and does not do: it does not recover the two hold classifications, because the information is not in the file. It makes their absence visible, which is all a parser can honestly do.

Exercise 4 — Debug a vanished worst slack

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)" && (w[chk] == "" || $3 + 0 < w[chk] + 0)) w[chk] = $3 } END { printf "worst setup %s, worst hold %s\n", (w["setup"]==""?"n/a":w["setup"]), (w["hold"]==""?"n/a":w["hold"]) }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst setup -0.112, worst hold n/a

The -0.041 hold violation is not in either figure. It was filed under setup, where -0.112 was already worse, so the comparison never changed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)") { n[chk]++; if (w[chk] == "" || $3 + 0 < w[chk] + 0) w[chk] = $3 }; ty = "" } END { printf "setup %d violation(s), worst %s\nhold  %d violation(s), worst %s\nunknown %d violation(s), worst %s\n", n["setup"]+0, (w["setup"]==""?"n/a":w["setup"]), n["hold"]+0, (w["hold"]==""?"n/a":w["hold"]), n["unknown"]+0, (w["unknown"]==""?"n/a":w["unknown"]) }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup 2 violation(s), worst -0.112
hold  0 violation(s), worst n/a
unknown 2 violation(s), worst -0.041

Clearing ty and reporting a count beside every extremum makes both facts visible: two violations are unclassified, and the worst of them is −0.041.

Exercise 5 — Engineering application

The per-check signoff summary, with the preconditions from Chapter 10.2 section 8 and the verdict discipline from Chapter 10.3.

Azvya Education Pvt. Ltd.VLSI Mentor
checks.awk — synthetic teaching script.
# checks.awk — per-check violation summary from a timing report.
#
# Usage:  awk -f checks.awk sh.rpt
# Exit:   0 no violations
#         1 violations present
#         2 the report cannot be classified (unknown paths, or counts disagree)

/^ *Path Type:/ { ty = $3; ntype++ }

/^ *slack/ {
    nslack++
    chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown"))
    if ($2 == "(VIOLATED)") {
        v[chk]++
        if (w[chk] == "" || $3 + 0 < w[chk] + 0) w[chk] = $3
    } else if ($2 == "(MET)") m[chk]++
    else other[chk]++
    ty = ""
}

END {
    printf "%-8s %10s %10s %12s\n", "check", "violating", "meeting", "worst slack"
    split("setup hold unknown", order, " ")
    for (i = 1; i <= 3; i++) {
        c = order[i]
        printf "%-8s %10d %10d %12s\n", c, v[c] + 0, m[c] + 0, (w[c] == "" ? "-" : sprintf("%.3f", w[c]))
    }
    printf "# %d slack line(s), %d Path Type line(s)\n", nslack + 0, ntype + 0

    if (ntype != nslack) { printf "ERROR: %d type line(s) for %d path(s) -- classification unreliable\n", ntype, nslack > "/dev/stderr"; exit 2 }
    if (v["unknown"] + m["unknown"] + other["unknown"] > 0) { print "ERROR: unclassified path(s) present" > "/dev/stderr"; exit 2 }
    if (v["setup"] + v["hold"] > 0) exit 1
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f checks.awk sh.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
check     violating    meeting  worst slack
setup             2          2       -0.112
hold              2          0       -0.041
unknown           0          0            -
# 6 slack line(s), 6 Path Type line(s)
status: 1

split("setup hold unknown", order, " ") fixes the row order without hardcoding three printf calls — Chapter 6.1 section 10's idiom for iterating an array in a defined sequence, and the answer to section 8's problem.

The broken report is refused rather than summarised:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f checks.awk noty.rpt 2>&1 >/dev/null
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ERROR: 4 type line(s) for 6 path(s) -- classification unreliable
status: 2

And the three-path report from Chapter 10.1, for continuity:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f checks.awk timing.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
check     violating    meeting  worst slack
setup             1          1       -0.018
hold              1          0       -0.023
unknown           0          0            -
# 3 slack line(s), 3 Path Type line(s)
status: 1

One setup violation and one hold violation — and the hold one is the numerically worse of the two, which Chapter 10.5 shows is exactly the comparison a single worst-path figure gets wrong.

Exercise 6 — Challenge: show that the stale-type bug cannot be caught by a total

Exercise 3's broken parser reports setup=6 hold=0. Show that the slack-line count, the violation count and the worst setup slack are all unchanged by the bug, so none of them can detect it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '%-10s ' 'cleared'; awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { n++; if ($2 == "(VIOLATED)") v++; chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)" && chk == "setup" && (ws == "" || $3 + 0 < ws + 0)) ws = $3; ty = "" } END { printf "paths=%d violations=%d worst-setup=%s\n", n+0, v+0, ws }' noty.rpt
printf '%-10s ' 'stale'; awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { n++; if ($2 == "(VIOLATED)") v++; chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); if ($2 == "(VIOLATED)" && chk == "setup" && (ws == "" || $3 + 0 < ws + 0)) ws = $3 } END { printf "paths=%d violations=%d worst-setup=%s\n", n+0, v+0, ws }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cleared    paths=6 violations=4 worst-setup=-0.112
stale      paths=6 violations=4 worst-setup=-0.112

Identical on all three aggregates. Six paths, four violations, worst setup −0.112, from a correct parser and a broken one.

The only figure that moves is the per-category breakdown, which is the thing the bug corrupts:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf '%-10s ' 'cleared'; awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++; ty = "" } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
printf '%-10s ' 'stale'; awk '/^ *Path Type:/ { ty = $3 } /^ *slack/ { chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown")); c[chk]++ } END { printf "setup=%d hold=%d unknown=%d\n", c["setup"]+0, c["hold"]+0, c["unknown"]+0 }' noty.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cleared    setup=4 hold=0 unknown=2
stale      setup=6 hold=0 unknown=0

So the detection has to be structural, not statistical: compare the number of Path Type lines against the number of slack lines, which is what checks.awk does and what Chapter 10.2 section 8 established as the module's precondition. Four type lines for six paths is a fact about the file that no amount of reading the output would reveal.

This is Chapter 10.3 section 6's lesson in a second setting — an aggregate check cannot find an error that does not change the aggregate — and Chapter 9.2 section 7's in a third: the check has to be placed where the information still exists.

11. Summary

  • Setup and hold both report a violation as a negative slack and are fixed in opposite directions.
  • A hold violation does not go away when the clock slows down. It is a race between paths, not against a period.
  • A combined violation count is not actionable — section 3.
  • Path Type is the classifier: max is setup, min is hold.
  • Clear ty once it is used. Without it, hold violations are reported as setup and the design appears to have no hold checks — section 6.
  • A misfiled value that is not its group's extremum vanishes entirely — section 7. Report a count beside every worst case.
  • Keep an unknown bucket and print it even when empty. It distinguishes "none" from "could not tell."
  • Name array keys, or order them with split. for (k in c) has no defined order and cannot print a category that did not occur.
  • The stale-type bug changes no aggregate — path count, violation count and worst setup slack are all identical. Detection is structural: compare the Path Type count against the slack count.

Violations are now classified. Chapter 10.5 — Worst Timing Paths takes the ranking, and starts from the observation this chapter's last output makes: the numerically worst path in timing.rpt is a hold check, so a single "worst slack" figure across the whole report compares two quantities that are not comparable.

Where this fits

Part of the awk curriculum.