-
Notifications
You must be signed in to change notification settings - Fork 2
/
magicDrc
executable file
·873 lines (785 loc) · 31.9 KB
/
magicDrc
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
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
#!/bin/bash
# Copyright (C) 2015, 2020 efabless Corporation. All Rights Reserved.
# filter out most options, so magic Natively sees/handles *only* -T <file>.
# for-bash\
declare -a C ; declare -a N ; export _CE= _NE= _M0= ;\
for i in "$@" ; do _M0="$_M0${_M0:+ }\"${i//\"/\\\"}\""; done ;\
while getopts "NFT:S:l:P:" o; do \
: echo got "optchar $o, with optarg $OPTARG" ;\
case "$o" in S) \
C+=(-${o} "$OPTARG") ;\
continue ; esac ;\
case "$o" in P) \
C+=(-${o} "$OPTARG") ;\
continue ; esac ;\
case "$o" in F|N) \
C+=(-${o}) ;\
continue ; esac ;\
case "$o" in l) \
C+=(-${o} "$OPTARG") ;\
continue ; esac ;\
case "$o" in T) \
N+=(-${o} "$OPTARG") ;\
continue ; esac ;\
done ;\
shift $((OPTIND-1)) ;\
for i in "${C[@]}" ; do _CE="$_CE${_CE:+ }\"${i//\"/\\\"}\""; done ;\
for i in "${N[@]}" ; do _NE="$_NE${_NE:+ }\"${i//\"/\\\"}\""; done ;\
exec magic -dnull -noconsole "${N[@]}" <"$0"
# for-magic:
# magicDrc: run magic-DRC in batch on a .mag file, tabulate/pareto the error counts.
#
# magicDrc [-T <techfilePath>] [-S <drcStyleName>] [-P <N> ] [-l FILE_NAME] <magFileName>
# -T name specific techfile (def .tech extension), passed to magic itself only, overrides tech implied by magFileName
# -S if given, changes from techfile's default drc style (perhaps "drc(fast)") to named style, for example: -S "drc(full)"
# -l if given, enumerates EVERY individual error bbox to the FILE_NAME
# -N if given, do Not use -dereference option of load for topcell (not available in older magics)
# -F flatten top cell in-memory only, not saved (experimental)
# -P do crude drc performance measurement. At top-cell, do 'drc find' <N> times and report time per call.
# Stdout will log a pareto of error type by count regardless.
#
# <magFileName>: names a .mag file, the toplevel of the hier. to DRC/pareto
#
# Normal magic init. files are STILL sourced: ~/.magicrc and either $CWD/.magicrc or $CWD/magic_setup.
# (This would NOT happen if -rcfile magic cmd-line option were used).
#
# WARNING: Before 8.1.70, *.mag on cmd-line that was only found in cell search path set by .magicrc inits,
# would FAIL to determine the default tech-file.
#
# rb@ef 2015-06-30 author
# rb 2020-03-11 embed some library functions, to standalone from efabless-opengalaxy env, test via magic-8.2.194
#
# magic itself outputs following usage message though -rcfile doesn't appear to work (in some versions):
# Usage: magic [-g gPort] [-d devType] [-m monType] [-i tabletPort] [-D] [-F objFile saveFile]
# [-T technology] [-rcfile startupFile | -norcfile][-noconsole] [-nowindow] [-wrapper] [file]
#
set Prog "magicDrc"
set argv [eval "list $env(_M0)"] ;# orig. mix of native plus custom args, for logging all args to script
proc usage {args} {
if {[llength $args] > 0} {
puts "ERROR: ${::Prog}: [join $args]"
}
puts {usage: [ -T <techfilePath> ] [-S <drcStyleName>] [-N] [-l FILE_NAME] <magFileName>}
puts " -T name specific techfile, passed to magic itself only, overrides tech implied by magFileName"
puts " -S if given, changes from techfile's default drc style (perhaps \"drc(fast)\") to named style, for example: -S \"drc(full)\""
puts " -l if given, enumerates EVERY individual error bbox to the FILE_NAME"
puts " -N if given, do not use -dereference option of load for topcell (not available in older magics)"
puts " Stdout will log a pareto of error type by count regardless."
puts ""
puts " Recommend to run in dir with a ./.magicrc (or ./magic_setup) to configure magic's"
puts " cell search path, thru addpath statements, to locate all cells."
}
# optionally hardcode library proc-s (part of site-wide extensions - always available - in context of efabless/open-galaxy)
# This is to make the script more standalone from efabless environment; but these capabilities should be native to magic.
if {[info command scratchWritable] == {}} {
puts "${::Prog}: hardcoding library proc-s..."
# Replacement for 'cellname list exists CELLNAME', to fix ambiguity for cell "0".
# For cell "0" test for membership in 'cellname list allcells'.
#
# Instead of returning 0 for (non-existent) and cellname for exists,
# returns regular 0/1 instead for non-existent/exists.
#
# Therefore NOT direct replacement for uses of 'cellname list exists CELL'.
# Requires code changes.
proc cellnameExists {cell} {
expr {$cell ne "0" && [cellname list exists $cell] eq $cell ||
$cell eq "0" && [lsearch -exact [cellname list allcells] $cell] > -1}
}
#
# scratchWritable [-cleanup] [cellname1 ...] --
#
# Turn readonly cells writable in-memory, via redirect to scratch dir.
# No cellname args: default is to process just all non-writable cells.
# Explicit cellname arguments: ARE scatchified EVEN if ALREADY writable.
# Limitation: Explicit named cell created in-mem, never saved, won't scratchify.
# If just -cleanup: default is to only do cleanup: don't scratchify
# any cells.
#
# -cleanup: Last scratch-dir, if any, and contents are deleted first.
# No restoring old filepath of cells, save after cleanup will fail.
#
# Caller strongly recommended to first do: 'select top cell; expand'
# to force whole hier. of a topcell to be loaded from disk into memory.
#
# This proc does not force expand cells. Before expanded, cells cannot be
# checked whether writable, and cannot have filepath changed.
#
# For batch DRC, for 'drc listall count', every cell in-memory must
# appear writable. This is the work-around (caller to 1st ensure
# hier. is loaded): Reset filepath of readonly cells to a scratch dir,
# make a dummy/empty .mag in scratch dir for each cell. Change cell's
# writeable flag.
#
# Skipped cells:
# In all cases, cells are skipped if
# 'cellname filepath' matches ::scratchWritableDir (already scratchified),
# This proc does NOT try and force expand; it presumes caller forced an expand
# thus cells are skipped if:
# 'cellname filepath' is "default" (can mean not expanded yet, or created never saved),
# 'cellname filepath' is <CELLNAME>.mag, indicates failed expand (unbound).
# Note: when filepath gives "default" or <CELLNAME>.mag, the writeable check not meaningful.
#
# How to scratchify all in-memory cells (still subject to internal skipping):
# scratchWritable {*}[cellname list allcells]
#
# TODO: use a combo of filepath & flags likely can detect created in-mem,
# and could redirect those too scratch dir if named explicitly.
#
# Side-effects:
# Runs zero or one subprocess, '/bin/mktemp -d' to make a scratch dir.
# Redirects where newly modified cells would be saved, if they ever are saved.
# Make's a scratch dir that needs to be cleaned-up.
# Leaves empty *.mag files in that scratch dir.
#
# Uses/requires proc cellnameExists.
#
# Same scratch-dir is reused if called multiple times, until next -cleanup.
#
# return value: list of cells not processed (skipped) for reasons cited above.
# Non-existent cells are also skipped but not included in the return list.
#
if {![info exists ::scratchWritableDir]} {set ::scratchWritableDir {}}
if {![info exists ::scratchWritableVerb]} {set ::scratchWritableVerb 0}
proc scratchWritable {args} {
# parse -cleanup option
set clean [expr {[lindex $args 0] eq {-cleanup}}]
if {$clean} {
set args [lrange $args 1 end]
}
# If explicit cells given: don't limit to processing just readOnly cells.
set onlyReadonly [expr {$args == {}}]
# only if no -cleanup, does empty cell list imply all cells
set allcell [cellname list allcells]
if {!$clean && $args == {}} {
set args $allcell
}
# do cleanup
if {$clean} {
if {$::scratchWritableDir != {} && [file isdir $::scratchWritableDir]} {
set files [glob -dir $::scratchWritableDir -- {*.ext} {*.mag}]
lappend files $::scratchWritableDir
if {$::scratchWritableVerb} {
puts "scratchWritable: running, file delete $files"
}
eval {file delete} $files
set ::scratchWritableDir {}
}
}
# Filter out non-existent or unbound cells.
# Optionally filter already writable cells.
#
# Unbounds result from placements of cells that now don't exist:
# fail to expand. This proc does not try and force expand; it
# presumes a forced expand was already done by caller (if caller
# wished).
#
# Referenced/used cells are initially unexpanded, not yet even located
# located in the search path, 'cellname filepath' returns "default".
# If expand fails (not found in search path), then 'cellname filepath'
# returns <CELLNAME>.mag, if expand worked, the directory containing
# the cell.
#
# If cell was 'cellname create' made, but never saved also "default".
# Such a cell is writable. So filter "default" and <CELLNAME>.mag.
set skipped {}
set ercell1 {}
set docells {}
foreach cell $args {
# filter (without recording as skipped) non-existent cells.
if {![cellnameExists $cell]} { continue }
# filepath = "default": unexpanded (not loaded from disk),
# or created in-mem and never saved (is writable already
# though flags won't say so): skip both.
# TODO: use a combo of filepath & flags likely can detect created in-mem,
# and might be able to redirect them too to scratch dir if named explicitly.
set tmppath [cellname list filepath $cell]
if {$tmppath eq "default"} {
lappend skipped $cell
continue
}
# flags not meaningful, until expanded or expand attempted.
# After expand attempt (filepath != "default"), and flags
# can now be used to determine cell unbound: not available.
set flags [cellname list flags $cell]
if {[lsearch -exact $flags available] < 0} {
lappend ercell1 $cell
continue
}
if {$onlyReadonly &&
[cellname list writeable $cell] eq "writeable"} {
lappend skipped $cell
continue
}
lappend docells $cell
}
if {$::scratchWritableVerb} {
puts "scratchWritable: skipped cells: $skipped"
}
# don't make a scratch dir if no work to do
if {$docells == {}} {
if {$::scratchWritableVerb} {
puts "scratchWritable: scratch-directed 0 cells"
}
return $skipped
}
# make a scratch dir if needed
if {$::scratchWritableDir == {}} {
if {[catch {set dir [string trimright [exec /bin/mktemp -d]]} msg]} {
error "ERROR: scratchWritable, '/bin/mktemp -d' failed, $msg"
}
if {![file isdir $dir] || ![file writable $dir]} {
error "ERROR: scratchWritable, mktemp gave $dir, not a writable dir"
}
set ::scratchWritableDir $dir
}
set ercell2 {}
set okcell {}
set madef 0
foreach cell $docells {
# Relocate if needed: filepath doesn't already point to the scratch dir).
# 'cellname list filepath <cellNm>' -> appears to omit .mag extension,
# but disk-file needs the .mag in the path.
set trgr [file join $::scratchWritableDir "$cell"] ;# expected "lookup" path
set trgw [file join $::scratchWritableDir "$cell.mag"] ;# true "write" disk path
set src [cellname list filepath $cell]
if {[cellname list filepath $cell] ne $trgr && [cellname list filepath $cell] ne $trgw} {
# make empty .mag for the cell
if {[catch {set outmag [open $trgw w]} msg]} {
lappend ercell2 $cell
continue
}
incr madef
close $outmag
# relocate cell to new file
cellname list filepath $cell $::scratchWritableDir
}
# make cell writable
cellname list writeable $cell true
lappend okcell $cell
}
if {$::scratchWritableVerb} {
puts "scratchWritable: scratch-directed $madef cells"
}
if {$ercell1 != {} || $ercell2 != {}} {
set pre "ERROR: scratchWritable, "
set msg {}
if {$ercell1 != {}} {
lappend msg "$pre unbound cell(s): $ercell1"
}
if {$ercell2 != {}} {
lappend msg "$pre failed to make .mag for cell(s): $ercell2"
}
error [join $msg "\n"]
}
set skipped
} ;# end proc scratchWritable
}
# without top-level proc around bulk of script, intermediate error statements don't abort script.
proc main {argv} {
# process name-value pair options, if any
set nbrErr 0
set ndx 0
set max [llength $argv]
set extTechOpt {} ;# -T ...
set enumFilel {} ;# -l ... enum output file
set variant {} ;# -S ... non-default drc style
set flatten 0
set perfN 0 ;# -P <N> do crude DRC perf. test
set noderef 0 ;# -N disable dereference option of: 'load ... -dereference'
while {$ndx < $max && [string match "-*" [lindex $argv $ndx]]} {
set opt [lindex $argv $ndx]
incr ndx
switch -exact -- $opt {
-T {
if {$ndx == $max} {
usage "missing tech-file argument for -T option"
exit 1
}
set extTechOpt [lindex $argv $ndx]
incr ndx
}
-S {
if {$ndx == $max} {
usage "missing drcStyle argument for -S option"
exit 1
}
set variant [lindex $argv $ndx]
incr ndx
}
-P {
if {$ndx == $max} {
usage "missing count argument for -P option"
exit 1
}
set perfN [lindex $argv $ndx]
incr ndx
}
-F {
set flatten 1
}
-N {
set noderef 1
}
-l {
if {$ndx == $max} {
usage "missing outputFile argument for -l option"
exit 1
}
set enumFilel [lindex $argv $ndx]
incr ndx
if {[catch {set enumOut [open $enumFilel w]} msg]} {
error "ERROR: ${::Prog}: failed to open-for-write '$enumFilel' threw error, $msg"
}
puts "${::Prog}: enumerating each error bbox to: $enumFilel"
}
default {
usage "unknown option: $opt"
exit 1
}
}
}
if {$ndx == $max} {
usage "missing magFileName argument, the topcell"
exit 1
}
# get cmd-line topcell, minus dir-path; and minus extension IFF ext is .mag
set topc [file tail [lindex $argv $ndx]] ; incr ndx
if {[file extension $topc] eq ".mag"} {
set topc [file rootname $topc]
}
set topcStr $topc
# abort if user supplies extra args.
if {$ndx != $max} {
usage "extra/unspported arg past magFileName, '[lindex $argv $ndx]'"
exit 1
}
# load the techfile
if {$extTechOpt != ""} {
if {![file readable $extTechOpt]} {
error "ERROR: ${::Prog}: tech-file \"$extTechOpt\" is not readable."
}
tech load $extTechOpt
# Verify the cmd-line -T option (if any) is still the current 'tech filename'. If we didn't
# explicitly 'tech load' ourselves, the .magicrc or magic.setup might 'tech load' something else.
# The 'file join [pwd] ...' makes relative path absolute, but without resolving
# all symlinks (which 'file normalize' would do).
set techf2 [file join [pwd] [tech filename]]
set techf1 [file join [pwd] $extTechOpt]
if {$techf1 != $techf2} {
error "ERROR: ${::Prog}: failed tech-load \"$techf1\" (tech-filename=\"$techf2\" not a match)"
}
}
# if mag-cell were passed natively on magic cmd-line, this is too late:
if {$noderef} {
load $topc
} else {
load $topc -dereference
}
# error checks: ensure (1st) cmd-line cellname now in-memory, and is now the current cell
set topcells [cellname list top]
# filter (UNNAMED)
set topcells [lsearch -exact -not -all -inline $topcells "(UNNAMED)"]
# puts "cellname-list-top is: $topcells"
# could use [cellname list flags $topc] and ensure non-null result (list with available),
# but if it fails (cell not found), it generates unwanted stdout.
if {[lsearch -exact [cellname list allcells] $topc] < 0} {
error "ERROR: ${::Prog}: cmd-line topcell \"$topc\" not in magic's list of allcells."
}
if {[lsearch -exact $topcells $topc] < 0} {
puts "WARNING: ${::Prog}: cmd-line topcell \"$topc\" not in magic's list of topcells: $topcells"
}
# crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
# WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
set topcw [cellname list window]
if {$topcw ne $topc} {
error "ERROR: ${::Prog}: cmd-line topcell, $topc, is not the current cell, 'cellname list window'=$topcw"
}
# for topcell, filepath==default doesn't change by expand,
# indicates unknown cell created in-memory by magic's startup sequence.
if {[cellnameExists $topc] &&
[cellname list filepath $topc] eq "default"} {
puts "Search path for cells is \"[path search]\""
error "ERROR: ${::Prog}: cmd-line topcell, $topc, auto-created in-memory: not found in cell search path"
}
if {$flatten} {
# delete (UNNAMED) if any.
set trg "(UNNAMED)"
if {[cellnameExists $trg]} {cellname delete $trg}
# rename top cell to (UNNAMED)
cellname rename $topc $trg
# now Edit Cell contents are original top cell, but under name (UNNAMED)
# flatten Edit-Cell into original top cell name
puts "${::Prog}: flattening..."
flatten $topc
# load and edit new version of top cell. This is from in-memory, just making it current-cell.
# (So with or without -dereference is expected would have discernable effect by now;
# and since it's flattened there are no subcell instances either).
if {$noderef} {
load $topc
} else {
load $topc -dereference
}
# crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
# WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
set topcw [cellname list window]
if {$topcw ne $topc} {
error "ERROR: ${::Prog}: assertion failed, post-flatten, $topc, is not the current cell, 'cellname list window'=$topcw"
}
# should not be necessary:
select top cell
edit
# crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
# WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
set topcw [cellname list window]
if {$topcw ne $topc} {
error "ERROR: ${::Prog}: assertion-2 failed, post-flatten, $topc, is not the current cell, 'cellname list window'=$topcw"
}
}
# todo: Need a check for non-existent topcell (though magic reported not-found and auto-created it).
# todo: We should locate fullpath to topcell on disk to record this in the log.
#
# WARNING, magic junkCell, or magic junkDir/junkCell (passing paths to cells that don't exist),
# generate startup error messages (could not open cell), but magic creates the new cell in memory.
# No simple way to detect this after the fact. Can walk the cell search path to verify it's on disk.
# For the non-existent cell, magic also discards the dirpath from the cmd-line arg.
# If it did exist at that path, magic opens it successfully, despite that dir not in search path.
# A proper check for implicit create of non-existent cell should account for this effect too.
# write a line with timestamp and all arguments to stdout (log)
# (magic renames the TCL clock command)
set clockp clock
if {[info command $clockp] == {} && [info command orig_clock] != {}} {
set clockp orig_clock
}
set nowSec [$clockp seconds]
set timestamp [$clockp format $nowSec -format "%Y-%m-%d.%T.%Z"]
# Show quoted logged argv here so it's machine readable for replay purposes.
puts "${::Prog}: timestamp: $timestamp, arguments: $::env(_M0)"
puts "${::Prog}: running drc on topcell: $topcStr"
puts "${::Prog}: tech-name: [tech name] -version: [tech version] -filename: [tech filename] -lambda [tech lambda]"
# log the cell search path for this run. Emulates format output by plain "path" (but which prints more than one the cell search path).
puts "Search path for cells is \"[path search]\""
set res {}
if {$variant != {}} {
if {[catch {set res [drc list style $variant]} msg]} {
puts "ERROR: ${::Prog}: but CONTINUING, 'drc style $variant' threw error, $msg"
}
} else {
if {[catch {set res [drc list style]} msg]} {
puts "ERROR: ${::Prog}: but CONTINUING, 'drc list style' threw error, $msg"
}
}
if {$res != {}} {
puts "drc style reports:\n$res"
}
# just Manhattan is default, turn on euclidean, and log new mode
drc euclidean on
drc euclidean
# 1st "select top cell": without it drc-list-count is blank, and error count reduced.
# May be unnecessary in some cases.
# WARNING: if topcell locked by another process, default box is NOT set to full top cell without this (as of 8.1.70 or earlier)
select top cell
# expand cell cells: scratchify step requires this up front else can't force all cells writable.
expand
# The expand triggered load of all subcells. Till then allcells may be incomplete.
set allcells [cellname list allcells]
# filter (UNNAMED)
set allcells [lsearch -exact -not -all -inline $allcells "(UNNAMED)"]
set nbrAllCells [llength $allcells]
# puts "DEBUG: cellname-list-allcells are: $allcells"
# TODO: do explicit separate unbound check here (don't rely on scratchWritable for this)
# make allcells writable. Can error out:
# if are unbounds, or couldn't make scratch dir or .mag files.
set scratch [expr {!$flatten}]
if {$scratch && [catch {scratchWritable} msg]} {
puts stderr "ERROR: ${::Prog}: aborting at scratchWritable due error(s):"
error $msg
}
# Erase all preexisting *.drtcl first. Else when cell transitions from
# dirty in previous run (leaving *.drtcl), to clean, the old *.drtcl
# remains.
# TODO: only delete *.drtcl of cells in 'cellname list allcells'?
# TODO: move this up, before scratchWritable?
set files [glob -nocomplain -types {f} -- ./*.drtcl]
if {$files != {}} {
# TODO: detect/report failure details better here?
puts "${::Prog}: deleting preexisting *.drtcl"
set msg {}
set delfail [catch {eval {file delete} $files} msg]
set files [glob -nocomplain -types {f} -- ./*.drtcl]
if {$delfail || $files != {}} {
puts "ERROR: ${::Prog}: failed to clean old ./*.drtcl files. $msg"
incr nbrErr
}
}
edit ;# Fails if topcell not writable, should not be not needed post scratchWritable
set outScale [cif scale out]
# "select top cell" and box [view bbox] should be equivalent in
# placing a box around whole cell extent.
# The box cmd ALSO prints lambda and micron user-friendly box data,
# but it prints microns with not enough resolution,
# (and no option to disable that flawed print out).
#
# todo: emulate box output in full, except for higher resolution,
# here we only scale/print the overall bbox in microns.
# select top cell ;# paranoid, reset the box to data extents post-expand
# set bbox [view bbox]
# set bbs {}
# foreach oord $bbox {
# lappend bbs [format "%.3f" [expr {$outScale * $oord}]]
# }
# puts "outScale: $outScale, view-bbox: $bbox"
# puts "Root cell box2: ([lindex $bbs 0] [lindex $bbs 1]), ([lindex $bbs 2] [lindex $bbs 3])"
# shouldn't need:
# drc on
# Want to use 'drc list count' to tell us which cells have errors, so we can
# run 'drc listall why' on just those cells to enumerate details (which reruns
# drc again unfortunately).
# For accurate DRC (as of 8.1.70), specifically 'drc list count', need:
# all-writable cells, then run: 'drc check' & 'drc catchup'.
# Now we have all writable cells.
set timeRepeat 1
if {$perfN > 0} {
set timeRepeat $perfN
}
set timeres [time {
set drcCheckTime1 [time {drc check}]
set drcCheckTime2 [time {drc catchup}] } $timeRepeat]
if {$perfN > 0} {
puts "perf: ${perfN}X 'drc check','drc catchup': $timeres"
puts "perf: last 'drc check' time: $drcCheckTime1"
puts "perf: last 'drc catchup' time: $drcCheckTime2"
drc statistics
drc rulestats
}
# todo: this 2nd select was in GDS version, test if needed in mag version:
# 2nd select top cell needed else error count may be reduced (why? bbox does not change due to DRC)
select top cell
set outScale [cif scale out]
set bbox [view bbox]
set bbs {}
foreach oord $bbox {
lappend bbs [format "%.3f" [expr {$outScale * $oord}]]
}
puts "outScale(ostyle=[cif list ostyle]): $outScale, view-bbox: $bbox"
puts "Root cell box: ([lindex $bbs 0] [lindex $bbs 1]), ([lindex $bbs 2] [lindex $bbs 3])"
# print several native bbox representations:
box
# listall vs list appear same as of 8.1.70 or earlier.
# warning: celllist order is not stable, not repeatable; run to run on same data.
# puts "DEBUG: (drc listall count total) is $drcListCountTot"
set celllist [drc listall count]
set celllist [lsearch -not -all -inline -index 0 -exact $celllist "(UNNAMED)"]
# puts "DEBUG: (drc listall count) is [drc listall count]"
set drcListCountTot [drc list count total]
set nbrErrCells [llength $celllist]
# TODO: major problem: 'drc listall why' repeated an every cell, will do subcells
# multiple times, as many times as their depth in the hier.
# canonicalize order of celllist, move topc to last (if present whatsoever).
# force our own artificial entry for topc (zero errors) if not present (was clean)
# puts "DEBUG: celllist before: $celllist"
set topcPair [lsearch -inline -index 0 -exact $celllist $topc]
set celllist [lsearch -not -all -inline -index 0 -exact $celllist $topc]
set celllist [lsort -index 0 -dictionary $celllist]
if {$topcPair == {}} {
# puts "DEBUG: $topc clean, forcing celllist entry for it"
set topcPair [list $topc 0]
}
lappend celllist $topcPair
# puts "DEBUG: celllist after: $celllist"
# puts "DEBUG: adjusted celllist(drc list count) is $celllist"
# loop over celllist
set doFeedback 1 ;# TODO: add cmd-line option to control this
# collect 'dry listall why' for the cells in 'cell list count' with non-zero errors
# If 'drc listall why' does report zero (shouldn't since we're only processing cells
# with non-zero counts), it unavoidably writes to console a No drc errors found message.
# We don't want such polluting our list of per-cell pareto's, so don't risk running
# drc why in-line, in-between per-cell paretos.
array set cell2why [list $topc {}] ;# default at least empty topcell why list
foreach pair $celllist {
if {[lindex $pair 1] < 1} {continue} ;# only happens for topcell if topcell clean
set acell [lindex $pair 0]
# TODO: magic needs a useful error checkable load command.
# The 'load' writes errors to console/stdout, but never throws an error,
# nor gives a useful return value. i.e. These catch never catch.
if {$noderef} {
if {[catch {set res [load $acell]} msg]} {
puts "ERROR: ${::Prog}: 'load $acell' threw error, $msg"
exit 1
}
} else {
if {[catch {set res [load $acell -dereference]} msg]} {
puts "ERROR: ${::Prog}: 'load $acell -dereference' threw error, $msg"
exit 1
}
}
select top cell ;# paranoid, that without it, drc's are reduced
# optionally do crude DRC perf. analysis here. Only for top-cell, only if -P <N> option given.
set timeRepeat 1
if {$perfN > 0 && $topc eq $acell} {
set timeRepeat $perfN
}
set timeres [time {set cell2why($acell) [drc listall why]} $timeRepeat]
if {$perfN > 0 && $topc eq $acell} {
puts "perf: ${::Prog}: for '$acell', ${perfN}X 'drc listall why': $timeres"
}
}
# done with all magic-specifics here. Shouldn't need scratch dir any longer.
# If this prints something (generally does), don't want it after the pareto table.
# clean/remove the tmp scratch dir and contents
# TODO: all fatal errors need to call a cleanup proc that includes this before abort
if {$scratch && [catch {scratchWritable -cleanup} msg]} {
puts "ERROR: ${::Prog}: 'scratchWritable -cleanup' threw error, $msg"
incr nbrErr
}
set gtotal 0
set gcells 0
foreach pair $celllist {
puts ""
set acell [lindex $pair 0]
if {![info exists cell2why($acell)]} {
puts "ERROR: ${::Prog}: cell: $acell, assertion failed, no drc-why list for 'drc list count' pair: $pair"
# exit 1
continue
}
set whys $cell2why($acell)
# enumerate errors under box, plain "drc why" only reports unique types, no quantities
# as-yet-undocumented "drc listall why" gives: {errStr1 {errBox1 ...} errStr2 {errBox1 ...} ... }
set pareto {}
set total 0
set enumTotal 0
set types 0
set typeDup 0
set dups 0
set fbOut {}
# file path for feedback, keep in CWD
if {$doFeedback && $fbOut == {}} {
set fbOut "./$acell.drtcl"
if {![file writable $fbOut] &&
([file exists $fbOut] || ![file writable [file dir $fbOut]])} {
puts stderr "ERROR: ${::Prog}: feedback output not writable, $fbOut"
incr nbrErr
set fbOut {}
} elseif {[catch {set outfb [open $fbOut w]} msg]} {
puts stderr "ERROR: ${::Prog}: failed to truncate previous feedback output, $fbOut : $msg"
incr nbrErr
set fbOut {}
}
}
foreach {str boxes} $whys {
# sort errors
set boxes [lsort -dictionary $boxes]
# for our pareto, gather data
set this [llength $boxes]
incr total $this
incr types
lappend pareto [list $this $str]
# for enumOut, emulate formatting of $CAD_ROOT/magic/tcl/drc.tcl, which is
# not tk pure: fails with complaint about winfo
# note: we walk these errors also in order to count/report stats on duplicates, even if not outputing enumerations
if {[info exists enumOut]} {
if {$types == 1} {
puts $enumOut "[join $pair]\n----------------------------------------"
}
puts $enumOut "${str}\n----------------------------------------"
}
set lastq {}
set thisDup 0
foreach quad $boxes {
set quadUM {}
foreach coord $quad {
set valum [expr {$coord * $outScale}]
set valumf [format "%.3f" $valum]
lappend quadUM "${valumf}um"
}
set dup [expr {$quad == $lastq}]
incr thisDup $dup
set line $quadUM
if {[info exists enumOut]} {
if {$dup} {
puts $enumOut "[join $line] #dup"
} else {
puts $enumOut [join $line]
}
}
if {$fbOut != {}} {
set line [join $quadUM]
regsub -all -- "(\[\[\"\$\\\\])" $str {\\\1} strdq
puts $outfb "[concat box $line]" nonewline
puts $outfb " ; feedback add \"$strdq\" medium" nonewline
if {$dup} {
puts $outfb " ;#dup"
} else {
puts $outfb ""
}
}
incr enumTotal
set lastq $quad
}
if {$thisDup} {
incr typeDup
incr dups $thisDup
}
if {[info exists enumOut]} {
puts $enumOut "----------------------------------------\n"
}
}
if {$fbOut != {}} {
close $outfb
set outfb {}
}
set pareto [lsort -integer -decreasing -index 0 $pareto]
if {$total > 0} {
puts "--- #err|description, table for cell: $acell"
}
foreach pair $pareto {
puts "[format {%8d} [lindex $pair 0]] [lindex $pair 1]"
}
if {$typeDup} {
puts "[format {%8d} $dups] total duplicate error(s) among $typeDup error type(s), cell: $acell"
}
puts "[format {%8d} $total] total error(s) among $types error type(s), cell: $acell"
# add to grand-totals
incr gcells
incr gtotal $total
# always compare the total from the enum to the pareto as error check
if {$total != $enumTotal} {
puts "ERROR: ${::Prog}: cell: $acell, assertion failed, pareto vs enum count mismatch: $total != $enumTotal"
incr nbrErr
}
}
# TODO: in the summary echo also techfile-full-path and drc-style name?
# grand totals
puts "[format {%8d} $nbrErrCells] of $nbrAllCells cell(s) report error(s)"
puts "[format {%8d} $gtotal] grand-total error(s) across $gcells cell(s)"
# wish to compare the drc-list-count-total to the pareto total.
# Per te 2014-08-27 : it is not an error.
# if {$total != $drcListCountTot} {
# puts "info: ${::Prog}: drc-list-count-total vs drc-listall-why mismatch {drc list count total} gave $drcListCountTot, but {drc listall why} gave $total"
# }
if {[info exists enumOut]} {
close $enumOut
}
# set celllist4 [drc list count]
# puts "DEBUG: drc list count0: $celllist0"
# puts "DEBUG: drc list count1: $celllist1"
# puts "DEBUG: drc list count2: $celllist2"
# puts "DEBUG: drc list count3: $celllist3"
# puts "DEBUG: native (drc list count) is $celllistn"
# puts "DEBUG: drc list count4: $celllist4"
# todo: implement super-pareto, ranked table of SUM of all DRC errs/counts from ALL cells.
# (It still would not reflect as-if-flat hierarchical expansion due to repetition of instances).
set nbrErr
}
# non-zero exit-status on errors, either if thrown by main, or counted and returned by main
set nbrErr 0
if {[catch {set nbrErr [main $argv]} msg]} {
puts stderr $msg
set nbrErr 1
} elseif {$nbrErr > 0} {
puts "ERROR: ${::Prog}: script terminated with errors reported above."
}
exit $nbrErr
# for emacs syntax-mode:
# Local Variables:
# mode:tcl
# End: