1443 lines
36 KiB
C
1443 lines
36 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. */
|
|
|
|
/* objlist.c -- manipulating lists of elements */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <limits.h>
|
|
#ifdef IBMPC
|
|
#include <alloc.h>
|
|
#endif
|
|
|
|
#ifdef TCL_NETGEN
|
|
#include <tcl.h>
|
|
#endif
|
|
|
|
#include "netgen.h"
|
|
#include "objlist.h"
|
|
#include "regexp.h"
|
|
#include "dbug.h"
|
|
#include "print.h"
|
|
#include "netfile.h"
|
|
#include "netcmp.h"
|
|
#include "hash.h"
|
|
|
|
#ifdef TCL_NETGEN
|
|
extern Tcl_Interp *netgeninterp;
|
|
#endif
|
|
|
|
struct nlist *CurrentCell = NULL;
|
|
struct objlist *CurrentTail = NULL;
|
|
|
|
/* shortcut pointer to last-placed object in list; used in netgen.c */
|
|
struct objlist *LastPlaced = NULL;
|
|
|
|
/* used to narrow list of cells to those belonging to a specific file */
|
|
int TopFile = -1;
|
|
|
|
/* global variable to enable/disable translation of UNIX wildcards */
|
|
int UnixWildcards = 1;
|
|
/* define the following to minimize use of regcomp/regexp stuff */
|
|
#define OPTIMIZE_WILDCARDS
|
|
|
|
/***************************************************************************/
|
|
/* */
|
|
/* NETGEN garbage collection / monitoring stuff */
|
|
/* */
|
|
/***************************************************************************/
|
|
|
|
|
|
#define GARBAGESIZE 100
|
|
/* list of allocated nodes awaiting garbage collection */
|
|
static struct objlist *garbage[GARBAGESIZE];
|
|
static int nextfree;
|
|
static int ObjectsAllocated = 0;
|
|
#ifdef DEBUG_GARBAGE
|
|
static int StringsAllocated = 0;
|
|
#endif
|
|
|
|
void GarbageCollect()
|
|
{
|
|
}
|
|
|
|
void InitGarbageCollection()
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < GARBAGESIZE; i++)
|
|
garbage[i] = NULL;
|
|
nextfree = 0;
|
|
}
|
|
|
|
void ThrowOutGarbage(int i)
|
|
{
|
|
struct objlist *tp, *tpnext;
|
|
|
|
tp = garbage[i];
|
|
while (tp != NULL) {
|
|
tpnext = tp->next;
|
|
FREE(tp);
|
|
ObjectsAllocated--;
|
|
tp = tpnext;
|
|
}
|
|
#ifdef DEBUG_GARBAGE
|
|
Printf("ThrowOutGarbage: objects left = %d\n",ObjectsAllocated);
|
|
#endif
|
|
garbage[i] = NULL;
|
|
}
|
|
|
|
|
|
void AddToGarbageList(struct objlist *head)
|
|
{
|
|
if (garbage[nextfree] != NULL) ThrowOutGarbage(nextfree);
|
|
garbage[nextfree] = head;
|
|
nextfree = (nextfree + 1) % GARBAGESIZE;
|
|
}
|
|
|
|
#ifdef DEBUG_GARBAGE
|
|
/* otherwise, inline these functions with macros */
|
|
|
|
struct keyvalue *NewKeyValue(void)
|
|
{
|
|
struct keyvalue *kv;
|
|
|
|
kv = (struct keyvalue *)CALLOC(1,sizeof(struct keyvalue));
|
|
if (kv == NULL) Fprintf(stderr,"NewKeyValue: Core allocation error\n");
|
|
return (kv);
|
|
}
|
|
|
|
struct property *NewProperty(void)
|
|
{
|
|
struct property *kl;
|
|
|
|
kl = (struct property *)CALLOC(1,sizeof(struct property));
|
|
if (kl == NULL) Fprintf(stderr,"NewProperty: Core allocation error\n");
|
|
kl->pin = (char *)NULL;
|
|
return (kl);
|
|
}
|
|
|
|
struct valuelist *NewPropValue(int entries)
|
|
{
|
|
struct valuelist *vl;
|
|
|
|
vl = (struct valuelist *)CALLOC(entries, sizeof(struct valuelist));
|
|
if (vl == NULL) Fprintf(stderr,"NewPropValue: Core allocation error\n");
|
|
return (vl);
|
|
}
|
|
|
|
struct objlist *GetObject(void)
|
|
{
|
|
struct objlist *tp;
|
|
|
|
#ifdef IBMPC
|
|
Printf("GetObject(): num = %d; mem left = %ld\n",
|
|
++ObjectsAllocated, (long)coreleft());
|
|
#endif
|
|
tp = (struct objlist *)CALLOC(1,sizeof(struct objlist));
|
|
if (tp == NULL) Fprintf(stderr,"GetObject: Core allocation error\n");
|
|
return (tp);
|
|
}
|
|
|
|
void FreeString(char *foo)
|
|
{
|
|
Printf("Freeing string: %s, number = %d\n", foo, --StringsAllocated);
|
|
FREE(foo);
|
|
}
|
|
|
|
char *strsave(char *s)
|
|
{
|
|
char *p;
|
|
|
|
#ifdef DEBUG_GARBAGE
|
|
Printf("\tstrsave(%s), num = %d\n",s,++StringsAllocated);
|
|
#endif
|
|
if ((p = (char *)MALLOC(strlen(s)+1)) == NULL)
|
|
Fprintf(stderr,"strsave: core allocation failure\n");
|
|
else strcpy(p, s);
|
|
return (p);
|
|
}
|
|
#endif /* DEBUG_GARBAGE */
|
|
|
|
/* Case-sensitive matching */
|
|
|
|
int match(char *st1, char *st2)
|
|
{
|
|
if (0==strcmp(st1,st2)) return(1);
|
|
else return(0);
|
|
}
|
|
|
|
/* Case-insensitive matching */
|
|
|
|
/* For case-insensitivity, the alphabet is lower-cased, but */
|
|
/* also all common vector delimiters are cast to the same */
|
|
/* character so that different vector notations will match. */
|
|
|
|
static char to_lower[256] = {
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
|
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
|
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
|
40, 41, 42, 43, 44, 45, 46, 47,
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
':', ';', '<', '=', '>', '?', '@',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'<', '\\', '>', '^', '_', '`',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'<', '|', '>', '~', 127,
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
|
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
|
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
|
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
|
|
40, 41, 42, 43, 44, 45, 46, 47,
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
':', ';', '<', '=', '>', '?', '@',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'<', '\\', '>', '^', '_', '`',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
|
|
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
|
|
'<', '|', '>', '~', 127
|
|
};
|
|
|
|
int matchnocase(char *st1, char *st2)
|
|
{
|
|
char *sp1 = st1;
|
|
char *sp2 = st2;
|
|
|
|
/* In case of a property that does not exist in one netlist, matchnocase()
|
|
* may be passed a null value, so return 0 to indicate a non-match.
|
|
* *Both* values null will also be treated as a mismatch (debatable
|
|
* behavior).
|
|
*/
|
|
if (!sp1 || !sp2) return 0;
|
|
|
|
while (*sp1 != '\0' && *sp2 != '\0') {
|
|
if (to_lower[*sp1] != to_lower[*sp2]) break;
|
|
sp1++;
|
|
sp2++;
|
|
}
|
|
if ((*sp1 != '\0') || (*sp2 != '\0')) return 0;
|
|
return 1;
|
|
}
|
|
|
|
/* Case-sensitive matching with file matching */
|
|
|
|
int matchfile(char *st1, char *st2, int f1, int f2)
|
|
{
|
|
if (f1 != f2) return 0;
|
|
else if (strcmp(st1,st2)) return(0);
|
|
else return(1);
|
|
}
|
|
|
|
/* Case-insensitive matching with file matching */
|
|
|
|
int matchfilenocase(char *st1, char *st2, int f1, int f2)
|
|
{
|
|
char *sp1 = st1;
|
|
char *sp2 = st2;
|
|
|
|
if (f1 != f2) return 0;
|
|
while (*sp1 != '\0' && *sp2 != '\0') {
|
|
if (to_lower[*sp1] != to_lower[*sp2]) break;
|
|
sp1++;
|
|
sp2++;
|
|
}
|
|
if ((*sp1 != '\0') || (*sp2 != '\0')) return 0;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef HAVE_MALLINFO
|
|
void PrintMemoryStats(void)
|
|
{
|
|
struct mallinfo minfo;
|
|
|
|
/* HPUX 7.0 defines mallinfo(void); use mallinfo(0) for HPUX 6.5 */
|
|
minfo = mallinfo();
|
|
|
|
Printf("total space = %d\n", minfo.arena);
|
|
Printf("small blocks = %5d, space = %7d, free space = %7d\n",
|
|
minfo.smblks, minfo.usmblks, minfo.fsmblks);
|
|
Printf("ordinary blocks = %5d, space = %7d, free space = %7d\n",
|
|
minfo.ordblks, minfo.uordblks, minfo.fordblks);
|
|
Printf("holding blocks = %5d, size of header = %d\n",
|
|
minfo.hblks, minfo.hblkhd);
|
|
}
|
|
#endif
|
|
|
|
|
|
/**************************************************************************
|
|
|
|
NETGEN cell hash table
|
|
|
|
**************************************************************************/
|
|
|
|
|
|
#define CELLHASHSIZE 1000
|
|
static struct hashdict cell_dict;
|
|
|
|
void InitCellHashTable(void)
|
|
{
|
|
hashfunc = hashcase;
|
|
matchfunc = NULL;
|
|
matchintfunc = matchfile;
|
|
InitializeHashTable(&cell_dict, CELLHASHSIZE);
|
|
}
|
|
|
|
struct nlist *LookupCell(char *s)
|
|
{
|
|
return((struct nlist *)HashLookup(s, &cell_dict));
|
|
}
|
|
|
|
/* Similar hash lookup to the above, but will check if the matching */
|
|
/* record has a file equal to "f". If not, prepends underscores until */
|
|
/* either a matching record is found, or there is no hash result. */
|
|
|
|
struct nlist *LookupCellFile(char *s, int f)
|
|
{
|
|
struct nlist *he;
|
|
|
|
if (f == -1) return LookupCell(s);
|
|
return HashIntLookup(s, f, &cell_dict);
|
|
}
|
|
|
|
struct nlist *InstallInCellHashTable(char *name, int fnum)
|
|
{
|
|
struct hashlist *ptr;
|
|
struct nlist *p;
|
|
|
|
p = LookupCellFile(name, fnum);
|
|
if (p != NULL) return(p);
|
|
|
|
/* It is not present, so add it to list */
|
|
|
|
p = (struct nlist *)CALLOC(1, sizeof(struct nlist));
|
|
if (p == NULL) return(NULL);
|
|
if ((p->name = strsave(name)) == NULL) goto fail;
|
|
p->file = fnum;
|
|
InitializeHashTable(&(p->objdict), OBJHASHSIZE);
|
|
InitializeHashTable(&(p->instdict), OBJHASHSIZE);
|
|
InitializeHashTable(&(p->propdict), OBJHASHSIZE);
|
|
p->permutes = NULL;
|
|
|
|
// Hash size 0 indicates to hash function that no binning is being done
|
|
p->classhash = (*hashfunc)(name, 0);
|
|
|
|
ptr = HashIntPtrInstall(name, fnum, p, &cell_dict);
|
|
if (ptr == NULL) return(NULL);
|
|
return(p);
|
|
fail:
|
|
if (p->name != NULL) FREE(p->name);
|
|
HashKill(&(p->objdict));
|
|
HashKill(&(p->instdict));
|
|
RecurseHashTable(&(p->propdict), freeprop);
|
|
HashKill(&(p->propdict));
|
|
FREE(p);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Rename the cell named "name" to "newname". Find the hash entry for
|
|
* cell "name", remove it from the hash table, generate a new hash entry
|
|
* for "newname", and set its pointer to the original cell.
|
|
*/
|
|
|
|
void CellRehash(char *name, char *newname, int file)
|
|
{
|
|
struct nlist *tp;
|
|
struct hashlist *ptr;
|
|
|
|
if (file == -1)
|
|
tp = LookupCell(name);
|
|
else
|
|
tp = LookupCellFile(name, file);
|
|
|
|
FREE(tp->name);
|
|
tp->name = strsave(newname);
|
|
|
|
ptr = HashIntPtrInstall(newname, file, (void *)tp, &cell_dict);
|
|
if (ptr != NULL)
|
|
HashIntDelete(name, file, &cell_dict);
|
|
|
|
// Change the classhash to reflect the new name
|
|
tp->classhash = (*hashfunc)(newname, 0);
|
|
}
|
|
|
|
struct nlist *OldCell;
|
|
|
|
int removeshorted(struct hashlist *p, int file)
|
|
{
|
|
struct nlist *ptr;
|
|
struct objlist *ob, *lob, *nob, *tob;
|
|
unsigned char shorted;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
|
|
if ((file != -1) && (ptr->file != file)) return 0;
|
|
|
|
lob = NULL;
|
|
for (ob = ptr->cell; ob != NULL;) {
|
|
nob = ob->next;
|
|
if ((ob->type == FIRSTPIN) && (ob->model.class != NULL)) {
|
|
if ((*matchfunc)(ob->model.class, OldCell->name)) {
|
|
shorted = (unsigned char)1;
|
|
for (tob = nob; tob->type > FIRSTPIN; tob = tob->next) {
|
|
if (tob->node != ob->node) {
|
|
shorted = (unsigned char)0;
|
|
break;
|
|
}
|
|
}
|
|
if (shorted == (unsigned char)0) {
|
|
lob = ob;
|
|
ob = nob;
|
|
continue;
|
|
}
|
|
HashDelete(ob->instance.name, &(ptr->instdict));
|
|
while (1) {
|
|
FreeObjectAndHash(ob, ptr);
|
|
ob = nob;
|
|
if (ob == NULL) break;
|
|
nob = ob->next;
|
|
if (ob->type != PROPERTY && ob->type <= FIRSTPIN) break;
|
|
}
|
|
if (lob == NULL)
|
|
ptr->cell = ob;
|
|
else
|
|
lob->next = ob;
|
|
}
|
|
else {
|
|
lob = ob;
|
|
ob = nob;
|
|
}
|
|
}
|
|
else {
|
|
lob = ob;
|
|
ob = nob;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Remove shorted instances of class "class" from the database */
|
|
|
|
void RemoveShorted(char *class, int file)
|
|
{
|
|
if (file == -1)
|
|
OldCell = LookupCell(class);
|
|
else
|
|
OldCell = LookupCellFile(class, file);
|
|
|
|
if (OldCell == NULL) return;
|
|
RecurseCellFileHashTable(removeshorted, file);
|
|
}
|
|
|
|
/* Remove instances of a deleted class from the database. */
|
|
/* NOTE: This treats deleted classes as not existing, so it */
|
|
/* needs to take care of disconnected ports in the same manner */
|
|
/* as flattenInstancesOf() in disconnecting the port of a */
|
|
/* parent cell if it connected to nothing other than the */
|
|
/* deleted instance. */
|
|
|
|
int deleteclass(struct hashlist *p, int file)
|
|
{
|
|
struct nlist *ptr;
|
|
struct objlist *ob, *lob, *nob, *portnode;
|
|
unsigned char *checknodes;
|
|
int i;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
|
|
if ((file != -1) && (ptr->file != file)) return 0;
|
|
|
|
/* Note: This could be made faster by enumerating all times each
|
|
* node is used during the full pass, then subtracting each time
|
|
* the node is deleted in a child, then disconnecting all nodes
|
|
* that ended up with a zero count.
|
|
*/
|
|
checknodes = (unsigned char *)CALLOC(ptr->nodename_cache_maxnodenum + 1,
|
|
sizeof(unsigned char));
|
|
|
|
lob = NULL;
|
|
for (ob = ptr->cell; ob != NULL;) {
|
|
nob = ob->next;
|
|
if ((ob->type == FIRSTPIN) && (ob->model.class != NULL)) {
|
|
if ((*matchfunc)(ob->model.class, OldCell->name)) {
|
|
HashDelete(ob->instance.name, &(ptr->instdict));
|
|
while (1) {
|
|
if (ob->type >= FIRSTPIN)
|
|
checknodes[ob->node] = (unsigned char)1;
|
|
FreeObjectAndHash(ob, ptr);
|
|
ob = nob;
|
|
if (ob == NULL) break;
|
|
nob = ob->next;
|
|
if (ob->type != PROPERTY && ob->type <= FIRSTPIN) break;
|
|
}
|
|
if (lob == NULL)
|
|
ptr->cell = ob;
|
|
else
|
|
lob->next = ob;
|
|
}
|
|
else {
|
|
lob = ob;
|
|
ob = nob;
|
|
}
|
|
}
|
|
else {
|
|
lob = ob;
|
|
ob = nob;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i <= ptr->nodename_cache_maxnodenum; i++) {
|
|
if (checknodes[i] != 0) {
|
|
portnode = NULL;
|
|
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
|
if ((ob->type != PORT) && (portnode == NULL))
|
|
break;
|
|
else if ((ob->type == PORT) && (ob->node == i))
|
|
portnode = ob;
|
|
else if ((ob->type >= FIRSTPIN) && (ob->node == i))
|
|
break;
|
|
}
|
|
if ((ob == NULL) && (portnode != NULL)) {
|
|
/* Port became disconnected when child was deleted */
|
|
portnode->node = -1;
|
|
}
|
|
}
|
|
}
|
|
FREE(checknodes);
|
|
}
|
|
|
|
/* Remove all instances of class "class" from the database */
|
|
|
|
void ClassDelete(char *class, int file)
|
|
{
|
|
if (file == -1)
|
|
OldCell = LookupCell(class);
|
|
else
|
|
OldCell = LookupCellFile(class, file);
|
|
|
|
if (OldCell == NULL) return;
|
|
RecurseCellFileHashTable(deleteclass, file);
|
|
}
|
|
|
|
/* Find all instances of the cell named "name" in the database, and */
|
|
/* change their model and instance information to "newname". */
|
|
|
|
char *NewName;
|
|
|
|
int renameinstances(struct hashlist *p, int file)
|
|
{
|
|
struct nlist *ptr;
|
|
struct objlist *ob, *ob2, *obp;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
|
|
if ((file != -1) && (ptr->file != file)) return 0;
|
|
|
|
for (ob = ptr->cell; ob != NULL; ob = ob->next) {
|
|
if ((ob->type >= FIRSTPIN) && (ob->model.class != NULL)) {
|
|
if ((*matchfunc)(ob->model.class, OldCell->name)) {
|
|
FreeString(ob->model.class);
|
|
ob->model.class = strsave(NewName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InstanceRename(char *from, char *to, int file)
|
|
{
|
|
if (file == -1)
|
|
OldCell = LookupCell(from);
|
|
else
|
|
OldCell = LookupCellFile(from, file);
|
|
|
|
if (OldCell == NULL) return;
|
|
NewName = to;
|
|
|
|
RecurseCellFileHashTable(renameinstances, file);
|
|
}
|
|
|
|
/* Delete contents of a hashed property structure */
|
|
|
|
int freeprop(struct hashlist *p)
|
|
{
|
|
struct property *prop;
|
|
|
|
prop = (struct property *)(p->ptr);
|
|
if (prop->type == PROP_STRING)
|
|
if (prop->pdefault.string != NULL)
|
|
FREE(prop->pdefault.string);
|
|
else if (prop->type == PROP_EXPRESSION) {
|
|
struct tokstack *stackptr, *nptr;
|
|
stackptr = prop->pdefault.stack;
|
|
while (stackptr != NULL) {
|
|
nptr = stackptr->next;
|
|
if (stackptr->toktype == TOK_STRING)
|
|
FREE(stackptr->data.string);
|
|
FREE(stackptr);
|
|
stackptr = nptr;
|
|
}
|
|
}
|
|
FREE(prop->key);
|
|
FREE(prop);
|
|
return 1;
|
|
}
|
|
|
|
void CellDelete(char *name, int fnum)
|
|
{
|
|
/* delete all the contents of cell 'name', and remove 'name' from
|
|
the cell hash table. NOTE: this procedure does not care or check
|
|
if 'name' has been instanced anywhere. It is assumed that if this
|
|
is the case, the user will (quickly) define a new cell of that name.
|
|
*/
|
|
struct objlist *ob, *obnext;
|
|
struct nlist *tp;
|
|
|
|
tp = LookupCellFile(name, fnum);
|
|
if (tp == NULL) {
|
|
Printf ("No cell '%s' found.\n", name);
|
|
return;
|
|
}
|
|
|
|
HashIntDelete(name, fnum, &cell_dict);
|
|
/* now make sure that we free all the fields of the nlist struct */
|
|
if (tp->name != NULL) FREE(tp->name);
|
|
HashKill(&(tp->objdict));
|
|
HashKill(&(tp->instdict));
|
|
RecurseHashTable(&(tp->propdict), freeprop);
|
|
HashKill(&(tp->propdict));
|
|
FreeNodeNames(tp);
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
obnext = ob->next;
|
|
FreeObject (ob);
|
|
ob = obnext;
|
|
}
|
|
}
|
|
|
|
static int PrintCellHashTableElement(struct hashlist *p)
|
|
{
|
|
struct nlist *ptr;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
if ((TopFile >= 0) && (ptr->file != TopFile)) return 1;
|
|
|
|
if ((ptr->class != CLASS_SUBCKT) && (ptr->class != CLASS_MODULE)) {
|
|
/* only print primitive cells if Debug is enabled */
|
|
if (Debug == 1) Printf("Cell: %s (instanced %d times); Primitive\n",
|
|
ptr->name, ptr->number);
|
|
else if (Debug == 3) { /* list */
|
|
#ifdef TCL_NETGEN
|
|
Tcl_AppendElement(netgeninterp, ptr->name);
|
|
#else
|
|
Printf("%s ", ptr->name);
|
|
#endif
|
|
}
|
|
}
|
|
else if ((Debug == 2) || (Debug == 3)) { /* list only */
|
|
#ifdef TCL_NETGEN
|
|
Tcl_AppendElement(netgeninterp, ptr->name);
|
|
#else
|
|
Printf("%s ", ptr->name);
|
|
#endif
|
|
}
|
|
else
|
|
Printf("Cell: %s (instanced %d times)\n", ptr->name, ptr->number);
|
|
return(1);
|
|
}
|
|
|
|
/* Print the contents of the cell hash table. */
|
|
/* if full == 1, print primitive elements also */
|
|
/* if full == 2, just output the cell names. */
|
|
|
|
void PrintCellHashTable(int full, int filenum)
|
|
{
|
|
int total, bins;
|
|
int OldDebug;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintCellHashTable(full, Circuit1->file);
|
|
PrintCellHashTable(full, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
TopFile = filenum;
|
|
|
|
bins = RecurseHashTable(&cell_dict, CountHashTableBinsUsed);
|
|
total = RecurseHashTable(&cell_dict, CountHashTableEntries);
|
|
if (full < 2)
|
|
Printf("Hash table: %d of %d bins used; %d cells total (%.2f per bin)\n",
|
|
bins, CELLHASHSIZE, total, (bins == 0) ? 0 :
|
|
(float)((float)total / (float)bins));
|
|
|
|
OldDebug = Debug;
|
|
Debug = full;
|
|
RecurseHashTable(&cell_dict, PrintCellHashTableElement);
|
|
Debug = OldDebug;
|
|
#ifndef TCL_NETGEN
|
|
if (full >= 2) Printf("\n");
|
|
#endif
|
|
}
|
|
|
|
struct nlist *FirstCell(void)
|
|
{
|
|
return((struct nlist *)HashFirst(&cell_dict));
|
|
}
|
|
|
|
struct nlist *NextCell(void)
|
|
{
|
|
return((struct nlist *)HashNext(&cell_dict));
|
|
}
|
|
|
|
static int ClearDumpedElement(struct hashlist *np)
|
|
{
|
|
struct nlist *p;
|
|
|
|
p = (struct nlist *)(np->ptr);
|
|
p->dumped = 0;
|
|
return(1);
|
|
}
|
|
|
|
void ClearDumpedList(void)
|
|
{
|
|
RecurseHashTable(&cell_dict, ClearDumpedElement);
|
|
}
|
|
|
|
int RecurseCellHashTable(int (*foo)(struct hashlist *np))
|
|
{
|
|
return RecurseHashTable(&cell_dict, foo);
|
|
}
|
|
|
|
int RecurseCellFileHashTable(int (*foo)(struct hashlist *, int), int value)
|
|
{
|
|
return RecurseHashTableValue(&cell_dict, foo, value);
|
|
}
|
|
|
|
/* Yet another version, passing one parameter that is a pointer */
|
|
|
|
struct nlist *RecurseCellHashTable2(struct nlist *(*foo)(struct hashlist *,
|
|
void *), void *pointer)
|
|
{
|
|
return RecurseHashTablePointer(&cell_dict, foo, pointer);
|
|
}
|
|
|
|
/************************** WILD-CARD STUFF *******************************/
|
|
|
|
char *FixTemplate(char *t)
|
|
{
|
|
char buffer[MAX_STR_LEN];
|
|
char *rstr;
|
|
int i,j;
|
|
int InsideBrace;
|
|
|
|
/* if we do not have to translate wildcards, just return input */
|
|
if (!UnixWildcards) return(strsave(t));
|
|
|
|
buffer[0] = '^';
|
|
InsideBrace=0;
|
|
for (i=0,j=1; i < strlen(t); i++)
|
|
switch (t[i]) {
|
|
case '{' : buffer[j++]='(';
|
|
InsideBrace++;
|
|
break;
|
|
case '}' : buffer[j++]=')';
|
|
InsideBrace--;
|
|
break;
|
|
case ',' : if (InsideBrace) {
|
|
buffer[j++]='|';
|
|
#if defined(HAVE_REGCMP) || defined(HAVE_RE_COMP)
|
|
Fprintf(stderr,
|
|
"Regular expression package unable to handle ',' operator.\n");
|
|
Fprintf(stderr,"Template = %s\n", t);
|
|
#endif
|
|
}
|
|
else buffer[j++]=',';
|
|
break;
|
|
case '*' : buffer[j++]='.';
|
|
buffer[j++]='*'; break;
|
|
case '?' : buffer[j++]='.'; break;
|
|
/* escape various characters */
|
|
case '^' :
|
|
case '.' :
|
|
case '(' :
|
|
case ')' :
|
|
case '+' :
|
|
case '|' :
|
|
case '$' :
|
|
buffer[j++]='\\';
|
|
buffer[j++]=t[i]; break;
|
|
/* escaped characters */
|
|
case '\\' : buffer[j++]='\\';
|
|
buffer[j++] = t[++i]; break;
|
|
/* subranges */
|
|
case '[' : buffer[j++]='[';
|
|
if (t[i+1] == '~') {
|
|
buffer[j++] = '^';
|
|
i++;
|
|
}
|
|
break;
|
|
case ']' : buffer[j++]=']'; break;
|
|
default : buffer[j++] = t[i]; break;
|
|
}
|
|
buffer[j++] = '$'; /* end of line delimeter */
|
|
buffer[j] = '\0'; /* null-terminated string */
|
|
|
|
/* printf ("Translated template = '%s'\n",buffer); */
|
|
rstr = strsave(buffer);
|
|
return(rstr);
|
|
}
|
|
|
|
#ifndef TCL_NETGEN
|
|
|
|
/*
|
|
*-------------------------------------------------------------------
|
|
* returns a list of objects in CurrentCell whose names match the
|
|
* regular expression in the 'list_template'
|
|
*-------------------------------------------------------------------
|
|
*/
|
|
|
|
struct objlist *List(char *list_template)
|
|
{
|
|
Regexp RegularExpression;
|
|
struct objlist *head, *tail;
|
|
struct objlist *test, *tmp;
|
|
char *template2;
|
|
int itmp;
|
|
|
|
if (CurrentCell == NULL) {
|
|
Fprintf(stderr,"No current cell in List()\n");
|
|
return (NULL);
|
|
}
|
|
if (QuickSearch)
|
|
test = LastPlaced;
|
|
else
|
|
test = CurrentCell->cell;
|
|
|
|
head = NULL;
|
|
tail = NULL;
|
|
|
|
#ifdef OPTIMIZE_WILDCARDS
|
|
if (strpbrk(list_template,"*?[{") == NULL && UnixWildcards) {
|
|
|
|
/* just find element, forget about regular expressions */
|
|
test = LookupObject(list_template, CurrentCell);
|
|
if (test != NULL) {
|
|
/* insert it directly into list (list has only this element) */
|
|
head = GetObject();
|
|
memcpy(head, test, sizeof(struct objlist));
|
|
head->next = NULL;
|
|
AddToGarbageList(head);
|
|
return(head);
|
|
}
|
|
}
|
|
/* otherwise, need to deal with wildcards */
|
|
|
|
#endif /* OPTIMIZE_WILDCARDS */
|
|
|
|
template2 = FixTemplate(list_template);
|
|
DBUG_PRINT("regex",("Compiling regular expression: %s => %s",
|
|
list_template, template2));
|
|
RegularExpression = RegexpCompile(template2);
|
|
DBUG_PRINT("regex",(" Result = %ld",(long)RegularExpression));
|
|
FreeString(template2);
|
|
for ( ; test != NULL; test = test->next) {
|
|
itmp = RegexpMatch(RegularExpression,test->name);
|
|
DBUG_PRINT("regex",("Testing string %s, result = %d", test->name, itmp));
|
|
if (itmp) {
|
|
tmp = GetObject();
|
|
memcpy(tmp, test, sizeof(struct objlist));
|
|
tmp->next = NULL;
|
|
|
|
/* now insert it into list */
|
|
|
|
if (head == NULL) {
|
|
head = tmp;
|
|
tail = tmp;
|
|
}
|
|
else { /* append it to list 'head' */
|
|
tail->next = tmp;
|
|
tail = tmp;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef REGEXP_FREE_TEMPLATE
|
|
FREE(RegularExpression);
|
|
#endif
|
|
|
|
AddToGarbageList(head);
|
|
return(head);
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
/*
|
|
*-------------------------------------------------------------------
|
|
* Version of the above (List) with wildcards disabled.
|
|
*
|
|
* Returns (a copy of) the object in CurrentCell whose name
|
|
* matches the 'obj_name'. For Tcl, we use Tcl's built-in
|
|
* regexp function to do template matching.
|
|
*-------------------------------------------------------------------
|
|
*/
|
|
|
|
struct objlist *List(char *obj_name)
|
|
{
|
|
struct objlist *head, *test;
|
|
|
|
if (CurrentCell == NULL) {
|
|
Fprintf(stderr,"No current cell in List()\n");
|
|
return (NULL);
|
|
}
|
|
if (QuickSearch)
|
|
test = LastPlaced;
|
|
else
|
|
test = CurrentCell->cell;
|
|
|
|
head = NULL;
|
|
|
|
test = LookupObject(obj_name, CurrentCell);
|
|
if (test != NULL) {
|
|
/* insert it directly into list (list has only this element) */
|
|
head = GetObject();
|
|
memcpy(head, test, sizeof(struct objlist));
|
|
head->next = NULL;
|
|
}
|
|
AddToGarbageList(head);
|
|
return(head);
|
|
}
|
|
|
|
#endif /* TCL_NETGEN */
|
|
|
|
/****************** LIST MANIPULATION STUFF *****************************/
|
|
|
|
struct objlist *ListCat(struct objlist *ls1, struct objlist *ls2)
|
|
{
|
|
struct objlist *head, *tail, *tmp;
|
|
|
|
head = NULL;
|
|
tail = NULL;
|
|
if (ls1 == NULL) {
|
|
ls1 = ls2;
|
|
ls2 = NULL;
|
|
}
|
|
while (ls1 != NULL) {
|
|
tmp = GetObject();
|
|
#if 1
|
|
memcpy(tmp, ls1, sizeof(struct objlist));
|
|
tmp->next = NULL;
|
|
#else
|
|
tmp->name = ls1->name;
|
|
tmp->type = ls1->type;
|
|
tmp->model.class = ls1->model.class;
|
|
tmp->instance.name = ls1->instance.name;
|
|
tmp->node = ls1->node;
|
|
tmp->next = NULL;
|
|
#endif
|
|
/* now insert it into list */
|
|
if (head == NULL) {
|
|
head = tmp;
|
|
tail = head;
|
|
}
|
|
else { /* append it to list 'head' */
|
|
tail->next = tmp;
|
|
tail = tmp;
|
|
}
|
|
if (ls1->next == NULL) {
|
|
ls1 = ls2;
|
|
ls2 = NULL;
|
|
}
|
|
else ls1 = ls1->next;
|
|
}
|
|
AddToGarbageList(head);
|
|
return(head);
|
|
}
|
|
|
|
int ListLen(struct objlist *head)
|
|
{
|
|
int n;
|
|
|
|
n = 0;
|
|
while (head != NULL) {
|
|
head = head->next;
|
|
n++;
|
|
}
|
|
return (n);
|
|
}
|
|
|
|
int ListLength(char *list_template)
|
|
{
|
|
struct objlist *head;
|
|
int n;
|
|
|
|
head = List(list_template);
|
|
n = ListLen(head);
|
|
return (n);
|
|
}
|
|
|
|
|
|
|
|
struct objlist *CopyObjList(struct objlist *oldlist, unsigned char doforall)
|
|
/* copies list pointed to by oldlist, creating
|
|
a list whose head pointer is returned */
|
|
{
|
|
struct objlist *head, *tail, *tmp, *newob;
|
|
|
|
tmp = oldlist;
|
|
head = NULL;
|
|
tail = NULL;
|
|
while (tmp != NULL) {
|
|
if ((newob = GetObject()) == NULL) {
|
|
Fprintf(stderr,"CopyObjList: core allocation failure\n");
|
|
#ifdef HAVE_MALLINFO
|
|
PrintMemoryStats();
|
|
#endif
|
|
return(NULL);
|
|
}
|
|
newob->name = (tmp->name) ? strsave(tmp->name) : NULL;
|
|
newob->type = tmp->type;
|
|
if (newob->type == PROPERTY)
|
|
CopyProperties(newob, tmp);
|
|
else {
|
|
if (tmp->model.class == NULL || IsPort(tmp))
|
|
newob->model.class = NULL;
|
|
else
|
|
newob->model.class = strsave(tmp->model.class);
|
|
newob->flags = tmp->flags;
|
|
newob->instance.name = (tmp->instance.name) ?
|
|
strsave(tmp->instance.name) : NULL;
|
|
}
|
|
newob->node = tmp->node;
|
|
newob->next = NULL;
|
|
if (head == NULL)
|
|
head = newob;
|
|
else
|
|
tail->next = newob;
|
|
tail = newob;
|
|
tmp = tmp->next;
|
|
|
|
// If "doforall" is 0, then only copy one object; otherwise,
|
|
// copy to the end of the list.
|
|
if (!doforall) {
|
|
if ((tmp == NULL) || ((tmp->type <= FIRSTPIN) && (tmp->type != PROPERTY)))
|
|
break;
|
|
}
|
|
}
|
|
return (head);
|
|
}
|
|
|
|
|
|
/********************* LIST SEARCHING STUFF ******************************/
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Search for exact match of object 'name' in cell 'WhichCell' */
|
|
/* This search excludes object properties. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
struct objlist *LookupObject(char *name, struct nlist *WhichCell)
|
|
{
|
|
return((struct objlist *)HashLookup(name, &(WhichCell->objdict)));
|
|
}
|
|
|
|
struct objlist *LookupInstance(char *name, struct nlist *WhichCell)
|
|
/* searches for exact match of instance 'name' in cell 'WhichCell' */
|
|
{
|
|
return((struct objlist *)HashLookup(name, &(WhichCell->instdict)));
|
|
}
|
|
|
|
|
|
void UpdateNodeNumbers(struct objlist *lst, int from, int to)
|
|
{
|
|
while (lst != NULL) {
|
|
if (lst->node == from)
|
|
lst->node = to;
|
|
lst = lst->next;
|
|
}
|
|
}
|
|
|
|
void AddToCurrentCell(struct objlist *ob)
|
|
{
|
|
AddToCurrentCellNoHash(ob);
|
|
|
|
/* add to object hash table for this cell */
|
|
if (CurrentCell != NULL) {
|
|
HashPtrInstall(ob->name, ob, &(CurrentCell->objdict));
|
|
}
|
|
}
|
|
|
|
void AddToCurrentCellNoHash(struct objlist *ob)
|
|
{
|
|
if (CurrentCell == NULL) {
|
|
Fprintf(stderr,"No current cell for ");
|
|
switch (ob->type) {
|
|
case PORT: Fprintf(stderr,"Port(%s)\n",ob->name); break;
|
|
case PROPERTY: Fprintf(stderr,"Property\n"); break;
|
|
case GLOBAL: Fprintf(stderr,"Global(%s)\n",ob->name); break;
|
|
case UNIQUEGLOBAL: Fprintf(stderr,"UniqueGlobal(%s)\n",ob->name); break;
|
|
default: Fprintf(stderr,"pin: %s\n",ob->name); break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (CurrentCell->cell == NULL) CurrentCell->cell = ob;
|
|
else CurrentTail->next = ob;
|
|
CurrentTail = ob;
|
|
ob->next = NULL;
|
|
}
|
|
|
|
void AddInstanceToCurrentCell(struct objlist *ob)
|
|
{
|
|
/* add to instance hash table for this cell */
|
|
HashPtrInstall(ob->instance.name, ob, &(CurrentCell->instdict));
|
|
}
|
|
|
|
void FreeObject(struct objlist *ob)
|
|
{
|
|
/* This just frees the object record. Beware of pointer left */
|
|
/* in the objlist hash table. Hash table records should be */
|
|
/* removed first. */
|
|
|
|
if (ob->name != NULL) FreeString(ob->name);
|
|
|
|
if (ob->type == PROPERTY) {
|
|
if (ob->instance.props != NULL) {
|
|
struct valuelist *kv;
|
|
int i;
|
|
for (i = 0; ; i++) {
|
|
kv = &(ob->instance.props[i]);
|
|
if (kv->type == PROP_ENDLIST) break;
|
|
FreeString(kv->key);
|
|
if (kv->type == PROP_STRING && kv->value.string != NULL)
|
|
FreeString(kv->value.string);
|
|
else if (kv->type == PROP_EXPRESSION) {
|
|
struct tokstack *stackptr, *nptr;
|
|
stackptr = kv->value.stack;
|
|
while (stackptr != NULL) {
|
|
nptr = stackptr->next;
|
|
if (stackptr->toktype == TOK_STRING)
|
|
FREE(stackptr->data.string);
|
|
FREE(stackptr);
|
|
stackptr = nptr;
|
|
}
|
|
}
|
|
}
|
|
FREE(ob->instance.props);
|
|
}
|
|
}
|
|
else {
|
|
/* All other records */
|
|
if (ob->instance.name != NULL) FreeString(ob->instance.name);
|
|
}
|
|
if (ob->model.class != NULL) FreeString(ob->model.class);
|
|
FREE(ob);
|
|
}
|
|
|
|
|
|
void FreeObjectAndHash(struct objlist *ob, struct nlist *ptr)
|
|
{
|
|
HashDelete(ob->name, &(ptr->objdict));
|
|
FreeObject(ob);
|
|
}
|
|
|
|
/*************** GENERAL UTILITIES ****************************/
|
|
|
|
int NumberOfPorts(char *cellname, int file)
|
|
{
|
|
struct nlist *tp;
|
|
struct objlist *ob;
|
|
int ports;
|
|
|
|
if (file == -1)
|
|
tp = LookupCell(cellname);
|
|
else
|
|
tp = LookupCellFile(cellname, file);
|
|
|
|
if (tp == NULL) return(0);
|
|
ports = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if (IsPort(ob)) ports++;
|
|
return(ports);
|
|
}
|
|
|
|
void FreePorts(char *cellname)
|
|
{
|
|
struct nlist *tp;
|
|
struct objlist *ob, *obnext, *oblast;
|
|
|
|
tp = LookupCell(cellname);
|
|
if (tp == NULL) return;
|
|
ob = tp->cell;
|
|
if (ob == NULL) return;
|
|
tp->cell = NULL;
|
|
|
|
while (ob && IsPort(ob)) {
|
|
obnext = ob->next;
|
|
if (ob->name != NULL) FreeString(ob->name);
|
|
if (ob->instance.name != NULL) FreeString(ob->instance.name);
|
|
FREE(ob);
|
|
ob = obnext;
|
|
}
|
|
tp->cell = ob;
|
|
|
|
oblast = ob;
|
|
while (ob) {
|
|
obnext = ob->next;
|
|
if (IsPort(ob)) {
|
|
if (ob->name != NULL) FreeString(ob->name);
|
|
if (ob->instance.name != NULL) FreeString(ob->instance.name);
|
|
FREE(ob);
|
|
oblast->next = obnext;
|
|
}
|
|
else
|
|
oblast = ob;
|
|
ob = obnext;
|
|
}
|
|
}
|
|
|
|
|
|
struct objlist *InstanceNumber(struct nlist *tp, int inst)
|
|
{
|
|
struct objlist *ob;
|
|
int count;
|
|
|
|
count = 1;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
if (count == inst) return(ob);
|
|
count ++;
|
|
}
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
|
|
/**********************************************************************/
|
|
/* NodeName cacheing stuff */
|
|
/**********************************************************************/
|
|
|
|
|
|
|
|
static char *OldNodeName(struct nlist *tp, int node)
|
|
{
|
|
/* return a legitimate node name for node number 'node' in cell *tp */
|
|
/* Order of preference:
|
|
1) Named ports of cells
|
|
2) Named internal nodes of cells
|
|
3) Unique global ports (these names are descriptive, but long)
|
|
4) Global ports
|
|
5) Pins on instances
|
|
*/
|
|
|
|
struct objlist *ob;
|
|
struct objlist *firstport;
|
|
struct objlist *firstnode;
|
|
struct objlist *firstuniqueglobal;
|
|
struct objlist *firstglobal;
|
|
struct objlist *firstpin;
|
|
static char StrBuffer[MAX_STR_LEN];
|
|
|
|
#if 0
|
|
/* make second pass, looking for ports */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if ((node == ob->node) && IsPort(ob)) {
|
|
strcpy(StrBuffer, ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
/* make third pass, looking for named internal nodes */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if ((node == ob->node) && (ob->type == NODE)) {
|
|
strcpy(StrBuffer, ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
/* make zeroth pass, looking for unique global ports */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if ((node == ob->node) && (ob->type == UNIQUEGLOBAL)) {
|
|
strcpy(StrBuffer, ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
/* make first pass, looking for global ports */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if ((node == ob->node) && (ob->type == GLOBAL)) {
|
|
strcpy(StrBuffer, ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
|
|
/* make fourth pass, looking for first pin */
|
|
ob = tp->cell;
|
|
while (ob != NULL) {
|
|
if (node == ob->node) {
|
|
strcpy(StrBuffer, ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
#endif
|
|
|
|
firstport = NULL;
|
|
firstnode = NULL;
|
|
firstuniqueglobal = NULL;
|
|
firstglobal = NULL;
|
|
firstpin = NULL;
|
|
if (node < 1) {
|
|
sprintf(StrBuffer, "Disconnected(%d)",node);
|
|
return(StrBuffer);
|
|
}
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (node == ob->node) {
|
|
if (ob->type >= FIRSTPIN) firstpin = ob;
|
|
else if (IsPort(ob)) {
|
|
firstport = ob;
|
|
strcpy(StrBuffer,ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
else if (ob->type == NODE) firstnode = ob;
|
|
else if (ob->type == UNIQUEGLOBAL) firstuniqueglobal = ob;
|
|
else if (ob->type == GLOBAL) firstglobal = ob;
|
|
else
|
|
Fprintf(stderr,"??? ob->type = %d on %s\n",ob->type,ob->name);
|
|
}
|
|
}
|
|
|
|
if (firstport != NULL) ob = firstport;
|
|
else if (firstnode != NULL) ob = firstnode;
|
|
else if (firstuniqueglobal != NULL) ob = firstuniqueglobal;
|
|
else if (firstglobal != NULL) ob = firstglobal;
|
|
else if (firstpin != NULL) ob = firstpin;
|
|
else {
|
|
/* if we got to here, we have a serious problem */
|
|
Fprintf (stderr, "NodeName(%d) called with bogus parameter\n", node);
|
|
sprintf(StrBuffer, "bogus(%d)",node);
|
|
return(StrBuffer);
|
|
}
|
|
strcpy(StrBuffer,ob->name);
|
|
return(StrBuffer);
|
|
}
|
|
|
|
|
|
char *NodeName(struct nlist *tp, int node)
|
|
{
|
|
if (node == -1) return("Disconnected");
|
|
if (tp->nodename_cache != NULL) {
|
|
if (node > tp->nodename_cache_maxnodenum ||
|
|
tp->nodename_cache[node] == NULL)
|
|
return ("IllegalNode");
|
|
else
|
|
return (tp->nodename_cache[node]->name);
|
|
}
|
|
return (OldNodeName(tp, node));
|
|
}
|
|
|
|
char *NodeAlias(struct nlist *tp, struct objlist *ob)
|
|
/* return the best name for 'ob'; safer than calling NodeName,
|
|
as it correctly handles disconnected nodes */
|
|
{
|
|
if (ob == NULL) return("NULL");
|
|
if (ob->node == -1) {
|
|
/* Fprintf(stderr,"Disconnected node in NodeAlias: %s\n",ob->name); */
|
|
return(ob->name);
|
|
}
|
|
if ((ob->node >= 0) && (tp->nodename_cache != NULL) &&
|
|
(ob->node <= tp->nodename_cache_maxnodenum))
|
|
return (tp->nodename_cache[ob->node]->name);
|
|
return (OldNodeName(tp, ob->node));
|
|
}
|
|
|
|
void FreeNodeNames(struct nlist *tp)
|
|
{
|
|
if (tp == NULL) return;
|
|
if (tp->nodename_cache != NULL)
|
|
FREE(tp->nodename_cache);
|
|
tp->nodename_cache = NULL;
|
|
tp->nodename_cache_maxnodenum = 0;
|
|
}
|
|
|
|
void CacheNodeNames(struct nlist *tp)
|
|
{
|
|
int nodes;
|
|
struct objlist *ob;
|
|
|
|
if (tp == NULL) return;
|
|
if (tp->nodename_cache != NULL) FreeNodeNames(tp);
|
|
nodes = 0;
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if (ob->node > nodes) nodes = ob->node;
|
|
|
|
if (nodes == 0) return;
|
|
|
|
tp->nodename_cache =
|
|
(struct objlist **)CALLOC(nodes+1, sizeof(*(tp->nodename_cache)));
|
|
if (tp->nodename_cache == NULL) return;
|
|
tp->nodename_cache_maxnodenum = nodes;
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
int present, new_type;
|
|
|
|
if (ob->node < 0) continue; /* do not cache it */
|
|
if ((tp->nodename_cache)[ob->node] == NULL)
|
|
(tp->nodename_cache)[ob->node] = ob;
|
|
|
|
present = ((tp->nodename_cache)[ob->node])->type;
|
|
new_type = ob->type;
|
|
|
|
if (new_type == present) continue;
|
|
if (new_type >= FIRSTPIN && present >= FIRSTPIN) continue;
|
|
|
|
switch (new_type) {
|
|
case PORT:
|
|
(tp->nodename_cache)[ob->node] = ob;
|
|
break;
|
|
case NODE:
|
|
if (present != PORT)
|
|
(tp->nodename_cache)[ob->node] = ob;
|
|
break;
|
|
case UNIQUEGLOBAL:
|
|
if (present != PORT && present != NODE)
|
|
(tp->nodename_cache)[ob->node] = ob;
|
|
break;
|
|
case GLOBAL:
|
|
if (present != PORT && present != NODE && present != UNIQUEGLOBAL)
|
|
(tp->nodename_cache)[ob->node] = ob;
|
|
break;
|
|
default: /* we have a pin or property, which we can ignore */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|