Finished basic implementation of matching device properties to

include calculations of effective width due to the addition of
width of multiple device instances in parallel.  The original
behavior of splitting all "M=" devices into individual instances
has been effectively inverted, instead combining all parallel
devices of the same class into one, with multiple property
records for devices with non-matching properties (e.g., width,
length, etc.).  Property matching combines devices with different
"critical properties" (e.g., FET gate width) if these are defined
in the setup using the "property merge" command.

Not yet done:  Matching of multiple property records when
critial properties are not defined, handling of critical properties
that combine in parallel instead of simple addition, handling of
devices that combine in series, and the combination of non-critical
properties (e.g., source and drain area, although these are usually
removed from matching).
This commit is contained in:
Tim Edwards 2016-07-11 08:41:07 -04:00
parent 758b5a249a
commit c7f27c909f
8 changed files with 3350 additions and 1098 deletions

View File

@ -1298,230 +1298,6 @@ typedef struct ecomplist {
ECompListPtr next;
} ECompList;
/*------------------------------------------------------*/
/* Split a device into multiple devices based on a */
/* critical property; e.g., MOSFET width. Devices with */
/* property name equal to kl->key and value "value" */
/* will be split into "ndev" devices with the property */
/* of each divided down by "ndev". */
/*------------------------------------------------------*/
void
SplitDevice(struct nlist *tc, struct nlist *cell, struct property *kl,
double value, int ndev, int file1, int file2, int which)
{
struct objlist *ob, *ob2, *newdevs, *ob3, *lastob, *nob;
struct nlist *tsub;
unsigned char found;
int file = (which == 0) ? file1 : file2;
newdevs = NULL; /* Linked list of the copied devices */
for (ob = tc->cell; ob; ob = ob->next) {
if (ob->type == FIRSTPIN) {
tsub = LookupCellFile(ob->model.class, file);
if (tsub == cell) {
// Advance ob to property list
found = 0;
for (ob2 = ob->next; ob2 && ob2->type != FIRSTPIN; ob2 = ob2->next) {
if (ob2->type == PROPERTY) {
struct valuelist *kv;
int i;
double dval = 0.0;
for (i = 0; ; i++) {
kv = &(ob2->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
if ((*matchfunc)(kv->key, kl->key)) {
switch(kv->type) {
case PROP_INTEGER:
dval = (double)kv->value.ival;
break;
case PROP_DOUBLE:
case PROP_VALUE:
dval = kv->value.dval;
break;
}
break;
}
}
/* To-do: Account for slop */
if (dval == value) {
switch(kv->type) {
case PROP_INTEGER:
kv->value.ival /= ndev;
found = 1;
break;
case PROP_DOUBLE:
case PROP_VALUE:
kv->value.dval /= ndev;
found = 1;
break;
}
}
}
if (found) break;
}
if (found) {
int i;
for (i = 1; i < ndev; i++) {
ob3 = CopyObjList(ob, 0); // Make exact copy
for (nob = ob3; nob->next != NULL; nob = nob->next);
nob->next = newdevs;
newdevs = ob3;
}
}
}
}
lastob = ob;
}
lastob->next = newdevs; // Append new devices to list
}
/*------------------------------------------------------*/
/* Survey a specific device in a cell and sort into a */
/* hash by critical property. */
/*------------------------------------------------------*/
void
SurveyDevice(struct nlist *tc, struct hashdict *devdict,
struct nlist *cell, struct property *kl,
int file1, int file2, int which)
{
struct objlist *ob, *ob2;
struct nlist *tsub, *teq;
ECompare *dcomp, *ncomp, *qcomp;
int file = (which == 0) ? file1 : file2;
int ofile = (which == 0) ? file2 : file1;
char *p1str, *p2str, *d1str, *d2str;
for (ob = tc->cell; ob; ob = ob->next) {
if (ob->type == FIRSTPIN) {
tsub = LookupCellFile(ob->model.class, file);
if (tsub == cell) {
p1str = (char *)MALLOC(strlen(ob->model.class) + 20);
sprintf(p1str, "%s", ob->model.class);
if (tsub->flags & CELL_DUPLICATE) {
// Always register a duplicate under the original name
d1str = strstr(p1str, "[[");
if (d1str) *d1str = '\0';
}
d1str = p1str + strlen(p1str);
sprintf(d1str, "::");
d1str += 2;
teq = LookupClassEquivalent(ob->model.class, file, ofile);
p2str = (char *)MALLOC(strlen(teq->name) + 20);
sprintf(p2str, "%s", teq->name);
if (teq->flags & CELL_DUPLICATE) {
// Always register a duplicate under the original name
d2str = strstr(p2str, "[[");
if (d2str) *d2str = '\0';
}
d2str = p2str + strlen(p2str);
sprintf(d2str, "::");
d2str += 2;
// Advance ob to property list
// To-do: Quantize values according to slop
for (ob2 = ob->next; ob2 && ob2->type != FIRSTPIN; ob2 = ob2->next) {
if (ob2->type == PROPERTY) {
struct valuelist *kv;
int i;
for (i = 0; ; i++) {
kv = &(ob2->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
if ((*matchfunc)(kv->key, kl->key)) {
switch(kv->type) {
case PROP_INTEGER:
sprintf(d1str, "%d", kv->value.ival);
sprintf(d2str, "%d", kv->value.ival);
break;
case PROP_DOUBLE:
case PROP_VALUE:
// To-do: Round to tolerance
sprintf(d1str, "%g", kv->value.dval);
sprintf(d2str, "%g", kv->value.dval);
break;
}
break;
}
}
break;
}
}
if (*d1str == '\0') {
// No critical property instanced, so use default
switch(kl->type) {
case PROP_INTEGER:
sprintf(d1str, "%d", kl->pdefault.ival);
sprintf(d2str, "%d", kl->pdefault.ival);
break;
case PROP_DOUBLE:
case PROP_VALUE:
// To-do: Round to tolerance
sprintf(d1str, "%g", kl->pdefault.dval);
sprintf(d2str, "%g", kl->pdefault.dval);
break;
}
}
// Create hash key from object class and the critical
// property affecting the device count.
dcomp = (ECompare *)HashInt2Lookup(p1str, file, devdict);
// Fill in the values for this device::property combination
if (dcomp == NULL) {
ncomp = (ECompare *)MALLOC(sizeof(ECompare));
if (which == 0) {
ncomp->num1 = 1;
ncomp->num2 = 0;
ncomp->cell1 = tsub;
ncomp->cell2 = teq;
}
else {
ncomp->num1 = 0;
ncomp->num2 = 1;
ncomp->cell2 = tsub;
ncomp->cell1 = teq;
}
ncomp->add1 = 0;
ncomp->add2 = 0;
ncomp->refcount = (char)1;
HashInt2PtrInstall(p1str, file, ncomp, devdict);
if (teq != NULL) {
qcomp = (ECompare *)HashInt2Lookup(p2str, ofile,
devdict);
if (qcomp == NULL) {
HashInt2PtrInstall(p2str, ofile, ncomp, devdict);
ncomp->refcount++;
}
}
}
else {
if (which == 0)
dcomp->num1++;
else
dcomp->num2++;
}
FREE(p1str);
FREE(p2str);
}
}
}
}
/*------------------------------------------------------*/
/* Survey the contents of a cell and sort into a hash */
/*------------------------------------------------------*/
@ -1867,176 +1643,6 @@ PrematchLists(char *name1, int file1, char *name2, int file2)
ecomp = (ECompare *)HashNext(&compdict);
}
// Check for cells, either low-level devices or subcircuits,
// that have properties allowing devices to be merged. If
// the classes of both cells are the same, and the number of
// instances is different, and merging the device with more
// instances improves the matching, then perform the merge.
ecomp = (ECompare *)HashFirst(&compdict);
while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) &&
(ecomp->cell1 != NULL) &&
(ecomp->cell2 != NULL) &&
(ecomp->cell1->classhash == ecomp->cell2->classhash)) {
struct hashdict devdict;
ECompare *dcomp;
// Determine if either device has mergeable properties. If so,
// sort the device into bins by critical (mergeable) property,
// and merge devices where merging makes a better match.
struct property *kl1, *kl2;
double slop = 0.0;
// Look for a mergeable property in cell1
kl1 = (struct property *)HashFirst(&(ecomp->cell1->propdict));
while (kl1 != NULL) {
if (kl1->merge == MERGE_ADD_CRIT || kl1->merge == MERGE_PAR_CRIT)
break;
kl1 = (struct property *)HashNext(&(ecomp->cell1->propdict));
}
// Look for the equivalent property in cell2 (mergeable or not).
// If cell1 had no mergeable properties, then look for one is cell2.
kl2 = (struct property *)HashFirst(&(ecomp->cell2->propdict));
while (kl2 != NULL) {
if (kl1 != NULL) {
if ((*matchfunc)(kl1->key, kl2->key))
break;
}
else if (kl2->merge == MERGE_ADD_CRIT || kl2->merge == MERGE_PAR_CRIT)
break;
kl2 = (struct property *)HashNext(&(ecomp->cell2->propdict));
}
if (kl2 != NULL) {
// Get slop value
switch (kl2->type) {
case PROP_INTEGER:
slop = (double)kl2->slop.ival;
break;
case PROP_DOUBLE:
case PROP_VALUE:
slop = kl2->slop.dval;
break;
}
}
// If cell2 had a mergeable property but cell1 didn't, then look
// through cell1 again to find the equivalent property.
if ((kl1 == NULL) && (kl2 != NULL)) {
kl1 = (struct property *)HashFirst(&(ecomp->cell1->propdict));
while (kl1 != NULL) {
if ((*matchfunc)(kl1->key, kl2->key))
break;
kl1 = (struct property *)HashNext(&(ecomp->cell1->propdict));
}
}
if (kl1 != NULL) {
// Get slop value
switch (kl1->type) {
case PROP_INTEGER:
slop = MAX(slop, (double)kl1->slop.ival);
break;
case PROP_DOUBLE:
case PROP_VALUE:
slop = MAX(slop, kl1->slop.dval);
break;
}
}
if ((kl1 != NULL) && (kl2 != NULL)) {
double dval, dval1, dval2, mindev, pd, df, dr;
unsigned char dosplit;
char *valptr;
int ndev;
// Create the device hash table
InitializeHashTable(&devdict, OBJHASHSIZE);
// Populate the device hash table
SurveyDevice(tc1, &devdict, ecomp->cell1, kl1, file1, file2, 0);
SurveyDevice(tc2, &devdict, ecomp->cell2, kl2, file1, file2, 1);
// Scan the device hash table. If devices can be merged
// and this improves the matching between cells, then do
// the merge.
mindev = 1E20;
dval1 = dval2 = 0.0;
dcomp = (ECompare *)HashFirst(&devdict);
while (dcomp != NULL) {
if ((dcomp->num1 == 0) || (dcomp->num2 == 0)) {
valptr = strstr(devdict.hashfirstptr->name, "::");
if (sscanf(valptr + 2, "%lg", &dval) == 1) {
if (dval < mindev) mindev = dval;
if (dcomp->num1 == 0)
dval2 += (dval * dcomp->num2) / dcomp->refcount;
else
dval1 += (dval * dcomp->num1) / dcomp->refcount;
}
}
dcomp = (ECompare *)HashNext(&devdict);
}
// If dval2 and dval1 agree within slop, and both are
// divisible by mindev, then break up all devices into
// sizes of mindev.
dosplit = 0;
pd = 2 * fabs(dval1 - dval2) / (dval1 + dval2);
if (pd < slop) {
df = dval1 / mindev;
dr = round(df);
pd = 2 * fabs(df - dr) / (df + dr);
if (pd < slop) dosplit = 1;
}
if (dosplit) {
dcomp = (ECompare *)HashFirst(&devdict);
while (dcomp != NULL) {
if (dcomp->num1 == 0 || dcomp->num2 == 0) {
valptr = strstr(devdict.hashfirstptr->name, "::");
sscanf(valptr + 2, "%lg", &dval);
ndev = (int)round(dval / mindev);
}
if (dcomp->num1 == 0) {
SplitDevice(tc2, ecomp->cell2, kl2, dval, ndev,
file1, file2, 1);
modified++;
}
else if (dcomp->num2 == 0) {
SplitDevice(tc1, ecomp->cell1, kl1, dval, ndev,
file1, file2, 0);
modified++;
}
dcomp = (ECompare *)HashNext(&devdict);
}
}
// Free the device hash table
dcomp = (ECompare *)HashFirst(&devdict);
while (dcomp != NULL) {
if (--dcomp->refcount == (char)0) FREE(dcomp);
dcomp = (ECompare *)HashNext(&devdict);
}
HashKill(&devdict);
}
}
ecomp = (ECompare *)HashNext(&compdict);
}
// Remove non-matching zero-value devices. This can
// be done on a per-instance basis.

2529
base/flatten.c.bak Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1800,11 +1800,12 @@ struct Node *CreateNodeList(char *name, short graph)
return (head);
}
void CreateLists(char *name, short graph)
/* creates two lists of the correct 'shape', then traverses nodes
in sequence to link up 'subelement' field of ElementList,
then 'node' field of NodeList structures.
*/
* in sequence to link up 'subelement' field of ElementList,
* then 'node' field of NodeList structures.
*/
void CreateLists(char *name, short graph)
{
struct Element *ElementScan;
struct ElementList *EListScan;
@ -1826,6 +1827,8 @@ void CreateLists(char *name, short graph)
return;
}
CombineParallel(name, graph);
Elements = CreateElementList(name, graph);
Nodes = CreateNodeList(name, graph);
if (LookupElementList == NULL) return;
@ -1952,6 +1955,7 @@ void CreateLists(char *name, short graph)
}
ConnectAllNodes(name, graph);
CombineParallel(name, graph);
E = CreateElementList(name, graph);
N = CreateNodeList(name, graph);
@ -3255,6 +3259,272 @@ int Iterate(void)
return(!notdone);
}
/*--------------------------------------------------------------*/
/* "ob" points to the first property record of an object */
/* instance. Check if there are multiple property records. If */
/* so, group them by same properties of interest, order them by */
/* critical property (if defined), and merge devices with the */
/* same properties (by summing property "M" for devices; does */
/* not apply to subcircuits, which must maintain individual */
/* property records for each instance). */
/*--------------------------------------------------------------*/
typedef struct _proplink *proplinkptr;
typedef struct _proplink {
struct property *prop;
proplinkptr next;
} proplink;
void PropertyOptimize(struct objlist *ob, struct nlist *tp)
{
struct objlist *ob2, *obt;
struct property *kl, *m_rec, **plist;
struct valuelist ***vlist, *vl, *vl2, *newvlist;
proplinkptr plink, ptop;
int pcount, p, i, j, icount, pmatch, ival, crit, ctype;
double dval;
static struct valuelist nullvl;
nullvl.type = PROP_INTEGER;
nullvl.value.ival = 0;
// How many property records are there?
// If there are not at least two property records then there
// is nothing to be optimized.
icount = 0;
for (ob2 = ob; ob2 && ob2->type == PROPERTY; ob2 = ob2->next) icount++;
if (icount < 2) return;
// Look through master cell property list and create
// an array of properties of interest to fill in order.
m_rec = NULL;
ptop = NULL;
pcount = 1;
crit = -1;
kl = (struct property *)HashFirst(&(tp->propdict));
while (kl != NULL) {
// Make a linked list so we don't have to iterate through the hash again
plink = (proplinkptr)MALLOC(sizeof(proplink));
plink->prop = kl;
plink->next = ptop;
ptop = plink;
if ((*matchfunc)(kl->key, "M")) {
kl->idx = 0;
m_rec = kl;
}
else
kl->idx = pcount++;
// Set critical property index, if there is one (TO-DO: Handle types
// other than MERGE_ADD_CRIT, and deal with possibility of multiple
// critical properties per instance).
if (kl->merge == MERGE_ADD_CRIT) crit = kl->idx;
kl = (struct property *)HashNext(&(tp->propdict));
}
// Recast the linked list as an array
plist = (struct property **)CALLOC(pcount, sizeof(struct property *));
vlist = (struct valuelist ***)CALLOC(pcount, sizeof(struct valuelist **));
if (m_rec == NULL)
vlist[0] = (struct valuelist **)CALLOC(icount, sizeof(struct valuelist *));
pcount = 1;
while (ptop != NULL) {
if (ptop->prop == m_rec) {
plist[0] = m_rec;
vlist[0] = (struct valuelist **)CALLOC(icount,
sizeof(struct valuelist *));
}
else {
plist[pcount] = ptop->prop;
vlist[pcount++] = (struct valuelist **)CALLOC(icount,
sizeof(struct valuelist *));
}
plink = ptop;
FREE(ptop);
ptop = plink->next;
}
// Now, for each property record, sort the properties of interest
// so that they are all in order. Property "M" goes in position
// zero.
i = 0;
for (ob2 = ob; ob2 && ob2->type == PROPERTY; ob2 = ob2->next) {
for (p = 0;; p++) {
vl = &(ob2->instance.props[p]);
if (vl->type == PROP_ENDLIST) break;
if (vl->key == NULL) continue;
kl = (struct property *)HashLookup(vl->key, &(tp->propdict));
if (kl == NULL && m_rec == NULL) {
if ((*matchfunc)(vl->key, "M")) {
vlist[0][i] = vl;
}
}
else if (kl != NULL) {
vlist[kl->idx][i] = vl;
}
}
i++;
}
// Now combine records with same properties by summing M.
for (i = 0; i < icount - 1; i++) {
for (j = 1; j < icount; 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;
}
// TO-DO: If either value is missing, it takes kl->pdefault
// and must apply promotions if necessary.
else if (vl == NULL || vl2 == NULL) continue;
// Critical properties will be multiplied up by M 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 = fabs(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;
/* 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 (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;
// Add "M" record behind it
vl = &newvlist[--p];
vl->key = strdup("M");
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;
}
// Replace instance properties with the new list
FREE(ob2->instance.props);
ob2->instance.props = newvlist;
}
// If there is a critical property, then M is never
// greater than 1.
if (crit >= 0) {
if (vlist[0][i] != NULL) {
if (vlist[0][i]->value.ival > 1) {
vl = vlist[crit][i];
if (vl->type == PROP_INTEGER)
vl->value.ival *= vlist[0][i]->value.ival;
else if (vl->type == PROP_DOUBLE)
vl->value.dval *= (double)vlist[0][i]->value.ival;
vlist[0][i]->value.ival = 1;
}
}
}
if (vlist[0][j] == NULL) {
vlist[0][j] = &nullvl; // Mark this position
if (crit >= 0) {
vl = vlist[crit][i];
if (vl->type == PROP_INTEGER)
vl->value.ival += vlist[crit][j]->value.ival;
else if (vl->type == PROP_DOUBLE)
vl->value.dval += vlist[crit][j]->value.dval;
}
else {
vlist[0][i]->value.ival++;
}
}
else if (vlist[0][i]->value.ival > 0) {
if (crit >= 0) {
vl = vlist[crit][i];
if (vl->type == PROP_INTEGER)
vl->value.ival += vlist[0][j]->value.ival *
vlist[crit][j]->value.ival;
else if (vl->type == PROP_DOUBLE)
vl->value.dval += (double)vlist[0][j]->value.ival *
vlist[crit][j]->value.dval;
}
else {
vlist[0][i]->value.ival += vlist[0][j]->value.ival;
}
vlist[0][j]->value.ival = 0;
}
}
else j++;
}
}
// Remove entries with M = 0
ob2 = ob;
for (i = 1; i < icount; i++) {
vl = vlist[0][i];
if (vl != NULL && vl->value.ival == 0) {
obt = ob2->next;
ob2->next = ob2->next->next;
FreeObjectAndHash(obt, tp);
}
else
ob2 = ob2->next;
}
// Cleanup memory allocation
for (p = 0; p < pcount; p++) {
kl = (struct property *)plist[p];
if (kl) kl->idx = 0;
FREE(vlist[p]);
}
FREE(plist);
FREE(vlist);
}
/*--------------------------------------------------------------*/
/* Compare the properties of two objects. The passed values */
/* ob1 and ob2 are pointers to the first entry (firstpin) of */
@ -3310,6 +3580,10 @@ int PropertyMatch(struct objlist *ob1, struct objlist *ob2, int do_print)
if ((t1type != PROPERTY) && (t2type != PROPERTY)) return 0;
// Organize and merge property records
if (t1type == PROPERTY) PropertyOptimize(tp1, tc1);
if (t2type == PROPERTY) PropertyOptimize(tp2, tc2);
if (t1type != PROPERTY) {
// t1 has no properties. See if t2's properties are required
// to be checked. If so, flag t1 as missing required properties
@ -3894,80 +4168,6 @@ int ResolveAutomorphisms()
return(VerifyMatching());
}
/*------------------------------------------------------*/
/* CombineSetup -- */
/* Add an entry to a cell describing the combination */
/* allowances. */
/* Return 1 if OK, 0 if error */
/*------------------------------------------------------*/
int CombineSetup(char *model, int filenum, int comb_type)
{
struct nlist *tp;
// If -1 is passed as filenum, then re-run this routine on
// each of Circuit1 and Circuit2 models.
if (filenum == -1) {
if ((Circuit1 != NULL) && (Circuit1->file != -1))
CombineSetup(model, Circuit1->file, comb_type);
if ((Circuit2 != NULL) && (Circuit2->file != -1))
CombineSetup(model, Circuit2->file, comb_type);
return 1;
}
tp = LookupCellFile(model, filenum);
if (tp == NULL) {
Printf("No such model %s\n", model);
return 0;
}
tp->flags |= comb_type;
return 1;
}
/*------------------------------------------------------*/
/* CombineForget -- */
/* Remove combination allowances for a cell */
/* */
/* Return 1 if OK, 0 if error */
/* */
/* If comb_type is 0, then forget all allowed */
/* combinations of the device. */
/*------------------------------------------------------*/
int CombineForget(char *model, int filenum, int comb_type)
{
struct nlist *tp;
// If -1 is passed as filenum, then re-run this routine on
// each of Circuit1 and Circuit2 models.
if (filenum == -1) {
if ((Circuit1 != NULL) && (Circuit1->file != -1))
CombineForget(model, Circuit1->file, comb_type);
if ((Circuit2 != NULL) && (Circuit2->file != -1))
CombineForget(model, Circuit2->file, comb_type);
return 1;
}
tp = LookupCellFile(model, filenum);
if (tp == NULL) {
Printf("No such model %s\n", model);
return 0;
}
if (comb_type != 0) {
/* Remove this specific combination allowance */
tp->flags &= ~comb_type;
}
else {
/* Blanket remove all combinations for this device */
tp->flags &= ~(COMB_SERIAL | COMB_PARALLEL);
}
return 1;
}
/*------------------------------------------------------*/
/* PermuteSetup -- */
/* Add an entry to a cell's "permutes" linked list. */
@ -4476,10 +4676,19 @@ int reorderpins(struct hashlist *p, int file)
}
ob = ob->next;
ob2 = ob2->next;
if (ob == NULL) {
Fprintf(stderr, "Instance of %s has only %d of %d ports\n",
if (i < numports - 1) {
if (ob == NULL || ob->type <= FIRSTPIN) {
Fprintf(stderr, "Instance of %s has only "
"%d of %d ports\n",
tc2->name, i + 1, numports);
break;
break;
}
else if (ob2 == NULL || ob2->type != PORT) {
Fprintf(stderr, "Instance of %s has "
"%d ports, expected %d\n",
tc2->name, i + 1, numports);
break;
}
}
}

View File

@ -30,6 +30,10 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <alloc.h>
#endif
#ifdef TCL_NETGEN
#include <tcl.h>
#endif
#include "netgen.h"
#include "hash.h"
#include "objlist.h"
@ -278,261 +282,279 @@ int ReduceExpressions(struct objlist *instprop, struct objlist *parprops,
for (i = 0;; i++) {
kv = &(instprop->instance.props[i]);
switch (kv->type) {
case PROP_ENDLIST:
break;
if (kv->type == PROP_ENDLIST)
break;
else if (kv->type == PROP_EXPRESSION) {
expstack = kv->value.stack;
}
else if (kv->type == PROP_STRING) {
expstack = NULL;
estr = kv->value.string;
tstr = estr;
case PROP_INTEGER:
case PROP_DOUBLE:
case PROP_VALUE:
continue;
numlast = 0;
while (*tstr != '\0') {
switch(*tstr) {
case PROP_EXPRESSION:
expstack = kv->value.stack;
break;
case PROP_STRING:
expstack = NULL;
estr = kv->value.string;
tstr = estr;
numlast = 0;
while (*tstr != '\0') {
switch(*tstr) {
case '+':
if (numlast == 0) {
/* This is part of a number */
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
}
/* Not a number, so must be arithmetic */
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_PLUS, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '-':
if (numlast == 0) {
/* This is part of a number */
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
}
/* Not a number, so must be arithmetic */
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_MINUS, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '0':
/* Numerical value. Use strtod() to capture */
if (numlast == 1) break;
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
case '/':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_DIVIDE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '*':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_MULTIPLY, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '(':
*tstr = '\0';
/* Check for predefined function keywords */
if (!strcmp(estr, "IF")) {
PushTok(TOK_FUNC_IF, NULL, &expstack);
}
else {
/* Treat as a parenthetical grouping */
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_FUNC_OPEN, NULL, &expstack);
}
estr = tstr + 1;
numlast = 0;
break;
case ')':
*tstr = '\0';
if (expstack == NULL) break;
savetok = expstack->toktype;
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
switch (savetok) {
case TOK_FUNC_THEN:
PushTok(TOK_FUNC_ELSE, NULL, &expstack);
case '+':
if (numlast == 0) {
/* This is part of a number */
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
default:
PushTok(TOK_FUNC_CLOSE, NULL, &expstack);
break;
}
numlast = 1;
estr = tstr + 1;
break;
case '\'':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_SGL_QUOTE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '"':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_DBL_QUOTE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '{':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_GROUP_OPEN, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '}':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
PushTok(TOK_GROUP_CLOSE, NULL, &expstack);
estr = tstr + 1;
numlast = 1;
break;
case '!':
if (*(tstr + 1) == '=') {
}
/* Not a number, so must be arithmetic */
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_NE, NULL, &expstack);
}
numlast = 0;
break;
case '=':
if (*(tstr + 1) == '=') {
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_EQ, NULL, &expstack);
PushTok(TOK_PLUS, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
}
break;
break;
case '>':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (*(tstr + 1) == '=') {
PushTok(TOK_GE, NULL, &expstack);
tstr++;
}
else
PushTok(TOK_GT, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '<':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (*(tstr + 1) == '=') {
PushTok(TOK_LE, NULL, &expstack);
tstr++;
}
else
PushTok(TOK_LT, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case ',':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (expstack == NULL) break;
lptr = expstack;
while (lptr->next) {
lptr = lptr->next;
if (lptr->toktype == TOK_FUNC_THEN) {
PushTok(TOK_FUNC_ELSE, NULL, &expstack);
case '-':
if (numlast == 0) {
/* This is part of a number */
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
}
else if (lptr->toktype == TOK_FUNC_IF) {
PushTok(TOK_FUNC_THEN, NULL, &expstack);
break;
}
}
estr = tstr + 1;
numlast = 0;
break;
/* Not a number, so must be arithmetic */
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_MINUS, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
default:
break;
case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9': case '0':
/* Numerical value. Use strtod() to capture */
if (numlast == 1) break;
dval = strtod(estr, &sstr);
if (sstr > estr && sstr > tstr) {
tstr = sstr - 1;
numlast = 1;
}
break;
case '/':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_DIVIDE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '*':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_MULTIPLY, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '(':
*tstr = '\0';
/* Check for predefined function keywords */
if (!strcmp(estr, "IF")) {
PushTok(TOK_FUNC_IF, NULL, &expstack);
}
else {
/* Treat as a parenthetical grouping */
result = TokGetValue(estr, parent, parprops,
glob, &dval);
if (result == 1)
PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1)
PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_FUNC_OPEN, NULL, &expstack);
}
estr = tstr + 1;
numlast = 0;
break;
case ')':
*tstr = '\0';
if (expstack == NULL) break;
savetok = expstack->toktype;
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
switch (savetok) {
case TOK_FUNC_THEN:
PushTok(TOK_FUNC_ELSE, NULL, &expstack);
break;
default:
PushTok(TOK_FUNC_CLOSE, NULL, &expstack);
break;
}
numlast = 1;
estr = tstr + 1;
break;
case '\'':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_SGL_QUOTE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '"':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_DBL_QUOTE, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '{':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_GROUP_OPEN, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '}':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
PushTok(TOK_GROUP_CLOSE, NULL, &expstack);
estr = tstr + 1;
numlast = 1;
break;
case '!':
if (*(tstr + 1) == '=') {
*tstr = '\0';
result = TokGetValue(estr, parent, parprops,
glob, &dval);
if (result == 1)
PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1)
PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_NE, NULL, &expstack);
}
numlast = 0;
break;
case '=':
if (*(tstr + 1) == '=') {
*tstr = '\0';
result = TokGetValue(estr, parent, parprops,
glob, &dval);
if (result == 1)
PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1)
PushTok(TOK_STRING, estr, &expstack);
PushTok(TOK_EQ, NULL, &expstack);
numlast = 0;
}
break;
case '>':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (*(tstr + 1) == '=') {
PushTok(TOK_GE, NULL, &expstack);
tstr++;
}
else
PushTok(TOK_GT, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case '<':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (*(tstr + 1) == '=') {
PushTok(TOK_LE, NULL, &expstack);
tstr++;
}
else
PushTok(TOK_LT, NULL, &expstack);
estr = tstr + 1;
numlast = 0;
break;
case ',':
*tstr = '\0';
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
if (expstack == NULL) break;
lptr = expstack;
while (lptr->next) {
lptr = lptr->next;
if (lptr->toktype == TOK_FUNC_THEN) {
PushTok(TOK_FUNC_ELSE, NULL, &expstack);
break;
}
else if (lptr->toktype == TOK_FUNC_IF) {
PushTok(TOK_FUNC_THEN, NULL, &expstack);
break;
}
}
estr = tstr + 1;
numlast = 0;
break;
default:
break;
}
tstr++;
}
tstr++;
}
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
result = TokGetValue(estr, parent, parprops, glob, &dval);
if (result == 1) PushTok(TOK_DOUBLE, &dval, &expstack);
else if (result == -1) PushTok(TOK_STRING, estr, &expstack);
FREE(kv->value.string);
kv->value.stack = expstack;
kv->type = PROP_EXPRESSION;
FREE(kv->value.string);
kv->value.stack = expstack;
kv->type = PROP_EXPRESSION;
break;
}
// Find the beginning of the expression, which is the bottom of
@ -1115,51 +1137,6 @@ struct property *PropertyString(char *name, int fnum, char *key, int range,
return kl;
}
/*----------------------------------------------------------------------*/
/* Find all instances of type "instance" in cell "name". For each one, */
/* determine if it can be combined in parallel with other devices. */
/* Capacitors add area or value. Resistors add width, or run a */
/* parallel calculation on value. Transistors add width. */
/* If "aggressive" is FALSE, then combinations may be made only if */
/* the devices have the same properties (except "M"), and will combine */
/* by incrementing "M". If "aggressive" is TRUE, then the device */
/* properties (width, area, etc.) will be modified in combination. */
/* Devices with pin permutations are parallel if nodes match when */
/* permuted. */
/*----------------------------------------------------------------------*/
void CombineParallel(char *name, int fnum, char *instance, int aggressive)
{
struct nlist *tc, *tp;
struct objlist *ob1, *ob2;
if ((tc = LookupCellFile(name, fnum)) == NULL) return;
if ((tp = LookupCellFile(instance, fnum)) == NULL) return;
for (ob1 = tc->cell; ob1; ob1 = ob1->next) {
if (ob1->type == FIRSTPIN && (*matchfunc)(ob1->model.class, tc->name)) {
for (ob2 = ob1->next; ob2; ob2 = ob2->next) {
if (ob2->type == FIRSTPIN && (*matchfunc)(ob2->model.class,
tc->name)) {
/* To-do: Handle permutations! */
while (ob1->node == ob2->node) {
ob1 = ob1->next;
ob2 = ob2->next;
if (ob1 == NULL || ob1->type <= FIRSTPIN) break;
if (ob2 == NULL || ob2->type <= FIRSTPIN) break;
}
if ((ob1 && ob1->type == PROPERTY) &&
(ob2 && ob2->type == PROPERTY)) {
if (PropertyMatch(ob1, ob2, FALSE)) {
/* WIP */
}
}
}
}
}
}
}
/*----------------------------------------------------------------------*/
/* Declare the element class of the current cell */
/*----------------------------------------------------------------------*/
@ -2833,12 +2810,12 @@ void Array(char *Cell, int num)
int NoDisconnectedNodes = 0;
/*----------------------------------------------------------------------*/
/* Within the definition of 'model', traverse the object list */
/* and connect all the otherwise disconnected nodes (i.e., those */
/* with node==-1) to unique node numbers */
/*----------------------------------------------------------------------*/
void ConnectAllNodes(char *model, int file)
/* Within the definition of 'model', traverse the object list
and connect all the otherwise disconnected nodes (i.e., those
with node==-1) to unique node numbers */
{
int nodenum;
struct nlist *tp;
@ -2857,6 +2834,190 @@ void ConnectAllNodes(char *model, int file)
if (ob->node == -1) ob->node = nodenum++;
}
/*----------------------------------------------------------------------*/
/* Find all devices that are of the same class and check for parallel */
/* combinations, and combine them where found, adjusting property "M" */
/* as needed. */
/* */
/* Procedure: Hash each cell by the model name and a space-separated */
/* list of node numbers connected to each pin, and the value of the */
/* property that affects number of devices (if any). The hash stores */
/* the instance record and property record (if any) of the first cell. */
/* If there is a hash match, then the cell instance gets deleted and */
/* the property M of the instance pointed to in the hash gets */
/* incremented. If it has no property M, one is created and the value */
/* set to 2. */
/* */
/* If the device has permutable pins, then duplicate hashes are made */
/* for each permutation. */
/*----------------------------------------------------------------------*/
void CombineParallel(char *model, int file)
{
struct nlist *tp, *tsub;
struct objlist *ob, *ob2, *nextob;
struct objlist *sob, *lob, *tlob, *nob, *pob, *obr;
struct hashdict devdict;
struct Permutation *perm;
size_t pcnt;
int dcnt = 0;
char *pstr, *p2str, *pptr;
struct valuelist *kv;
if ((tp = LookupCellFile(model, file)) == NULL) {
Printf("Cell: %s does not exist.\n", model);
return;
}
InitializeHashTable(&devdict, OBJHASHSIZE);
lob = NULL;
for (ob = tp->cell; ob; ) {
if (ob->type == FIRSTPIN) {
/* ------------------------------------*/
/* Generate hash key from pins */
/* Handle pin permuations */
/* ------------------------------------*/
tsub = LookupCellFile(ob->model.class, file);
if ((tsub != NULL) && (tsub->permutes != NULL))
perm = tsub->permutes;
else
perm = NULL; /* Device has no pin permutations */
pcnt = strlen(ob->model.class) + 2;
pptr = (char *)pcnt;
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
tlob = ob2;
pcnt += 10;
}
/* Find last record in device and first record in next object */
while (ob2 && ob2->type == PROPERTY) {
tlob = ob2;
ob2 = ob2->next;
}
nextob = ob2;
pstr = (char *)MALLOC(pcnt);
sprintf(pstr, "%s", ob->model.class);
pptr += pstr - (char *)2;
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob); ob2 = ob2->next) {
sprintf(pptr, "_%d", ob2->node);
pptr += strlen(pptr);
}
/* Now check the hash table for any similar instance */
sob = (struct objlist *)HashLookup(pstr, &devdict);
if (sob == NULL) {
/* Generate hash entry */
HashPtrInstall(pstr, ob, &devdict);
/* If pins are permutable, generate alternative hash entries */
if (perm != NULL) {
char *pname;
struct objlist *pob1, *pob2;
/* NOTE: This is only set up for a single permutation */
/* per cell and needs to be expanded to the general */
/* case, which requires one nested loop per permute */
/* pair */
p2str = (char *)MALLOC(pcnt);
sprintf(p2str, "%s", ob->model.class);
pptr = p2str + strlen(ob->model.class);
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob);
ob2 = ob2->next) {
pname = ob2->name + strlen(ob2->instance.name) + 1;
if ((*matchfunc)(perm->pin1, pname))
pob1 = ob2;
else if ((*matchfunc)(perm->pin2, pname))
pob2 = ob2;
}
for (ob2 = ob; ob2 && (ob2->type > FIRSTPIN || ob2 == ob);
ob2 = ob2->next) {
if (ob2 == pob1)
sprintf(pptr, "_%d", pob2->node);
else if (ob2 == pob2)
sprintf(pptr, "_%d", pob1->node);
else
sprintf(pptr, "_%d", ob2->node);
pptr += strlen(pptr);
}
HashPtrInstall(p2str, ob, &devdict);
FREE((char *)p2str);
}
/* Set last object ptr to end of this record */
lob = tlob;
}
else {
/* Remove 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 ; ) {
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
nextob = ob->next;
ob = nextob;
}
HashKill(&devdict);
if (dcnt > 0) {
Fprintf(stdout, "Class %s: Merged %d devices.\n", model, dcnt);
}
}
/*----------------------------------------------------------------------*/
/*----------------------------------------------------------------------*/

View File

@ -116,6 +116,7 @@ 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 int NoDisconnectedNodes;
extern int PropertyKeyMatch(char *, char *);
extern int PropertyValueMatch(char *, char *);

View File

@ -73,7 +73,7 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
for (ob = tp->cell; ob != NULL; ob = ob->next)
if (ob->node > maxnode) maxnode = ob->node;
/* was: for (node = 0; node <= maxnode; node++) */
/* was: for (node = 0; node <= maxnode; node++) */
for (node = 1; node <= maxnode; node++)
FlushString("# %3d = %s\n", node, NodeName(tp, node));
@ -154,7 +154,6 @@ void SpiceSubCell(struct nlist *tp, int IsSubCell)
/* caps and resistors, print out device value */
/* print out device type (model/subcircuit name) */
switch (tp2->class) {
@ -473,7 +472,7 @@ extern void IncludeSpice(char *, int, struct cellstack **, int);
void ReadSpiceFile(char *fname, int filenum, struct cellstack **CellStackPtr,
int blackbox)
{
int cdnum = 1, rdnum = 1, ndev, multi;
int cdnum = 1, rdnum = 1;
int warnings = 0, update = 0, hasports = 0;
char *eqptr, devtype, in_subckt;
struct keyvalue *kvlist = NULL;
@ -821,7 +820,6 @@ skip_ends:
/* Read the device model */
snprintf(model, 99, "%s", nexttok);
ndev = 1;
while (nexttok != NULL)
{
/* Parse for M and other parameters */
@ -831,10 +829,7 @@ skip_ends:
if ((eqptr = strchr(nexttok, '=')) != NULL)
{
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
}
@ -843,6 +838,7 @@ skip_ends:
Port("collector");
Port("base");
Port("emitter");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_BJT);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -853,16 +849,10 @@ skip_ends:
goto baddevice;
}
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0)
{
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
Cell(instname, model, collector, base, emitter);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
Cell(instname, model, collector, base, emitter);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
else if (toupper(nexttok[0]) == 'M') {
@ -896,7 +886,6 @@ skip_ends:
/* Read the device model */
snprintf(model, 99, "%s", nexttok);
ndev = 1;
while (nexttok != NULL)
{
/* Parse for parameters; treat "M" separately */
@ -906,14 +895,7 @@ skip_ends:
if ((eqptr = strchr(nexttok, '=')) != NULL)
{
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else if (!strcasecmp(nexttok, "L"))
AddProperty(&kvlist, "L", eqptr + 1);
else if (!strcasecmp(nexttok, "W"))
AddProperty(&kvlist, "W", eqptr + 1);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
}
@ -929,6 +911,7 @@ skip_ends:
Port("bulk");
PropertyDouble(model, filenum, "L", 0.01, 0.0);
PropertyDouble(model, filenum, "W", 0.01, 0.0);
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_FET);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -939,16 +922,10 @@ skip_ends:
goto baddevice;
}
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0)
{
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
Cell(instname, model, drain, gate, source, bulk);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
Cell(instname, model, drain, gate, source, bulk);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
SpiceSkipNewLine();
}
@ -993,17 +970,13 @@ skip_ends:
snprintf(model, 99, "%s", nexttok);
/* Any other device properties? */
ndev = 1;
while (nexttok != NULL)
{
SpiceTokNoNewline();
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
if ((eqptr = strchr(nexttok, '=')) != NULL) {
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
else if (!strncmp(nexttok, "$[", 2)) {
// Support for CDL modeled capacitor format
@ -1026,6 +999,7 @@ skip_ends:
Port("top");
Port("bottom");
PropertyValue(model, filenum, "value", 0.01, 0.0);
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_CAP);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1039,19 +1013,13 @@ skip_ends:
usemodel = 1;
}
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0) {
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
if (usemodel)
Cell(instname, model, ctop, cbot);
else
Cap((*CellStackPtr)->cellname, instname, ctop, cbot);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
if (usemodel)
Cell(instname, model, ctop, cbot);
else
Cap((*CellStackPtr)->cellname, instname, ctop, cbot);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
}
@ -1096,16 +1064,12 @@ skip_ends:
snprintf(model, 99, "%s", nexttok);
/* Any other device properties? */
ndev = 1;
while (nexttok != NULL) {
SpiceTokNoNewline();
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
if ((eqptr = strchr(nexttok, '=')) != NULL) {
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
else if (!strncmp(nexttok, "$[", 2)) {
// Support for CDL modeled resistor format
@ -1126,6 +1090,7 @@ skip_ends:
Port("end_a");
Port("end_b");
PropertyValue(model, filenum, "value", 0.01, 0.0);
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_RES);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1141,19 +1106,13 @@ skip_ends:
else
strcpy(model, "r"); /* Use default resistor model */
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0) {
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
if (usemodel)
Cell(instname, model, rtop, rbot);
else
Res((*CellStackPtr)->cellname, instname, rtop, rbot);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
if (usemodel)
Cell(instname, model, rtop, rbot);
else
Res((*CellStackPtr)->cellname, instname, rtop, rbot);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
}
@ -1178,7 +1137,6 @@ skip_ends:
/* Read the device model */
snprintf(model, 99, "%s", nexttok);
ndev = 1;
while (nexttok != NULL)
{
/* Parse for M and other parameters */
@ -1188,10 +1146,7 @@ skip_ends:
if ((eqptr = strchr(nexttok, '=')) != NULL)
{
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
}
@ -1199,6 +1154,7 @@ skip_ends:
CellDefNoCase(model, filenum);
Port("anode");
Port("cathode");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_DIODE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1208,16 +1164,10 @@ skip_ends:
Fprintf(stderr, "Device \"%s\" has wrong number of ports for a diode.\n");
goto baddevice;
}
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0)
{
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
Cell(instname, model, anode, cathode);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
Cell(instname, model, anode, cathode);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
else if (toupper(nexttok[0]) == 'T') { /* transmission line */
@ -1260,16 +1210,12 @@ skip_ends:
snprintf(model, 99, "%s", nexttok);
/* Any other device properties? */
ndev = 1;
while (nexttok != NULL) {
SpiceTokNoNewline();
if ((nexttok == NULL) || (nexttok[0] == '\0')) break;
if ((eqptr = strchr(nexttok, '=')) != NULL) {
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
}
@ -1281,6 +1227,7 @@ skip_ends:
Port("node2");
Port("node3");
Port("node4");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_XLINE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1296,20 +1243,15 @@ skip_ends:
else
strcpy(model, "t"); /* Use default xline model */
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0) {
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
if (usemodel)
Cell(instname, model, node1, node2, node3, node4);
else
XLine((*CellStackPtr)->cellname, instname, node1, node2,
if (usemodel)
Cell(instname, model, node1, node2, node3, node4);
else
XLine((*CellStackPtr)->cellname, instname, node1, node2,
node3, node4);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
}
@ -1349,7 +1291,6 @@ skip_ends:
snprintf(model, 99, "%s", nexttok);
/* Any other device properties? */
ndev = 1;
while (nexttok != NULL)
{
/* Parse for M and other parameters */
@ -1359,10 +1300,7 @@ skip_ends:
if ((eqptr = strchr(nexttok, '=')) != NULL)
{
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
}
@ -1372,6 +1310,7 @@ skip_ends:
CellDefNoCase(model, filenum);
Port("end_a");
Port("end_b");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_INDUCTOR);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1387,19 +1326,13 @@ skip_ends:
else
strcpy(model, "l"); /* Use default inductor model */
multi = (ndev > 1) ? 1 : 0;
if (!multi) snprintf(instname, 255, "%s%s", model, inst);
while (ndev > 0)
{
if (multi) snprintf(instname, 255, "%s%s.%d", model, inst, ndev);
if (usemodel)
Cell(instname, model, end_a, end_b);
else
Inductor((*CellStackPtr)->cellname, instname, end_a, end_b);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
}
snprintf(instname, 255, "%s%s", model, inst);
if (usemodel)
Cell(instname, model, end_a, end_b);
else
Inductor((*CellStackPtr)->cellname, instname, end_a, end_b);
pobj = LinkProperties(model, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
DeleteProperties(&kvlist);
}
@ -1445,6 +1378,7 @@ skip_ends:
CellDefNoCase(model, filenum);
Port("pos");
Port("neg");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1498,6 +1432,7 @@ skip_ends:
CellDefNoCase(model, filenum);
Port("pos");
Port("neg");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1562,6 +1497,7 @@ skip_ends:
Port("neg");
Port("ctrlp");
Port("ctrln");
PropertyInteger(model, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1601,7 +1537,6 @@ skip_ends:
head = NULL;
tail = NULL;
SpiceTokNoNewline();
ndev = 1;
while (nexttok != NULL) {
/* must still be a node or a parameter */
struct portelement *new_port;
@ -1629,10 +1564,7 @@ skip_ends:
((tp = LookupCellFile(nexttok, filenum)) == NULL))
{
*eqptr = '\0';
if (!strcasecmp(nexttok, "M"))
sscanf(eqptr + 1, "%d", &ndev);
else
AddProperty(&kvlist, nexttok, eqptr + 1);
AddProperty(&kvlist, nexttok, eqptr + 1);
}
else
{
@ -1704,6 +1636,7 @@ skip_ends:
if (head == NULL) {
Port((char *)NULL); // Must have something for pin 1
}
PropertyInteger(subcktname, filenum, "M", 0, 1);
SetClass(CLASS_MODULE);
EndCell();
ReopenCellDef((*CellStackPtr)->cellname, filenum); /* Reopen */
@ -1711,38 +1644,30 @@ skip_ends:
}
/* nexttok is now NULL, scan->name points to class */
multi = (ndev > 1) ? 1 : 0;
if (multi) strcat(instancename, ".");
while (ndev > 0) {
if (multi) {
char *dotptr = strrchr(instancename, '.');
sprintf(dotptr + 1, "%d", ndev);
}
Instance(subcktname, instancename);
pobj = LinkProperties(subcktname, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
ndev--;
Instance(subcktname, instancename);
pobj = LinkProperties(subcktname, kvlist);
ReduceExpressions(pobj, NULL, CurrentCell, TRUE);
/* (Diagnostic) */
/* Fprintf(stderr, "instancing subcell: %s (%s):", subcktname, instancename); */
/*
/* (Diagnostic) */
/* Fprintf(stderr, "instancing subcell: %s (%s):", subcktname, instancename); */
/*
for (scan = head; scan != NULL; scan = scan->next)
Fprintf(stderr," %s", scan->name);
Fprintf(stderr," %s", scan->name);
Fprintf(stderr,"\n");
*/
*/
obptr = LookupInstance(instancename, CurrentCell);
if (obptr != NULL) {
scan = head;
if (scan != NULL)
do {
if (LookupObject(scan->name, CurrentCell) == NULL) Node(scan->name);
join(scan->name, obptr->name);
obptr = obptr->next;
scan = scan->next;
} while (obptr != NULL && obptr->type > FIRSTPIN && scan != NULL);
obptr = LookupInstance(instancename, CurrentCell);
if (obptr != NULL) {
scan = head;
if (scan != NULL)
do {
if (LookupObject(scan->name, CurrentCell) == NULL) Node(scan->name);
join(scan->name, obptr->name);
obptr = obptr->next;
scan = scan->next;
} while (obptr != NULL && obptr->type > FIRSTPIN && scan != NULL);
if ((obptr == NULL && scan != NULL) ||
if ((obptr == NULL && scan != NULL) ||
(obptr != NULL && scan == NULL && obptr->type > FIRSTPIN)) {
if (warnings <= 100) {
Fprintf(stderr,"Parameter list mismatch in %s: ", instancename);
@ -1756,8 +1681,7 @@ skip_ends:
Fprintf(stderr, "Too many warnings. . . will not report any more.\n");
}
warnings++;
}
} // repeat over ndev
}
}
DeleteProperties(&kvlist);

View File

@ -126,7 +126,6 @@ proc netgen::lvs { name1 name2 {setupfile setup.tcl} {logfile comp.out}} {
}
} else {
netgen::permute default ;# transistors and resistors
netgen::combine default ;# standard parallel/serial allowances
}
puts stdout "Comparison output logged to file $logfile"

View File

@ -91,7 +91,6 @@ int _netcmp_automorphs(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_equate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_ignore(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_permute(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_combine(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_property(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_exhaustive(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
int _netcmp_restart(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]);
@ -231,13 +230,6 @@ Command netcmp_cmds[] = {
"capacitor: enable capacitor permutations\n "
"transistor: enable transistor permutations\n "
"(none): enable transistor and resistor permutations"},
{"combine", _netcmp_combine,
"[transistors|resistors|capacitors|<model>]\n "
"<model> [<serial>|<parallel>]: enable combinations of device model\n "
"resistor: enable resistor serial/parallel combinations\n "
"capacitor: enable capacitor parallel combinations\n "
"transistor: enable transistor parallel combinations\n "
"(none): enable standard combinations"},
{"property", _netcmp_property,
"<device>|<model> <property_key> [...]\n "
"<device>: name of a device type (capacitor, etc.)\n "
@ -3279,175 +3271,6 @@ _netcmp_property(ClientData clientData,
return TCL_OK;
}
/*--------------------------------------------------------------*/
/* Function name: _netcmp_combine */
/* Syntax: netgen::combine [default] */
/* netgen::combine combine_class */
/* netgen::combine valid_cellname serial|parallel */
/* netgen::combine forget valid_cellname */
/* netgen::combine forget */
/* Formerly: t */
/* Results: */
/* Side Effects: */
/*--------------------------------------------------------------*/
int
_netcmp_combine(ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
{
char *model;
char *combineclass[] = {
"transistors", "resistors", "capacitors", "default", "forget",
"cell", NULL
};
enum OptionIdx {
TRANS_IDX, RES_IDX, CAP_IDX, DEFLT_IDX, FORGET_IDX, CELL_IDX
};
int result, index, index2, fnum = -1;
struct nlist *tp = NULL;
char *combinetype[] = {
"serial", "parallel", NULL
};
enum SubOptionIdx {
SERIAL_IDX, PARALLEL_IDX
};
if (objc > 3) {
Tcl_WrongNumArgs(interp, 1, objv, "?valid_cellname serial|parallel?");
return TCL_ERROR;
}
if (objc == 1) {
index = DEFLT_IDX;
}
else {
if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)combineclass,
"combine class", 0, &index) != TCL_OK) {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "?valid_cellname serial|parallel?");
return TCL_ERROR;
}
result = CommonParseCell(interp, objv[1], &tp, &fnum);
if (result != TCL_OK) {
Fprintf(stdout, "No such device \"%s\".\n",
Tcl_GetString(objv[1]));
return result;
}
index = CELL_IDX;
if (Tcl_GetIndexFromObj(interp, objv[2],
(CONST84 char **)combinetype,
"combine type", 0, &index2) != TCL_OK) {
Tcl_WrongNumArgs(interp, 1, objv,
"?valid_cellname serial|parallel?");
return TCL_ERROR;
}
}
else if (index == CELL_IDX) {
if (objc != 4) {
Tcl_WrongNumArgs(interp, 1, objv, "cell ?valid_cellname serial|parallel?");
return TCL_ERROR;
}
result = CommonParseCell(interp, objv[2], &tp, &fnum);
if (result != TCL_OK) {
Fprintf(stdout, "No such device \"%s\".\n",
Tcl_GetString(objv[2]));
return result;
}
if (Tcl_GetIndexFromObj(interp, objv[3],
(CONST84 char **)combinetype,
"combine type", 0, &index2) != TCL_OK) {
Tcl_WrongNumArgs(interp, 1, objv,
"?valid_cellname serial|parallel?");
return TCL_ERROR;
}
}
else if (index == FORGET_IDX) {
if (objc < 3) {
/* General purpose combine forget */
tp = FirstCell();
while (tp != NULL) {
CombineForget(tp->name, tp->file, NULL);
tp = NextCell();
}
return TCL_OK;
}
else {
/* Specific combine forget */
result = CommonParseCell(interp, objv[2], &tp, &fnum);
if (result != TCL_OK) {
Fprintf(stdout, "No such device \"%s\".\n",
Tcl_GetString(objv[2]));
return result;
}
if (objc != 4) {
if (Tcl_GetIndexFromObj(interp, objv[3],
(CONST84 char **)combinetype,
"combine type", 0, &index2) != TCL_OK) {
Tcl_WrongNumArgs(interp, 1, objv,
"?valid_cellname serial|parallel?");
return TCL_ERROR;
}
if (CombineForget(tp->name, fnum, (index2 == SERIAL_IDX)
? COMB_SERIAL : COMB_PARALLEL))
Fprintf(stdout, "Model %s combine %s\n", tp->name,
(index2 == SERIAL_IDX) ? "serial" : "parallel");
else
Fprintf(stderr, "Unable to reset model %s %s combination.\n",
tp->name, (index2 == SERIAL_IDX) ? "serial" : "parallel");
}
else {
if (CombineForget(tp->name, fnum, NULL))
Fprintf(stdout, "No combinations of circuit %s allowed\n", tp->name);
else
Fprintf(stderr, "Unable to reset model %s %s combinations\n",
tp->name, (index2 == SERIAL_IDX) ? "serial" : "parallel");
}
return TCL_OK;
}
}
}
if (objc == 1 || objc == 2) {
tp = FirstCell();
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:
if (index == TRANS_IDX || index == DEFLT_IDX)
CombineSetup(tp->name, tp->file, COMB_PARALLEL);
break;
case CLASS_RES: case CLASS_RES3:
if (index == RES_IDX || index == DEFLT_IDX) {
CombineSetup(tp->name, tp->file, COMB_SERIAL);
CombineSetup(tp->name, tp->file, COMB_PARALLEL);
}
break;
case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3:
if (index == CAP_IDX)
CombineSetup(tp->name, tp->file, COMB_PARALLEL);
break;
}
tp = NextCell();
}
}
else if (index == CELL_IDX) {
if (CombineSetup(tp->name, fnum, ((index2 == SERIAL_IDX) ?
COMB_SERIAL : COMB_PARALLEL)))
Fprintf(stdout, "Model %s allow %s combinations\n", tp->name,
(index2 == SERIAL_IDX) ? "serial" : "parallel");
else
Fprintf(stderr, "Unable to set model %s %s combinations.\n",
tp->name, (index2 == SERIAL_IDX) ?
"serial" : "parallel");
}
else {
Tcl_WrongNumArgs(interp, 1, objv, "?valid_cellname serial|parallel?");
return TCL_ERROR;
}
return TCL_OK;
}
/*--------------------------------------------------------------*/
/* Function name: _netcmp_permute */
/* Syntax: netgen::permute [default] */