Compare commits

...
25 Commits
Author SHA1 Message Date
Tim Edwards a13491e358 Merge branch 'master' into netgen-1.5 2021-05-25 03:00:09 -04:00
Tim Edwards 2ac2a3b6f8 Fixed a bad assumption; PropertyOptimize allows devices with
different additive properties (like transistor width) to combine;
this is totally wrong and may have been left over from code written
before the routine was split into simple combinations (add similar
devices) and agressive combinations (e.g., add widths together).
2021-05-24 10:05:10 -04:00
Tim Edwards 1d05b8b676 Merge branch 'master' into netgen-1.5 2021-05-20 03:00:31 -04:00
Tim Edwards 759d63cea9 Changed a comment in the code that no longer applies after the last
modification.
2021-05-19 16:42:03 -04:00
Tim Edwards 476da015f0 Added support for expression parameters to be added to a device
class during setup, for the purpose of generating some derived
value that is used for merging and sorting, such as area = l*w.
Note that this likely needs adjusting so that the expression is
evaluated but not replaced for the purpose of sorting, since the
values to the parameter may change after parallel and series
merging.
2021-05-19 16:27:52 -04:00
Tim Edwards 4aa8a72769 Corrected an error found in ResolveAutomorphsByProperty which would
cause inexplicable output in case of a property error by showing a
netlist topography error instead of a property error (but the output
shows that the netlists match, and there is no reporting of any
property errors).  This error was discovered while implementing a
better sorting method for parallel combination.  The improved method
sorts on two properties rather than one, and so should not fall into
the error where, say, devices are sorted on W but have different L
for a device like a capacitor where no "critical" property is
specified (and other similar cases, although that is a common one).
2021-05-19 14:56:38 -04:00
Tim Edwards e97d6f1aeb Merge branch 'master' into netgen-1.5 2021-05-14 03:00:10 -04:00
Tim Edwards 47e7367c26 Updated the versio with the last commit. 2021-05-13 15:58:49 -04:00
Tim Edwards 362fb7120b Modified the behavior of the parallel combination of devices with
"similar no connect" pins so that it is not done on top-level
netlists.  This is mainly to deal with the problem where connections
that would normally be pins (but may have, for example, been
connected to a voltage source in a schematic that was deleted because
it was not a netlistable device) are treated as no-connects.
The parallel combination of devices with similar no-connects can then
differ between netlists that differ in describing transistors as
fingered vs. individual devices.  This is an obscure case, but the
output of netgen then becomes not only wrong but difficult to
understand what happened, so it is better to avoid.
2021-05-13 15:50:12 -04:00
Tim Edwards fb4759a8cd Merge branch 'master' into netgen-1.5 2021-05-04 03:00:10 -04:00
Tim Edwards 92e8fed8be Corrected an error in checking merge flags when doing sorting of
parallel and series devices.  This was a fairly major error
undermining the property sorting (the case where the same number
of devices are in parallel in both circuits and need to be sorted
by, e.g., width, prior to checking for matches).
2021-05-03 11:20:44 -04:00
Tim Edwards 45d4004d43 Merge branch 'master' into netgen-1.5 2021-05-02 03:00:09 -04:00
Tim Edwards 01b2484be3 Updated version. 2021-05-01 22:49:59 -04:00
Tim Edwards fe47d84ace Missed a corner case in the series combination code where a device
has been shuffled to the beginning of the cell, which requires
different handling.
2021-05-01 22:48:59 -04:00
Tim Edwards 0b014e6efd Merge branch 'master' into netgen-1.5 2021-03-20 03:00:09 -04:00
Tim Edwards 0a2adeb593 Modified the "flatten" command to allow class "module" to be
flattened.  This is marginally useful---since the "module" class
is a black box, it is essentially the same as using the "ignore"
command.
2021-03-19 13:57:11 -04:00
Tim Edwards 330b34139c Slight modification to print the list of cells being forced to
match for both the filename option and immediate list.
2021-03-19 10:51:42 -04:00
Tim Edwards 62d0352149 Modified the "-noflatten" command option to "lvs" so that it can be
given a filename as a value in addition to being passed a list of
cells directly.
2021-03-19 09:41:42 -04:00
Tim Edwards 9ca713dfd9 Merge branch 'master' into netgen-1.5 2021-03-18 03:00:29 -04:00
Tim Edwards b1b04b8e87 Corrected a statement related to proxy pins that can cause a
segfault condition.
2021-03-17 16:31:22 -04:00
Tim Edwards 851a1f941e Merge branch 'master' into netgen-1.5 2021-03-06 03:00:09 -05:00
Tim Edwards 6b9d92de65 Corrected some pointer-to-int and int-to-pointer conversions to
keep the compiler happy.
2021-03-05 20:19:04 -05:00
Tim Edwards d763ad0e60 First and simplest (but most effective) cut at aligning the two
sides of the output when presenting side-by-side results.  This
matches up net or device names within a partition.  Where net or
device names match, the contents are also aligned on either side.
I attempted to also do a "best match" of contents between sides,
but as this involves a lot of analyzing the contents, it is very
computationally expensive, and so the code has been disabled.  It
could be added back in as an option.  There are also various ways
to optimize it for speed.
2021-03-05 17:23:22 -05:00
Tim Edwards fd72e24a86 Corrected an error in the code that handles no-connects as valid
pins for enabling parallel combinations, which could cause a crash.
Added a "-force" option to "equate pins" to allow pins to be matched
even on subcircuits that did not correctly match;  this was done in
conjuction with an extra option to the "lvs" command "-noflatten="
to pass a list of cellname to not be flattened even if they do not
match.  This is generally discouraged, as it prevents netgen from
resolving differences between layout and schematic hierarchy, but it
can be useful for checking that the hierarchy above a certain cell
is correct, given that if a subcell is really unmatched, then its
errors will keep propagating up the top level, making additional
errors hard to diagnose.
2021-03-05 11:32:17 -05:00
Tim Edwards 20077d3d56 Modifed the handling of parallelized no-connects so that the behavior
can be turned on or off from the setup using "property parallel open"
to allow parallelizing devices with no-connect pins vs. "property
parallel connected" to only allow parallelizing of devices with all
pins connected.
2021-03-03 17:31:45 -05:00
7 changed files with 1152 additions and 659 deletions
+1 -1
View File
@@ -1 +1 @@
1.5.170
1.5.178
+1 -1
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}