Skip to content
VLSI Mentor

Tcl · Module 24

Parsing a Power Report

One report, three different units, no error. This is the chapter where dropping a two-character string inverts the engineering conclusion.

Intermediate19 min readtcledapart-iireportspowerunits

Part II · Module 24 · Chapter 24.3 · Parsing a Power Report

Timing reports are all in nanoseconds. Area reports are all in library units. A power report is not all in one unit, and that single fact is this chapter.

1. What the report actually says

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the four lines that matter, verbatim ===
    'Cell Internal Power  =   8.4213 mW   (62%)'
    'Net Switching Power  =   5.1680 mW   (38%)'
    'Total Dynamic Power    =  13.5893 mW  (100%)'
    'Cell Leakage Power     = 742.3100 uW'

Three lines in mW. One line in uW.

That is not a quirk of the mock report used here — it is the normal shape, because the report declares its dynamic and leakage units separately in its own header:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the unit declarations in the report header ===
Power-specific unit information :
    Dynamic Power Units = 1mW    (derived from V,C,T units)
    Leakage Power Units = 1pW

Dynamic power and leakage power differ by three to six orders of magnitude in a modern design, so a single unit would print either as a long string of zeros or in exponential notation. Reports solve that by using different units per quantity, and by saying so in a header block most people never read.

Note also that the declared leakage unit is 1pW while the leakage summary line prints uW. Both are true: the declaration governs the group table at the bottom of the report, and the summary line carries its own suffix. A parser that reads the declaration and applies it everywhere would be wrong, and a parser that reads suffixes and ignores the declaration cannot interpret the group table at all. §5 is about that split.

2. The parser that drops them

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc parse_power_naive {path} {
    set fh [open $path r] ; set p [dict create]
    while {[gets $fh line] >= 0} {
        if {[regexp {^\s*Cell Internal Power\s+=\s+(\d+\.?\d*)}  $line -> v]} { dict set p internal  $v }
        if {[regexp {^\s*Net Switching Power\s+=\s+(\d+\.?\d*)}  $line -> v]} { dict set p switching $v }
        if {[regexp {^\s*Cell Leakage Power\s+=\s+(\d+\.?\d*)}   $line -> v]} { dict set p leakage   $v }
    }
    close $fh ; return $p
}

Nothing in it is wrong as Tcl. The patterns are anchored, braced and correct; each field is found; regexp returns 1 three times.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== parsed WITHOUT the units — every number looks comparable ===
    internal   8.4213
    switching  5.1680
    leakage    742.3100
    naive total : 755.8993  <- leakage counted as if it were mW
    and leakage would be 98.2% of it

This is the same failure shape as Chapter 24.1 §7's truncated slack: a successful match producing a plausible wrong number. There the cause was a regex rule; here it is throwing away part of the value. Both are invisible to every check except arithmetic that knows what the answer should be.

3. Keep the unit with the number

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
array set ::SCALE {W 1.0 mW 1e-3 uW 1e-6 nW 1e-9 pW 1e-12}

proc to_watts {value unit} {
    if {![info exists ::SCALE($unit)]} {
        error "unknown power unit '$unit'" {} [list RPT BADUNIT $unit]
    }
    return [expr {$value * $::SCALE($unit)}]
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
proc parse_power {path} {
    set fh [open $path r] ; set p [dict create]
    set fields {
        {internal  {^\s*Cell Internal Power\s+=\s+(\d+\.?\d*)\s*([munpW]+)}}
        {switching {^\s*Net Switching Power\s+=\s+(\d+\.?\d*)\s*([munpW]+)}}
        {leakage   {^\s*Cell Leakage Power\s+=\s+(\d+\.?\d*)\s*([munpW]+)}}
    }
    while {[gets $fh line] >= 0} {
        foreach f $fields {
            set key [lindex $f 0] ; set re [lindex $f 1]
            set v "" ; set u ""
            if {[regexp $re $line -> v u]} {
                dict set p $key [list $v $u [to_watts $v $u]]
            }
        }
    }
    close $fh ; return $p
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== parsed WITH the unit kept alongside ===
    FIELD           VALUE UNIT          WATTS
    internal       8.4213 mW      0.008421300
    switching      5.1680 mW      0.005168000
    leakage      742.3100 uW      0.000742310
    TOTAL                         0.014331610

=== the same question, answered correctly ===
    total power   : 14.3316 mW
    leakage share : 5.2%
    (the unit-blind parser said 98.2%)

Four design decisions in that parser, each worth stating.

The unit is captured, not assumed. ([munpW]+) reads the suffix as part of the field. A parser that hard-codes "leakage is in µW" is correct until a tool release or a different setup changes it, and then it is wrong in exactly the silent way §2 describes.

Each entry stores all three forms — {742.3100 uW 0.00074231}. The original value and unit are kept so a report can be reproduced verbatim, and the normalised watts figure is kept so arithmetic is possible. Throwing away either one loses something: the raw pair cannot be summed, and the watts figure alone cannot be printed back in the units the engineer expects.

Normalisation happens once, at parse time. The alternative — converting at each use — means every consumer has to remember, and one that forgets reintroduces §2. Converting on entry makes the invariant "everything inside this dict is watts" true by construction.

The patterns live in a list of {key regex} pairs. Adding a field is one line rather than one if. The foreach over $fields with lindex is the Part I shape; note the set v ""/set u "" before each regexp, which is Chapter 13.9's rule — a failed match leaves the capture variables holding the previous line's values, so in a loop they must be cleared or a non-matching line silently reuses stale data.

4. The parse checks itself again

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    total power   : 14.3316 mW

Compare that against the report's own bottom line, which the parser never read:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the report's own total row ===
Total              8.4213 mW        5.1680 mW   7.4231e+05 pW       14.3316 mW

14.3316 mW, to the last digit. Independently computed from three fields in two different units, and it lands exactly on the figure the tool printed.

That is Chapter 24.2 §4's free cross-check doing more work than it did there. In the area report the sum validated field extraction. Here it validates the unit conversion as well, and the margins are wide enough to be unmistakable:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== what a WRONG leakage scale would give as the total ===
    leakage scaled as uW  -> total 14.3316 mW
    leakage scaled as nW  -> total 13.5900 mW
    leakage scaled as pW  -> total 13.5893 mW
    leakage scaled as mW  -> total 755.8993 mW

Only one row reproduces the report. Every wrong scale is caught, and no pattern test could have caught any of them — the patterns match identically in all four cases.

Worth noticing which way the errors fall. Scaling leakage too small (nW, pW) gives 13.59 mW — within 5% of the truth, which is close enough to look like a rounding difference and be accepted. Scaling it too large (mW) gives 755.9 mW, which is absurd enough that someone would notice. The dangerous direction is the one that produces a believable number, and that is the direction a cross-check against the report's own total catches and eyeballing does not.

Whenever a report states a total beside its components, compute it and compare. It is the cheapest correctness test available and it needs no reference run.

5. What a unit table cannot fix

Being honest about the limits, because a conversion table invites more trust than it earns.

A report that omits the unit. The group table at the bottom of this report prints bare numbers under Internal Power and Switching Power columns, with the unit stated only in the header block and the total row. A pattern reading those columns gets a float and no suffix, and the only thing that makes it interpretable is the declared Dynamic Power Units — which must be parsed from the header and applied. Capturing the unit from the field works for = 8.4213 mW lines and not for table columns.

A scale that is per-report, not universal. Leakage Power Units = 1pW is a setting. Another run of the same design with a different library can declare different units, so a hard-coded table is a statement about one report. The robust version reads the declaration — and the obvious way to write that pattern does not work:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
regexp {Power Units\s*=\s*1(\S+?)\s}    $line -> u    ;# one of these fails
regexp {Power Units\s*=\s*1([a-zA-Z]+)} $line -> u    ;# the other does not
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== reading the declaration: one pattern works on both lines, one does not ===
    Dynamic Power Units       \S+?-then-\s : 1 'mW'   [a-zA-Z]+ : 1 'mW'
    Leakage Power Units       \S+?-then-\s : 0 ''   [a-zA-Z]+ : 1 'pW'

The general form of the lesson: a pattern that relies on what comes after the field is making an assumption about the rest of the line, and the last line of a block, the last field of a row, and the last line of a file are exactly where that assumption fails.

Exponential notation. The group table's leakage column reads 7.4231e+05. Tcl's expr handles that string as a number without special effort, but a pattern of (\d+\.?\d*) does not capture it whole:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== the exponent the numeric pattern silently truncates ===
    plain  : 1 -> '7.4231'
    exp ok : 1 -> '7.4231e+05'

It matched — 1 — and captured 7.4231, stopping at the e and dropping five orders of magnitude. The fix allows an exponent:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
{(\d+\.?\d*(?:[eE][-+]?\d+)?)}

(?:…) is a non-capturing group, so the exponent is part of the number rather than a separate capture — which matters because -> v binds capture groups positionally, and an extra group would shift every variable after it.

This is the third failure in this chapter with the same signature: regexp returns 1, a number comes back, and the number is wrong. A successful match is not evidence that the right thing was matched.

6. Refuse an unknown unit

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
=== an unrecognised unit is refused, not guessed ===
    1 / unknown power unit 'MW'

to_watts raises on a suffix it does not know, rather than defaulting to 1.0 or to the last unit seen.

That is the Chapter 23.6 argument applied to arithmetic: the discipline you can rely on is the one the code enforces. A conversion function with a fallback is a conversion function that will eventually convert something wrongly and tell nobody — and MW versus mW is exactly the kind of difference that produces a nine-order-of-magnitude error from a one-character change.

The errorCode is RPT BADUNIT MW, so a caller can distinguish "this report uses a unit I have not been taught" from a genuine parse failure and extend the table, which is Chapter 14.2's reason for machine-readable error codes.

7. Exercises

Exercise 1. Why does one power report use more than one unit?

Exercise 2. The unit-blind parser reported leakage as 98.2% of total power. What is the real figure, and why is the wrong one dangerous rather than merely inaccurate?

Exercise 3. Why capture the unit from each line instead of hard-coding "leakage is in µW"?

Exercise 4. Why store the raw value, the unit and the watts figure?

Exercise 5. Why normalise at parse time rather than at each use?

Exercise 6. Why are set v "" and set u "" needed before each regexp inside the loop?

Exercise 7. The computed total matched the report's own 14.3316 mW. What does that check that the area-report version did not, and which wrong scale would have been hardest to notice?

Exercise 8. Why does (\d+\.?\d*) mishandle 7.4231e+05, and what does it return?

Exercise 9. Why should to_watts raise on an unknown unit rather than defaulting?

Where this fits

Part of the Tcl curriculum.