From c7f27c909f2d274acd0f28558e2e2d18470803ed Mon Sep 17 00:00:00 2001 From: Tim Edwards Date: Mon, 11 Jul 2016 08:41:07 -0400 Subject: [PATCH] Finished basic implementation of matching device properties to include calculations of effective width due to the addition of width of multiple device instances in parallel. The original behavior of splitting all "M=" devices into individual instances has been effectively inverted, instead combining all parallel devices of the same class into one, with multiple property records for devices with non-matching properties (e.g., width, length, etc.). Property matching combines devices with different "critical properties" (e.g., FET gate width) if these are defined in the setup using the "property merge" command. Not yet done: Matching of multiple property records when critial properties are not defined, handling of critical properties that combine in parallel instead of simple addition, handling of devices that combine in series, and the combination of non-critical properties (e.g., source and drain area, although these are usually removed from matching). --- base/flatten.c | 394 ------- base/flatten.c.bak | 2529 +++++++++++++++++++++++++++++++++++++++++++ base/netcmp.c | 371 +++++-- base/netgen.c | 737 ++++++++----- base/netgen.h | 1 + base/spice.c | 238 ++-- tcltk/netgen.tcl.in | 1 - tcltk/tclnetgen.c | 177 --- 8 files changed, 3350 insertions(+), 1098 deletions(-) create mode 100644 base/flatten.c.bak 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] */