Skip to content
VLSI Mentor

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

Worst Timing Paths

The worst slack in a report is the number everyone quotes, and on its own it is close to useless: Chapter 10.4 showed setup and hold need opposite fixes, so a single minimum taken across both compares quantities that are not comparable. This chapter builds the ranking properly — per check, per clock, with the path named — and then shows the comparison that goes wrong most often in practice. A slack threshold written in quotes is a string comparison, and on the six-path report it flags three paths that pass while missing the only one that fails.

Advanced18 min readawkPart IIISTAMinimumSorting

Part III · Module 10 · Chapter 10.5 · Worst Timing Paths

1. Why This Matters

"What's the worst slack?" is the first question asked about any timing run, and a single number is the wrong shape of answer.

Chapter 10.4 established why: setup and hold are different checks with opposite remedies. A minimum taken across both tells you the magnitude of the largest failure and not which of two unrelated problems it belongs to — and as this chapter's section 4 shows, which check owns the worst path changes from report to report, so you cannot assume.

The second half of the chapter is about the comparison itself. Ranking means comparing, and comparing a field against a threshold is the single place in awk where a plausible-looking expression silently does something else.

2. The Running Minimum

The idiom, on the three-path report from Chapter 10.1:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if (w == "" || $3 + 0 < w + 0) w = $3 } END { printf "worst slack: %s\n", w }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack: -0.023

Two conditions, and both are load-bearing:

  • w == "" seeds the minimum on the first record. Section 3 is what happens without it.
  • $3 + 0 < w + 0 forces a numeric comparison. Here it is belt-and-braces — section 6 explains exactly when it is required and when it is not — but it costs nothing and it documents the intent.

Keeping the path name alongside the value needs Chapter 10.2's carried state:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) { w = $3; ws = s } s = "" } END { printf "worst slack %s on %s\n", w, ws }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack -0.023 on u_io/pad_reg[0]

Note that ws is updated inside the comparison, so the name and the number can never disagree. Updating them in separate rules is how a report ends up quoting one path's slack against another path's name.

3. An All-Passing Report Reports Zero

Drop the w == "" guard and give it a report with nothing wrong:

Azvya Education Pvt. Ltd.VLSI Mentor
allpass.rpt — synthetic teaching data. Two paths, both meeting.
  slack (MET)   0.067
  slack (MET)   0.031
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 + 0 < w + 0) w = $3 } END { printf "worst slack: [%s]  (as a number %.3f)\n", w, w + 0 }' allpass.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack: []  (as a number 0.000)

Empty, and zero if anything formats it as a number. An uninitialised variable is the empty string, which converts to 0 — Chapter 5.1 section 5 — and no positive slack is below zero, so the comparison never fires and w is never assigned.

The guard gives the right answer:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if (w == "" || $3 + 0 < w + 0) w = $3 } END { printf "worst slack: [%s]\n", w }' allpass.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack: [0.031]

4. The Worst Path Overall Belongs to One Check

Which check owns the worst path in timing.rpt:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) { w = $3; ws = s; wt = ty } s = ""; ty = "" } END { printf "worst overall: %s on %s (a %s check)\n", w, ws, (wt == "max" ? "SETUP" : "HOLD") }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst overall: -0.023 on u_io/pad_reg[0] (a HOLD check)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { c = (ty == "max" ? "setup" : "hold"); if (w[c] == "" || $3 + 0 < w[c] + 0) { w[c] = $3; p[c] = s } s = ""; ty = "" } END { printf "worst setup:   %s on %s\nworst hold:    %s on %s\n", w["setup"], p["setup"], w["hold"], p["hold"] }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst setup:   -0.018 on u_regfile/dout_reg[3]
worst hold:    -0.023 on u_io/pad_reg[0]

-0.023 is the worst number in the report and it is a hold check. A summary quoting "worst slack −0.023" to a team working on setup closure is quoting a figure from a different problem — and the worst setup path, the one that work would act on, is −0.018 and does not appear.

Now the six-path report from Chapter 10.4:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) { w = $3; ws = s; wt = ty } s = ""; ty = "" } END { printf "worst overall: %s on %s (a %s check)\n", w, ws, (wt == "max" ? "SETUP" : "HOLD") }' sh.rpt
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { c = (ty == "max" ? "setup" : "hold"); if (w[c] == "" || $3 + 0 < w[c] + 0) { w[c] = $3; p[c] = s } s = ""; ty = "" } END { printf "worst setup:   %s on %s\nworst hold:    %s on %s\n", w["setup"], p["setup"], w["hold"], p["hold"] }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst overall: -0.112 on u_dma/len_reg[4] (a SETUP check)
worst setup:   -0.112 on u_dma/len_reg[4]
worst hold:    -0.041 on u_alu/acc_reg[0]

The two reports disagree about which check owns the worst path. On timing.rpt it is hold; on sh.rpt it is setup. There is nothing to generalise and no sensible default — the only way to know is to split, which is why a per-check minimum is the correct primitive and the combined one is a derived curiosity.

5. Per Check, Per Clock

A design has more than one clock, and a path's slack is only comparable against paths checked against the same clock at the same corner. The signoff shape is therefore one row per (check, clock):

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { g = $3 } /^ *Path Type:/ { ty = $3 } /^ *Startpoint:/ { s = $2 } /^ *slack/ { c = (ty == "max" ? "setup" : "hold"); k = c "/" g; n[k]++; if ($2 == "(VIOLATED)") v[k]++; if (w[k] == "" || $3 + 0 < w[k] + 0) { w[k] = $3; p[k] = s } s=""; ty=""; g="" } END { split("setup/clk_core setup/clk_io hold/clk_core hold/clk_io", o, " "); printf "%-18s %6s %6s %9s  %s\n", "check/clock", "paths", "viol", "worst", "worst path"; for (i=1;i<=4;i++) { k=o[i]; printf "%-18s %6d %6d %9s  %s\n", k, n[k]+0, v[k]+0, (w[k]==""?"-":w[k]), (p[k]==""?"-":p[k]) } }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
check/clock         paths   viol     worst  worst path
setup/clk_core          3      2    -0.112  u_dma/len_reg[4]
setup/clk_io            1      0     0.031  u_io/cfg_reg[2]
hold/clk_core           1      1    -0.041  u_alu/acc_reg[0]
hold/clk_io             1      1    -0.023  u_io/pad_reg[0]

Four rows, and each one is a question with an owner. setup/clk_core has two violations out of three paths and the worst at −0.112 — the core frequency or the core logic. hold/clk_io has one path and it violates — an I/O race. setup/clk_io is clean.

Three carried-over disciplines in that command, all from earlier chapters:

  • The compound key c "/" g — Chapter 6.3 section 4, and Chapter 9.6's finding that the grouping key decides how many problems you appear to have.
  • split(...) for a fixed row order — Chapter 10.4 section 8, because for (k in w) has no defined order.
  • Every carried variable cleared — Chapter 10.2 section 7 and Chapter 10.4 section 6.

Printing every combination from the split list rather than only the ones that occurred also means a (check, clock) pair with no paths at all shows as a row of zeros instead of silently not existing. On this report all four pairs occur; on a real one, a missing hold/clk_io row would be the news.

6. A Quoted Threshold Is a String Comparison

Flag every path worse than −0.050 ns. The obvious expression, with the threshold in quotes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 < "-0.050") { n++; printf "  flagged: %s\n", $3 } } END { printf "quoted threshold -0.050 flagged %d path(s)\n", n+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  flagged: -0.018
  flagged: -0.041
  flagged: -0.023
quoted threshold -0.050 flagged 3 path(s)

Three paths. Every one of them has a slack above −0.050, and the one path in the report that is genuinely worse — -0.112 — is not listed.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 + 0 < -0.050) { n++; printf "  flagged: %s\n", $3 } } END { printf "numeric threshold -0.050 flagged %d path(s)\n", n+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  flagged: -0.112
numeric threshold -0.050 flagged 1 path(s)

One path, and it is the right one.

The quoted comparison is lexical, so it asks whether the text -0.018 sorts before the text -0.050. Both start -0.0; at the next character 1 sorts before 5, so it does. Every slack between −0.001 and −0.049 is "less than" −0.050 as text, and every slack from −0.100 downwards is not.

Which raises the question of when + 0 is actually needed, because a running minimum in section 2 compared two fields with no apparent trouble. The three cases:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'BEGIN { print "two constants:      \"-0.018\" < \"-0.112\" ->", ("-0.018" < "-0.112" ? "TRUE (lexical)" : "false") }'
awk '/^ *slack/ { n++; if (n == 1) a = $3; if (n == 5) b = $3 } END { printf "two numeric fields: %s < %s -> %s\n", a, b, (a < b ? "TRUE" : "false (numeric)") }' sh.rpt
awk '/^ *slack/ { n++; if (n == 1) a = $3 } END { printf "field vs constant:  %s < \"-0.112\" -> %s\n", a, (a < "-0.112" ? "TRUE (lexical)" : "false") }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
two constants:      "-0.018" < "-0.112" -> TRUE (lexical)
two numeric fields: -0.018 < -0.112 -> false (numeric)
field vs constant:  -0.018 < "-0.112" -> TRUE (lexical)

The rule, which is POSIX awk's comparison rule from Chapter 5.2 section 7 in its sharpest form:

  • Two values that both came from input and both look numeric compare numerically. This is why the running minimum in section 2 is correct without + 0: both sides are fields.
  • A string constant on either side forces a lexical comparison, whatever the other side is.

So the dangerous construct is specifically a literal threshold in quotes, and it is dangerous regardless of how carefully the rest of the expression is written.

Two safe forms:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v t=-0.050 '/^ *slack/ { if ($3 + 0 < t + 0) n++ } END { printf "-v t=-0.050 with +0 -> %d path(s)\n", n+0 }' sh.rpt
awk -v t=-0.050 '/^ *slack/ { if ($3 < t) n++ } END { printf "-v t=-0.050 without +0 -> %d path(s)\n", n+0 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-v t=-0.050 with +0 -> 1 path(s)
-v t=-0.050 without +0 -> 1 path(s)

Both give 1. A value assigned with -v is treated the same way as a field — numeric-looking, so compared numerically — which is Chapter 8.2's point about -v and why a threshold belongs on the command line rather than baked into the program. Write + 0 anyway. It is free, it makes the intent explicit, and it is the one habit that makes the quoted form impossible to write by accident.

7. Ranking More Than One Path

For a top-N, emit a delimited stream and sort it — Chapter 7.6's shape:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { if ($2 == "(VIOLATED)") printf "%s\t%s\t%s\n", $3, (ty == "max" ? "setup" : "hold"), s; s=""; ty="" }' sh.rpt | sort -t"$(printf '\t')" -k1,1n -k2,2 -k3,3 | head -3
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-0.112	setup	u_dma/len_reg[4]
-0.041	hold	u_alu/acc_reg[0]
-0.023	hold	u_io/pad_reg[0]

The three worst violations, worst first, each labelled with its check so the ranking is readable even though it mixes them.

The sort keys are fully specified on purpose. sort -k1,1n alone orders by slack and says nothing about paths with equal slack — and equal slacks are common, because a report quantises to three decimals and because many paths share a critical sub-path:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf -- '-0.018\tsetup\tu_b/r\n-0.018\tsetup\tu_a/r\n-0.041\thold\tu_c/r\n' > ties.tsv
sort -t"$(printf '\t')" -k1,1n ties.tsv
sort -t"$(printf '\t')" -k1,1n -k3,3 ties.tsv
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-0.041	hold	u_c/r
-0.018	setup	u_a/r
-0.018	setup	u_b/r
-0.041	hold	u_c/r
-0.018	setup	u_a/r
-0.018	setup	u_b/r

Both orders agree here. That is not a guarantee of anything — sort defines no order among records whose specified keys are equal, so the agreement is a property of this input and this implementation, not of the command. Naming -k3,3 as a tiebreaker makes the order a property of the data, which is what a report diffed against yesterday's run needs. Module 9 lost ten fences to exactly this before the keys were specified.

8. Common Mistakes

Quoting a numeric threshold. $3 < "-0.050" is lexical and returns a disjoint set — section 6.

An unseeded minimum. Reports 0.000 on an all-passing report, which reads as zero margin — section 3.

One worst slack across both checks. Which check owns it varies by report — section 4.

Updating the value and the name in separate rules. They drift apart; update both inside the comparison.

Comparing slacks across clocks. Only paths checked against the same clock are comparable.

sort -k1,1n with no tiebreaker. No defined order among equal slacks — section 7.

Taking the top N from a default report. It holds one path per group, so N groups, not N paths.

Reporting a worst slack with no path count. A single extremum hides how much was summarised — and Chapter 10.4 section 7 showed a misfiled value leaves no trace in an extremum at all.

9. Exercises

Exercise 1 — Worst slack with its path

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) { w = $3; ws = s } s = "" } END { printf "worst slack %s on %s\n", w, ws }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack -0.112 on u_dma/len_reg[4]

Exercise 2 — Worst per check

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *Path Type:/ { ty = $3 } /^ *slack/ { c = (ty == "max" ? "setup" : "hold"); if (w[c] == "" || $3 + 0 < w[c] + 0) { w[c] = $3; p[c] = s } s = ""; ty = "" } END { printf "setup %8s  %s\nhold  %8s  %s\n", w["setup"], p["setup"], w["hold"], p["hold"] }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
setup   -0.112  u_dma/len_reg[4]
hold    -0.041  u_alu/acc_reg[0]

Exercise 3 — Debug a worst slack of zero

A nightly summary reports a worst slack of 0.000 on a report where everything passes.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 + 0 < w + 0) w = $3 } END { printf "worst slack: [%s] -> %.3f\n", w, w + 0 }' allpass.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack: [] -> 0.000

w is never assigned. It starts as the empty string, which is 0 numerically, and no positive slack is below 0.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if (w == "" || $3 + 0 < w + 0) w = $3 } END { printf "worst slack: [%s]\n", w }' allpass.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack: [0.031]

The direction of the error is worth noting: on a report with a violation the unseeded version is correct, because a negative value does fall below zero. The bug only shows on healthy runs.

Exercise 4 — Debug a threshold that flags the wrong paths

A script is asked for every path worse than −0.050 and returns three, none of which look bad.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *slack/ { if ($3 < "-0.050") printf "flagged %s\n", $3 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
flagged -0.018
flagged -0.041
flagged -0.023

Three paths, all better than −0.050, and -0.112 is missing. The threshold is a string constant, so the comparison is lexical: -0.018 sorts before -0.050 because 1 precedes 5.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v t=-0.050 '/^ *slack/ { if ($3 + 0 < t + 0) printf "flagged %s\n", $3 }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
flagged -0.112

Exercise 5 — Engineering application

The worst-path table a timing review opens with: per check and clock, with the path named, the violation count beside every extremum, and the report's own path count so the reader knows what was summarised.

Azvya Education Pvt. Ltd.VLSI Mentor
worst.awk — synthetic teaching script.
# worst.awk — worst path per (check, clock) from a timing report.
#
# Usage:  awk -f worst.awk sh.rpt
#         awk -v t=-0.050 -f worst.awk sh.rpt     # also list paths past a threshold
# Exit:   0 nothing violating   1 violations present

/^ *Startpoint:/ { s = $2 }
/^ *Path Group:/ { g = $3 }
/^ *Path Type:/  { ty = $3 }

/^ *slack/ {
    npath++
    chk = (ty == "max" ? "setup" : (ty == "min" ? "hold" : "unknown"))
    key = chk "/" g
    if (!(key in n)) order[++nk] = key
    n[key]++
    if ($2 == "(VIOLATED)") { v[key]++; nviol++ }
    if (!(key in w) || $3 + 0 < w[key] + 0) { w[key] = $3; p[key] = s }
    if (t != "" && $3 + 0 < t + 0) past = past sprintf("  %8s  %-6s %s\n", $3, chk, s)
    s = ""; g = ""; ty = ""
}

END {
    printf "%-20s %6s %6s %9s  %s\n", "check/clock", "paths", "viol", "worst", "worst path"
    for (i = 1; i <= nk; i++) {
        k = order[i]
        printf "%-20s %6d %6d %9.3f  %s\n", k, n[k], v[k] + 0, w[k], p[k]
    }
    printf "# %d path(s) in this report, %d violating\n", npath + 0, nviol + 0
    if (past != "") printf "paths past %s:\n%s", t, past
    if (nviol) exit 1
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f worst.awk sh.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
check/clock           paths   viol     worst  worst path
setup/clk_core            3      2    -0.112  u_dma/len_reg[4]
hold/clk_core             1      1    -0.041  u_alu/acc_reg[0]
hold/clk_io               1      1    -0.023  u_io/pad_reg[0]
setup/clk_io              1      0     0.031  u_io/cfg_reg[2]
# 6 path(s) in this report, 4 violating
status: 1

The row order is first-seen rather than alphabetical, which is what order[++nk] = key on the !(key in n) guard gives — Chapter 6.3 section 6's per-key seeding used to record an insertion order, and the third answer to Chapter 10.4 section 8's unordered-iteration problem. First-seen order means the rows come out in the order the tool reported the groups, which is usually worst-first and is at least reproducible.

With a threshold:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v t=-0.050 -f worst.awk sh.rpt | tail -3
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
# 6 path(s) in this report, 4 violating
paths past -0.050:
    -0.112  setup  u_dma/len_reg[4]

And on the three-path report:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f worst.awk timing.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
check/clock           paths   viol     worst  worst path
setup/clk_core            2      1    -0.018  u_regfile/dout_reg[3]
hold/clk_io               1      1    -0.023  u_io/pad_reg[0]
# 3 path(s) in this report, 2 violating
status: 1

Two rows rather than four, because this report has no setup/clk_io or hold/clk_core paths at all — and the first-seen construction cannot invent a row for a combination the report never mentioned. That is the trade against Chapter 10.4's split list: a fixed list shows an expected-but-absent category as zeros, and first-seen order shows only what was there. Use the fixed list when you know which categories must exist, and first-seen when you do not.

Exercise 6 — Challenge: show that the worst-slack figure cannot be checked against itself

A reviewer wants to validate a worst-slack script by confirming the number appears somewhere in the report. Show that the check passes for a figure attributed to the wrong path.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) w = $3; ws = s; s = "" } END { printf "worst slack %s on %s\n", w, ws }' sh.rpt
grep -c -- "-0.112" sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack -0.112 on u_io/cfg_reg[2]
1

The slack is right and the path is wrong. ws = s sits outside the comparison — note the semicolon placement — so it is reassigned on every slack line and ends up holding the last path in the report rather than the worst one.

And the proposed check passes: -0.112 does appear in the file, once, exactly as the script reported it. The check confirms the number was read correctly and says nothing whatsoever about which path it was read from — which is the whole of the defect. On a full report with its delay tables the situation is worse rather than better, because a slack value can coincide with an incremental or cumulative delay elsewhere in the table, so a grep count of more than one proves even less.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2 } /^ *slack/ { if (w == "" || $3 + 0 < w + 0) { w = $3; ws = s } s = "" } END { printf "worst slack %s on %s\n", w, ws }' sh.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
worst slack -0.112 on u_dma/len_reg[4]

Moving ws = s inside the braces makes the pairing structural: the name can only be written when the number is.

The general lesson is that a value and its label have to be bound by construction, not by convention. A check that validates them separately — the number exists in the report, the path name exists in the report — passes for every possible mispairing of the two, which is Chapter 10.2 section 6's finding reached from the opposite direction.

10. Summary

  • Seed a running minimum with w == "" or !(k in w). Unseeded, it reports 0.000 — zero margin — on reports where everything passes.
  • Update the value and its path name inside the same comparison. Separated, they drift to the last record.
  • Which check owns the worst path varies by report — hold on one fixture, setup on the other. Split before quoting a worst slack.
  • The signoff shape is one row per (check, clock), each with a path count, a violation count and a named worst path.
  • A quoted threshold is lexical. $3 < "-0.050" flagged three paths that pass and missed the only one that fails.
  • Two numeric-looking values from input compare numerically; a string constant on either side forces a lexical comparison. Write + 0, and pass thresholds with -v.
  • Specify every sort key. No order is defined among equal slacks, and equal slacks are common at three decimals.
  • A default report holds one path per group, so the top N of a report is not the top N of the design. Print the path count.
  • Bind a number to its label by construction. Checking that each exists in the report passes for every mispairing.

The ranking is sound. Chapter 10.6 — Grouping Paths by Clock takes the other axis: aggregating per clock domain, where the path group is not always the clock you think it is, and a report with two clocks can hide a third.

Where this fits

Part of the awk curriculum.