netgen/base/flatten.c

1635 lines
46 KiB
C
Raw Normal View History

/* "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>
#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"
#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);
/* 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->objtab, OBJHASHSIZE);
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->objtab, OBJHASHSIZE);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, ThisCell->insttab, OBJHASHSIZE);
}
/* 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 *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
/* 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);
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);
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);
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->objtab, OBJHASHSIZE);
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->objtab, OBJHASHSIZE);
if (tmp->type == FIRSTPIN)
HashPtrInstall(tmp->instance.name, tmp, ThisCell->insttab, OBJHASHSIZE);
}
/* 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));
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);
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->objtab, OBJHASHSIZE);
}
// 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->objtab, OBJHASHSIZE);
}
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->objtab, OBJHASHSIZE);
/* 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
// objtab 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->objtab, OBJHASHSIZE);
}
}
tob = tob->next;
if (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->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 insttab 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->objtab, OBJHASHSIZE);
}
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 */
/* insttab 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
// objtab hash table
if (LookupObject(ob->name, ptr) == ob) {
HashDelete(ob->name, ptr->objtab, OBJHASHSIZE);
}
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 insttab, in case the first pin got removed */
if (firstpin && (firstpin->type == FIRSTPIN))
HashPtrInstall(firstpin->instance.name, firstpin, ptr->insttab,
OBJHASHSIZE);
}
}
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) 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
// objtab hash table
if (LookupObject(ob->name, ThisCell) == ob) {
HashDelete(ob->name, ThisCell->objtab, OBJHASHSIZE);
}
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;
/*------------------------------------------------------*/
/* Survey the contents of a cell and
/*------------------------------------------------------*/
void
SurveyCell(struct nlist *tc, struct hashlist **comptab, int file1, int file2, int which)
{
struct objlist *ob;
struct nlist *tsub, *teq;
ECompare *ecomp, *qcomp, *ncomp;
for (ob = tc->cell; ob; ob = ob->next) {
if (ob->type == FIRSTPIN) {
tsub = LookupCellFile(ob->model.class, (which == 0) ? file1 : file2);
ecomp = (ECompare *)HashLookup(ob->model.class, comptab, OBJHASHSIZE);
if (ecomp == NULL) {
ncomp = (ECompare *)MALLOC(sizeof(ECompare));
if (which == 0) {
ncomp->num1 = 1;
ncomp->num2 = 0;
ncomp->cell1 = tsub;
teq = LookupClassEquivalent(ob->model.class, file1, file2);
ncomp->cell2 = teq;
}
else {
ncomp->num1 = 0;
ncomp->num2 = 1;
ncomp->cell2 = tsub;
teq = LookupClassEquivalent(ob->model.class, file2, file1);
ncomp->cell1 = teq;
}
ncomp->add1 = 0;
ncomp->add2 = 0;
ncomp->refcount = (char)1;
HashPtrInstall(ob->model.class, ncomp, comptab, OBJHASHSIZE);
if (teq != NULL) {
qcomp = (ECompare *)HashLookup(teq->name, comptab, OBJHASHSIZE);
if (qcomp == NULL) {
HashPtrInstall(teq->name, ncomp, comptab, OBJHASHSIZE);
ncomp->refcount++;
}
}
}
else {
if (which == 0)
ecomp->num1++;
else
ecomp->num2++;
}
}
}
}
/*------------------------------------------------------*/
/* 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, accoring to */
/* combination rules. */
/* */
/* Return the number of modifications made. */
/*------------------------------------------------------*/
int
PrematchLists(char *name1, int file1, char *name2, int file2)
{
struct nlist *tc1, *tc2, *teq, *tsub1, *tsub2;
struct objlist *ob1, *ob2, *lob;
struct hashlist **comptab;
ECompare *ecomp, *ncomp;
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;
comptab = (struct hashlist **)CALLOC(OBJHASHSIZE,
sizeof(struct hashlist *));
// Gather information about instances of cell "name1"
SurveyCell(tc1, comptab, file1, file2, 0);
// Gather information about instances of cell "name2"
SurveyCell(tc2, comptab, 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 more in the compared circuit.
ecomp = (ECompare *)HashFirst(comptab, OBJHASHSIZE);
while (ecomp != NULL) {
if ((ecomp->num1 != ecomp->num2) && (ecomp->cell2 != NULL) &&
(ecomp->cell2->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 *)HashLookup(ob2->model.class,
comptab, OBJHASHSIZE);
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) {
flattenInstancesOf(name2, file2, ecomp->cell2->name);
flattenInstancesOf(name1, file1, ecomp->cell1->name);
modified++;
}
/* Reset or apply the count adjustments */
for (ob2 = ecomp->cell2->cell; ob2; ob2 = ob2->next) {
if (ob2->type == FIRSTPIN) {
ncomp = (ECompare *)HashLookup(ob2->model.class,
comptab, OBJHASHSIZE);
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;
}
ecomp = (ECompare *)HashNext(comptab, OBJHASHSIZE);
}
// Remove non-matching zero-value devices. This can
// be done on a per-instance basis.
ecomp = (ECompare *)HashFirst(comptab, OBJHASHSIZE);
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) {
/* 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) {
/* 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(comptab, OBJHASHSIZE);
}
// Free the hash table and its contents.
ecomp = (ECompare *)HashFirst(comptab, OBJHASHSIZE);
while (ecomp != NULL) {
if (--ecomp->refcount == (char)0) FREE(ecomp);
ecomp = (ECompare *)HashNext(comptab, OBJHASHSIZE);
}
HashKill(comptab, OBJHASHSIZE);
FREE(comptab);
return modified;
}