netgen/base/flatten.c

2390 lines
68 KiB
C

/* "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 <stdio.h>
#include <math.h>
#ifdef IBMPC
#include <alloc.h>
#endif
#ifdef TCL_NETGEN
#include <tcl.h>
#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[MAX_STR_LEN];
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 (%d) found.\n", name, file);
return;
}
/* Placeholder cells must not be flattened */
if (ThisCell->flags & CELL_PLACEHOLDER) 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);
if ((ChildObjList = tmp) == NULL) break;
}
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);
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);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
#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);
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 */
}
/* Structure used to keep track of nodes needing checking */
struct linkednode {
int node;
struct linkednode *next;
};
/*--------------------------------------------------------------*/
/* 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, *CurrentProp;
struct objlist *NextObj, *LastObj, *prepp;
struct objlist *ChildObjList, *ChildListEnd;
struct objlist *ChildStart, *ChildEnd, *ParentEnd, *ParentNext;
struct nlist *ThisCell;
struct nlist *ChildCell;
struct objlist *tmp, *ob2, *ob3;
struct linkednode *checknodes = NULL, *newlnode, *chknode;
int notdone, rnodenum;
char tmpstr[1024];
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 (%d)\n", instance,
name, fnum);
if (fnum == -1)
ThisCell = LookupCell(name);
else
ThisCell = LookupCellFile(name, fnum);
if (ThisCell == NULL) {
Printf("No cell %s (%d) found.\n", name, fnum);
return 0;
}
}
/* Placeholder cells must not be flattened */
if (ThisCell->flags & CELL_PLACEHOLDER) 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;
LastObj = NULL;
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) {
LastObj = ParentParams;
continue;
}
if (!(*matchfunc)(ParentParams->model.class, instance)) {
LastObj = ParentParams;
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) && (ChildCell->class != CLASS_MODULE)) {
LastObj = ParentParams;
continue;
}
if (ChildCell == ThisCell) {
LastObj = ParentParams;
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;
CurrentProp = NULL;
}
else
CurrentProp = ParentProps;
/* Find the end record of the parent cell and save it */
for (ParentEnd = (ParentProps) ? ParentProps : ParentParams;
ParentEnd && ParentEnd->next &&
/* Stop on a node or the next instance */
((ParentEnd->next->type > FIRSTPIN) ||
(ParentEnd->next->type == PROPERTY));
ParentEnd = ParentEnd->next);
/* Not primitive, so need to flatten this instance */
notdone = 1;
/* Loop over property records. Need to flatten once per */
/* property record (or more, if property has M > 1) */
ob2 = NULL;
ChildObjList = NULL;
ChildListEnd = NULL;
while (1) {
ChildStart = CopyObjList(ChildCell->cell, 1);
numflat++;
/* Find the end record of the child cell and save it */
for (ChildEnd = ChildStart; ChildEnd && ChildEnd->next;
ChildEnd = ChildEnd->next);
if (ChildListEnd == NULL) ChildListEnd = ChildEnd;
/* update node numbers in child to unique numbers */
oldmax = 0;
for (tmp = ChildStart; tmp != NULL; tmp = tmp->next) {
if (tmp->node > oldmax) oldmax = tmp->node;
if (tmp->node > 0) tmp->node += (nextnode - 1);
}
nextnode += oldmax;
/* copy nodenumbers of ports from parent */
ob2 = ParentParams;
for (tmp = ChildStart; tmp && IsPort(tmp); tmp = tmp->next) {
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(ChildStart, tmp->node, ob2->node);
}
else if (tmp->node == -1) {
/* Opposite case: If child port is an unconnected node, then */
/* removing the instance may make the parent node become */
/* unconnected. For now, just record the node number. At the */
/* end we'll check if these nodes are actually disconnected. */
newlnode = (struct linkednode *)MALLOC(sizeof(struct linkednode));
newlnode->node = ob2->node;
newlnode->next = checknodes;
checknodes = newlnode;
}
/* 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 ((ChildStart != NULL) && IsPort(ChildStart)) {
/* delete all ports at beginning of list */
if (Debug) Printf("deleting leading port from child\n");
tmp = ChildStart->next;
FreeObject(ChildStart);
ChildStart = tmp;
}
tmp = ChildStart;
while (tmp && (tmp->next != NULL)) {
if (IsPort(tmp->next)) {
ob2 = (tmp->next)->next;
if (Debug) Printf("deleting a port from child\n");
FreeObject(tmp->next);
tmp->next = ob2;
}
else tmp = tmp->next;
}
if (ChildStart == NULL) {
if (ChildListEnd == ChildEnd) ChildListEnd = NULL;
ChildEnd = NULL;
}
}
/* 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 = ChildStart; 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 = ChildStart; 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);
HashPtrInstall(tmp->name, tmp, &(ThisCell->objdict));
if ((tmp->type != NODE) && (tmp->instance.name != NULL)) {
#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);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, &(ThisCell->instdict));
}
}
/* Do property inheritance */
if (CurrentProp) {
int i, mval;
struct valuelist *kv;
for (ob2 = ChildStart; 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, CurrentProp, ChildCell,
(spiceparams.hashtab == NULL) ? 0 : 1);
}
}
/* Check for property M. If it exists and is greater than */
/* one, reduce it and repeat; i.e., generate multiple */
/* child instances to match the M value. Probably this */
/* could be done quicker by just creating a new property */
/* M for a single child. */
mval = 0;
for (i = 0; ; i++) {
kv = &(CurrentProp->instance.props[i]);
if (kv->type == PROP_ENDLIST) break;
if ((*matchfunc)(kv->key, "M")) {
if (kv->type == PROP_INTEGER) {
mval = kv->value.ival;
kv->value.ival = mval - 1;
break;
}
else if (kv->type == PROP_DOUBLE) {
mval = (int)kv->value.dval;
kv->value.dval = (double)mval - 1;
break;
}
}
}
if (mval > 1) {
/* Put the child cell at the start of ChildObjList */
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
/* Continue without moving CurrentProp */
/* Note that if ChildEnd is NULL then the child cell
* is optimized out and there is no need to do it
* M times.
*/
continue;
}
}
/* Repeat for each property record, as each property represents a
* unique instance that must be flattened individually.
*/
CurrentProp = CurrentProp->next;
if ((CurrentProp == NULL) || (CurrentProp->type != PROPERTY)) break;
}
else break;
/* Put the child cell at the start of ChildObjList */
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
}
/* Put the child cell at the start of ChildObjList */
if (ChildEnd) {
ChildEnd->next = ChildObjList;
ChildObjList = ChildStart;
}
/* Pull the instance out of the parent */
if ((ParentParams != ThisCell->cell) || (ChildObjList != NULL)) {
if (ParentParams == ThisCell->cell) {
/* ParentParams are the very first thing in the list */
ThisCell->cell = ChildObjList;
}
else if (ChildObjList) {
/* find ParentParams in ThisCell list. In most cases, LastObj */
/* should be pointing to it. */
if (LastObj && (LastObj->next == ParentParams)) {
LastObj->next = ChildObjList;
}
else {
for (ob2 = LastObj; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
if (ob2 == NULL) {
/* It should not happen that LastObj is ahead of ParentParams */
/* but just in case, this long loop will find it. */
for (ob2 = ThisCell->cell; ob2 && ob2->next != ParentParams;
ob2 = ob2->next);
}
ob2->next = ChildObjList;
}
}
else {
/* The child was completely optimized out, so close list around it */
LastObj->next = ParentEnd->next;
}
/* Link end of child list into the parent */
if (ChildListEnd && ParentEnd)
ChildListEnd->next = ParentEnd->next;
}
ParentNext = (ParentEnd) ? ParentEnd->next : NULL;
while (ParentParams != ParentNext) {
ob2 = ParentParams->next;
FreeObjectAndHash(ParentParams, ThisCell);
ParentParams = ob2;
}
NextObj = ParentParams;
} /* repeat until no more instances found */
}
/* Check nodes that may have become disconnected after child flattening */
while (checknodes != NULL) {
struct objlist *portnode = NULL;
chknode = checknodes;
checknodes = checknodes->next;
for (ob3 = ThisCell->cell; ob3; ob3 = ob3->next) {
if ((ob3->type != PORT) && (portnode == NULL))
break;
else if ((ob3->type == PORT) && (ob3->node == chknode->node))
portnode = ob3;
else if ((ob3->type >= FIRSTPIN) && (ob3->node == chknode->node))
break;
}
if ((ob3 == NULL) && (portnode != NULL)) {
/* Port became disconnected when child was flattened */
portnode->node = -1;
}
FREE(chknode);
}
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 (%d) found.\n", name, fnum);
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 (%d) found.\n", name, filenum);
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) {
if (!(*matchfunc)(firstport[ob->node]->name, ob->name)) {
Printf("Pins %s and %s are shorted in cell %s (%d)\n", ob->name,
firstport[ob->node]->name, ThisCell->name, ThisCell->file);
/* Do not count this as a duplicate pin. */
nodecount[ob->node]--;
/* Note: Previously there was code here to move the shorted port
* next to the pin it is shorted to. This causes the cell def pins
* to become scrambled with respect to the pin order of its instances.
* Removed the code 9/1/2023. But---Not sure if any code depends
* on shorted pins being adjacent.
*/
/* When two pins are shorted, they are by definition permutable */
PermuteSetup(ThisCell->name, ThisCell->file, ob->name,
firstport[ob->node]->name);
continue;
}
else {
Printf("Duplicate pin %s in cell %s (%d)\n", ob->name,
ThisCell->name, filenum);
}
}
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;
}
/* Structure used below for keeping track of node numbers
* belonging to removed nodes.
*/
struct LinkedNum {
int node;
struct LinkedNum *next;
};
/*------------------------------------------------------*/
/* 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, *pob;
struct nlist *tc = (struct nlist *)clientdata;
struct LinkedNum *newnodenum, *removedNodes = (struct LinkedNum *)NULL;
int pinnum;
char *saveinst = NULL;
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 == -2)) {
/* 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) {
/* Keep a copy of the instance name (see below) */
if (saveinst != NULL) FREE(saveinst);
saveinst = ob->instance.name;
}
if (ob->model.class != NULL) FREE(ob->model.class);
// Record the net number of the pin being removed, to
// check at the end if the net belonged to a pin that
// got orphaned.
newnodenum = (struct LinkedNum *)MALLOC(sizeof(struct LinkedNum));
newnodenum->node = ob->node;
newnodenum->next = removedNodes;
removedNodes = newnodenum;
FREE(ob);
}
else {
if ((ob->type == PROPERTY) && (pinnum == 1))
{
/* If this happens, then all the pins got removed,
* and there is probably something very much wrong
* with the setup. However, to keep netgen from
* blowing up, add back a "proxy(no pins)" record
* in front; otherwise we'd have an orphaned
* property record.
*/
pob = GetObject();
pob->name = (char *)MALLOC(15);
sprintf(pob->name, "proxy(no pins)");
pob->model.class = strsave(ob->model.class);
if (saveinst != NULL)
pob->instance.name = strsave(saveinst);
else
/* This should never happen */
pob->instance.name = strsave("error");
pob->type = pinnum++;
pob->node = -1;
pob->next = ob;
lob->next = pob;
lob = 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));
}
}
while (removedNodes != NULL) {
int nodenum = removedNodes->node;
struct objlist *ob2;
/* Only concerned with nodes that are in the pin list of ptr->cell */
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
if (ob->type != PORT) break;
if (ob->node == nodenum) break;
}
if (ob && (ob->type == PORT)) {
/* Check if this node number exists only in the port record */
for (nob = ob->next; nob != NULL; nob = nob->next)
if (nob->node == nodenum) break;
if (nob == NULL) {
ob->node = -1; /* This pin is now disconnected */
}
}
newnodenum = removedNodes;
removedNodes = removedNodes->next;
FREE(newnodenum);
}
if (saveinst != NULL) FREE(saveinst);
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 (%d) found.\n", name, filenum);
return 0;
}
// If cell is type MODULE, this is a black-box circuit and
// pins are expected to be disconnected (so don't remove them).
if (ThisCell->class == CLASS_MODULE) 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 == -2) {
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 == -2) {
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;
/*----------------------------------------------------------------------*/
/* Determine if a cell contains at least one device or subcircuit */
/*----------------------------------------------------------------------*/
int
HasContents(struct nlist *tc)
{
struct objlist *ob;
for (ob = tc->cell; ob; ob = ob->next)
if (ob->type == FIRSTPIN)
return TRUE;
return FALSE;
}
/*------------------------------------------------------*/
/* 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/series 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 hascontents1, hascontents2;
int match, modified1 = 0, modified2 = 0;
int not_top;
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 0;
InitializeHashTable(&compdict, OBJHASHSIZE);
listX0 = list0X = NULL;
// 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.
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 {
// cell exists in one circuit but not the other, so flatten it.
// match = 0;
break;
}
}
}
if (match) {
if (ecomp->cell1 && (ecomp->num1 > 0) &&
(!(ecomp->cell1->flags & CELL_PLACEHOLDER))) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" makes a better match\n", ecomp->cell1->name,
name1, file1);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified1++;
}
else if (ecomp->cell1 && (ecomp->num1 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell1->name, name1, file1);
}
if (ecomp->cell2 && (ecomp->num2 > 0) &&
(!(ecomp->cell2->flags & CELL_PLACEHOLDER))) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" makes a better match\n", ecomp->cell2->name,
name2, file2);
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified2++;
}
else if (ecomp->cell2 && (ecomp->num2 > 0)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell2->name, name2, file2);
}
}
/* 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->num2 != 0) && (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) {
/* Don't flatten if cell1 is a black box, because it */
/* can't also be flattened. */
if ((ecomp->num1 == 0) || (ecomp->cell1->class !=
CLASS_MODULE)) {
if (ecomp->cell2 && !(ecomp->cell2->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" makes a better match\n", ecomp->cell2->name,
name2, file2);
flattenInstancesOf(name2, file2, ecomp->cell2->name);
modified2++;
}
else if (ecomp->cell2) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell2->name, name2, file2);
}
}
}
/* 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 cell2) improves the matching. */
else if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
(ecomp->num1 != 0) && (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) {
/* Don't flatten if cell2 is a black box, because it */
/* can't also be flattened. */
if ((ecomp->num2 == 0) || (ecomp->cell2->class !=
CLASS_MODULE)) {
if (ecomp->cell1 && !(ecomp->cell1->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" makes a better match\n", ecomp->cell1->name,
name1, file1);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified1++;
}
else if (ecomp->cell1) {
Fprintf(stdout, "Flattening instances of %s in cell %s (%d)"
" would make a better match but is prohibited.\n",
ecomp->cell1->name, name1, file1);
}
}
}
/* 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);
}
// Remove non-matching zero-value devices. This can
// be done on a per-instance basis.
not_top = (PeekCompareQueueTop(NULL, NULL, NULL, NULL) == -1) ? FALSE : TRUE;
ecomp = (ECompare *)HashFirst(&compdict);
while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell1 != NULL) &&
((ecomp->cell1->class == CLASS_RES) ||
(ecomp->cell1->class == CLASS_VSOURCE) ||
(ecomp->cell1->class == CLASS_ISOURCE))) {
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;
}
}
/* Do NOT remove shorting devices that */
/* connect two ports. Otherwise the */
/* port lists get screwed up. It is */
/* better in that case to force the */
/* cells to be declared mismatched. */
/* This is ignored for a top-level cell */
/* because it will just show up as a */
/* port mismatch error as it should. */
if ((not_top == TRUE) &&
(ecomp->cell1->class != CLASS_ISOURCE)) {
int found1 = FALSE;
int found2 = FALSE;
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (!IsPort(ob2)) break;
else if (ob2->node == node1)
found1 = TRUE;
else if (ob2->node == node2)
found2 = TRUE;
if (found1 && found2) {
found = FALSE;
break;
}
}
}
if (found) break;
}
if (found) {
Fprintf(stdout, "Removing zero-valued device "
"%s from cell %s (%d) makes a better "
"match\n",
tsub1->name, tc1->name, tc1->file);
/* A current source is an open, while a */
/* resistor or voltage source is a short. */
if (ecomp->cell1->class != CLASS_ISOURCE) {
/* merge node of endpoints */
/* Prefer a port node over a non-port node */
for (ob2 = tc1->cell; ob2; ob2 = ob2->next) {
if (!IsPort(ob2)) break;
else if (ob2->node == node1) break;
else if (ob2->node == node2) {
int ntemp = node1;
node1 = node2;
node2 = ntemp;
}
}
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--;
modified1++;
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) ||
(ecomp->cell2->class == CLASS_VSOURCE) ||
(ecomp->cell2->class == CLASS_ISOURCE))) {
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;
}
}
/* (See comments above about removing shorts */
/* between two ports.) */
if ((not_top == TRUE) &&
(ecomp->cell2->class != CLASS_ISOURCE)) {
int found1 = FALSE;
int found2 = FALSE;
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
if (!IsPort(ob1)) break;
else if (ob1->node == node1)
found1 = TRUE;
else if (ob1->node == node2)
found2 = TRUE;
if (found1 && found2) {
found = FALSE;
break;
}
}
}
if (found) break;
}
if (found) {
Fprintf(stdout, "Removing zero-valued device "
"%s from cell %s (%d) makes a better "
"match\n",
tsub2->name, tc2->name, tc2->file);
/* merge node of endpoints */
if (ecomp->cell2->class != CLASS_ISOURCE) {
/* Prefer a port node over a non-port node */
for (ob1 = tc2->cell; ob1; ob1 = ob1->next) {
if (!IsPort(ob1)) break;
else if (ob1->node == node1) break;
else if (ob1->node == node2) {
int ntemp = node1;
node1 = node2;
node2 = ntemp;
}
}
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->num2--;
modified2++;
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) && ((modified1 + modified2) == 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)) {
if (!(ecompX0->cell1->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell"
" %s (%d) makes a better match\n",
ecompX0->cell1->name, name1, file1);
flattenInstancesOf(name1, file1,
ecompX0->cell1->name);
ecompX0->num1 = 0;
ecomp0X->num1 += ecompX0->num1;
modified1++;
}
else
{
Fprintf(stdout, "Flattening instances of %s in "
"cell %s (%d) would make a better "
"match but is prohibited.\n",
ecompX0->cell1->name, name1, file1);
}
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(ob2->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)) {
if (!(ecomp0X->cell2->flags & CELL_PLACEHOLDER)) {
Fprintf(stdout, "Flattening instances of %s in cell"
" %s (%d) makes a better match\n",
ecomp0X->cell2->name, name2, file2);
flattenInstancesOf(name2, file2,
ecomp0X->cell2->name);
ecomp0X->num2 = 0;
ecompX0->num2 += ecomp0X->num2;
modified2++;
}
else {
Fprintf(stdout, "Flattening instances of %s in "
"cell %s (%d) would make a better "
"match but is prohibited.\n",
ecompX0->cell2->name, name2, file2);
}
break;
}
}
}
}
}
}
}
done:
// 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;
}
// If either netlist was modified, rebuild its node cache
if (modified1 > 0) {
FreeNodeNames(tc1);
CacheNodeNames(tc1);
}
if (modified2 > 0) {
FreeNodeNames(tc2);
CacheNodeNames(tc2);
}
return modified1 + modified2;
}