summaryrefslogtreecommitdiff
path: root/scripts/decongest.tcl.in
blob: c257b0150904d00e68a40143eb5553bceee2a672 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
#!TCLSH_PATH
#
# Usage:
#	decongest.tcl <rootname> <leffile> <fillcells> [<scale> <offset>]
#
# Take information produced by qrouter's "congested" command,
# and use it to add fill-cell padding to the most congested
# instances in the .cel file. 
#
# or
#	decongest.tcl <rootname> <leffile> <fillcells> <density>
#
# Take the existing <rootname>.cel file for graywolf input,
# and pad the area out with randomly-assigned fill cells
# to reduce the density of actively routed cells in the design.
#
# This procedure is necessary because GrayWolf does not
# have a way (apparently) to reduce internal density by
# adding spacers.  So we use feedback from the router to
# find targeted areas to add extra fill.  Since GrayWolf
# only looks at the contents of the .cel file, padding can
# be added by rewriting the cell width for specific cells
# to include the width of one or more filler cells.  By
# annotating the cell instance name, it becomes easy to
# add the fill cells into the DEF file after placement.
#
# "fillcells" is either a single name for a fill cell, or a
# comma-separated list of three fill cells (of which one or two may
# be empty strings) for plain fill, decap fill, and antenna fill.
#
# "scale" gives a scalefactor which is the amount of
# increase in congestion that will cause an additional fill
# cell to be added.  Default is 0.1.  Scale will be adjusted
# upward according to the percentage of failing nets.

# "offset" is the amount of congestion in the .cinfo file
# that is the baseline for adding fill.  Should be equal to
# the minimum amount of congestion for which a fill cell is
# added.  Default is 0.6.
#
# "density" is a fraction of the area of the design that
# is made up of actively routed cells;  that is, the area
# given to fill cells is (1 - density).  It is used in place
# of "<scale> <offset>", and does not expect a ".cinfo" file
# of congestion information.  The fill cells will be randomly
# placed.  There is no default density.
#
# Option --units=<units> where units are distance units per micron
# in the .cel file (default 100)
#
#------------------------------------------------------------
# Written by Tim Edwards, November 23, 2013
#------------------------------------------------------------
# LEF dimensions are microns unless otherwise stated.
#------------------------------------------------------------

# To-do:  Just split the argument list into options and arguments,
# where options begin with "--".  For now, just assume options come
# at the end.

set units 100

while {true} {
    set option [lindex $argv $argc-1]
    if {[string first "--" $option] == 0} {
	incr argc -1
	set optkey [string range $option 2 end]
        set eqptr [string first "=" $optkey]
	if {$eqptr > 0} {
	    set optval [string range $optkey $eqptr+1 end]
	    set optkey [string range $optkey 0 $eqptr-1]
	} else {
	    set optval true
	}
	switch -exact $optkey {
	    units {set units $optval}
	    default {puts stderr "Unknown option switch $option"}
	}
    } else {
	break
    }
}

if {$argc < 4 || $argc > 7} {
   puts stderr "Bad argument list"
   puts stderr "Usage: decongest.tcl <rootname> <leffile> <fillcell> [<scale> <offset>]"
   puts stderr "or:    decongest.tcl <rootname> <leffile> <fillcell> <density>"
   puts stderr "or:    decongest.tcl <rootname> <leffile> <fillcells> <density> <ratios>"
   puts stderr "<fillcell> is a comma-separated list of up to three fill cell types."
   puts stderr "<ratios> is a space-separated list of ratios for each fill cell type."
   puts stderr "use option --units=<units> to set units other than centimicrons."
   exit 1
}

set scale 0.1
set offset 0.5

set cellname [file rootname [lindex $argv 0]]

set celfile ${cellname}.cel
set annofile ${cellname}.acel
set cinfofile ${cellname}.cinfo
set lefname [lindex $argv 1]
set filltypes [split [lindex $argv 2] ,]
set fillratios {}

if {$argc == 4} {
   set density [lindex $argv 3]
   set scale 0.0	;# scale 0 used to denote density planning
} elseif {$argc == 5} {
   set scale [lindex $argv 3]
   set offset [lindex $argv 4]
   # Check if fills were passed as a single argument, space or comma
   # separated.  If so, this is not scale and offset.
   if {[llength $offset] > 1} {
      set density $scale
      set scale 0.0
      set fillratios $offset
   } elseif {[llength [split $offset ,]] > 1} {
      set density $scale
      set scale 0.0
      set fillratios {}
      set tmpfillr [split $offset ,]
      for {set i 0} {$i < [llength $tmpfillr]} {incr i} {
         set rfill [lindex $tmpfillr $i]
	 if {$rfill == {}} {set rfill 0}
	 lappend fillratios $rfill
      }
   }
} else {
   # For 6 or 7 arguments.  Note that there is no 5-argument syntax
   # for ratios, since if only one fill type is specified, then by
   # definition it must be 100% of the fill.

   set scale 0.0
   set density [lindex $argv 3]
   for {set i 0} {$i < [expr {$argc - 4}]} {incr i} {
      lappend fillratios [lindex $argv [expr {$i + 4}]]
   }
}

if [catch {open $lefname r} flef] {
   puts stderr "Error: can't open file $lefname for input"
   return
}

if [catch {open $celfile r} fcel] {
   puts stderr "Error: can't open file $celfile for input"
   return
}

if {$scale > 0.0} {
   if [catch {open $cinfofile r} finf] {
      puts stderr "Error: can't open file $cinfofile for input"
      return
   }
}

if [catch {open $annofile w} fanno] {
   puts stderr "Error: can't open file $annofile for output"
   return
}

#----------------------------------------------------------------
# Check filltypes and determine how many are requested.  This
# code is complicated mostly to allow flexibility in the way
# fill cells and their respective percentages are specified.
#----------------------------------------------------------------

# Count fill types by adding all non-empty-string components of $filltypes
set num_filltypes 0
foreach filltype $filltypes {
   if {$filltype != ""} {incr num_filltypes}
}

if {$num_filltypes > 3} {
   puts stderr "Can only handle up to three fill cell types.  Ignoring the rest."
   set filltypes [lrange $filltypes 0 2]
} elseif {$filltypes == {}} {
   puts stderr "Warning:  No fill cell types specified."
}

# If filltypes is truncated, then pad the array.  However, it is
# much preferable for the caller to supply all three entries,
# with empty strings for those that don't exist, because the
# following method does not try to match fill cell names to fill
# cell types.

if {[llength $filltypes] == 1} {
      lappend filltypes ""
      lappend filltypes ""
} elseif {[llength $filltypes] == 2} {
      lappend filltypes ""
}

# Automatically assign fill ratios if ratios were not specified.

if {$fillratios == {}} {
   if {$num_filltypes == 1} {
      foreach filltype $filltypes {
	  if {$filltype != ""} {
	      lappend fillratios 100
	  } else {
	      lappend fillratios 0
	  }
      }
   } elseif {$num_filltypes == 2} {
      foreach filltype $filltypes {
	  if {$filltype != ""} {
	      lappend fillratios 50
	  } else {
	      lappend fillratios 0
	  }
      }
   } else {
      set fillratios {34 33 33}
   }
}

if {[llength $filltypes] == 3} {
   if {[llength $fillratios] > 3} {
      puts stderr "Error:  Too many fill ratios.  Ignoring all but the first three."
      set fillratios [lrange $fillratios 0 2]
   }

   if {[llength $fillratios] == 3} {
      set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1] + [lindex $fillratios 2]}]
      if {$addr > 100} {
         puts stderr "Error:  Fill ratios sum to greater than 100 percent."
         set fillratios [lrange $fillratios 0 1]
      }
   }

   if {[llength $fillratios] == 2} {
      set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1]}]
      if {$addr > 100} {
         puts stderr "Error:  First two fill ratios sum to greater than 100 percent."
         set fillratios [lindex $fillratios 0]
      } else {
         lappend fillratios [expr {100 - [lindex $fillratios 0] - [lindex $fillratios 1]}]
         lappend fillratios [expr {100 - $addr}]
      }
   }

   if {[llength $fillratios] == 1} {
      puts stderr "Error:  Only one fill ratio.  Evenly splitting the remainder."
      set remain [expr {int((100 - [lindex $fillratios 0]) / 2)}]
      lappend fillratios $remain
      set addr [expr {[lindex $fillratios 0] + [lindex $fillratios 1]}]
      lappend fillratios [expr {100 - $addr}]
   }
}

#----------------------------------------------------------------
# Read through a LEF file section that we don't care about.
#----------------------------------------------------------------

proc skip_section {leffile sectionname} {
   while {[gets $leffile line] >= 0} {
      if [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch sectiontest] {
         if {"$sectiontest" != "$sectionname"} {
            puts -nonewline stderr "Unexpected END statement $line "
            puts stderr "while reading section $sectionname"
         }
         break
      }
   }
}

#----------------------------------------------------------------
# Parse the macro contents of the LEF file and retain the information
# about cell size and pin positions.
#----------------------------------------------------------------

proc parse_macro {leffile macroname} {
   global $macroname units

   while {[gets $leffile line] >= 0} {
      if [regexp {[ \t]*SYMMETRY[ \t]+(.+)[ \t]*;} $line lmatch symmetry] {
         set ${macroname}(symmetry) $symmetry
      } elseif [regexp {[ \t]*ORIGIN[ \t]+(.+)[ \t]+(.+)[ \t]*;} $line lmatch x y] {
         set x [expr {int($x * $units + 0.5)}]
         set y [expr {int($y * $units + 0.5)}]
         set ${macroname}(x) $x
         set ${macroname}(y) $y
      } elseif [regexp {[ \t]*SIZE[ \t]+(.+)[ \t]+BY[ \t]+(.+)[ \t]*;} \
                        $line lmatch w h] {
         set w [expr {int($w * $units + 0.5)}]
         set h [expr {int($h * $units + 0.5)}]
         set ${macroname}(w) $w
         set ${macroname}(h) $h

      } elseif [regexp {[ \t]*PIN[ \t]+(.+)[ \t]*$} $line lmatch pinname] {
	 # The fill cell is not expected to have any usable pins
	 skip_section $leffile $pinname
      } elseif [regexp {[ \t]*END[ \t]+([^ \t]+)[ \t]*$} $line lmatch macrotest] {
         if {"$macrotest" == "$macroname"} {
            break
         } else {
            puts stderr "Unexpected END statement $line while reading macro $macroname"
         }
      }
   }
}

#-----------------------------------------------------------------
# Read the lef macro file and get the fill cells and their widths
#-----------------------------------------------------------------

puts stdout "Reading ${filltypes} macros from LEF file."
flush stdout

set fillcells {}
set decapcells {}
set antennacells {}

set fillcell [lindex $filltypes 0]
set decapcell [lindex $filltypes 1]
set antennacell [lindex $filltypes 2]

while {[gets $flef line] >= 0} {
   if [regexp {[ \t]*MACRO[ \t]+(.+)[ \t]*$} $line lmatch macroname] {
      # Parse the "macro" statement
      parse_macro $flef $macroname
      if {$fillcell != "" && [regexp "^$fillcell" $macroname] == 1} {
	 # Remember this for later if it's a plain fill cell
	 lappend fillcells $macroname
      } elseif {$decapcell != "" && [regexp "^$decapcell" $macroname] == 1} {
	 # Remember this for later if it's a decap fill cell
	 lappend decapcells $macroname
      } elseif {$antennacell != "" && [regexp "^$antennacell" $macroname] == 1} {
	 # Remember this for later if it's a antenna fill cell
	 lappend antennacells $macroname
      }
   } elseif [regexp {[ \t]*LAYER[ \t]+([^ \t]+)} $line lmatch layername] {
      skip_section $flef $layername
   } elseif [regexp {[ \t]*VIA[ \t]+([^ \t]+)} $line lmatch vianame] {
      skip_section $flef $vianame
   } elseif [regexp {[ \t]*VIARULE[ \t]+([^ \t]+)} $line lmatch viarulename] {
      skip_section $flef $viarulename
   } elseif [regexp {[ \t]*SITE[ \t]+(.+)[ \t]*$} $line lmatch sitename] {
      skip_section $flef $sitename
   } elseif [regexp {[ \t]*UNITS[ \t]*$} $line lmatch] {
      skip_section $flef UNITS
   } elseif [regexp {[ \t]*SPACING[ \t]*$} $line lmatch] {
      skip_section $flef SPACING
   } elseif [regexp {[ \t]*END[ \t]+LIBRARY[ \t]*$} $line lmatch] {
      break
   } elseif [regexp {^[ \t]*#} $line lmatch] {
      # Comment line, ignore.
   } elseif ![regexp {^[ \t]*$} $line lmatch] {
      # Other things we don't care about
      set matches 0
      if [regexp {[ \t]*NAMESCASESENSITIVE} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*VERSION} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*BUSBITCHARS} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*DIVIDERCHAR} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*USEMINSPACING} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*CLEARANCEMEASURE} $line lmatch] {
         incr matches
      } elseif [regexp {[ \t]*MANUFACTURINGGRID} $line lmatch] {
         incr matches
      } else {
         puts stderr "Unexpected input in LEF file:  Only macro defs were expected!"
         puts -nonewline stdout "Line is: $line"
	 flush stdout
      }
   }
}

# If the macro file doesn't define any fill cells, there's not a
# whole lot we can do. . .

set totalfills [expr {[llength $fillcells] + [llength $decapcells] + \
	[llength $antennacells]}]
set usedfills [expr {[llength $fillcells] * [lindex $fillratios 0] + \
	[llength $decapcells] * [lindex $fillratios 1] + \
	[llength $antennacells] * [lindex $fillratios 2]}]

if {$totalfills == 0} {
   puts stdout "No fill cells (${fillcell}, ${decapcell}, ${antennacell}) found in macro file ${lefname}!"
   close $flef
   exit 1
}
if {$usedfills == 0} {
   puts stdout "No fill cells in macro file ${lefname} are being used!"
   set foundcells {}
   if {[llength $fillcells] > 0} {lappend foundcells $fillcell}
   if {[llength $decapcells] > 0} {lappend foundcells $decapcell}
   if {[llength $antennacells] > 0} {lappend foundcells $antennacell}
   puts stdout "Fill cells found are: $foundcells"
   close $flef
   exit 1
}

close $flef

# Sort each array of fill cells by width

set fillwidths {}
foreach macro $fillcells {
   lappend fillwidths [list $macro [subst \$${macro}(w)]]
}
set fillwidths [lsort -decreasing -index 1 -real $fillwidths]
set fillinfo [lindex $fillwidths 0]
set fillmacro [lindex $fillinfo 0]
set fillvalue [lindex $fillinfo 1]

set decapwidths {}
foreach macro $decapcells {
   lappend decapwidths [list $macro [subst \$${macro}(w)]]
}
set decapwidths [lsort -decreasing -index 1 -real $decapwidths]
set decapinfo [lindex $decapwidths 0]
set decapmacro [lindex $decapinfo 0]
set decapvalue [lindex $decapinfo 1]

set antennawidths {}
foreach macro $antennacells {
   lappend antennawidths [list $macro [subst \$${macro}(w)]]
}
set antennawidths [lsort -decreasing -index 1 -real $antennawidths]
set antennainfo [lindex $antennawidths 0]
set antennamacro [lindex $antennainfo 0]
set antennavalue [lindex $antennainfo 1]

#------------------------------------------------------------------------
# Now read the contents of the cinfo file so that we have a list of the
# cells to add padding to
#------------------------------------------------------------------------

if {$scale > 0.0} {

   gets $finf line		;# Throw-away line
   gets $finf line		;# Throw-away line
   gets $finf line		;# Failures: X Y

   if {![regexp {[ \t]*Failures:[ \t]+([0-9]+)[ \t]+([0-9]+)} $line \
		lmatch failures numnets]} {
      puts stdout "Cannot parse number of route failures from .cinfo file!"
      exit 1
   }

   gets $finf line		;# Throw-away line

   #------------------------------------------------------------------------
   # Scale and offset adjustments
   #------------------------------------------------------------------------

   set failratio [expr {($failures + 0.0) / $numnets}]

   # Redefine scale and offset so that the calculations are easier
   # Adjust the scale according to the fail ratio

   set scale [expr {1.0 / $scale}]
   set scale [expr {$scale + 60 * $failratio}]
   set offset [expr {$offset - (1.0 / $scale)}]

   #------------------------------------------------------------------------

   set instlist {}
   set filllist {}
   while {[gets $finf line] >= 0} {
      if [regexp {[ \t]*([^ \t]+)[ \t]+([^ \t]+)} $line lmatch instname congest] {
         set numfill [expr {int(($congest - $offset) * $scale + 0.5)}]
         if {$numfill > 0} {
            lappend instlist $instname
            lappend filllist $numfill
         }
      }   
   }

   close $finf
} else {

   #------------------------------------------------------------------------
   # Fixed density planning (No .cinfo file, just fixed density value)
   # Read .cel file to get a list of all instances.  Record the total width
   # of all cells.  Determine how many fill cells need to be added to
   # reduce the density of actively routed cells to the specified amount.
   # Assign fill cells until the density requirement is met.  Use a sigma-
   # delta type approach to assigning cells to maintain close to the right
   # ratio of widths for each fill type.
   #------------------------------------------------------------------------

   set totalwidth 0
   set instlist {}
   while {[gets $fcel line] >= 0} {
      if [regexp {[ \t]*cell[ \t]*([0-9]+)[ \t]+([^ \t]+)} $line \
		lmatch instnum instname] {
         lappend instlist $instname
	 gets $fcel line
	 regexp {[ \t]*left[ \t]+([-]*[0-9]+)[ \t]+right[ \t]+([-]*[0-9]+)} $line \
		lmatch left right
	 incr totalwidth [expr {$right - $left}]
      }
   }

   # alltotal is the total width of fill needed to achieve the specified
   # density.

   set alltotal [expr {$totalwidth * ((1.0 / $density) - 1.0)}]

   # calculate the total width of each type of fill cell to be added
   # to make up the total required fill.

   set filltotal [expr {int($alltotal * [lindex $fillratios 0] / 100)}]
   set decaptotal [expr {int($alltotal * [lindex $fillratios 1] / 100)}]
   set antennatotal [expr {int($alltotal - ($filltotal + $decaptotal))}]

   set numcells [llength $instlist]

   # Diagnostic information
   puts stdout ""
   puts stdout "decongest.tcl:"
   puts stdout "Fixed density planning, density = $density"
   puts stdout "Number of cells = $numcells, total width = $totalwidth"
   puts stdout "Width of fill = $filltotal"
   puts stdout "Width of decap = $decaptotal"
   puts stdout "Width of antenna  = $antennatotal"
   puts stdout ""

   # Rewind the cel file for the next step
   seek $fcel 0 start
}

#------------------------------------------------------------------------
# Now read the contents of the cel file.  When a cell is found that is
# in the "instlist" list of cells, annotate the next line to increase
# the width by an amount equal to the width of a fill cell.
#
# Sigma delta approach:  Keep running total of the widths of all cells
# of each fill type used and a running total of the width of all cells
# in the design parsed so far.
#
# For each cell in the design:
# 1. Add the width of the cell to the running total
# 2. For each fill cell type,
#    A. Calculate the ratio of total width of the fill cells used so far
#	to the total width of standard cells parsed so far;
#    B. Knowing the target ratio of fill cell width to total cell width,
#	calculate the width of fill needed to add to reach that ratio.
#    C. Find the largest available fill cell width that does not exceed
#	the target ratio.
#    D. If there is such a fill cell, attach it to the standard cell and
#       add its width to the running total of fill cell width.
#
# Values used:
#    Total standard cell width = totalwidth
#    Aggregate standard cell width = aggwidth
#    Target fill cell width = filltotal
#    Target fill cell ratio = fillratio = (filltotal / totalwidth)
#    Aggregate fill cell width = fillagg
#    Target decap cell width = decaptotal
#    Target decap cell ratio = decapratio = (decaptotal / totalwidth)
#    Aggregate decap cell width = decapagg
#    Target antenna cell width = antennatotal
#    Target antenna cell ratio = antennaratio = (antennatotal / totalwidth)
#    Aggregate antenna cell width = antennaagg
# 
#------------------------------------------------------------------------

set fillratio    [expr {(0.0 + $filltotal) / $totalwidth}]
set decapratio   [expr {(0.0 + $decaptotal) / $totalwidth}]
set antennaratio [expr {(0.0 + $antennatotal) / $totalwidth}]

set fillagg 0
set decapagg 0
set antennaagg 0

set aggwidth 0

while {[gets $fcel line] >= 0} {
   if [regexp {[ \t]*cell[ \t]*([0-9]+)[ \t]+([^ \t]+)} \
		$line lmatch instnum instname] {
      set instidx [lsearch $instlist $instname]

      gets $fcel cline
      if ![regexp {[ \t]*left[ \t]+([^ \t]+)[ \t]+right[ \t]+([^ \t]+)[ \t]+(.*)} \
			$cline lmatch left right rest] {
         puts $fanno $line	;# failed to parse, so output unchanged
         puts $fanno $cline
      } else {

         # Calculate aggregate cell width
	 set cellwidth [expr {$right - $left}]
         # puts stdout "Diagnostic: instance $instname cell width = $cellwidth"
         incr aggwidth $cellwidth
	 if {$instidx < 0} {
            puts $fanno $line	;# unknown cell, so output unchanged
            puts $fanno $cline
         } else {
	    set needfill [expr {$aggwidth * $fillratio}]
            set cstr ""
	    set lastfill ""
	    set lastwid 0
	    while {$needfill > $fillagg} {
	       set cwid 0
	       foreach fillinfo $fillwidths {
		  set cmac [lindex $fillinfo 0]
		  set cwid [lindex $fillinfo 1]
		  if {[expr {$fillagg + $cwid}] < $needfill} {
		     break
		  }
	       }
	       if {$cwid == 0} {break}
	       incr fillagg $cwid
	       incr cellwidth $cwid
               # puts stdout "Diagnostic: add fill width = $cwid"
               # puts stdout "Diagnostic: new cell width = $cellwidth"
	       if {$lastfill == ""} {
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       } elseif {$lastfill == $cmac} {
		  incr multiplier
	       } else {
		  if {$multiplier == 1} {
		     set cstr ${lastfill}.${lastwid}.${cstr}
		  } else {
		     set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
		  }
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       }
	    }
	    if {$lastfill != ""} {
	       if {$multiplier == 1} {
		  set cstr ${lastfill}.${lastwid}.${cstr}
	       } else {
		  set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
	       }
	    }

	    set needdecap [expr {$aggwidth * $decapratio}]
	    set lastfill ""
	    set lastwid 0
	    while {$needdecap > $decapagg} {
	       set cwid 0
	       foreach decapinfo $decapwidths {
		  set cmac [lindex $decapinfo 0]
		  set cwid [lindex $decapinfo 1]
		  if {[expr {$decapagg + $cwid}] < $needdecap} {
		     break
		  }
	       }
	       if {$cwid == 0} {break}
	       incr decapagg $cwid
	       incr cellwidth $cwid
               # puts stdout "Diagnostic: add decap width = $cwid"
               # puts stdout "Diagnostic: new cell width = $cellwidth"
	       if {$lastfill == ""} {
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       } elseif {$lastfill == $cmac} {
		  incr multiplier
	       } else {
		  if {$multiplier == 1} {
		     set cstr ${lastfill}.${lastwid}.${cstr}
		  } else {
		     set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
		  }
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       }
	    }
	    if {$lastfill != ""} {
	       if {$multiplier == 1} {
		  set cstr ${lastfill}.${lastwid}.${cstr}
	       } else {
		  set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
	       }
	    }

	    set needantenna [expr {$aggwidth * $antennaratio}]
	    set lastfill ""
	    set lastwid 0
	    while {$needantenna > $antennaagg} {
	       set cwid 0
	       foreach antennainfo $antennawidths {
		  set cmac [lindex $antennainfo 0]
		  set cwid [lindex $antennainfo 1]
		  if {[expr {$antennaagg + $cwid}] < $needantenna} {
		     break
		  }
	       }
	       if {$cwid == 0} {break}
	       incr antennaagg $cwid
	       incr cellwidth $cwid
               # puts stdout "Diagnostic: add antenna width = $cwid"
               # puts stdout "Diagnostic: new cell width = $cellwidth"
	       if {$lastfill == ""} {
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       } elseif {$lastfill == $cmac} {
		  incr multiplier
	       } else {
		  if {$multiplier == 1} {
		     set cstr ${lastfill}.${lastwid}.${cstr}
		  } else {
		     set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
		  }
		  set lastfill $cmac
		  set lastwid $cwid
		  set multiplier 1
	       }
	    }
	    if {$lastfill != ""} {
	       if {$multiplier == 1} {
		  set cstr ${lastfill}.${lastwid}.${cstr}
	       } else {
		  set cstr ${lastfill}.${lastwid}X${multiplier}.${cstr}
	       }
	    }
            
	    puts $fanno "cell $instnum ${cstr}${instname}"

            # puts stdout "Diagnostic: final cell width = $cellwidth"
	    set fleft [expr {-($cellwidth / 2)}]
	    set fright [expr {$fleft + $cellwidth}]
	    puts $fanno "left $fleft right $fright $rest"
	 }
      }
   } else {
      puts $fanno $line
   }
}

puts stdout "Done!"