Compare commits

...
8 Commits
Author SHA1 Message Date
Tim Edwards a888502038 Merge branch 'master' into netgen-1.5 2021-03-02 03:00:35 -05:00
Tim Edwards ea07642172 Removed the symmetry breaking option from the scripted "lvs"
command.
2021-03-01 16:57:49 -05:00
Tim Edwards e9da037001 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.
2021-03-01 16:55:07 -05:00
Tim Edwards 7ee50a3f8f Modified the parallel combination code to treat cells as equivalently
parallel if the same pins are no-connects.  These were previously not
treated as parallel because each no-connect has a unique node number,
and cells were only considered as parallel if all pins connected to
the same node numbers.  This avoids issues with long-running symmetry
breaking on standard cell designs due to cells like antenna taps or
any cell that is placed without connecting it up.  To do:  This
makes indistinguishable certain cases, e.g., N cells in circuit 1
with pin X open vs. N cells in circuit 2 with pin X all tied together.
This could be caught during property matching.
2021-03-01 16:33:55 -05:00
Tim Edwards c16258d4d1 Merge branch 'master' into netgen-1.5 2021-02-25 03:00:36 -05:00
Tim Edwards c7848c9c02 Corrected an error that (in a rare circumstance) can cause netgen
to go into an infinite loop and fill memory until it crashes, due
to a complete pin mismatch between devices causing one device to
have its pins removed and replaced with proxy pins.
2021-02-24 16:12:19 -05:00
Tim Edwards 5341d7a5fd Merge branch 'master' into netgen-1.5 2021-02-17 03:00:31 -05:00
Tim Edwards 402e1f0f25 Found a chokepoint in FlattenInstancesOf that was unnecessary as it
was running through the entire object linked list to find the
predecessor of a record that it had already found.  Solved by simply
keeping track of the predecessor record.
2021-02-16 17:12:00 -05:00
9 changed files with 959 additions and 236 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.166
1.5.169
+76 -17
View File
@@ -253,7 +253,7 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
{
struct objlist *ParentParams;
struct objlist *ParentProps;
struct objlist *NextObj;
struct objlist *NextObj, *LastObj, *prepp;
struct objlist *ChildObjList;
struct nlist *ThisCell;
struct nlist *ChildCell;
@@ -296,21 +296,35 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
while (notdone) {
notdone = 0;
ParentParams = ThisCell->cell;
LastObj = NULL;
for (ParentParams = ThisCell->cell; ParentParams != NULL;
ParentParams = NextObj) {
ParentParams = NextObj) {
if (Debug) Printf("Parent = %s, type = %d\n",
ParentParams->name, ParentParams->type);
NextObj = ParentParams->next;
if (ParentParams->type != FIRSTPIN) continue;
if (!(*matchfunc)(ParentParams->model.class, instance)) continue;
if (ParentParams->type != FIRSTPIN) {
LastObj = ParentParams;
continue;
}
if (!(*matchfunc)(ParentParams->model.class, instance)) {
LastObj = ParentParams;
continue;
}
ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file);
if (Debug)
Printf(" Flattening instance: %s, primitive = %s\n",
ParentParams->instance.name, (ChildCell->class ==
CLASS_SUBCKT) ? "no" : "yes");
if (ChildCell->class != CLASS_SUBCKT) continue;
if (ChildCell == ThisCell) continue; // Avoid infinite loop
if (ChildCell->class != CLASS_SUBCKT) {
LastObj = ParentParams;
continue;
}
if (ChildCell == ThisCell) {
LastObj = ParentParams;
continue; // Avoid infinite loop
}
/* Does the parent cell have properties? If so, save a pointer to them */
for (ParentProps = ParentParams->next; ParentProps &&
@@ -373,10 +387,10 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
}
/* in pathological cases, the lengths of the port lists may
change. This is an error, but that is no reason to allow
the code to core dump. We avoid this by placing a
superfluous check on ob2->type
*/
* change. This is an error, but that is no reason to allow
* the code to core dump. We avoid this by placing a
* superfluous check on ob2->type
*/
if (ob2 != NULL)
ob2 = ob2->next;
@@ -497,10 +511,21 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ;
}
else {
/* find ParentParams in ThisCell list */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next);
/* find ParentParams in ThisCell list. In most cases, LastObj */
/* should be pointing to it. */
if (LastObj && (LastObj->next == ParentParams)) {
ob2 = LastObj;
}
else {
for (ob2 = LastObj; ob2 && ob2->next != ParentParams; ob2=ob2->next);
if (ob2 == NULL) {
/* It should not happen that LastObj is ahead of ParentParams */
/* but just in case, this long loop will find it. */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next);
}
}
if (ob2)
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ;
for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ;
}
/* now, ob2 is last element in child list, so skip and reclaim parent */
@@ -1136,9 +1161,10 @@ int UniquePins(char *name, int filenum)
struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
{
struct nlist *ptr;
struct objlist *ob, *obt, *lob, *nob, *firstpin;
struct objlist *ob, *obt, *lob, *nob, *firstpin, *pob;
struct nlist *tc = (struct nlist *)clientdata;
int pinnum;
char *saveinst = NULL;
ptr = (struct nlist *)(p->ptr);
if (tc->file != ptr->file) return NULL;
@@ -1186,13 +1212,44 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
}
FREE(ob->name);
if (ob->instance.name != NULL) FREE(ob->instance.name);
if (ob->instance.name != NULL) {
/* Keep a copy of the instance name (see below) */
if (saveinst != NULL) FREE(saveinst);
saveinst = ob->instance.name;
}
if (ob->model.class != NULL) FREE(ob->model.class);
FREE(ob);
}
else {
lob = ob;
ob->type = pinnum++; // Renumber pins in order
if ((ob->type == PROPERTY) && (pinnum == 1))
{
/* If this happens, then all the pins got removed,
* and there is probably something very much wrong
* with the setup. However, to keep netgen from
* blowing up, add back a "proxy(no pins)" record
* in front; otherwise we'd have an orphaned
* property record.
*/
pob = GetObject();
pob->name = (char *)MALLOC(15);
sprintf(pob->name, "proxy(no pins)");
pob->model.class = strsave(ob->model.class);
if (saveinst != NULL)
pob->instance.name = strsave(saveinst);
else
/* This should never happen */
pob->instance.name = strsave("error");
pob->type = pinnum++;
pob->node = -1;
pob->next = ob;
lob->next = pob;
lob = ob;
}
else
{
lob = ob;
ob->type = pinnum++; // Renumber pins in order
}
}
ob = nob;
obt = obt->next;
@@ -1203,6 +1260,8 @@ struct nlist *cleanuppins(struct hashlist *p, void *clientdata)
HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict));
}
}
if (saveinst != NULL) FREE(saveinst);
return NULL; /* Keep the search going */
}
+28 -181
View File
@@ -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. */
@@ -6715,18 +6549,31 @@ struct nlist *addproxies(struct hashlist *p, void *clientdata)
firstpin = ob;
while (tob && (tob->type == PORT || tob->type == UNKNOWN)) {
if (tob->type == UNKNOWN) {
/* ??? Do not do anything with (no pins) entries */
/* Do not do anything with (no pins) entries (in the reference cell) */
if (strcmp(tob->name, "proxy(no pins)")) {
obn = (struct objlist *)CALLOC(1, sizeof(struct objlist));
obn->name = (char *)MALLOC(strlen(firstpin->instance.name)
+ strlen(tob->name) + 2);
sprintf(obn->name, "%s/%s", firstpin->instance.name, tob->name);
obn->instance.name = strsave(firstpin->instance.name);
obn->model.class = strsave(tc->name);
/* But if the target cell instance has proxy(no pins), then reuse
* the record and modify it.
*/
if (!strcmp(ob->name, "proxy(no pins)")) {
obn = ob;
FREE(ob->name);
obn->name = (char *)MALLOC(strlen(ob->instance.name)
+ strlen(tob->name) + 2);
sprintf(obn->name, "%s/%s", ob->instance.name, tob->name);
ob = obn->next;
}
else {
obn = (struct objlist *)CALLOC(1, sizeof(struct objlist));
obn->name = (char *)MALLOC(strlen(firstpin->instance.name)
+ strlen(tob->name) + 2);
sprintf(obn->name, "%s/%s", firstpin->instance.name, tob->name);
obn->instance.name = strsave(firstpin->instance.name);
obn->model.class = strsave(tc->name);
obn->next = ob; // Splice into object list
lob->next = obn;
}
obn->type = i++;
obn->node = numnodes++;
obn->next = ob; // Splice into object list
lob->next = obn;
lob = obn;
// Hash the new pin record for "LookupObject()"
-1
View File
@@ -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;
+38 -4
View File
@@ -3132,6 +3132,9 @@ int remove_group_tags(struct objlist *ob)
/* If the device has permutable pins, then duplicate hashes are made */
/* for each permutation. */
/* */
/* If the device has isolated (unconnected) pins, then treat them as */
/* all belonging to the same net for the purpose of parallel merging. */
/* */
/* Return the number of devices merged. */
/*----------------------------------------------------------------------*/
@@ -3146,6 +3149,7 @@ int CombineParallel(char *model, int file)
size_t pcnt;
int i, dcnt = 0, hastag;
char *pstr, *p2str, *pptr;
int *nodecount;
struct valuelist *kv;
if ((tp = LookupCellFile(model, file)) == NULL) {
@@ -3155,6 +3159,17 @@ int CombineParallel(char *model, int file)
InitializeHashTable(&devdict, OBJHASHSIZE);
/* Make one pass to count the number of times each node number is */
/* used. This list indicates which pins are no-connects, so they */
/* can be treated as equivalent for the purpose of parallelization. */
nodecount = (int *)CALLOC((tp->nodename_cache_maxnodenum + 1), sizeof(int));
for (ob = tp->cell; ob; ob = ob->next) {
if (ob->node >= 0)
if (ob->type != NODE)
nodecount[ob->node]++;
}
lob = NULL;
for (ob = tp->cell; ob; ) {
if (ob->type == FIRSTPIN) {
@@ -3201,7 +3216,10 @@ int CombineParallel(char *model, int file)
pptr += pstr - (char *)2;
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
sprintf(pptr, "_%d", ob2->node);
if ((ob->node >= 0) && (nodecount[ob->node] == 1))
strcat(pptr, "_nc");
else
sprintf(pptr, "_%d", ob2->node);
pptr += strlen(pptr);
}
@@ -3237,11 +3255,26 @@ int CombineParallel(char *model, int file)
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob);
ob2 = ob2->next) {
if (ob2 == pob1)
sprintf(pptr, "_%d", pob2->node);
{
if ((pob2->node >= 0) && (nodecount[pob2->node] == 1))
strcat(pptr, "_nc");
else
sprintf(pptr, "_%d", pob2->node);
}
else if (ob2 == pob2)
sprintf(pptr, "_%d", pob1->node);
{
if ((pob1->node >= 0) && (nodecount[pob1->node] == 1))
strcat(pptr, "_nc");
else
sprintf(pptr, "_%d", pob1->node);
}
else
sprintf(pptr, "_%d", ob2->node);
{
if ((ob2->node >= 0) && (nodecount[ob2->node] == 1))
strcat(pptr, "_nc");
else
sprintf(pptr, "_%d", ob2->node);
}
pptr += strlen(pptr);
}
HashPtrInstall(p2str, ob, &devdict);
@@ -3365,6 +3398,7 @@ int CombineParallel(char *model, int file)
if (dcnt > 0) {
Fprintf(stdout, "Class %s: Merged %d devices.\n", model, dcnt);
}
FREE(nodecount);
return dcnt;
}
+60
View File
@@ -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
+753
View File
@@ -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 <filename>, 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 <path>/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 <path>/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 <path>/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
-3
View File
@@ -382,9 +382,6 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
} 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
}
}
+3 -29
View File
@@ -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;
}