Tcl · Module 24
Parsing an Area Report
Every field sits on its own line, so no state is needed and the regexps are simple. What is not simple is making two runs' numbers comparable — and noticing the field that is not a number at all.
Intermediate17 min readtcledapart-iireportsarearegex
Part II · Module 24 · Chapter 24.2 · Parsing an Area Report
Area is the simplest report in the flow to parse. Every value sits on its own line, labelled, with nothing to pair it against — so none of Chapter 24.1 §4's state machinery is needed.
It is also the report people compare most carelessly, which is where this chapter spends its time.
1. The parser
proc parse_area {path} {
set fh [open $path r]
set a [dict create]
while {[gets $fh line] >= 0} {
if {[regexp {^Combinational area:\s+(\d+\.?\d*)} $line -> v]} { dict set a combinational $v }
if {[regexp {^Noncombinational area:\s+(\d+\.?\d*)} $line -> v]} { dict set a sequential $v }
if {[regexp {^Buf/Inv area:\s+(\d+\.?\d*)} $line -> v]} { dict set a bufinv $v }
if {[regexp {^Macro/Black Box area:\s+(\d+\.?\d*)} $line -> v]} { dict set a macro $v }
if {[regexp {^Total cell area:\s+(\d+\.?\d*)} $line -> v]} { dict set a total $v }
if {[regexp {^Number of (\S+) cells:\s+(\d+)} $line -> k n]} { dict set a cells_$k $n }
}
close $fh
return $a
}=== area, as data ===
bufinv 6190.144531
cells_combinational 11908
cells_sequential 4318
combinational 41820.726562
macro 88000.000000
sequential 29447.281250
total 159268.007812The report is a teaching mock written for this module — its own Version: line says so, and field names differ between tools and releases. The technique is what transfers.
Three details worth naming.
^ on every pattern. Combinational area: also appears inside Noncombinational area:, so an unanchored pattern for the first would match the second line too — and since the if chain has no continue, both assignments would fire, leaving combinational holding the sequential number. Anchoring is not stylistic here; it is the difference between right and wrong. (The reason there is no continue is §2.)
(\d+\.?\d*) not (\d+\.\d+). Cell counts are integers and areas are floats, and \.?\d* accepts both. A pattern demanding a decimal point silently skips every integer field — the Number of lines above.
dict set a cells_$k $n builds the key from the match. One pattern captures combinational and sequential from the label itself, giving cells_combinational and cells_sequential without a line per field. Chapter 7.2 is the mechanism; the idea is that a report with a regular label format can often be parsed with fewer patterns than it has fields.
2. Why there is no continue here
Chapter 24.1 §4 put continue after every match, because a line that is a Startpoint: cannot also be a slack line.
Here the patterns are mutually exclusive by their anchored labels, so continue would be correct but would buy nothing except speed — and leaving it out makes the overlap hazard in §1 visible rather than hidden behind an early exit. In a real parser with dozens of fields, add the continue: it both documents exclusivity and stops a misanchored pattern from doing damage.
3. The field whose value is not a number
Real area reports contain this:
=== the field that is not a number ===
report says : 'Net Interconnect area: undefined (Wire load has zero net area)'
parsed key 'interconnect' exists : 0
numeric pattern matched : 0Two habits follow, and they are the same two from Chapter 7.3:
proc area_field {a key} {
if {![dict exists $a $key]} {
error "area report did not supply '$key'" {} [list RPT MISSINGFIELD $key]
}
return [dict get $a $key]
}Ask before reading, and raise with the report's vocabulary rather than the dictionary's. area report did not supply 'interconnect' points at the file; key not known in dictionary points at your data structure.
And if the field is genuinely optional, say so explicitly with a default rather than discovering it is missing:
set ic [expr {[dict exists $a interconnect] ? [dict get $a interconnect] : 0.0}]The honest caveat: a default of 0.0 for interconnect area is defensible only because undefined here means there is no wire load model, so the true value is not merely unknown — it is absent from the model. A default that papers over a genuinely unknown number is worse than the error it replaced.
4. Cross-check the parse for free
The report prints both the parts and the total, so the parse can check itself:
set c [dict get $a combinational] ; set s [dict get $a sequential]
set m [dict get $a macro] ; set t [dict get $a total]
puts [format " comb + seq + macro : %.6f" [expr {$c + $s + $m}]]
puts [format " total cell area : %.6f" $t]
puts [format " difference : %.6f" [expr {$c + $s + $m - $t}]]=== the arithmetic the report does not print ===
comb + seq + macro : 159268.007812
total cell area : 159268.007812
difference : 0.000000
sequential share : 18.5% of total
buf/inv share : 14.8% of combinationaldifference : 0.000000 to the last digit. That is not decoration — it is a correctness test of the parse that costs nothing and needs no reference data.
If a pattern had picked up the wrong line, matched a truncated number (Chapter 24.1 §7's greediness trap), or missed a component, the sum would not land on the total. A report that states a total next to its parts hands you a free assertion, and Chapter 24.7 generalises exactly this into the module's closing idea.
Note that bufinv is deliberately not in the sum. Buffers and inverters are combinational cells, so their area is already inside combinational — adding it would double-count. Knowing which reported figures are components and which are subsets of other components is report knowledge, not Tcl knowledge, and getting it wrong produces a cross-check that fails on a correct parse.
5. The numbers the report does not print
puts [format " sequential share : %.1f%% of total" [expr {100.0 * $s / $t}]]
puts [format " buf/inv share : %.1f%% of combinational" [expr {100.0 * [dict get $a bufinv] / $c}]]Those two ratios are the reason to parse an area report at all, because they are what a human actually wants:
- Sequential share, 18.5%. Flip-flop area is the floor — optimisation moves combinational logic, not registers. A design that is mostly registers has little headroom, and the number tells you before you spend a week finding out.
- Buf/inv share of combinational, 14.8%. Buffers do no logic; they exist to drive loads. A high share says the design is spending area on distribution rather than computation, which usually points at high-fanout nets or a floorplan that makes everything far apart.
Neither appears in the report. Both are one division away once the fields are in a dict.
6. What makes two runs comparable
The registry's phrase for this chapter is "values a script can compare between runs", and the comparison is where the real mistakes live.
proc compare_area {before after} {
foreach k {combinational sequential macro total} {
set b [dict get $before $k] ; set a [dict get $after $k]
set d [expr {$a - $b}]
puts [format " %-14s %14.3f -> %14.3f %+10.3f %+6.2f%%" \
$k $b $a $d [expr {100.0 * $d / $b}]]
}
}The arithmetic is trivial. The preconditions are not, and most of them cannot be checked from the area report:
- Same technology library. Area is in library units. Two runs against different libraries produce numbers that subtract cleanly and mean nothing. The report names its library — capture that string and compare it too, before comparing any number.
- Same tool release. What counts as combinational area has changed across releases of real tools. The
Version:line is worth parsing for the same reason. - Cell area, not total area.
Total cell areais the sum of the cells.Total areaincludes interconnect estimates and in this report isundefined. Comparing one run's cell area against another's total area is a comparison nobody intends and nothing prevents. - Same design intent. A 3% area increase accompanying a timing fix is a trade, not a regression. The area number alone cannot tell you which.
7. Exercises
Exercise 1. Why must ^ anchor the Combinational area: pattern?
Exercise 2. Why (\d+\.?\d*) rather than (\d+\.\d+)?
Exercise 3. What does the parser do with Net Interconnect area: undefined, and where does the problem surface?
Exercise 4. Why raise area report did not supply 'interconnect' rather than letting dict get raise?
Exercise 5. Why is bufinv excluded from the sum that cross-checks the total?
Exercise 6. What does difference : 0.000000 actually test?
Exercise 7. Name two preconditions for comparing area between runs that the area report cannot verify on its own.
Exercise 8. Why is a 3% area increase not necessarily a regression?
Where this fits
Part of the Tcl curriculum.
