Extended the series/parallel merging setup commands to include

the possibility that a device (e.g., resistor or capacitor) may
not be a semiconductor device (in other words, a parasitic or
ideal device), and therefore uses "value" but not width and
length, and therefore "value" is a critical property to merge
both in series and parallel.  Corrected the series/parallel
network optimization to prevent it from setting both M and S
records > 1 on the same device (which is ambiguous).  To try
to get number of devices to match, where there are both series
and parallel devices, they will be merged across the critical
property early (before property matching).
This commit is contained in:
Tim Edwards 2018-11-18 13:04:57 -05:00
parent 079e0ab5d3
commit 2cdf3c450f
7 changed files with 664 additions and 315 deletions

View File

@ -1391,7 +1391,7 @@ SurveyCell(struct nlist *tc, struct hashdict *compdict, int file1, int file2, in
/* */
/* If there is a mismatch between instances of low- */
/* level devices, determine if the mismatches can be */
/* resolved by parallel/serial combining, according to */
/* resolved by parallel/series combining, according to */
/* combination rules. */
/* */
/* Return the number of modifications made. */

View File

@ -500,7 +500,7 @@ void SummarizeDataStructures(void)
if (E->graph == Circuit1->file) cell1++;
else cell2++;
}
Printf("Circuit 1 contains %d elements, Circuit 2 contains %d elements.",
Printf("Circuit 1 contains %d devices, Circuit 2 contains %d devices.",
cell1, cell2);
if (cell1 != cell2) Printf(" *** MISMATCH ***");
Printf("\n");
@ -518,12 +518,12 @@ void SummarizeDataStructures(void)
if (N->elementlist == NULL) orphan2++;
}
}
Printf("Circuit 1 contains %d nodes, Circuit 2 contains %d nodes.",
Printf("Circuit 1 contains %d nets, Circuit 2 contains %d nets.",
cell1, cell2);
if (cell1 != cell2) Printf(" *** MISMATCH ***");
Printf("\n");
if (orphan1 || orphan2) {
Printf("Circuit 1 contains %d orphan nodes, Circuit 2 contains %d orphans.");
Printf("Circuit 1 contains %d orphan nets, Circuit 2 contains %d orphans.");
if (orphan1 != orphan2) Printf(" *** MISMATCH ***");
Printf("\n");
}
@ -2076,7 +2076,7 @@ struct Node *CreateNodeList(char *name, short graph)
* in sequence to link up 'subelement' field of ElementList,
* then 'node' field of NodeList structures.
*
* Return the number of devices combined by serial/parallel merging
* Return the number of devices combined by series/parallel merging
*/
int CreateLists(char *name, short graph)
@ -2102,7 +2102,7 @@ int CreateLists(char *name, short graph)
return 0;
}
/* Parallel and serial combinations. Run until networks of */
/* Parallel and series combinations. Run until networks of */
/* devices are resolved into a single device with the network */
/* represented by a number of property records. */
@ -2112,19 +2112,21 @@ int CreateLists(char *name, short graph)
total += pcnt;
if (ppass > 0 && pcnt == 0) break;
for (spass = 0; ; spass++) {
scnt = CombineSerial(name, graph);
scnt = CombineSeries(name, graph);
total += scnt;
if (scnt == 0) break;
}
if (spass == 0) break;
}
/* Uncomment this for series/parallel network diagnostics */
/* DumpNetworkAll(name, graph); */
Elements = CreateElementList(name, graph);
Nodes = CreateNodeList(name, graph);
if (LookupElementList == NULL) return total;
ElementScan = NULL;
NListScan = NULL; /* just to stop the compiler from bitching */
NListScan = NULL;
for (ob = tp->cell; ob != NULL; ob = ob->next) {
if (ob->type == FIRSTPIN) {
if (ElementScan == NULL) ElementScan = Elements;
@ -2221,7 +2223,7 @@ struct Node *CreateNodeList(char *name, short graph)
* in sequence to link up 'subelement' field of ElementList,
* then 'node' field of NodeList structures.
*
* Return the number of devices combined by serial/parallel merging
* Return the number of devices combined by series/parallel merging
*/
int CreateLists(char *name, short graph)
@ -2250,7 +2252,7 @@ int CreateLists(char *name, short graph)
ConnectAllNodes(name, graph);
/* Parallel and serial combinations. Run until networks of */
/* Parallel and series combinations. Run until networks of */
/* devices are resolved into a single device with the network */
/* represented by a number of property records. */
@ -2260,12 +2262,14 @@ int CreateLists(char *name, short graph)
total += pcnt;
if (ppass > 0 && pcnt == 0) break;
for (spass = 0; ; spass++) {
scnt = CombineSerial(name, graph);
scnt = CombineSeries(name, graph);
total += scnt;
if (scnt == 0) break;
}
if (spass == 0) break;
}
/* Uncomment this for series/parallel network diagnostics */
/* DumpNetworkAll(name, graph); */
E = CreateElementList(name, graph);
N = CreateNodeList(name, graph);
@ -3464,7 +3468,7 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2, int dolist)
}
if (modified > 0) {
Printf("Circuit was modified by parallel/serial device merging.\n");
Printf("Circuit was modified by parallel/series device merging.\n");
Printf("New circuit summary:\n\n");
/* print preliminary statistics */
Printf("Contents of circuit 1: ");
@ -3685,18 +3689,19 @@ int Iterate(void)
/*--------------------------------------------------------------*/
/* Combine properties of ob1 starting at property idx1 up to */
/* property (idx1 + run1), where devices match critical serial */
/* property (idx1 + run1), where devices match critical series */
/* values and can be combined by summing over the "S" record. */
/*--------------------------------------------------------------*/
int serial_optimize(struct objlist *ob1, struct nlist *tp1, int idx1, int run1)
int series_optimize(struct objlist *ob1, struct nlist *tp1, int idx1,
int run1, int comb)
{
struct objlist *obn;
int i;
obn = ob1;
for (i = 0; i < idx1; i++) obn = obn->next;
return PropertyOptimize(obn, tp1, run1, TRUE);
return PropertyOptimize(obn, tp1, run1, TRUE, comb);
}
/*--------------------------------------------------------------*/
@ -3705,7 +3710,7 @@ int serial_optimize(struct objlist *ob1, struct nlist *tp1, int idx1, int run1)
/* idx2 to (idx2 + run2). run1 is always larger than run2. */
/*--------------------------------------------------------------*/
int serial_combine(struct objlist *ob1, struct nlist *tp1, int idx1, int run1,
int series_combine(struct objlist *ob1, struct nlist *tp1, int idx1, int run1,
struct objlist *ob2, struct nlist *tp2, int idx2, int run2)
{
struct objlist *obn, *obp;
@ -3717,11 +3722,6 @@ int serial_combine(struct objlist *ob1, struct nlist *tp1, int idx1, int run1,
obp = ob2;
for (i = 0; i < idx2; i++) obp = obp->next;
// for (j = 0; j < run2; j++) {
// for (i = 0; i < run1; i++) {
// }
// }
return changed;
}
@ -3747,18 +3747,18 @@ static int compsort(const void *p1, const void *p2)
/*--------------------------------------------------------------*/
/* Sort properties of ob1 starting at property idx1 up to */
/* property (idx1 + run). Use serial critical property for */
/* property (idx1 + run). Use series critical property for */
/* sorting. Multiply critical property by S before sort. */
/* ob1 is the record before the first property. */
/*--------------------------------------------------------------*/
void serial_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
{
struct objlist *obn, *obp;
propsort *proplist;
struct property *kl;
struct valuelist *vl;
int i, p, sval;
struct valuelist *vl, *sl;
int i, p, sval, merge_type;
double cval;
obn = ob1->next;
@ -3774,22 +3774,34 @@ void serial_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
sval = 1;
cval = 0.0;
for (i = 0; i < run; i++) {
merge_type = MERGE_NONE;
for (p = 0;; p++) {
vl = &(obp->instance.props[p]);
if (vl->type == PROP_ENDLIST) break;
if (vl->key == NULL) continue;
if (!strcmp(vl->key, "S"))
if (!strcmp(vl->key, "S")) {
sval = vl->value.ival;
sl = vl;
}
else {
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
if (kl && (kl->merge == MERGE_SER_CRIT))
if (kl && (kl->merge & MERGE_S_CRIT)) {
if (vl->type == PROP_INTEGER)
cval = (double)vl->value.ival;
else
cval = vl->value.dval;
merge_type = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
}
}
}
proplist[i].value = (double)sval * cval;
if (merge_type == MERGE_S_ADD) {
proplist[i].value = cval * (double)sval;
sl->value.ival = 1;
}
else if (merge_type == MERGE_S_PAR) {
proplist[i].value = cval / (double)sval;
sl->value.ival = 1;
}
proplist[i].idx = i;
proplist[i].ob = obp;
obp = obp->next;
@ -3816,14 +3828,15 @@ void serial_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
/* record. */
/*--------------------------------------------------------------*/
int parallel_optimize(struct objlist *ob1, struct nlist *tp1, int idx1, int run1)
int parallel_optimize(struct objlist *ob1, struct nlist *tp1, int idx1,
int run1, int comb)
{
struct objlist *obn;
int i;
obn = ob1;
for (i = 0; i < idx1; i++) obn = obn->next;
return PropertyOptimize(obn, tp1, run1, FALSE);
return PropertyOptimize(obn, tp1, run1, FALSE, comb);
}
/*--------------------------------------------------------------*/
@ -3844,11 +3857,6 @@ int parallel_combine(struct objlist *ob1, struct nlist *tp1, int idx1, int run1,
obp = ob2;
for (i = 0; i < idx2; i++) obp = obp->next;
// for (j = 0; j < run2; j++) {
// for (i = 0; i < run1; i++) {
// }
// }
return changed;
}
@ -3864,8 +3872,8 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
struct objlist *obn, *obp;
propsort *proplist;
struct property *kl;
struct valuelist *vl;
int i, p, sval, has_crit = FALSE;
struct valuelist *vl, *ml;
int i, p, mval, merge_type, has_crit = FALSE;
char *subs_crit = NULL;
double cval;
@ -3887,18 +3895,21 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
proplist = (propsort *)MALLOC(run * sizeof(propsort));
obp = obn;
sval = 1;
mval = 1;
cval = 0.0;
for (i = 0; i < run; i++) {
merge_type = MERGE_NONE;
for (p = 0;; p++) {
vl = &(obp->instance.props[p]);
if (vl->type == PROP_ENDLIST) break;
if (vl->key == NULL) continue;
if (!strcmp(vl->key, "S"))
sval = vl->value.ival;
if (!strcmp(vl->key, "M")) {
mval = vl->value.ival;
ml = vl;
}
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
if (kl == NULL) continue; /* Ignored property */
if (kl->merge == MERGE_ADD_CRIT) {
if (kl->merge & MERGE_P_CRIT) {
has_crit = TRUE;
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
(kl->type != vl->type))
@ -3911,9 +3922,17 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
+ (double)vl->value.string[1] / 10.0;
else
cval = vl->value.dval;
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
}
}
proplist[i].value = (double)sval * cval;
if (merge_type == MERGE_P_ADD) {
proplist[i].value = cval * (double)mval;
ml->value.ival = 1;
}
else if (merge_type == MERGE_P_PAR) {
proplist[i].value = cval / (double)mval;
ml->value.ival = 1;
}
proplist[i].idx = i;
proplist[i].ob = obp;
obp = obp->next;
@ -3922,15 +3941,18 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
if (has_crit == FALSE) {
/* If no critical property was specified, then choose the first one found */
/* and recalculate all the proplist values. */
sval = 1;
mval = 1;
obp = obn;
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 (!strcmp(vl->key, "S"))
sval = vl->value.ival;
if (!strcmp(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)
@ -3947,9 +3969,17 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
+ (double)vl->value.string[1] / 10.0;
else
cval = vl->value.dval;
merge_type = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
}
}
proplist[i].value = (double)sval * cval;
if (merge_type == MERGE_P_ADD) {
proplist[i].value = cval * (double)mval;
ml->value.ival = 1;
}
else if (merge_type == MERGE_P_PAR) {
proplist[i].value = cval / (double)mval;
ml->value.ival = 1;
}
obp = obp->next;
}
}
@ -3970,11 +4000,11 @@ void parallel_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
}
/*--------------------------------------------------------------*/
/* Attempt to match two property lists representing serial/ */
/* Attempt to match two property lists representing series/ */
/* parallel combinations of devices. Where the number of */
/* devices is not equal, try to reduce the one with more */
/* devices to match. If there are the same number of parallel */
/* or serial devices, check if they match better by swapping. */
/* or series devices, check if they match better by swapping. */
/* The goal is to get two property lists that can be checked by */
/* 1-to-1 matching in PropertyMatch(). */
/*--------------------------------------------------------------*/
@ -3987,7 +4017,7 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
int p, n;
int run, cnt, idx1, idx2, max1, max2;
int icount1, icount2, changed;
int icount1, icount2, changed, result;
char *netwk1, *netwk2;
char *c1, *c2;
struct valuelist *vl;
@ -4001,6 +4031,10 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
iterations++;
changed = 0;
/* Remove group tags if they no longer contain series devices */
while (remove_group_tags(pre1));
while (remove_group_tags(pre2));
// How many property records are there?
// If there is only one property record in each instance then
// there is nothing to be sorted.
@ -4061,8 +4095,8 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
strcat(netwk2, "D");
}
// Printf("Diagnostic: network1 is \"%s\" "
// "network2 is \"%s\"\n", netwk1, netwk2);
/* Printf("Diagnostic: network1 is \"%s\" "
"network2 is \"%s\"\n", netwk1, netwk2); */
/* Method to resolve any network to the largest solution that */
/* matches both sides. Use the netwk1, netwk2 strings to determine */
@ -4075,7 +4109,7 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
/* in the other. If non-summing parameters of interest match, */
/* then merge all devices that can be merged until both sides */
/* have the same number of devices. */
/* 2) Find serial devices that have more elements in one circuit */
/* 2) Find series devices that have more elements in one circuit */
/* than in the other. If non-summing parameters of interest */
/* match, then merge all devices that can be merged until */
/* both sides have the same number of devices. */
@ -4130,8 +4164,19 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
// Printf("Circuit 1 has %d devices in parallel while circuit 2 has %d\n",
// (max1 == 1) ? 0 : max1, (max2 == 1) ? 0 : max2);
if (max1 > 1) changed += parallel_optimize(ob1, tp1, idx1, max1);
if (max2 > 1) changed += parallel_optimize(ob2, tp2, idx2, max2);
if (max1 > 1) {
result = parallel_optimize(ob1, tp1, idx1, max1, FALSE);
if (result > 0) changed += result;
else if ((result < 0) && (max1 > max2))
changed += series_optimize(ob1, tp1, idx1, max1, TRUE);
}
if (max2 > 1) {
result = parallel_optimize(ob2, tp2, idx2, max2, FALSE);
if (result > 0) changed += result;
else if ((result < 0) && (max2 > max1))
changed += series_optimize(ob2, tp2, idx2, max2, TRUE);
}
if (changed > 0) {
FREE(netwk1);
@ -4164,9 +4209,9 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
}
}
/* Case 2: Serial devices with more elements in one circuit */
/* Case 2: Series devices with more elements in one circuit */
/* Find the largest group of serial devices in circuit1 */
/* Find the largest group of series devices in circuit1 */
run = 0;
cnt = 0;
idx1 = 0;
@ -4190,7 +4235,7 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
if (*c1 == '\0') break;
}
/* Find the largest group of serial devices in circuit2 */
/* Find the largest group of series devices in circuit2 */
run = 0;
cnt = 0;
idx2 = 0;
@ -4218,10 +4263,21 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
// Printf("Circuit 1 has %d devices in series while circuit 2 has %d\n",
// (max1 == 1) ? 0 : max1, (max2 == 1) ? 0 : max2);
if (max1 > 1) changed += serial_optimize(ob1, tp1, idx1, max1);
if (max2 > 1) changed += serial_optimize(ob2, tp2, idx2, max2);
if (max1 > 1) {
result = series_optimize(ob1, tp1, idx1, max1, FALSE);
if (result > 0) changed += result;
else if ((result < 0) && (max1 > max2))
changed += parallel_optimize(ob1, tp1, idx1, max1, TRUE);
}
if (max2 > 1) {
result = series_optimize(ob2, tp2, idx2, max2, FALSE);
if (result > 0) changed += result;
else if ((result < 0) && (max2 > max1))
changed += parallel_optimize(ob2, tp2, idx2, max2, TRUE);
}
if (changed > 0) {
FREE(netwk1);
FREE(netwk2);
continue;
@ -4229,21 +4285,21 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
if (max1 > 1) {
/* Re-link first property, because it may have been moved */
serial_sort(pre1, tp1, idx1, max1);
series_sort(pre1, tp1, idx1, max1);
ob1 = pre1->next;
}
if (max2 > 1) {
/* Re-link first property, because it may have been moved */
serial_sort(pre2, tp2, idx2, max2);
series_sort(pre2, tp2, idx2, max2);
ob2 = pre2->next;
}
/* Do not run serial_combine until all other changes have been resolved */
/* Do not run series_combine until all other changes have been resolved */
if (changed == 0) {
if (max2 > max1)
changed += serial_combine(ob2, tp2, idx2, max2, ob1, tp1, idx1, max1);
changed += series_combine(ob2, tp2, idx2, max2, ob1, tp1, idx1, max1);
else if (max1 > max2)
changed += serial_combine(ob1, tp1, idx1, max1, ob2, tp2, idx2, max2);
changed += series_combine(ob1, tp1, idx1, max1, ob2, tp2, idx2, max2);
}
FREE(netwk1);
@ -4252,7 +4308,7 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
/* Continue looping until there are no further changes to be made */
}
if (iterations > 1)
Printf("No more changes can be made to serial/parallel networks.\n");
Printf("No more changes can be made to series/parallel networks.\n");
}
/*--------------------------------------------------------------*/
@ -4262,7 +4318,7 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
/* critical property (if defined), and merge devices with the */
/* same properties (by summing property "M" for devices) */
/* */
/* For final optimization, if run == 1 and M > 1, then merge */
/* For final optimization, if comb == 1 and M > 1, then merge */
/* the critical property over M and set M to 1. */
/* */
/* Return the number of devices modified. */
@ -4274,7 +4330,8 @@ typedef struct _proplink {
proplinkptr next;
} proplink;
int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int series,
int comb)
{
struct objlist *ob2, *obt;
struct property *kl, *m_rec, **plist;
@ -4283,11 +4340,13 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
int pcount, p, i, j, k, pmatch, ival, crit, ctype;
double dval;
static struct valuelist nullvl, dfltvl;
char multiple[2];
int changed = 0;
char multiple[2], other[2];
int changed = 0, fail = 0;
multiple[1] = '\0';
multiple[0] = (serial == TRUE) ? 'S' : 'M';
multiple[0] = (series == TRUE) ? 'S' : 'M';
other[1] = '\0';
other[0] = (series == TRUE) ? 'M' : 'S';
nullvl.type = PROP_INTEGER;
nullvl.value.ival = 0;
@ -4320,15 +4379,15 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
// To do: deal with possibility of multiple critical properties
// per instance?
if ((serial == FALSE) && (kl->merge == MERGE_ADD_CRIT ||
kl->merge == MERGE_PAR_CRIT)) {
if ((series == FALSE) && (kl->merge & MERGE_P_CRIT)) {
crit = kl->idx;
ctype = kl->merge;
ctype = kl->merge & (MERGE_P_ADD | MERGE_P_PAR);
}
else if ((serial == TRUE) && (kl->merge == MERGE_SER_CRIT)) {
else if ((series == TRUE) && (kl->merge & MERGE_S_CRIT)) {
crit = kl->idx;
ctype = MERGE_ADD_CRIT;
ctype = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
}
kl = (struct property *)HashNext(&(tp->propdict));
}
// Recast the linked list as an array
@ -4362,6 +4421,16 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
vlist[0][i] = vl;
}
}
if (kl == NULL) {
/* Prevent setting both M > 1 and S > 1 in any one */
/* device, as it is ambiguous. */
if ((*matchfunc)(vl->key, other)) {
if (vl->type == PROP_INTEGER)
if (vl->value.ival > 1)
fail = 1;
}
}
else if (kl != NULL) {
vlist[kl->idx][i] = vl;
}
@ -4381,174 +4450,181 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
}
// Now combine records with same properties by summing M (S).
for (i = 0; i < run - 1; i++) {
for (j = 1; j < run; j++) {
pmatch = 0;
for (p = 1; p < pcount; p++) {
kl = plist[p];
vl = vlist[p][i];
vl2 = vlist[p][j];
if (vl == NULL && vl2 == NULL) {
pmatch++;
continue;
}
if (comb == FALSE) {
for (i = 0; i < run - 1; i++) {
for (j = 1; j < run; j++) {
pmatch = 0;
for (p = 1; p < pcount; p++) {
kl = plist[p];
vl = vlist[p][i];
vl2 = vlist[p][j];
if (vl == NULL && vl2 == NULL) {
pmatch++;
continue;
}
// If either value is missing, it takes kl->pdefault
// and must apply promotions if necessary.
// If either value is missing, it takes kl->pdefault
// and must apply promotions if necessary.
else if (vl == NULL || vl2 == NULL) {
if (vl == NULL) {
if (kl->type != vlist[p][j]->type)
else if (vl == NULL || vl2 == NULL) {
if (vl == NULL) {
if (kl->type != vlist[p][j]->type)
PromoteProperty(kl, vl2);
}
else {
if (kl->type != vlist[p][i]->type)
}
else {
if (kl->type != vlist[p][i]->type)
PromoteProperty(kl, vl);
}
vl = &dfltvl;
dfltvl.type = kl->type;
switch (kl->type) {
case PROP_STRING:
}
vl = &dfltvl;
dfltvl.type = kl->type;
switch (kl->type) {
case PROP_STRING:
dfltvl.value.string = kl->pdefault.string;
break;
case PROP_INTEGER:
case PROP_INTEGER:
dfltvl.value.ival = kl->pdefault.ival;
break;
case PROP_DOUBLE:
case PROP_VALUE:
case PROP_DOUBLE:
case PROP_VALUE:
dfltvl.value.ival = kl->pdefault.ival;
break;
case PROP_EXPRESSION:
case PROP_EXPRESSION:
dfltvl.value.stack = kl->pdefault.stack;
break;
}
}
}
}
// Critical properties can be multiplied up by M (S) and do not
// need to match. May want a more nuanced comparison, though.
if (p == crit) {
pmatch++;
continue;
}
// Critical properties can be multiplied up by M (S) and do not
// need to match. May want a more nuanced comparison, though.
if (p == crit) {
pmatch++;
continue;
}
switch(vl->type) {
case PROP_DOUBLE:
case PROP_VALUE:
dval = 2 * fabs(vl->value.dval - vl2->value.dval)
switch(vl->type) {
case PROP_DOUBLE:
case PROP_VALUE:
dval = 2 * fabs(vl->value.dval - vl2->value.dval)
/ (vl->value.dval + vl2->value.dval);
if (dval <= kl->slop.dval) pmatch++;
break;
case PROP_INTEGER:
ival = abs(vl->value.ival - vl2->value.ival);
if (ival <= kl->slop.ival) pmatch++;
break;
case PROP_STRING:
if ((*matchfunc)(vl->value.string, vl2->value.string)) pmatch++;
break;
if (dval <= kl->slop.dval) pmatch++;
break;
case PROP_INTEGER:
ival = abs(vl->value.ival - vl2->value.ival);
if (ival <= kl->slop.ival) pmatch++;
break;
case PROP_STRING:
if ((*matchfunc)(vl->value.string, vl2->value.string)) pmatch++;
break;
/* will not attempt to match expressions, but it could
* be done with some minor effort by matching each
* stack token and comparing those that are strings.
*/
}
}
if (pmatch == (pcount - 1)) {
// Sum M (S) (p == 0) records and remove one record
if (vlist[0][i] == NULL) {
// Add this to the end of the property record
// find ith record in ob
p = 0;
for (ob2 = ob; p != i; ob2 = ob2->next, p++);
// Count entries, add one, reallocate
for (p = 0;; p++) {
vl = &ob2->instance.props[p];
if (vl->type == PROP_ENDLIST) break;
/* will not attempt to match expressions, but it could
* be done with some minor effort by matching each
* stack token and comparing those that are strings.
*/
}
p++;
newvlist = (struct valuelist *)CALLOC(p + 1,
}
if (fail == 1) {
/* If failure due to need to prevent M > 1 and S > 1 on */
/* the same device, then do not do optimization. If */
/* optimization could have been done, return -1. */
if (pmatch == (pcount - 1))
changed = -1;
}
else if (pmatch == (pcount - 1)) {
// Sum M (S) (p == 0) records and remove one record
if (vlist[0][i] == NULL) {
// Add this to the end of the property record
// find ith record in ob
p = 0;
for (ob2 = ob; p != i; ob2 = ob2->next, p++);
// Count entries, add one, reallocate
for (p = 0;; p++) {
vl = &ob2->instance.props[p];
if (vl->type == PROP_ENDLIST) break;
}
p++;
newvlist = (struct valuelist *)CALLOC(p + 1,
sizeof(struct valuelist));
// Move end record forward
vl = &newvlist[p];
vl->key = NULL;
vl->type = PROP_ENDLIST;
vl->value.ival = 0;
// Move end record forward
vl = &newvlist[p];
vl->key = NULL;
vl->type = PROP_ENDLIST;
vl->value.ival = 0;
// Add "M" ("S") record behind it
vl = &newvlist[--p];
vl->key = strsave(multiple);
vl->type = PROP_INTEGER;
vl->value.ival = 1;
vlist[0][i] = vl;
// Copy the rest of the records and regenerate vlist
for (--p; p >= 0; p--) {
vl = &newvlist[p];
vl->key = ob2->instance.props[p].key;
vl->type = ob2->instance.props[p].type;
vl->value = ob2->instance.props[p].value;
// Add "M" ("S") record behind it
vl = &newvlist[--p];
vl->key = strsave(multiple);
vl->type = PROP_INTEGER;
vl->value.ival = 1;
vlist[0][i] = vl;
// Copy the rest of the records and regenerate vlist
for (--p; p >= 0; p--) {
vl = &newvlist[p];
vl->key = ob2->instance.props[p].key;
vl->type = ob2->instance.props[p].type;
vl->value = ob2->instance.props[p].value;
kl = (struct property *)HashLookup(vl->key, &(tp->propdict));
if (kl != NULL) vlist[kl->idx][i] = vl;
kl = (struct property *)HashLookup(vl->key, &(tp->propdict));
if (kl != NULL) vlist[kl->idx][i] = vl;
}
// Replace instance properties with the new list
FREE(ob2->instance.props);
ob2->instance.props = newvlist;
}
// Replace instance properties with the new list
FREE(ob2->instance.props);
ob2->instance.props = newvlist;
}
if (vlist[0][j] == NULL) {
vlist[0][j] = &nullvl; // Mark this position
vlist[0][i]->value.ival++;
}
else if (vlist[0][i]->value.ival > 0) {
vlist[0][i]->value.ival += vlist[0][j]->value.ival;
vlist[0][j]->value.ival = 0;
if (vlist[0][j] == NULL) {
vlist[0][j] = &nullvl; // Mark this position
vlist[0][i]->value.ival++;
}
else if (vlist[0][j]->value.ival > 0) {
vlist[0][i]->value.ival += vlist[0][j]->value.ival;
vlist[0][j]->value.ival = 0;
}
}
else j++;
}
else j++;
}
}
// For the special case of run == 1, reduce M (or S) to 1 by
// merging the critical property (if any)
// If comb == TRUE, reduce M (or S) to 1 by merging the critical
// property (if any)
if ((run == 1) && (crit != -1) && (vlist[0][0] != NULL)) {
int mult = vlist[0][0]->value.ival;
if (mult > 1) {
vl = vlist[crit][0];
if ((comb == TRUE) && (crit != -1)) {
int mult;
for (i = 0; i < run; i++) {
if (vlist[0][i] == NULL) continue;
mult = vlist[0][i]->value.ival;
if (mult > 1) {
changed = 0;
vl = vlist[crit][i];
if ((serial == TRUE) && (ctype = MERGE_SER_CRIT)) {
if (vl->type == PROP_INTEGER)
vl->value.ival *= mult;
else if (vl->type == PROP_DOUBLE)
vl->value.dval *= (double)mult;
vlist[0][0]->value.ival = 1;
changed += mult;
if ((ctype == MERGE_S_ADD) || (ctype == MERGE_P_ADD)) {
if (vl->type == PROP_INTEGER)
vl->value.ival *= mult;
else if (vl->type == PROP_DOUBLE)
vl->value.dval *= (double)mult;
vlist[0][i]->value.ival = 1;
changed += mult;
}
else if ((ctype == MERGE_S_PAR) || (ctype == MERGE_P_PAR)) {
/* Technically one should check if divide-by-mult */
/* reduces the value to < 1 and promote to double */
/* if so, but that's a very unlikely case. */
if (vl->type == PROP_INTEGER)
vl->value.ival /= mult;
else if (vl->type == PROP_DOUBLE)
vl->value.dval /= (double)mult;
vlist[0][i]->value.ival = 1;
changed += mult;
}
if (changed > 0) {
if (series)
Printf("Combined %d series devices.\n", changed);
else
Printf("Combined %d parallel devices.\n", changed);
}
}
else if (serial == FALSE && (ctype = MERGE_ADD_CRIT)) {
if (vl->type == PROP_INTEGER)
vl->value.ival *= mult;
else if (vl->type == PROP_DOUBLE)
vl->value.dval *= (double)mult;
vlist[0][0]->value.ival = 1;
changed += mult;
}
else if (serial == FALSE && (ctype = MERGE_PAR_CRIT)) {
/* Technically one should check if divide-by-mult */
/* reduces the value to < 1 and promote to double */
/* if so, but that's a very unlikely case. */
if (vl->type == PROP_INTEGER)
vl->value.ival /= mult;
else if (vl->type == PROP_DOUBLE)
vl->value.dval /= (double)mult;
vlist[0][0]->value.ival = 1;
changed += mult;
}
if (changed > 0) {
if (serial)
Printf("Combined %d serial devices.\n", changed);
else
Printf("Combined %d parallel devices.\n", changed);
}
}
}
@ -4566,6 +4642,8 @@ int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial)
ob2 = ob2->next;
}
cleanup:
// Cleanup memory allocation
for (p = 0; p < pcount; p++) {
kl = (struct property *)plist[p];
@ -5057,7 +5135,7 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
}
/*--------------------------------------------------------------*/
/* Dump a description of a device's serial/parallel network */
/* Dump a description of a device's series/parallel network */
/*--------------------------------------------------------------*/
void DumpNetwork(struct objlist *ob, int cidx)
@ -5102,6 +5180,23 @@ void DumpNetwork(struct objlist *ob, int cidx)
}
}
/*--------------------------------------------------------------*/
/* Call DumpNetwork() on each device in the object list */
/*--------------------------------------------------------------*/
void DumpNetworkAll(char *model, int file)
{
struct nlist *tp;
struct objlist *ob;
if ((tp = LookupCellFile(model, file)) == NULL) {
Printf("Cell: %s does not exist.\n", model);
}
for (ob = tp->cell; ob; ob = ob->next)
if (ob->type == FIRSTPIN)
DumpNetwork(ob, file);
}
/*--------------------------------------------------------------*/
/* Compare the properties of two objects. The passed values */
/* ob1 and ob2 are pointers to the first entry (firstpin) of */
@ -5206,7 +5301,7 @@ PropertyMatch(struct objlist *ob1, struct objlist *ob2, int do_print,
#endif
}
// Attempt to organize devices by serial and parallel combination
// Attempt to organize devices by series and parallel combination
if (t1type == PROPERTY && t2type == PROPERTY)
PropertySortAndCombine(obn1, tc1, obn2, tc2);
@ -5227,7 +5322,7 @@ PropertyMatch(struct objlist *ob1, struct objlist *ob2, int do_print,
if ((t1type != PROPERTY) && (checked_one == TRUE)) {
// t2 has more property records than t1, and they did not get
// merged equally by PropertySortAndCombine().
Fprintf(stdout, "Circuit 1 parallel/serial network does not match"
Fprintf(stdout, "Circuit 1 parallel/series network does not match"
" Circuit 2\n");
DumpNetwork(ob1, 1);
DumpNetwork(ob2, 2);
@ -5278,7 +5373,7 @@ PropertyMatch(struct objlist *ob1, struct objlist *ob2, int do_print,
else if ((t2type != PROPERTY) && (checked_one == TRUE)) {
// t1 has more property records than t2, and they did not get
// merged equally by PropertySortAndCombine().
Fprintf(stdout, "Circuit 2 parallel/serial network does not match"
Fprintf(stdout, "Circuit 2 parallel/series network does not match"
" Circuit 1\n");
DumpNetwork(ob1, 1);
DumpNetwork(ob2, 2);
@ -5332,13 +5427,13 @@ PropertyMatch(struct objlist *ob1, struct objlist *ob2, int do_print,
inst2, FALSE, FALSE, &multmatch, NULL);
if (multmatch == 1) {
/* Final attempt: Reduce M to 1 on both devices */
PropertyOptimize(tp1, tc1, 1, FALSE);
PropertyOptimize(tp2, tc2, 1, FALSE);
PropertyOptimize(tp1, tc1, 1, FALSE, TRUE);
PropertyOptimize(tp2, tc2, 1, FALSE, TRUE);
}
else if (multmatch == 2) {
/* Final attempt: Reduce S to 1 on both devices */
PropertyOptimize(tp1, tc1, 1, TRUE);
PropertyOptimize(tp2, tc2, 1, TRUE);
PropertyOptimize(tp1, tc1, 1, TRUE, TRUE);
PropertyOptimize(tp2, tc2, 1, TRUE, TRUE);
}
#ifdef TCL_NETGEN
mlist =

View File

@ -36,7 +36,8 @@ extern int EquivalenceNodes(char *name1, int file1, char *name2, int file2);
extern int EquivalenceClasses(char *name1, int file1, char *name2, int file2);
extern int IgnoreClass(char *name, int file, unsigned char type);
extern int MatchPins(struct nlist *tp1, struct nlist *tp2, int dolist);
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial);
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run,
int series, int comb);
extern int CreateCompareQueue(char *, int, char *, int);
extern int GetCompareQueueTop(char **, int *, char **, int *);
@ -47,6 +48,9 @@ extern void PrintIllegalClasses();
extern void PrintIllegalNodeClasses();
extern void PrintIllegalElementClasses();
extern void DumpNetwork(struct objlist *ob, int cidx);
extern void DumpNetworkAll(char *name, int file);
#ifdef TCL_NETGEN
extern int EquivalentNode();
extern int EquivalentElement();

View File

@ -897,6 +897,32 @@ void SetParallelCombine(int value)
RecurseCellHashTable2(SetParallelCombineFlag, (void *)(&value));
}
/*----------------------------------------------------------------------*/
/* Same as above, for series (here for symmetry, although "property */
/* series all" would be an odd command to issue, and "property series */
/* none" is the default, so not needed). */
/*----------------------------------------------------------------------*/
struct nlist *SetSeriesCombineFlag(struct hashlist *p, void *clientdata)
{
struct nlist *ptr;
int *value = (int *)clientdata;
ptr = (struct nlist *)(p->ptr);
if (*value == TRUE)
ptr->flags &= (~COMB_SERIES);
else
ptr->flags |= COMB_SERIES;
return NULL; /* NULL keeps search alive */
}
void SetSeriesCombine(int value)
{
ClearDumpedList();
RecurseCellHashTable2(SetSeriesCombineFlag, (void *)(&value));
}
/*----------------------------------------------------------------------*/
/* Delete a property from the master cell record. */
/*----------------------------------------------------------------------*/
@ -992,15 +1018,15 @@ PropertyTolerance(char *name, int fnum, char *key, int ival, double dval)
/*----------------------------------------------------------------------*/
int
PropertyMerge(char *name, int fnum, char *key, int merge_type)
PropertyMerge(char *name, int fnum, char *key, int merge_type, int merge_mask)
{
struct property *kl = NULL;
struct nlist *tc;
int result;
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
result = PropertyMerge(name, Circuit1->file, key, merge_type);
result = PropertyMerge(name, Circuit2->file, key, merge_type);
result = PropertyMerge(name, Circuit1->file, key, merge_type, merge_mask);
result = PropertyMerge(name, Circuit2->file, key, merge_type, merge_mask);
return result;
}
@ -1016,7 +1042,8 @@ PropertyMerge(char *name, int fnum, char *key, int merge_type)
return -1;
}
else {
kl->merge = merge_type;
kl->merge &= ~merge_mask;
kl->merge |= merge_type;
}
return 0;
}
@ -2868,50 +2895,50 @@ void ConnectAllNodes(char *model, int file)
}
/*----------------------------------------------------------------------*/
/* Serial and Parallel combination: */
/* All devices of the same type that exist in serial and parallel */
/* Series and Parallel combination: */
/* All devices of the same type that exist in series and parallel */
/* combinations will be treated as a single device in a network. */
/* Serial connections are only allowed for resistors and inductors. */
/* Any device may be connected in parallel. For combinations of serial */
/* Series connections are only allowed for resistors and inductors. */
/* Any device may be connected in parallel. For combinations of series */
/* and parallel, as in a resistor network, there is a set of rules: */
/* */
/* Running parallel and serial checks: */
/* Running parallel and series checks: */
/* 1. Run parallel once. If a repeat run and no devices are merged, */
/* then go to 4. */
/* 2. Run serial until no devices are merged. */
/* 3. If serial ran more than once, then go to 1. */
/* 2. Run series until no devices are merged. */
/* 3. If series ran more than once, then go to 1. */
/* 4. End merge */
/* */
/* Each merge procedure, when it finds two devices that can be merged, */
/* removes the second device from the netlist and adds its properties */
/* to the first device. If a serial merge, then the nodes are adjusted */
/* to the first device. If a series merge, then the nodes are adjusted */
/* appropriately. Where A is the property list of the first device and */
/* B is the property list of the second device, the first and last */
/* properties of A and the first property of B may require a marker to */
/* indicate the topology of the network, as follows (in order): */
/* */
/* For a parallel merge: */
/* 1) If A has serial components then tag first property of A with */
/* 1) If A has series components then tag first property of A with */
/* "open" and tag first property of B with "close". */
/* 2) If B has serial components then tag first property of B with */
/* 2) If B has series components then tag first property of B with */
/* "open". */
/* */
/* For a serial merge: */
/* For a series merge: */
/* 1) If A has unbalanced "opens", then add "close" to first */
/* property of B to balance the "opens". */
/* 2) Always tag B with "serial". */
/* 2) Always tag B with "series". */
/* */
/* Tags are indicated by a property named "_tag" which has a string */
/* value of ordered characters, "+" for serial, "(" for open, and ")" */
/* value of ordered characters, "+" for series, "(" for open, and ")" */
/* for close. A device with only one property record has no "_tag" */
/* record. A device which is in parallel with the device(s) in front */
/* of it is implicitly parallel by not having an "+" tag, and may not */
/* have a tag at all. */
/* */
/* The property check routine is responsible for comparing device */
/* serial/parallel networks against each other. Otherwise, each */
/* serial/parallel network is considered topologically as a single */
/* device, and any differences in the serial/parallel networks between */
/* series/parallel networks against each other. Otherwise, each */
/* series/parallel network is considered topologically as a single */
/* device, and any differences in the series/parallel networks between */
/* two circuits being matched will be treated as a property error. */
/*----------------------------------------------------------------------*/
@ -2927,7 +2954,8 @@ int add_prop_tag(struct objlist *obr, char tagc)
char *tmpstr;
hastag = FALSE;
for (nob = obr; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
for (nob = obr; nob; nob = nob->next) {
if (nob->type != PROPERTY) break;
for (i = 0; ; i++) {
kv = &(nob->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
@ -3018,6 +3046,75 @@ void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
while (opentags-- > 0) add_prop_tag(nob, ')');
}
/* Remove unneeded group tags, when device merging has removed all but */
/* one, or all but parallel devices inside a group. For simplicity, */
/* handle only one group at a time. Return 1 if a group was removed */
/* and 0 if not. The caller should run repeatedly until the routine */
/* returns 0. */
int remove_group_tags(struct objlist *ob)
{
struct objlist *nob, *sob;
int i, si, stags;
struct valuelist *kv;
char *tag;
/* Find the first property record in ob. */
for (nob = ob->next; nob && nob->type != FIRSTPIN; nob = nob->next)
if (nob->type == PROPERTY)
break;
if (nob->type != PROPERTY) return; // shouldn't happen
for (sob = NULL; nob && nob->type == PROPERTY; nob = nob->next) {
for (i = 0; ; i++) {
kv = &(nob->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
if (kv->type == PROP_STRING) {
if (!strcmp(kv->key, "_tag")) {
for (tag = kv->value.string; *tag != '\0'; tag++) {
if (*tag == '(') {
sob = nob; /* Save position of open group */
si = i; /* Save index of open group */
stags = 0; /* Check for series tags */
}
else if (*tag == '+')
stags++;
else if (*tag == ')') {
if (stags == 0) {
/* Remove close tag */
for (++i; ; i++) {
nob->instance.props[i - 1] = nob->instance.props[i];
if (nob->instance.props[i].type == PROP_ENDLIST) break;
}
/* Remove open tag */
for (i = si + 1; ; i++) {
sob->instance.props[i - 1] = sob->instance.props[i];
if (sob->instance.props[i].type == PROP_ENDLIST) break;
}
return 1;
}
sob = NULL;
stags = 0;
}
}
}
}
}
}
if (sob != NULL) {
/* Implicit close tag at end */
if (stags == 0) {
/* Remove open tag */
for (i = si + 1; ; i++) {
sob->instance.props[i - 1] = sob->instance.props[i];
if (sob->instance.props[i].type == PROP_ENDLIST) break;
}
return 1;
}
}
return 0;
}
/*----------------------------------------------------------------------*/
/* Find all devices that are of the same class and check for parallel */
/* combinations, and combine them where found, adjusting property "M" */
@ -3229,7 +3326,7 @@ int CombineParallel(char *model, int file)
if (propfirst != NULL) {
// Serial/Parallel logic:
// Series/Parallel logic:
// If propfirst has _tag in properties,
// then add an "open" tag at propfirst
add_prop_tag(propfirst, '(');
@ -3272,9 +3369,9 @@ int CombineParallel(char *model, int file)
}
/*----------------------------------------------------------------------*/
/* For the purposes of serial connection checking, find if all pins */
/* For the purposes of series connection checking, find if all pins */
/* of two instances after the first two pins are connected to the name */
/* nodes. This depends on the definition of a serial device as having */
/* nodes. This depends on the definition of a series device as having */
/* two ports, but any additional ports (such as a substrate connection) */
/* must be the same for all devices in series. */
/*----------------------------------------------------------------------*/
@ -3284,7 +3381,7 @@ int check_pin_nodes(struct objlist *ob1, struct objlist *ob2)
struct objlist *nob, *pob;
/* A dummy device may have both terminals connected to the same */
/* point, triggering a false check for a serial device. */
/* point, triggering a false check for a series device. */
if (ob1 == ob2) return FALSE;
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
@ -3313,12 +3410,12 @@ int check_pin_nodes(struct objlist *ob1, struct objlist *ob2)
/* one. */
/* */
/* This routine depends on CombineParallel() being run first so that no */
/* parallel devices are reported as serial. */
/* parallel devices are reported as series. */
/* */
/* Return the number of devices merged. */
/*----------------------------------------------------------------------*/
int CombineSerial(char *model, int file)
int CombineSeries(char *model, int file)
{
struct nlist *tp, *tp2;
struct objlist ***instlist;
@ -3331,7 +3428,7 @@ int CombineSerial(char *model, int file)
return -1;
}
/* Diagnostic */
/* Printf("CombineSerial start model = %s file = %d\n", model, file); */
/* Printf("CombineSeries start model = %s file = %d\n", model, file); */
instlist = (struct objlist ***)CALLOC((tp->nodename_cache_maxnodenum + 1),
sizeof(struct objlist **));
@ -3346,26 +3443,36 @@ int CombineSerial(char *model, int file)
instlist[ob->node] = (struct objlist **)CALLOC(2,
sizeof(struct objlist *));
/* Device must be marked as able to be combined in serial. */
/* Device must be marked as able to be combined in series. */
/* Note that devices with more than two pins are expected */
/* to serial connect along the first two pins, and the */
/* to series connect along the first two pins, and the */
/* remaining pins must all connect to the same nodes. By */
/* default, CLASS_RES, CLASS_RES3, and CLASS_INDUCTOR are */
/* all allowed to combine in serial. All other devices */
/* must have serial combination explicitly enabled. */
/* all allowed to combine in series. All other devices */
/* must have series combination explicitly enabled. */
/* NOTE: Arbitrarily, the first two pins of a device are */
/* assumed to be the ones that make serial connections. */
/* assumed to be the ones that make series connections. */
/* Additional pins, if any, do not. */
tp2 = LookupCellFile(ob->model.class, file);
if ((tp2->flags & COMB_SERIAL) && (ob->type <= 2))
if ((tp2->flags & COMB_SERIES) && (ob->type <= 2)) {
instlist[ob->node][0] = obp;
/* Node may not be a port of the subcircuit */
for (obn = tp->cell; obn && obn->type == PORT; obn = obn->next) {
if (obn->node == ob->node) {
/* invalidate node */
instlist[ob->node][0] = NULL;
break;
}
}
}
else
/* invalidate node */
instlist[ob->node][0] = NULL;
}
else if (instlist[ob->node][0] == NULL) {
/* Node is not valid for serial connection */
/* Node is not valid for series connection */
}
else if (instlist[ob->node][1] == NULL) {
/* Check if first instance is the same type */
@ -3393,7 +3500,7 @@ int CombineSerial(char *model, int file)
struct valuelist *kv2;
/* Diagnostic */
/* Fprintf(stdout, "Found serial instances %s and %s\n",
/* Fprintf(stdout, "Found series instances %s and %s\n",
instlist[i][0]->instance.name,
instlist[i][1]->instance.name); */
scnt++;
@ -3504,7 +3611,7 @@ int CombineSerial(char *model, int file)
obs = obn;
}
/* If 2nd device appears anywhere else in the serial device */
/* If 2nd device appears anywhere else in the series device */
/* list, replace it with the 1st device. */
for (j = i + 1; j <= tp->nodename_cache_maxnodenum; j++) {
if (instlist[j] == NULL) continue;

View File

@ -36,9 +36,11 @@ extern struct property *PropertyString(char *name, int fnum, char *key,
double slop, char *pdefault);
extern int PropertyDelete(char *name, int fnum, char *key);
extern void SetParallelCombine(int value);
extern void SetSeriesCombine(int value);
extern int PropertyTolerance(char *name, int fnum, char *key, int ival,
double dval);
extern int PropertyMerge(char *name, int fnum, char *key, int merge_type);
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 *);
@ -120,7 +122,7 @@ extern void ConvertGlobals(char *name, int fnum);
extern int CleanupPins(char *name, int fnum);
extern void ConnectAllNodes(char *model, int fnum);
extern int CombineParallel(char *model, int fnum);
extern int CombineSerial(char *model, int fnum);
extern int CombineSeries(char *model, int fnum);
extern int NoDisconnectedNodes;
extern int PropertyKeyMatch(char *, char *);
extern int PropertyValueMatch(char *, char *);

View File

@ -103,18 +103,28 @@ struct valuelist {
/* Part 3: Keys & Defaults (kept in the cell record as a hash table) */
#define MERGE_NONE 0 /* Property does not change when devices merge */
#define MERGE_ADD 1 /* Properties sum with device parallel merge */
#define MERGE_ADD_CRIT 2 /* Properties sum with device parallel merge */
#define MERGE_PAR 3 /* Properties add in parallel with device merge */
#define MERGE_PAR_CRIT 4 /* Properties add in parallel with device merge */
#define MERGE_SER 5 /* Properties sum with device parallel merge */
#define MERGE_SER_CRIT 6 /* Properties sum with device parallel merge */
#define MERGE_NONE 0x00 /* Property does not change when devices merge */
#define MERGE_P_ADD 0x01 /* Properties sum with device parallel merge */
#define MERGE_P_PAR 0x02 /* Properties add in parallel with parallel merge */
#define MERGE_P_CRIT 0x04 /* This property enables parallel merging */
#define MERGE_S_ADD 0x08 /* Properties sum with device series merge */
#define MERGE_S_PAR 0x10 /* Properties add in parallel with series merge */
#define MERGE_S_CRIT 0x20 /* This property enables series merging */
#define MERGE_P_MASK (MERGE_P_ADD | MERGE_P_PAR | MERGE_P_CRIT)
#define MERGE_S_MASK (MERGE_S_ADD | MERGE_S_PAR | MERGE_S_CRIT)
#define MERGE_ALL_MASK (MERGE_P_MASK | MERGE_S_MASK)
/* Although the above are flags, "ADD" and "PAR" are mutually exclusive. */
/* Note: A "critical" merge means that the property causes the number of */
/* devices to change. e.g., transistor width is critical; transistor drain */
/* area sums when devices are merged, but does not change the number of devices.*/
/* More than one property can be critical. e.g., width and number of fingers. */
/* Also it is possible for a property (e.g., "value") to be critical for both */
/* series and parallel merging. */
struct property {
char *key; /* name of the property */
@ -217,7 +227,7 @@ struct nlist {
/* Flags for combination allowances and prohibitions */
#define COMB_SERIAL 0x40
#define COMB_SERIES 0x40
#define COMB_NO_PARALLEL 0x80
extern struct nlist *CurrentCell;

View File

@ -3157,19 +3157,25 @@ _netcmp_equate(ClientData clientData,
/* add --- add new property */
/* remove --- delete existing property */
/* tolerance --- set property tolerance */
/* merge --- set property merge behavior */
/* merge --- (deprecated) */
/* or */
/* netgen::property default */
/* or */
/* netgen::property <device>|<model> <option> */
/* yes|no */
/* Where <option> is one of: */
/* serial --- allow/prohibit serial combination */
/* series --- allow/prohibit series combination */
/* parallel --- allow/prohibit parallel combination */
/* or */
/* netgen::property parallel none */
/* --- prohibit parallel combinations by */
/* default. */
/* default (for all devices). */
/* */
/* series|parallel options are: */
/* enable|disable|none|{<key> <combine_option>} */
/* */
/* combine options are: */
/* par|add|par_critical|add_critical */
/* */
/* Formerly: (none) */
/* Results: */
@ -3189,11 +3195,11 @@ _netcmp_property(ClientData clientData,
char *options[] = {
"add", "create", "remove", "delete", "tolerance", "merge", "serial",
"parallel", NULL
"series", "parallel", NULL
};
enum OptionIdx {
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX,
SERIAL_IDX, PARALLEL_IDX
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX
};
int result, index, idx2;
@ -3204,16 +3210,31 @@ _netcmp_property(ClientData clientData,
INTEGER_IDX, DOUBLE_IDX, VALUE_IDX, STRING_IDX
};
/* Note: "merge" has been deprecated, but kept for backwards compatibility. */
/* It has been replaced by "combineoptions" below, used with "series" and */
/* "parallel". */
char *mergeoptions[] = {
"none", "add", "add_critical", "par", "par_critical",
"parallel", "parallel_critical", "ser_critical", "ser",
"serial_critical", "serial", NULL
"serial_critical", "series_critical", "serial", "series", NULL
};
enum MergeOptionIdx {
NONE_IDX, ADD_ONLY_IDX, ADD_CRIT_IDX,
PAR_ONLY_IDX, PAR_CRIT_IDX, PAR2_ONLY_IDX, PAR2_CRIT_IDX,
SER_CRIT_IDX, SER_IDX, SER2_CRIT_IDX, SER2_IDX
SER_CRIT_IDX, SER_IDX, SER2_CRIT_IDX, SER3_CRIT_IDX, SER2_IDX, SER3_IDX
};
char *combineoptions[] = {
"none", "par", "add", "par_critical", "add_critical", NULL
};
enum CombineOptionIdx {
COMB_NONE_IDX, COMB_PAR_IDX, COMB_ADD_IDX, COMB_PAR_CRITICAL_IDX,
COMB_ADD_CRITICAL_IDX
};
char *yesno[] = {
"on", "yes", "true", "enable", "allow",
"off", "no", "false", "disable", "prohibit", NULL
@ -3238,25 +3259,34 @@ _netcmp_property(ClientData clientData,
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
case CLASS_NMOS4: case CLASS_PMOS4: case CLASS_FET4:
case CLASS_FET:
PropertyMerge(tp->name, tp->file, "w", MERGE_ADD_CRIT);
PropertyMerge(tp->name, tp->file, "w", MERGE_P_ADD | MERGE_P_CRIT,
MERGE_ALL_MASK);
PropertyDelete(tp->name, tp->file, "as");
PropertyDelete(tp->name, tp->file, "ad");
PropertyDelete(tp->name, tp->file, "ps");
PropertyDelete(tp->name, tp->file, "pd");
break;
case CLASS_RES: case CLASS_RES3:
PropertyMerge(tp->name, tp->file, "w", MERGE_ADD_CRIT);
PropertyMerge(tp->name, tp->file, "l", MERGE_SER_CRIT);
tp->flags |= COMB_SERIAL;
PropertyMerge(tp->name, tp->file, "w",
MERGE_P_PAR | MERGE_P_CRIT, MERGE_ALL_MASK);
PropertyMerge(tp->name, tp->file, "l",
MERGE_S_ADD | MERGE_S_CRIT, MERGE_ALL_MASK);
PropertyMerge(tp->name, tp->file, "value",
MERGE_S_ADD | MERGE_P_PAR, MERGE_ALL_MASK);
tp->flags |= COMB_SERIES;
break;
case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3:
// NOTE: No attempt to combine area, width, or length;
// only value.
PropertyMerge(tp->name, tp->file, "value", MERGE_ADD_CRIT);
PropertyMerge(tp->name, tp->file, "value",
MERGE_P_ADD | MERGE_P_CRIT |
MERGE_S_PAR | MERGE_S_CRIT, MERGE_ALL_MASK);
break;
case CLASS_INDUCTOR:
PropertyMerge(tp->name, tp->file, "value", MERGE_PAR_CRIT);
tp->flags |= COMB_SERIAL;
PropertyMerge(tp->name, tp->file, "value",
MERGE_P_PAR | MERGE_P_CRIT |
MERGE_S_PAR | MERGE_S_CRIT, MERGE_ALL_MASK);
tp->flags |= COMB_SERIES;
break;
}
tp = NextCell();
@ -3274,7 +3304,22 @@ _netcmp_property(ClientData clientData,
}
else {
Tcl_SetResult(interp, "Bad option, should be property parallel none|all",
NONE);
NULL);
return TCL_ERROR;
}
return TCL_OK;
}
else if ((objc == 3) && ((!strcmp(Tcl_GetString(objv[1]), "series")) ||
(!strcmp(Tcl_GetString(objv[1]), "serial")))) {
if (!strcmp(Tcl_GetString(objv[2]), "none")) {
SetSeriesCombine(FALSE);
}
else if (!strcmp(Tcl_GetString(objv[2]), "all")) {
SetSeriesCombine(TRUE);
}
else {
Tcl_SetResult(interp, "Bad option, should be property series none|all",
NULL);
return TCL_ERROR;
}
return TCL_OK;
@ -3329,10 +3374,11 @@ _netcmp_property(ClientData clientData,
switch (index) {
case SERIAL_IDX:
case SERIES_IDX:
case PARALLEL_IDX:
if (objc == 3) {
if (index == SERIAL_IDX) {
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_SERIAL) ? 1 : 0);
if (index == SERIAL_IDX || index == SERIES_IDX) {
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_SERIES) ? 1 : 0);
Tcl_SetObjResult(interp, tobj1);
return TCL_OK;
}
@ -3342,30 +3388,107 @@ _netcmp_property(ClientData clientData,
return TCL_OK;
}
}
else if (objc == 4) {
if (Tcl_GetIndexFromObj(interp, objv[3],
(CONST84 char **)yesno,
"combine", 0, &idx2) != TCL_OK) {
Tcl_WrongNumArgs(interp, 3, objv, "enable|disable");
return TCL_ERROR;
}
if (idx2 <= 4) { /* true, enable, etc. */
if (index == SERIAL_IDX)
tp->flags |= COMB_SERIAL;
else
tp->flags &= ~COMB_NO_PARALLEL;
}
else { /* false, disable, etc. */
if (index == SERIAL_IDX)
tp->flags &= ~COMB_SERIAL;
else
tp->flags |= COMB_NO_PARALLEL;
}
}
else {
Tcl_WrongNumArgs(interp, 2, objv, "serial|parallel enable|disable");
else if (objc < 4) {
Tcl_WrongNumArgs(interp, 2, objv, "series|parallel enable|disable");
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
// Each value must be a list of two, or a yes/no answer.
if (Tcl_GetIndexFromObj(interp, objv[i],
(CONST84 char **)yesno,
"combine", 0, &idx2) == TCL_OK) {
if (idx2 <= 4) { /* true, enable, etc. */
if (index == SERIAL_IDX || index == SERIES_IDX)
tp->flags |= COMB_SERIES;
else
tp->flags &= ~COMB_NO_PARALLEL;
}
else { /* false, disable, etc. */
if (index == SERIAL_IDX || index == SERIES_IDX)
tp->flags &= ~COMB_SERIES;
else
tp->flags |= COMB_NO_PARALLEL;
}
continue;
}
result = Tcl_ListObjLength(interp, objv[i], &llen);
if ((result != TCL_OK) || (llen != 2)) {
Tcl_SetResult(interp, "Not a {key merge_type} pair list.",
NULL);
}
else {
int mergeval = MERGE_NONE;
int mergemask = MERGE_NONE;
result = Tcl_ListObjIndex(interp, objv[i], 0, &tobj1);
if (result != TCL_OK) return result;
result = Tcl_ListObjIndex(interp, objv[i], 1, &tobj2);
if (result != TCL_OK) return result;
result = Tcl_GetIndexFromObj(interp, tobj2,
(CONST84 char **)combineoptions,
"combine_type", 0, &idx2);
if (result != TCL_OK) return result;
if (index == SERIAL_IDX || index == SERIES_IDX) {
mergemask = MERGE_S_MASK;
switch (idx2) {
case COMB_NONE_IDX:
mergeval &= ~(MERGE_S_ADD | MERGE_S_PAR
| MERGE_S_CRIT);
tp->flags &= ~COMB_SERIES;
break;
case COMB_PAR_IDX:
mergeval = MERGE_S_PAR;
tp->flags |= COMB_SERIES;
break;
case COMB_ADD_IDX:
mergeval |= MERGE_S_ADD;
tp->flags |= COMB_SERIES;
break;
case COMB_PAR_CRITICAL_IDX:
mergeval |= (MERGE_S_PAR | MERGE_S_CRIT);
tp->flags |= COMB_SERIES;
break;
case COMB_ADD_CRITICAL_IDX:
mergeval |= (MERGE_S_ADD | MERGE_S_CRIT);
tp->flags |= COMB_SERIES;
break;
}
}
else { /* index == PARALLEL_IDX */
mergemask = MERGE_P_MASK;
switch (idx2) {
case COMB_NONE_IDX:
mergeval &= ~(MERGE_P_ADD | MERGE_P_PAR
| MERGE_P_CRIT);
tp->flags |= COMB_NO_PARALLEL;
break;
case COMB_PAR_IDX:
mergeval |= MERGE_P_PAR;
tp->flags &= ~COMB_NO_PARALLEL;
break;
case COMB_ADD_IDX:
mergeval |= MERGE_P_ADD;
tp->flags &= ~COMB_NO_PARALLEL;
break;
case COMB_PAR_CRITICAL_IDX:
mergeval |= (MERGE_P_PAR | MERGE_P_CRIT);
tp->flags &= ~COMB_NO_PARALLEL;
break;
case COMB_ADD_CRITICAL_IDX:
mergeval |= (MERGE_P_ADD | MERGE_P_CRIT);
tp->flags &= ~COMB_NO_PARALLEL;
break;
}
}
PropertyMerge(tp->name, fnum, Tcl_GetString(tobj1), mergeval,
mergemask);
}
}
break;
case ADD_IDX:
@ -3535,6 +3658,11 @@ _netcmp_property(ClientData clientData,
break;
case MERGE_IDX:
// NOTE: This command option is deprecated, kept for backwards
// compatibility, with updated flag values. This command format
// is unable to specify a property as being a critical property
// for merging both in series and in parallel.
if (objc == 3) {
Tcl_WrongNumArgs(interp, 1, objv, "{property_key merge_type} ...");
return TCL_ERROR;
@ -3564,29 +3692,32 @@ _netcmp_property(ClientData clientData,
mergeval = MERGE_NONE;
break;
case ADD_ONLY_IDX:
mergeval = MERGE_ADD;
mergeval = MERGE_P_ADD;
break;
case ADD_CRIT_IDX:
mergeval = MERGE_ADD_CRIT;
mergeval = MERGE_P_ADD | MERGE_P_CRIT;
break;
case PAR_ONLY_IDX:
case PAR2_ONLY_IDX:
mergeval = MERGE_PAR;
mergeval = MERGE_P_PAR;
break;
case PAR_CRIT_IDX:
case PAR2_CRIT_IDX:
mergeval = MERGE_PAR_CRIT;
mergeval = MERGE_P_PAR | MERGE_P_CRIT;
break;
case SER_CRIT_IDX:
case SER2_CRIT_IDX:
mergeval = MERGE_SER_CRIT;
case SER3_CRIT_IDX:
mergeval = MERGE_S_ADD | MERGE_S_CRIT;
break;
case SER_IDX:
case SER2_IDX:
mergeval = MERGE_SER;
case SER3_IDX:
mergeval = MERGE_S_ADD;
break;
}
PropertyMerge(tp->name, fnum, Tcl_GetString(tobj1), mergeval);
PropertyMerge(tp->name, fnum, Tcl_GetString(tobj1), mergeval,
MERGE_ALL_MASK);
}
}
break;