mirror of
https://github.com/RTimothyEdwards/netgen.git
synced 2026-08-22 22:17:22 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb72af6d54 | ||
|
|
e7f2cacc88 | ||
|
|
7dfe6ee19f | ||
|
|
c7f27c909f |
-394
@@ -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.
|
||||
|
||||
|
||||
+290
-81
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+449
-288
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
+81
-157
@@ -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);
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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] */
|
||||
|
||||
Reference in New Issue
Block a user