diff --git a/.gitignore b/.gitignore index aa0d413..1b2fcdc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ config.log scripts/config.log config.status scripts/config.status +python/lvs_manager.py *.o *.so *~ diff --git a/VERSION b/VERSION index 4ddfe6b..ff2d7ab 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.5.215 +1.5.215-P1 diff --git a/base/flatten.c b/base/flatten.c index 567862c..9df6cbc 100644 --- a/base/flatten.c +++ b/base/flatten.c @@ -66,7 +66,7 @@ void flattenCell(char *name, int file) else ThisCell = LookupCellFile(name, file); if (ThisCell == NULL) { - Printf("No cell %s found.\n", name); + Printf("No cell %s (%d) found.\n", name, file); return; } FreeNodeNames(ThisCell); @@ -278,13 +278,13 @@ int flattenInstancesOf(char *name, int fnum, char *instance) } else { if (Debug) - Printf("Flattening instances of %s within cell: %s\n", instance, name); + Printf("Flattening instances of %s within cell: %s (%d)\n", instance, name, fnum); if (fnum == -1) ThisCell = LookupCell(name); else ThisCell = LookupCellFile(name, fnum); if (ThisCell == NULL) { - Printf("No cell %s found.\n", name); + Printf("No cell %s (%d) found.\n", name, fnum); return 0; } } @@ -370,23 +370,30 @@ int flattenInstancesOf(char *name, int fnum, char *instance) if (ChildListEnd == NULL) ChildListEnd = ChildEnd; - /* update node numbers in child to unique numbers */ + /* update node numbers in child to unique numbers + by adding previous greatest node number. */ oldmax = 0; - for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) + for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) { if (tmp->node > oldmax) oldmax = tmp->node; - if (nextnode <= oldmax) nextnode = oldmax + 1; + if (tmp->node > 0) tmp->node += (nextnode - 1); + } + //if (nextnode <= oldmax) nextnode = oldmax + 1; + nextnode += oldmax; +/* This block is unnecessary for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) if (tmp->node <= oldmax && tmp->node > 0) { if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode); UpdateNodeNumbers(ChildStart, tmp->node, nextnode); nextnode++; } +*/ /* copy nodenumbers of ports from parent */ ob2 = ParentParams; - for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) { - if (IsPort(tmp)) { + // Since ports are grouped at the front of the list and only ports are processed + // quit loop when non port is found + for (tmp = ChildStart; tmp && IsPort(tmp); tmp = tmp->next) { if (tmp->node > 0) { if (ob2->node == -1) { @@ -423,7 +430,6 @@ int flattenInstancesOf(char *name, int fnum, char *instance) if (ob2 != NULL) ob2 = ob2->next; if (ob2 == NULL) break; - } } /* Using name == NULL to indicate that a .ext file is being */ @@ -688,7 +694,7 @@ void convertGlobalsOf(char *name, int fnum, char *instance) else ThisCell = LookupCellFile(name, fnum); if (ThisCell == NULL) { - Printf("No cell %s found.\n", name); + Printf("No cell %s (%d) found.\n", name, fnum); return; } } @@ -1118,7 +1124,7 @@ int UniquePins(char *name, int filenum) ThisCell = LookupCellFile(name, filenum); if (ThisCell == NULL) { - Printf("No cell %s found.\n", name); + Printf("No cell %s (%d) found.\n", name, filenum); return 0; } @@ -1140,7 +1146,7 @@ int UniquePins(char *name, int filenum) if (ob->node > 0) { nodecount[ob->node]++; if (nodecount[ob->node] == 2) { - Printf("Duplicate pin %s in cell %s\n", ob->name, ThisCell->name); + Printf("Duplicate pin %s in cell %s (%d)\n", ob->name, ThisCell->name, filenum); } if (nodecount[ob->node] > 1) { /* Remove this node; prep for removal by marking with UNKNOWN */ @@ -1339,7 +1345,7 @@ int CleanupPins(char *name, int filenum) ThisCell = LookupCellFile(name, filenum); if (ThisCell == NULL) { - Printf("No cell %s found.\n", name); + Printf("No cell %s (%d) found.\n", name, filenum); return 0; } @@ -1607,15 +1613,15 @@ PrematchLists(char *name1, int file1, char *name2, int file2) } if (match) { if (ecomp->cell1 && (ecomp->num1 > 0)) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecomp->cell1->name, - name1); + name1, file1); flattenInstancesOf(name1, file1, ecomp->cell1->name); } if (ecomp->cell2 && (ecomp->num2 > 0)) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecomp->cell2->name, - name2); + name2, file2); flattenInstancesOf(name2, file2, ecomp->cell2->name); } modified++; @@ -1696,9 +1702,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) } if (match) { if (ecomp->cell2) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecomp->cell2->name, - name2); + name2, file2); flattenInstancesOf(name2, file2, ecomp->cell2->name); } modified++; @@ -1754,9 +1760,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) } if (match) { if (ecomp->cell1) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecomp->cell1->name, - name1); + name1, file1); flattenInstancesOf(name1, file1, ecomp->cell1->name); } modified++; @@ -1837,9 +1843,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) } if (found) { Fprintf(stdout, "Removing zero-valued device " - "%s from cell %s makes a better match\n", + "%s from cell %s (%d) makes a better match\n", tsub1->name, - tc1->name); + tc1->name, tc1->file); /* A current source is an open, while a */ /* resistor or voltage source is a short. */ @@ -1948,9 +1954,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) } if (found) { Fprintf(stdout, "Removing zero-valued device " - "%s from cell %s makes a better match\n", + "%s from cell %s (%d) makes a better match\n", tsub2->name, - tc2->name); + tc2->name, tc2->file); /* merge node of endpoints */ if (ecomp->cell2->class != CLASS_ISOURCE) { @@ -2040,9 +2046,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) ecompX0->cell1->file, &compdict); if (dstr) *dstr = '['; if ((ncomp == ecomp0X) && (ecomp0X->num2 <= ecompX0->num1)) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecompX0->cell1->name, - name1); + name1, file1); flattenInstancesOf(name1, file1, ecompX0->cell1->name); ecompX0->num1 = 0; ecomp0X->num1 += ecompX0->num1; @@ -2066,9 +2072,9 @@ PrematchLists(char *name1, int file1, char *name2, int file2) ecomp0X->cell2->file, &compdict); if (dstr) *dstr = '['; if ((ncomp == ecompX0) && (ecompX0->num1 <= ecomp0X->num2)) { - Fprintf(stdout, "Flattening instances of %s in cell %s" + Fprintf(stdout, "Flattening instances of %s in cell %s (%d)" " makes a better match\n", ecomp0X->cell2->name, - name2); + name2, file2); flattenInstancesOf(name2, file2, ecomp0X->cell2->name); ecomp0X->num2 = 0; ecompX0->num2 += ecomp0X->num2; diff --git a/base/netcmp.c b/base/netcmp.c index 9b7456d..062c585 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -3122,6 +3122,47 @@ struct nlist *LookupPrematchedClass(struct nlist *tc1, int file2) return tc2; } +/*----------------------------------------------------------------------*/ +/* Scan the property list of a device to find the number of devices */ +/* implied by the total of M records. If the device does not have a */ +/* property list, then return 1. If any property list does not have an */ +/* "M" record, treat it as 1. */ +/*----------------------------------------------------------------------*/ + +int GetNumDevices(struct objlist *ob) +{ + int p, found, M = 0; + struct objlist *obs; + struct valuelist *vl; + + obs = ob; + if (obs->type != PROPERTY) + for (obs = ob->next; obs && (obs->type != FIRSTPIN) && + (obs->type != PROPERTY); obs = obs->next); + + if ((obs == NULL) || (obs->type != PROPERTY)) return 1; + + while (obs && (obs->type == PROPERTY)) { + found = FALSE; + for (p = 0; ; p++) { + vl = &obs->instance.props[p]; + if (vl->type == PROP_ENDLIST) break; + if (vl->key == NULL) continue; + if ((*matchfunc)(vl->key, "M")) { + if (vl->type == PROP_DOUBLE) + M += (int)vl->value.dval; + else + M += vl->value.ival; + found = TRUE; + break; + } + } + if (found == FALSE) M++; + obs = obs->next; + } + return M; +} + /*----------------------------------------------------------------------*/ /* Attempt to define FirstElementPass that will generate element */ /* classes by names of pins, which will allow elements with different */ @@ -3143,7 +3184,7 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) struct Element *Esrch, *Ecorr; struct NodeList *n; struct nlist *tp1, *tp2, *tp; - int C1, C2, i; + int C1, C2, M1, M2, i; char *ostr; int needflat = 0; #ifdef TCL_NETGEN @@ -3184,6 +3225,8 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) Esrch->hashval = 1; C1 = 1; C2 = 0; + M2 = 0; + M1 = GetNumDevices(Esrch->object); tp1 = LookupCellFile(Esrch->object->model.class, Circuit1->file); tp2 = LookupClassEquivalent(Esrch->object->model.class, Circuit1->file, Circuit2->file); @@ -3195,6 +3238,7 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) if (tp && tp2 && (tp->classhash == tp2->classhash)) { Ecorr->hashval = 1; C2++; + M2 += GetNumDevices(Ecorr->object); } } else if (Ecorr->graph == Circuit1->file) { @@ -3203,6 +3247,7 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) if (tp && tp1 && (tp->classhash == tp1->classhash)) { Ecorr->hashval = 1; C1++; + M1 += GetNumDevices(Ecorr->object); } } } @@ -3219,10 +3264,19 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) for (i = 0; i < left_col_end; i++) *(ostr + i) = ' '; for (i = left_col_end + 1; i < right_col_end; i++) *(ostr + i) = ' '; - snprintf(ostr, left_col_end, "%s (%d)", Esrch->object->model.class, C1); - if (C2 > 0) - snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d)%s", tp2->name, C2, - (C2 == C1) ? "" : " **Mismatch**"); + if (M1 == C1) + snprintf(ostr, left_col_end, "%s (%d)", Esrch->object->model.class, C1); + else + snprintf(ostr, left_col_end, "%s (%d->%d)", Esrch->object->model.class, + M1, C1); + if (C2 > 0) { + if (M2 == C2) + snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d)%s", tp2->name, + C2, (C2 == C1) ? "" : " **Mismatch**"); + else + snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d->%d)%s", + tp2->name, M2, C2, (C2 == C1) ? "" : " **Mismatch**"); + } else { snprintf(ostr + left_col_end + 1, left_col_end, "(no matching element)"); } @@ -3261,6 +3315,7 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) if (Esrch->graph == Circuit2->file && Esrch->hashval == 0) { Esrch->hashval = 1; C2 = 1; + M2 = GetNumDevices(Esrch->object); tp2 = LookupCellFile(Esrch->object->model.class, Circuit2->file); tp1 = LookupClassEquivalent(Esrch->object->model.class, Circuit2->file, Circuit1->file); @@ -3272,6 +3327,7 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) if (tp->classhash == tp2->classhash) { Ecorr->hashval = 1; C2++; + M2 += GetNumDevices(Ecorr->object); } } } @@ -3287,7 +3343,12 @@ int FirstElementPass(struct Element *E, int noflat, int dolist) for (i = 0; i < left_col_end; i++) *(ostr + i) = ' '; for (i = left_col_end + 1; i < right_col_end; i++) *(ostr + i) = ' '; snprintf(ostr, left_col_end, "(no matching element)"); - snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d)", Esrch->object->model.class, C2); + if (C2 == M2) + snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d)", + Esrch->object->model.class, C2); + else + snprintf(ostr + left_col_end + 1, left_col_end, "%s (%d->%d)", + Esrch->object->model.class, M2, C2); for (i = 0; i < right_col_end + 1; i++) if (*(ostr + i) == '\0') *(ostr + i) = ' '; Fprintf(stdout, ostr); } @@ -3810,11 +3871,11 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2, int dolist) modified = CreateLists(name1, file1); if (Elements == NULL) { - Printf("Circuit %s contains no devices.\n", name1); + Printf("\nCircuit %s (%d) contains no devices. Compared as black-box.\n", name1, file1); return; } if (Nodes == NULL) { - Printf("Circuit %s contains no nets.\n", name1); + Printf("\nCircuit %s (%d) contains no nets. Compared as black-box.\n", name1, file1); return; } @@ -3855,13 +3916,13 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2, int dolist) modified += CreateLists(name2, file2); if (Elements == NULL) { - Printf("Circuit %s contains no devices.\n", name2); + Printf("\nCircuit %s (%d) contains no devices. Compared as black-box.\n", name2, file2); ResetState(); return; } if (Nodes == NULL) { - Printf("Circuit %s contains no nets.\n", name2); + Printf("\nCircuit %s (%d) contains no nets. Compared as black-box.\n", name2, file2); ResetState(); return; } @@ -4860,6 +4921,7 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series, // Now combine records with same properties by summing M (S). if (comb == FALSE) { for (i = 0; i < run - 1; i++) { + int nr_empty = 0; for (j = i + 1; j < run; j++) { pmatch = 0; for (p = 1; p < pcount; p++) { @@ -4983,8 +5045,14 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series, vlist[0][i]->value.ival += vlist[0][j]->value.ival; vlist[0][j]->value.ival = 0; } + else + nr_empty++; } } + // If everything from i to the end of the run has been matched + // and zeroed out, then nothing more can be merged. + if (nr_empty == (run - (i + 1))) + break; } } @@ -6113,7 +6181,8 @@ void PrintPropertyResults(int do_list) /*----------------------------------------------------------------------*/ /* Return 0 if perfect matching found, else return number of */ -/* automorphisms, and return -1 if invalid matching found. */ +/* automorphisms, and return -1 if invalid matching found, */ +/* return -2 if port count mismatch. */ /*----------------------------------------------------------------------*/ int VerifyMatching(void) @@ -6124,6 +6193,8 @@ int VerifyMatching(void) struct Element *E; struct Node *N; int C1, C2, result; + int P1, P2; + struct objlist *ob1, *ob2; if (BadMatchDetected) return(-1); @@ -6144,14 +6215,39 @@ int VerifyMatching(void) } } + int portMismatch = 0; + P1 = P2 = 0; for (NC = NodeClasses; NC != NULL; NC = NC->next) { C1 = C2 = 0; for (N = NC->nodes; N != NULL; N = N->next) { (N->graph == Circuit1->file) ? C1++ : C2++; + if (IsPort(N->object)) (N->graph == Circuit1->file) ? P1++ : P2++; } if (C1 != C2) return(-1); + if (P1 != P2) portMismatch = 1; // portCounts should match for each NodeClass if (C1 != 1) ret++; } + + P1 = P2 = 0; + if (ret == 0 ) { // automorphisms have precedence over port count mismatches + if (portMismatch) return(-2); + // Count ports in each circuit including disconnected ports + for (ob1 = Circuit1->cell; ob1 && IsPort(ob1); ob1 = ob1->next, P1++); + for (ob2 = Circuit2->cell; ob2 && IsPort(ob2); ob2 = ob2->next, P2++); + if (P1 == P2) { // pin counts match. Make sure disconnected pins match too. + for (ob1 = Circuit1->cell; ob1 && IsPort(ob1); ob1 = ob1->next) { + if (ob1->node == -1) { // disconnected pin + for (ob2 = Circuit2->cell; ob2 && IsPort(ob2); ob2 = ob2->next) { + if (ob2->node == -1 && strcmp(ob1->name, ob2->name) == 0) break; // disconnected pin match + } + if (ob2 == NULL) return(-2); // disconnected node mismatch + } + } + } else { // pin count mismatch + //Fprintf(stdout, "DEBUG: port mismatch %d %d\n", P1, P2); + return(-2); // Port count mismatch cause flattening + } + } return(ret); } @@ -6250,7 +6346,7 @@ int ResolveAutomorphsByPin() FractureElementClass(&ElementClasses); FractureNodeClass(&NodeClasses); ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() != -1); + while (!Iterate() && VerifyMatching() >= 0); return(VerifyMatching()); } @@ -6353,7 +6449,7 @@ int ResolveAutomorphsByProperty() FractureElementClass(&ElementClasses); FractureNodeClass(&NodeClasses); ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() != -1); + while (!Iterate() && VerifyMatching() >= 0); return(VerifyMatching()); } @@ -6430,7 +6526,7 @@ int ResolveAutomorphisms() FractureElementClass(&ElementClasses); FractureNodeClass(&NodeClasses); ExhaustiveSubdivision = 1; - while (!Iterate() && VerifyMatching() != -1); + while (!Iterate() && VerifyMatching() >= 0); return(VerifyMatching()); } @@ -7142,6 +7238,7 @@ struct nlist *addproxies(struct hashlist *p, void *clientdata) /* circuit pair that has not been matched. If a */ /* circuit pair has been matched with automorphisms, */ /* then some pins may be matched arbitrarily. */ +/* Modified to handle unmatched circuit pairs */ /* */ /* If "dolist" is 1, append the list representing the */ /* output (if any) to variable tcl_out, if it exists. */ @@ -7149,17 +7246,18 @@ struct nlist *addproxies(struct hashlist *p, void *clientdata) /* Return codes: */ /* 2: Neither cell had pins, so matching is unnecessary */ /* 1: Exact match */ -/* 0: Inexact match resolved by proxy pin insertion */ +/* 0: Circuit match, but pin name mismatch. Force match.*/ +/* -1: Pin mismatch. Flatten cell. */ /*------------------------------------------------------*/ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) { - char *cover, *ctemp; + char *cover1, *cover2, *ctemp; char *bangptr1, *bangptr2; struct objlist *ob1, *ob2, *obn, *obp, *ob1s, *ob2s, *obt; struct NodeClass *NC; struct Node *N1, *N2; - int i, j, k, m, a, b, swapped, numnodes, numorig; + int i, j, k, m, a, b, swapped, numnodes, numnodes2, numorig; int result = 1, haspins = 0, notempty = 0; int hasproxy1 = 0, hasproxy2 = 0; int needclean1 = 0, needclean2 = 0; @@ -7171,10 +7269,12 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) if (tc1 == NULL) tc1 = Circuit1; if (tc2 == NULL) tc2 = Circuit2; + numnodes2 = 0; for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) { if (ob2->type != PORT) break; else haspins = 1; ob2->model.port = -1; + numnodes2++; } numnodes = 0; for (ob1 = tc1->cell; ob1 != NULL; ob1 = ob1->next) { @@ -7189,7 +7289,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) return 2; } - cover = (char *)CALLOC(numnodes, sizeof(char)); + cover1 = (char *)CALLOC(numnodes, sizeof(char)); + cover2 = (char *)CALLOC(numnodes2, sizeof(char)); numorig = numnodes; #ifdef TCL_NETGEN @@ -7222,161 +7323,152 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) } for (NC = NodeClasses; NC != NULL; NC = NC->next) { - a = 0; + a = 0; // circuit1 node offset for (N1 = NC->nodes; N1 != NULL; N1 = N1->next) { if (N1->graph == Circuit1->file) { - obn = N1->object; - if (IsPort(obn)) { - i = 0; - for (ob1 = tc1->cell; ob1 != NULL; ob1 = ob1->next, i++) { - if ((IsPort(ob1)) - && (*matchfunc)(ob1->name, obn->name)) { - b = 0; - for (N2 = NC->nodes; N2 != NULL; N2 = N2->next) { - if (N2->graph != Circuit1->file) { - if (b == a) break; - else b++; - } - } - if (N2 == NULL) { -#ifdef TCL_NETGEN - if (dolist) { - Tcl_SetVar2Ex(netgeninterp, "lvs_out", NULL, - Tcl_NewStringObj("pins", -1), - TCL_APPEND_VALUE | TCL_LIST_ELEMENT); - Tcl_SetVar2Ex(netgeninterp, "lvs_out", NULL, mlist, - TCL_APPEND_VALUE | TCL_LIST_ELEMENT); - } -#endif - FREE(ostr); - return 1; - } + obn = N1->object; + b = 0; // circuit2 node offset + for (N2 = NC->nodes; N2 != NULL; N2 = N2->next) { + if (N2->graph != Circuit1->file) { + if (b == a) break; + else b++; + } + } + //Fprintf(stdout, "DEBUG: port match %s - %s\n", obn->name, (N2) ? N2->object->name : ""); - obp = N2->object; - j = 0; - for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next, j++) { - if ((IsPort(ob2)) - && (*matchfunc)(ob2->name, obp->name)) { - if (Debug == 0) { - for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; - for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, left_col_end, "%s", obn->name); - if ((*matchfunc)(obn->name, obp->name)) - snprintf(ostr + left_col_end + 1, left_col_end, "%s", obp->name); - else { - snprintf(ostr + left_col_end + 1, left_col_end, "%s **Mismatch**", obp->name); - /* Pins with different names are on different nets, - * so this should trigger an error return code. - */ - result = 0; - } - for (m = 0; m < right_col_end + 1; m++) - if (*(ostr + m) == '\0') *(ostr + m) = ' '; - Fprintf(stdout, ostr); - } - else { - Fprintf(stdout, "Circuit %s port %d \"%s\"" - " = cell %s port %d \"%s\"\n", - tc1->name, i, obn->name, - tc2->name, j, obp->name); - } + if (N2 == NULL) { // This should never occur in matched nets + continue; + /* #ifdef TCL_NETGEN - if (dolist) { - Tcl_ListObjAppendElement(netgeninterp, plist1, - Tcl_NewStringObj(obn->name, -1)); - Tcl_ListObjAppendElement(netgeninterp, plist2, - Tcl_NewStringObj(obp->name, -1)); - } -#endif - ob2->model.port = i; /* save order */ - *(cover + i) = (char)1; - break; - } - } - if (ob2 == NULL) { - if (Debug == 0) { - // If first cell has no pins but 2nd cell - // does, then "no matching pin" entries will - // be generated for all pins in the 2nd cell, - // so don't print out the "no pins" entry. - - if (strcmp(obn->name, "(no pins)")) { - for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; - for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, 32, "%s", obn->name); - snprintf(ostr + left_col_end + 1, left_col_end, "(no matching pin)"); - for (m = 0; m < right_col_end + 1; m++) - if (*(ostr + m) == '\0') *(ostr + m) = ' '; - Fprintf(stdout, ostr); - } - } - else { - Fprintf(stderr, "No matching pin in cell %s for " - "cell %s pin %s\n", - tc2->name, tc1->name, obn->name); - } -#ifdef TCL_NETGEN - if (dolist && strcmp(obn->name, "(no pins)")) { - Tcl_ListObjAppendElement(netgeninterp, plist1, - Tcl_NewStringObj(obn->name, -1)); - Tcl_ListObjAppendElement(netgeninterp, plist2, - Tcl_NewStringObj("(no matching pin)", -1)); - } + if (dolist) { + Tcl_SetVar2Ex(netgeninterp, "lvs_out", NULL, + Tcl_NewStringObj("pins", -1), + TCL_APPEND_VALUE | TCL_LIST_ELEMENT); + Tcl_SetVar2Ex(netgeninterp, "lvs_out", NULL, mlist, + TCL_APPEND_VALUE | TCL_LIST_ELEMENT); + } #endif - result = 0; - - /* Make a pass through circuit 1 to find out if */ - /* the pin really is connected to anything, or */ - /* has been left orphaned after flattening. If */ - /* disconnected, set its node number to -2. */ - - notempty = 0; - for (obt = ob1->next; obt; obt = obt->next) { - if (obt->type >= FIRSTPIN) { - notempty = 1; - if (obt->node == ob1->node) - break; - } - } - if ((obt == NULL) && (notempty == 1)) { - ob1->node = -2; // Will run this through cleanuppins - needclean1 = 1; - } - } - break; - } - } - - if (ob1 == NULL) { - if (Debug == 0) { - for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; - for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, left_col_end, "%s", obn->name); - snprintf(ostr + left_col_end + 1, left_col_end, "(no matching pin)"); - for (m = 0; m < right_col_end + 1; m++) - if (*(ostr + m) == '\0') *(ostr + m) = ' '; - Fprintf(stdout, ostr); - } - else { - Fprintf(stderr, "No netlist match for cell %s pin %s\n", - tc1->name, obn->name); - } + FREE(ostr); + return 1; + */ + } else { + obp = N2->object; + } + ob1 = NULL; + if (IsPort(obn)) { + i = 0; + for (ob1 = tc1->cell; ob1 != NULL; ob1 = ob1->next, i++) { + if ((IsPort(ob1)) + && (*matchfunc)(ob1->name, obn->name)) { + break; + } + } + //assert(ob1); // there should always be a port if obn is a port + *(cover1 + i) = (char)1; + } + ob2 = NULL; + if (obp && IsPort(obp)) { + j = 0; + for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next, j++) { + if ((IsPort(ob2)) + && (*matchfunc)(ob2->name, obp->name)) { + break; + } + } + //assert(ob2); // there should always be a port if obp is a port + *(cover2 + j) = (char)1; + } + if (Debug == 0) { + for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; + for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; + if (ob1 && ob2) { // pins both in the layout and the source + if (NC->legalpartition) { // net match + snprintf(ostr, left_col_end, "%s", obn->name); + if ((*matchfunc)(obn->name, obp->name)) + // MatchPins - case 1: portA(0) = portA(1) pin names match + snprintf(ostr + left_col_end + 1, left_col_end, "%s", obp->name); + else { + // MatchPins - case 2: portA(0) = portB(1) circuits match but pin names don't + snprintf(ostr + left_col_end + 1, left_col_end, "%s **Mismatch**", obp->name); + /* Pins with different names are on different nets, + * so this should trigger an error return code. + */ + result = (result == 1) ? 0 : result; // only set if 1 + } + ob2->model.port = i; /* save order */ + } else { + snprintf(ostr, left_col_end, "*%s", obn->name); + snprintf(ostr + left_col_end + 1, left_col_end, "*%s **Mismatch**", obp->name); + for (m = 0; m < right_col_end + 1; m++) + if (*(ostr + m) == '\0') *(ostr + m) = ' '; + result = -1; + } + } else if (ob1) { // implies (&& !ob2) + // MatchPins - case 3: portA(0) = netA(1) circuits match but source is missing pin + snprintf(ostr, left_col_end, "%s", obn->name); + if (NC->legalpartition) { // net match + snprintf(ostr + left_col_end + 1, left_col_end, "*%s **not a pin**", obp->name); + } else { + snprintf(ostr + left_col_end + 1, left_col_end, "*%s **Mismatch**", obp->name); + } + result = -1; + } else if (ob2) { // implies (&& !ob1) + // MatchPins - case 4: netA(0) = portA(1) circuits match but layout is missing pin + if (NC->legalpartition) { // net match + snprintf(ostr, left_col_end, "*%s **not pin**", obn->name); + } else { + snprintf(ostr, left_col_end, "*%s **Mismatch**", obn->name); + } + snprintf(ostr + left_col_end + 1, left_col_end, "%s", obp->name); + result = -1; + } + if (ob1 || ob2) { + for (m = 0; m < right_col_end + 1; m++) + if (*(ostr + m) == '\0') *(ostr + m) = ' '; + Fprintf(stdout, ostr); + } + } + else { + Fprintf(stdout, "Circuit %s port %d \"%s\"" + " = cell %s port %d \"%s\"\n", + tc1->name, i, (obn) ? obn->name : "", + tc2->name, j, (obp) ? obp->name : ""); + } #ifdef TCL_NETGEN - if (dolist) { - Tcl_ListObjAppendElement(netgeninterp, plist1, - Tcl_NewStringObj(obn->name, -1)); - Tcl_ListObjAppendElement(netgeninterp, plist2, - Tcl_NewStringObj("(no matching pin)", -1)); - } + if (dolist) { + Tcl_ListObjAppendElement(netgeninterp, plist1, + Tcl_NewStringObj((obn) ? obn->name : "", -1)); + Tcl_ListObjAppendElement(netgeninterp, plist2, + Tcl_NewStringObj((obp) ? obp->name : "", -1)); + } #endif - result = 0; - } - } a++; } } } + // Check for unconnected nodes in tc1 + for (ob1 = tc1->cell; ob1 && IsPort(ob1); ob1 = ob1->next ) { + if (ob1->node >= 0) { + /* Check if ob1->node might really be disconnected */ + for (obn = ob1->next; obn; obn = obn->next) { + if (obn->node == ob1->node) break; + } + if (obn == NULL) ob1->node = -1; /* Make disconnected */ + } + } + + // Check for unconnected nodes in tc2 + for (ob2 = tc2->cell; ob2 && IsPort(ob2); ob2 = ob2->next ) { + if (ob2->node >= 0) { + /* Check if ob2->node might really be disconnected */ + for (obp = ob2->next; obp; obp = obp->next) { + if (obp->node == ob2->node) break; + } + if (obp == NULL) ob2->node = -1; /* Make disconnected */ + } + } + /* Do any unmatched pins have the same name? */ /* This should not happen if unconnected pins are eliminated */ /* so apply only to black-box (CELL_PLACEHOLDER) entries. */ @@ -7388,13 +7480,16 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) *bangptr1 = '\0'; else bangptr1 = NULL; + int isBlackBox1 = (tc1->flags & CELL_PLACEHOLDER); + int isBlackBox2 = (tc2->flags & CELL_PLACEHOLDER); for (i = 0; i < numorig; i++) { - if (*(cover + i) == (char)0) { + if (*(cover1 + i) == (char)0) { j = 0; - for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) { + for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next, j++) { char *name1, *name2; if (!IsPort(ob2)) break; + if (*(cover2 + j) == (char)1) continue; // this port was processed bangptr2 = strrchr(ob2->name, '!'); if (bangptr2 && (*(bangptr2 + 1) == '\0')) @@ -7409,6 +7504,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) if (!strncmp(name2, "proxy", 5) && (ob1->node == -1)) name2 +=5; if ((*matchfunc)(name1, name2)) { + //Fprintf(stdout, "DEBUG: disconnected port match %s %d(%d) - %s %d(%d)\n", name1, i, ob1->node, name2, j, ob2->node); /* If both sides have unconnected nodes, then pins with */ /* matching names are an automatic match. Otherwise, if */ @@ -7416,18 +7512,20 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) /* matched by name. */ if (((ob1->node == -1) && (ob2->node == -1)) || - (((tc1->flags & CELL_PLACEHOLDER) && - (tc2->flags & CELL_PLACEHOLDER)) || - (NodeClasses == NULL))) { + (isBlackBox1 && isBlackBox2) || + (NodeClasses == NULL)) { ob2->model.port = i; /* save order */ - *(cover + i) = (char)1; + *(cover1 + i) = (char)1; + *(cover2 + j) = (char)1; if (Debug == 0) { for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, left_col_end, "%s", ob1->name); - snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name); + snprintf(ostr, left_col_end, + "%s %s", ob1->name, (ob1->node == -1 && ! isBlackBox1) ? "(disconnected)" : ""); + snprintf(ostr + left_col_end + 1, left_col_end, + "%s %s", ob2->name, (ob2->node == -1 && ! isBlackBox2) ? "(disconnected)" : ""); for (m = 0; m < right_col_end + 1; m++) if (*(ostr + m) == '\0') *(ostr + m) = ' '; Fprintf(stdout, ostr); @@ -7451,7 +7549,6 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) } } if (bangptr2) *bangptr2 = '!'; - j++; } } ob1 = ob1->next; @@ -7470,8 +7567,9 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) /* connections in the cell to the end. Create pins */ /* in tc1 to match. */ - for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) { + for (j = 0, ob2 = tc2->cell; ob2 != NULL; j++, ob2 = ob2->next) { if (ob2->type != PORT) break; + if (*(cover2 + j) == (char)1) continue; // this port was processed if (ob2->model.port == -1) { if (Debug == 0) { @@ -7479,8 +7577,8 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) if (strcmp(ob2->name, "(no pins)")) { for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, left_col_end, "(no matching pin)"); - snprintf(ostr + left_col_end + 1, left_col_end, "%s", ob2->name); + snprintf(ostr, left_col_end, "**no match**"); + snprintf(ostr + left_col_end + 1, left_col_end, "%s %s", ob2->name, (ob2->node == -1) ? "(disconnected)" : ""); for (m = 0; m < right_col_end + 1; m++) if (*(ostr + m) == '\0') *(ostr + m) = ' '; Fprintf(stdout, ostr); @@ -7490,77 +7588,9 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) Fprintf(stderr, "No netlist match for cell %s pin %s\n", tc2->name, ob2->name); } - - /* Before making a proxy pin, check to see if */ - /* flattening instances has left a port with a */ - /* net number that doesn't connect to anything */ - - notempty = 0; - for (obt = ob2->next; obt; obt = obt->next) { - if (obt->type >= FIRSTPIN) { - notempty = 1; - if (obt->node == ob2->node) - break; - } - } - if ((obt == NULL) && (notempty == 1)) { - ob2->node = -2; // Will run this through cleanuppins - needclean2 = 1; - - /* On the top level, missing pins are an error, even if */ - /* they appear to match unconnected pins on the other side. */ - if (CompareQueue == NULL) - result = 0; - -#ifdef TCL_NETGEN - if (dolist) { - Tcl_ListObjAppendElement(netgeninterp, plist1, - Tcl_NewStringObj("(no pin)", -1)); - Tcl_ListObjAppendElement(netgeninterp, plist2, - Tcl_NewStringObj(ob2->name, -1)); - } -#endif - continue; - } - else if (notempty == 1) { - /* Flag this as an error */ - result = 0; -#ifdef TCL_NETGEN - if (dolist) { - Tcl_ListObjAppendElement(netgeninterp, plist1, - Tcl_NewStringObj("(no matching pin)", -1)); - Tcl_ListObjAppendElement(netgeninterp, plist2, - Tcl_NewStringObj(ob2->name, -1)); - } -#endif - } ob2->model.port = numnodes++; // Assign a port order + result = -1; - /* Add a proxy pin to tc1 */ - /* Technically, this should have a matching net number. */ - /* But nothing connects to it, so it is only needed to */ - /* make sure the "pin magic" numbers are correctly */ - /* assigned to both cells. */ - - obn = (struct objlist *)CALLOC(1, sizeof(struct objlist)); - obn->name = (char *)MALLOC(6 + strlen(ob2->name)); - sprintf(obn->name, "proxy%s", ob2->name); - obn->type = UNKNOWN; - // obn->model.port = -1; - obn->instance.name = NULL; - obn->node = -1; - if (ob1 == tc1->cell) { - obn->next = ob1; - tc1->cell = obn; - } - else { - obn->next = ob1->next; - ob1->next = obn; - } - ob1 = obn; - hasproxy1 = 1; - - HashPtrInstall(obn->name, obn, &(tc1->objdict)); } } @@ -7577,97 +7607,65 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) /* sequence, then fill in the missing numbers. Otherwise, add the */ /* extra nodes to the end. */ - j = 0; - for (i = 0; i < numorig; i++) { - if (*(cover + i) == (char)1) continue; + for (i = 0, ob1 = tc1->cell; i < numorig; i++, ob1 = ob1->next) { + if (*(cover1 + i) == (char)1) continue; /* If the equivalent node in tc1 is not disconnected */ /* (node != -1) then we should report a match error, */ /* although this does not necessarily imply an error in */ /* netlist connectivity. */ +/* ob1 = tc1->cell; for (k = i; k > 0 && ob1 != NULL; k--) ob1 = ob1->next; + */ - if (ob1 == NULL || ob1->type != PORT || ob1->node >= 0) { - /* Check if ob1->node might really be disconnected */ - for (obn = ob1->next; obn; obn = obn->next) { - if (obn->node == ob1->node) break; - } - if (obn == NULL) ob1->node = -1; /* Make disconnected */ - } - - if (ob1 == NULL || ob1->type != PORT || ob1->node >= 0 - || (ob1->node < 0 && tc1->class == CLASS_MODULE) - || (ob1->node < 0 && ob1->model.port == -1)) { - - /* Add a proxy pin to tc2 */ - obn = (struct objlist *)CALLOC(1, sizeof(struct objlist)); - if (ob1 == NULL) { - obn->name = (char *)MALLOC(15); - sprintf(obn->name, "proxy%d", rand() & 0x3ffffff); - } - else { - obn->name = (char *)MALLOC(6 + strlen(ob1->name)); - sprintf(obn->name, "proxy%s", ob1->name); - } - obn->type = UNKNOWN; - obn->model.port = (i - j); - obn->instance.name = NULL; - obn->node = -1; - - /* Note: Has this pin already been accounted for? */ - if (Debug == 0) { - if (strcmp(ob1->name, "(no pins)")) { - for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; - for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; - snprintf(ostr, left_col_end, "%s", ob1->name); - snprintf(ostr + left_col_end + 1, left_col_end, "(no matching pin)"); - for (m = 0; m < right_col_end + 1; m++) - if (*(ostr + m) == '\0') *(ostr + m) = ' '; - Fprintf(stdout, ostr); - } - } - else { - Fprintf(stderr, "No netlist match for cell %s pin %s\n", - tc1->name, ob1->name); - } - - if (ob2 == tc2->cell) { - obn->next = ob2; - tc2->cell = obn; - } - else { - obn->next = ob2->next; - ob2->next = obn; - } - ob2 = obn; - hasproxy2 = 1; - - HashPtrInstall(obn->name, obn, &(tc2->objdict)); + if (Debug == 0) { + if (strcmp(ob1->name, "(no pins)")) { + for (m = 0; m < left_col_end; m++) *(ostr + m) = ' '; + for (m = left_col_end + 1; m < right_col_end; m++) *(ostr + m) = ' '; + snprintf(ostr, left_col_end, "%s %s", ob1->name, (ob1->node == -1) ? "(disconnected)" : ""); + snprintf(ostr + left_col_end + 1, left_col_end, "**no match**"); + for (m = 0; m < right_col_end + 1; m++) + if (*(ostr + m) == '\0') *(ostr + m) = ' '; + Fprintf(stdout, ostr); + } + result = -1; } - - else if (ob1 != NULL && ob1->type == PORT) { - /* Disconnected node was not meaningful, has no pin match in */ - /* the compared circuit, and so should be discarded. */ - ob1->node = -2; - needclean1 = 1; - - /* Adjust numbering around removed node */ - for (ob2s = tc2->cell; ob2s != NULL && ob2s->type == PORT; ob2s = ob2s->next) { - if (ob2s->model.port > (i - j)) ob2s->model.port--; - } - j++; + else { + Fprintf(stderr, "No netlist match for cell %s pin %s\n", + tc1->name, ob1->name); } } - FREE(cover); + FREE(cover1); + FREE(cover2); if (Debug == 0) { for (i = 0; i < right_col_end; i++) *(ostr + i) = '-'; Fprintf(stdout, ostr); } + int hasDevices1 = 0; + int hasDevices2 = 0; + for (ob1 = tc1->cell; ob1 != NULL; ob1 = ob1->next) { + if (ob1->type == FIRSTPIN) { + hasDevices1 = 1; + break; + } + } + for (ob2 = tc2->cell; ob2 != NULL; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + hasDevices2 = 1; + break; + } + } + if (hasDevices1 != hasDevices2) + result = -2; // Attempt to compare empty cell to non-empty cell + //Fprintf(stdout, "DEBUG: %s device check %d %d\n", tc1->name, hasDevices1, hasDevices2); + + if (result < 0) return result; + /* Run cleanuppins on circuit 1 */ if (needclean1) { CleanupPins(tc1->name, tc1->file); @@ -7707,7 +7705,7 @@ int MatchPins(struct nlist *tc1, struct nlist *tc2, int dolist) } /* Check for ports that did not get ordered */ - for (obn = tc2->cell; obn && (obn->type == PORT); obn = obn->next) { + for (obn = tc2->cell; obn && IsPort(obn); obn = obn->next) { if (obn->model.port == -1) { if (obn->node == -1) { // This only happens when pins have become separated from any net. @@ -7885,10 +7883,10 @@ int EquivalentElement(char *name, struct nlist *circuit, struct objlist **retobj void FlattenCurrent() { if (Circuit1 != NULL && Circuit2 != NULL) { - Fprintf(stdout, "Flattening subcell %s\n", Circuit1->name); + Fprintf(stdout, "Flattening subcell %s (%d)\n", Circuit1->name, Circuit1->file); FlattenInstancesOf(Circuit1->name, Circuit1->file); - Fprintf(stdout, "Flattening subcell %s\n", Circuit2->name); + Fprintf(stdout, "Flattening subcell %s (%d)\n", Circuit2->name, Circuit2->file); FlattenInstancesOf(Circuit2->name, Circuit2->file); } } @@ -7959,6 +7957,35 @@ int Compare(char *cell1, char *cell2) PrintIllegalClasses(); return(0); } + if (automorphisms > 0) { + Fprintf(stdout, "Circuits match with %d automorphisms.\n", automorphisms); + if (VerboseOutput) PrintAutomorphisms(); + + /* arbitrarily resolve automorphisms */ + Fprintf(stdout, "\n"); + Fprintf(stdout, "Resolving automorphisms by arbitrary symmetry breaking:\n"); + while ((automorphisms = ResolveAutomorphisms()) > 0) ; + if (automorphisms == -1) { + MatchFail(cell1, cell2); + Fprintf(stdout, "Circuits do not match.\n"); + return(0); + } + } + if (automorphisms == -2) { // port count mismatch + Fprintf(stderr, "Port counts do not match(5).\n"); + //PrintPins(cell1, cell2, 0); + return(0); + } + if (PropertyErrorDetected == 1) { + Fprintf(stdout, "There were property errors.\n"); + PrintPropertyResults(0); + } + else if (PropertyErrorDetected == -1) { + Fprintf(stdout, "There were missing properties.\n"); + PrintPropertyResults(0); + } + else Fprintf(stdout, "Circuits match correctly.\n"); + /* if (automorphisms == 0) Fprintf(stdout, "Circuits match correctly.\n"); if (PropertyErrorDetected == 1) { Fprintf(stdout, "There were property errors.\n"); @@ -7983,6 +8010,7 @@ int Compare(char *cell1, char *cell2) return(0); } Fprintf(stdout, "Circuits match correctly.\n"); + */ return(1); } @@ -8037,6 +8065,10 @@ void NETCOMP(void) PrintIllegalClasses(); Fprintf(stdout, "Netlists do not match.\n"); } + else if (automorphisms == -2) { // port count mismatch + Fprintf(stdout, "Port counts do not match(1).\n"); + //PrintPins(Circuit1, Circuit2, 0); + } else { if (automorphisms) Printf("Circuits match with %d automorphisms.\n", automorphisms); @@ -8052,11 +8084,13 @@ void NETCOMP(void) while (!Iterate()) ; automorphisms = VerifyMatching(); if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); + else (automorphisms == -2) Fprintf(stdout, "Port counts do not match(2).\n"); else { Printf("Netlists match with %d automorphisms.\n", automorphisms); while ((automorphisms = ResolveAutomorphisms()) > 0) Printf(" automorphisms = %d.\n", automorphisms); if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); + else if (automorphisms == -2) Fprintf(stdout, "Port counts do not match(3).\n"); else Printf("Circuits match correctly.\n"); } } diff --git a/base/netgen.c b/base/netgen.c index dc15c7e..8b9d504 100644 --- a/base/netgen.c +++ b/base/netgen.c @@ -3337,16 +3337,13 @@ int CombineParallel(char *model, int file) /* "sob" to it. If "ob" does not have properties, then */ /* create a property record and set property "M" to 1. */ - /* Find last non-property record of sob ( = pob) */ - /* Find first property record of sob ( = spropfirst) */ - /* Find last property record of sob ( = sproplast) */ + /* Find last non-property record of sob ( = pob) */ + /* Find first property record of sob ( = spropfirst) */ - spropfirst = sproplast = NULL; + spropfirst = NULL; for (ob2 = sob; ob2->type > FIRSTPIN || ob2 == sob; ob2 = ob2->next) pob = ob2; if (ob2->type == PROPERTY) spropfirst = ob2; - for (; ob2->type == PROPERTY; ob2 = ob2->next) - sproplast = ob2; if (spropfirst == NULL) { /* Create new property instance record if one doesn't exist */ @@ -3372,8 +3369,9 @@ int CombineParallel(char *model, int file) nob->next = pob->next; pob->next = nob; + /* Handle case of contiguous entries */ if (lob == pob) lob = nob; - spropfirst = sproplast = nob; + spropfirst = nob; } if (propfirst == NULL) { /* Create new property instance record if one doesn't exist */ @@ -3396,27 +3394,26 @@ int CombineParallel(char *model, int file) kv->type = PROP_ENDLIST; kv->value.ival = 0; - /* Append to sob's property list */ - nob->next = sproplast->next; - sproplast->next = nob; - if (lob == sproplast) lob = nob; + /* Prepend to sob's property list */ + nob->next = pob->next; + pob->next = nob; + /* Handle case of contiguous entries */ + if (lob == pob) lob = nob; } - - if (propfirst != NULL) { + else { // Series/Parallel logic: - // If propfirst has _tag in properties, - // then add an "open" tag at propfirst - add_prop_tag(propfirst, '('); - - // if spropfirst has _tag in properties then add an "open" tag - // to spropfirst and a "close" tag to propfirst - if (add_prop_tag(spropfirst, '(')) add_prop_tag(propfirst, ')'); - - /* Append ob's property list to sob */ - proplast->next = sproplast->next; - sproplast->next = propfirst; - if (lob == sproplast) lob = proplast; + // If spropfirst has _tag in properties, + // then add an "open" tag at spropfirst + add_prop_tag(spropfirst, '('); + + // if propfirst has _tag in properties then add an "open" tag + // to propfirst and a "close" tag to spropfirst + if (add_prop_tag(propfirst, '(')) add_prop_tag(spropfirst, ')'); + + /* Prepend ob's property list to sob */ + proplast->next = pob->next; + pob->next = propfirst; } /* Link up around object to be removed */ @@ -3429,7 +3426,6 @@ int CombineParallel(char *model, int file) obr = nob; } dcnt++; - } FREE((char *)pstr); } @@ -3441,7 +3437,8 @@ int CombineParallel(char *model, int file) } HashKill(&devdict); if (dcnt > 0) { - Fprintf(stdout, "Class %s(%d): Merged %d parallel devices.\n", model, file, dcnt); + Fprintf(stdout, "Class %s (%d): Merged %d parallel devices.\n", + model, file, dcnt); } FREE(nodecount); return dcnt; @@ -3739,7 +3736,8 @@ int CombineSeries(char *model, int file) } FREE(instlist); if (scnt > 0) { - Fprintf(stdout, "Class %s(%d): Merged %d series devices.\n", model, file, scnt); + Fprintf(stdout, "Class %s (%d): Merged %d series devices.\n", + model, file, scnt); } return scnt; } diff --git a/base/verilog.c b/base/verilog.c index d27ea6c..8c3cfe7 100644 --- a/base/verilog.c +++ b/base/verilog.c @@ -2203,7 +2203,7 @@ char *ReadVerilogTop(char *fname, int *fnum, int blackbox) /* be case insensitive, with a stern warning. */ if (matchfunc == matchnocase) { - Printf("Warning: A case-insensitive file has been read and so the " + Printf("Warning: A case-insensitive file has been read and so the " "verilog file must be treated case-insensitive to match.\n"); } else { diff --git a/python/lvs_manager.py b/python/lvs_manager.py deleted file mode 100644 index cd8559a..0000000 --- a/python/lvs_manager.py +++ /dev/null @@ -1,891 +0,0 @@ -#!/bin/env python3 -# -#-------------------------------------------------------- -# LVS Manager GUI. -# -# This is a Python tkinter script that handles the -# process of running LVS and interpreting results. -# -#-------------------------------------------------------- -# Written by Tim Edwards -# efabless, inc. -# Version 1. November 30, 2016 -# Version 2. March 6, 2017. Reads JSON format output -# Version 3. April 25, 2018. Handles layout vs. verilog -#-------------------------------------------------------- - -import io -import os -import re -import sys -import json -import shutil -import signal -import socket -import select -import datetime -import contextlib -import subprocess - -import tkinter -from tkinter import ttk -from tkinter import filedialog - -import tksimpledialog -import tooltip -from consoletext import ConsoleText -from helpwindow import HelpWindow -from treeviewsplit import TreeViewSplit - -# User preferences file (if it exists) -prefsfile = '~/.profile/prefs.json' - -netgen_script_dir = '/usr/local/lib/netgen/python' - -#------------------------------------------------------ -# Simple dialog for confirming quit -#------------------------------------------------------ - -class ConfirmDialog(tksimpledialog.Dialog): - def body(self, master, warning, seed): - if warning: - ttk.Label(master, text=warning, wraplength=500).grid(row = 0, columnspan = 2, sticky = 'wns') - return self - - def apply(self): - return 'okay' - -#------------------------------------------------------ -# Main class for this application -#------------------------------------------------------ - -class LVSManager(ttk.Frame): - """LVS Manager GUI.""" - - def __init__(self, parent, *args, **kwargs): - ttk.Frame.__init__(self, parent, *args, **kwargs) - self.root = parent - self.init_gui() - parent.protocol("WM_DELETE_WINDOW", self.on_quit) - - def on_quit(self): - """Exits program.""" - if self.msock: - self.msock.close() - quit() - - def init_gui(self): - """Builds GUI.""" - global prefsfile - - message = [] - fontsize = 11 - - # Read user preferences file, get default font size from it. - prefspath = os.path.expanduser(prefsfile) - if os.path.exists(prefspath): - with open(prefspath, 'r') as f: - self.prefs = json.load(f) - if 'fontsize' in self.prefs: - fontsize = self.prefs['fontsize'] - else: - self.prefs = {} - - s = ttk.Style() - - available_themes = s.theme_names() - s.theme_use(available_themes[0]) - - s.configure('bg.TFrame', background='gray40') - s.configure('italic.TLabel', font=('Helvetica', fontsize, 'italic')) - s.configure('title.TLabel', font=('Helvetica', fontsize, 'bold italic'), - foreground = 'brown', anchor = 'center') - s.configure('normal.TLabel', font=('Helvetica', fontsize)) - s.configure('red.TLabel', font=('Helvetica', fontsize), foreground = 'red') - s.configure('green.TLabel', font=('Helvetica', fontsize), foreground = 'green3') - s.configure('blue.TLabel', font=('Helvetica', fontsize), foreground = 'blue') - s.configure('normal.TButton', font=('Helvetica', fontsize), - border = 3, relief = 'raised') - s.configure('red.TButton', font=('Helvetica', fontsize), foreground = 'red', - border = 3, relief = 'raised') - s.configure('green.TButton', font=('Helvetica', fontsize), foreground = 'green3', - border = 3, relief = 'raised') - s.configure('blue.TButton', font=('Helvetica', fontsize), foreground = 'blue', - border = 3, relief = 'raised') - s.configure('redtitle.TButton', font=('Helvetica', fontsize, 'bold italic'), - foreground = 'red', border = 3, relief = 'raised') - s.configure('bluetitle.TButton', font=('Helvetica', fontsize, 'bold italic'), - foreground = 'blue', border = 3, relief = 'raised') - - # These values to be overridden from arguments - self.rootpath = None - self.project = None - self.logfile = None - self.msock = None - self.help = None - - # Create the help window - if os.path.exists(netgen_script_dir + '/netgen_help.txt'): - self.help = HelpWindow(self, fontsize = fontsize) - with io.StringIO() as buf, contextlib.redirect_stdout(buf): - self.help.add_pages_from_file('lvs_help.txt') - message = buf.getvalue() - - # Set the help display to the first page - self.help.page(0) - - # Variables used by option menus and other stuff - self.project = "(no selection)" - self.layout = "(default)" - self.schematic = "(default)" - self.tech = "(none)" - self.lvs_setup = '' - self.lvsdata = {} - - # Root window title - self.root.title('LVS Manager') - self.root.option_add('*tearOff', 'FALSE') - self.pack(side = 'top', fill = 'both', expand = 'true') - - pane = tkinter.PanedWindow(self, orient = 'vertical', sashrelief='groove', sashwidth=6) - pane.pack(side = 'top', fill = 'both', expand = 'true') - self.toppane = ttk.Frame(pane) - self.botpane = ttk.Frame(pane) - - # Get username - if 'username' in self.prefs: - username = self.prefs['username'] - else: - username = os.environ['USER'] - - # Label with the user - self.toppane.title_frame = ttk.Frame(self.toppane) - self.toppane.title_frame.pack(side = 'top', fill = 'x') - - self.toppane.title_frame.title = ttk.Label(self.toppane.title_frame, text='User:', style = 'red.TLabel') - self.toppane.title_frame.user = ttk.Label(self.toppane.title_frame, text=username, style = 'blue.TLabel') - - self.toppane.title_frame.title.grid(column=0, row=0, ipadx = 5) - self.toppane.title_frame.user.grid(column=1, row=0, ipadx = 5) - - self.toppane.title2_frame = ttk.Frame(self.toppane) - self.toppane.title2_frame.pack(side = 'top', fill = 'x') - self.toppane.title2_frame.project_label = ttk.Label(self.toppane.title2_frame, text="Project:", - style = 'title.TLabel') - self.toppane.title2_frame.project_label.grid(column=0, row=0, ipadx = 5) - - # New project select button - self.toppane.title2_frame.project_select = ttk.Button(self.toppane.title2_frame, - text=self.project, style='normal.TButton', command=self.choose_project) - self.toppane.title2_frame.project_select.grid(column=1, row=0, ipadx = 5) - - tooltip.ToolTip(self.toppane.title2_frame.project_select, - text = "Select new project") - - # Show path to project - self.toppane.title2_frame.path_label = ttk.Label(self.toppane.title2_frame, text=self.project, - style = 'normal.TLabel') - self.toppane.title2_frame.path_label.grid(column=2, row=0, ipadx = 5, padx = 10) - - # Show top-level layout cellname with select button. Initial cell name is the top-level cell. - self.toppane.title2_frame.tech_label = ttk.Label(self.toppane.title2_frame, text="Technology setup:", - style = 'title.TLabel') - - self.toppane.title2_frame.tech_label.grid(column=3, row=0, ipadx = 5) - self.toppane.title2_frame.tech_select = ttk.Button(self.toppane.title2_frame, - text=self.tech, style='normal.TButton', command=self.choose_tech) - self.toppane.title2_frame.tech_select.grid(column=4, row=0, ipadx = 3, padx = 3) - - self.toppane.title2_frame.layout_label = ttk.Label(self.toppane.title2_frame, text="Layout:", - style = 'title.TLabel') - self.toppane.title2_frame.layout_label.grid(column=0, row=1, ipadx = 5) - self.toppane.title2_frame.layout_select = ttk.Button(self.toppane.title2_frame, - text=self.layout, style='normal.TButton', command=self.choose_layout) - self.toppane.title2_frame.layout_select.grid(column=1, row=1, ipadx = 3, padx = 3) - - # Show top-level schematic cellname with select button. Initial cell name is the top-level cell. - self.toppane.title2_frame.schem_label = ttk.Label(self.toppane.title2_frame, text="Schematic:", - style = 'title.TLabel') - self.toppane.title2_frame.schem_label.grid(column=3, row=1, ipadx = 5) - self.toppane.title2_frame.schem_select = ttk.Button(self.toppane.title2_frame, - text=self.schematic, style='normal.TButton', command=self.choose_netlist) - self.toppane.title2_frame.schem_select.grid(column=4, row=1, ipadx = 3, padx = 3) - - tooltip.ToolTip(self.toppane.title2_frame.project_select, - text = "Select new project") - tooltip.ToolTip(self.toppane.title2_frame.layout_select, - text = "Select a layout subcirucit to compare") - tooltip.ToolTip(self.toppane.title2_frame.schem_select, - text = "Select a schematic subcirucit to compare") - - #--------------------------------------------- - ttk.Separator(self.toppane, orient='horizontal').pack(side = 'top', fill = 'x') - #--------------------------------------------- - - # Create listbox of Circuit1 vs. Circuit2 results - height = 10 - self.toppane.lvsreport = TreeViewSplit(self.toppane, fontsize = fontsize) - self.toppane.lvsreport.populate("Layout:", [], "Schematic:", [], - [["Run", True, self.run_lvs], - # ["Find", True, self.findrecord] - ], height = height) - self.toppane.lvsreport.set_title("Line") - self.toppane.lvsreport.pack(side = 'top', fill = 'both', expand = 'true') - - tooltip.ToolTip(self.toppane.lvsreport.get_button(0), text="Run LVS") - - #--------------------------------------------- - # ttk.Separator(self, orient='horizontal').grid(column=0, row=3, sticky='ew') - #--------------------------------------------- - - # Add a text window below the project name to capture output. Redirect - # print statements to it. - - self.botpane.console = ttk.Frame(self.botpane) - self.botpane.console.pack(side = 'top', fill = 'both', expand = 'true') - - self.text_box = ConsoleText(self.botpane.console, wrap='word', height = 4) - self.text_box.pack(side='left', fill='both', expand='true') - console_scrollbar = ttk.Scrollbar(self.botpane.console) - console_scrollbar.pack(side='right', fill='y') - # attach console to scrollbar - self.text_box.config(yscrollcommand = console_scrollbar.set) - console_scrollbar.config(command = self.text_box.yview) - - # Add button bar at the bottom of the window - self.botpane.bbar = ttk.Frame(self.botpane) - self.botpane.bbar.pack(side = 'top', fill = 'x') - - # Define the "quit" button and action - self.botpane.bbar.quit_button = ttk.Button(self.botpane.bbar, text='Quit', command=self.on_quit, - style = 'normal.TButton') - self.botpane.bbar.quit_button.grid(column=0, row=0, padx = 5) - - # Define help button - if self.help: - self.botpane.bbar.help_button = ttk.Button(self.botpane.bbar, text='Help', - command=self.help.open, style = 'normal.TButton') - self.botpane.bbar.help_button.grid(column = 2, row = 0, padx = 5) - tooltip.ToolTip(self.botpane.bbar.help_button, text = "Show help window") - - # Add the panes once the internal geometry is known. - pane.add(self.toppane) - pane.add(self.botpane) - pane.paneconfig(self.toppane, stretch='first') - - # Redirect stdout and stderr to the console as the last thing to do. . . - # Otherwise errors in the GUI get sucked into the void. - - self.stdout = sys.stdout - self.stderr = sys.stderr - sys.stdout = ConsoleText.StdoutRedirector(self.text_box) - sys.stderr = ConsoleText.StderrRedirector(self.text_box) - - if message: - print(message) - - def logprint(self, message, doflush=False): - if self.logfile: - self.logfile.buffer.write(message.encode('utf-8')) - self.logfile.buffer.write('\n'.encode('utf-8')) - if doflush: - self.logfile.flush() - - def printout(self, output): - # Generate output - if not output: - return - - outlines = output.splitlines() - for line in outlines: - try: - print(line) - except TypeError: - line = line.decode('utf-8') - pritn(line) - - def printwarn(self, output): - # Check output for warning or error - if not output: - return 0 - - warnrex = re.compile('.*warning', re.IGNORECASE) - errrex = re.compile('.*error', re.IGNORECASE) - - errors = 0 - outlines = output.splitlines() - for line in outlines: - try: - wmatch = warnrex.match(line) - except TypeError: - line = line.decode('utf-8') - wmatch = warnrex.match(line) - ematch = errrex.match(line) - if ematch: - errors += 1 - if ematch or wmatch: - print(line) - return errors - - def choose_tech(self): - try: - project_path = self.rootpath - initdirname = self.rootpath + '/tech', - except: - print('Must choose a project first.') - return - techname = filedialog.askopenfilename(multiple=False, - initialdir = initdirname, - filetypes = (("Tcl script", "*.tcl"),("All Files","*.*")), - title = "Choose a netgen technology setup script.") - if techname != '': - print("Selected technology setup script " + techname) - techbase = os.path.split(techname)[1] - self.tech = os.path.splitext(techbase)[0] - self.lvs_setup = techname - self.toppane.title2_frame.tech_select.config(text = self.tech) - - def choose_layout(self): - try: - project_path = self.rootpath - initdirname = self.rootpath + '/layout', - except: - print('Must choose a project first.') - return - cellname = filedialog.askopenfilename(multiple=False, - initialdir = initdirname, - filetypes = (("Magic layout", "*.mag"),("All Files","*.*")), - title = "Choose a layout cell to compare.") - if cellname != '': - print("Selected compare cell " + cellname) - self.layout = cellname - cellbase = os.path.split(cellname)[1] - layoutname = os.path.splitext(cellbase)[0] - self.toppane.title2_frame.layout_select.config(text = layoutname) - fileext = os.path.splitext(cellbase)[1] - if fileext == '.mag': - self.toppane.title2_frame.layout_label.config(text = 'Layout:') - else: - self.toppane.title2_frame.layout_label.config(text = 'Layout netlist:') - - def choose_netlist(self): - try: - project_path = self.rootpath - initdirname = self.rootpath + '/netlist/' + self.project + '.spice' - except: - print('Must choose a project first.') - return - cellname = filedialog.askopenfilename(multiple=False, - initialdir = initdirname, - filetypes = (("Spice netlist", "*.spice"),("Verilog netlist", "*.v"),("All Files","*.*")), - title = "Choose a netlist to compare.") - if cellname != '': - print("Selected compare cell " + cellname) - self.schematic = cellname - cellbase = os.path.split(cellname)[1] - schematic_name = os.path.splitext(cellbase)[0] - self.toppane.title2_frame.schem_select.config(text = schematic_name) - fileext = os.path.splitext(cellbase)[1] - if fileext == '.v': - self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') - elif fileext == '.sp' or fileext == '.spice' or fileext == '.spi' or fileext == '.spc' or fileext == '.ckt': - self.toppane.title2_frame.schem_label.config(text = 'SPICE netlist:') - elif fileext == '.cdl': - self.toppane.title2_frame.schem_label.config(text = 'CDL netlist:') - else: - self.toppane.title2_frame.schem_label.config(text = 'Unknown netlist:') - - def choose_project(self): - project = filedialog.askdirectory(initialdir = os.getcwd(), - title = "Find a project.") - if project != '': - print("Selected project " + str(project)) - result = self.set_project(project) - - def set_project(self, rootpath, project_name=None): - - # Check if rootpath is valid. For LVS, there should be subdirectories - # "layout/" and "netlist/" or "verilog/". - - haslay = os.path.isdir(rootpath + '/layout') - hasvlog = os.path.isdir(rootpath + '/verilog') - hasnet = os.path.isdir(rootpath + '/netlist') - if not haslay or not (hasvlog or hasnet): - if not haslay: - print("Project path has no layout (/layout) subdirectory.") - if not (hasvlog or hasnet): - print("Project path has no verilog (/verilog), or netlist (/netlist) subdirectory.") - # Continue anyway; assume that netlists will be selected manually - - if self.logfile: - self.logfile.close() - self.logfile = None - - if not project_name: - project = os.path.split(rootpath)[1] - else: - project = project_name - - if self.project != project: - - self.rootpath = rootpath - self.project = project - - # Clear out old project data - self.toppane.lvsreport.repopulate([], []) - - # Close any open logfile. - if self.logfile: - self.logfile.close() - self.logfile = None - - # Put new log file called 'lvs.log' in the mag/ subdirectory - if os.path.exists(rootpath + '/layout'): - self.logfile = open(rootpath + '/layout/lvs.log', 'w') - else: - self.logfile = open(rootpath + '/lvs.log', 'w') - # Print some initial information to the logfile. - self.logprint('Starting new log file ' + datetime.datetime.now().strftime('%c'), - doflush=True) - - # Update project button - self.toppane.title2_frame.project_select.config(text = self.project) - self.toppane.title2_frame.path_label.config(text = self.rootpath) - # Cell name is the same as project name initially - self.layout = self.project - self.schematic = self.project - layname = os.path.splitext(os.path.split(self.layout)[1])[0] - self.toppane.title2_frame.layout_select.config(text = layname) - schemname = os.path.splitext(os.path.split(self.schematic)[1])[0] - self.toppane.title2_frame.schem_select.config(text = schemname) - - # Update schematic button - if os.path.splitext(self.schematic)[1] == '.v': - self.toppane.title2_frame.schem_label.config(text = 'Verilog netlist:') - else: - self.toppane.title2_frame.schem_label.config(text = 'Schematic netlist:') - - # Update layout button - if os.path.splitext(self.layout)[1] == '.mag': - self.toppane.title2_frame.schem_label.config(text = 'Layout:') - else: - self.toppane.title2_frame.schem_label.config(text = 'Layout netlist:') - - # If there is a comparison file that post-dates both netlists, load it. - self.check_lvs() - return True - - def check_layout_out_of_date(self, spipath, layoutpath): - # Check if a netlist (spipath) is out-of-date relative to the layouts - # (layoutpath). Need to read the netlist and check all of the subcells. - need_capture = False - if not os.path.isfile(spipath): - return True - if os.path.isfile(layoutpath): - spi_statbuf = os.stat(spipath) - lay_statbuf = os.stat(layoutpath) - if spi_statbuf.st_mtime < lay_statbuf.st_mtime: - # netlist exists but is out-of-date - need_capture = True - else: - # only found that the top-level-layout is older than the - # netlist. Now need to read the netlist, find all subcircuits, - # and check those dates, too. - layoutdir = os.path.split(layoutpath)[0] - subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) - with open(spipath, 'r') as ifile: - duttext = ifile.read() - - dutlines = duttext.replace('\n+', ' ').splitlines() - for line in dutlines: - lmatch = subrex.match(line) - if lmatch: - subname = lmatch.group(1) - sublayout = layoutdir + '/' + subname + '.mag' - # subcircuits that cannot be found in the current directory are - # assumed to be library components and therefore never out-of-date. - if os.path.exists(sublayout): - sub_statbuf = os.stat(sublayout) - if spi_statbuf.st_mtime < lay_statbuf.st_mtime: - # netlist exists but is out-of-date - need_capture = True - break - return need_capture - - def check_schematic_out_of_date(self, spipath, schempath): - # Check if a netlist (spipath) is out-of-date relative to the schematics - # (schempath). Need to read the netlist and check all of the subcells. - need_capture = False - if not os.path.isfile(spipath): - return True - if os.path.isfile(schempath): - spi_statbuf = os.stat(spipath) - sch_statbuf = os.stat(schempath) - if spi_statbuf.st_mtime < sch_statbuf.st_mtime: - # netlist exists but is out-of-date - need_capture = True - else: - # only found that the top-level-schematic is older than the - # netlist. Now need to read the netlist, find all subcircuits, - # and check those dates, too. - schemdir = os.path.split(schempath)[0] - subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) - with open(spipath, 'r') as ifile: - duttext = ifile.read() - - dutlines = duttext.replace('\n+', ' ').splitlines() - for line in dutlines: - lmatch = subrex.match(line) - if lmatch: - subname = lmatch.group(1) - # NOTE: Electric uses library:cell internally to track libraries, - # and maps the ":" to "__" in the netlist. Not entirely certain that - # the double-underscore uniquely identifies the library:cell. . . - librex = re.compile('(.*)__(.*)', re.IGNORECASE) - lmatch = librex.match(subname) - if lmatch: - elecpath = os.path.split(os.path.split(schempath)[0])[0] - libname = lmatch.group(1) - subschem = elecpath + '/' + libname + '.delib/' + lmatch.group(2) + '.sch' - else: - libname = {} - subschem = schemdir + '/' + subname + '.sch' - # subcircuits that cannot be found in the current directory are - # assumed to be library components and therefore never out-of-date. - if os.path.exists(subschem): - sub_statbuf = os.stat(subschem) - if spi_statbuf.st_mtime < sub_statbuf.st_mtime: - # netlist exists but is out-of-date - need_capture = True - break - # mapping of characters to what's allowed in SPICE makes finding - # the associated schematic file a bit difficult. Requires wild-card - # searching. - elif libname: - restr = lmatch.group(2) + '.sch' - restr = restr.replace('.', '\.') - restr = restr.replace('_', '.') - schrex = re.compile(restr, re.IGNORECASE) - libpath = elecpath + '/' + libname + '.delib' - if os.path.exists(libpath): - liblist = os.listdir(libpath) - for file in liblist: - lmatch = schrex.match(file) - if lmatch: - subschem = libpath + '/' + file - sub_statbuf = os.stat(subschem) - if spi_statbuf.st_mtime < sch_statbuf.st_mtime: - # netlist exists but is out-of-date - need_capture = True - break - return need_capture - - def check_lvs(self): - # If both netlists exist, and comp.json is more recent than both, then - # load LVS results from comp.json - project_path = self.rootpath - project_name = self.project - layout_path = project_path + '/layout/' + project_name + '.spice' - net_path = project_path + '/netlist/' + project_name + '.spice' - comp_path = project_path + '/layout/comp.json' - - if os.path.exists(layout_path) and os.path.exists(net_path) and os.path.exists(comp_path): - magtime = os.stat(layout_path).st_mtime - schemtime = os.stat(net_path).st_mtime - comptime = os.stat(comp_path).st_mtime - if comptime > magtime and comptime > schemtime: - print("Loading LVS results from file.") - self.generate(comp_path) - - def generate_layout_netlist(self, layout_path, layout_src, project_path): - # Does layout netlist exist and is it current? - if self.check_layout_out_of_date(layout_path, layout_src): - print('Generating layout netlist.') - self.update_idletasks() - mproc = subprocess.Popen(['magic', '-dnull', '-noconsole', - self.layout], stdin = subprocess.PIPE, stdout = subprocess.PIPE, - stderr = subprocess.PIPE, cwd = project_path + '/layout', - universal_newlines = True) - mproc.stdin.write("select top cell\n") - mproc.stdin.write("expand\n") - mproc.stdin.write("extract all\n") - mproc.stdin.write("ext2spice hierarchy on\n") - mproc.stdin.write("ext2spice format ngspice\n") - mproc.stdin.write("ext2spice scale off\n") - mproc.stdin.write("ext2spice renumber off\n") - mproc.stdin.write("ext2spice subcircuit top auto\n") - mproc.stdin.write("ext2spice cthresh infinite\n") - mproc.stdin.write("ext2spice rthresh infinite\n") - mproc.stdin.write("ext2spice blackbox on\n") - mproc.stdin.write("ext2spice -o " + self.layout + ".spice\n") - mproc.stdin.write("quit -noprompt\n") - magicout = mproc.communicate()[0] - self.printwarn(magicout) - if mproc.returncode != 0: - print('Failure to generate new layout netlist.') - return False - - # Move .spice netlist to project_dir/netlist/lvs/ - shutil.move(project_path + '/layout/' + self.layout + '.spice', layout_path) - # Remove extraction files - for file in os.listdir(project_path + '/layout'): - if os.path.splitext(file)[1] == '.ext': - os.remove(project_path + '/layout/' + file) - else: - print('Layout netlist is up-to-date, not regenerating.') - return True - - def run_lvs(self, value): - # "value" is ignored (?) - - # Check if netlists exist and are current; otherwise create them. - # Then run LVS. - - project_path = self.rootpath - project_name = self.project - - # Diagnostic: - print('project_name is ' + project_name) - print('project_path is ' + project_path) - print('self.layout is ' + self.layout) - print('self.schematic is ' + self.schematic) - print('self.lvs_setup is ' + self.lvs_setup) - - has_vlog = False - vlog_path = project_path + '/verilog/' + project_name + '.v' - - if os.path.isfile(self.layout): - layout_path = self.layout - layout_src = None - else: - layout_path = project_path + '/netlist/lvs/' + self.layout + '.spice' - layout_src = project_path + '/layout/' + self.layout + '.mag' - - comp_dir = os.path.split(layout_path)[0] - comp_path = comp_dir + '/comp.json' - - if os.path.isfile(self.schematic): - net_path = self.schematic - else: - net_path = project_path + '/netlist/schem/' + self.schematic + '.spice' - - # Does the setup file exist (this is optional)? - if self.lvs_setup == '' and not os.path.isfile('setup.tcl'): - print('No technology setup file selected.') - elif not os.path.isfile(self.lvs_setup): - print("Can't find technology setup file " + self.lvs_setup) - - # Does schematic netlist exist? - if not os.path.isfile(vlog_path) and not os.path.isfile(net_path): - print('Error: No schematic netlist or verilog netlist.') - return - - # Does LVS netlist subdirectory exist? - if os.path.exists(project_path + '/netlist'): - if not os.path.exists(project_path + '/netlist/lvs'): - os.makedirs(project_path + '/netlist/lvs') - - # Does layout netlist exist and is it current? - if layout_src: - if not self.generate_layout_netlist(layout_path, layout_src, project_path): - return False - - # Final checks - if not os.path.isfile(layout_path): - print('Error: No netlist generated from magic.') - return - - else: - # Read in netlist and convert commas from [X,Y] arrays to vertical bars - # as something that can be converted back as necessary. ngspice treats - # commas as special characters for some reason. - with open(layout_path) as ifile: - spitext = ifile.read() - - # Check the netlist to see if the cell to match is a subcircuit. If - # not, then assume it is the top level. - - layoutcell = os.path.splitext(os.path.split(self.layout)[1])[0] - is_subckt = False - subname = None - subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) - dutlines = spitext.replace('\n+', ' ').splitlines() - for line in dutlines: - lmatch = subrex.match(line) - if lmatch: - subname = lmatch.group(1) - if subname == layoutcell: - is_subckt = True - break - - if is_subckt: - layout_arg = self.layout + ' ' + layoutcell - layout_text = '"' + layout_arg + '"' - elif subname: - layout_arg = self.layout + ' ' + subname - layout_text = '"' + layout_arg + '"' - else: - layout_arg = layout_path - layout_text = layout_arg - - if has_vlog: - schem_arg = vlog_path + ' ' + self.schematic - else: - # Final checks - if not os.path.isfile(net_path): - print('Error: No netlist from schematic.') - return - - with open(net_path) as ifile: - spitext = ifile.read() - - # Check the netlist to see if the cell to match is a subcircuit. If - # not, then assume it is the top level. - - schemcell = os.path.splitext(os.path.split(self.schematic)[1])[0] - subname = None - is_subckt = False - subrex = re.compile('^[^\*]*[ \t]*.subckt[ \t]+([^ \t]+).*$', re.IGNORECASE) - dutlines = spitext.replace('\n+', ' ').splitlines() - for line in dutlines: - lmatch = subrex.match(line) - if lmatch: - subname = lmatch.group(1) - if subname == schemcell: - is_subckt = True - break - - if is_subckt: - schem_arg = self.schematic + ' ' + schemcell - schem_text = '"' + schem_arg + '"' - elif subname: - schem_arg = self.schematic + ' ' + subname - schem_text = '"' + schem_arg + '"' - else: - schem_arg = net_path - schem_text = schem_arg - - # Remove any previous comparison output file - comp_out_path = os.path.splitext(comp_path)[0] + '.out' - if os.path.exists(comp_out_path): - os.remove(comp_out_path) - - # Run netgen as subprocess - print('Running: netgen -batch lvs ' + layout_text + - ' ' + schem_text + ' ' + self.lvs_setup + ' ' + comp_out_path + - ' -json -blackbox') - # Note: Because arguments to subprocess are list items, the {filename cell} - # pair does *not* have to be quoted or braced. Doing so causes a parse - # error. - self.lvsproc = subprocess.Popen(['netgen', '-batch', 'lvs', - layout_arg, schem_arg, - self.lvs_setup, comp_out_path, '-json', '-blackbox'], - cwd=comp_dir, - stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) - # This is largely unnecessary as netgen usually runs to completion very quickly. - self.watchclock(comp_path) - - def watchclock(self, filename): - if self.lvsproc == None: - return - - lvs_status = self.lvsproc.poll() - sresult = select.select([self.lvsproc.stdout, self.lvsproc.stderr], [], [], 0)[0] - if self.lvsproc.stdout in sresult: - outstring = self.lvsproc.stdout.readline().decode().strip() - self.logprint(outstring, doflush=True) - print(outstring) - elif self.lvsproc.stderr in sresult: - errstring = self.lvsproc.stderr.readline().decode().strip() - self.logprint(errstring, doflush = True) - print(errstring, file=sys.stderr) - - if lvs_status != None: - print("netgen LVS exited with status " + str(lvs_status)) - self.lvsproc = None - if lvs_status != 0: - print('Errors encountered in LVS.') - self.logprint('Errors in LVS, lvs status = ' + str(lvs_status), doflush=True) - # Done; now read comp.json and fill the treeview listbox. - self.generate(filename) - else: - self.after(500, lambda: self.watchclock(filename)) - - # Generate display from "comp.out" file (json file now preferred) - - def generate_orig(self, lvspath): - lefttext = [] - righttext = [] - print("Reading LVS output file " + lvspath) - if os.path.exists(lvspath): - with open(lvspath, 'r') as ifile: - lvslines = ifile.read().splitlines() - for line in lvslines: - if '|' in line: - # parts = line.split('|') - # lefttext.append(parts[0]) - # righttext.append(parts[1]) - lefttext.append(line[0:42].strip()) - righttext.append(line[44:].strip()) - else: - lefttext.append(line) - righttext.append('') - # Populate treeview with text - self.toppane.lvsreport.repopulate(lefttext, righttext) - - else: - print("Error: No output file generated from LVS.") - - # Generate output from LVS report JSON file comp.json - - def generate(self, lvspath): - lefttext = [] - righttext = [] - print("Reading LVS output file " + lvspath) - if os.path.exists(lvspath): - with open(lvspath, 'r') as ifile: - self.lvsdata = json.load(ifile) - - # Populate treeview with text - self.toppane.lvsreport.json_repopulate(self.lvsdata) - - else: - print("Error: No output file generated from LVS.") - - def findrecord(self, value): - print("Unimplemented function") - - def findrecord_test(self, value): - # Check if socket is defined; if not, attempt to open one - if not self.msock: - try: - self.msock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - except: - print("No response from layout tool.") - - if self.msock: - self.msock.connect(("0.0.0.0", 12946)) - self.msock.setblocking(False) - if self.msock: - # Pull name of net or device from 'value' - # This is a test: - self.msock.sendall(b'box 0 0 100 100\r\n') - -if __name__ == '__main__': - options = [] - arguments = [] - for item in sys.argv[1:]: - if item.find('-', 0) == 0: - options.append(item) - else: - arguments.append(item) - - root = tkinter.Tk() - app = LVSManager(root) - if arguments: - if len(arguments) >= 2: - app.set_project(arguments[0], project_name=arguments[1]) - else: - app.set_project(arguments[0]) - - root.mainloop() diff --git a/tcltk/netgen.tcl.in b/tcltk/netgen.tcl.in index f63364f..c3f19b8 100644 --- a/tcltk/netgen.tcl.in +++ b/tcltk/netgen.tcl.in @@ -393,7 +393,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { # If argument is a filename then read the list of cells from it; # otherwise, argument is the list of files itself in quotes or # braces. - if {![catch {file exists $value}]} { + if {![catch {file exists $value}] && [file exists $value]} { if {![catch {open $value r} fnf]} { while {[gets $fnf line] >= 0} { if {[lindex $line 0] != "#"} { @@ -532,14 +532,25 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { } set properr {} set matcherr {} - set pinsgood -1 + set childMismatch 0 ; # 1 indicates black-box child subcircuit mismatch + #set pinsgood -1 while {$endval != {}} { if {$dolist == 1} { netgen::run -list converge } else { netgen::run converge } - netgen::log echo on + set pinMismatch 0 ; # indicates pinMismatch in current cell (only used for top cell) + #netgen::log echo on + set doCheckFlatten 0 + set doFlatten 0 + if {[netgen::print queue] == {}} { + set doEquatePins 1 ; # run equate pins top cell + } else { + set doEquatePins 0 ; # don't run equate pins unless unique match, property errors, and pin errors + } + set forceMatch 0 ; # for pin matching + netgen::log echo off if {[verify equivalent]} { # Resolve automorphisms by pin and property if {$dolist == 1} { @@ -548,96 +559,178 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { netgen::run resolve } set uresult [verify unique] + # -3 - unique with property error, -2 - equivalent with port errors, -1 - blackbox, 0 - equivalent but not unique, 1 - unique if {$uresult == 0} { + netgen::log echo on 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" + netgen::log echo off if {$dolist == 1} { verify -list nodes } else { verify nodes } + set doCheckFlatten 1 - # Flatten the non-matching subcircuit (but not the top-level cells) - if {[netgen::print queue] != {}} { - if {([lsearch $noflat [lindex $endval 0]] == -1) && - ([lsearch $noflat [lindex $endval 1]] == -1)} { - netgen::log put " Flattening non-matched subcircuits $endval\n" - netgen::flatten class "[lindex $endval 0] $fnum1" - netgen::flatten class "[lindex $endval 1] $fnum2" - } else { - netgen::log put " Continuing with black-boxed subcircuits $endval\n" - lappend matcherr [lindex $endval 0]"($fnum1)" - lappend matcherr [lindex $endval 1]"($fnum2)" - # Match pins - netgen::log echo off - if {$dolist == 1} { - set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \ - "$fnum2 [lindex $endval 1]"] - } else { - set result [equate -force 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 - } - } - } else { - 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]" - } - # If $uresult == -1 then these are black-box entries and - # $pinsgood should not be set to the resulting value. - if {$uresult > 0} { - set pinsgood $result - } - netgen::log echo on + } else { ; # Equate pins for black boxes, unique matches (including property errors), and unique with port errors + set doEquatePins 1 + } + if {$uresult == -1} { ; # black box + set forceMatch 1 + } elseif {$uresult == -3} { ; # property error + lappend properr [lindex $endval 0] + } elseif {$uresult == -2} { ; # unmatched pins + set doCheckFlatten 1 } - if {$uresult == 2} {lappend properr [lindex $endval 0]} } else { - # Flatten the non-matching subcircuit (but not the top-level cells) + # not equivalent + netgen::log echo on + netgen::log put " DEBUG: not equivalent $endval\n" + netgen::log echo off + set doCheckFlatten 1 + } + if ($doCheckFlatten) { + # Flatten the non-matching subcircuit (but not the top-level cells, nor explicit no flat cells) if {[netgen::print queue] != {}} { - if {([lsearch $noflat [lindex $endval 0]] == -1) && - ([lsearch $noflat [lindex $endval 1]] == -1)} { - netgen::log put " Flattening non-matched subcircuits $endval\n" - netgen::flatten class "[lindex $endval 0] $fnum1" - netgen::flatten class "[lindex $endval 1] $fnum2" - } else { - netgen::log put " Continuing with black-boxed subcircuits $endval\n" - lappend matcherr [lindex $endval 0]"($fnum1)" - lappend matcherr [lindex $endval 0]"($fnum2)" - # Match pins - netgen::log echo off - if {$dolist == 1} { - set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \ - "$fnum2 [lindex $endval 1]"] - } else { - set result [equate -force 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 + if {([lsearch $noflat [lindex $endval 0]] == -1) && + ([lsearch $noflat [lindex $endval 1]] == -1)} { + set doFlatten 1 + } else { netgen::log echo on + netgen::log put " Continuing with unmatched black-boxed subcircuits $endval\n" + netgen::log echo off + lappend matcherr [lindex $endval 0]"($fnum1)" + lappend matcherr [lindex $endval 1]"($fnum2)" + set doEquatePins 1 + set childMismatch 1 + set forceMatch 1 + } + } + } + if {$doEquatePins} { + # Match pins + if {$dolist == 1} { + if {$forceMatch} { + set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \ + "$fnum2 [lindex $endval 1]"] + } else { + set result [equate -list pins "$fnum1 [lindex $endval 0]" \ + "$fnum2 [lindex $endval 1]"] + } + } else { + if {$forceMatch} { + set result [equate -force 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]" + } + # Do not set pinMismatch for black boxes + if {$result < 0} { + #set pinMismatch 0 + if {$result == -1 && [netgen::print queue] != {} && $forceMatch != 1} { + # flatten pin mismatch, but not empty cells (-2) or top cell or noflatten cells + set doFlatten 1 } + } elseif { [netgen::print queue] == {} && $result == 0 } { + set pinMismatch 1 } } - netgen::log echo off + if {$doFlatten} { + netgen::log echo on + netgen::log put " Flattening non-matched subcircuits $endval\n" + netgen::log echo off + netgen::flatten class "[lindex $endval 0] $fnum1" + netgen::flatten class "[lindex $endval 1] $fnum2" + } + # if {} { + # if {} { +# # Flatten the non-matching subcircuit (but not the top-level cells) +# if {[netgen::print queue] != {}} { +# if {([lsearch $noflat [lindex $endval 0]] == -1) && +# ([lsearch $noflat [lindex $endval 1]] == -1)} { +# netgen::log put " Flattening non-matched subcircuits $endval\n" +# netgen::flatten class "[lindex $endval 0] $fnum1" +# netgen::flatten class "[lindex $endval 1] $fnum2" +# } else { +# netgen::log put " Continuing with black-boxed subcircuits $endval\n" +# lappend matcherr [lindex $endval 0] ($fnum1) +# lappend matcherr [lindex $endval 1] ($fnum2) +# # Match pins +# netgen::log echo off +# if {$dolist == 1} { +# set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \ +# "$fnum2 [lindex $endval 1]"] +# } else { +# set result [equate -force 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 +# } +# } +# } else { +# 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]" +# } +# # If $uresult == -1 then these are black-box entries and +# # $pinsgood should not be set to the resulting value. +# if {$uresult > 0} { +# 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] != {}} { +# if {([lsearch $noflat [lindex $endval 0]] == -1) && +# ([lsearch $noflat [lindex $endval 1]] == -1)} { +# netgen::log put " Flattening non-matched subcircuits $endval\n" +# netgen::flatten class "[lindex $endval 0] $fnum1" +# netgen::flatten class "[lindex $endval 1] $fnum2" +# } else { +# netgen::log put " Continuing with black-boxed subcircuits $endval\n" +# lappend matcherr [lindex $endval 0] ($fnum1) +# lappend matcherr [lindex $endval 0] ($fnum2) +# # Match pins +# netgen::log echo off +# if {$dolist == 1} { +# set result [equate -list -force pins "$fnum1 [lindex $endval 0]" \ +# "$fnum2 [lindex $endval 1]"] +# } else { +# set result [equate -force 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 +# } +# } +# } +# netgen::log echo off if {$dolist == 1} { catch {lappend lvs_final $lvs_out} set lvs_out {} @@ -646,20 +739,29 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} { set endval [netgen::compare hierarchical] } } - netgen::log echo off - puts stdout "Result: " nonewline + #netgen::log echo off + #puts stdout "Result: " nonewline netgen::log echo on - if {$pinsgood == 0} { - # NOTE: Need to disambiguate these two cases. . . - netgen::log put "Cells failed matching, or top level cell failed pin matching.\n" + #if {$pinsgood == 0} {} + # # NOTE: Need to disambiguate these two cases. . . + # netgen::log put "Cells failed matching, or top level cell failed pin matching.\n" + #put stdout "Result: " nonewline + netgen::log put "\nFinal result: " + if {$pinMismatch || $childMismatch} { + if {$childMismatch} { + netgen::log put "Subcell(s) failed matching.\n" + } + if {$pinMismatch} { + netgen::log put "Top level cell failed pin matching.\n" + } } else { verify only } if {$properr != {}} { - netgen::log put "The following cells had property errors:\n " [regsub -all { } $properr "\n "] "\n" + netgen::log put "\nThe following cells had property errors:\n " [regsub -all { } $properr "\n "] "\n" } if {$matcherr != {}} { - netgen::log put "The following subcells failed to match:\n " [regsub -all { } $matcherr "\n "] "\n" + netgen::log put "\nThe following subcells failed to match:\n " [regsub -all { } $matcherr "\n "] "\n" } if {$dolog} { netgen::log end diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index f6c573c..c25a106 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -1931,9 +1931,17 @@ _netgen_log(ClientData clientData, switch(index) { case START_IDX: LoggingFile = fopen(LogFileName, "w"); + if (! LoggingFile) { + Tcl_SetResult(interp, "Could not open log file.", NULL); + return TCL_ERROR; + } break; case RESUME_IDX: LoggingFile = fopen(LogFileName, "a"); + if (! LoggingFile) { + Tcl_SetResult(interp, "Could not open log file.", NULL); + return TCL_ERROR; + } break; case END_IDX: fclose(LoggingFile); @@ -1942,6 +1950,10 @@ _netgen_log(ClientData clientData, case RESET_IDX: fclose(LoggingFile); LoggingFile = fopen(LogFileName, "w"); + if (! LoggingFile) { + Tcl_SetResult(interp, "Could not open log file.", NULL); + return TCL_ERROR; + } break; case SUSPEND_IDX: fclose(LoggingFile); @@ -2203,11 +2215,11 @@ _netcmp_compare(ClientData clientData, hascontents2 = HasContents(tp2); if (hascontents1 && !hascontents2 && (tp2->flags & CELL_PLACEHOLDER)) { - Fprintf(stdout, "Circuit 2 cell %s is a black box; will not flatten " + Fprintf(stdout, "\nCircuit 2 cell %s is a black-box; will not flatten " "Circuit 1\n", name2); } else if (hascontents2 && !hascontents1 && (tp1->flags & CELL_PLACEHOLDER)) { - Fprintf(stdout, "Circuit 1 cell %s is a black box; will not flatten " + Fprintf(stdout, "\nCircuit 1 cell %s is a black-box; will not flatten " "Circuit 2\n", name1); } else if (hascontents1 || hascontents2) { @@ -2223,6 +2235,10 @@ _netcmp_compare(ClientData clientData, DescribeContents(name1, fnum1, name2, fnum2); } } + else { // two empty subcircuits + Fprintf(stdout, "\nCircuit 1 cell %s and Circuit 2 cell %s are black-boxes.\n", + name1, name2); + } CreateTwoLists(name1, fnum1, name2, fnum2, dolist); // Return the names of the two cells being compared, if doing "compare @@ -2479,6 +2495,38 @@ _netcmp_run(ClientData clientData, ExhaustiveSubdivision = 1; while (!Iterate() && !InterruptPending); automorphisms = VerifyMatching(); + if (automorphisms > 0) { + // First try to resolve automorphisms uniquely using + // property matching + automorphisms = ResolveAutomorphsByProperty(); + if (automorphisms > 0) { + // Next, attempt to resolve automorphisms uniquely by + // using the pin names + automorphisms = ResolveAutomorphsByPin(); + } + if (automorphisms > 0) { + // Anything left is truly indistinguishable + while (ResolveAutomorphisms() > 0); + } + } + if (automorphisms == -1) + Fprintf(stdout, "Netlists do not match.\n"); + else if (automorphisms == -2) + Fprintf(stdout, "Netlists match uniquely with port errors.\n"); + else { + if (automorphisms == 0) + Fprintf(stdout, "Netlists match uniquely"); + else + Fprintf(stdout, "Netlists match with %d symmetr%s", + automorphisms, (automorphisms == 1) ? "y" : "ies"); + if (PropertyErrorDetected) { + Fprintf(stdout, " with property errors.\n"); + PrintPropertyResults(dolist); + } else + Fprintf(stdout, ".\n"); + } + disable_interrupt(); + /* if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); else if (automorphisms == 0) @@ -2514,6 +2562,7 @@ _netcmp_run(ClientData clientData, PrintPropertyResults(dolist); } disable_interrupt(); + */ } break; } @@ -2529,8 +2578,15 @@ _netcmp_run(ClientData clientData, /* all, or no option. */ /* Formerly: v */ /* Results: */ -/* For only, equivalent, unique: Return 1 if */ -/* verified, zero if not. */ +/* For only, equivalent, unique: Return */ +/* 1: verified */ +/* 0: not verified */ +/* -1: no elements or no nodes */ +/* -3: verified with property error */ +/* equiv option */ +/* n: number of automorphisms */ +/* unique option */ +/* -2: pin mismatch */ /* Side Effects: */ /* For options elements, nodes, and all without */ /* option -list: Write output to log file. */ @@ -2624,13 +2680,22 @@ _netcmp_verify(ClientData clientData, else Fprintf(stdout, "Netlists do not match.\n"); } + else if (automorphisms == -2) { + //result = MatchPins(Circuit1, Circuit2, dolist); + if (index == EQUIV_IDX) + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); + else if (index == UNIQUE_IDX) + Tcl_SetObjResult(interp, Tcl_NewIntObj(-2)); + else if (index > 0) + Fprintf(stdout, "Circuits match uniquely with port errors.\n"); + } else { if (automorphisms) { if (index == EQUIV_IDX) Tcl_SetObjResult(interp, Tcl_NewIntObj((int)automorphisms)); else if (index == UNIQUE_IDX) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); - else + else if (index > 0) Printf("Circuits match with %d symmetr%s.\n", automorphisms, (automorphisms == 1) ? "y" : "ies"); } @@ -2639,12 +2704,14 @@ _netcmp_verify(ClientData clientData, if (PropertyErrorDetected == 0) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); else - Tcl_SetObjResult(interp, Tcl_NewIntObj(2)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(-3)); } - else { - Fprintf(stdout, "Circuits match uniquely.\n"); - if (PropertyErrorDetected != 0) - Fprintf(stdout, "Property errors were found.\n"); + else if (index > 0) { + Fprintf(stdout, "Circuits match uniquely"); + if (PropertyErrorDetected == 0) + Fprintf(stdout, ".\n"); + else + Fprintf(stdout, " with property errors.\n"); } } #if 0 @@ -3054,19 +3121,21 @@ _netcmp_equate(ClientData clientData, else if ((tp1->flags & CELL_PLACEHOLDER) || (tp2->flags & CELL_PLACEHOLDER)) { if (tp1->flags & CELL_PLACEHOLDER) { - Fprintf(stdout, "Warning: Equate pins: cell %s " - "is a placeholder, treated as a black box.\n", name1); + Fprintf(stdout, "Warning: Equate pins: cell %s (%d) " + "is a placeholder, treated as a black-box.\n", name1, file1); } if (tp2->flags & CELL_PLACEHOLDER) { - Fprintf(stdout, "Warning: Equate pins: cell %s " - "is a placeholder, treated as a black box.\n", name2); + Fprintf(stdout, "Warning: Equate pins: cell %s (%d) " + "is a placeholder, treated as a black-box.\n", name2, file2); } // If a cell in either circuit is marked as a black box, then // the cells in both circuits should be marked as a black box. tp1->flags |= CELL_PLACEHOLDER; tp2->flags |= CELL_PLACEHOLDER; } - else { + else if (doforce != TRUE) { + // when doforce is true, ElementClass has been set to NULL even though circuits contain elements. + // so this message is not correct. Fprintf(stdout, "Equate pins: cell %s and/or %s " "has no elements.\n", name1, name2); /* This is not necessarily an error, so go ahead and match pins. */ @@ -3098,7 +3167,13 @@ _netcmp_equate(ClientData clientData, else if (result > 0) { Fprintf(stdout, "Cell pin lists are equivalent.\n"); } - else { + else if (result == -1) { + Fprintf(stdout, "Cell pin lists for %s and %s do not match.\n", + name1, name2); + } + else if (result == -2) { + Fprintf(stdout, "Attempt to match empty cell to non-empty cell.\n"); + } else { Fprintf(stdout, "Cell pin lists for %s and %s altered to match.\n", name1, name2); } @@ -3124,7 +3199,7 @@ _netcmp_equate(ClientData clientData, /* Objects must be CLASS_MODULE or CLASS_SUBCKT */ if (tp1->class != CLASS_MODULE && tp1->class != CLASS_SUBCKT) { - Tcl_SetResult(interp, "Device class is not black box" + Tcl_SetResult(interp, "Device class is not black-box" " or subcircuit!", NULL); return TCL_ERROR; }