(1) Fixed the series sorting, which needed to be modified to match

the parallel sorting routine.  This fixes occasional property
errors with series-connected devices such as resistors.  (2) Added
a method to associate properties with specific pins when pins are
permutable.  This allows netgen to properly check a value like
source/drain area when the definition of source and drain has
changed due to permutation of the device.  (3) Added a "property"
command extension "associate" to associate a property with a pin,
for use with the method described in (2).
This commit is contained in:
Tim Edwards 2022-12-15 21:34:56 -05:00
parent 2292ab813b
commit 013fff9f37
7 changed files with 212 additions and 43 deletions

View File

@ -1 +1 @@
1.5.242 1.5.243

View File

@ -4214,7 +4214,11 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
struct property *kl; struct property *kl;
struct valuelist *vl, *sl; struct valuelist *vl, *sl;
int i, p, sval, merge_type; int i, p, sval, merge_type;
double cval, slop; // double cval, slop;
int has_crit;
char ca, co;
double tval, tslop;
double aval, pval, oval, aslop, pslop;
obn = ob1->next; obn = ob1->next;
for (i = 0; i < idx1; i++) obn = obn->next; for (i = 0; i < idx1; i++) obn = obn->next;
@ -4227,55 +4231,94 @@ void series_sort(struct objlist *ob1, struct nlist *tp1, int idx1, int run)
obp = obn; obp = obn;
sval = 1; sval = 1;
cval = slop = 0.0; pval = aval = oval = 0.0;
for (i = 0; i < run; i++) { for (i = 0; i < run; i++) {
sl = NULL; has_crit = FALSE;
merge_type = MERGE_NONE; merge_type = MERGE_NONE;
ca = co = (char)0;
for (p = 0;; p++) { for (p = 0;; p++) {
vl = &(obp->instance.props[p]); vl = &(obp->instance.props[p]);
if (vl->type == PROP_ENDLIST) break; if (vl->type == PROP_ENDLIST) break;
if (vl->key == NULL) continue; if (vl->key == NULL) continue;
if ((*matchfunc)(vl->key, "S")) { if ((*matchfunc)(vl->key, "S")) {
sval = vl->value.ival; sval = vl->value.ival;
sl = vl; continue;
} }
else {
kl = (struct property *)HashLookup(vl->key, &(tp1->propdict)); kl = (struct property *)HashLookup(vl->key, &(tp1->propdict));
if (kl && (kl->merge & (MERGE_S_ADD | MERGE_S_PAR))) { if (kl == NULL) continue; /* Ignored property */
/* Get the property value and slop. Promote if needed. Save */
/* property and slop as type double so they can be sorted. */
if ((vl->type == PROP_STRING || vl->type == PROP_EXPRESSION) &&
(kl->type != vl->type))
PromoteProperty(kl, vl, obp, tp1);
if (vl->type == PROP_INTEGER) { if (vl->type == PROP_INTEGER) {
cval = (double)vl->value.ival; tval = (double)vl->value.ival;
slop = (double)kl->slop.ival; tslop = (double)kl->slop.ival;
}
else if (vl->type == PROP_STRING) {
/* This is unlikely---no method to merge string properties! */
tval = (double)vl->value.string[0]
+ (double)vl->value.string[1] / 10.0;
tslop = (double)0;
} }
else { else {
cval = vl->value.dval; tval = vl->value.dval;
slop = kl->slop.dval; tslop = kl->slop.dval;
} }
if (kl->merge & MERGE_S_CRIT) {
has_crit = TRUE;
pval = tval;
pslop = tslop;
}
else if (kl->merge & (MERGE_S_ADD | MERGE_S_PAR)) {
if ((ca == (char)0) || (toupper(vl->key[0]) > ca)) {
merge_type = kl->merge & (MERGE_S_ADD | MERGE_S_PAR); merge_type = kl->merge & (MERGE_S_ADD | MERGE_S_PAR);
aval = tval;
aslop = tslop;
ca = toupper(vl->key[0]);
} }
} }
else if ((co == (char)0) || (toupper(vl->key[0]) > co)) {
oval = tval;
co = toupper(vl->key[0]);
} }
if (merge_type == MERGE_S_ADD) {
proplist[i].value = cval * (double)sval;
proplist[i].slop = slop;
proplist[i].avalue = 0;
if (sl) sl->value.ival = 1;
} }
else if (merge_type == MERGE_S_PAR) { if (has_crit == TRUE) {
proplist[i].value = cval / (double)sval; /* If there is a critical value, then sort first */
proplist[i].slop = slop; /* by critical value */
proplist[i].avalue = 0; proplist[i].value = pval;
if (sl) sl->value.ival = 1; proplist[i].slop = pslop;
/* then sort on additive value times S */
/* or on non-additive value. */
if (merge_type == MERGE_S_ADD)
proplist[i].avalue = aval * (double)sval;
else if (merge_type == MERGE_S_PAR)
proplist[i].avalue = aval / (double)sval;
else
proplist[i].avalue = (double)sval;
} }
else { else {
/* Components which declare no series addition method stay unsorted */ if (merge_type != MERGE_NONE) {
proplist[i].value = (double)0; proplist[i].value = aval;
proplist[i].avalue = (double)0; proplist[i].slop = aslop;
proplist[i].slop = (double)1E-6; proplist[i].avalue = (double)sval;
}
else {
proplist[i].value = (double)sval;
proplist[i].slop = (double)0;
proplist[i].avalue = oval;
}
} }
proplist[i].idx = i; proplist[i].idx = i;
proplist[i].ob = obp; proplist[i].ob = obp;
obp = obp->next; obp = obp->next;
} }
obn = obp; /* Link from last property */ obn = obp; /* Link from last property */
qsort(&proplist[0], run, sizeof(propsort), compsort); qsort(&proplist[0], run, sizeof(propsort), compsort);
@ -4598,8 +4641,8 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
/* and manage the topology. Note that at this point devices have */ /* and manage the topology. Note that at this point devices have */
/* already been combined if all critical properties match, so any */ /* already been combined if all critical properties match, so any */
/* parallel devices remaining are not considered mergeable unless as */ /* parallel devices remaining are not considered mergeable unless as */
/* a last resort. Parallel devices need to be checked for swapping, */ /* a last resort. Devices also need to be checked for swapping. */
/* however. So the steps are: */ /* So the steps are: */
/* 1) Find parallel devices with more elements in one circuit than */ /* 1) Find parallel devices with more elements in one circuit than */
/* in the other. If non-summing parameters of interest match, */ /* in the other. If non-summing parameters of interest match, */
/* then merge all devices that can be merged until both sides */ /* then merge all devices that can be merged until both sides */
@ -4612,6 +4655,10 @@ void PropertySortAndCombine(struct objlist *pre1, struct nlist *tp1,
/* circuits. Check if critical parameters match between the */ /* circuits. Check if critical parameters match between the */
/* circuits. If not, check if swapping devices in circuit1 */ /* circuits. If not, check if swapping devices in circuit1 */
/* makes a better match to circuit2. */ /* makes a better match to circuit2. */
/* 4) Find series devices that have the same number in both */
/* circuits. Check if critical parameters match between the */
/* circuits. If not, check if swapping devices in circuit1 */
/* makes a better match to circuit2. */
/* Case 1: Parallel devices with more elements in one circuit */ /* Case 1: Parallel devices with more elements in one circuit */
@ -5317,13 +5364,14 @@ void
#endif #endif
PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1, PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
char *inst1, struct objlist *tp2, struct nlist *tc2, char *inst1, struct objlist *tp2, struct nlist *tc2,
char *inst2, int do_print, int do_list, int *count, char *inst2, struct Element *E1, struct Element *E2,
int *rval) int do_print, int do_list, int *count, int *rval)
{ {
int mismatches = 0; int mismatches = 0;
int len2, *check2; int len2, *check2;
struct property *kl1, *kl2, *klt; struct property *kl1, *kl2, *klt;
struct valuelist *vl1, *vl2; struct valuelist *vl1, *vl2;
char *vl1key;
int i, j; int i, j;
int islop; int islop;
int ival1, ival2; int ival1, ival2;
@ -5427,22 +5475,71 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
} }
} }
/* If the property is associated with a pin, then */
/* determine if the pin is permutable; If so, then */
/* determine if the pins are swapped between the two */
/* elements. If so, then swap the property keys. */
vl1key = vl1->key;
if (kl1->pin != NULL) {
struct objlist *tob1, *tob2;
struct NodeList *nl1, *nl2;
struct property *kla;
struct valuelist *vla;
int k;
nl1 = E1->nodelist;
for (tob1 = tc1->cell; tob1 && tob1->type == PORT; tob1 = tob1->next) {
if ((*matchfunc)(tob1->name, kl1->pin)) break;
nl1 = nl1->next;
}
if (tob1->type == PORT) {
/* Found the node list record corresponding to the given pin. */
/* Now find the pin corresponding to the matching node. */
nl2 = E2->nodelist;
for (tob2 = tc1->cell; tob2 && tob2->type == PORT; tob2 = tob2->next) {
if (nl2->node->nodeclass == nl1->node->nodeclass) break;
nl2 = nl2->next;
}
if (nl2->node->nodeclass == nl1->node->nodeclass) {
if (tob2 != tob1) { /* Element pins were swapped */
/* Find the other property to swap with */
for (k = 0;; k++) {
vla = &(tp1->instance.props[k]);
if (vla->type == PROP_ENDLIST) break;
if (vla != vl1) {
kla = (struct property *)HashLookup(vla->key,
&(tc1->propdict));
if (kla && kla->pin)
if ((*matchfunc)(tob2->name, kla->pin))
break;
}
}
if (vla->type != PROP_ENDLIST) {
/* Swap vla->key and vl1->key */
vl1key = vla->key;
}
}
}
}
}
/* Find the matching property in vl2. */ /* Find the matching property in vl2. */
for (j = 0;; j++) { for (j = 0;; j++) {
vl2 = &(tp2->instance.props[j]); vl2 = &(tp2->instance.props[j]);
if (vl2->type == PROP_ENDLIST) break; if (vl2->type == PROP_ENDLIST) break;
if (check2[j] == 0) if (check2[j] == 0)
if ((*matchfunc)(vl1->key, vl2->key)) break; if ((*matchfunc)(vl1key, vl2->key)) break;
} }
if (vl2->type == PROP_ENDLIST) { if (vl2->type == PROP_ENDLIST) {
/* Check against M and S records; a missing M or S */ /* Check against M and S records; a missing M or S */
/* record is equivalent to M = 1 or S = 1. */ /* record is equivalent to M = 1 or S = 1. */
if (vl1 != &mvl) if (vl1 != &mvl)
if ((*matchfunc)(vl1->key, mvl.key)) vl2 = &mvl; if ((*matchfunc)(vl1key, mvl.key)) vl2 = &mvl;
if (vl1 != &svl) if (vl1 != &svl)
if ((*matchfunc)(vl1->key, svl.key)) vl2 = &svl; if ((*matchfunc)(vl1key, svl.key)) vl2 = &svl;
} }
if (vl2->type == PROP_ENDLIST) { if (vl2->type == PROP_ENDLIST) {
/* vl1 had a property of interest that was not found */ /* vl1 had a property of interest that was not found */
@ -5451,7 +5548,7 @@ PropertyCheckMismatch(struct objlist *tp1, struct nlist *tc1,
if (do_print) { if (do_print) {
Fprintf(stdout, "%s vs. %s:\n", inst1, inst2); Fprintf(stdout, "%s vs. %s:\n", inst1, inst2);
Fprintf(stdout, "Property %s in circuit1 has no matching " Fprintf(stdout, "Property %s in circuit1 has no matching "
"property in circuit2\n", vl1->key); "property in circuit2\n", vl1key);
} }
#ifdef TCL_NETGEN #ifdef TCL_NETGEN
if (do_list) { if (do_list) {
@ -6095,7 +6192,7 @@ PropertyMatch(struct Element *E1, struct Element *E2,
else { else {
int multmatch, count; int multmatch, count;
PropertyCheckMismatch(tp1, tc1, inst1, tp2, tc2, PropertyCheckMismatch(tp1, tc1, inst1, tp2, tc2,
inst2, FALSE, FALSE, &multmatch, NULL); inst2, E1, E2, FALSE, FALSE, &multmatch, NULL);
if (multmatch == 1) { if (multmatch == 1) {
/* Final attempt: Reduce M to 1 on both devices */ /* Final attempt: Reduce M to 1 on both devices */
run1 = run2 = 0; run1 = run2 = 0;
@ -6116,7 +6213,7 @@ PropertyMatch(struct Element *E1, struct Element *E2,
mlist = mlist =
#endif #endif
PropertyCheckMismatch(tp1, tc1, inst1, tp2, tc2, PropertyCheckMismatch(tp1, tc1, inst1, tp2, tc2,
inst2, do_print, do_list, &count, &rval); inst2, E1, E2, do_print, do_list, &count, &rval);
mismatches += count; mismatches += count;
#ifdef TCL_NETGEN #ifdef TCL_NETGEN
if (do_list && (mlist != NULL)) { if (do_list && (mlist != NULL)) {

View File

@ -984,6 +984,51 @@ PropertyDelete(char *name, int fnum, char *key)
return 0; return 0;
} }
/*----------------------------------------------------------------------*/
/* Associate a property with a specific pin */
/*----------------------------------------------------------------------*/
int
PropertyAssociatePin(char *name, int fnum, char *key, char *pin)
{
struct property *kl = NULL;
struct nlist *tc;
struct objlist *ob;
int result;
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
result = PropertyAssociatePin(name, Circuit1->file, key, pin);
result = PropertyAssociatePin(name, Circuit2->file, key, pin);
return result;
}
tc = LookupCellFile(name, fnum);
if (tc == NULL) {
Printf("No device %s found for PropertyAssociatePin()\n", name);
return -1;
}
kl = (struct property *)HashLookup(key, &(tc->propdict));
if (kl == NULL) {
Printf("No property %s found for device %s\n", key, name);
return -1;
}
else {
for (ob = tc->cell; ob != NULL; ob = ob->next) {
if (ob->type != PORT) break;
else if ((*matchfunc)(ob->name, pin)) {
kl->pin = ob->name;
break;
}
}
if (ob == NULL) {
Printf("No pin %s found for device %s\n", pin, name);
return -1;
}
}
return 0;
}
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/
/* Set the tolerance of a property in the master cell record. */ /* Set the tolerance of a property in the master cell record. */
/*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/

View File

@ -39,6 +39,7 @@ extern void SetParallelCombine(int value);
extern void SetSeriesCombine(int value); extern void SetSeriesCombine(int value);
extern int PropertyTolerance(char *name, int fnum, char *key, int ival, extern int PropertyTolerance(char *name, int fnum, char *key, int ival,
double dval); double dval);
extern int PropertyAssociatePin(char *name, int fnum, char *key, char *pin);
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); int merge_mask);
extern void ResolveProperties(char *name1, int file1, char *name2, int file2); extern void ResolveProperties(char *name1, int file1, char *name2, int file2);

View File

@ -130,6 +130,7 @@ struct property *NewProperty(void)
kl = (struct property *)CALLOC(1,sizeof(struct property)); kl = (struct property *)CALLOC(1,sizeof(struct property));
if (kl == NULL) Fprintf(stderr,"NewProperty: Core allocation error\n"); if (kl == NULL) Fprintf(stderr,"NewProperty: Core allocation error\n");
kl->pin = (char *)NULL;
return (kl); return (kl);
} }

View File

@ -135,6 +135,7 @@ struct property {
unsigned char idx; /* index into valuelist */ unsigned char idx; /* index into valuelist */
unsigned char type; /* string, integer, double, value, expression */ unsigned char type; /* string, integer, double, value, expression */
unsigned char merge; /* how property changes when devices are merged */ unsigned char merge; /* how property changes when devices are merged */
char *pin; /* associated pin (or NULL if not associated) */
union { union {
char *string; char *string;
double dval; double dval;

View File

@ -3377,6 +3377,7 @@ _netcmp_equate(ClientData clientData,
/* add --- add new property */ /* add --- add new property */
/* remove --- delete existing property */ /* remove --- delete existing property */
/* tolerance --- set property tolerance */ /* tolerance --- set property tolerance */
/* associate --- associate property with a pin */
/* merge --- (deprecated) */ /* merge --- (deprecated) */
/* or */ /* or */
/* netgen::property default */ /* netgen::property default */
@ -3415,11 +3416,11 @@ _netcmp_property(ClientData clientData,
char *options[] = { char *options[] = {
"add", "create", "remove", "delete", "tolerance", "merge", "serial", "add", "create", "remove", "delete", "tolerance", "merge", "serial",
"series", "parallel", NULL "series", "parallel", "associate", NULL
}; };
enum OptionIdx { enum OptionIdx {
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX, ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX,
SERIAL_IDX, SERIES_IDX, PARALLEL_IDX SERIAL_IDX, SERIES_IDX, PARALLEL_IDX, ASSOCIATE_IDX
}; };
int result, index, idx2; int result, index, idx2;
@ -3842,6 +3843,29 @@ _netcmp_property(ClientData clientData,
} }
break; break;
case ASSOCIATE_IDX:
if (objc == 3) {
Tcl_WrongNumArgs(interp, 1, objv, "{property_key pin_name} ...");
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
// Each value must be a duplet
result = Tcl_ListObjLength(interp, objv[i], &llen);
if ((result != TCL_OK) || (llen != 2)) {
Tcl_SetResult(interp, "Not a {key pin} pair list.",
NULL);
}
else {
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;
PropertyAssociatePin(tp->name, fnum, Tcl_GetString(tobj1),
Tcl_GetString(tobj2));
}
}
break;
case TOLERANCE_IDX: case TOLERANCE_IDX:
if (objc == 3) { if (objc == 3) {
Tcl_WrongNumArgs(interp, 1, objv, "{property_key tolerance} ..."); Tcl_WrongNumArgs(interp, 1, objv, "{property_key tolerance} ...");