From e9da0370011d03732035b57bfda6bb328489373a Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 1 Mar 2021 16:55:07 -0500 Subject: [PATCH] Removed the code for fast symmetry breaking, as it has been found to generate incorrect results on occasion. The method to parallelize cells with the same no-connect pins should avoid the worst-case symmetry breaking that was previously plaguing the LVS of large standard-cell layouts. --- base/netcmp.c | 178 +---------- base/netcmp.h | 1 - tcltk/netgen.sh | 60 ++++ tcltk/netgen.tcl | 753 ++++++++++++++++++++++++++++++++++++++++++++++ tcltk/tclnetgen.c | 32 +- 5 files changed, 822 insertions(+), 202 deletions(-) create mode 100644 tcltk/netgen.sh create mode 100644 tcltk/netgen.tcl diff --git a/base/netcmp.c b/base/netcmp.c index 8ea1f60..1585d4b 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -158,9 +158,6 @@ int right_col_end = 87; /* if TRUE, always partition ALL classes */ int ExhaustiveSubdivision = 0; -/* if TRUE, use fast symmetry breaking to resolve automorphisms */ -int FastSymmetryBreaking = 1; - #ifdef TEST static void PrintElement_List(struct Element *E) { @@ -5865,16 +5862,19 @@ int ResolveAutomorphsByProperty() /* *------------------------------------------------------------------------- * - * ResolveAutormorphismsFull -- + * ResolveAutormorphisms -- * - * Arbitrarily equivalence one pair of elements within an automorphic class + * Do symmetry breaking of automorphisms. + * + * Arbitrarily equivalence all pairs of elements and nodes within all + * automorphic classes, and run to completion. * * Return value is the same as VerifyMatching() * *------------------------------------------------------------------------- */ -int ResolveAutomorphismsFull() +int ResolveAutomorphisms() { struct ElementClass *EC; struct Element *E; @@ -5936,172 +5936,6 @@ int ResolveAutomorphismsFull() return(VerifyMatching()); } -/* - *------------------------------------------------------------------------- - * - * ResolveElementAutomorphismsFast -- - * - * Apply arbitrary symmetry breaking to symmetric element lists, then - * iterate exhaustively to resolve all automorphisms. - * - * Return value is the same as VerifyMatching() - * - *------------------------------------------------------------------------- - */ - -int ResolveElementAutomorphismsFast() -{ - struct ElementClass *EC; - struct Element *E; - int C1, C2; - - for (EC = ElementClasses; EC != NULL; EC = EC->next) { - struct Element *E1, *E2; - C1 = C2 = 0; - E1 = E2 = NULL; - for (E = EC->elements; E != NULL; E = E->next) { - if (E->graph == Circuit1->file) { - C1++; - E1 = E; - } - else { - C2++; - E2 = E; - } - } - if (C1 == C2 && C1 != 1) { - unsigned long newhash; - - /* Do all of them at once. */ - /* NOTE: If this were to fail for some reason, it would */ - /* probably be necessary to do this the original slow way */ - /* which is to rehash one pair at a time and iterate to */ - /* convergence, and repeat. */ - - E1 = E2 = EC->elements; - while (E1 != NULL && E2 != NULL) { - while (E1->graph != Circuit1->file) E1 = E1->next; - while (E2->graph != Circuit2->file) E2 = E2->next; - Magic(newhash); - E1->hashval = newhash; - E2->hashval = newhash; - E1 = E1->next; - E2 = E2->next; - } - } - } - - FractureElementClass(&ElementClasses); - FractureNodeClass(&NodeClasses); - ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() != -1); - return(VerifyMatching()); -} - -/* - *------------------------------------------------------------------------- - * - * ResolveNodeAutomorphismsFast -- - * - * Apply arbitrary symmetry breaking to symmetric node lists, then iterate - * exhaustively to resolve all automorphisms. Normally, all automorphisms - * should be resolved by ResolveElementAutomorphisms(). It is likely true - * that this routine will never run, by definition. - * - * Return value is the same as VerifyMatching() - * - *------------------------------------------------------------------------- - * - */ - -int ResolveNodeAutomorphismsFast() -{ - struct Node *N; - struct NodeClass *NC; - int C1, C2; - - for (NC = NodeClasses; NC != NULL; NC = NC->next) { - struct Node *N1, *N2; - C1 = C2 = 0; - N1 = N2 = NULL; - for (N = NC->nodes; N != NULL; N = N->next) { - if (N->graph == Circuit1->file) { - C1++; - N1 = N; - } - else { - C2++; - N2 = N; - } - } - if (C1 == C2 && C1 != 1) { - unsigned long newhash; - - /* Do all of them at once */ - /* NOTE: If this were to fail for some reason, it would */ - /* probably be necessary to do this the original slow way */ - /* which is to rehash one pair at a time and iterate to */ - /* convergence, and repeat. */ - - N1 = N2 = NC->nodes; - while (N1 != NULL && N2 != NULL) { - while (N1->graph != Circuit1->file) N1 = N1->next; - while (N2->graph != Circuit2->file) N2 = N2->next; - Magic(newhash); - N1->hashval = newhash; - N2->hashval = newhash; - N1 = N1->next; - N2 = N2->next; - } - } - } - - FractureElementClass(&ElementClasses); - FractureNodeClass(&NodeClasses); - ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() != -1); - return(VerifyMatching()); -} - -/* - *------------------------------------------------------------------------- - * - * ResolveAutormorphisms -- - * - * Do symmetry breaking of automorphisms. If FastSymmetryBreaking is - * False (not the default), then run ResolveAutomorphismsFull() (q.v.). - * Otherwise, run the fast symmetry breaking method: - * - * Arbitrarily equivalence all pairs of elements and nodes within all - * automorphic classes, and run to completion. Note that this is a - * fast version of ResolveAutomorphismsFull(), and can run many orders - * of magnitude faster. However, it can also produce an incorrect - * result, declaring a global mismatch where none exists. In that - * case, the matching should be re-run with ResolveAutomorphismsFull(). - * - * Some investigation needs to be done to determine if there is a way - * to execute faster symmetry breaking without the possibility of - * producing an incorrect result. - * - * Return value is the same as VerifyMatching() - * - *------------------------------------------------------------------------- - */ - -int ResolveAutomorphisms() -{ - int result; - - if (FastSymmetryBreaking == 0) - return ResolveAutomorphismsFull(); - - result = ResolveElementAutomorphismsFast(); - if (result != 0) - result = ResolveNodeAutomorphismsFast(); - - return result; -} - /*------------------------------------------------------*/ /* PermuteSetup -- */ /* Add an entry to a cell's "permutes" linked list. */ diff --git a/base/netcmp.h b/base/netcmp.h index fba1bb0..9d289f0 100644 --- a/base/netcmp.h +++ b/base/netcmp.h @@ -7,7 +7,6 @@ extern struct nlist *Circuit1; extern struct nlist *Circuit2; extern int ExhaustiveSubdivision; -extern int FastSymmetryBreaking; extern int left_col_end; extern int right_col_end; diff --git a/tcltk/netgen.sh b/tcltk/netgen.sh new file mode 100644 index 0000000..4a10144 --- /dev/null +++ b/tcltk/netgen.sh @@ -0,0 +1,60 @@ +#!/bin/bash +# +# For installation, put this file (netgen.sh) in a standard executable path. +# Put startup script "netgen.tcl" and shared library "tclnetgen.so" +# in ${CAD_ROOT}/netgen/tcl/, with a symbolic link from file +# ".wishrc" to "netgen.tcl". +# +# This script starts irsim under the Tcl interpreter, +# reading commands from a special .wishrc script which +# launches irsim and retains the Tcl interactive interpreter. + +# Parse for the argument "-c[onsole]". If it exists, run netgen +# with the TkCon console. Strip this argument from the argument list. + +TKCON=true +BATCH= +GUI= +NETGEN_WISH=/usr/bin/wish +export NETGEN_WISH + +# Hacks for Cygwin +if [ ${TERM:=""} = "cygwin" ]; then + export PATH="$PATH:/usr/lib" + export DISPLAY=${DISPLAY:=":0"} +fi + +# Preserve quotes in arguments (thanks, Stackoverflow!) +arglist='' +for i in "$@" ; do + case $i in + -noc*) TKCON=;; + -bat*) BATCH=true; TKCON=;; + -gui) GUI=true; TKCON=;; + *) arglist="$arglist${arglist:+ }\"${i//\"/\\\"}\"";; + esac +done + +if [ $TKCON ]; then + + exec /usr/local/lib/netgen/tcl/tkcon.tcl \ + -eval "source /usr/local/lib/netgen/tcl/console.tcl" \ + -slave "package require Tk; set argc $#; set argv [list $arglist]; \ + source /usr/local/lib/netgen/tcl/netgen.tcl" + +# Run the Python LVS manager GUI + +elif [ $GUI ]; then + exec /usr/local/lib/netgen/python/lvs_manager.py $@ + +# +# Run the stand-in for wish (netgenexec), which acts exactly like "wish" +# except that it replaces ~/.wishrc with netgen.tcl. This executable is +# *only* needed when running without the console; the console itself is +# capable of sourcing the startup script. +# + +else + exec /usr/local/lib/netgen/tcl/netgenexec -- "$@" + +fi diff --git a/tcltk/netgen.tcl b/tcltk/netgen.tcl new file mode 100644 index 0000000..322c649 --- /dev/null +++ b/tcltk/netgen.tcl @@ -0,0 +1,753 @@ +# Wishrc startup for ToolScript (netgen) +# +# For installation: Put this file and also tclnetgen.so into +# directory ${CAD_ROOT}/netgen/tcl/, and set the "load" line below +# to point to the location of tclnetgen.so. Also see comments +# in shell script "netgen.sh". +# + +# Check namespaces for existence of other applications +set UsingMagic 0 +set UsingXCircuit 0 +set UsingIRSIM 0 +set batchmode 0 +set nlist [namespace children] +foreach i $nlist { + switch $i { + ::magic { set UsingMagic 1 } + ::xcircuit { set UsingXCircuit 1 } + ::irsim { set UsingIRSIM 1 } + } +} + +# -lazy option not needed if stubs libraries are handled correctly +# load -lazy /usr/local/lib/netgen/tcl/tclnetgen.so + +load /usr/local/lib/netgen/tcl/tclnetgen.so + +#---------------------------------------------------------------- +# Convert LVS list result into a JSON file +#---------------------------------------------------------------- + +proc netgen::convert_to_json {filename lvs_final} { + set pidx [string last . $filename] + set jsonname [string replace $filename $pidx end ".json"] + if {![catch {open $jsonname w} fjson]} { + puts $fjson "\[" + # Outer list is of each cell compared + set clen [llength $lvs_final] + set cidx 0 + foreach circuit $lvs_final { + incr cidx + puts $fjson " \{" + set nkeys [llength $circuit] + set kidx 0 + foreach {key value} $circuit { + incr kidx 2 + switch $key { + name { + puts $fjson " \"${key}\": \[" + set cktval [lindex $value 0] + puts $fjson " \"${cktval}\"," + set cktval [lindex $value 1] + puts $fjson " \"${cktval}\"" + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + pins { + puts $fjson " \"${key}\": \[" + puts $fjson " \[" + set cktval [lindex $value 0] + foreach pin [lrange $cktval 0 end-1] { + puts $fjson " \"$pin\"," + } + set pin [lindex $cktval end] + puts $fjson " \"$pin\"" + puts $fjson " \], \[" + set cktval [lindex $value 1] + foreach pin [lrange $cktval 0 end-1] { + puts $fjson " \"$pin\"," + } + set pin [lindex $cktval end] + puts $fjson " \"$pin\"" + puts $fjson " \]" + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + nets { + puts $fjson " \"${key}\": \[" + set cktval [lindex $value 0] + puts $fjson " $cktval," + set cktval [lindex $value 1] + puts $fjson " $cktval" + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + devices { + puts $fjson " \"${key}\": \[" + puts $fjson " \[" + set cktval [lindex $value 0] + foreach dev [lrange $cktval 0 end-1] { + set devname [lindex $dev 0] + set devnum [lindex $dev 1] + puts $fjson " \[\"${devname}\", ${devnum}\]," + } + set dev [lindex $cktval end] + set devname [lindex $dev 0] + set devnum [lindex $dev 1] + puts $fjson " \[\"${devname}\", ${devnum} \]" + puts $fjson " \], \[" + set cktval [lindex $value 1] + foreach dev [lrange $cktval 0 end-1] { + set devname [lindex $dev 0] + set devnum [lindex $dev 1] + puts $fjson " \[\"${devname}\", ${devnum} \]," + } + set dev [lindex $cktval end] + set devname [lindex $dev 0] + set devnum [lindex $dev 1] + puts $fjson " \[\"${devname}\", ${devnum} \]" + puts $fjson " \]" + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + goodnets - + badnets { + puts $fjson " \"${key}\": \[" + set glen [llength $value] + set gidx 0 + foreach group $value { + incr gidx + puts $fjson " \[" + puts $fjson " \[" + set cktval [lindex $group 0] + set nlen [llength $cktval] + set nidx 0 + foreach net $cktval { + incr nidx + puts $fjson " \[" + set netname [string map {"\\" "\\\\"} [lindex $net 0]] + puts $fjson " \"$netname\"," + puts $fjson " \[" + set netconn [lindex $net 1] + foreach fanout [lrange $netconn 0 end-1] { + set devname [lindex $fanout 0] + set pinname [lindex $fanout 1] + set count [lindex $fanout 2] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$devname\", \"$pinname\", $count \]," + } + set fanout [lindex $netconn end] + set devname [lindex $fanout 0] + set pinname [lindex $fanout 1] + set count [lindex $fanout 2] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$devname\", \"$pinname\", $count \]" + puts $fjson " \]" + if {$nidx == $nlen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + puts $fjson " \], \[" + set cktval [lindex $group 1] + set nlen [llength $cktval] + set nidx 0 + foreach net $cktval { + incr nidx + puts $fjson " \[" + set netname [string map {"\\" "\\\\"} [lindex $net 0]] + puts $fjson " \"$netname\"," + puts $fjson " \[" + set netconn [lindex $net 1] + foreach fanout [lrange $netconn 0 end-1] { + set devname [lindex $fanout 0] + set pinname [lindex $fanout 1] + set count [lindex $fanout 2] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$devname\", \"$pinname\", $count \]," + } + set fanout [lindex $netconn end] + set devname [lindex $fanout 0] + set pinname [lindex $fanout 1] + set count [lindex $fanout 2] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$devname\", \"$pinname\", $count \]" + puts $fjson " \]" + if {$nidx == $nlen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + puts $fjson " \]" + if {$gidx == $glen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + goodelements - + badelements { + puts $fjson " \"${key}\": \[" + set glen [llength $value] + set gidx 0 + foreach group $value { + incr gidx + puts $fjson " \[" + puts $fjson " \[" + set cktval [lindex $group 0] + set ilen [llength $cktval] + set iidx 0 + foreach inst $cktval { + incr iidx + puts $fjson " \[" + set instname [string map {"\\" "\\\\"} [lindex $inst 0]] + puts $fjson " \"$instname\"," + puts $fjson " \[" + set instpins [lindex $inst 1] + foreach fanout [lrange $instpins 0 end-1] { + set pinname [lindex $fanout 0] + set count [lindex $fanout 1] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$pinname\", $count \]," + } + set fanout [lindex $instpins end] + set pinname [lindex $fanout 0] + set count [lindex $fanout 1] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$pinname\", $count \]" + puts $fjson " \]" + if {$iidx == $ilen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + puts $fjson " \], \[" + set cktval [lindex $group 1] + set ilen [llength $cktval] + set iidx 0 + foreach inst $cktval { + incr iidx + puts $fjson " \[" + set instname [string map {"\\" "\\\\"} [lindex $inst 0]] + puts $fjson " \"$instname\"," + puts $fjson " \[" + set instpins [lindex $inst 1] + foreach fanout [lrange $instpins 0 end-1] { + set pinname [lindex $fanout 0] + set count [lindex $fanout 1] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$pinname\", $count \]," + } + set fanout [lindex $instpins end] + set pinname [lindex $fanout 0] + set count [lindex $fanout 1] + if {$count == {}} {set count 0} + puts $fjson " \[ \"$pinname\", $count \]" + puts $fjson " \]" + if {$iidx == $ilen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + puts $fjson " \]" + if {$gidx == $glen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + properties { + puts $fjson " \"${key}\": \[" + set plen [llength $value] + set pidx 0 + foreach instance $value { + incr pidx + puts $fjson " \[" + set instnames [string map {"\\" "\\\\"} [lindex $instance 0]] + set instname0 [string map {"\\" "\\\\"} [lindex $instnames 0]] + puts $fjson " \[" + puts $fjson " \"${instname0}\"," + puts $fjson " \[" + foreach property [lrange $instance 1 end-1] { + set prop0 [lindex $property 0] + set propname [lindex $prop0 0] + set propval [lindex $prop0 1] + puts $fjson " \[\"${propname}\", \"${propval}\"\]," + } + set property [lindex $instance end] + set prop0 [lindex $property 0] + set propname [lindex $prop0 0] + set propval [lindex $prop0 1] + puts $fjson " \[\"${propname}\", \"${propval}\"\]" + puts $fjson " \]" + puts $fjson " \]," + set instname1 [string map {"\\" "\\\\"} [lindex $instnames 1]] + puts $fjson " \[" + puts $fjson " \"${instname1}\"," + puts $fjson " \[" + foreach property [lrange $instance 1 end-1] { + set prop1 [lindex $property 1] + set propname [lindex $prop1 0] + set propval [lindex $prop1 1] + puts $fjson " \[\"${propname}\", \"${propval}\"\]," + } + set property [lindex $instance end] + set prop1 [lindex $property 1] + set propname [lindex $prop1 0] + set propval [lindex $prop1 1] + puts $fjson " \[\"${propname}\", \"${propval}\"\]" + puts $fjson " \]" + puts $fjson " \]" + if {$pidx == $plen} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + if {$kidx == $nkeys} { + puts $fjson " \]" + } else { + puts $fjson " \]," + } + } + } + } + if {$cidx == $clen} { + puts $fjson " \}" + } else { + puts $fjson " \}," + } + } + puts $fjson "\]" + } + close $fjson +} + +#---------------------------------------------------------------- +# Define the "lvs" command as a way of calling the netgen options +# for standard compare, essentially the same as the old "netcomp" +# standalone program. +# +# Use the "canonical" command to parse the file and cell names, +# although if the cells have not been read in yet, then the +# original syntax of filename or {filename cellname} is required. +# +# "args" is passed to verify and may therefore contain only the +# value "-list" or nothing. If "-list", then output is returned +# as a nested list. +#---------------------------------------------------------------- + +proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { + set dolist 0 + set dojson 0 + foreach arg $args { + if {$arg == "-list"} { + puts stdout "Generating list result" + set dolist 1 + set lvs_final {} + } elseif {$arg == "-json"} { + puts stdout "Generating JSON file result" + set dolist 1 + set dojson 1 + set lvs_final {} + } elseif {$arg == "-blackbox"} { + puts stdout "Treating empty subcircuits as black-box cells" + netgen::model blackbox on + } elseif {$arg == "-full"} { + puts stdout "Using full symmetry breaking method" + netgen::symmetry full + } + } + + # Allow name1 or name2 to be a list of {filename cellname}, + # A single , or any valid_cellname form if the + # file has already been read. + + if {[catch {set flist1 [canonical $name1]}]} { + if {[llength $name1] == 2} { + set file1 [lindex $name1 0] + set cell1 [lindex $name1 1] + } else { + set file1 $name1 + set cell1 $name1 + } + puts stdout "Reading netlist file $file1" + set fnum1 [netgen::readnet $file1] + } else { + set cell1 [lindex $flist1 0] + set fnum1 [lindex $flist1 1] + set flist1 [canonical $fnum1] + set file1 [lindex $flist1 0] + } + + if {[catch {set flist2 [canonical $name2]}]} { + if {[llength $name2] == 2} { + set file2 [lindex $name2 0] + set cell2 [lindex $name2 1] + } else { + set file2 $name2 + set cell2 $name2 + } + puts stdout "Reading netlist file $file2" + set fnum2 [netgen::readnet $file2] + } else { + set cell2 [lindex $flist2 0] + set fnum2 [lindex $flist2 1] + set flist2 [canonical $fnum2] + set file2 [lindex $flist2 0] + } + + if {$fnum1 == $fnum2} { + puts stderr "Both cells are in the same netlist: Cannot compare!" + return + } + + set clist1 [cells list $fnum1] + set cidx [lsearch -regexp $clist1 ^$cell1$] + if {$cidx < 0} { + puts stderr "Cannot find cell $cell1 in file $file1" + return + } else { + set cell1 [lindex $clist1 $cidx] + } + set clist2 [cells list $fnum2] + set cidx [lsearch -regexp $clist2 ^$cell2$] + if {$cidx < 0} { + puts stderr "Cannot find cell $cell2 in file $file2" + return + } else { + set cell2 [lindex $clist2 $cidx] + } + + netgen::compare assign "$fnum1 $cell1" "$fnum2 $cell2" + + if {[file exists $setupfile]} { + puts stdout "Reading setup file $setupfile" + # Instead of sourcing the setup file, run each line so we can + # catch individual errors and not let them halt the LVS process + set perrors 0 + if {![catch {open $setupfile r} fsetup]} { + set sline 0 + set command {} + while {[gets $fsetup line] >= 0} { + incr sline + append command $line "\n" + if {[info complete $command]} { + if {[catch {uplevel 1 [list namespace eval netgen $command]} msg]} { + set msg [string trimright $msg "\n"] + puts stderr "Error $setupfile:$sline (ignoring), $msg" + incr perrors + } + set command {} + } + } + close $fsetup + } else { + puts stdout "Error: Cannot read the setup file $setupfile" + } + + if {$perrors > 0} { + puts stdout "Warning: There were errors reading the setup file" + } + } elseif {[string first nosetup $setupfile] < 0} { + netgen::permute default ;# transistors and resistors + netgen::property default + } + + if {[string first nolog $logfile] < 0} { + puts stdout "Comparison output logged to file $logfile" + netgen::log file $logfile + + netgen::log start + netgen::log echo off + set dolog true + } else { + set dolog false + } + + if {$dolist == 1} { + set endval [netgen::compare -list hierarchical "$fnum1 $cell1" "$fnum2 $cell2"] + } else { + set endval [netgen::compare hierarchical "$fnum1 $cell1" "$fnum2 $cell2"] + } + if {$endval == {}} { + netgen::log put "No cells in queue!\n" + return + } + set properr {} + set pinsgood 0 + while {$endval != {}} { + if {$dolist == 1} { + netgen::run -list converge + } else { + netgen::run converge + } + netgen::log echo on + if {[verify equivalent]} { + # Resolve automorphisms by pin and property + if {$dolist == 1} { + netgen::run -list resolve + } else { + netgen::run resolve + } + set uresult [verify unique] + if {$uresult == 0} { + netgen::log put " Networks match locally but not globally.\n" + netgen::log put " Probably connections are swapped.\n" + netgen::log put " Check the end of logfile ${logfile} for implicated nodes.\n" + if {$dolist == 1} { + verify -list nodes + } else { + verify nodes + } + + # Flatten the non-matching subcircuit (but not the top-level cells) + if {[netgen::print queue] != {}} { + netgen::log put " Flattening non-matched subcircuits $endval" + netgen::flatten class "[lindex $endval 0] $fnum1" + netgen::flatten class "[lindex $endval 1] $fnum2" + } + } else { + # Match pins + netgen::log echo off + if {$dolist == 1} { + set result [equate -list pins "$fnum1 [lindex $endval 0]" \ + "$fnum2 [lindex $endval 1]"] + } else { + set result [equate pins "$fnum1 [lindex $endval 0]" \ + "$fnum2 [lindex $endval 1]"] + } + if {$result != 0} { + equate classes "$fnum1 [lindex $endval 0]" \ + "$fnum2 [lindex $endval 1]" + } + set pinsgood $result + netgen::log echo on + } + if {$uresult == 2} {lappend properr [lindex $endval 0]} + } else { + # Flatten the non-matching subcircuit (but not the top-level cells) + if {[netgen::print queue] != {}} { + netgen::log put " Flattening non-matched subcircuits $endval" + netgen::flatten class "[lindex $endval 0] $fnum1" + netgen::flatten class "[lindex $endval 1] $fnum2" + } + } + netgen::log echo off + if {$dolist == 1} { + catch {lappend lvs_final $lvs_out} + set lvs_out {} + set endval [netgen::compare -list hierarchical] + } else { + set endval [netgen::compare hierarchical] + } + } + netgen::log echo off + puts stdout "Result: " nonewline + netgen::log echo on + if {$pinsgood == 0} { + netgen::log put "The top level cell failed pin matching.\n" + } else { + verify only + } + if {$properr != {}} { + netgen::log put "The following cells had property errors: $properr\n" + } + if {$dolog} { + netgen::log end + } + puts stdout "LVS Done." + if {$dojson == 1} { + netgen::convert_to_json $logfile $lvs_final + } elseif {$dolist == 1} { + return $lvs_final + } +} + +# It is important to make sure no netgen commands overlap with Tcl built-in +# commands, because otherwise the namespace import will fail. + +proc pushnamespace { name } { + + set y [namespace eval ${name} info commands ::${name}::*] + set z [info commands] + + foreach v $y { + regsub -all {\*} $v {\\*} i + set x [namespace tail $i] + if {[lsearch $z $x] < 0} { + namespace import $i + } else { + puts "Warning: ${name} command '$x' use fully-qualified name '$v'" + } + } +} + +proc popnamespace { name } { + set z [info commands] + set l [expr [string length ${name}] + 5] + + while {[set v [lsearch $z ${name}_tcl_*]] >= 0} { + set y [lindex $z $v] + set w [string range $y $l end] + interp alias {} ::$w {} + rename ::$y ::$w + puts "Info: replacing ::$w with ::$y" + } + namespace forget ::${name}::* +} + +set auto_noexec 1 ;# don't EVER call UNIX commands w/o "shell" in front + +#---------------------------------------------------------------------- +# Cross-Application section +#---------------------------------------------------------------------- + +# Setup IRSIM assuming that the Tcl version is installed. +# We do not need to rename procedure irsim to NULL because it is +# redefined in a script, which simply overwrites the original. + +proc irsim { args } { + global CAD_ROOT + set irsimscript [glob -nocomplain ${CAD_ROOT}/irsim/tcl/irsim.tcl] + if { ${irsimscript} == {} } { + puts stderr "\"irsim\" requires Tcl-based IRSIM version 9.6 or newer." + puts stderr "Could not find script \"irsim.tcl\". If IRSIM is installed in a" + puts stderr "place other than CAD_ROOT (=${CAD_ROOT}), use the command" + puts stderr "\"source /irsim.tcl\" before doing \"irsim\"." + } else { + source $irsimscript + eval {irsim} $args + } +} + +# Setup Xcircuit assuming that the Tcl version is installed. + +proc xcircuit { args } { + global CAD_ROOT + global argc + global argv + set xcircscript [glob -nocomplain ${CAD_ROOT}/xcircuit*/xcircuit.tcl] + if { ${xcircscript} == {} } { + puts stderr "\"xcircuit\" requires Tcl-based XCircuit version 3.1 or newer." + puts stderr "Could not find script \"xcircuit.tcl\". If XCircuit is installed in a" + puts stderr "place other than CAD_ROOT (=${CAD_ROOT}), use the command" + puts stderr "\"source /xcircuit.tcl\"." + } else { + # if there are multiple installed versions, choose the highest version. + if {[llength $xcircscript] > 1} { + set xcircscript [lindex [lsort -decreasing -dictionary $xcircscript] 0] + } + set argv $args + set argc [llength $args] + uplevel #0 source $xcircscript + } +} + +# Setup Magic assuming that the Tcl version is installed. + +proc magic { args } { + global CAD_ROOT + global argc + global argv + set magicscript [glob -nocomplain ${CAD_ROOT}/magic/tcl/magic.tcl] + if { ${magicscript} == {} } { + puts stderr "\"magic\" requires Tcl-based Magic version 7.2 or newer." + puts stderr "Could not find script \"magic.tcl\". If Magic is installed in a" + puts stderr "place other than CAD_ROOT (=${CAD_ROOT}), use the command" + puts stderr "\"source /magic.tcl\"." + } else { + set argv $args + set argc [llength $args] + uplevel #0 source $magicscript + } +} + +#---------------------------------------------------------------------------- +# Have we called netgen from tkcon or a clone thereof? If so, set NetgenConsole +#---------------------------------------------------------------------------- + +if {! $UsingMagic } { + if {[lsearch [interp aliases] tkcon] != -1} { + set NetgenConsole tkcon + wm withdraw . + } +} + +pushnamespace netgen + +#---------------------------------------------------------------------------- +# For now, if we are standalone, pop down the default Tk window. +# Sometime later we may wish to provide a standalone GUI frontend in Tk +# to improve upon the original X11 "xnetgen" frontend. If so, its +# definitions would go below. + +if {! $UsingMagic } { + if {[lsearch [interp aliases] tkcon] != -1} { + if {[string range [wm title .] 0 3] == "wish"} { + wm withdraw . + } + } +} + +#---------------------------------------------------------------------------- +# No-console mode drops "--" in front of the argument list and "-noc" +# is retained, so remove them. Internally, the console will be determined +# by checking for a slave interpreter, so there is no need for any +# action here other than removing the argument. + +if {[lindex $argv 0] == "--"} { + incr argc -1 + set argv [lrange $argv 1 end] +} + +if {[string range [lindex $argv 0] 0 3] == "-noc"} { + incr argc -1 + set argv [lrange $argv 1 end] +} + +if {[string range [lindex $argv 0] 0 3] == "-bat"} { + incr argc -1 + set argv [lrange $argv 1 end] + set batchmode 1 +} + +#---------------------------------------------------------------------------- +# Anything on the command line is assumed to be a netgen command to evaluate + +if {[catch {eval $argv}]} { + puts stdout "$errorInfo" +} +if {$batchmode == 1} {quit} + +#---------------------------------------------------------------------------- +# Netgen start function drops back to interpreter after initialization & setup diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index 05b0800..6f57700 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -246,8 +246,7 @@ Command netcmp_cmds[] = { "\n " "toggle exhaustive subdivision"}, {"symmetry", _netcmp_symmetry, - "[fast|full]\n " - "apply method for symmetry breaking"}, + "(deprecated)"}, {"restart", _netcmp_restart, "\n " "start over (reset data structures)"}, @@ -3982,39 +3981,14 @@ _netcmp_permute(ClientData clientData, /* Formerly: x */ /* Results: */ /* Side Effects: */ +/* Notes: Deprecated, retained for compatibility. */ /*------------------------------------------------------*/ int _netcmp_symmetry(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { - char *fastfull[] = { - "fast", "full", NULL - }; - enum OptionIdx { - FAST_IDX, FULL_IDX - }; - int result, index; - - if (objc == 1) - index = -1; - else { - if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)fastfull, - "option", 0, &index) != TCL_OK) - return TCL_ERROR; - } - - switch(index) { - case FAST_IDX: - FastSymmetryBreaking = TRUE; - break; - case FULL_IDX: - FastSymmetryBreaking = FALSE; - break; - } - Printf("Symmetry breaking method: %s.\n", - FastSymmetryBreaking ? "FAST" : "FULL"); - + Printf("Symmetry breaking method has been deprecated.\n"); return TCL_OK; }