diff --git a/base/flatten.c b/base/flatten.c index c1d1132..883173e 100644 --- a/base/flatten.c +++ b/base/flatten.c @@ -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. diff --git a/base/flatten.c.bak b/base/flatten.c.bak new file mode 100644 index 0000000..ca17517 --- /dev/null +++ b/base/flatten.c.bak @@ -0,0 +1,2529 @@ +/* "NETGEN", a netlist-specification tool for VLSI + Copyright (C) 1989, 1990 Massimo A. Sivilotti + Author's address: mass@csvax.cs.caltech.edu; + Caltech 256-80, Pasadena CA 91125. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation (any version). + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file copying. If not, write to +the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +/* flatten.c -- flatten hierarchical netlists, either totally, or + just particular classes of subcells +*/ + +#include "config.h" + +#include +#include + +#ifdef IBMPC +#include +#endif + +#ifdef TCL_NETGEN +#include +#endif + +#include "netgen.h" +#include "hash.h" +#include "objlist.h" +#include "print.h" +#include "netcmp.h" + +extern struct hashdict spiceparams; + +#define OLDPREFIX 1 + +void flattenCell(char *name, int file) +{ + struct objlist *ParentParams; + struct objlist *NextObj; + struct objlist *ChildObjList; + struct nlist *ThisCell; + struct nlist *ChildCell; + struct objlist *tmp, *ob2, *ob3; + int notdone, rnodenum; + char tmpstr[200]; + int nextnode, oldmax; +#if !OLDPREFIX + int prefixlength; +#endif + + if (Debug) + Printf("Flattening cell: %s\n", name); + if (file == -1) + ThisCell = LookupCell(name); + else + ThisCell = LookupCellFile(name, file); + if (ThisCell == NULL) { + Printf("No cell %s found.\n", name); + return; + } + FreeNodeNames(ThisCell); + + ParentParams = ThisCell->cell; + nextnode = 0; + for (tmp = ParentParams; tmp != NULL; tmp = tmp->next) + if (tmp->node >= nextnode) nextnode = tmp->node + 1; + + notdone = 1; + while (notdone) { + notdone = 0; + for (ParentParams = ThisCell->cell; ParentParams != NULL; + ParentParams = NextObj) { + if (Debug) Printf("Parent = %s, type = %d\n", + ParentParams->name, ParentParams->type); + NextObj = ParentParams->next; + if (ParentParams->type != FIRSTPIN) continue; + ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file); + if (Debug) Printf(" Flattening instance: %s, primitive = %s\n", + ParentParams->name, (ChildCell->class == CLASS_SUBCKT) ? + "no" : "yes"); + if (ChildCell->class != CLASS_SUBCKT) continue; + if (ChildCell == ThisCell) continue; // Avoid infinite loop + + /* not primitive, so need to flatten this instance */ + notdone = 1; + /* if this is a new instance, flatten it */ + if (ChildCell->dumped == 0) flattenCell(ParentParams->model.class, + ChildCell->file); + + ChildObjList = CopyObjList(ChildCell->cell, 1); + + /* update node numbers in child to unique numbers */ + oldmax = 0; + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (tmp->node > oldmax) oldmax = tmp->node; + if (nextnode <= oldmax) nextnode = oldmax + 1; + + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (tmp->node <= oldmax && tmp->node != -1) { + UpdateNodeNumbers(ChildObjList, tmp->node, nextnode); + nextnode ++; + } + + /* copy nodenumbers of ports from parent */ + ob2 = ParentParams; + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (IsPort(tmp)) { + if (tmp->node != -1) { + if (Debug) + Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node); + UpdateNodeNumbers(ChildObjList, tmp->node, ob2->node); + } + + /* in pathological cases, the lengths of the port lists may + change. This is an error, but that is no reason to allow + the code to core dump. We avoid this by placing a + superfluous check on ob2->type + */ + + if (ob2 != NULL) + ob2 = ob2->next; + } + + + /* delete all port elements from child */ + while (IsPort(ChildObjList)) { + /* delete all ports at beginning of list */ + if (Debug) Printf("deleting leading port from child\n"); + tmp = ChildObjList->next; + FreeObjectAndHash(ChildObjList, ChildCell); + ChildObjList = tmp; + } + tmp = ChildObjList; + while (tmp->next != NULL) { + if (IsPort(tmp->next)) { + ob2 = (tmp->next)->next; + if (Debug) Printf("deleting a port from child\n"); + FreeObjectAndHash(tmp->next, ChildCell); + tmp->next = ob2; + } + else tmp = tmp->next; + } + + /* for each element in child, prepend 'prefix' */ +#if !OLDPREFIX + /* replaces all the sprintf's below */ + strcpy(tmpstr,ParentParams->instance.name); + strcat(tmpstr,SEPARATOR); + prefixlength = strlen(tmpstr); +#endif + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) { + if (tmp->type == PROPERTY) continue; + else if (IsGlobal(tmp)) { + /* Keep the name but search for node of same name in parent */ + /* and replace the node number, if found. */ + + for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) { + if (ob2->type == tmp->type) { + if ((*matchfunc)(tmp->name, ob2->name)) { + if (ob2->node >= 0) { + // Replace all child objects with this node number + rnodenum = tmp->node; + for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) { + if (ob3->node == rnodenum) + ob3->node = ob2->node; + } + break; + } + } + } + } + HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); + continue; + } + +#if OLDPREFIX + sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, + tmp->name); +#else + strcpy(tmpstr+prefixlength,tmp->name); +#endif + if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr); + FreeString(tmp->name); + tmp->name = strsave(tmpstr); +#if OLDPREFIX + sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, + tmp->instance.name); +#else + strcpy(tmpstr+prefixlength,tmp->instance.name); +#endif + FreeString(tmp->instance.name); + tmp->instance.name = strsave(tmpstr); + HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); + if (tmp->type == FIRSTPIN) + HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict)); + } + + /* splice instance out of parent */ + if (ParentParams == ThisCell->cell) { + /* ParentParams are the very first thing in the list */ + ThisCell->cell = ChildObjList; + for (ob2 = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ; + } + else { + /* find ParentParams in ThisCell list */ + for (ob2 = ThisCell->cell; ob2->next != ParentParams; ob2=ob2->next); + for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ; + } + /* now, ob2 is last element in child list, so skip and reclaim parent */ + tmp = ParentParams; + do { + tmp = tmp->next; + } while ((tmp != NULL) && (tmp->type > FIRSTPIN)); + ob2->next = tmp; + while (ParentParams != tmp) { + ob2 = ParentParams->next; + + /* ParentParams are PORTS */ + FreeObjectAndHash(ParentParams, ThisCell); + ParentParams = ob2; + } + NextObj = ParentParams; + } /* repeat until no more instances found */ + } + CacheNodeNames(ThisCell); + ThisCell->dumped = 1; /* indicate cell has been flattened */ +} + +/*--------------------------------------------------------------*/ +/* flattenInstancesOf -- */ +/* */ +/* Causes all instances of 'instance' within cell 'name' to be */ +/* flattened. For the purpose of on-the-fly flattening of .ext */ +/* files as they are read in, "name" can be NULL, in which case */ +/* CurrentCell (global variable) is taken as the parent. */ +/* */ +/* NOTE: do not flatten 'instance' itself !! */ +/* Return the number of instances flattened. */ +/*--------------------------------------------------------------*/ + +int flattenInstancesOf(char *name, int fnum, char *instance) +{ + struct objlist *ParentParams; + struct objlist *ParentProps; + struct objlist *NextObj; + struct objlist *ChildObjList; + struct nlist *ThisCell; + struct nlist *ChildCell; + struct objlist *tmp, *ob2, *ob3; + int notdone, rnodenum; + char tmpstr[200]; + int nextnode, oldmax, numflat = 0; +#if !OLDPREFIX + int prefixlength; +#endif + + if (name == NULL) { + if (CurrentCell == NULL) { + Printf("Error: no current cell.\n"); + return 0; + } + else + ThisCell = CurrentCell; + } + else { + if (Debug) + Printf("Flattening instances of %s within cell: %s\n", instance, name); + if (fnum == -1) + ThisCell = LookupCell(name); + else + ThisCell = LookupCellFile(name, fnum); + if (ThisCell == NULL) { + Printf("No cell %s found.\n", name); + return 0; + } + } + FreeNodeNames(ThisCell); + + ParentParams = ThisCell->cell; + nextnode = 0; + for (tmp = ParentParams; tmp != NULL; tmp = tmp->next) + if (tmp->node >= nextnode) nextnode = tmp->node + 1; + + notdone = 1; + while (notdone) { + notdone = 0; + ParentParams = ThisCell->cell; + for (ParentParams = ThisCell->cell; ParentParams != NULL; + ParentParams = NextObj) { + if (Debug) Printf("Parent = %s, type = %d\n", + ParentParams->name, ParentParams->type); + NextObj = ParentParams->next; + if (ParentParams->type != FIRSTPIN) continue; + if (!(*matchfunc)(ParentParams->model.class, instance)) continue; + + ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file); + if (Debug) + Printf(" Flattening instance: %s, primitive = %s\n", + ParentParams->instance.name, (ChildCell->class == + CLASS_SUBCKT) ? "no" : "yes"); + if (ChildCell->class != CLASS_SUBCKT) continue; + if (ChildCell == ThisCell) continue; // Avoid infinite loop + + /* Does the parent cell have properties? If so, save a pointer to them */ + for (ParentProps = ParentParams->next; ParentProps && + ParentProps->type != FIRSTPIN; + ParentProps = ParentProps->next) { + if (ParentProps->type == PROPERTY) break; + } + if (ParentProps && (ParentProps->type != PROPERTY)) ParentProps = NULL; + + /* not primitive, so need to flatten this instance */ + notdone = 1; + /* if this is a new instance, flatten it */ + /* if (ChildCell->dumped == 0) flattenCell(ParentParams->model.class, file); */ + + ChildObjList = CopyObjList(ChildCell->cell, 1); + numflat++; + + /* update node numbers in child to unique numbers */ + oldmax = 0; + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (tmp->node > oldmax) oldmax = tmp->node; + if (nextnode <= oldmax) nextnode = oldmax + 1; + + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (tmp->node <= oldmax && tmp->node > 0) { + if (Debug) Printf("Update node %d --> %d\n", tmp->node, nextnode); + UpdateNodeNumbers(ChildObjList, tmp->node, nextnode); + nextnode++; + } + + /* copy nodenumbers of ports from parent */ + ob2 = ParentParams; + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) + if (IsPort(tmp)) { + if (tmp->node > 0) { + if (ob2->node == -1) { + + // Before commiting to attaching to a unconnected node, see + // if there is another node in ParentParams with the same + // name and a valid node number. If so, connect them. In + // the broader case, it may be necessary to consider all + // nodes, not just those with node == -1, and call join() + // here to update all node numbers in the parent cell. + // In that case, a more efficient method is needed for + // tracking same-name ports. + + for (ob3 = ParentParams; ob3 && ob3->type >= FIRSTPIN; ob3 = ob3->next) { + if (ob3 == ob2) continue; + if ((*matchfunc)(ob3->name, ob2->name) && ob3->node != -1) { + ob2->node = ob3->node; + break; + } + } + } + if (Debug) { + // Printf(" Sealing port: %d to node %d\n", tmp->node, ob2->node); + Printf("Update node %d --> %d\n", tmp->node, ob2->node); + } + UpdateNodeNumbers(ChildObjList, tmp->node, ob2->node); + } + + /* in pathological cases, the lengths of the port lists may + change. This is an error, but that is no reason to allow + the code to core dump. We avoid this by placing a + superfluous check on ob2->type + */ + + if (ob2 != NULL) + ob2 = ob2->next; + + if (ob2 == NULL) break; + } + + /* Using name == NULL to indicate that a .ext file is being */ + /* flattened on the fly. This is quick & dirty. */ + + if (name != NULL) { + /* delete all port elements from child */ + while ((ChildObjList != NULL) && IsPort(ChildObjList)) { + /* delete all ports at beginning of list */ + if (Debug) Printf("deleting leading port from child\n"); + tmp = ChildObjList->next; + // FreeObjectAndHash(ChildObjList, ChildCell); + FreeObject(ChildObjList); + ChildObjList = tmp; + } + tmp = ChildObjList; + while (tmp && (tmp->next != NULL)) { + if (IsPort(tmp->next)) { + ob2 = (tmp->next)->next; + if (Debug) Printf("deleting a port from child\n"); + // FreeObjectAndHash(tmp->next, ChildCell); + FreeObject(tmp->next); + tmp->next = ob2; + } + else tmp = tmp->next; + } + } + + /* for each element in child, prepend 'prefix' */ +#if !OLDPREFIX + /* replaces all the sprintf's below */ + strcpy(tmpstr,ParentParams->instance.name); + strcat(tmpstr,SEPARATOR); + prefixlength = strlen(tmpstr); +#endif + for (tmp = ChildObjList; tmp != NULL; tmp = tmp->next) { + if (tmp->type == PROPERTY) + continue; + + else if (IsGlobal(tmp)) { + /* Keep the name but search for node of same name in parent */ + /* and replace the node number, if found. */ + + for (ob2 = ThisCell->cell; ob2 != NULL; ob2 = ob2->next) { + /* Type in parent may be a port, not a global */ + if (ob2->type == tmp->type || ob2->type == PORT) { + if ((*matchfunc)(tmp->name, ob2->name)) { + if (ob2->node >= 0) { + // Replace all child objects with this node number + rnodenum = tmp->node; + for (ob3 = ChildObjList; ob3 != NULL; ob3 = ob3->next) { + if (ob3->node == rnodenum) + ob3->node = ob2->node; + } + break; + } + } + } + } + // Don't hash this if the parent had a port of this name + if (!ob2 || ob2->type != PORT) + HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); + continue; + } + +#if OLDPREFIX + sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, + tmp->name); +#else + strcpy(tmpstr+prefixlength,tmp->name); +#endif + if (Debug) Printf("Renaming %s to %s\n", tmp->name, tmpstr); + FreeString(tmp->name); + tmp->name = strsave(tmpstr); +#if OLDPREFIX + sprintf(tmpstr, "%s%s%s", ParentParams->instance.name, SEPARATOR, + tmp->instance.name); +#else + strcpy(tmpstr+prefixlength,tmp->instance.name); +#endif + FreeString(tmp->instance.name); + tmp->instance.name = strsave(tmpstr); + HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict)); + if (tmp->type == FIRSTPIN) + HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict)); + } + + /* Do property inheritance */ + + if (ParentProps) { + for (ob2 = ChildObjList; ob2 != NULL; ob2=ob2->next) { + + /* If the parent cell has properties to declare, then */ + /* pass them on to children. Use globals only if the */ + /* spiceparams dictionary is active (during file reading */ + /* only). */ + + if (ob2->type == PROPERTY) + ReduceExpressions(ob2, ParentProps, ChildCell, + (spiceparams.hashtab == NULL) ? 0 : 1); + } + } + + /* splice instance out of parent */ + if (ParentParams == ThisCell->cell) { + /* ParentParams are the very first thing in the list */ + ThisCell->cell = ChildObjList; + for (ob2 = ChildObjList; ob2 && ob2->next != NULL; ob2 = ob2->next) ; + } + else { + /* find ParentParams in ThisCell list */ + for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams; ob2=ob2->next); + if (ob2) + for (ob2->next = ChildObjList; ob2->next != NULL; ob2 = ob2->next) ; + } + /* now, ob2 is last element in child list, so skip and reclaim parent */ + + tmp = ParentParams; + do { + tmp = tmp->next; + } while ((tmp != NULL) && ((tmp->type > FIRSTPIN) || (tmp->type == PROPERTY))); + if (ob2) ob2->next = tmp; + while (ParentParams != tmp) { + ob2 = ParentParams->next; + FreeObjectAndHash(ParentParams, ThisCell); + ParentParams = ob2; + } + NextObj = ParentParams; + } /* repeat until no more instances found */ + } + CacheNodeNames(ThisCell); + ThisCell->dumped = 1; /* indicate cell has been flattened */ + return numflat; +} + + +void Flatten(char *name, int file) +{ + ClearDumpedList(); /* keep track of flattened cells */ + flattenCell(name, file); +} + + +static char *model_to_flatten; + +int flattenoneentry(struct hashlist *p, int file) +{ + struct nlist *ptr; + + ptr = (struct nlist *)(p->ptr); + if (file == ptr->file) { + if (!(*matchfunc)(ptr->name, model_to_flatten) && (ptr->class == CLASS_SUBCKT)) + flattenInstancesOf(ptr->name, file, model_to_flatten); + else if (ptr->flags & CELL_DUPLICATE) { + char *bptr = strstr(ptr->name, "[["); + if (bptr != NULL) { + *bptr = '\0'; + if (!(*matchfunc)(ptr->name, model_to_flatten) + && (ptr->class == CLASS_SUBCKT)) { + *bptr = '['; + flattenInstancesOf(ptr->name, file, model_to_flatten); + } + *bptr = '['; + } + } + } + return(1); +} + + +void FlattenInstancesOf(char *model, int file) +{ + if ((file == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) { + FlattenInstancesOf(model, Circuit1->file); + FlattenInstancesOf(model, Circuit2->file); + return; + } + ClearDumpedList(); /* keep track of flattened cells */ + model_to_flatten = strsave(model); + RecurseCellFileHashTable(flattenoneentry, file); + FREE(model_to_flatten); +} + +/* + *----------------------------------------------------------- + * convertGlobalsOf --- + * + * Called once for each cell that instantiates one or + * more cells "model_to_flatten" (global variable). A + * global variable has just been added to the front of + * the cell's master pin list. Get the global variable + * name. Find it in the parent cell or else create it + * if it does not exist. Add the new node to the pin + * list for each instance call. + * + *----------------------------------------------------------- + */ + +void convertGlobalsOf(char *name, int fnum, char *instance) +{ + struct objlist *ParentParams; + struct objlist *ChildOb, *Ob2, *Ob; + struct objlist *newpin, *newnode, *snode, *lnode; + struct nlist *ThisCell; + struct nlist *ChildCell; + int maxnode, maxpin; + + if (name == NULL) { + if (CurrentCell == NULL) { + Printf("Error: no current cell.\n"); + return; + } + else + ThisCell = CurrentCell; + } + else { + if (fnum == -1) + ThisCell = LookupCell(name); + else + ThisCell = LookupCellFile(name, fnum); + if (ThisCell == NULL) { + Printf("No cell %s found.\n", name); + return; + } + } + + FreeNodeNames(ThisCell); + + for (ParentParams = ThisCell->cell; ParentParams != NULL; + ParentParams = ParentParams->next) { + if (ParentParams->type != FIRSTPIN) continue; + if (!(*matchfunc)(ParentParams->model.class, instance)) continue; + + // Move forward to last pin in the pin list. The "type" record + // holds the pin numbering, so we want to find the maximum pin + // number and keep going from there. + + maxpin = 0; + while (ParentParams->next != NULL) { + if (ParentParams->type >= maxpin) maxpin = ParentParams->type + 1; + if (ParentParams->next->type < FIRSTPIN) break; + else if (!(*matchfunc)(ParentParams->instance.name, + ParentParams->next->instance.name)) + break; + ParentParams = ParentParams->next; + } + if (ParentParams->type >= maxpin) maxpin = ParentParams->type + 1; + + ChildCell = LookupCellFile(ParentParams->model.class, ThisCell->file); + ChildOb = ChildCell->cell; + + // The node to make local will be the last pin in the child + while (IsPort(ChildOb) && ChildOb->next != NULL + && IsPort(ChildOb->next)) + ChildOb = ChildOb->next; + + newpin = GetObject(); + if (newpin == NULL) return; /* Memory allocation error */ + + newpin->next = ParentParams->next; + ParentParams->next = newpin; + newpin->instance.name = (ParentParams->instance.name) ? + strsave(ParentParams->instance.name) : NULL; + newpin->name = (char *)MALLOC(strlen(newpin->instance.name) + + strlen(ChildOb->name) + 2); + sprintf(newpin->name, "%s/%s", newpin->instance.name, ChildOb->name); + newpin->model.class = strsave(ParentParams->model.class); + newpin->type = maxpin; + newpin->node = 0; /* placeholder */ + + // Find the next valid unused node number + + maxnode = -1; + for (Ob2 = ThisCell->cell; Ob2 != NULL; Ob2 = Ob2->next) + if (Ob2->node >= maxnode) maxnode = Ob2->node + 1; + + // Does the global node exist in the parent? Note that + // the node may have been declared as a port in the parent, + // which is fine; we just don't create a new node in the + // parent for it. + + for (Ob2 = ThisCell->cell; Ob2 != NULL; Ob2 = Ob2->next) { + if (IsGlobal(Ob2) || IsPort(Ob2)) + if ((*matchfunc)(Ob2->name, ChildOb->name)) { + // This node may never have been used in the parent. If + // so, give it a valid node number in the parent. + if (Ob2->node == -1) Ob2->node = maxnode; + newpin->node = Ob2->node; + break; + } + } + if (Ob2 == NULL) { // No such node; create it + newnode = GetObject(); + + // Place the node after the pin list of the parent cell. + lnode = NULL; + for (snode = ThisCell->cell; snode && IsPort(snode); + snode = snode->next) + lnode = snode; + if (lnode == NULL) { + newnode->next = ThisCell->cell; + ThisCell->cell = newnode; + } + else { + newnode->next = lnode->next; + lnode->next = newnode; + } + newnode->type = GLOBAL; + newnode->node = maxnode; + newnode->name = (ChildOb->name) ? strsave(ChildOb->name) : NULL; + // newnode->instance.name = (ParentParams->instance.name) ? + // strsave(ParentParams->instance.name) : NULL; + // newnode->model.class = strsave(ParentParams->model.class); + newnode->instance.name = NULL; + newnode->model.class = NULL; + newpin->node = maxnode; + HashPtrInstall(newnode->name, newnode, &(ThisCell->objdict)); + } + + // Remove any references to the net as a GLOBAL type in the instance + + /* + Ob2 = ParentParams; + for (Ob = ParentParams->next; Ob != NULL && Ob->type != FIRSTPIN;) { + if (IsGlobal(Ob)) { + Ob2->next = Ob->next; + FreeObjectAndHash(Ob, ThisCell); + Ob = Ob2->next; + } + else { + Ob2 = Ob; + Ob = Ob->next; + } + } + */ + + // Now there should be only one object of this name in the instance, + // which is the pin, and we will set the hash table to point to it. + + HashPtrInstall(newpin->name, newpin, &(ThisCell->objdict)); + + } + CacheNodeNames(ThisCell); +} + +/* + *----------------------------------------------------------- + * convertglobals --- + * + * Routine to search database for cells that instantiate + * cell "model_to_flatten". For each cell, call the routine + * convertGlobalsOf(). Do not call convertGlobalsOf() on + * self, and only call convertGlobalsOf() on cells in the + * same file. + * + *----------------------------------------------------------- + */ + +int convertglobals(struct hashlist *p, int file) +{ + struct nlist *ptr; + + ptr = (struct nlist *)(p->ptr); + if (file == ptr->file) + if (!(*matchfunc)(ptr->name, model_to_flatten)) + convertGlobalsOf(ptr->name, file, model_to_flatten); + return 1; +} + +/* + *----------------------------------------------------------- + * ConvertGlobals --- + * + * Remove global node references in a subcircuit by changing + * them to local nodes and adding a port. Check all parent + * cells, adding the global node if it does not exist, and + * connecting it to the port of the instance. + * + *----------------------------------------------------------- + */ + +void ConvertGlobals(char *name, int filenum) +{ + struct nlist *ThisCell; + struct objlist *ObjList, *Ob2, *NewObj; + int globalnet, result; + + if (Debug) + Printf("Converting globals in circuit: %s\n", name); + + if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) { + ConvertGlobals(name, Circuit1->file); + ConvertGlobals(name, Circuit2->file); + return; + } + + ThisCell = LookupCellFile(name, filenum); + + if (ThisCell == NULL) { + Printf("No circuit %s found.\n", name); + return; + } + + /* First check if this object has any ports. If not, it is a top- */ + /* level cell, and we do not need to process global nodes. */ + + for (ObjList = ThisCell->cell; ObjList != NULL; ObjList = ObjList->next) { + if (IsPort(ObjList)) + break; + else + return; + } + + /* Remove the cached node names, because we are changing them */ + FreeNodeNames(ThisCell); + + for (ObjList = ThisCell->cell; ObjList != NULL; ObjList = ObjList->next) { + if (IsGlobal(ObjList)) { + globalnet = ObjList->node; + + /* Make sure this node is not in the port list already */ + for (Ob2 = ThisCell->cell; Ob2 != NULL; Ob2 = Ob2->next) { + if (Ob2->type != PORT) break; + if (Ob2->node == globalnet) break; + } + if (Ob2 != NULL && IsPort(Ob2) && Ob2->node == globalnet) + continue; + + /* Add this node to the cell as a port */ + NewObj = GetObject(); + if (NewObj == NULL) return; /* Memory allocation error */ + + /* Find the last port and add the new net to the end */ + for (Ob2 = ThisCell->cell; Ob2 != NULL; Ob2 = Ob2->next) + if (IsPort(Ob2) && (Ob2->next == NULL || !IsPort(Ob2->next))) + break; + + if (Ob2 == NULL) { + NewObj->next = ThisCell->cell; + ThisCell->cell = NewObj; + } + else { + NewObj->next = Ob2->next; + Ob2->next = NewObj; + } + NewObj->type = PORT; + NewObj->node = globalnet; + NewObj->model.port = -1; + NewObj->instance.name = (ObjList->instance.name) ? + strsave(ObjList->instance.name) : NULL; + NewObj->name = (ObjList->name) ? strsave(ObjList->name) : NULL; + + HashPtrInstall(NewObj->name, NewObj, &(ThisCell->objdict)); + + /* Find all parent cells of this cell. Find the global node */ + /* if it exists or create it if it doesn't. Add the node to */ + /* the beginning of the list of pins for this device. */ + + ClearDumpedList(); /* keep track of flattened cells */ + model_to_flatten = strsave(name); + RecurseCellFileHashTable(convertglobals, filenum); + FREE(model_to_flatten); + } + } + + /* Now remove all global nodes from the cell. */ + /* Do not remove the hash entry, because we still have a */ + /* node (a pin) of the same name, and have reassigned the */ + /* hash table value to it. */ + + Ob2 = NULL; + for (ObjList = ThisCell->cell; ObjList != NULL;) { + if (IsGlobal(ObjList)) { + if (Ob2 == NULL) + ThisCell->cell = ObjList->next; + else + Ob2->next = ObjList->next; + + FreeObject(ObjList); /* not FreeObjectAndHash(), see above */ + + if (Ob2 == NULL) + ObjList = ThisCell->cell; + else + ObjList = Ob2->next; + } + else { + Ob2 = ObjList; + ObjList = ObjList->next; + } + } + + /* Regenerate the node name cache */ + CacheNodeNames(ThisCell); +} + +/*------------------------------------------------------*/ +/* Callback function for UniquePins */ +/*------------------------------------------------------*/ + +struct nlist *uniquepins(struct hashlist *p, void *clientdata) +{ + struct nlist *ptr; + struct objlist *ob, *tob, *ob2, *lob, *nob; + struct objlist *sob, *firstpin, *saveob; + struct nlist *tc = (struct nlist *)clientdata; + int refnode, i; + int modified = 0; + + ptr = (struct nlist *)(p->ptr); + if (tc->file == ptr->file) { + + /* Find each instance of cell tc used in cell ptr */ + + lob = NULL; + ob = ptr->cell; + while (ob != NULL) { + while (ob && ob->type != FIRSTPIN) { + lob = ob; + ob = ob->next; + } + if (ob && ob->model.class != NULL) { + firstpin = ob; + if (!(*matchfunc)(ob->model.class, tc->name)) { + lob = ob; + ob = ob->next; + continue; + } + } + if (ob == NULL) break; + + /* 1st pass---resolve node numbers in pin list */ + + tob = tc->cell; + ob = firstpin; + for (ob = firstpin; ob->type >= FIRSTPIN && ob->model.class != NULL; + ob = ob->next) { + if (tob->type == UNKNOWN) { + + /* If net is different from the net of the first */ + /* such pin, then merge the nets in cell tc. */ + + if (tob->model.port == FIRSTPIN) { + saveob = firstpin; + refnode = firstpin->node; + } + else { + i = FIRSTPIN + 1; + for (sob = firstpin->next; sob && sob->type > FIRSTPIN; + sob = sob->next) { + if (tob->model.port == i) { + saveob = sob; + refnode = sob->node; + break; + } + i++; + } + } + if (ob->node != refnode) { + if (refnode == -1) { + refnode = ob->node; + saveob->node = refnode; + } + else if (ob->node != -1) { + for (sob = ptr->cell; sob != NULL; sob = sob->next) + if (sob->node == ob->node) + sob->node = refnode; + } + } + modified = 1; + + // Check if the node we're about to remove is in the + // objdict hash table; if so, replace it with the one + // that we are going to keep. + + if (LookupObject(ob->name, ptr) == ob) { + HashPtrInstall(ob->name, saveob, &(ptr->objdict)); + } + } + tob = tob->next; + if (tob == NULL || (tob->type != PORT && tob->type != UNKNOWN)) break; + } + + /* 2nd pass---remove the pins */ + + tob = tc->cell; + ob = firstpin; + while (ob->type >= FIRSTPIN && ob->model.class != NULL) { + if (tob->type == UNKNOWN) { + + // lob cannot be NULL here by definition; if there + // are duplicate pins, the first is kept, so the first + // pin is never removed. + + lob->next = ob->next; + FREE(ob->name); + if (ob->instance.name != NULL) + FREE(ob->instance.name); + FREE(ob); + ob = lob->next; + } + else { + lob = ob; + ob = ob->next; + } + tob = tob->next; + if (tob == NULL || (tob->type != PORT && tob->type != UNKNOWN)) break; + } + + // Renumber the pins in order. Since when removing duplicates, the + // first entry is always kept, the first pin is never changed, so + // the instdict record is never corrupted. + + i = FIRSTPIN; + firstpin->type = i++; + for (sob = firstpin->next; sob && sob->type > FIRSTPIN; sob = sob->next) { + sob->type = i++; + } + } + } + if (modified) CacheNodeNames(ptr); + + return NULL; +} + +/*------------------------------------------------------*/ +/* Check a subcircuit for duplicate pins. If found, */ +/* remove the duplicate entry or entries, and merge */ +/* nets in all parent cells. Duplicate pins are */ +/* determined by net number, not by name. */ +/* */ +/* This routine must be run after CleanupPins(), */ +/* otherwise the unique pin may be left with no node */ +/* number. */ +/* */ +/* Return 0 if nothing was modified, 1 otherwise. */ +/*------------------------------------------------------*/ + +int UniquePins(char *name, int filenum) +{ + struct nlist *ThisCell; + struct objlist *ob, *lob, **firstport; + int maxnode, *nodecount, *firstpin, portcount; + int haspins = 0; + int needscleanup = 0; + + if (filenum == -1) + ThisCell = LookupCell(name); + else + ThisCell = LookupCellFile(name, filenum); + + if (ThisCell == NULL) { + Printf("No cell %s found.\n", name); + return 0; + } + + maxnode = 0; + for (ob = ThisCell->cell; ob != NULL; ob = ob->next) { + if (ob->type != PORT) break; + haspins = 1; + if (ob->node > maxnode) maxnode = ob->node; + } + if (haspins == 0) return 0; + + nodecount = (int *)CALLOC(maxnode + 1, sizeof(int)); + firstpin = (int *)CALLOC(maxnode + 1, sizeof(int)); + firstport = (struct objlist **)CALLOC(maxnode + 1, sizeof(struct objlist *)); + + portcount = FIRSTPIN; + for (ob = ThisCell->cell; ob != NULL; ob = ob->next) { + if (ob->type != PORT) break; + if (ob->node > 0) { + nodecount[ob->node]++; + if (nodecount[ob->node] == 2) { + Printf("Duplicate pin %s in cell %s\n", ob->name, ThisCell->name); + } + if (nodecount[ob->node] > 1) { + /* Remove this node; prep for removal by marking with UNKNOWN */ + ob->type = UNKNOWN; + /* Replace port number with first port number used */ + ob->model.port = firstpin[ob->node]; + needscleanup = 1; + } + else { + firstpin[ob->node] = portcount; + firstport[ob->node] = ob; + } + } + portcount++; + } + + if (needscleanup) + RecurseCellHashTable2(uniquepins, (void *)ThisCell); + + /* Remove all entries marked UNKNOWN */ + + lob = NULL; + for (ob = ThisCell->cell; ob != NULL; ) { + if (ob->type == UNKNOWN) { + struct objlist *testob; + + testob = LookupObject(ob->name, ThisCell); + if (testob == ob) { + // The hash table is pointing at the cell we are + // about to delete. Hash the one we're keeping instead. + + HashPtrInstall(ob->name, firstport[ob->node], &(ThisCell->objdict)); + } + + if (lob == NULL) { + ThisCell->cell = ob->next; + if (ob->instance.name != NULL) + FREE(ob->instance.name); + FREE(ob); + ob = ThisCell->cell; + } + else { + lob->next = ob->next; + if (ob->instance.name != NULL) + FREE(ob->instance.name); + FREE(ob); + ob = lob->next; + } + } + else { + if (ob->type != PORT) break; + lob = ob; + ob = ob->next; + } + } + + if (needscleanup) CacheNodeNames(ThisCell); + + FREE(nodecount); + FREE(firstpin); + FREE(firstport); + return 1; +} + +/*------------------------------------------------------*/ +/* Callback function for CleanupPins */ +/* Note that if the first pin of the instance is a */ +/* disconnected node, then removing it invalidates the */ +/* instdict hash. */ +/*------------------------------------------------------*/ + +struct nlist *cleanuppins(struct hashlist *p, void *clientdata) +{ + struct nlist *ptr; + struct objlist *ob, *obt, *lob, *nob, *firstpin; + struct nlist *tc = (struct nlist *)clientdata; + int pinnum; + + ptr = (struct nlist *)(p->ptr); + if (tc->file != ptr->file) return NULL; + + /* Find each instance of cell tc used in cell ptr */ + + lob = NULL; + for (ob = ptr->cell; ob != NULL; ) { + while (ob && ob->type != FIRSTPIN) { + lob = ob; + ob = ob->next; + } + if (ob && ob->model.class != NULL) { + if (!(*matchfunc)(ob->model.class, tc->name)) { + lob = ob; + ob = ob->next; + continue; + } + if (ob == NULL) break; + + firstpin = ob; + pinnum = FIRSTPIN; + obt = tc->cell; + + while (ob && obt && (ob->type > FIRSTPIN || ob == firstpin) && + ob->model.class != NULL) { + nob = ob->next; + if ((obt->type == PORT) && (obt->node == -1)) { + + /* Remove this pin */ + + if (ob == firstpin) firstpin = nob; + + if (lob == NULL) { + ptr->cell = ob->next; + } + else { + lob->next = ob->next; + } + + // Check if the node we're about to remove is in the + // objdict hash table + if (LookupObject(ob->name, ptr) == ob) { + HashDelete(ob->name, &(ptr->objdict)); + } + + FREE(ob->name); + if (ob->instance.name != NULL) FREE(ob->instance.name); + if (ob->model.class != NULL) FREE(ob->model.class); + FREE(ob); + } + else { + lob = ob; + ob->type = pinnum++; // Renumber pins in order + } + ob = nob; + obt = obt->next; + } + + /* Rehash the instdict, in case the first pin got removed */ + if (firstpin && (firstpin->type == FIRSTPIN)) + HashPtrInstall(firstpin->instance.name, firstpin, &(ptr->instdict)); + } + } + return NULL; /* Keep the search going */ +} + +/*------------------------------------------------------*/ +/* Check a circuit for pins that are not connected to */ +/* any real node, and remove them from the circuit and */ +/* all instances of that circuit. */ +/* */ +/* Return 0 if nothing was modified, 1 otherwise. */ +/*------------------------------------------------------*/ + +int CleanupPins(char *name, int filenum) +{ + struct nlist *ThisCell; + struct objlist *ob, *lob, *nob; + int needscleanup = 0; + + if (filenum == -1) + ThisCell = LookupCell(name); + else + ThisCell = LookupCellFile(name, filenum); + + if (ThisCell == NULL) { + Printf("No cell %s found.\n", name); + return 0; + } + + // Avoid a loop through all cells unless we have to do it. + + for (ob = ThisCell->cell; ob != NULL; ob = ob->next) { + if (ob->type != PORT) break; + if (ob->node == -1) { + needscleanup = 1; + break; + } + } + + if (needscleanup == 0) return 0; + + // If there is only one port in the cell, don't remove it + if (ob && ob == ThisCell->cell && ob->next && ob->next->type != PORT) + return 0; + + // Remove the disconnected nodes from all instances of the cell + + RecurseCellHashTable2(cleanuppins, (void *)ThisCell); + + // Remove the disconnected nodes from cell + + lob = NULL; + for (ob = ThisCell->cell; ob != NULL; ) { + if (ob->type == UNKNOWN) { + lob = ob; + ob = ob->next; + continue; + } + else if (ob->type != PORT) break; + nob = ob->next; + if (ob->node == -1) { + if (lob == NULL) { + ThisCell->cell = ob->next; + } + else { + lob->next = ob->next; + } + + // Check if the node we're about to remove is in the + // objdict hash table + if (LookupObject(ob->name, ThisCell) == ob) { + HashDelete(ob->name, &(ThisCell->objdict)); + } + + FREE(ob->name); + if (ob->instance.name != NULL) + FREE(ob->instance.name); + FREE(ob); + } + else + lob = ob; + ob = nob; + } + return 1; +} + +/*------------------------------------------------------*/ + +typedef struct ecompare { + struct nlist *cell1; + struct nlist *cell2; + int num1, num2; + int add1, add2; + char refcount; +} ECompare; + +typedef struct ecomplist *ECompListPtr; + +typedef struct ecomplist { + ECompare *ecomp; + ECompListPtr next; +} ECompList; + +typedef struct dcompare { + int num1, num2; + double total1, total2; + unsigned char processed; + char refcount; +} DCompare; + +/*------------------------------------------------------*/ +/* Split a device into multiple devices based on a */ +/* critical property; e.g., MOSFET width. Each device */ +/* with property name equal to kl->key and value of */ +/* "value" will be split into "ndev" devices of width */ +/* "mindev". It is the responsibility of the calling */ +/* routine to ensure that the total width after the */ +/* split is equal to the total width before the split. */ +/*------------------------------------------------------*/ + +void +SplitDevice(struct nlist *tc, struct nlist *cell, struct property *kl, + double value, double mindev, int ndev, int file1, int file2, + int which, double slop) +{ + struct objlist *ob, *ob2, *newdevs, *ob3, *lastob, *nob, *saveob; + struct nlist *tsub; + int i, 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 + 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; + } + } + + if ((2 * fabs(value - dval) / (value + dval)) < slop) { + switch(kv->type) { + case PROP_INTEGER: + kv->value.ival = mindev; + break; + case PROP_DOUBLE: + case PROP_VALUE: + kv->value.dval = mindev; + break; + } + + // Make (ndev - 1) copies of the device + for (i = 1; i < ndev; i++) { + ob3 = CopyObjList(ob, 0); + 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; + DCompare *dcomp, *ncomp, *qcomp; + int file = (which == 0) ? file1 : file2; + int ofile = (which == 0) ? file2 : file1; + char *p1str, *p2str, *d1str, *d2str; + double dvalue; + + 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 + + dvalue = 0.0; + 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: + dvalue = (double)kv->value.ival; + break; + case PROP_DOUBLE: + case PROP_VALUE: + // To-do: Round to tolerance + dvalue = kv->value.dval; + break; + } + break; + } + } + break; + } + } + if (dvalue == 0.0) { + // No critical property instanced, so use default + switch(kl->type) { + case PROP_INTEGER: + dvalue = (double)kl->pdefault.ival; + break; + case PROP_DOUBLE: + case PROP_VALUE: + // To-do: Round to tolerance + dvalue = kl->pdefault.dval; + break; + } + } + + sprintf(d1str, "%g", dvalue); + sprintf(d2str, "%g", dvalue); + + // Create hash key from object class and the critical + // property affecting the device count. + + dcomp = (DCompare *)HashInt2Lookup(p1str, file, devdict); + + // Fill in the values for this device::property combination + + if (dcomp == NULL) { + ncomp = (DCompare *)MALLOC(sizeof(DCompare)); + if (which == 0) { + ncomp->num1 = 1; + ncomp->num2 = 0; + ncomp->total1 = dvalue; + ncomp->total2 = 0.0; + } + else { + ncomp->num1 = 0; + ncomp->num2 = 1; + ncomp->total1 = 0.0; + ncomp->total2 = dvalue; + } + ncomp->processed = 0; + ncomp->refcount = (char)1; + + HashInt2PtrInstall(p1str, file, ncomp, devdict); + if (teq != NULL) { + qcomp = (DCompare *)HashInt2Lookup(p2str, ofile, + devdict); + if (qcomp == NULL) { + HashInt2PtrInstall(p2str, ofile, ncomp, devdict); + ncomp->refcount++; + } + } + } + else { + if (which == 0) { + dcomp->num1++; + dcomp->total1 += dvalue; + } + else { + dcomp->num2++; + dcomp->total2 += dvalue; + } + } + + FREE(p1str); + FREE(p2str); + } + } + } +} + +/*------------------------------------------------------*/ +/* Survey the contents of a cell and sort into a hash */ +/*------------------------------------------------------*/ + +void +SurveyCell(struct nlist *tc, struct hashdict *compdict, int file1, int file2, int which) +{ + struct objlist *ob; + struct nlist *tsub, *teq; + ECompare *ecomp, *qcomp, *ncomp; + int file = (which == 0) ? file1 : file2; + int ofile = (which == 0) ? file2 : file1; + char *dstr; + + for (ob = tc->cell; ob; ob = ob->next) { + if (ob->type == FIRSTPIN) { + tsub = LookupCellFile(ob->model.class, file); + if (tsub->flags & CELL_DUPLICATE) { + // Always register a duplicate under the original name + dstr = strstr(ob->model.class, "[["); + if (dstr) *dstr = '\0'; + } + else dstr = NULL; + + teq = LookupClassEquivalent(ob->model.class, file, ofile); + ecomp = (ECompare *)HashInt2Lookup(ob->model.class, file, compdict); + + if (ecomp == 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(ob->model.class, file, ncomp, compdict); + if (teq != NULL) { + char *bstr = NULL; + if (teq->flags & CELL_DUPLICATE) { + bstr = strstr(teq->name, "[["); + if (bstr) *bstr = '\0'; + } + qcomp = (ECompare *)HashInt2Lookup(teq->name, ofile, compdict); + if (qcomp == NULL) { + HashInt2PtrInstall(teq->name, ofile, ncomp, compdict); + ncomp->refcount++; + } + if (bstr) *bstr = '['; + } + } + else { + if (which == 0) + ecomp->num1++; + else + ecomp->num2++; + } + if (dstr) *dstr = '['; + } + } +} + +/*------------------------------------------------------*/ +/* Create a preliminary matchup of two cells. */ +/* For each type of cell in the instance lists, if */ +/* there is a mismatch between the total count of */ +/* instances, where the instances are subcells, then */ +/* determine whether decomposing the cells into their */ +/* constituent parts one or more times would make the */ +/* lists match better. If so, flatten those cells in */ +/* both parents. */ +/* */ +/* If there is a mismatch between instances of low- */ +/* level devices, determine if the mismatches can be */ +/* resolved by parallel/serial combining, according to */ +/* combination rules. */ +/* */ +/* Return the number of modifications made. */ +/*------------------------------------------------------*/ + +int +PrematchLists(char *name1, int file1, char *name2, int file2) +{ + struct nlist *tc1, *tc2, *tsub1, *tsub2; + struct objlist *ob1, *ob2, *lob; + struct hashdict compdict; + ECompare *ecomp, *ncomp; + ECompList *list0X, *listX0; + int match, modified = 0; + + if (file1 == -1) + tc1 = LookupCell(name1); + else + tc1 = LookupCellFile(name1, file1); + + if (file2 == -1) + tc2 = LookupCell(name2); + else + tc2 = LookupCellFile(name2, file2); + + if (tc1 == NULL || tc2 == NULL) return; + + InitializeHashTable(&compdict, OBJHASHSIZE); + + // Gather information about instances of cell "name1" + SurveyCell(tc1, &compdict, file1, file2, 0); + + // Gather information about instances of cell "name2" + SurveyCell(tc2, &compdict, file1, file2, 1); + + // Find all instances of one cell that have fewer in + // the compared circuit. Check whether subcircuits + // in the hierarchy of each instance contain devices + // or subcircuits that have more in the compared circuit. + + listX0 = list0X = NULL; + ecomp = (ECompare *)HashFirst(&compdict); + while (ecomp != NULL) { + + /* Case 1: Both cell1 and cell2 classes are subcircuits, */ + /* and flattening both of them improves the matching. */ + + if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) && + (ecomp->cell1 != NULL) && + (ecomp->cell2->class == CLASS_SUBCKT) && + (ecomp->cell1->class == CLASS_SUBCKT)) { + ecomp->add2 = -ecomp->num2; + ecomp->add1 = -ecomp->num1; + match = 1; + for (ob2 = ecomp->cell2->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell2->file, &compdict); + if (ncomp != NULL) { + if ((ncomp->num1 > ncomp->num2) && + ((ncomp->add2 + ecomp->num2) >= + (ncomp->add1 + ecomp->num1))) { + ncomp->add2 += ecomp->num2; + ncomp->add1 += ecomp->num1; + } + else if ((ncomp->num1 < ncomp->num2) && + ((ncomp->add2 + ecomp->num2) <= + (ncomp->add1 + ecomp->num1))) { + ncomp->add2 += ecomp->num2; + ncomp->add1 += ecomp->num1; + } + else { + match = 0; + break; + } + } + else { + match = 0; + break; + } + } + } + if (match) { + if (ecomp->cell1) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecomp->cell1->name, + name1); + flattenInstancesOf(name1, file1, ecomp->cell1->name); + } + if (ecomp->cell2) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecomp->cell2->name, + name2); + flattenInstancesOf(name2, file2, ecomp->cell2->name); + } + modified++; + } + + /* Reset or apply the count adjustments */ + if (ecomp->cell2) + for (ob2 = ecomp->cell2->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell2->file, &compdict); + if (ncomp != NULL) { + if (match) { + ncomp->num1 += ncomp->add1; + ncomp->num2 += ncomp->add2; + } + ncomp->add1 = 0; + ncomp->add2 = 0; + } + } + } + if (match) { + ecomp->num1 = 0; + ecomp->num2 = 0; + } + ecomp->add1 = 0; + ecomp->add2 = 0; + + /* If the pair was unresolved, and the number of */ + /* instances is either N:0 or 0:M, add to a list so */ + /* they can be cross-checked at the end. */ + + if ((ecomp->num1 != 0) && (ecomp->num2 == 0)) { + ECompList *newcomplist; + + newcomplist = (ECompList *)MALLOC(sizeof(ECompList)); + newcomplist->ecomp = ecomp; + newcomplist->next = listX0; + listX0 = newcomplist; + } + else if ((ecomp->num1 == 0) && (ecomp->num2 != 0)) { + ECompList *newcomplist; + + newcomplist = (ECompList *)MALLOC(sizeof(ECompList)); + newcomplist->ecomp = ecomp; + newcomplist->next = list0X; + list0X = newcomplist; + } + } + + /* Case 2: Cell2 class is a subcircuit, and flattening */ + /* (it without regard to cell1) improves the matching. */ + + else if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) && + (ecomp->cell2->class == CLASS_SUBCKT)) { + ecomp->add2 = -ecomp->num2; + match = 1; + for (ob2 = ecomp->cell2->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell2->file, &compdict); + if (ncomp != NULL) { + if ((ncomp->num1 > ncomp->num2) && + ((ncomp->add2 + ecomp->num2) <= + ncomp->num1)) { + ncomp->add2 += ecomp->num2; + } + else { + match = 0; + break; + } + } + else { + match = 0; + break; + } + } + } + if (match) { + if (ecomp->cell2) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecomp->cell2->name, + name2); + flattenInstancesOf(name2, file2, ecomp->cell2->name); + } + modified++; + } + + /* Reset or apply the count adjustments */ + if (ecomp->cell2) + for (ob2 = ecomp->cell2->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell2->file, &compdict); + if (ncomp != NULL) { + if (match) { + ncomp->num2 += ncomp->add2; + } + ncomp->add2 = 0; + } + } + } + if (match) { + ecomp->num2 = 0; + } + ecomp->add2 = 0; + } + + /* Case 3: Cell1 class is a subcircuit, and flattening */ + /* (it without regard to cell1) improves the matching. */ + + else if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) && + (ecomp->cell1->class == CLASS_SUBCKT)) { + ecomp->add1 = -ecomp->num1; + match = 1; + for (ob2 = ecomp->cell1->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell1->file, &compdict); + if (ncomp != NULL) { + if ((ncomp->num2 > ncomp->num1) && + ((ncomp->add1 + ecomp->num1) <= + ncomp->num2)) { + ncomp->add1 += ecomp->num1; + } + else { + match = 0; + break; + } + } + else { + match = 0; + break; + } + } + } + if (match) { + if (ecomp->cell1) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecomp->cell1->name, + name1); + flattenInstancesOf(name1, file1, ecomp->cell1->name); + } + modified++; + } + + /* Reset or apply the count adjustments */ + if (ecomp->cell1) + for (ob2 = ecomp->cell1->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp->cell1->file, &compdict); + if (ncomp != NULL) { + if (match) { + ncomp->num1 += ncomp->add1; + } + ncomp->add1 = 0; + } + } + } + if (match) { + ecomp->num1 = 0; + } + ecomp->add1 = 0; + } + 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, devcon; + DCompare *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); + InitializeHashTable(&devcon, OBJHASHSIZE); + + // Populate the device hash table + + SurveyDevice(tc1, &devdict, ecomp->cell1, kl1, file1, file2, 0); + SurveyDevice(tc2, &devdict, ecomp->cell2, kl2, file1, file2, 1); + + // Rehash the devices according to (property * count) totals + + dcomp = (DCompare *)HashFirst(&devdict); + while (dcomp != NULL) { + char *pstr; + DCompare *dcomp2; + + if ((dcomp->num1 == 0) || (dcomp->num2 == 0)) { + valptr = strstr(devdict.hashfirstptr->name, "::"); + pstr = (char *)MALLOC(strlen(devdict.hashfirstptr->name + 20)); + *valptr = '\0'; + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + (dcomp->total1 + dcomp->total2)); + *valptr = ':'; + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + HashPtrInstall(pstr, dcomp, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num1 += dcomp->num1; + dcomp2->num2 += dcomp->num2; + dcomp2->total1 += dcomp->total1; + dcomp2->total2 += dcomp->total2; + } + dcomp->processed = 1; + } + else if (2 * fabs(dcomp->total1 - dcomp->total2) / + (dcomp->total1 + dcomp->total2) < slop) { + /* Copy this entry verbatim to devcon */ + valptr = strstr(devdict.hashfirstptr->name, "::"); + pstr = (char *)MALLOC(strlen(devdict.hashfirstptr->name + 20)); + *valptr = '\0'; + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + dcomp->total1); + *valptr = ':'; + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + HashPtrInstall(pstr, dcomp, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num1 += dcomp->num1; + dcomp2->num2 += dcomp->num2; + dcomp2->total1 += dcomp->total1; + dcomp2->total2 += dcomp->total2; + } + dcomp->processed = 1; + } + else if (dcomp->num1 < dcomp->num2) { + /* Process the smaller number and break out */ + /* the remainder from the larger number. */ + int irem = dcomp->num2 - dcomp->num1; + double drem = dcomp->total2 - dcomp->total1; + + valptr = strstr(devdict.hashfirstptr->name, "::"); + pstr = (char *)MALLOC(strlen(devdict.hashfirstptr->name + 20)); + *valptr = '\0'; + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + dcomp->total1); + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + dcomp->num2 = dcomp->num1; + dcomp->total2 = dcomp->total1; + HashPtrInstall(pstr, dcomp, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num1 += dcomp->num1; + dcomp2->num2 += dcomp->num1; + dcomp2->total1 += dcomp->total1; + dcomp2->total2 += dcomp->total1; + } + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + drem); + *valptr = ':'; + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + // Make a copy of dcomp with only dev2 + // Set refcount to -1 so we can do garbage collection + dcomp2 = (DCompare *)MALLOC(sizeof(DCompare)); + dcomp2->num1 = 0; + dcomp2->num2 = irem; + dcomp2->total1 = 0; + dcomp2->total2 = drem; + dcomp2->processed = 0; + dcomp2->refcount = (char)-1; + HashPtrInstall(pstr, dcomp2, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num2 += irem; + dcomp2->total2 += drem; + } + dcomp->processed = 1; + } + else if (dcomp->num1 > dcomp->num2) { + /* Process the smaller number and break out */ + /* the remainder from the larger number. */ + int irem = dcomp->num1 - dcomp->num2; + double drem = dcomp->total1 - dcomp->total2; + + valptr = strstr(devdict.hashfirstptr->name, "::"); + pstr = (char *)MALLOC(strlen(devdict.hashfirstptr->name + 20)); + *valptr = '\0'; + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + dcomp->total2); + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + dcomp->num1 = dcomp->num2; + dcomp->total1 = dcomp->total2; + HashPtrInstall(pstr, dcomp, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num1 += dcomp->num2; + dcomp2->num2 += dcomp->num2; + dcomp2->total1 += dcomp->total2; + dcomp2->total2 += dcomp->total2; + } + sprintf(pstr, "%s::%g", devdict.hashfirstptr->name, + drem); + *valptr = ':'; + dcomp2 = (DCompare *)HashLookup(pstr, &devcon); + if (dcomp2 == NULL) { + // Make a copy of dcomp with only dev2 + // Set refcount to -1 so we can do garbage collection + dcomp2 = (DCompare *)MALLOC(sizeof(DCompare)); + dcomp2->num1 = irem; + dcomp2->num2 = 0; + dcomp2->total1 = drem; + dcomp2->total2 = 0; + dcomp2->processed = 0; + dcomp2->refcount = (char)-1; + HashPtrInstall(pstr, dcomp2, &devcon); + } + else if (dcomp->processed == 0) { + dcomp2->num1 += irem; + dcomp2->total1 += drem; + } + dcomp->processed = 1; + } + dcomp = (DCompare *)HashNext(&devdict); + } + + // 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 = (DCompare *)HashFirst(&devcon); + while (dcomp != NULL) { + + if ((dcomp->num1 == 0) || (dcomp->num2 == 0)) { + dval1 += dcomp->total1; + dval2 += dcomp->total2; + if ((dcomp->num1 > 0) && ((dcomp->total1 / dcomp->num1) + < mindev)) mindev = (dcomp->total1 / dcomp->num1); + if ((dcomp->num2 > 0) && ((dcomp->total2 / dcomp->num2) + < mindev)) mindev = (dcomp->total2 / dcomp->num2); + } + else if (2 * fabs(dcomp->total1 - dcomp->total2) / + (dcomp->total1 + dcomp->total2) < slop) { + int np; + + np = MAX(dcomp->num1, dcomp->num2); + mindev = dcomp->total1 / (double)np; + + dval = dcomp->total1 / dcomp->num1; + ndev = np / dcomp->num1; + if (ndev > 1) { + SplitDevice(tc1, ecomp->cell1, kl1, dval, + mindev, ndev, file1, file2, 0, slop); + modified++; + } + dval = dcomp->total2 / dcomp->num2; + ndev = np / dcomp->num2; + if (ndev > 1) { + SplitDevice(tc2, ecomp->cell2, kl2, dval, + mindev, ndev, file1, file2, 1, slop); + modified++; + } + } + dcomp = (DCompare *)HashNext(&devcon); + } + + // 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 = (DCompare *)HashFirst(&devcon); + while (dcomp != NULL) { + if (dcomp->num1 == 0) { + dval = dcomp->total2 / dcomp->num2; + ndev = (int)round(dval / mindev); + if (ndev > 1) { + SplitDevice(tc2, ecomp->cell2, kl2, dval, + mindev, ndev, file1, file2, 1, slop); + modified++; + } + } + else if (dcomp->num2 == 0) { + dval = dcomp->total1 / dcomp->num1; + ndev = (int)round(dval / mindev); + if (ndev > 1) { + SplitDevice(tc1, ecomp->cell1, kl1, dval, + mindev, ndev, file1, file2, 0, slop); + modified++; + } + } + dcomp = (DCompare *)HashNext(&devcon); + } + } + + // Free entries that were specially marked with -1. + // All other entries belong to devdict. + + dcomp = (DCompare *)HashFirst(&devcon); + while (dcomp != NULL) { + if (dcomp->refcount == (char)-1) FREE(dcomp); + dcomp = (DCompare *)HashNext(&devdict); + } + HashKill(&devcon); + + // Free the device hash table + dcomp = (DCompare *)HashFirst(&devdict); + while (dcomp != NULL) { + if (--dcomp->refcount == (char)0) FREE(dcomp); + dcomp = (DCompare *)HashNext(&devdict); + } + HashKill(&devdict); + } + } + + ecomp = (ECompare *)HashNext(&compdict); + } + + // Remove non-matching zero-value devices. This can + // be done on a per-instance basis. + + ecomp = (ECompare *)HashFirst(&compdict); + while (ecomp != NULL) { + if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) && + (ecomp->cell1->class == CLASS_RES)) { + int node1 = -1, node2 = -1; + lob = NULL; + for (ob1 = tc1->cell; ob1; ) { + if (ob1->type == FIRSTPIN) { + tsub1 = LookupCellFile(ob1->model.class, file1); + if (tsub1 == ecomp->cell1) { + node1 = ob1->node; + for (ob1 = ob1->next; ob1 && ob1->type != FIRSTPIN; ) { + if (ob1->type == FIRSTPIN + 1) + node2 = ob1->node; + if (ob1->type == PROPERTY && node1 != -1 && node2 != -1) { + if (ob1->instance.props != NULL) { + struct valuelist *kv; + int i; + int found = 0; + for (i = 0; ; i ++) { + kv = &(ob1->instance.props[i]); + if (kv->type == PROP_ENDLIST) break; + if ((*matchfunc)(kv->key, "value") || + (*matchfunc)(kv->key, "length")) { + switch(kv->type) { + case PROP_STRING: + if ((*matchfunc)(kv->value.string, + "0")) + found = 1; + break; + case PROP_INTEGER: + if (kv->value.ival == 0) + found = 1; + break; + case PROP_DOUBLE: + case PROP_VALUE: + if (kv->value.dval == 0.0) + found = 1; + break; + case PROP_EXPRESSION: + /* Unresolved expression */ + break; + } + } + if (found) break; + } + if (found) { + Fprintf(stdout, "Removing zero-valued device" + "%s from cell %s makes a better match\n", + tsub1->name, + tc1->name); + + /* merge node of endpoints */ + for (ob2 = tc1->cell; ob2; ob2 = ob2->next) { + if (ob2->node == node2) + ob2->node = node1; + } + + /* snip, snip. Excise this device */ + if (lob == NULL) { + ob2 = tc1->cell; + tc1->cell = ob1->next; + } + else { + ob2 = lob->next; + lob->next = ob1->next; + } + + /* free the device */ + for (; ob2 && ob2 != ob1->next; ) { + struct objlist *nob; + nob = ob2->next; + FreeObjectAndHash(ob2, tc1); + ob2 = nob; + } + + /* Remove from list */ + ecomp->num1--; + modified++; + + ob1 = lob; + } + else + ob1 = ob1->next; + } + else + ob1 = ob1->next; + } + else + ob1 = ob1->next; + } + } + else { + lob = ob1; + ob1 = ob1->next; + } + } + else { + lob = ob1; + ob1 = ob1->next; + } + } + } + + // Repeat the last section for the other circuit + + if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) && + (ecomp->cell2->class == CLASS_RES)) { + int node1 = -1, node2 = -1; + lob = NULL; + for (ob2 = tc2->cell; ob2; ) { + if (ob2->type == FIRSTPIN) { + tsub2 = LookupCellFile(ob2->model.class, file2); + if (tsub2 == ecomp->cell2) { + node1 = ob2->node; + for (ob2 = ob2->next; ob2 && ob2->type != FIRSTPIN; ) { + if (ob2->type == FIRSTPIN + 1) + node2 = ob2->node; + if (ob2->type == PROPERTY && node1 != -1 && node2 != -1) { + if (ob2->instance.props != NULL) { + struct valuelist *kv; + int i; + int found = 0; + for (i = 0; ; i ++) { + kv = &(ob2->instance.props[i]); + if (kv->type == PROP_ENDLIST) break; + if ((*matchfunc)(kv->key, "value") || + (*matchfunc)(kv->key, "length")) { + switch(kv->type) { + case PROP_STRING: + if ((*matchfunc)(kv->value.string, + "0")) + found = 1; + break; + case PROP_INTEGER: + if (kv->value.ival == 0) + found = 1; + break; + case PROP_DOUBLE: + case PROP_VALUE: + if (kv->value.dval == 0.0) + found = 1; + break; + case PROP_EXPRESSION: + /* Unresolved expression. */ + break; + } + } + if (found) break; + } + if (found) { + Fprintf(stdout, "Removing zero-valued device" + "%s from cell %s makes a better match\n", + tsub2->name, + tc2->name); + + /* merge node of endpoints */ + for (ob1 = tc2->cell; ob1; ob1 = ob1->next) { + if (ob1->node == node2) + ob1->node = node1; + } + + /* snip, snip. Excise this device */ + if (lob == NULL) { + ob1 = tc2->cell; + tc2->cell = ob2->next; + } + else { + ob1 = lob->next; + lob->next = ob2->next; + } + + /* free the device */ + for (; ob1 && ob1 != ob2->next; ) { + struct objlist *nob; + nob = ob1->next; + FreeObjectAndHash(ob1, tc2); + ob1 = nob; + } + + /* Remove from list */ + ecomp->num1--; + modified++; + + ob2 = lob; + } + else + ob2 = ob2->next; + } + else + ob2 = ob2->next; + } + else + ob2 = ob2->next; + } + } + else { + lob = ob2; + ob2 = ob2->next; + } + } + else { + lob = ob2; + ob2 = ob2->next; + } + } + } + ecomp = (ECompare *)HashNext(&compdict); + } + + // Finally, check all entries in listX0 vs. all entries in list0X to see + // if flattening one side will improve the matching. Ignore entries + // that are duplicates (already matched). Also do this only if there + // are no other modifications, as this rule is relaxed compared to other + // rules, and the other rules should be exhaustively applied first. + + if ((listX0 != NULL) && (list0X != NULL) && (modified == 0)) { + ECompare *ecomp0X, *ecompX0; + ECompList *elist0X, *elistX0; + for (elistX0 = listX0; elistX0; elistX0 = elistX0->next) { + ecompX0 = elistX0->ecomp; + + for (elist0X = list0X; elist0X; elist0X = elist0X->next) { + ecomp0X = elist0X->ecomp; + + // Check that this has not already been processed and flattened + if (ecompX0->num1 > 0 && ecomp0X->num2 > 0) { + + // Are any components of ecompX0->cell1 in the ecomp0X list? + + for (ob1 = ecompX0->cell1->cell; ob1; ob1 = ob1->next) { + if (ob1->type == FIRSTPIN) { + char *dstr = NULL; + tc1 = LookupCellFile(ob1->model.class, ecompX0->cell1->file); + if (tc1->flags & CELL_DUPLICATE) { + dstr = strstr(ob1->model.class, "[["); + if (dstr) *dstr = '\0'; + } + ncomp = (ECompare *)HashInt2Lookup(ob1->model.class, + ecompX0->cell1->file, &compdict); + if (dstr) *dstr = '['; + if ((ncomp == ecomp0X) && (ecomp0X->num2 <= ecompX0->num1)) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecompX0->cell1->name, + name1); + flattenInstancesOf(name1, file1, ecompX0->cell1->name); + ecompX0->num1 = 0; + ecomp0X->num1 += ecompX0->num1; + modified++; + break; + } + } + } + + // Are any components of ecomp0X->cell2 in the ecompX0 list? + + for (ob2 = ecomp0X->cell2->cell; ob2; ob2 = ob2->next) { + if (ob2->type == FIRSTPIN) { + char *dstr = NULL; + tc2 = LookupCellFile(ob2->model.class, ecomp0X->cell2->file); + if (tc2->flags & CELL_DUPLICATE) { + dstr = strstr(ob1->model.class, "[["); + if (dstr) *dstr = '\0'; + } + ncomp = (ECompare *)HashInt2Lookup(ob2->model.class, + ecomp0X->cell2->file, &compdict); + if (dstr) *dstr = '['; + if ((ncomp == ecompX0) && (ecompX0->num1 <= ecomp0X->num2)) { + Fprintf(stdout, "Flattening instances of %s in cell %s" + " makes a better match\n", ecomp0X->cell2->name, + name2); + flattenInstancesOf(name2, file2, ecomp0X->cell2->name); + ecomp0X->num2 = 0; + ecompX0->num2 += ecomp0X->num2; + modified++; + break; + } + } + } + } + } + } + } + + // Free the hash table and its contents. + + ecomp = (ECompare *)HashFirst(&compdict); + while (ecomp != NULL) { + if (--ecomp->refcount == (char)0) FREE(ecomp); + ecomp = (ECompare *)HashNext(&compdict); + } + HashKill(&compdict); + + // Free the 0:X and X:0 lists + while (listX0 != NULL) { + ECompList *nextptr = listX0->next; + FREE(listX0); + listX0 = nextptr; + } + while (list0X != NULL) { + ECompList *nextptr = list0X->next; + FREE(list0X); + list0X = nextptr; + } + return modified; +} diff --git a/base/netcmp.c b/base/netcmp.c index b663553..2f77115 100644 --- a/base/netcmp.c +++ b/base/netcmp.c @@ -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; + } } } diff --git a/base/netgen.c b/base/netgen.c index 9d40349..b1b24a6 100644 --- a/base/netgen.c +++ b/base/netgen.c @@ -30,6 +30,10 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #endif +#ifdef TCL_NETGEN +#include +#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); + } +} + /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ diff --git a/base/netgen.h b/base/netgen.h index c156053..7f3fe14 100644 --- a/base/netgen.h +++ b/base/netgen.h @@ -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 *); diff --git a/base/spice.c b/base/spice.c index f41d369..c4bb5c0 100644 --- a/base/spice.c +++ b/base/spice.c @@ -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); diff --git a/tcltk/netgen.tcl.in b/tcltk/netgen.tcl.in index b019150..c076ae2 100644 --- a/tcltk/netgen.tcl.in +++ b/tcltk/netgen.tcl.in @@ -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" diff --git a/tcltk/tclnetgen.c b/tcltk/tclnetgen.c index 924d9c3..99734c4 100644 --- a/tcltk/tclnetgen.c +++ b/tcltk/tclnetgen.c @@ -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|]\n " - " [|]: 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, "| [...]\n " ": 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] */