mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-26 07:56:56 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a13491e358 | ||
|
|
2ac2a3b6f8 | ||
|
|
1d05b8b676 | ||
|
|
759d63cea9 | ||
|
|
476da015f0 | ||
|
|
4aa8a72769 | ||
|
|
e97d6f1aeb | ||
|
|
47e7367c26 | ||
|
|
362fb7120b | ||
|
|
fb4759a8cd | ||
|
|
92e8fed8be | ||
|
|
45d4004d43 | ||
|
|
01b2484be3 | ||
|
|
fe47d84ace | ||
|
|
0b014e6efd | ||
|
|
0a2adeb593 | ||
|
|
330b34139c | ||
|
|
62d0352149 | ||
|
|
9ca713dfd9 | ||
|
|
b1b04b8e87 | ||
|
|
851a1f941e | ||
|
|
6b9d92de65 | ||
|
|
d763ad0e60 | ||
|
|
fd72e24a86 | ||
|
|
20077d3d56 |
+1
-1
@@ -319,7 +319,7 @@ int flattenInstancesOf(char *name, int fnum, char *instance)
|
||||
Printf(" Flattening instance: %s, primitive = %s\n",
|
||||
ParentParams->instance.name, (ChildCell->class ==
|
||||
CLASS_SUBCKT) ? "no" : "yes");
|
||||
if (ChildCell->class != CLASS_SUBCKT) {
|
||||
if ((ChildCell->class != CLASS_SUBCKT) && (ChildCell->class != CLASS_MODULE)) {
|
||||
LastObj = ParentParams;
|
||||
continue;
|
||||
}
|
||||
|
||||
+441
-105
@@ -1141,6 +1141,323 @@ Tcl_Obj *ListElementClasses(int legal)
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* Sort the fanout lists of two formatted list entries so that they
|
||||
* are as well aligned as they can be made, practically. The matching
|
||||
* is ad hoc as it does not affect LVS results but only how the results
|
||||
* are organized and presented in the output.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
SortFanoutLists(nlist1, nlist2)
|
||||
struct FormattedList *nlist1, *nlist2;
|
||||
{
|
||||
struct hashdict f1hash, f2hash;
|
||||
int f1, f2, total;
|
||||
struct FanoutList temp;
|
||||
int *matched;
|
||||
char pinname[1024], pinnameA[1024], pinnameB[1024];
|
||||
|
||||
InitializeHashTable(&f1hash, OBJHASHSIZE);
|
||||
InitializeHashTable(&f2hash, OBJHASHSIZE);
|
||||
|
||||
if (nlist1->fanout < nlist2->fanout) {
|
||||
matched = (int *)CALLOC(nlist2->fanout, sizeof(int));
|
||||
total = 0;
|
||||
|
||||
for (f2 = 0; f2 < nlist2->fanout; f2++) {
|
||||
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
|
||||
nlist2->flist[f2].name);
|
||||
HashPtrInstall(pinname, (void *)((long)f2 + 1), &f2hash);
|
||||
}
|
||||
|
||||
for (f1 = 0; f1 < nlist1->fanout; f1++) {
|
||||
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
|
||||
nlist1->flist[f1].name);
|
||||
f2 = (int)(long)HashLookup(pinname, &f2hash);
|
||||
if (f2 != 0) {
|
||||
f2 -= 1;
|
||||
matched[f1] = -1;
|
||||
total++;
|
||||
if (f2 != f1) {
|
||||
temp = nlist2->flist[f2];
|
||||
nlist2->flist[f2] = nlist2->flist[f1];
|
||||
nlist2->flist[f1] = temp;
|
||||
sprintf(pinnameA, "%s/%s", nlist2->flist[f1].model,
|
||||
nlist2->flist[f1].name);
|
||||
sprintf(pinnameB, "%s/%s", nlist2->flist[f2].model,
|
||||
nlist2->flist[f2].name);
|
||||
HashPtrInstall(pinnameA, (void *)((long)f1 + 1), &f2hash);
|
||||
HashPtrInstall(pinnameB, (void *)((long)f2 + 1), &f2hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* To do: If full pin names don't match, match by model name only */
|
||||
}
|
||||
else {
|
||||
matched = (int *)CALLOC(nlist1->fanout, sizeof(int));
|
||||
total = 0;
|
||||
|
||||
for (f1 = 0; f1 < nlist1->fanout; f1++) {
|
||||
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
|
||||
nlist1->flist[f1].name);
|
||||
HashPtrInstall(pinname, (void *)((long)f1 + 1), &f1hash);
|
||||
}
|
||||
|
||||
for (f2 = 0; f2 < nlist2->fanout; f2++) {
|
||||
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
|
||||
nlist2->flist[f2].name);
|
||||
f1 = (int)(long)HashLookup(pinname, &f1hash);
|
||||
if (f1 != 0) {
|
||||
f1 -= 1;
|
||||
matched[f2] = -1;
|
||||
total++;
|
||||
if (f1 != f2) {
|
||||
temp = nlist1->flist[f1];
|
||||
nlist1->flist[f1] = nlist1->flist[f2];
|
||||
nlist1->flist[f2] = temp;
|
||||
sprintf(pinnameA, "%s/%s", nlist1->flist[f1].model,
|
||||
nlist1->flist[f1].name);
|
||||
sprintf(pinnameB, "%s/%s", nlist1->flist[f2].model,
|
||||
nlist1->flist[f2].name);
|
||||
HashPtrInstall(pinnameA, (void *)((long)f1 + 1), &f1hash);
|
||||
HashPtrInstall(pinnameB, (void *)((long)f2 + 1), &f1hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* To do: If full pin names don't match, match by model name only */
|
||||
}
|
||||
|
||||
FREE(matched);
|
||||
HashKill(&f1hash);
|
||||
HashKill(&f2hash);
|
||||
}
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* Determine the match between two entries in a bad node fragment
|
||||
* according to an ad hoc metric of how many fanout entries are
|
||||
* the same between the two. This is not used to match circuits
|
||||
* for LVS, but is used to sort the dump of unmatched nets generated
|
||||
* for an unmatched subcell, so that the end-user is not presented
|
||||
* with a list in a confusingly arbitrary order.
|
||||
*
|
||||
* Score is normalized to 100.
|
||||
* If a model/pin has an equivalent on the other size, add 1
|
||||
* If a model/pin equivalent has the same count, add 1
|
||||
* Total values and normalize to a 100 score for an exact match.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
NodeMatchScore(nlist1, nlist2)
|
||||
struct FormattedList *nlist1, *nlist2;
|
||||
{
|
||||
struct hashdict f1hash, f2hash;
|
||||
char pinname[1024];
|
||||
int f1, f2, maxfanout;
|
||||
int score = 0;
|
||||
|
||||
InitializeHashTable(&f1hash, OBJHASHSIZE);
|
||||
InitializeHashTable(&f2hash, OBJHASHSIZE);
|
||||
|
||||
if (nlist1->fanout < nlist2->fanout) {
|
||||
for (f2 = 0; f2 < nlist2->fanout; f2++) {
|
||||
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
|
||||
nlist2->flist[f2].name);
|
||||
HashPtrInstall(pinname, (void *)((long)f2 + 1), &f2hash);
|
||||
}
|
||||
|
||||
for (f1 = 0; f1 < nlist1->fanout; f1++) {
|
||||
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
|
||||
nlist1->flist[f1].name);
|
||||
f2 = (int)(long)HashLookup(pinname, &f2hash);
|
||||
if (f2 != 0) {
|
||||
f2 -= 1;
|
||||
score++;
|
||||
if (nlist1->flist[f1].count == nlist2->flist[f2].count)
|
||||
score++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (f1 = 0; f1 < nlist1->fanout; f1++) {
|
||||
sprintf(pinname, "%s/%s", nlist1->flist[f1].model,
|
||||
nlist1->flist[f1].name);
|
||||
HashPtrInstall(pinname, (void *)((long)f1 + 1), &f1hash);
|
||||
}
|
||||
|
||||
for (f2 = 0; f2 < nlist2->fanout; f2++) {
|
||||
sprintf(pinname, "%s/%s", nlist2->flist[f2].model,
|
||||
nlist2->flist[f2].name);
|
||||
f1 = (int)(long)HashLookup(pinname, &f1hash);
|
||||
if (f1 != 0) {
|
||||
f1 -= 1;
|
||||
score++;
|
||||
if (nlist2->flist[f2].count == nlist1->flist[f1].count)
|
||||
score++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HashKill(&f1hash);
|
||||
HashKill(&f2hash);
|
||||
|
||||
maxfanout = (nlist1->fanout < nlist2->fanout) ? nlist2->fanout : nlist1->fanout;
|
||||
score = (50 * score) / maxfanout;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*
|
||||
* Sort node list 2 to match the entries in list 1, to the extent
|
||||
* possible. Exact name matching is preferred, followed by matching
|
||||
* of the largest percentage of components.
|
||||
*
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void SortUnmatchedLists(nlists1, nlists2, n1max, n2max)
|
||||
struct FormattedList **nlists1, **nlists2;
|
||||
int n1max, n2max;
|
||||
{
|
||||
struct FormattedList *temp;
|
||||
int n1, n2;
|
||||
int *matched, total, best, ibest;
|
||||
|
||||
struct hashdict n1hash, n2hash;
|
||||
|
||||
InitializeHashTable(&n1hash, OBJHASHSIZE);
|
||||
InitializeHashTable(&n2hash, OBJHASHSIZE);
|
||||
|
||||
if (n1max < n2max) {
|
||||
matched = (int *)CALLOC(n2max, sizeof(int));
|
||||
total = 0;
|
||||
|
||||
for (n2 = 0; n2 < n2max; n2++)
|
||||
HashPtrInstall(nlists2[n2]->name, (void *)((long)n2 + 1), &n2hash);
|
||||
|
||||
/* Match by name */
|
||||
for (n1 = 0; n1 < n1max; n1++) {
|
||||
n2 = (int)(long)HashLookup(nlists1[n1]->name, &n2hash);
|
||||
if (n2 != 0) {
|
||||
n2 -= 1;
|
||||
matched[n1] = -1;
|
||||
total++;
|
||||
if (n2 != n1) {
|
||||
temp = nlists2[n2];
|
||||
nlists2[n2] = nlists2[n1];
|
||||
nlists2[n1] = temp;
|
||||
HashPtrInstall(nlists2[n1]->name, (void *)((long)n1 + 1), &n2hash);
|
||||
HashPtrInstall(nlists2[n2]->name, (void *)((long)n2 + 1), &n2hash);
|
||||
SortFanoutLists(nlists1[n1], nlists2[n1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For all nets that didn't match by name, match by content */
|
||||
#if 0
|
||||
/* This is ifdef'd out because the improvement in the presentation
|
||||
* of the output is minimal, but the amount of computation is huge.
|
||||
* There are numerous ways to optimize this.
|
||||
*/
|
||||
if (total < n1max) {
|
||||
for (n1 = 0; n1 < n1max; n1++) {
|
||||
if (matched[n1] != -1) {
|
||||
best = 0;
|
||||
ibest = -1;
|
||||
for (n2 = 0; n2 < n2max; n2++) {
|
||||
if (matched[n2] != -1) {
|
||||
matched[n2] = NodeMatchScore(nlists1[n1], nlists2[n2]);
|
||||
if (matched[n2] > best) {
|
||||
best = matched[n2];
|
||||
ibest = n2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ibest >= 0) {
|
||||
matched[n1] = -1;
|
||||
temp = nlists2[ibest];
|
||||
nlists2[ibest] = nlists2[n1];
|
||||
nlists2[n1] = temp;
|
||||
SortFanoutLists(nlists1[n1], nlists2[n1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
matched = (int *)CALLOC(n1max, sizeof(int));
|
||||
total = 0;
|
||||
|
||||
for (n1 = 0; n1 < n1max; n1++)
|
||||
HashPtrInstall(nlists1[n1]->name, (void *)((long)n1 + 1), &n1hash);
|
||||
|
||||
for (n2 = 0; n2 < n2max; n2++) {
|
||||
n1 = (int)(long)HashLookup(nlists2[n2]->name, &n1hash);
|
||||
if (n1 != 0) {
|
||||
n1 -= 1;
|
||||
matched[n2] = -1;
|
||||
total++;
|
||||
if (n1 != n2) {
|
||||
temp = nlists1[n1];
|
||||
nlists1[n1] = nlists1[n2];
|
||||
nlists1[n2] = temp;
|
||||
HashPtrInstall(nlists1[n1]->name, (void *)((long)n1 + 1), &n1hash);
|
||||
HashPtrInstall(nlists1[n2]->name, (void *)((long)n2 + 1), &n1hash);
|
||||
SortFanoutLists(nlists2[n2], nlists1[n2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* For all nets that didn't match by name, match by content */
|
||||
#if 0
|
||||
/* This is ifdef'd out because the improvement in the presentation
|
||||
* of the output is minimal, but the amount of computation is huge.
|
||||
* There are numerous ways to optimize this.
|
||||
*/
|
||||
if (total < n2max) {
|
||||
for (n2 = 0; n2 < n2max; n2++) {
|
||||
if (matched[n2] != -1) {
|
||||
best = 0;
|
||||
ibest = -1;
|
||||
for (n1 = 0; n1 < n1max; n1++) {
|
||||
if (matched[n1] != -1) {
|
||||
matched[n1] = NodeMatchScore(nlists2[n2], nlists1[n1]);
|
||||
if (matched[n1] > best) {
|
||||
best = matched[n1];
|
||||
ibest = n1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ibest >= 0) {
|
||||
matched[n2] = -1;
|
||||
temp = nlists1[ibest];
|
||||
nlists1[ibest] = nlists1[n2];
|
||||
nlists1[n2] = temp;
|
||||
SortFanoutLists(nlists2[n2], nlists1[n2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
FREE(matched);
|
||||
HashKill(&n1hash);
|
||||
HashKill(&n2hash);
|
||||
}
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------
|
||||
*---------------------------------------------------------------------
|
||||
@@ -1219,8 +1536,9 @@ void FormatIllegalElementClasses()
|
||||
n2++;
|
||||
}
|
||||
}
|
||||
Fprintf(stdout, "\n");
|
||||
SortUnmatchedLists(elist1, elist2, n1, n2);
|
||||
|
||||
Fprintf(stdout, "\n");
|
||||
for (n = 0; n < ((n1 > n2) ? n1 : n2); n++) {
|
||||
if (n != 0) {
|
||||
for (i = 0; i < left_col_end; i++) *(ostr + i) = ' ';
|
||||
@@ -1522,6 +1840,12 @@ void FormatIllegalNodeClasses()
|
||||
ostr = CALLOC(right_col_end + 2, sizeof(char));
|
||||
found = 0;
|
||||
|
||||
/*
|
||||
* To do: match net names across partitions, to make it much clearer how
|
||||
* two nets are mismatched, when they have been dropped into different
|
||||
* partitions.
|
||||
*/
|
||||
|
||||
for (nscan = NodeClasses; nscan != NULL; nscan = nscan->next)
|
||||
if (!(nscan->legalpartition)) {
|
||||
struct Node *N;
|
||||
@@ -1574,8 +1898,9 @@ void FormatIllegalNodeClasses()
|
||||
n2++;
|
||||
}
|
||||
}
|
||||
Fprintf(stdout, "\n");
|
||||
SortUnmatchedLists(nlists1, nlists2, n1, n2);
|
||||
|
||||
Fprintf(stdout, "\n");
|
||||
for (n = 0; n < ((n1 > n2) ? n1 : n2); n++) {
|
||||
if (n != 0) {
|
||||
for (i = 0; i < left_col_end; i++) *(ostr + i) = ' ';
|
||||
@@ -3733,24 +4058,33 @@ int series_optimize(struct objlist *ob1, struct nlist *tp1, int idx1,
|
||||
}
|
||||
|
||||
typedef struct _propsort {
|
||||
double value;
|
||||
double value; /* Primary sorting value */
|
||||
double avalue; /* Secondary sorting value */
|
||||
double slop; /* Delta for accepting equality */
|
||||
int idx;
|
||||
unsigned char flags;
|
||||
struct objlist *ob;
|
||||
} propsort;
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Property sorting routine used by qsort() */
|
||||
/* Property sorting routine used by qsort(). Sorts on "value" */
|
||||
/* unless the "value" for the two entries differs by less than */
|
||||
/* "slop", in which case the entries are sorted on "avalue". */
|
||||
/*--------------------------------------------------------------*/
|
||||
|
||||
static int compsort(const void *p1, const void *p2)
|
||||
{
|
||||
propsort *s1, *s2;
|
||||
double smax;
|
||||
|
||||
s1 = (propsort *)p1;
|
||||
s2 = (propsort *)p2;
|
||||
|
||||
return (s1->value > s2->value) ? 1 : 0;
|
||||
smax = fmax(s1->slop, s2->slop);
|
||||
if (fabs(s1->value - s2->value) <= smax)
|
||||
return (s1->avalue > s2->avalue) ? 1 : 0;
|
||||
else
|
||||
return (s1->value > s2->value) ? 1 : 0;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
@@ -3769,7 +4103,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
|
||||
struct property *kl;
|
||||
struct valuelist *vl, *sl;
|
||||
int i, p, sval, merge_type;
|
||||
double cval;
|
||||
double cval, slop;
|
||||
|
||||
obn = ob1->next;
|
||||
for (i = 0; i < idx1; i++) obn = obn->next;
|
||||
@@ -3782,7 +4116,7 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
|
||||
|
||||
obp = obn;
|
||||
sval = 1;
|
||||
cval = 0.0;
|
||||
cval = slop = 0.0;
|
||||
for (i = 0; i < run; i++) {
|
||||
merge_type = MERGE_NONE;
|
||||
for (p = 0;; p++) {
|
||||
@@ -3795,23 +4129,37 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
|
||||
}
|
||||
else {
|
||||
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
|
||||
if (kl && (kl->merge & MERGE_S_CRIT)) {
|
||||
if (vl->type == PROP_INTEGER)
|
||||
if (kl && (kl->merge & (MERGE_S_ADD | MERGE_S_PAR))) {
|
||||
if (vl->type == PROP_INTEGER) {
|
||||
cval = (double)vl->value.ival;
|
||||
else
|
||||
slop = (double)kl->slop.ival;
|
||||
}
|
||||
else {
|
||||
cval = vl->value.dval;
|
||||
slop = kl->slop.dval;
|
||||
}
|
||||
merge_type = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (merge_type == MERGE_S_ADD) {
|
||||
proplist[i].value = cval * (double)sval;
|
||||
proplist[i].slop = slop;
|
||||
proplist[i].avalue = 0;
|
||||
sl->value.ival = 1;
|
||||
}
|
||||
else if (merge_type == MERGE_S_PAR) {
|
||||
proplist[i].value = cval / (double)sval;
|
||||
proplist[i].slop = slop;
|
||||
proplist[i].avalue = 0;
|
||||
sl->value.ival = 1;
|
||||
}
|
||||
else {
|
||||
/* Components which declare no series addition method stay unsorted */
|
||||
proplist[i].value = (double)0;
|
||||
proplist[i].avalue = (double)0;
|
||||
proplist[i].slop = (double)1E-6;
|
||||
}
|
||||
proplist[i].idx = i;
|
||||
proplist[i].ob = obp;
|
||||
obp = obp->next;
|
||||
@@ -3908,32 +4256,31 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
|
||||
struct property *kl;
|
||||
struct valuelist *vl, *ml;
|
||||
int i, p, mval, merge_type, has_crit = FALSE;
|
||||
char *subs_crit = NULL;
|
||||
double cval;
|
||||
char c, *subs_crit = NULL;
|
||||
double cval, tval, aval, slop, tslop;
|
||||
|
||||
obn = ob1->next;
|
||||
for (i = 0; i < idx1; i++) obn = obn->next;
|
||||
|
||||
// Create a structure of length (run) to hold critical property
|
||||
// value and index. Then sort that list, then use the sorted
|
||||
// indexes to sort the actual property linked list.
|
||||
|
||||
// If there is no critical property listed, then it is still better
|
||||
// to sort on any random property than on no properties. Note that
|
||||
// this can (and should!) be made better by sorting on *all*
|
||||
// properties, not just the first. Otherwise, circuit 1 can have, e.g.,
|
||||
// parallel transistors with W=1, L=1 and W=1, L=2 while circuit two
|
||||
// has W=1, L=2 and W=1, L=1 and property matching will fail because
|
||||
// sorting was done on W only.
|
||||
/* Create a structure of length (run) to hold critical property
|
||||
* value and index. Then sort that list, then use the sorted
|
||||
* indexes to sort the actual property linked list.
|
||||
*
|
||||
* If there is no critical property listed, then it will sort on
|
||||
* M first, and any additive property second, or any property at
|
||||
* all if no additive property was found. It is doubtful that any
|
||||
* use case requires more than two properties for sorting.
|
||||
*/
|
||||
|
||||
proplist = (propsort *)MALLOC(run * sizeof(propsort));
|
||||
|
||||
obp = obn;
|
||||
mval = 1;
|
||||
cval = 0.0;
|
||||
cval = aval = slop = 0.0;
|
||||
for (i = 0; i < run; i++) {
|
||||
merge_type = MERGE_NONE;
|
||||
ml = NULL;
|
||||
c = (char)0;
|
||||
for (p = 0;; p++) {
|
||||
vl = &(obp->instance.props[p]);
|
||||
if (vl->type == PROP_ENDLIST) break;
|
||||
@@ -3944,82 +4291,77 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
|
||||
}
|
||||
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
|
||||
if (kl == NULL) continue; /* Ignored property */
|
||||
if (kl->merge & MERGE_P_CRIT) {
|
||||
if (kl->merge & MERGE_P_CRIT)
|
||||
has_crit = TRUE;
|
||||
else if (kl->merge & (MERGE_P_ADD | MERGE_P_PAR)) {
|
||||
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
|
||||
(kl->type != vl->type))
|
||||
PromoteProperty(kl, vl);
|
||||
if (vl->type == PROP_INTEGER)
|
||||
cval = (double)vl->value.ival;
|
||||
else if (vl->type == PROP_STRING)
|
||||
PromoteProperty(kl, vl, obp, tp1);
|
||||
if (vl->type == PROP_INTEGER) {
|
||||
tval = (double)vl->value.ival;
|
||||
tslop = (double)kl->slop.ival;
|
||||
}
|
||||
else if (vl->type == PROP_STRING) {
|
||||
/* This is unlikely---no method to merge string properties! */
|
||||
cval = (double)vl->value.string[0]
|
||||
tval = (double)vl->value.string[0]
|
||||
+ (double)vl->value.string[1] / 10.0;
|
||||
else
|
||||
cval = vl->value.dval;
|
||||
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
|
||||
tslop = (double)0;
|
||||
}
|
||||
else {
|
||||
tval = vl->value.dval;
|
||||
tslop = kl->slop.dval;
|
||||
}
|
||||
|
||||
if (merge_type == MERGE_NONE)
|
||||
{
|
||||
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
|
||||
cval = tval;
|
||||
slop = tslop;
|
||||
}
|
||||
else {
|
||||
if ((c == (char)0) || (toupper(vl->key[0]) > c)) {
|
||||
c = toupper(vl->key[0]);
|
||||
aval = tval;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((c == (char)0) || (toupper(vl->key[0]) > c)) {
|
||||
if (vl->type == PROP_INTEGER) {
|
||||
aval = (double)vl->value.ival;
|
||||
c = toupper(vl->key[0]);
|
||||
}
|
||||
else if (vl->type == PROP_DOUBLE) {
|
||||
aval = vl->value.dval;
|
||||
c = toupper(vl->key[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (merge_type == MERGE_P_ADD) {
|
||||
proplist[i].value = cval * (double)mval;
|
||||
proplist[i].avalue = aval;
|
||||
proplist[i].slop = tslop;
|
||||
if (ml) ml->value.ival = 1;
|
||||
}
|
||||
else if (merge_type == MERGE_P_PAR) {
|
||||
proplist[i].value = cval / (double)mval;
|
||||
proplist[i].avalue = aval;
|
||||
proplist[i].slop = tslop;
|
||||
if (ml) ml->value.ival = 1;
|
||||
}
|
||||
else {
|
||||
/* If there are no additive values, then sort first by M */
|
||||
/* and second by aval */
|
||||
proplist[i].value = (double)mval;
|
||||
proplist[i].avalue = aval;
|
||||
proplist[i].slop = (double)0;
|
||||
}
|
||||
proplist[i].idx = i;
|
||||
proplist[i].ob = obp;
|
||||
obp = obp->next;
|
||||
}
|
||||
|
||||
if (has_crit == FALSE) {
|
||||
/* If no critical property was specified, then choose the first one found */
|
||||
/* and recalculate all the proplist values. */
|
||||
mval = 1;
|
||||
obp = obn;
|
||||
ml = NULL;
|
||||
merge_type = MERGE_NONE;
|
||||
for (i = 0; i < run; i++) {
|
||||
for (p = 0;; p++) {
|
||||
vl = &(obp->instance.props[p]);
|
||||
if (vl->type == PROP_ENDLIST) break;
|
||||
if (vl->key == NULL) continue;
|
||||
if ((*matchfunc)(vl->key, "M")) {
|
||||
mval = vl->value.ival;
|
||||
ml = vl;
|
||||
}
|
||||
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
|
||||
if (kl == NULL) continue; /* Ignored property */
|
||||
if (subs_crit == NULL)
|
||||
subs_crit = vl->key;
|
||||
if ((subs_crit != NULL) && (*matchfunc)(vl->key, subs_crit)) {
|
||||
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
|
||||
(kl->type != vl->type))
|
||||
PromoteProperty(kl, vl);
|
||||
if (vl->type == PROP_INTEGER)
|
||||
cval = (double)vl->value.ival;
|
||||
else if (vl->type == PROP_STRING)
|
||||
/* In case property is non-numeric, sort by dictionary order */
|
||||
cval = (double)vl->value.string[0]
|
||||
+ (double)vl->value.string[1] / 10.0;
|
||||
else
|
||||
cval = vl->value.dval;
|
||||
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
|
||||
}
|
||||
}
|
||||
if (merge_type == MERGE_P_ADD) {
|
||||
proplist[i].value = cval * (double)mval;
|
||||
if (ml) ml->value.ival = 1;
|
||||
}
|
||||
else if (merge_type == MERGE_P_PAR) {
|
||||
proplist[i].value = cval / (double)mval;
|
||||
if (ml) ml->value.ival = 1;
|
||||
}
|
||||
obp = obp->next;
|
||||
}
|
||||
}
|
||||
|
||||
obn = obp; /* Link from last property */
|
||||
|
||||
qsort(&proplist[0], run, sizeof(propsort), compsort);
|
||||
@@ -4483,12 +4825,12 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
|
||||
else if (vl == NULL || vl2 == NULL) {
|
||||
if (vl == NULL) {
|
||||
if (kl->type != vlist[p][j]->type)
|
||||
PromoteProperty(kl, vl2);
|
||||
PromoteProperty(kl, vl2, ob2, tp);
|
||||
vl = &dfltvl;
|
||||
}
|
||||
else {
|
||||
if (kl->type != vlist[p][i]->type)
|
||||
PromoteProperty(kl, vl);
|
||||
PromoteProperty(kl, vl, ob2, tp);
|
||||
vl2 = &dfltvl;
|
||||
}
|
||||
dfltvl.type = kl->type;
|
||||
@@ -4509,22 +4851,6 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
|
||||
}
|
||||
}
|
||||
|
||||
// Additive properties do not need to be matched, since
|
||||
// they can be combined. Critical paroperties must be
|
||||
// matched. Properties with no merge behavior must match.
|
||||
|
||||
ctype = clist[p][i];
|
||||
if (!(ctype & MERGE_S_CRIT)) {
|
||||
if ((series == TRUE) && (ctype & (MERGE_S_ADD | MERGE_S_PAR))) {
|
||||
pmatch++;
|
||||
continue;
|
||||
}
|
||||
if ((series == FALSE) && (ctype & (MERGE_P_ADD | MERGE_P_PAR))) {
|
||||
pmatch++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
switch(vl->type) {
|
||||
case PROP_DOUBLE:
|
||||
case PROP_VALUE:
|
||||
@@ -4963,8 +5289,8 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
|
||||
}
|
||||
|
||||
/* Promote properties as necessary to make sure they all match */
|
||||
if (kl1->type != vl1->type) PromoteProperty(kl1, vl1);
|
||||
if (kl2->type != vl2->type) PromoteProperty(kl2, vl2);
|
||||
if (kl1->type != vl1->type) PromoteProperty(kl1, vl1, tc1, tp1);
|
||||
if (kl2->type != vl2->type) PromoteProperty(kl2, vl2, tc2, tp2);
|
||||
|
||||
/* If kl1 and kl2 types differ, choose one type to target. Prefer */
|
||||
/* double if either type is double, otherwise string. */
|
||||
@@ -4984,8 +5310,8 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
|
||||
else
|
||||
klt = kl1;
|
||||
|
||||
if (vl2->type != klt->type) PromoteProperty(klt, vl2);
|
||||
if (vl1->type != klt->type) PromoteProperty(klt, vl1);
|
||||
if (vl2->type != klt->type) PromoteProperty(klt, vl2, tc2, tp2);
|
||||
if (vl1->type != klt->type) PromoteProperty(klt, vl1, tc1, tp1);
|
||||
|
||||
if (vl1->type != vl2->type) {
|
||||
if (do_print && (vl1->type != vl2->type)) {
|
||||
@@ -5363,13 +5689,21 @@ PropertyMatch(struct objlist *ob1, int file1,
|
||||
/* WIP---Check for no-connect pins in merged devices on both sides. */
|
||||
/* Both sides should either have no-connects marked, or neither. */
|
||||
/* (Permutable pins may need to be handled correctly. . . */
|
||||
|
||||
for (tp1 = ob1, tp2 = ob2; (tp1 != NULL) && tp1->type >= FIRSTPIN &&
|
||||
(tp2 != NULL) && tp2->type >= FIRSTPIN; tp1 = tp1->next, tp2 = tp2->next)
|
||||
{
|
||||
struct objlist *node1, *node2;
|
||||
|
||||
node1 = Circuit1->nodename_cache[tp1->node];
|
||||
node2 = Circuit2->nodename_cache[tp2->node];
|
||||
if (file1 == Circuit1->file)
|
||||
node1 = Circuit1->nodename_cache[tp1->node];
|
||||
else
|
||||
node1 = Circuit2->nodename_cache[tp1->node];
|
||||
|
||||
if (file2 == Circuit1->file)
|
||||
node2 = Circuit1->nodename_cache[tp2->node];
|
||||
else
|
||||
node2 = Circuit2->nodename_cache[tp2->node];
|
||||
|
||||
if (node1->instance.flags != node2->instance.flags)
|
||||
{
|
||||
@@ -5863,6 +6197,7 @@ int ResolveAutomorphsByProperty()
|
||||
if ((E2->graph != E1->graph) && (E2->hashval == newhash)) {
|
||||
E2->hashval = orighash;
|
||||
C2--;
|
||||
if (C2 == C1) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5871,6 +6206,7 @@ int ResolveAutomorphsByProperty()
|
||||
if ((E2->graph == E1->graph) && (E2->hashval == newhash)) {
|
||||
E2->hashval = orighash;
|
||||
C1--;
|
||||
if (C1 == C2) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6580,7 +6916,7 @@ struct nlist *addproxies(struct hashlist *p, void *clientdata)
|
||||
/* But if the target cell instance has proxy(no pins), then reuse
|
||||
* the record and modify it.
|
||||
*/
|
||||
if (!strcmp(ob->name, "proxy(no pins)")) {
|
||||
if (ob && !strcmp(ob->name, "proxy(no pins)")) {
|
||||
obn = ob;
|
||||
FREE(ob->name);
|
||||
obn->name = (char *)MALLOC(strlen(ob->instance.name)
|
||||
|
||||
+571
-528
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -43,7 +43,8 @@ extern int PropertyMerge(char *name, int fnum, char *key, int merge_type,
|
||||
int merge_mask);
|
||||
extern void ResolveProperties(char *name1, int file1, char *name2, int file2);
|
||||
extern void CopyProperties(struct objlist *obj_to, struct objlist *obj_from);
|
||||
extern int PromoteProperty(struct property *, struct valuelist *);
|
||||
extern int PromoteProperty(struct property *, struct valuelist *,
|
||||
struct objlist *, struct nlist *);
|
||||
extern int SetPropertyDefault(struct property *, struct valuelist *);
|
||||
extern struct objlist *LinkProperties(char *model, struct keyvalue *topptr);
|
||||
extern int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
|
||||
@@ -149,6 +150,7 @@ extern int NoOutput; /* set this to 1 to disable stdout output */
|
||||
extern int Composition; /* direction of composition */
|
||||
extern int UnixWildcards; /* TRUE if *,?,{},[] only; false if full REGEXP */
|
||||
extern int GlobalParallelNone; /* If TRUE, don't parallel combine any cells */
|
||||
extern int GlobalParallelOpen; /* If TRUE, parallel combine cells w/no-connects */
|
||||
/* magic internal flag to restrict searches to recently placed cells */
|
||||
extern int QuickSearch;
|
||||
/* does re"CellDef"ing a cell add to it or overwrite it??? */
|
||||
|
||||
+89
-11
@@ -352,7 +352,7 @@ proc netgen::convert_to_json {filename lvs_final} {
|
||||
close $fjson
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------
|
||||
#-----------------------------------------------------------------------
|
||||
# Define the "lvs" command as a way of calling the netgen options
|
||||
# for standard compare, essentially the same as the old "netcomp"
|
||||
# standalone program.
|
||||
@@ -361,14 +361,20 @@ proc netgen::convert_to_json {filename lvs_final} {
|
||||
# although if the cells have not been read in yet, then the
|
||||
# original syntax of filename or {filename cellname} is required.
|
||||
#
|
||||
# "args" is passed to verify and may therefore contain only the
|
||||
# value "-list" or nothing. If "-list", then output is returned
|
||||
# as a nested list.
|
||||
#----------------------------------------------------------------
|
||||
# "args" may be "-list", "-json", or "-blackbox".
|
||||
# "-list" returns output as a nested list.
|
||||
# "-json" creates a .json-format output file in addition to stdout.
|
||||
# "-blackbox" treats empty cells as black-box entries.
|
||||
# "-noflatten={list}" is a list of cells not to flatten if mismatched.
|
||||
# i.e., the cells are expected to match and any mismatch cannot be
|
||||
# expected to be resolved by flattening the contents of the mismatched
|
||||
# cells.
|
||||
#-----------------------------------------------------------------------
|
||||
|
||||
proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
set dolist 0
|
||||
set dojson 0
|
||||
set noflat {}
|
||||
foreach arg $args {
|
||||
if {$arg == "-list"} {
|
||||
puts stdout "Generating list result"
|
||||
@@ -382,6 +388,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
} elseif {$arg == "-blackbox"} {
|
||||
puts stdout "Treating empty subcircuits as black-box cells"
|
||||
netgen::model blackbox on
|
||||
} elseif {[string first "-noflatten=" $arg] == 0} {
|
||||
set value [string range $arg 11 end]
|
||||
# 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 {open $value r} fnf]} {
|
||||
while {[gets $fnf line] >= 0} {
|
||||
if {[lindex $line 0] != "#"} {
|
||||
foreach cell $line {
|
||||
lappend noflat $cell
|
||||
}
|
||||
}
|
||||
}
|
||||
close $fnf
|
||||
} else {
|
||||
puts stderr "Cannot open file $value for reading cell list."
|
||||
}
|
||||
} else {
|
||||
set noflat [string trim $value \"\{\}]
|
||||
}
|
||||
if {[llength $noflat] > 0} {
|
||||
puts stdout "Will not flatten these subcells: $noflat"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,6 +531,7 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
return
|
||||
}
|
||||
set properr {}
|
||||
set matcherr {}
|
||||
set pinsgood 0
|
||||
while {$endval != {}} {
|
||||
if {$dolist == 1} {
|
||||
@@ -529,9 +560,30 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
|
||||
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||
if {[netgen::print queue] != {}} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
if {([lsearch $noflat [lindex $endval 0]] == -1) &&
|
||||
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
} else {
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
# 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 {
|
||||
# Match pins
|
||||
@@ -554,9 +606,32 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
} else {
|
||||
# Flatten the non-matching subcircuit (but not the top-level cells)
|
||||
if {[netgen::print queue] != {}} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
if {([lsearch $noflat [lindex $endval 1]] == -1) &&
|
||||
([lsearch $noflat [lindex $endval 1]] == -1)} {
|
||||
netgen::log put " Flattening non-matched subcircuits $endval"
|
||||
netgen::flatten class "[lindex $endval 0] $fnum1"
|
||||
netgen::flatten class "[lindex $endval 1] $fnum2"
|
||||
} else {
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
netgen::log put " Continuing with black-boxed subcircuits $endval"
|
||||
lappend matcherr [lindex $endval 0]
|
||||
# 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
|
||||
@@ -579,6 +654,9 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out} args} {
|
||||
if {$properr != {}} {
|
||||
netgen::log put "The following cells had property errors: $properr\n"
|
||||
}
|
||||
if {$matcherr != {}} {
|
||||
netgen::log put "The following subcells failed to match: $matcherr\n"
|
||||
}
|
||||
if {$dolog} {
|
||||
netgen::log end
|
||||
}
|
||||
|
||||
+46
-12
@@ -2457,8 +2457,7 @@ _netcmp_run(ClientData clientData,
|
||||
automorphisms = ResolveAutomorphsByProperty();
|
||||
if (automorphisms == 0)
|
||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||
else {
|
||||
|
||||
else if (automorphisms > 0) {
|
||||
// Next, attempt to resolve automorphisms uniquely by
|
||||
// using the pin names
|
||||
automorphisms = ResolveAutomorphsByPin();
|
||||
@@ -2466,14 +2465,17 @@ _netcmp_run(ClientData clientData,
|
||||
|
||||
if (automorphisms == 0)
|
||||
Fprintf(stdout, "Netlists match uniquely.\n");
|
||||
else
|
||||
else if (automorphisms > 0) {
|
||||
// Anything left is truly indistinguishable
|
||||
Fprintf(stdout, "Netlists match with %d symmetr%s.\n",
|
||||
automorphisms, (automorphisms == 1) ? "y" : "ies");
|
||||
|
||||
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
||||
if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n");
|
||||
else Fprintf(stdout, "Circuits match correctly.\n");
|
||||
while ((automorphisms = ResolveAutomorphisms()) > 0);
|
||||
}
|
||||
if (automorphisms == -1)
|
||||
Fprintf(stdout, "Netlists do not match.\n");
|
||||
else
|
||||
Fprintf(stdout, "Circuits match correctly.\n");
|
||||
}
|
||||
if (PropertyErrorDetected) {
|
||||
Fprintf(stdout, "There were property errors.\n");
|
||||
@@ -2827,11 +2829,13 @@ _netcmp_equate(ClientData clientData,
|
||||
char *name1 = NULL, *name2 = NULL, *optstart;
|
||||
struct nlist *tp1, *tp2, *SaveC1, *SaveC2;
|
||||
struct objlist *ob1, *ob2;
|
||||
struct ElementClass *saveEclass = NULL;
|
||||
struct NodeClass *saveNclass = NULL;
|
||||
int file1, file2;
|
||||
int i, l1, l2, ltest, lent, dolist = 0;
|
||||
int i, l1, l2, ltest, lent, dolist = 0, doforce = 0;
|
||||
Tcl_Obj *tobj1, *tobj2, *tobj3;
|
||||
|
||||
if (objc > 1) {
|
||||
while (objc > 1) {
|
||||
optstart = Tcl_GetString(objv[1]);
|
||||
if (*optstart == '-') optstart++;
|
||||
if (!strcmp(optstart, "list")) {
|
||||
@@ -2839,6 +2843,13 @@ _netcmp_equate(ClientData clientData,
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else if (!strcmp(optstart, "force")) {
|
||||
doforce = 1;
|
||||
objv++;
|
||||
objc--;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if ((objc != 2) && (objc != 4) && (objc != 6)) {
|
||||
@@ -2996,6 +3007,12 @@ _netcmp_equate(ClientData clientData,
|
||||
break;
|
||||
|
||||
case PINS_IDX:
|
||||
if ((ElementClasses != NULL) && (doforce == TRUE)) {
|
||||
saveEclass = ElementClasses;
|
||||
saveNclass = NodeClasses;
|
||||
ElementClasses = NULL;
|
||||
NodeClasses = NULL;
|
||||
}
|
||||
if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) {
|
||||
if (CurrentCell == NULL) {
|
||||
Fprintf(stderr, "Equate elements: no current cell.\n");
|
||||
@@ -3058,6 +3075,12 @@ _netcmp_equate(ClientData clientData,
|
||||
/* Recover temporarily set global variables (see above) */
|
||||
Circuit1 = SaveC1;
|
||||
Circuit2 = SaveC2;
|
||||
|
||||
/* Recover ElementClasses if forcing pins on mismatched circuits */
|
||||
if (doforce == TRUE) {
|
||||
ElementClasses = saveEclass;
|
||||
NodeClasses = saveNclass;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -3320,10 +3343,10 @@ _netcmp_property(ClientData clientData,
|
||||
int result, index, idx2;
|
||||
|
||||
char *suboptions[] = {
|
||||
"integer", "double", "value", "string", NULL
|
||||
"integer", "double", "value", "string", "expression", NULL
|
||||
};
|
||||
enum SubOptionIdx {
|
||||
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX
|
||||
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX, EXPRESSION_IDX
|
||||
};
|
||||
|
||||
/* Note: "merge" has been deprecated, but kept for backwards compatibility. */
|
||||
@@ -3417,9 +3440,15 @@ _netcmp_property(ClientData clientData,
|
||||
GlobalParallelNone = FALSE;
|
||||
SetParallelCombine(TRUE);
|
||||
}
|
||||
else if (!strcmp(Tcl_GetString(objv[2]), "connected")) {
|
||||
GlobalParallelOpen = FALSE;
|
||||
}
|
||||
else if (!strcmp(Tcl_GetString(objv[2]), "open")) {
|
||||
GlobalParallelOpen = TRUE;
|
||||
}
|
||||
else {
|
||||
Tcl_SetResult(interp, "Bad option, should be property parallel none|all",
|
||||
NULL);
|
||||
Tcl_SetResult(interp, "Bad option, should be property parallel "
|
||||
"none|all|connected", NULL);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
@@ -3706,6 +3735,11 @@ _netcmp_property(ClientData clientData,
|
||||
PropertyString(tp->name, fnum,
|
||||
Tcl_GetString(tobj1), ival, NULL);
|
||||
break;
|
||||
case EXPRESSION_IDX:
|
||||
PropertyString(tp->name, fnum,
|
||||
Tcl_GetString(tobj1), 0,
|
||||
Tcl_GetString(tobj3));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user