Skip to content
VLSI Mentor

awk Part III · Module 11 · Synthesis and Coverage Report Analysis

Toggle Coverage

Line coverage says a statement executed. Toggle coverage says a bit took both values — and the gap between them is one of the most useful diagnostics a coverage report offers, because it separates code that was never reached from code that ran on data that never varied. This chapter extracts toggle coverage per block, measures that gap across a design, and then goes one level down into a single unit to show what a block-level percentage conceals: at 61.90% this one has three signals that never changed at all, and its own output has twenty-four of thirty-two bits stuck.

Intermediate17 min readawkPart IIICoverageToggleDiagnostics

Part III · Module 11 · Chapter 11.5 · Toggle Coverage

1. Why This Matters

Toggle coverage asks, of every bit in the design, whether it was ever observed at 0 and ever observed at 1. That is a question about data, where line coverage asks a question about control flow, and the two fail for different reasons:

  • A line at 0% was never reached. Some branch, mode or configuration was not exercised.
  • A bit at 0% was reached and never changed. The code ran; the value was constant.

The second is the harder problem to find by reading a test list, and it is the one that hides behind a respectable-looking block percentage.

2. Toggle Coverage per Unit

From cov.rpt, listed in full in Chapter 11.4 section 2:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^Design Unit:/ { u = $3 } /^  Toggle Coverage/ { printf "  %-10s %7.2f%%  %s of %s bits\n", u, $3 + 0, substr($4,2), $6; u = "" }' cov.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  alu_core     74.19%  46 of 62 bits
  u_regfile    98.44%  126 of 128 bits
  u_alu        61.90%  78 of 126 bits
  u_ctrl       45.31%  29 of 64 bits
  u_io        100.00%  16 of 16 bits
  u_debug       0.00%  0 of 24 bits

Same shape as Chapter 11.4, same + 0 on the percentage for the reason section 4 of that chapter measured, same substr($4, 2) to drop the parenthesis.

3. Bits and Lines Are Not the Same Unit

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^Design Unit: u_alu/ { inu=1 } inu && /^  Line Coverage/ { printf "  u_alu lines:  %s of %s\n", substr($4,2), $6 } inu && /^  Toggle Coverage/ { printf "  u_alu bits:   %s of %s\n", substr($4,2), $6; inu=0 }' cov.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  u_alu lines:  75 of 85
  u_alu bits:   78 of 126

Eighty-five lines and a hundred and twenty-six bits. They are different populations of different things, and the consequences are worth stating plainly because they are all violations people commit:

  • They cannot be summed. Chapter 11.4's Exercise 6 added them and got a thoroughly plausible 74.35% that measures nothing.
  • They cannot be averaged together into "overall coverage" without a weighting decision, which is Chapter 11.8's subject.
  • Their magnitudes are not comparable. 88.24% of lines and 61.90% of bits are not two measurements of the same thing at different levels.

What they can do is be compared as a pair, which is the next section.

4. The Gap Is the Diagnostic

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^Design Unit:/ { u = $3 } /^  Line Coverage/ { l[u] = $3 + 0 } /^  Toggle Coverage/ { t[u] = $3 + 0; if (!(u in seen)) { seen[u]=1; o[++n]=u } }
 END { printf "  %-10s %8s %8s %8s\n", "unit", "line", "toggle", "gap"
       for (i=1;i<=n;i++) { k=o[i]; printf "  %-10s %7.2f%% %7.2f%% %+7.2f\n", k, l[k], t[k], t[k]-l[k] } }' cov.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  unit           line   toggle      gap
  alu_core     96.55%   74.19%  -22.36
  u_regfile   100.00%   98.44%   -1.56
  u_alu        88.24%   61.90%  -26.34
  u_ctrl       72.00%   45.31%  -26.69
  u_io        100.00%  100.00%   +0.00
  u_debug       0.00%    0.00%   +0.00

Toggle sits at or below line coverage in every unit. That is the normal relationship and the size of the gap is what carries information:

  • u_regfile, −1.56. Lines all ran and bits nearly all moved. A register file written and read with varied data.
  • u_alu, −26.34 and u_ctrl, −26.69. Most lines ran; well over a third of the bits never changed. The tests reach the code and then feed it a narrow set of values.
  • u_io, +0.00 at 100% of both. Fully exercised.
  • u_debug, +0.00 at 0% of both. Never reached at all, which is a different problem — there is no stimulus, not narrow stimulus.

The last two rows share a gap of zero and mean opposite things, which is why the gap is read alongside the levels and not instead of them.

5. The Block Percentage Hides Stuck Signals

A per-signal breakdown of the worst-gap unit:

Azvya Education Pvt. Ltd.VLSI Mentor
toggle.rpt — synthetic teaching data, generated so its ten signals sum to exactly the 78 of 126 bits cov.rpt reports for u_alu.
Toggle Coverage Detail : u_alu
Signal                        Bits  Toggled   Percent
------------------------------------------------------
u_alu/operand_a                 32       32   100.00%
u_alu/operand_b                 32       30    93.75%
u_alu/result                    32        8    25.00%
u_alu/opcode                     5        4    80.00%
u_alu/flags                      4        2    50.00%
u_alu/carry_chain                8        0     0.00%
u_alu/enable                     1        1   100.00%
u_alu/mode_sel                   2        0     0.00%
u_alu/overflow                   1        1   100.00%
u_alu/spare                      9        0     0.00%
------------------------------------------------------
TOTAL                          126       78    61.90%

The detail must agree with the summary, and checking that is the first thing to do with any drill-down:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^Design Unit: u_alu/ { inu = 1 } inu && /^  Toggle Coverage/ { printf "  cov.rpt says u_alu toggle = %s (%s of %s bits)\n", $3, substr($4,2), $6; inu = 0 }' cov.rpt
awk 'NR>3 && !/^-|^TOTAL/ { b += $2; t += $3 } END { printf "  toggle.rpt detail sums to  %d of %d bits = %.2f%%\n", t, b, 100*t/b }' toggle.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  cov.rpt says u_alu toggle = 61.90% (78 of 126 bits)
  toggle.rpt detail sums to  78 of 126 bits = 61.90%

Now what the 61.90% was covering for:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ && $3 == 0 { printf "  %-22s %2d bit(s), 0 toggled\n", $1, $2; n++; bits += $2 } END { printf "  %d signal(s), %d bits never toggled\n", n, bits }' toggle.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  u_alu/carry_chain       8 bit(s), 0 toggled
  u_alu/mode_sel          2 bit(s), 0 toggled
  u_alu/spare             9 bit(s), 0 toggled
  3 signal(s), 19 bits never toggled

Three signals that never changed state at all, nineteen bits between them:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ { if ($3 == 0) { z += $2; zn++ }; b += $2; t += $3 }
  END { printf "  unit reads %.2f%% (%d of %d bits)\n", 100*t/b, t, b
        printf "  but %d signal(s) / %d bits (%.1f%% of the unit) never toggled at all\n", zn, z, 100*z/b
        printf "  and removing them would read %.2f%% on the remaining %d bits\n", 100*t/(b-z), b-z }' toggle.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  unit reads 61.90% (78 of 126 bits)
  but 3 signal(s) / 19 bits (15.1% of the unit) never toggled at all
  and removing them would read 72.90% on the remaining 107 bits

A $3 == 0 test is a one-line scan with a specific meaning: a signal that never toggled is almost always either tied off, unreachable, or a leftover. u_alu/spare at nine bits is the shape of a signal that was never connected to anything.

6. Percentage and Bit Count Rank Differently

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
printf "  by percent:   "; awk 'NR>3 && !/^-|^TOTAL/ { printf "%s\t%.2f\t%d\n", $1, $4+0, $2-$3 }' toggle.rpt | sort -t"$(printf '\t')" -k2,2n -k1,1 | head -4 | awk -F'\t' '{ printf "[%s %.0f%%]", $1, $2 } END { print "" }'
printf "  by untoggled: "; awk 'NR>3 && !/^-|^TOTAL/ { printf "%s\t%.2f\t%d\n", $1, $4+0, $2-$3 }' toggle.rpt | sort -t"$(printf '\t')" -k3,3rn -k1,1 | head -4 | awk -F'\t' '{ printf "[%s %d bits]", $1, $3 } END { print "" }'
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  by percent:   [u_alu/carry_chain 0%][u_alu/mode_sel 0%][u_alu/spare 0%][u_alu/result 25%]
  by untoggled: [u_alu/result 24 bits][u_alu/spare 9 bits][u_alu/carry_chain 8 bits][u_alu/flags 2 bits]

The two lists share only their membership, not their order, and the disagreement is the whole point:

  • u_alu/result is first by bit count and fourth by percentage. It is the unit's output, 32 bits wide, with 24 of them never observed changing. Whatever this ALU computes, three quarters of its result bus has been constant for the entire regression.
  • u_alu/mode_sel is joint-first by percentage and does not make the top four by bit count. It is 2 bits at 0% — real, and two bits of work.

Both rankings are defensible and they prioritise differently, which is Chapter 11.2 section 7's lesson in a second setting: rank by the quantity you will act on. For "which signal is most likely to be hiding a bug", result at 24 stuck bits is the answer. For "which signals are completely unexercised", the 0% list is.

The -k2,2n -k1,1 and -k3,3rn -k1,1 key specifications are the tie discipline from Module 9 — carry_chain, mode_sel and spare are all at exactly 0.00%, so without the name as a tiebreaker their order is undefined.

7. Common Mistakes

Comparing a toggle percentage against a line percentage as magnitudes. Different populations — section 3.

Summing bits and lines. Chapter 11.4 Exercise 6's 74.35% measures nothing.

Reading the gap without the levels. u_io and u_debug both have a gap of zero and mean opposite things — section 4.

Interpreting a toggle gap on a unit with low line coverage. Most of it is unreached code.

Trusting a block percentage. u_alu at 61.90% has three signals at 0% — section 5.

Not cross-checking a drill-down against its summary. The detail must sum to the block figure.

Ranking signals only by percentage. The 24-bit stuck output lands fourth — section 6.

Sorting tied percentages without a tiebreaker. Three signals sit at exactly 0.00%.

Treating a 0% signal as a coverage problem. It is usually tied off, unreachable or a leftover — a design question first.

8. Exercises

Exercise 1 — Toggle coverage per unit

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk '/^Design Unit:/ { u = $3 } /^  Toggle Coverage/ { printf "%-10s %7.2f%%  %3s of %3s bits\n", u, $3 + 0, substr($4,2), $6; u = "" }' cov.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
alu_core     74.19%   46 of  62 bits
u_regfile    98.44%  126 of 128 bits
u_alu        61.90%   78 of 126 bits
u_ctrl       45.31%   29 of  64 bits
u_io        100.00%   16 of  16 bits
u_debug       0.00%    0 of  24 bits

Exercise 2 — Signals that never toggled

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ && $3 + 0 == 0 { printf "%-22s %2d bit(s)\n", $1, $2 }' toggle.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_alu/carry_chain       8 bit(s)
u_alu/mode_sel          2 bit(s)
u_alu/spare             9 bit(s)

Exercise 3 — Debug a block figure that looks acceptable

A unit reports 61.90% toggle coverage and a reviewer asks whether any signal is completely stuck.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ { n++; if ($3 + 0 == 0) z++ } END { printf "%d signal(s), %d entirely untoggled\n", n, z }' toggle.rpt
awk 'NR>3 && !/^-|^TOTAL/ && $3 + 0 == 0 { b += $2 } END { printf "%d bits in those signals\n", b }' toggle.rpt
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10 signal(s), 3 entirely untoggled
19 bits in those signals

Three of ten signals contribute nothing at all. The block percentage averages them against operand_a at 100%, which is what makes 61.90% look like a uniform shortfall rather than a mix of fully covered and fully dead.

Exercise 4 — Debug a priority list that misses the output bus

A triage list ranks signals by percentage and u_alu/result is fourth.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ { printf "%-22s %6.2f%%  %2d untoggled of %2d\n", $1, $4+0, $2-$3, $2 }' toggle.rpt | sort -k2,2n | head -4
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_alu/carry_chain        0.00%   8 untoggled of  8
u_alu/mode_sel           0.00%   2 untoggled of  2
u_alu/spare              0.00%   9 untoggled of  9
u_alu/result            25.00%  24 untoggled of 32

result has more untoggled bits than the three 0% signals combined have bits, and ranks below all of them. Ranked by the count:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk 'NR>3 && !/^-|^TOTAL/ { printf "%-22s %6.2f%%  %2d untoggled of %2d\n", $1, $4+0, $2-$3, $2 }' toggle.rpt | sort -k3,3rn -k1,1 | head -3
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
u_alu/result            25.00%  24 untoggled of 32
u_alu/spare              0.00%   9 untoggled of  9
u_alu/carry_chain        0.00%   8 untoggled of  8

Exercise 5 — Engineering application

The toggle triage a verification review wants: the block figure cross-checked against the detail, stuck signals separated from partially-covered ones, and both rankings available.

Azvya Education Pvt. Ltd.VLSI Mentor
toggles.awk — synthetic teaching script.
# toggles.awk — toggle triage from a per-signal detail report.
#
# Usage:  awk -f toggles.awk toggle.rpt
#         awk -v expect=61.90 -f toggles.awk toggle.rpt
# Exit:   0 nothing entirely stuck
#         1 at least one signal never toggled
#         2 the detail does not match the block figure given in -v expect

BEGIN { TOL = 0.005 }

/^Signal|^-|^Toggle Coverage Detail/ { next }

$1 == "TOTAL" { rtot_b = $2; rtot_t = $3; next }

NF >= 4 {
    n++
    bits[$1] = $2 + 0
    tog[$1] = $3 + 0
    order[n] = $1
    sumb += $2 + 0
    sumt += $3 + 0
    if ($3 + 0 == 0) { stuck[$1] = 1; nstuck++; stuckbits += $2 + 0 }
}

END {
    if (sumb == 0) { print "ERROR: no signals found" > "/dev/stderr"; exit 2 }
    pct = 100 * sumt / sumb

    # the detail must reproduce the report's own TOTAL row
    if (rtot_b != "" && (rtot_b != sumb || rtot_t != sumt)) {
        printf "ERROR: detail sums to %d of %d but the TOTAL row says %d of %d\n", sumt, sumb, rtot_t, rtot_b > "/dev/stderr"
        exit 2
    }
    # and, when given, the block figure from the summary report
    if (expect != "" && !(pct - expect < TOL + 0.005 && expect - pct < TOL + 0.005)) {
        printf "ERROR: detail gives %.2f%% but the block figure is %.2f%%\n", pct, expect > "/dev/stderr"
        exit 2
    }

    printf "%-22s %5s %5s %8s %6s\n", "signal", "bits", "tog", "percent", "gap"
    for (i = 1; i <= n; i++) {
        s = order[i]
        printf "%-22s %5d %5d %7.2f%% %6d%s\n", s, bits[s], tog[s], 100 * tog[s] / bits[s], bits[s] - tog[s],
               (s in stuck ? "  STUCK" : "")
    }
    printf "#\n# %d signal(s), %d of %d bits = %.2f%%\n", n, sumt, sumb, pct
    printf "# %d signal(s) never toggled, %d bits (%.1f%% of the unit)\n", nstuck + 0, stuckbits + 0, 100 * stuckbits / sumb
    if (stuckbits) printf "# excluding them, the rest reads %.2f%% on %d bits\n", 100 * sumt / (sumb - stuckbits), sumb - stuckbits
    if (nstuck) exit 1
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v expect=61.90 -f toggles.awk toggle.rpt
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
signal                  bits   tog  percent    gap
u_alu/operand_a           32    32  100.00%      0
u_alu/operand_b           32    30   93.75%      2
u_alu/result              32     8   25.00%     24
u_alu/opcode               5     4   80.00%      1
u_alu/flags                4     2   50.00%      2
u_alu/carry_chain          8     0    0.00%      8  STUCK
u_alu/enable               1     1  100.00%      0
u_alu/mode_sel             2     0    0.00%      2  STUCK
u_alu/overflow             1     1  100.00%      0
u_alu/spare                9     0    0.00%      9  STUCK
#
# 10 signal(s), 78 of 126 bits = 61.90%
# 3 signal(s) never toggled, 19 bits (15.1% of the unit)
# excluding them, the rest reads 72.90% on 107 bits
status: 1

The gap column is the untoggled bit count, so the table can be read for either priority without re-sorting, and the three STUCK markers say which rows are a design question rather than a stimulus question.

Two independent cross-checks run before anything is printed: the detail must reproduce its own TOTAL row, and — when -v expect is given from the summary report — it must reproduce the block figure too. Passing a wrong one is refused:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
awk -v expect=70.00 -f toggles.awk toggle.rpt 2>&1 >/dev/null
echo "status: $?"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
ERROR: detail gives 61.90% but the block figure is 70.00%
status: 2

That is the guard proven able to fire, which Chapter 11.2 Exercise 6 argued is the only way to know a guard works.

Exercise 6 — Challenge: two distributions, one headline

u_alu reads 61.90%. Build a second signal list with the same bits, the same toggled count and therefore the same block figure, but no stuck signal at all — then show which statistics distinguish them.

Azvya Education Pvt. Ltd.VLSI Mentor
flat.rpt — synthetic teaching data. Eleven signals summing to exactly 78 of 126 bits, the same as u_alu, with nothing stuck.
Toggle Coverage Detail : u_flat
Signal                        Bits  Toggled   Percent
------------------------------------------------------
u_flat/sig1                     12        7    58.33%
u_flat/sig2                     12        7    58.33%
u_flat/sig3                     12        7    58.33%
u_flat/sig4                     12        7    58.33%
u_flat/sig5                     12        7    58.33%
u_flat/sig6                     12        7    58.33%
u_flat/sig7                     12        7    58.33%
u_flat/sig8                     12        7    58.33%
u_flat/sig9                     12        8    66.67%
u_flat/sig10                    12        8    66.67%
u_flat/sig11                     6        6   100.00%
------------------------------------------------------
TOTAL                          126       78    61.90%

Selecting rows by shape rather than by line number, since the two files have different headers:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
SEL='NF >= 4 && $1 !~ /^(Signal|TOTAL|-)/ && $2 + 0 > 0'
for f in toggle.rpt flat.rpt; do
  printf "  %-12s " "$f"
  awk "$SEL { n++; b += \$2; t += \$3; if (\$3 + 0 == 0) z++; p = 100*\$3/\$2; if (n == 1 || p < lo) lo = p } END { printf \"%d signal(s), %d of %d bits = %.2f%%, %d stuck, lowest signal %.2f%%\n\", n, t, b, 100*t/b, z+0, lo }" $f
done
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  toggle.rpt   10 signal(s), 78 of 126 bits = 61.90%, 3 stuck, lowest signal 0.00%
  flat.rpt     11 signal(s), 78 of 126 bits = 61.90%, 0 stuck, lowest signal 58.33%

Byte-identical on bits, toggled bits and percentage. 78 of 126 = 61.90% for both, and the two reports describe completely different situations:

  • toggle.rpt has three signals that never moved and a 32-bit output at 25%. The work is to find out why three signals are dead, and that is a design question.
  • flat.rpt has nothing below 58.33% and nothing stuck. The work is to broaden the stimulus a little across the board, and that is a testbench question.

The two statistics that separate them are the stuck count and the minimum per-signal percentage, and neither is derivable from the block figure — which is the general claim: a ratio over a population says nothing about the distribution within it.

Note the row selector. NR > 3 would have been wrong here, because the two files do not have the same number of header lines, and a selector keyed to line numbers silently includes a header row or drops a data row when the format shifts. Selecting on NF >= 4 plus a negative name match plus $2 + 0 > 0 keys on what a data row is, which survives both files — the same argument as Chapter 10.6 section 8, where a check had to count markers rather than trust positions.

9. Summary

  • Line coverage is control flow; toggle coverage is data. A line at 0% was never reached; a bit at 0% never changed.
  • Bits and lines are different populations — 85 lines against 126 bits in one unit. Never summed, never compared as magnitudes.
  • Toggle sits at or below line coverage, and the gap is the diagnostic: −1.56 on a well-exercised unit, −26.69 on one whose stimulus is narrow.
  • Read the gap with the levels. A gap of zero at 100% and at 0% mean opposite things.
  • A gap only means something where line coverage is already high.
  • A block percentage hides stuck signals. u_alu at 61.90% has three signals at 0% — 19 bits, 15.1% of the unit.
  • Cross-check a drill-down against its summary, and against the detail's own TOTAL row.
  • The two rankings disagree. The 24-bit stuck output ranks fourth by percentage; a 2-bit signal ranks first.
  • A 0% signal is a design question first — tied off, unreachable, or a leftover.
  • When a derived count disagrees with one you already verified, print the fields. Exercise 6's row selection, not its arithmetic, was the fault.

Chapter 11.6 — FSM Coverage takes the metric that is really two metrics: state coverage and transition coverage are different denominators printed side by side, and a state machine can reach every state while exercising barely half its arcs.

Where this fits

Part of the awk curriculum.