mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-23 06:27:07 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f24915661 | ||
|
|
fdab366627 | ||
|
|
310d7512a0 | ||
|
|
86f52b164a | ||
|
|
4a47c3076d | ||
|
|
777498b30e | ||
|
|
8300531858 | ||
|
|
80378d8816 | ||
|
|
80d981a672 | ||
|
|
809a3c16e9 | ||
|
|
8d976e5686 | ||
|
|
577dfb5a28 | ||
|
|
46252ad6b9 | ||
|
|
564fab9667 | ||
|
|
0df4190e19 | ||
|
|
2f661ea256 | ||
|
|
fca075e1ed | ||
|
|
cc2a5e0ee6 | ||
|
|
b7a8d3cfda | ||
|
|
0205902baa |
+995
-256
File diff suppressed because it is too large
Load Diff
@@ -34,6 +34,7 @@ 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);
|
||||
extern int PropertyOptimize(struct objlist *ob, struct nlist *tp, int run, int serial);
|
||||
|
||||
extern int CreateCompareQueue(char *, int, char *, int);
|
||||
extern int GetCompareQueueTop(char **, int *, char **, int *);
|
||||
|
||||
+426
-127
@@ -948,10 +948,10 @@ PropertyTolerance(char *name, int fnum, char *key, int ival, double dval)
|
||||
switch (kl->type) {
|
||||
case PROP_DOUBLE:
|
||||
case PROP_VALUE:
|
||||
case PROP_STRING:
|
||||
kl->slop.dval = dval;
|
||||
break;
|
||||
case PROP_INTEGER:
|
||||
case PROP_STRING:
|
||||
case PROP_EXPRESSION:
|
||||
kl->slop.ival = ival;
|
||||
break;
|
||||
@@ -1103,15 +1103,15 @@ struct property *PropertyInteger(char *name, int fnum, char *key,
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
struct property *PropertyString(char *name, int fnum, char *key, int range,
|
||||
struct property *PropertyString(char *name, int fnum, char *key, double dval,
|
||||
char *pdefault)
|
||||
{
|
||||
struct property *kl = NULL;
|
||||
struct nlist *tc;
|
||||
|
||||
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
||||
PropertyString(name, Circuit1->file, key, range, pdefault);
|
||||
PropertyString(name, Circuit2->file, key, range, pdefault);
|
||||
PropertyString(name, Circuit1->file, key, dval, pdefault);
|
||||
PropertyString(name, Circuit2->file, key, dval, pdefault);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1127,7 +1127,7 @@ struct property *PropertyString(char *name, int fnum, char *key, int range,
|
||||
kl->idx = 0;
|
||||
kl->merge = MERGE_NONE;
|
||||
kl->type = PROP_STRING;
|
||||
kl->slop.ival = (range >= 0) ? range : 0;
|
||||
kl->slop.dval = dval;
|
||||
if (pdefault != NULL)
|
||||
kl->pdefault.string = strsave(pdefault);
|
||||
else
|
||||
@@ -2054,7 +2054,7 @@ struct objlist *LinkProperties(char *model, struct keyvalue *topptr)
|
||||
kl->idx = 0;
|
||||
kl->merge = MERGE_NONE;
|
||||
kl->type = PROP_STRING;
|
||||
kl->slop.ival = 0;
|
||||
kl->slop.dval = 0.0;
|
||||
kl->pdefault.string = NULL;
|
||||
HashPtrInstall(kl->key, kl, &(cell->propdict));
|
||||
}
|
||||
@@ -2318,7 +2318,7 @@ void ResolveProperties(char *name1, int file1, char *name2, int file2)
|
||||
switch (kl1->type) {
|
||||
case PROP_STRING:
|
||||
kl2 = PropertyString(tp2->name, tp2->file, kl1->key,
|
||||
kl1->slop.ival, kl1->pdefault.string);
|
||||
kl1->slop.dval, kl1->pdefault.string);
|
||||
break;
|
||||
case PROP_INTEGER:
|
||||
kl2 = PropertyInteger(tp2->name, tp2->file, kl1->key,
|
||||
@@ -2350,7 +2350,7 @@ void ResolveProperties(char *name1, int file1, char *name2, int file2)
|
||||
switch (kl2->type) {
|
||||
case PROP_STRING:
|
||||
kl1 = PropertyString(tp1->name, tp1->file, kl2->key,
|
||||
kl2->slop.ival, kl2->pdefault.string);
|
||||
kl2->slop.dval, kl2->pdefault.string);
|
||||
break;
|
||||
case PROP_INTEGER:
|
||||
kl1 = PropertyInteger(tp1->name, tp1->file, kl2->key,
|
||||
@@ -2849,6 +2849,157 @@ void ConnectAllNodes(char *model, int file)
|
||||
if (ob->node == -1) ob->node = nodenum++;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Serial and Parallel combination: */
|
||||
/* All devices of the same type that exist in serial 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 */
|
||||
/* and parallel, as in a resistor network, there is a set of rules: */
|
||||
/* */
|
||||
/* Running parallel and serial 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. */
|
||||
/* 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 */
|
||||
/* 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 */
|
||||
/* "open" and tag first property of B with "close". */
|
||||
/* 2) If B has serial components then tag first property of B with */
|
||||
/* "open". */
|
||||
/* */
|
||||
/* For a serial 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". */
|
||||
/* */
|
||||
/* Tags are indicated by a property named "_tag" which has a string */
|
||||
/* value of ordered characters, "+" for serial, "(" 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 */
|
||||
/* two circuits being matched will be treated as a property error. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* add_prop_tag --- add the tag character tagc to the property list of */
|
||||
/* obr. obr points to the first property record. */
|
||||
|
||||
int add_prop_tag(struct objlist *obr, char tagc)
|
||||
{
|
||||
struct objlist *nob;
|
||||
int i, k, l;
|
||||
struct valuelist *kv, *kv2;
|
||||
int hastag;
|
||||
char *tmpstr;
|
||||
|
||||
hastag = FALSE;
|
||||
for (nob = obr; nob->next && nob->next->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")) {
|
||||
hastag = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hastag) {
|
||||
if (nob == obr) {
|
||||
// If _tag was first in the list, then just prepend tagc to the tag value
|
||||
tmpstr = kv->value.string;
|
||||
kv->value.string = (char *)MALLOC(strlen(tmpstr) + 2);
|
||||
sprintf(kv->value.string, "%c%s", tagc, tmpstr);
|
||||
FREE(tmpstr);
|
||||
}
|
||||
else {
|
||||
// Add a _tag key to the first property list and set value to tagc
|
||||
|
||||
kv = &(obr->instance.props[i]);
|
||||
k = 0;
|
||||
for (k = 0; ; k++) {
|
||||
kv = &(obr->instance.props[k]);
|
||||
if (kv->type == PROP_ENDLIST)
|
||||
break;
|
||||
}
|
||||
kv2 = (struct valuelist *)MALLOC((k + 2) * sizeof(struct valuelist));
|
||||
kv2->key = strsave("_tag");
|
||||
kv2->type = PROP_STRING;
|
||||
/* Value is set to tagc */
|
||||
kv2->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv2->value.string, "%c", tagc);
|
||||
for (l = 0; l <= k; l++)
|
||||
kv2[l + 1] = obr->instance.props[l];
|
||||
FREE(obr->instance.props);
|
||||
obr->instance.props = kv2;
|
||||
}
|
||||
}
|
||||
return hastag;
|
||||
}
|
||||
|
||||
/* add_balancing_close --- find the number of unbalanced 'open' */
|
||||
/* records in ob1's property list, and prepend the correct number of */
|
||||
/* 'C' closures to the property list of ob2. */
|
||||
|
||||
void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
|
||||
{
|
||||
struct objlist *nob;
|
||||
int i, k, l;
|
||||
struct valuelist *kv, *kv2;
|
||||
int opentags;
|
||||
char *tmpstr, *tag;
|
||||
|
||||
/* Find the first property record in ob1. */
|
||||
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == PROPERTY)
|
||||
break;
|
||||
if (nob->type != PROPERTY) return; // shouldn't happen
|
||||
|
||||
opentags = 0;
|
||||
for (; nob->next && nob->next->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 == '(') opentags++;
|
||||
else if (*tag == ')') opentags--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opentags == 0) return;
|
||||
|
||||
/* Find the first property record in ob2. */
|
||||
for (nob = ob2->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == PROPERTY)
|
||||
break;
|
||||
if (nob->type != PROPERTY) return; // shouldn't happen
|
||||
|
||||
// This is slow but it's the easiest way to do it
|
||||
while (opentags-- > 0) add_prop_tag(nob, ')');
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Find all devices that are of the same class and check for parallel */
|
||||
/* combinations, and combine them where found, adjusting property "M" */
|
||||
@@ -2865,23 +3016,26 @@ void ConnectAllNodes(char *model, int file)
|
||||
/* */
|
||||
/* If the device has permutable pins, then duplicate hashes are made */
|
||||
/* for each permutation. */
|
||||
/* */
|
||||
/* Return the number of devices merged. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void CombineParallel(char *model, int file)
|
||||
int CombineParallel(char *model, int file)
|
||||
{
|
||||
struct nlist *tp, *tsub;
|
||||
struct objlist *ob, *ob2, *nextob;
|
||||
struct objlist *sob, *lob, *tlob, *nob, *pob, *obr;
|
||||
struct objlist *sob, *lob, *nob, *pob, *obr;
|
||||
struct objlist *propfirst, *proplast, *spropfirst, *sproplast;
|
||||
struct hashdict devdict;
|
||||
struct Permutation *perm;
|
||||
size_t pcnt;
|
||||
int dcnt = 0;
|
||||
int i, dcnt = 0, hastag;
|
||||
char *pstr, *p2str, *pptr;
|
||||
struct valuelist *kv;
|
||||
|
||||
if ((tp = LookupCellFile(model, file)) == NULL) {
|
||||
Printf("Cell: %s does not exist.\n", model);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
InitializeHashTable(&devdict, OBJHASHSIZE);
|
||||
@@ -2890,12 +3044,21 @@ void CombineParallel(char *model, int file)
|
||||
for (ob = tp->cell; ob; ) {
|
||||
if (ob->type == FIRSTPIN) {
|
||||
|
||||
/* Watch for devices prohibited from parallel combination. */
|
||||
/* All devices allow parallel combination by default. */
|
||||
|
||||
tsub = LookupCellFile(ob->model.class, file);
|
||||
if ((tsub != NULL) && (tsub->flags & COMB_NO_PARALLEL)) {
|
||||
lob = ob;
|
||||
ob = ob->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ------------------------------------*/
|
||||
/* Generate hash key from pins */
|
||||
/* Handle pin permuations */
|
||||
/* ------------------------------------*/
|
||||
|
||||
tsub = LookupCellFile(ob->model.class, file);
|
||||
if ((tsub != NULL) && (tsub->permutes != NULL))
|
||||
perm = tsub->permutes;
|
||||
else
|
||||
@@ -2903,14 +3066,17 @@ void CombineParallel(char *model, int file)
|
||||
|
||||
pcnt = strlen(ob->model.class) + 2;
|
||||
pptr = (char *)pcnt;
|
||||
|
||||
propfirst = proplast = NULL;
|
||||
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
|
||||
tlob = ob2;
|
||||
pob = ob2;
|
||||
pcnt += 10;
|
||||
}
|
||||
if (ob2->type == PROPERTY) propfirst = ob2;
|
||||
|
||||
/* Find last record in device and first record in next object */
|
||||
while (ob2 && ob2->type == PROPERTY) {
|
||||
tlob = ob2;
|
||||
proplast = ob2;
|
||||
ob2 = ob2->next;
|
||||
}
|
||||
nextob = ob2;
|
||||
@@ -2967,70 +3133,157 @@ void CombineParallel(char *model, int file)
|
||||
FREE((char *)p2str);
|
||||
}
|
||||
|
||||
/* Set last object ptr to end of this record */
|
||||
lob = tlob;
|
||||
/* Move last object marker to end of sob record */
|
||||
if (proplast != NULL)
|
||||
lob = proplast;
|
||||
else
|
||||
lob = pob;
|
||||
}
|
||||
else {
|
||||
/* Remove parallel device "ob" and append properties of */
|
||||
/* Find parallel device "ob" and append properties of */
|
||||
/* "sob" to it. If "ob" does not have properties, then */
|
||||
/* create a property record and set property "M" to 2. */
|
||||
|
||||
for (obr = ob; obr != ob2 ; ) {
|
||||
/* Find last non-property record of sob ( = pob) */
|
||||
/* Find first property record of sob ( = spropfirst) */
|
||||
/* Find last property record of sob ( = sproplast) */
|
||||
|
||||
spropfirst = sproplast = NULL;
|
||||
for (ob2 = sob; ob2->type > FIRSTPIN || ob2 == sob; ob2 = ob2->next)
|
||||
pob = ob2;
|
||||
if (ob2->type == PROPERTY) spropfirst = ob2;
|
||||
for (; ob2->type == PROPERTY; ob2 = ob2->next)
|
||||
sproplast = ob2;
|
||||
|
||||
if (spropfirst == NULL) {
|
||||
/* Create new property instance record if one doesn't exist */
|
||||
nob = GetObject();
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = strsave(sob->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "M" and set to 1 */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("M");
|
||||
kv->type = PROP_INTEGER;
|
||||
kv->value.ival = 1;
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
kv->key = NULL;
|
||||
kv->type = PROP_ENDLIST;
|
||||
kv->value.ival = 0;
|
||||
|
||||
nob->next = pob->next;
|
||||
pob->next = nob;
|
||||
|
||||
if (lob == pob) lob = nob;
|
||||
spropfirst = sproplast = nob;
|
||||
}
|
||||
if (propfirst == NULL) {
|
||||
/* Create new property instance record if one doesn't exist */
|
||||
nob = GetObject();
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = strsave(ob->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "M" and set to 1 */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("M");
|
||||
kv->type = PROP_INTEGER;
|
||||
kv->value.ival = 1;
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
kv->key = NULL;
|
||||
kv->type = PROP_ENDLIST;
|
||||
kv->value.ival = 0;
|
||||
|
||||
/* Append to sob's property list */
|
||||
nob->next = sproplast->next;
|
||||
sproplast->next = nob;
|
||||
if (lob == sproplast) lob = nob;
|
||||
}
|
||||
|
||||
if (propfirst != NULL) {
|
||||
|
||||
// Serial/Parallel logic:
|
||||
// If propfirst has _tag in properties,
|
||||
// then add an "open" tag at propfirst
|
||||
add_prop_tag(propfirst, '(');
|
||||
|
||||
// if spropfirst has _tag in properties then add an "open" tag
|
||||
// to spropfirst and a "close" tag to propfirst
|
||||
if (add_prop_tag(spropfirst, '(')) add_prop_tag(propfirst, ')');
|
||||
|
||||
/* Append ob's property list to sob */
|
||||
proplast->next = sproplast->next;
|
||||
sproplast->next = propfirst;
|
||||
if (lob == sproplast) lob = proplast;
|
||||
}
|
||||
|
||||
/* Link up around object to be removed */
|
||||
lob->next = nextob;
|
||||
|
||||
/* Remove the object */
|
||||
for (obr = ob; obr != propfirst && obr != nextob; ) {
|
||||
nob = obr->next;
|
||||
FreeObjectAndHash(obr, tp);
|
||||
obr = nob;
|
||||
}
|
||||
dcnt++;
|
||||
/* Find (first) property record of sob */
|
||||
for (pob = sob->next; pob->type > FIRSTPIN; pob = pob->next) {
|
||||
if (pob->next->type == PROPERTY)
|
||||
break;
|
||||
else if (pob->next->type == FIRSTPIN) {
|
||||
/* Create new property instance record if one doesn't exist */
|
||||
nob = GetObject();
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = strsave(sob->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "M" and set to 1 */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("M");
|
||||
kv->type = PROP_INTEGER;
|
||||
kv->value.ival = (obr->type != PROPERTY) ? 2 : 1;
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
kv->key = NULL;
|
||||
kv->type = PROP_ENDLIST;
|
||||
kv->value.ival = 0;
|
||||
|
||||
nob->next = pob->next;
|
||||
pob->next = nob;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lob == pob) lob = tlob;
|
||||
if (obr->type == PROPERTY) {
|
||||
/* Pull out property records and append to sob's */
|
||||
/* property list */
|
||||
tlob->next = pob->next;
|
||||
pob->next = obr;
|
||||
}
|
||||
lob->next = nextob;
|
||||
}
|
||||
FREE((char *)pstr);
|
||||
}
|
||||
else
|
||||
else {
|
||||
lob = ob;
|
||||
nextob = ob->next;
|
||||
}
|
||||
ob = nextob;
|
||||
}
|
||||
HashKill(&devdict);
|
||||
if (dcnt > 0) {
|
||||
Fprintf(stdout, "Class %s: Merged %d devices.\n", model, dcnt);
|
||||
}
|
||||
return dcnt;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* For the purposes of serial 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 */
|
||||
/* two ports, but any additional ports (such as a substrate connection) */
|
||||
/* must be the same for all devices in series. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
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. */
|
||||
if (ob1 == ob2) return FALSE;
|
||||
|
||||
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == 3) break;
|
||||
|
||||
for (pob = ob2->next; pob && pob->type != FIRSTPIN; pob = pob->next)
|
||||
if (pob->type == 3) break;
|
||||
|
||||
while (nob && pob && nob->type > FIRSTPIN && pob->type > FIRSTPIN) {
|
||||
if (nob->node != pob->node)
|
||||
return FALSE;
|
||||
nob = nob->next;
|
||||
pob = pob->next;
|
||||
}
|
||||
|
||||
if (nob->type > FIRSTPIN || pob->type > FIRSTPIN) return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -3041,20 +3294,30 @@ void CombineParallel(char *model, int file)
|
||||
/* */
|
||||
/* This routine depends on CombineParallel() being run first so that no */
|
||||
/* parallel devices are reported as serial. */
|
||||
/* */
|
||||
/* Return the number of devices merged. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void CombineSerial(char *model, int file)
|
||||
int CombineSerial(char *model, int file)
|
||||
{
|
||||
struct nlist *tp, *tp2;
|
||||
struct objlist ***instlist;
|
||||
struct objlist *ob, *ob2, *obs, *obp, *obn;
|
||||
int i, j;
|
||||
int i, j, scnt = 0;
|
||||
struct valuelist *kv;
|
||||
|
||||
// To avoid posting a non-working version, serial combination is
|
||||
// disabled here until code is finished to compare the serial/parallel
|
||||
// property networks.
|
||||
// return 0;
|
||||
|
||||
if ((tp = LookupCellFile(model, file)) == NULL) {
|
||||
Printf("Cell: %s does not exist.\n", model);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
/* Diagnostic */
|
||||
/* Printf("CombineSerial start model = %s file = %d\n", model, file); */
|
||||
|
||||
instlist = (struct objlist ***)CALLOC((tp->nodename_cache_maxnodenum + 1),
|
||||
sizeof(struct objlist **));
|
||||
|
||||
@@ -3062,105 +3325,127 @@ void CombineSerial(char *model, int file)
|
||||
if ((ob->type >= FIRSTPIN) && (ob->node >= 0)) {
|
||||
if (ob->type == FIRSTPIN)
|
||||
obp = ob; // Save pointer to first pin of device
|
||||
|
||||
if (instlist[ob->node] == NULL) {
|
||||
/* Node has not been seen before, so add it to list */
|
||||
instlist[ob->node] = (struct objlist **)CALLOC(2,
|
||||
sizeof(struct objlist *));
|
||||
|
||||
/* For now, simple rule: Device must be a resistor */
|
||||
/* Device must be marked as able to be combined in serial. */
|
||||
/* Note that devices with more than two pins are expected */
|
||||
/* to serial 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. */
|
||||
/* NOTE: Arbitrarily, the first two pins of a device are */
|
||||
/* assumed to be the ones that make serial connections. */
|
||||
/* Additional pins, if any, do not. */
|
||||
|
||||
tp2 = LookupCellFile(ob->model.class, file);
|
||||
if (tp2->class != CLASS_RES)
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
else
|
||||
if ((tp2->flags & COMB_SERIAL) && (ob->type <= 2))
|
||||
instlist[ob->node][0] = obp;
|
||||
else
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
}
|
||||
else if (instlist[ob->node][0] == NULL) {
|
||||
/* Node is not valid for serial connection */
|
||||
}
|
||||
else if (instlist[ob->node][1] == NULL) {
|
||||
/* Check if first instance is the same type */
|
||||
if ((*matchfunc)(instlist[ob->node][0]->model.class, ob->model.class))
|
||||
instlist[ob->node][1] = obp;
|
||||
if ((*matchfunc)(instlist[ob->node][0]->model.class, ob->model.class)) {
|
||||
if (check_pin_nodes(instlist[ob->node][0], obp))
|
||||
instlist[ob->node][1] = obp;
|
||||
else
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
}
|
||||
else
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
}
|
||||
else {
|
||||
/* More than two devices connect here, so invalidate */
|
||||
instlist[ob->node][0] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i <= tp->nodename_cache_maxnodenum; i++) {
|
||||
if (instlist[i] != NULL) {
|
||||
if ((instlist[i][0] != NULL) && (instlist[i][1] != NULL)) {
|
||||
Fprintf(stdout, "Found serial instances %s and %s\n",
|
||||
int k, l;
|
||||
struct valuelist *kv2;
|
||||
|
||||
/* Diagnostic */
|
||||
/* Fprintf(stdout, "Found serial instances %s and %s\n",
|
||||
instlist[i][0]->instance.name,
|
||||
instlist[i][1]->instance.name);
|
||||
instlist[i][1]->instance.name); */
|
||||
scnt++;
|
||||
|
||||
/* To maintain knowledge of the topology, each device gets */
|
||||
/* a parameter 'S', string value set to "<node1>/<node2>". */
|
||||
/* To maintain knowledge of the topology, 2nd device gets */
|
||||
/* a parameter '_tag', string value set to "+". */
|
||||
|
||||
for (j = 0; j <= 1; j++) {
|
||||
for (obp = instlist[i][j]; ; obp = obp->next) {
|
||||
int found = FALSE;
|
||||
int k, l;
|
||||
char *nodename1, *nodename2;
|
||||
struct valuelist *kv2;
|
||||
for (obn = instlist[i][1]; obn->next &&
|
||||
obn->next->type != PROPERTY &&
|
||||
obn->next->type != FIRSTPIN; obn = obn->next);
|
||||
obp = obn->next;
|
||||
|
||||
nodename1 = tp->nodename_cache[instlist[i][j]->node]->name;
|
||||
nodename2 = tp->nodename_cache[instlist[i][j]->next->node]->name;
|
||||
if (obp->type == PROPERTY) {
|
||||
/* Add to properties */
|
||||
k = 0;
|
||||
for (k = 0; ; k++) {
|
||||
kv = &(obp->instance.props[k]);
|
||||
if (kv->type == PROP_ENDLIST)
|
||||
break;
|
||||
}
|
||||
if (obp == NULL || obp->type == FIRSTPIN) {
|
||||
struct objlist *nob;
|
||||
/* No property record, so insert one */
|
||||
nob = GetObject();
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = (obp->model.class == NULL) ? NULL :
|
||||
strsave(obp->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "_tag" */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("_tag");
|
||||
kv->type = PROP_STRING;
|
||||
/* Value is set to "+" */
|
||||
kv->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv->value.string, "+");
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
kv->key = NULL;
|
||||
kv->type = PROP_ENDLIST;
|
||||
kv->value.ival = 0;
|
||||
|
||||
nob->next = obp;
|
||||
obn->next = nob;
|
||||
}
|
||||
else if (obp->type == PROPERTY) {
|
||||
/* Add to properties */
|
||||
k = 0;
|
||||
for (k = 0; ; k++) {
|
||||
kv = &(obp->instance.props[k]);
|
||||
if (kv->type == PROP_ENDLIST) {
|
||||
kv2 = (struct valuelist *)MALLOC((k + 2) *
|
||||
sizeof(struct valuelist));
|
||||
kv2->key = strsave("S");
|
||||
kv2->key = strsave("_tag");
|
||||
kv2->type = PROP_STRING;
|
||||
/* Value is set to "<node1>/<node2>" */
|
||||
kv2->value.string = (char *)MALLOC(strlen(nodename1) +
|
||||
strlen(nodename2) + 2);
|
||||
sprintf(kv2->value.string, "%s/%s", nodename1, nodename2);
|
||||
|
||||
/* Value is set to "+" */
|
||||
kv2->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv2->value.string, "+");
|
||||
for (l = 0; l <= k; l++)
|
||||
kv2[l + 1] = obp->instance.props[l];
|
||||
kv2[l + 1] = obp->instance.props[l];
|
||||
FREE(obp->instance.props);
|
||||
obp->instance.props = kv2;
|
||||
found = TRUE;
|
||||
}
|
||||
if (obp->next == NULL || obp->next->type == FIRSTPIN) {
|
||||
struct objlist *nob;
|
||||
/* No property record, so insert one */
|
||||
if (found == FALSE) {
|
||||
nob = GetObject();
|
||||
nob->type = PROPERTY;
|
||||
nob->name = strsave("properties");
|
||||
nob->node = -2; /* Don't report as disconnected node */
|
||||
nob->model.class = strsave(obp->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "S" */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("S");
|
||||
kv->type = PROP_STRING;
|
||||
/* Value is set to "<node1>/<node2>" */
|
||||
kv->value.string = (char *)MALLOC(strlen(nodename1) +
|
||||
strlen(nodename2) + 2);
|
||||
sprintf(kv->value.string, "%s/%s", nodename1, nodename2);
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
kv->key = NULL;
|
||||
kv->type = PROP_ENDLIST;
|
||||
kv->value.ival = 0;
|
||||
|
||||
nob->next = obp->next;
|
||||
obp->next = nob;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (!strcmp(kv->key, "_tag")) {
|
||||
int l = strlen(kv->value.string);
|
||||
char *newstr = (char *)MALLOC(l + 2);
|
||||
sprintf(newstr, "S%s", kv->value.string);
|
||||
FREE(kv->value.string);
|
||||
kv->value.string = newstr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3185,6 +3470,10 @@ void CombineSerial(char *model, int file)
|
||||
ob2->next = obs->next;
|
||||
if (obs->next) obs->next = NULL; // Terminate 2nd instance record
|
||||
|
||||
/* If 1st device has unbalanced 'open' records, then add 'close' */
|
||||
/* records to the 2nd device to balance. */
|
||||
add_balancing_close(instlist[i][0], instlist[i][1]);
|
||||
|
||||
/* Move property record(s) of the 2nd device to the first */
|
||||
for (obs = instlist[i][1]; obs && obs->type != PROPERTY; obs = obs->next);
|
||||
while (obs && (obs->type == PROPERTY)) {
|
||||
@@ -3197,10 +3486,19 @@ void CombineSerial(char *model, int file)
|
||||
/* If 2nd device appears anywhere else in the serial device */
|
||||
/* list, replace it with the 1st device. */
|
||||
for (j = i + 1; j <= tp->nodename_cache_maxnodenum; j++) {
|
||||
if (instlist[j] == NULL) continue;
|
||||
|
||||
if (instlist[j][0] == instlist[i][1])
|
||||
instlist[j][0] = instlist[i][0];
|
||||
if (instlist[j][1] == instlist[i][1])
|
||||
instlist[j][1] = instlist[i][0];
|
||||
|
||||
/* If instlist[j]'s two entries point to the same device */
|
||||
/* then invalidate it. */
|
||||
if (instlist[j][0] == instlist[j][1]) {
|
||||
FREE(instlist[j]);
|
||||
instlist[j] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free 2nd device's object */
|
||||
@@ -3224,6 +3522,7 @@ void CombineSerial(char *model, int file)
|
||||
}
|
||||
}
|
||||
FREE(instlist);
|
||||
return scnt;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
+5
-5
@@ -33,7 +33,7 @@ extern struct property *PropertyDouble(char *name, int fnum, char *key,
|
||||
extern struct property *PropertyInteger(char *name, int fnum, char *key,
|
||||
int slop, int pdefault);
|
||||
extern struct property *PropertyString(char *name, int fnum, char *key,
|
||||
int range, char *pdefault);
|
||||
double slop, char *pdefault);
|
||||
extern int PropertyDelete(char *name, int fnum, char *key);
|
||||
extern int PropertyTolerance(char *name, int fnum, char *key, int ival,
|
||||
double dval);
|
||||
@@ -100,9 +100,9 @@ extern char *Res(char *fname, char *inststr, char *, char *);
|
||||
extern char *XLine(char *fname, char *inststr, char *, char *, char *, char *);
|
||||
extern char *Inductor(char *fname, char *inststr, char *, char *);
|
||||
|
||||
extern int StringIsValue(char *);
|
||||
extern int StringIsValue(char *);
|
||||
extern char *ConvertParam(char *);
|
||||
extern int ConvertStringToFloat(char *, double *);
|
||||
extern int ConvertStringToFloat(char *, double *);
|
||||
extern char *ScaleStringFloatValue(char *, double);
|
||||
extern void join(char *node1, char *node2);
|
||||
extern void Connect(char *tplt1, char *tplt2);
|
||||
@@ -116,8 +116,8 @@ extern void FlattenCurrent();
|
||||
extern void ConvertGlobals(char *name, int fnum);
|
||||
extern int CleanupPins(char *name, int fnum);
|
||||
extern void ConnectAllNodes(char *model, int fnum);
|
||||
extern void CombineParallel(char *model, int fnum);
|
||||
extern void CombineSerial(char *model, int fnum);
|
||||
extern int CombineParallel(char *model, int fnum);
|
||||
extern int CombineSerial(char *model, int fnum);
|
||||
extern int NoDisconnectedNodes;
|
||||
extern int PropertyKeyMatch(char *, char *);
|
||||
extern int PropertyValueMatch(char *, char *);
|
||||
|
||||
+7
-5
@@ -104,10 +104,12 @@ 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 merge */
|
||||
#define MERGE_ADD_CRIT 2 /* Properties sum with device 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 */
|
||||
|
||||
/* Note: A "critical" merge means that the property causes the number of */
|
||||
/* devices to change. e.g., transistor width is critical; transistor drain */
|
||||
@@ -213,10 +215,10 @@ struct nlist {
|
||||
#define CELL_PROPSMATCHED 0x10 /* properties matched to matching cell */
|
||||
#define CELL_DUPLICATE 0x20 /* cell has a duplicate */
|
||||
|
||||
/* Flags for combination allowances */
|
||||
/* Flags for combination allowances and prohibitions */
|
||||
|
||||
#define COMB_SERIAL 0x20
|
||||
#define COMB_PARALLEL 0x40
|
||||
#define COMB_SERIAL 0x40
|
||||
#define COMB_NO_PARALLEL 0x80
|
||||
|
||||
extern struct nlist *CurrentCell;
|
||||
extern struct objlist *CurrentTail;
|
||||
|
||||
+1
-1
@@ -795,7 +795,7 @@ skip_ends:
|
||||
kl->key = strsave(nexttok);
|
||||
kl->idx = 0;
|
||||
kl->type = PROP_STRING;
|
||||
kl->slop.ival = 0;
|
||||
kl->slop.dval = 0.0;
|
||||
kl->pdefault.string = strsave(eqptr + 1);
|
||||
HashPtrInstall(nexttok, kl, &spiceparams);
|
||||
}
|
||||
|
||||
+79
-16
@@ -497,7 +497,9 @@ CommonParseCell(Tcl_Interp *interp, Tcl_Obj *objv,
|
||||
fnum = -1;
|
||||
}
|
||||
else
|
||||
filename = Tcl_GetString(fobj);
|
||||
/* Both file numbers have been provided, so a */
|
||||
/* filename is not required. */
|
||||
filename = NULL;
|
||||
}
|
||||
else {
|
||||
filename = NULL;
|
||||
@@ -2022,10 +2024,6 @@ _netcmp_compare(ClientData clientData,
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
// WIP!
|
||||
// CleanupPins(name1, fnum1); // Remove unconnected pins
|
||||
// CleanupPins(name2, fnum2); // Remove unconnected pins
|
||||
|
||||
UniquePins(name1, fnum1); // Check for and remove duplicate pins
|
||||
UniquePins(name2, fnum2); // Check for and remove duplicate pins
|
||||
|
||||
@@ -2039,10 +2037,6 @@ _netcmp_compare(ClientData clientData,
|
||||
// but define global nodes that are brought out as ports by
|
||||
// ConvertGlobals().
|
||||
|
||||
// WIP!
|
||||
// CleanupPins(name1, fnum1);
|
||||
// CleanupPins(name2, fnum2);
|
||||
|
||||
CreateTwoLists(name1, fnum1, name2, fnum2);
|
||||
while (PrematchLists(name1, fnum1, name2, fnum2) > 0) {
|
||||
Fprintf(stdout, "Making another compare attempt.\n");
|
||||
@@ -2973,6 +2967,13 @@ _netcmp_equate(ClientData clientData,
|
||||
/* merge --- set property merge behavior */
|
||||
/* or */
|
||||
/* netgen::property default */
|
||||
/* or */
|
||||
/* netgen::property <device>|<model> <option> */
|
||||
/* yes|no */
|
||||
/* Where <option> is one of: */
|
||||
/* serial --- allow/prohibit serial combination */
|
||||
/* parallel --- allow/prohibit parallel combination */
|
||||
/* */
|
||||
/* Formerly: (none) */
|
||||
/* Results: */
|
||||
/* Side Effects: */
|
||||
@@ -2990,10 +2991,12 @@ _netcmp_property(ClientData clientData,
|
||||
int ival, argstart;
|
||||
|
||||
char *options[] = {
|
||||
"add", "create", "remove", "delete", "tolerance", "merge", NULL
|
||||
"add", "create", "remove", "delete", "tolerance", "merge", "serial",
|
||||
"parallel", NULL
|
||||
};
|
||||
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, PARALLEL_IDX
|
||||
};
|
||||
int result, index, idx2;
|
||||
|
||||
@@ -3005,10 +3008,18 @@ _netcmp_property(ClientData clientData,
|
||||
};
|
||||
|
||||
char *mergeoptions[] = {
|
||||
"none", "add", "add_critical", "par", "par_critical", NULL
|
||||
"none", "add", "add_critical", "par", "par_critical",
|
||||
"parallel", "parallel_critical", "ser_critical", "ser",
|
||||
"serial_critical", "serial", NULL
|
||||
};
|
||||
enum MergeOptionIdx {
|
||||
NONE_IDX, ADD_ONLY_IDX, ADD_CRIT_IDX, PAR_ONLY_IDX, PAR_CRIT_IDX
|
||||
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
|
||||
};
|
||||
char *yesno[] = {
|
||||
"on", "yes", "true", "enable", "allow",
|
||||
"off", "no", "false", "disable", "prohibit", NULL
|
||||
};
|
||||
|
||||
if (objc < 2) {
|
||||
@@ -3025,8 +3036,8 @@ _netcmp_property(ClientData clientData,
|
||||
/* compare source/drain area and perimeter. */
|
||||
|
||||
tp = FirstCell();
|
||||
while (tp != NULL) {
|
||||
switch (tp->class) {
|
||||
while (tp != NULL) {
|
||||
switch (tp->class) {
|
||||
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
|
||||
case CLASS_NMOS4: case CLASS_PMOS4: case CLASS_FET4:
|
||||
case CLASS_FET:
|
||||
@@ -3037,7 +3048,9 @@ _netcmp_property(ClientData clientData,
|
||||
PropertyDelete(tp->name, tp->file, "pd");
|
||||
break;
|
||||
case CLASS_RES: case CLASS_RES3:
|
||||
PropertyMerge(tp->name, tp->file, "l", MERGE_PAR_CRIT);
|
||||
PropertyMerge(tp->name, tp->file, "w", MERGE_ADD_CRIT);
|
||||
PropertyMerge(tp->name, tp->file, "l", MERGE_SER_CRIT);
|
||||
tp->flags |= COMB_SERIAL;
|
||||
break;
|
||||
case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3:
|
||||
// NOTE: No attempt to combine area, width, or length;
|
||||
@@ -3046,6 +3059,7 @@ _netcmp_property(ClientData clientData,
|
||||
break;
|
||||
case CLASS_INDUCTOR:
|
||||
PropertyMerge(tp->name, tp->file, "value", MERGE_PAR_CRIT);
|
||||
tp->flags |= COMB_SERIAL;
|
||||
break;
|
||||
}
|
||||
tp = NextCell();
|
||||
@@ -3101,6 +3115,45 @@ _netcmp_property(ClientData clientData,
|
||||
argstart = 3;
|
||||
|
||||
switch (index) {
|
||||
case SERIAL_IDX:
|
||||
case PARALLEL_IDX:
|
||||
if (objc == 3) {
|
||||
if (index == SERIAL_IDX) {
|
||||
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_SERIAL) ? 1 : 0);
|
||||
Tcl_SetObjResult(interp, tobj1);
|
||||
return TCL_OK;
|
||||
}
|
||||
else {
|
||||
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_NO_PARALLEL) ? 0 : 1);
|
||||
Tcl_SetObjResult(interp, tobj1);
|
||||
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");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADD_IDX:
|
||||
case CREATE_IDX:
|
||||
@@ -3304,11 +3357,21 @@ _netcmp_property(ClientData clientData,
|
||||
mergeval = MERGE_ADD_CRIT;
|
||||
break;
|
||||
case PAR_ONLY_IDX:
|
||||
case PAR2_ONLY_IDX:
|
||||
mergeval = MERGE_PAR;
|
||||
break;
|
||||
case PAR_CRIT_IDX:
|
||||
case PAR2_CRIT_IDX:
|
||||
mergeval = MERGE_PAR_CRIT;
|
||||
break;
|
||||
case SER_CRIT_IDX:
|
||||
case SER2_CRIT_IDX:
|
||||
mergeval = MERGE_SER_CRIT;
|
||||
break;
|
||||
case SER_IDX:
|
||||
case SER2_IDX:
|
||||
mergeval = MERGE_SER;
|
||||
break;
|
||||
}
|
||||
PropertyMerge(tp->name, fnum, Tcl_GetString(tobj1), mergeval);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user