blob: 94b0b7bcf153effda0c25e19b7a9f5b2e3ab7b09 [file] [log] [blame]
mkk91ada922020-12-02 09:08:26 -08001#!/bin/bash
2# Copyright (C) 2015, 2020 efabless Corporation. All Rights Reserved.
3# filter out most options, so magic Natively sees/handles *only* -T <file>.
4# for-bash\
5 declare -a C ; declare -a N ; export _CE= _NE= _M0= ;\
6 for i in "$@" ; do _M0="$_M0${_M0:+ }\"${i//\"/\\\"}\""; done ;\
7 while getopts "NFT:S:l:P:" o; do \
8 : echo got "optchar $o, with optarg $OPTARG" ;\
9 case "$o" in S) \
10 C+=(-${o} "$OPTARG") ;\
11 continue ; esac ;\
12 case "$o" in P) \
13 C+=(-${o} "$OPTARG") ;\
14 continue ; esac ;\
15 case "$o" in F|N) \
16 C+=(-${o}) ;\
17 continue ; esac ;\
18 case "$o" in l) \
19 C+=(-${o} "$OPTARG") ;\
20 continue ; esac ;\
21 case "$o" in T) \
22 N+=(-${o} "$OPTARG") ;\
23 continue ; esac ;\
24 done ;\
25 shift $((OPTIND-1)) ;\
26 for i in "${C[@]}" ; do _CE="$_CE${_CE:+ }\"${i//\"/\\\"}\""; done ;\
27 for i in "${N[@]}" ; do _NE="$_NE${_NE:+ }\"${i//\"/\\\"}\""; done ;\
28 exec magic -dnull -noconsole "${N[@]}" <"$0"
29# for-magic:
30# magicDrc: run magic-DRC in batch on a .mag file, tabulate/pareto the error counts.
31#
32# magicDrc [-T <techfilePath>] [-S <drcStyleName>] [-P <N> ] [-l FILE_NAME] <magFileName>
33# -T name specific techfile (def .tech extension), passed to magic itself only, overrides tech implied by magFileName
34# -S if given, changes from techfile's default drc style (perhaps "drc(fast)") to named style, for example: -S "drc(full)"
35# -l if given, enumerates EVERY individual error bbox to the FILE_NAME
36# -N if given, do Not use -dereference option of load for topcell (not available in older magics)
37# -F flatten top cell in-memory only, not saved (experimental)
38# -P do crude drc performance measurement. At top-cell, do 'drc find' <N> times and report time per call.
39# Stdout will log a pareto of error type by count regardless.
40#
41# <magFileName>: names a .mag file, the toplevel of the hier. to DRC/pareto
42#
43# Normal magic init. files are STILL sourced: ~/.magicrc and either $CWD/.magicrc or $CWD/magic_setup.
44# (This would NOT happen if -rcfile magic cmd-line option were used).
45#
46# WARNING: Before 8.1.70, *.mag on cmd-line that was only found in cell search path set by .magicrc inits,
47# would FAIL to determine the default tech-file.
48#
49# rb@ef 2015-06-30 author
50# rb 2020-03-11 embed some library functions, to standalone from efabless-opengalaxy env, test via magic-8.2.194
51#
52# magic itself outputs following usage message though -rcfile doesn't appear to work (in some versions):
53# Usage: magic [-g gPort] [-d devType] [-m monType] [-i tabletPort] [-D] [-F objFile saveFile]
54# [-T technology] [-rcfile startupFile | -norcfile][-noconsole] [-nowindow] [-wrapper] [file]
55#
56set Prog "magicDrc"
57
58set argv [eval "list $env(_M0)"] ;# orig. mix of native plus custom args, for logging all args to script
59
60proc usage {args} {
61 if {[llength $args] > 0} {
62 puts "ERROR: ${::Prog}: [join $args]"
63 }
64 puts {usage: [ -T <techfilePath> ] [-S <drcStyleName>] [-N] [-l FILE_NAME] <magFileName>}
65 puts " -T name specific techfile, passed to magic itself only, overrides tech implied by magFileName"
66 puts " -S if given, changes from techfile's default drc style (perhaps \"drc(fast)\") to named style, for example: -S \"drc(full)\""
67 puts " -l if given, enumerates EVERY individual error bbox to the FILE_NAME"
68 puts " -N if given, do not use -dereference option of load for topcell (not available in older magics)"
69 puts " Stdout will log a pareto of error type by count regardless."
70 puts ""
71 puts " Recommend to run in dir with a ./.magicrc (or ./magic_setup) to configure magic's"
72 puts " cell search path, thru addpath statements, to locate all cells."
73}
74
75# optionally hardcode library proc-s (part of site-wide extensions - always available - in context of efabless/open-galaxy)
76# This is to make the script more standalone from efabless environment; but these capabilities should be native to magic.
77
78if {[info command scratchWritable] == {}} {
79 puts "${::Prog}: hardcoding library proc-s..."
80# Replacement for 'cellname list exists CELLNAME', to fix ambiguity for cell "0".
81# For cell "0" test for membership in 'cellname list allcells'.
82#
83# Instead of returning 0 for (non-existent) and cellname for exists,
84# returns regular 0/1 instead for non-existent/exists.
85#
86# Therefore NOT direct replacement for uses of 'cellname list exists CELL'.
87# Requires code changes.
88proc cellnameExists {cell} {
89 expr {$cell ne "0" && [cellname list exists $cell] eq $cell ||
90 $cell eq "0" && [lsearch -exact [cellname list allcells] $cell] > -1}
91}
92
93#
94# scratchWritable [-cleanup] [cellname1 ...] --
95#
96# Turn readonly cells writable in-memory, via redirect to scratch dir.
97# No cellname args: default is to process just all non-writable cells.
98# Explicit cellname arguments: ARE scatchified EVEN if ALREADY writable.
99# Limitation: Explicit named cell created in-mem, never saved, won't scratchify.
100# If just -cleanup: default is to only do cleanup: don't scratchify
101# any cells.
102#
103# -cleanup: Last scratch-dir, if any, and contents are deleted first.
104# No restoring old filepath of cells, save after cleanup will fail.
105#
106# Caller strongly recommended to first do: 'select top cell; expand'
107# to force whole hier. of a topcell to be loaded from disk into memory.
108#
109# This proc does not force expand cells. Before expanded, cells cannot be
110# checked whether writable, and cannot have filepath changed.
111#
112# For batch DRC, for 'drc listall count', every cell in-memory must
113# appear writable. This is the work-around (caller to 1st ensure
114# hier. is loaded): Reset filepath of readonly cells to a scratch dir,
115# make a dummy/empty .mag in scratch dir for each cell. Change cell's
116# writeable flag.
117#
118# Skipped cells:
119# In all cases, cells are skipped if
120# 'cellname filepath' matches ::scratchWritableDir (already scratchified),
121# This proc does NOT try and force expand; it presumes caller forced an expand
122# thus cells are skipped if:
123# 'cellname filepath' is "default" (can mean not expanded yet, or created never saved),
124# 'cellname filepath' is <CELLNAME>.mag, indicates failed expand (unbound).
125# Note: when filepath gives "default" or <CELLNAME>.mag, the writeable check not meaningful.
126#
127# How to scratchify all in-memory cells (still subject to internal skipping):
128# scratchWritable {*}[cellname list allcells]
129#
130# TODO: use a combo of filepath & flags likely can detect created in-mem,
131# and could redirect those too scratch dir if named explicitly.
132#
133# Side-effects:
134# Runs zero or one subprocess, '/bin/mktemp -d' to make a scratch dir.
135# Redirects where newly modified cells would be saved, if they ever are saved.
136# Make's a scratch dir that needs to be cleaned-up.
137# Leaves empty *.mag files in that scratch dir.
138#
139# Uses/requires proc cellnameExists.
140#
141# Same scratch-dir is reused if called multiple times, until next -cleanup.
142#
143# return value: list of cells not processed (skipped) for reasons cited above.
144# Non-existent cells are also skipped but not included in the return list.
145#
146if {![info exists ::scratchWritableDir]} {set ::scratchWritableDir {}}
147if {![info exists ::scratchWritableVerb]} {set ::scratchWritableVerb 0}
148proc scratchWritable {args} {
149 # parse -cleanup option
150 set clean [expr {[lindex $args 0] eq {-cleanup}}]
151 if {$clean} {
152 set args [lrange $args 1 end]
153 }
154
155 # If explicit cells given: don't limit to processing just readOnly cells.
156 set onlyReadonly [expr {$args == {}}]
157
158 # only if no -cleanup, does empty cell list imply all cells
159 set allcell [cellname list allcells]
160 if {!$clean && $args == {}} {
161 set args $allcell
162 }
163
164 # do cleanup
165 if {$clean} {
166 if {$::scratchWritableDir != {} && [file isdir $::scratchWritableDir]} {
167 set files [glob -dir $::scratchWritableDir -- {*.ext} {*.mag}]
168 lappend files $::scratchWritableDir
169 if {$::scratchWritableVerb} {
170 puts "scratchWritable: running, file delete $files"
171 }
172 eval {file delete} $files
173 set ::scratchWritableDir {}
174 }
175 }
176
177 # Filter out non-existent or unbound cells.
178 # Optionally filter already writable cells.
179 #
180 # Unbounds result from placements of cells that now don't exist:
181 # fail to expand. This proc does not try and force expand; it
182 # presumes a forced expand was already done by caller (if caller
183 # wished).
184 #
185 # Referenced/used cells are initially unexpanded, not yet even located
186 # located in the search path, 'cellname filepath' returns "default".
187 # If expand fails (not found in search path), then 'cellname filepath'
188 # returns <CELLNAME>.mag, if expand worked, the directory containing
189 # the cell.
190 #
191 # If cell was 'cellname create' made, but never saved also "default".
192 # Such a cell is writable. So filter "default" and <CELLNAME>.mag.
193 set skipped {}
194 set ercell1 {}
195 set docells {}
196 foreach cell $args {
197 # filter (without recording as skipped) non-existent cells.
198 if {![cellnameExists $cell]} { continue }
199
200 # filepath = "default": unexpanded (not loaded from disk),
201 # or created in-mem and never saved (is writable already
202 # though flags won't say so): skip both.
203 # TODO: use a combo of filepath & flags likely can detect created in-mem,
204 # and might be able to redirect them too to scratch dir if named explicitly.
205 set tmppath [cellname list filepath $cell]
206 if {$tmppath eq "default"} {
207 lappend skipped $cell
208 continue
209 }
210
211 # flags not meaningful, until expanded or expand attempted.
212 # After expand attempt (filepath != "default"), and flags
213 # can now be used to determine cell unbound: not available.
214 set flags [cellname list flags $cell]
215 if {[lsearch -exact $flags available] < 0} {
216 lappend ercell1 $cell
217 continue
218 }
219
220 if {$onlyReadonly &&
221 [cellname list writeable $cell] eq "writeable"} {
222 lappend skipped $cell
223 continue
224 }
225 lappend docells $cell
226 }
227
228 if {$::scratchWritableVerb} {
229 puts "scratchWritable: skipped cells: $skipped"
230 }
231
232 # don't make a scratch dir if no work to do
233 if {$docells == {}} {
234 if {$::scratchWritableVerb} {
235 puts "scratchWritable: scratch-directed 0 cells"
236 }
237 return $skipped
238 }
239
240 # make a scratch dir if needed
241 if {$::scratchWritableDir == {}} {
242 if {[catch {set dir [string trimright [exec /bin/mktemp -d]]} msg]} {
243 error "ERROR: scratchWritable, '/bin/mktemp -d' failed, $msg"
244 }
245 if {![file isdir $dir] || ![file writable $dir]} {
246 error "ERROR: scratchWritable, mktemp gave $dir, not a writable dir"
247 }
248 set ::scratchWritableDir $dir
249 }
250
251 set ercell2 {}
252 set okcell {}
253 set madef 0
254 foreach cell $docells {
255 # Relocate if needed: filepath doesn't already point to the scratch dir).
256 # 'cellname list filepath <cellNm>' -> appears to omit .mag extension,
257 # but disk-file needs the .mag in the path.
258 set trgr [file join $::scratchWritableDir "$cell"] ;# expected "lookup" path
259 set trgw [file join $::scratchWritableDir "$cell.mag"] ;# true "write" disk path
260 set src [cellname list filepath $cell]
261 if {[cellname list filepath $cell] ne $trgr && [cellname list filepath $cell] ne $trgw} {
262
263 # make empty .mag for the cell
264 if {[catch {set outmag [open $trgw w]} msg]} {
265 lappend ercell2 $cell
266 continue
267 }
268 incr madef
269 close $outmag
270
271 # relocate cell to new file
272 cellname list filepath $cell $::scratchWritableDir
273 }
274
275 # make cell writable
276 cellname list writeable $cell true
277 lappend okcell $cell
278 }
279
280 if {$::scratchWritableVerb} {
281 puts "scratchWritable: scratch-directed $madef cells"
282 }
283 if {$ercell1 != {} || $ercell2 != {}} {
284 set pre "ERROR: scratchWritable, "
285 set msg {}
286 if {$ercell1 != {}} {
287 lappend msg "$pre unbound cell(s): $ercell1"
288 }
289 if {$ercell2 != {}} {
290 lappend msg "$pre failed to make .mag for cell(s): $ercell2"
291 }
292 error [join $msg "\n"]
293 }
294 set skipped
295} ;# end proc scratchWritable
296}
297
298# without top-level proc around bulk of script, intermediate error statements don't abort script.
299proc main {argv} {
300
301# process name-value pair options, if any
302set nbrErr 0
303set ndx 0
304set max [llength $argv]
305set extTechOpt {} ;# -T ...
306set enumFilel {} ;# -l ... enum output file
307set variant {} ;# -S ... non-default drc style
308set flatten 0
309set perfN 0 ;# -P <N> do crude DRC perf. test
310set noderef 0 ;# -N disable dereference option of: 'load ... -dereference'
311
312while {$ndx < $max && [string match "-*" [lindex $argv $ndx]]} {
313 set opt [lindex $argv $ndx]
314 incr ndx
315 switch -exact -- $opt {
316 -T {
317 if {$ndx == $max} {
318 usage "missing tech-file argument for -T option"
319 exit 1
320 }
321 set extTechOpt [lindex $argv $ndx]
322 incr ndx
323 }
324 -S {
325 if {$ndx == $max} {
326 usage "missing drcStyle argument for -S option"
327 exit 1
328 }
329 set variant [lindex $argv $ndx]
330 incr ndx
331 }
332 -P {
333 if {$ndx == $max} {
334 usage "missing count argument for -P option"
335 exit 1
336 }
337 set perfN [lindex $argv $ndx]
338 incr ndx
339 }
340 -F {
341 set flatten 1
342 }
343 -N {
344 set noderef 1
345 }
346 -l {
347 if {$ndx == $max} {
348 usage "missing outputFile argument for -l option"
349 exit 1
350 }
351 set enumFilel [lindex $argv $ndx]
352 incr ndx
353 if {[catch {set enumOut [open $enumFilel w]} msg]} {
354 error "ERROR: ${::Prog}: failed to open-for-write '$enumFilel' threw error, $msg"
355 }
356 puts "${::Prog}: enumerating each error bbox to: $enumFilel"
357 }
358 default {
359 usage "unknown option: $opt"
360 exit 1
361 }
362 }
363}
364
365if {$ndx == $max} {
366 usage "missing magFileName argument, the topcell"
367 exit 1
368}
369
370# get cmd-line topcell, minus dir-path; and minus extension IFF ext is .mag
371set topc [file tail [lindex $argv $ndx]] ; incr ndx
372if {[file extension $topc] eq ".mag"} {
373 set topc [file rootname $topc]
374}
375set topcStr $topc
376
377# abort if user supplies extra args.
378if {$ndx != $max} {
379 usage "extra/unspported arg past magFileName, '[lindex $argv $ndx]'"
380 exit 1
381}
382
383# load the techfile
384if {$extTechOpt != ""} {
385 if {![file readable $extTechOpt]} {
386 error "ERROR: ${::Prog}: tech-file \"$extTechOpt\" is not readable."
387 }
388
389 tech load $extTechOpt
390
391 # Verify the cmd-line -T option (if any) is still the current 'tech filename'. If we didn't
392 # explicitly 'tech load' ourselves, the .magicrc or magic.setup might 'tech load' something else.
393 # The 'file join [pwd] ...' makes relative path absolute, but without resolving
394 # all symlinks (which 'file normalize' would do).
395 set techf2 [file join [pwd] [tech filename]]
396 set techf1 [file join [pwd] $extTechOpt]
397 if {$techf1 != $techf2} {
398 error "ERROR: ${::Prog}: failed tech-load \"$techf1\" (tech-filename=\"$techf2\" not a match)"
399 }
400}
401
402# if mag-cell were passed natively on magic cmd-line, this is too late:
403if {$noderef} {
404 load $topc
405} else {
406 load $topc -dereference
407}
408
409# error checks: ensure (1st) cmd-line cellname now in-memory, and is now the current cell
410
411set topcells [cellname list top]
412# filter (UNNAMED)
413set topcells [lsearch -exact -not -all -inline $topcells "(UNNAMED)"]
414# puts "cellname-list-top is: $topcells"
415
416# could use [cellname list flags $topc] and ensure non-null result (list with available),
417# but if it fails (cell not found), it generates unwanted stdout.
418if {[lsearch -exact [cellname list allcells] $topc] < 0} {
419 error "ERROR: ${::Prog}: cmd-line topcell \"$topc\" not in magic's list of allcells."
420}
421
422if {[lsearch -exact $topcells $topc] < 0} {
423 puts "WARNING: ${::Prog}: cmd-line topcell \"$topc\" not in magic's list of topcells: $topcells"
424}
425
426# crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
427# WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
428set topcw [cellname list window]
429if {$topcw ne $topc} {
430 error "ERROR: ${::Prog}: cmd-line topcell, $topc, is not the current cell, 'cellname list window'=$topcw"
431}
432
433# for topcell, filepath==default doesn't change by expand,
434# indicates unknown cell created in-memory by magic's startup sequence.
435if {[cellnameExists $topc] &&
436 [cellname list filepath $topc] eq "default"} {
437 puts "Search path for cells is \"[path search]\""
438 error "ERROR: ${::Prog}: cmd-line topcell, $topc, auto-created in-memory: not found in cell search path"
439}
440
441if {$flatten} {
442 # delete (UNNAMED) if any.
443 set trg "(UNNAMED)"
444 if {[cellnameExists $trg]} {cellname delete $trg}
445
446 # rename top cell to (UNNAMED)
447 cellname rename $topc $trg
448
449 # now Edit Cell contents are original top cell, but under name (UNNAMED)
450 # flatten Edit-Cell into original top cell name
451 puts "${::Prog}: flattening..."
452 flatten $topc
453
454 # load and edit new version of top cell. This is from in-memory, just making it current-cell.
455 # (So with or without -dereference is expected would have discernable effect by now;
456 # and since it's flattened there are no subcell instances either).
457 if {$noderef} {
458 load $topc
459 } else {
460 load $topc -dereference
461 }
462
463 # crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
464 # WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
465 set topcw [cellname list window]
466 if {$topcw ne $topc} {
467 error "ERROR: ${::Prog}: assertion failed, post-flatten, $topc, is not the current cell, 'cellname list window'=$topcw"
468 }
469
470 # should not be necessary:
471 select top cell
472 edit
473
474 # crude way even in batch to determine the "current" cell; perhaps not yet the "Edit" cell
475 # WARNING, if topcell locked elsewhere or not writable, it can't become the "Edit" cell.
476 set topcw [cellname list window]
477 if {$topcw ne $topc} {
478 error "ERROR: ${::Prog}: assertion-2 failed, post-flatten, $topc, is not the current cell, 'cellname list window'=$topcw"
479 }
480}
481
482# todo: Need a check for non-existent topcell (though magic reported not-found and auto-created it).
483# todo: We should locate fullpath to topcell on disk to record this in the log.
484#
485# WARNING, magic junkCell, or magic junkDir/junkCell (passing paths to cells that don't exist),
486# generate startup error messages (could not open cell), but magic creates the new cell in memory.
487# No simple way to detect this after the fact. Can walk the cell search path to verify it's on disk.
488# For the non-existent cell, magic also discards the dirpath from the cmd-line arg.
489# If it did exist at that path, magic opens it successfully, despite that dir not in search path.
490# A proper check for implicit create of non-existent cell should account for this effect too.
491
492# write a line with timestamp and all arguments to stdout (log)
493# (magic renames the TCL clock command)
494set clockp clock
495if {[info command $clockp] == {} && [info command orig_clock] != {}} {
496 set clockp orig_clock
497}
498set nowSec [$clockp seconds]
499set timestamp [$clockp format $nowSec -format "%Y-%m-%d.%T.%Z"]
500# Show quoted logged argv here so it's machine readable for replay purposes.
501puts "${::Prog}: timestamp: $timestamp, arguments: $::env(_M0)"
502
503puts "${::Prog}: running drc on topcell: $topcStr"
504puts "${::Prog}: tech-name: [tech name] -version: [tech version] -filename: [tech filename] -lambda [tech lambda]"
505
506# log the cell search path for this run. Emulates format output by plain "path" (but which prints more than one the cell search path).
507puts "Search path for cells is \"[path search]\""
508
509set res {}
510if {$variant != {}} {
511 if {[catch {set res [drc list style $variant]} msg]} {
512 puts "ERROR: ${::Prog}: but CONTINUING, 'drc style $variant' threw error, $msg"
513 }
514} else {
515 if {[catch {set res [drc list style]} msg]} {
516 puts "ERROR: ${::Prog}: but CONTINUING, 'drc list style' threw error, $msg"
517 }
518}
519if {$res != {}} {
520 puts "drc style reports:\n$res"
521}
522
523# just Manhattan is default, turn on euclidean, and log new mode
524drc euclidean on
525drc euclidean
526
527# 1st "select top cell": without it drc-list-count is blank, and error count reduced.
528# May be unnecessary in some cases.
529# 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)
530select top cell
531# expand cell cells: scratchify step requires this up front else can't force all cells writable.
532expand
533
534# The expand triggered load of all subcells. Till then allcells may be incomplete.
535set allcells [cellname list allcells]
536# filter (UNNAMED)
537set allcells [lsearch -exact -not -all -inline $allcells "(UNNAMED)"]
538set nbrAllCells [llength $allcells]
539# puts "DEBUG: cellname-list-allcells are: $allcells"
540
541# TODO: do explicit separate unbound check here (don't rely on scratchWritable for this)
542
543# make allcells writable. Can error out:
544# if are unbounds, or couldn't make scratch dir or .mag files.
545set scratch [expr {!$flatten}]
546if {$scratch && [catch {scratchWritable} msg]} {
547 puts stderr "ERROR: ${::Prog}: aborting at scratchWritable due error(s):"
548 error $msg
549}
550
551# Erase all preexisting *.drtcl first. Else when cell transitions from
552# dirty in previous run (leaving *.drtcl), to clean, the old *.drtcl
553# remains.
554# TODO: only delete *.drtcl of cells in 'cellname list allcells'?
555# TODO: move this up, before scratchWritable?
556set files [glob -nocomplain -types {f} -- ./*.drtcl]
557if {$files != {}} {
558 # TODO: detect/report failure details better here?
559 puts "${::Prog}: deleting preexisting *.drtcl"
560 set msg {}
561 set delfail [catch {eval {file delete} $files} msg]
562 set files [glob -nocomplain -types {f} -- ./*.drtcl]
563 if {$delfail || $files != {}} {
564 puts "ERROR: ${::Prog}: failed to clean old ./*.drtcl files. $msg"
565 incr nbrErr
566 }
567}
568
569edit ;# Fails if topcell not writable, should not be not needed post scratchWritable
570
571set outScale [cif scale out]
572
573# "select top cell" and box [view bbox] should be equivalent in
574# placing a box around whole cell extent.
575# The box cmd ALSO prints lambda and micron user-friendly box data,
576# but it prints microns with not enough resolution,
577# (and no option to disable that flawed print out).
578#
579# todo: emulate box output in full, except for higher resolution,
580# here we only scale/print the overall bbox in microns.
581# select top cell ;# paranoid, reset the box to data extents post-expand
582# set bbox [view bbox]
583# set bbs {}
584# foreach oord $bbox {
585# lappend bbs [format "%.3f" [expr {$outScale * $oord}]]
586# }
587# puts "outScale: $outScale, view-bbox: $bbox"
588# puts "Root cell box2: ([lindex $bbs 0] [lindex $bbs 1]), ([lindex $bbs 2] [lindex $bbs 3])"
589
590# shouldn't need:
591# drc on
592
593# Want to use 'drc list count' to tell us which cells have errors, so we can
594# run 'drc listall why' on just those cells to enumerate details (which reruns
595# drc again unfortunately).
596
597# For accurate DRC (as of 8.1.70), specifically 'drc list count', need:
598# all-writable cells, then run: 'drc check' & 'drc catchup'.
599# Now we have all writable cells.
600set timeRepeat 1
601if {$perfN > 0} {
602 set timeRepeat $perfN
603}
604set timeres [time {
605 set drcCheckTime1 [time {drc check}]
606 set drcCheckTime2 [time {drc catchup}] } $timeRepeat]
607
608if {$perfN > 0} {
609 puts "perf: ${perfN}X 'drc check','drc catchup': $timeres"
610 puts "perf: last 'drc check' time: $drcCheckTime1"
611 puts "perf: last 'drc catchup' time: $drcCheckTime2"
612 drc statistics
613 drc rulestats
614}
615
616# todo: this 2nd select was in GDS version, test if needed in mag version:
617# 2nd select top cell needed else error count may be reduced (why? bbox does not change due to DRC)
618select top cell
619set outScale [cif scale out]
620set bbox [view bbox]
621set bbs {}
622foreach oord $bbox {
623 lappend bbs [format "%.3f" [expr {$outScale * $oord}]]
624}
625puts "outScale(ostyle=[cif list ostyle]): $outScale, view-bbox: $bbox"
626puts "Root cell box: ([lindex $bbs 0] [lindex $bbs 1]), ([lindex $bbs 2] [lindex $bbs 3])"
627# print several native bbox representations:
628box
629
630# listall vs list appear same as of 8.1.70 or earlier.
631# warning: celllist order is not stable, not repeatable; run to run on same data.
632# puts "DEBUG: (drc listall count total) is $drcListCountTot"
633set celllist [drc listall count]
634set celllist [lsearch -not -all -inline -index 0 -exact $celllist "(UNNAMED)"]
635# puts "DEBUG: (drc listall count) is [drc listall count]"
636set drcListCountTot [drc list count total]
637set nbrErrCells [llength $celllist]
638
639# TODO: major problem: 'drc listall why' repeated an every cell, will do subcells
640# multiple times, as many times as their depth in the hier.
641
642# canonicalize order of celllist, move topc to last (if present whatsoever).
643# force our own artificial entry for topc (zero errors) if not present (was clean)
644# puts "DEBUG: celllist before: $celllist"
645set topcPair [lsearch -inline -index 0 -exact $celllist $topc]
646set celllist [lsearch -not -all -inline -index 0 -exact $celllist $topc]
647set celllist [lsort -index 0 -dictionary $celllist]
648if {$topcPair == {}} {
649 # puts "DEBUG: $topc clean, forcing celllist entry for it"
650 set topcPair [list $topc 0]
651}
652lappend celllist $topcPair
653# puts "DEBUG: celllist after: $celllist"
654# puts "DEBUG: adjusted celllist(drc list count) is $celllist"
655
656# loop over celllist
657set doFeedback 1 ;# TODO: add cmd-line option to control this
658
659# collect 'dry listall why' for the cells in 'cell list count' with non-zero errors
660# If 'drc listall why' does report zero (shouldn't since we're only processing cells
661# with non-zero counts), it unavoidably writes to console a No drc errors found message.
662# We don't want such polluting our list of per-cell pareto's, so don't risk running
663# drc why in-line, in-between per-cell paretos.
664array set cell2why [list $topc {}] ;# default at least empty topcell why list
665foreach pair $celllist {
666 if {[lindex $pair 1] < 1} {continue} ;# only happens for topcell if topcell clean
667 set acell [lindex $pair 0]
668
669 # TODO: magic needs a useful error checkable load command.
670 # The 'load' writes errors to console/stdout, but never throws an error,
671 # nor gives a useful return value. i.e. These catch never catch.
672 if {$noderef} {
673 if {[catch {set res [load $acell]} msg]} {
674 puts "ERROR: ${::Prog}: 'load $acell' threw error, $msg"
675 exit 1
676 }
677 } else {
678 if {[catch {set res [load $acell -dereference]} msg]} {
679 puts "ERROR: ${::Prog}: 'load $acell -dereference' threw error, $msg"
680 exit 1
681 }
682 }
683 select top cell ;# paranoid, that without it, drc's are reduced
684
685 # optionally do crude DRC perf. analysis here. Only for top-cell, only if -P <N> option given.
686 set timeRepeat 1
687 if {$perfN > 0 && $topc eq $acell} {
688 set timeRepeat $perfN
689 }
690 set timeres [time {set cell2why($acell) [drc listall why]} $timeRepeat]
691 if {$perfN > 0 && $topc eq $acell} {
692 puts "perf: ${::Prog}: for '$acell', ${perfN}X 'drc listall why': $timeres"
693 }
694}
695
696# done with all magic-specifics here. Shouldn't need scratch dir any longer.
697# If this prints something (generally does), don't want it after the pareto table.
698
699# clean/remove the tmp scratch dir and contents
700# TODO: all fatal errors need to call a cleanup proc that includes this before abort
701if {$scratch && [catch {scratchWritable -cleanup} msg]} {
702 puts "ERROR: ${::Prog}: 'scratchWritable -cleanup' threw error, $msg"
703 incr nbrErr
704}
705
706set gtotal 0
707set gcells 0
708foreach pair $celllist {
709 puts ""
710 set acell [lindex $pair 0]
711 if {![info exists cell2why($acell)]} {
712 puts "ERROR: ${::Prog}: cell: $acell, assertion failed, no drc-why list for 'drc list count' pair: $pair"
713 # exit 1
714 continue
715 }
716 set whys $cell2why($acell)
717
718 # enumerate errors under box, plain "drc why" only reports unique types, no quantities
719 # as-yet-undocumented "drc listall why" gives: {errStr1 {errBox1 ...} errStr2 {errBox1 ...} ... }
720 set pareto {}
721 set total 0
722 set enumTotal 0
723 set types 0
724 set typeDup 0
725 set dups 0
726
727 set fbOut {}
728 # file path for feedback, keep in CWD
729 if {$doFeedback && $fbOut == {}} {
730 set fbOut "./$acell.drtcl"
731 if {![file writable $fbOut] &&
732 ([file exists $fbOut] || ![file writable [file dir $fbOut]])} {
733 puts stderr "ERROR: ${::Prog}: feedback output not writable, $fbOut"
734 incr nbrErr
735 set fbOut {}
736 } elseif {[catch {set outfb [open $fbOut w]} msg]} {
737 puts stderr "ERROR: ${::Prog}: failed to truncate previous feedback output, $fbOut : $msg"
738 incr nbrErr
739 set fbOut {}
740 }
741 }
742 foreach {str boxes} $whys {
743 # sort errors
744 set boxes [lsort -dictionary $boxes]
745
746 # for our pareto, gather data
747 set this [llength $boxes]
748 incr total $this
749 incr types
750 lappend pareto [list $this $str]
751
752 # for enumOut, emulate formatting of $CAD_ROOT/magic/tcl/drc.tcl, which is
753 # not tk pure: fails with complaint about winfo
754 # note: we walk these errors also in order to count/report stats on duplicates, even if not outputing enumerations
755 if {[info exists enumOut]} {
756 if {$types == 1} {
757 puts $enumOut "[join $pair]\n----------------------------------------"
758 }
759 puts $enumOut "${str}\n----------------------------------------"
760 }
761 set lastq {}
762 set thisDup 0
763 foreach quad $boxes {
764 set quadUM {}
765 foreach coord $quad {
766 set valum [expr {$coord * $outScale}]
767 set valumf [format "%.3f" $valum]
768 lappend quadUM "${valumf}um"
769 }
770 set dup [expr {$quad == $lastq}]
771 incr thisDup $dup
772 set line $quadUM
773 if {[info exists enumOut]} {
774 if {$dup} {
775 puts $enumOut "[join $line] #dup"
776 } else {
777 puts $enumOut [join $line]
778 }
779 }
780 if {$fbOut != {}} {
781 set line [join $quadUM]
782 regsub -all -- "(\[\[\"\$\\\\])" $str {\\\1} strdq
783 puts $outfb "[concat box $line]" nonewline
784 puts $outfb " ; feedback add \"$strdq\" medium" nonewline
785 if {$dup} {
786 puts $outfb " ;#dup"
787 } else {
788 puts $outfb ""
789 }
790 }
791
792 incr enumTotal
793 set lastq $quad
794 }
795 if {$thisDup} {
796 incr typeDup
797 incr dups $thisDup
798 }
799 if {[info exists enumOut]} {
800 puts $enumOut "----------------------------------------\n"
801 }
802 }
803
804 if {$fbOut != {}} {
805 close $outfb
806 set outfb {}
807 }
808
809 set pareto [lsort -integer -decreasing -index 0 $pareto]
810 if {$total > 0} {
811 puts "--- #err|description, table for cell: $acell"
812 }
813 foreach pair $pareto {
814 puts "[format {%8d} [lindex $pair 0]] [lindex $pair 1]"
815 }
816 if {$typeDup} {
817 puts "[format {%8d} $dups] total duplicate error(s) among $typeDup error type(s), cell: $acell"
818 }
819 puts "[format {%8d} $total] total error(s) among $types error type(s), cell: $acell"
820 # add to grand-totals
821 incr gcells
822 incr gtotal $total
823
824 # always compare the total from the enum to the pareto as error check
825 if {$total != $enumTotal} {
826 puts "ERROR: ${::Prog}: cell: $acell, assertion failed, pareto vs enum count mismatch: $total != $enumTotal"
827 incr nbrErr
828 }
829}
830
831# TODO: in the summary echo also techfile-full-path and drc-style name?
832# grand totals
833puts "[format {%8d} $nbrErrCells] of $nbrAllCells cell(s) report error(s)"
834puts "[format {%8d} $gtotal] grand-total error(s) across $gcells cell(s)"
835
836# wish to compare the drc-list-count-total to the pareto total.
837# Per te 2014-08-27 : it is not an error.
838# if {$total != $drcListCountTot} {
839# puts "info: ${::Prog}: drc-list-count-total vs drc-listall-why mismatch {drc list count total} gave $drcListCountTot, but {drc listall why} gave $total"
840# }
841
842if {[info exists enumOut]} {
843 close $enumOut
844}
845
846# set celllist4 [drc list count]
847# puts "DEBUG: drc list count0: $celllist0"
848# puts "DEBUG: drc list count1: $celllist1"
849# puts "DEBUG: drc list count2: $celllist2"
850# puts "DEBUG: drc list count3: $celllist3"
851# puts "DEBUG: native (drc list count) is $celllistn"
852# puts "DEBUG: drc list count4: $celllist4"
853
854# todo: implement super-pareto, ranked table of SUM of all DRC errs/counts from ALL cells.
855# (It still would not reflect as-if-flat hierarchical expansion due to repetition of instances).
856
857set nbrErr
858}
859
860# non-zero exit-status on errors, either if thrown by main, or counted and returned by main
861set nbrErr 0
862if {[catch {set nbrErr [main $argv]} msg]} {
863 puts stderr $msg
864 set nbrErr 1
865} elseif {$nbrErr > 0} {
866 puts "ERROR: ${::Prog}: script terminated with errors reported above."
867}
868exit $nbrErr
869
870# for emacs syntax-mode:
871# Local Variables:
872# mode:tcl
873# End: