Skip to content
VLSI Mentor

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

Grouping Paths by Clock

Per-clock aggregation looks like the easiest thing in the module: the Path Group line holds a clock name, so group on it. The difficulty is that a timing path names up to three clocks — the one that launched the data, the one that captures it, and the group the tool filed the path under — and on a cross-domain path they are not the same. This chapter builds the summary three ways on one report and gets three different answers, including a clock that owns a violation under one grouping and has no paths at all under another, and a fourth clock that launches the report's worst violation while appearing in no path group.

Advanced19 min readawkPart IIISTAClock DomainsGrouping

Part III · Module 10 · Chapter 10.6 · Grouping Paths by Clock

1. Why This Matters

A per-clock timing summary is the table a design review opens with: how many paths in each clock domain, how many violating, and the worst slack in each. It is also the table most likely to be built on an assumption nobody states.

The assumption is that a path has a clock. It does not. A path has a launch clock at its startpoint, a capture clock at its endpoint, and a path group that the tool assigned it to — and on any path that crosses between domains, which is where the hard timing problems live, those are different names. Grouping on one of them is a choice, and this chapter shows that the three choices produce genuinely different reports from the same file.

2. The Easy Case

On the three-path report from Chapter 10.1, where every path stays in one domain, the obvious thing works:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { g = $3 } /^ *slack/ { n[g]++; if ($2 == "(VIOLATED)") v[g]++; if (!(g in w) || $3 + 0 < w[g] + 0) w[g] = $3; if (!(g in seen)) { seen[g]=1; o[++k]=g } g = "" } END { for (i=1;i<=k;i++) { gg=o[i]; printf "%-10s %d path(s), %d violating, worst %s\n", gg, n[gg], v[gg]+0, w[gg] } }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk_core   2 path(s), 1 violating, worst -0.018
clk_io     1 path(s), 1 violating, worst -0.023

Two domains, one violation each. The !(g in seen) guard records first-seen order — Chapter 10.5 section 9's construction — and !(g in w) seeds each per-group minimum, which is Chapter 10.5 section 3's lesson applied per key rather than once.

That command is correct for this report and it will keep being correct right up to the first report containing a path that crosses domains.

3. The Clock Is Named in Two Other Places

The clock name appears in the continuation lines under the startpoint and the endpoint:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep "clocked by" timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
              (rising edge-triggered flip-flop clocked by clk_core)
            (rising edge-triggered flip-flop clocked by clk_core)
              (rising edge-triggered flip-flop clocked by clk_core)
            (rising edge-triggered flip-flop clocked by clk_core)
              (rising edge-triggered flip-flop clocked by clk_io)
            (rising edge-triggered flip-flop clocked by clk_io)

Six lines for three paths — two per path, one for each end. Extracting the name means taking the last field and dropping the closing parenthesis:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/clocked by/ { c = $NF; gsub(/[()]/, "", c); printf "[%s]", c } END { print "" }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[clk_core][clk_core][clk_core][clk_core][clk_io][clk_io]

On this report every pair matches, so the distinction has nothing to show. The next section gives it something.

4. A Cross-Domain Report

Azvya Education Pvt. Ltd.VLSI Mentor
hidden.rpt — synthetic teaching data. Three paths; the middle one launches from clk_periph and is captured by clk_ddr.
  Startpoint: u_regfile/dout_reg[3]
              (rising edge-triggered flip-flop clocked by clk_core)
  Endpoint: u_alu/sum_reg[7]
            (rising edge-triggered flip-flop clocked by clk_core)
  Path Group: clk_core
  Path Type: max
  slack (VIOLATED)                                   -0.018

  Startpoint: u_periph/req_reg[0]
              (rising edge-triggered flip-flop clocked by clk_periph)
  Endpoint: u_mem/wdata_reg[0]
            (rising edge-triggered flip-flop clocked by clk_ddr)
  Path Group: clk_ddr
  Path Type: max
  slack (VIOLATED)                                   -0.205

  Startpoint: u_io/pad_reg[0]
              (rising edge-triggered flip-flop clocked by clk_io)
  Endpoint: u_io/out_reg[0]
            (rising edge-triggered flip-flop clocked by clk_io)
  Path Group: clk_io
  Path Type: min
  slack (VIOLATED)                                   -0.023

Pulling all three clock notions out of each path:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { stage = "launch" } /^ *Endpoint:/ { stage = "capture" } /clocked by/ { c = $NF; gsub(/[()]/, "", c); if (stage == "launch") lc = c; else cc = c } /^ *Path Group:/ { g = $3 } /^ *slack/ { printf "group=%-10s launch=%-11s capture=%-10s %8s  [%s]\n", g, lc, cc, $3, (lc == cc ? "same domain" : "CROSS-DOMAIN"); lc=""; cc=""; g=""; stage="" }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
group=clk_core   launch=clk_core    capture=clk_core     -0.018  [same domain]
group=clk_ddr    launch=clk_periph  capture=clk_ddr      -0.205  [CROSS-DOMAIN]
group=clk_io     launch=clk_io      capture=clk_io       -0.023  [same domain]

The stage variable is the technique: the Startpoint: and Endpoint: markers set which end the next clocked by line belongs to. Section 7 is about how fragile that is.

The middle path is the interesting one. It launches from clk_periph, is captured by clk_ddr, is filed under path group clk_ddr, and holds the worst slack in the report at −0.205. Which clock domain does that violation belong to? There are three defensible answers and the next section shows they are not interchangeable.

5. Three Clocks, Three Summaries

A timing path receives a launch clock from the startpoint continuation line, a capture clock from the endpoint continuation line, and a group name from the Path Group line. Those three sources fan out into three different per-clock summaries, which disagree about which clock owns the path.Startpoint lineclocked by clk_periph — thelaunching clockEndpoint lineclocked by clk_ddr — thecapturing clockPath Group lineclk_ddr — the group thetool filed it underOne timing pathslack -0.205, the worst inthe reportBy launch clockclk_periph owns it; clk_ddrhas no pathsBy capture clockclk_ddr owns it; clk_periphnever appearsBy path groupclk_ddr — the captureclock, by conventionlaunchcapturegroup12
Figure 1 — The three sources for the clock of a path, and the three per-clock summaries they produce. For a path that stays in one domain all three agree; for the cross-domain path in hidden.rpt they name three different clocks, and only two of the three summaries mention the clock that launched the violation.

Two of those three summaries, built on hidden.rpt:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf "by Path Group:   "; awk '/^ *Path Group:/ { g = $3 } /^ *slack/ { n[g]++; if (!(g in s)) { s[g]=1; o[++k]=g } g="" } END { for (i=1;i<=k;i++) printf "[%s=%d]", o[i], n[o[i]]; print "" }' hidden.rpt
printf "by launch clock: "; awk '/^ *Startpoint:/ { stage="launch" } /clocked by/ { if (stage=="launch") { c=$NF; gsub(/[()]/,"",c); lc=c; stage="" } } /^ *slack/ { n[lc]++; if (!(lc in s)) { s[lc]=1; o[++k]=lc } lc="" } END { for (i=1;i<=k;i++) printf "[%s=%d]", o[i], n[o[i]]; print "" }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
by Path Group:   [clk_core=1][clk_ddr=1][clk_io=1]
by launch clock: [clk_core=1][clk_periph=1][clk_io=1]

Two per-clock summaries of one report, naming different sets of clocks. clk_ddr exists only in the first and clk_periph only in the second, and both are labelled "paths per clock".

6. A Clock That Appears in No Path Group

The stronger version of the same problem. Which clocks does the report mention?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { print "  " $3 }' hidden.rpt | sort -u
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  clk_core
  clk_ddr
  clk_io
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/clocked by/ { c = $NF; gsub(/[()]/, "", c); print "  " c }' hidden.rpt | sort -u
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  clk_core
  clk_ddr
  clk_io
  clk_periph

Three path groups, four clocks. clk_periph is in the report, launches its worst violation, and is absent from every grouping built on Path Group.

So a per-clock summary has a prerequisite that is easy to skip: enumerate the clocks from the continuation lines, not from the path groups, and then say which of them the summary covers. A table with three rows on a report naming four clocks is incomplete regardless of how correct each row is.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { pg[$3] = 1 } /clocked by/ { c = $NF; gsub(/[()]/, "", c); ck[c] = 1 } END { for (c in ck) if (!(c in pg)) miss = miss " " c; printf "clock(s) named in the report but in no path group:%s\n", (miss == "" ? " none" : miss) }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clock(s) named in the report but in no path group: clk_periph
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { pg[$3] = 1 } /clocked by/ { c = $NF; gsub(/[()]/, "", c); ck[c] = 1 } END { for (c in ck) if (!(c in pg)) miss = miss " " c; printf "clock(s) named in the report but in no path group:%s\n", (miss == "" ? " none" : miss) }' timing.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clock(s) named in the report but in no path group: none

One rule, and it answers the question the summary cannot: is there a clock here I am not reporting on? Note that for (c in ck) is used only to accumulate into a string whose content does not depend on the order — the same reasoning as Chapter 10.4 section 8, where the order mattered and explicit keys were required. With one missing clock the order is unobservable; with several it would need sorting.

7. The Positional Parse Is Fragile

The stage variable in section 4 works by position: the first clocked by line after a Startpoint: marker is the launch clock. Remove the Endpoint: marker line while leaving its continuation line — the shape a header-wording change produces:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep -v "Endpoint: u_mem" hidden.rpt > noend.rpt
printf "cleared:  "; awk '/^ *Startpoint:/ { stage = "launch" } /^ *Endpoint:/ { stage = "capture" } /clocked by/ { c = $NF; gsub(/[()]/, "", c); if (stage == "launch") lc = c; else if (stage == "capture") cc = c } /^ *slack/ { printf "[%s->%s]", (lc==""?"?":lc), (cc==""?"?":cc); lc=""; cc=""; stage="" } END { print "" }' noend.rpt
printf "stale:    "; awk '/^ *Startpoint:/ { stage = "launch" } /^ *Endpoint:/ { stage = "capture" } /clocked by/ { c = $NF; gsub(/[()]/, "", c); if (stage == "launch") lc = c; else cc = c } /^ *slack/ { printf "[%s->%s]", (lc==""?"?":lc), (cc==""?"?":cc) } END { print "" }' noend.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cleared:  [clk_core->clk_core][clk_ddr->?][clk_io->clk_io]
stale:    [clk_core->clk_core][clk_ddr->clk_core][clk_io->clk_io]

Both are wrong, and it is worth being exact about how.

The middle path's launch clock is reported as clk_ddr in both. With no Endpoint: marker, stage was still launch when the second continuation line arrived, so the capture clock overwrote the launch clock. clk_periph is gone from both outputs.

Clearing the state still helps, but less than it did in Chapters 10.2 and 10.4: it turns the capture clock into ?, which at least says something is missing here. The uncleared version instead reports clk_ddr -> clk_core, a cross-domain path between two clocks that has no counterpart in the design — it inherited path one's capture clock.

8. Count the Markers, Not the Continuation Lines

The obvious check is per path: each path should have two clocked by lines.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for f in hidden.rpt noend.rpt; do printf "%-12s " "$f"; awk '/^ *Startpoint:/ { nc = 0 } /clocked by/ { nc++ } /^ *slack/ { printf "[path %d: %d clock line(s)%s]", ++p, nc, (nc == 2 ? "" : " BAD"); nc = 0 } END { print "" }' $f; done
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hidden.rpt   [path 1: 2 clock line(s)][path 2: 2 clock line(s)][path 3: 2 clock line(s)]
noend.rpt    [path 1: 2 clock line(s)][path 2: 2 clock line(s)][path 3: 2 clock line(s)]

The check passes on the broken file. Both have two continuation lines per path, because what was removed was the Endpoint: marker and not the line describing the endpoint's clock. A check counting the lines that carry the data cannot see a missing line that carries the structure.

Counting the markers does see it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for f in hidden.rpt noend.rpt; do printf "%-12s " "$f"; awk '/^ *Startpoint:/ { ns++ } /^ *Endpoint:/ { ne++ } /clocked by/ { nc++ } /^ *slack/ { nk++ } END { printf "start=%d end=%d clocklines=%d slack=%d -> %s\n", ns+0, ne+0, nc+0, nk+0, (ns == ne && nc == 2 * nk ? "consistent" : "INCONSISTENT") }' $f; done
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
hidden.rpt   start=3 end=3 clocklines=6 slack=3 -> consistent
noend.rpt    start=3 end=2 clocklines=6 slack=3 -> INCONSISTENT

Three startpoints and two endpoints. The contract has three parts and each catches something different:

  • ns == ne — every path has both ends named. This is the one that fires here.
  • nc == 2 * nk — every path has exactly two clock lines. Catches a missing continuation line, which the per-path version also catches.
  • ns == nk from Chapter 10.2 section 8 — every path has a slack, and every slack has a path.

Together they say the file has the shape the positional parse assumes. None of them says the parse produced the right answer, which is the honest limit: a precondition buys you the right to trust a parse, and nothing more.

9. Common Mistakes

Assuming a path has one clock. It has a launch clock, a capture clock and a path group — section 4.

Grouping on Path Group and calling it per-clock. That is the capture clock, so a cross-domain violation is booked entirely against the receiving domain — section 5.

Enumerating clocks from the path groups. A clock can launch the worst violation and appear in no group — section 6.

Not seeding the per-group minimum. !(g in w) per key, or every group with only passing paths reports zero — Chapter 10.5 section 3.

Not clearing g, lc and cc. A path with a missing line inherits the previous one's.

Trusting the positional parse without checking the file's shape. Clearing makes a gap visible but cannot fix a mis-stepped position — section 7.

Counting the continuation lines as the precondition. It passes on a file with a missing marker — section 8.

Reporting a cross-domain path under one clock. It belongs to two; report the pair.

10. Exercises

Exercise 1 — Per-group summary

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { g = $3 } /^ *slack/ { n[g]++; if ($2 == "(VIOLATED)") v[g]++; if (!(g in w) || $3 + 0 < w[g] + 0) w[g] = $3; if (!(g in seen)) { seen[g]=1; o[++k]=g } g = "" } END { for (i=1;i<=k;i++) { gg=o[i]; printf "%-11s %d path(s), %d violating, worst %s\n", gg, n[gg], v[gg]+0, w[gg] } }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clk_core    1 path(s), 1 violating, worst -0.018
clk_ddr     1 path(s), 1 violating, worst -0.205
clk_io      1 path(s), 1 violating, worst -0.023

Exercise 2 — Name the cross-domain paths

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { s = $2; stage = "launch" } /^ *Endpoint:/ { e = $2; stage = "capture" } /clocked by/ { c = $NF; gsub(/[()]/, "", c); if (stage == "launch") lc = c; else if (stage == "capture") cc = c } /^ *slack/ { if (lc != cc) printf "%s (%s) -> %s (%s)  %s\n", s, lc, e, cc, $3; s=""; e=""; lc=""; cc=""; stage="" } END { }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_periph/req_reg[0] (clk_periph) -> u_mem/wdata_reg[0] (clk_ddr)  -0.205

One path, with both domains named — the row a review needs in order to send it to two owners rather than one.

Exercise 3 — Debug a missing clock

A per-clock table has three rows. A reviewer asks whether that is all the clocks.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Path Group:/ { pg[$3] = 1 } /clocked by/ { c = $NF; gsub(/[()]/, "", c); ck[c] = 1 } END { for (c in ck) if (!(c in pg)) miss = miss " " c; printf "clock(s) named in the report but in no path group:%s\n", (miss == "" ? " none" : miss) }' hidden.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
clock(s) named in the report but in no path group: clk_periph

clk_periph launches the report's worst violation and has no path group of its own, so no grouping on Path Group will ever produce a row for it.

Exercise 4 — Debug an invented clock crossing

A domain-crossing report lists a clk_ddr -> clk_core path. No such crossing exists in the design.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grep -v "Endpoint: u_mem" hidden.rpt > noend.rpt
awk '/^ *Startpoint:/ { stage = "launch" } /^ *Endpoint:/ { stage = "capture" } /clocked by/ { c = $NF; gsub(/[()]/, "", c); if (stage == "launch") lc = c; else cc = c } /^ *slack/ { if (lc != cc) printf "crossing %s -> %s  %s\n", lc, cc, $3 }' noend.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
crossing clk_ddr -> clk_core  -0.205
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^ *Startpoint:/ { ns++ } /^ *Endpoint:/ { ne++ } /^ *slack/ { nk++ } END { printf "start=%d end=%d slack=%d -> %s\n", ns+0, ne+0, nk+0, (ns == ne && ns == nk ? "consistent" : "INCONSISTENT") }' noend.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
start=3 end=2 slack=3 -> INCONSISTENT

Three startpoints, two endpoints. One path has no Endpoint: marker, so its endpoint's clock was read as its startpoint's and the capture clock was inherited from the previous path. The crossing is an artefact of the parse, and the structural check is what says so — the crossing itself looks entirely plausible.

Exercise 5 — Engineering application

A per-clock summary that states its grouping, enumerates the clocks independently of it, reports cross-domain paths as pairs, and refuses a report whose shape it cannot trust.

Azvya Education Pvt. Ltd.VLSI Mentor
clocks.awk — synthetic teaching script.
# clocks.awk — per-clock timing summary with an explicit grouping.
#
# Usage:  awk -f clocks.awk hidden.rpt            # group on the path group
#         awk -v by=launch -f clocks.awk hidden.rpt
# Exit:   0 clean   1 violations present   2 the report's shape is inconsistent

BEGIN { if (by == "") by = "group" }

/^ *Startpoint:/ { s = $2; stage = "launch";  ns++ }
/^ *Endpoint:/   { e = $2; stage = "capture"; ne++ }

/clocked by/ {
    nc++
    c = $NF; gsub(/[()]/, "", c)
    allclk[c] = 1
    if (stage == "launch") lc = c
    else if (stage == "capture") cc = c
}

/^ *Path Group:/ { g = $3; pg[g] = 1 }

/^ *slack/ {
    nk++
    key = (by == "launch" ? lc : (by == "capture" ? cc : g))
    if (key == "") key = "<unknown>"
    if (!(key in n)) order[++nord] = key
    n[key]++
    if ($2 == "(VIOLATED)") { v[key]++; nviol++ }
    if (!(key in w) || $3 + 0 < w[key] + 0) w[key] = $3
    if (lc != "" && cc != "" && lc != cc) cross = cross sprintf("  %-20s %-11s -> %-11s %8s\n", s, lc, cc, $3)
    s = ""; e = ""; g = ""; lc = ""; cc = ""; stage = ""
}

END {
    if (ns != ne || nc != 2 * nk || ns != nk) {
        printf "ERROR: start=%d end=%d clocklines=%d slack=%d -- shape inconsistent\n", ns+0, ne+0, nc+0, nk+0 > "/dev/stderr"
        exit 2
    }
    printf "grouped by: %s\n", by
    printf "%-12s %6s %6s %9s\n", "clock", "paths", "viol", "worst"
    for (i = 1; i <= nord; i++) { k = order[i]; printf "%-12s %6d %6d %9.3f\n", k, n[k], v[k] + 0, w[k] }
    for (c in allclk) if (!(c in n)) nonrow = nonrow " " c
    if (nonrow != "") printf "# clock(s) in the report with no row under this grouping:%s\n", nonrow
    if (cross != "") printf "cross-domain path(s):\n%s", cross
    if (nviol) exit 1
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f clocks.awk hidden.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grouped by: group
clock         paths   viol     worst
clk_core          1      1    -0.018
clk_ddr           1      1    -0.205
clk_io            1      1    -0.023
# clock(s) in the report with no row under this grouping: clk_periph
cross-domain path(s):
  u_periph/req_reg[0]  clk_periph  -> clk_ddr       -0.205
status: 1

The table is the familiar one and the two lines under it are what the familiar one leaves out: a clock with no row, and the path whose two ends disagree.

The same report grouped on the launch clock:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v by=launch -f clocks.awk hidden.rpt | head -6
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
grouped by: launch
clock         paths   viol     worst
clk_core          1      1    -0.018
clk_periph        1      1    -0.205
clk_io            1      1    -0.023
# clock(s) in the report with no row under this grouping: clk_ddr

clk_periph now has a row and clk_ddr does not. Both tables are correct and neither is complete, which is why the grouped by: line is printed first — a per-clock summary without it is not a well-formed claim.

And the broken report is refused:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f clocks.awk noend.rpt 2>&1 >/dev/null
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ERROR: start=3 end=2 clocklines=6 slack=3 -- shape inconsistent
status: 2

Exercise 6 — Challenge: show that the grouping choice is not detectable from the output

Both tables in Exercise 5 have three rows, one violation each, and the same three slack values. Show that nothing but the grouped by: line distinguishes them.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f clocks.awk hidden.rpt | sed -n '3,5p' | awk '{ print $2, $3, $4 }'
awk -v by=launch -f clocks.awk hidden.rpt | sed -n '3,5p' | awk '{ print $2, $3, $4 }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 1 -0.018
1 1 -0.205
1 1 -0.023
1 1 -0.018
1 1 -0.205
1 1 -0.023

Strip the clock names and the two tables are byte-identical — the same three rows, in the same order, with the same counts and the same worst slack. The row order survives because both groupings happen to meet the three clocks in the same sequence, so even the one cue that might have hinted at a difference is absent.

The only thing that differs is the first column:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -f clocks.awk hidden.rpt | sed -n '3,5p' | awk '{ printf "[%s]", $1 } END { print "" }'
awk -v by=launch -f clocks.awk hidden.rpt | sed -n '3,5p' | awk '{ printf "[%s]", $1 } END { print "" }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[clk_core][clk_ddr][clk_io]
[clk_core][clk_periph][clk_io]

One cell out of twelve. clk_ddr against clk_periph, in the middle row, and everything else about the two tables is the same.

Every aggregate agrees too:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
for m in group launch capture; do printf "%-8s " "$m"; awk -v by=$m -f clocks.awk hidden.rpt | awk '/^clk/ { r++; n += $2; v += $3 } END { printf "rows=%d paths=%d violations=%d\n", r+0, n+0, v+0 }'; done
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
group    rows=3 paths=3 violations=3
launch   rows=3 paths=3 violations=3
capture  rows=3 paths=3 violations=3

Identical under all three groupings. Three rows, three paths, three violations, whichever clock the summary was built on.

So the grouping cannot be recovered from the numbers. It has to be declared, which is the whole reason clocks.awk prints it on the first line. The same argument as Chapter 9.6's finding that the grouping key decides how many problems you appear to have: when a choice of key changes the answer and leaves no trace, the key becomes part of the result and belongs in the output.

11. Summary

  • A path names three clocks: a launch clock, a capture clock, and a path group.
  • The path group is the capture clock, so a cross-domain violation is booked entirely against the receiving domain — section 5.
  • The clock name is in the continuation lines, extracted with $NF and gsub(/[()]/, "", c).
  • A clock can launch the worst violation and appear in no path group — clk_periph on hidden.rpt. Enumerate clocks independently of the grouping.
  • Seed each per-group minimum with !(g in w), not once globally.
  • Clearing carried state makes a gap visible; it cannot fix a positional parse. A missing Endpoint: marker made the capture clock overwrite the launch clock, and the uncleared version invented a crossing that does not exist — section 7.
  • The precondition must count the markers. Counting continuation lines passes on the broken file — section 8.
  • Report a cross-domain path as a pair. It belongs to two domains.
  • Declare the grouping. All three give identical aggregates, so the choice leaves no trace in the numbers.

Everything so far has read one line at a time with state carried between records. Chapter 10.7 — Parsing Multi-Line EDA Reports asks whether awk's paragraph mode is a better fit for the whole job, and answers it with a measurement: state-carrying survives both report shapes tested, paragraph mode survives one, and the difference between them is a single blank line.

Where this fits

Part of the awk curriculum.