1193 lines
32 KiB
C
1193 lines
32 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. */
|
|
|
|
/* query.c -- simple command-line interpreter */
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <setjmp.h>
|
|
#include <signal.h>
|
|
|
|
#ifdef HAVE_MALLINFO
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#ifdef IBMPC
|
|
#include <alloc.h>
|
|
#include <process.h> /* for system() */
|
|
#include <bios.h> /* for bioskey() */
|
|
#endif
|
|
|
|
#include "netgen.h"
|
|
#include "timing.h"
|
|
#include "hash.h"
|
|
#include "objlist.h"
|
|
#include "query.h"
|
|
#include "netfile.h"
|
|
#include "print.h"
|
|
#include "dbug.h"
|
|
#include "netcmp.h"
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* I/O support for Query() routine */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
static int SuppressPrompts = 0;
|
|
static char InputLine[200];
|
|
|
|
void typeahead(char *str)
|
|
{
|
|
if (strlen(str) + strlen(InputLine) + 3 < sizeof(InputLine)) {
|
|
strcat(InputLine," ");
|
|
strcat(InputLine,str);
|
|
}
|
|
else fprintf(stderr, "InputLine too long: ignored command '%s'\n",str);
|
|
}
|
|
|
|
/* change the following to redirect the input stream */
|
|
FILE *promptstring_infile = NULL;
|
|
|
|
void promptstring(char *prompt, char *buf)
|
|
/* tries to get a token out of 'line' variable,
|
|
but reads from 'promptstring_infile' if nec. */
|
|
/* copy it to buffer, when found */
|
|
/* If interactive, puts out 'prompt' */
|
|
{
|
|
char *nexttok;
|
|
char tmpstr[200];
|
|
int echo;
|
|
|
|
if (promptstring_infile == NULL)
|
|
promptstring_infile = stdin;
|
|
|
|
if (!SuppressPrompts) {
|
|
Printf("%s",prompt);
|
|
Fflush(stdout);
|
|
}
|
|
echo = 1; /* assume we got it from typeahead */
|
|
nexttok = InputLine;
|
|
while (isspace(*nexttok) && *nexttok != '\0') nexttok++;
|
|
if (*nexttok == '\0') {
|
|
fgets(InputLine, sizeof(InputLine), promptstring_infile);
|
|
if (promptstring_infile == stdin) echo = 0;
|
|
nexttok = InputLine;
|
|
while (isspace(*nexttok) && *nexttok != '\0') nexttok++;
|
|
if (*nexttok == '\0') {
|
|
*buf = '\0';
|
|
return;
|
|
}
|
|
}
|
|
/* nexttok points to beginning of valid token */
|
|
strcpy(tmpstr,nexttok);
|
|
nexttok = tmpstr;
|
|
while (*nexttok != '\0' && !isspace(*nexttok)) nexttok++;
|
|
strcpy(InputLine, nexttok);
|
|
*nexttok = '\0';
|
|
strcpy(buf, tmpstr);
|
|
if (echo && !SuppressPrompts) Printf("%s\n",buf);
|
|
}
|
|
|
|
|
|
void InitializeCommandLine(int argc, char **argv)
|
|
{
|
|
/* neither of the two Inits below are strictly necessary, */
|
|
/* as static objects are initialized to 0 (NULL) automatically */
|
|
|
|
InitCellHashTable();
|
|
InitGarbageCollection();
|
|
|
|
RemoveCompareQueue();
|
|
|
|
#ifdef TCL_NETGEN
|
|
return;
|
|
|
|
#else
|
|
if (argv == NULL) return; /* Don't run Query() */
|
|
|
|
/* initialize command-line parser, including dbug code */
|
|
DBUG_PROCESS(argv[0]);
|
|
if (argc > 1) {
|
|
int start;
|
|
int usekbd, forceinteractive;
|
|
usekbd = 1; /* assume we are interactive */
|
|
forceinteractive = 0; /* try to figure it out from cmd line */
|
|
|
|
/* quiet prompting if program name is not netgen */
|
|
SuppressPrompts = (strstr(argv[0], "netgen") == NULL);
|
|
|
|
for (start = 1; start < argc; start++) {
|
|
if (argv[start][0] == '-') {
|
|
switch (argv[start][1]) {
|
|
case '#': DBUG_PUSH(&(argv[start][2]));
|
|
break;
|
|
case '\0': forceinteractive = 1;
|
|
break;
|
|
default: Fprintf(stderr,"Unrecognized switch: %s\n",argv[start]);
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
typeahead(argv[start]);
|
|
usekbd = 0;
|
|
}
|
|
}
|
|
if (!usekbd && !forceinteractive) typeahead("Q"); /* exit when done */
|
|
}
|
|
|
|
/* permit command-line typeahead even for X-windows */
|
|
if (getenv("DISPLAY")) {
|
|
int oldSuppressPrompts = SuppressPrompts;
|
|
SuppressPrompts = 1;
|
|
typeahead("q"); /* get out of one level of query */
|
|
Query();
|
|
SuppressPrompts = oldSuppressPrompts;
|
|
}
|
|
|
|
#endif /* TCL_NETGEN */
|
|
}
|
|
|
|
void Initialize(void)
|
|
{
|
|
#ifdef HAVE_MALLINFO
|
|
char *wasted;
|
|
|
|
wasted = (char *)MALLOC(2); /* need to initialize memory allocator */
|
|
#endif
|
|
|
|
InitializeCommandLine(0, NULL);
|
|
}
|
|
|
|
|
|
/* Print the type of object (mostly diagnostic) */
|
|
|
|
void PrintObjectType(int type)
|
|
{
|
|
switch(type) {
|
|
case UNIQUEGLOBAL:
|
|
Printf("Unique Global");
|
|
break;
|
|
case GLOBAL:
|
|
Printf("Global");
|
|
break;
|
|
case PORT:
|
|
Printf("Port");
|
|
break;
|
|
case PROPERTY:
|
|
Printf("Properties");
|
|
break;
|
|
case NODE:
|
|
Printf("Net");
|
|
break;
|
|
default:
|
|
if (type < 0)
|
|
Printf("Error!");
|
|
else
|
|
Printf("Pin %d", type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************/
|
|
/* */
|
|
/* Some convenient routines for printing internal data structures */
|
|
/* */
|
|
/*************************************************************************/
|
|
|
|
#ifndef TCL_NETGEN
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* generate and print a list of elements that match a regexp */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void PrintElement(char *cell, char *list_template)
|
|
{
|
|
|
|
struct objlist *list;
|
|
|
|
if (strlen(cell)) CurrentCell = LookupCell(cell);
|
|
list = List(list_template);
|
|
Printf("Devices matching template: %s\n",list_template);
|
|
while (list != NULL) {
|
|
Printf (" %s\n",list->name);
|
|
list = list->next;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* PrintElement() for use with Tcl---return all elements, */
|
|
/* let Tcl sort them out. */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void PrintAllElements(char *cell, int filenum)
|
|
{
|
|
struct nlist *np;
|
|
struct objlist *ob;
|
|
char *sfx;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintAllElements(cell, Circuit1->file);
|
|
PrintAllElements(cell, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
if (((cell == NULL) || (*cell == '\0')) && (CurrentCell != NULL))
|
|
np = CurrentCell;
|
|
else
|
|
np = LookupCellFile(cell, filenum);
|
|
|
|
if (np == NULL) {
|
|
Printf("Circuit '%s' not found.\n",cell);
|
|
return;
|
|
}
|
|
|
|
ob = np->cell;
|
|
for (ob = np->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
if ((sfx = strrchr(ob->name, '/')) != NULL) *sfx = '\0';
|
|
Printf("%s\n", ob->name);
|
|
if (sfx != NULL) *sfx = '/';
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/*--------------------------------------------------------------*/
|
|
/* Print connectivity between objects belonging to a specific */
|
|
/* node. 'filter' may be used to restrict the returned list to */
|
|
/* a specific type of object (node, element, port, pin, etc.). */
|
|
/*--------------------------------------------------------------*/
|
|
|
|
void Fanout(char *cell, char *node, int filter)
|
|
{
|
|
struct nlist *np;
|
|
struct objlist *ob;
|
|
int nodenum;
|
|
|
|
if (*cell == '\0') np = CurrentCell;
|
|
else np = LookupCell(cell);
|
|
|
|
if (np == NULL) {
|
|
Printf("Cell '%s' not found.\n",cell);
|
|
return;
|
|
}
|
|
|
|
nodenum = -999;
|
|
for (ob = np->cell; ob != NULL; ob = ob->next) {
|
|
if ((*matchfunc)(node, ob->name)) {
|
|
nodenum = ob->node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* now print out all elements that connect to that node */
|
|
|
|
if (nodenum == -999)
|
|
Printf("Net '%s' not found in circuit '%s'.\n", node, cell);
|
|
else if (nodenum < 0)
|
|
Printf("Net '%s' is disconnected.\n", node);
|
|
else {
|
|
if (ob != NULL)
|
|
PrintObjectType(ob->type);
|
|
else
|
|
Printf("Object");
|
|
Printf (" '%s' in circuit '%s' connects to:\n", node, cell);
|
|
ob = np->cell;
|
|
while (ob != NULL) {
|
|
char *obname = ob->name;
|
|
if (*obname == '/') obname++;
|
|
if (ob->node == nodenum)
|
|
if (filter == ALLOBJECTS) {
|
|
Printf(" %s (", obname);
|
|
PrintObjectType(ob->type);
|
|
Printf(")\n");
|
|
}
|
|
else if ((filter == ALLELEMENTS) && (ob->type >= FIRSTPIN)) {
|
|
Printf(" %s\n", obname);
|
|
}
|
|
else if (ob->type == filter) {
|
|
Printf(" %s\n", obname);
|
|
}
|
|
ob = ob->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef TCL_NETGEN
|
|
|
|
/* Print the nodes connected to each pin of the specified element */
|
|
|
|
void ElementNodes(char *cell, char *element, int fnum)
|
|
{
|
|
struct nlist *np;
|
|
struct objlist *ob, *nob, *nob2;
|
|
int ckto;
|
|
char *elementname, *obname;
|
|
|
|
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
ElementNodes(cell, element, Circuit1->file);
|
|
ElementNodes(cell, element, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
if (((cell == NULL) || (*cell == '\0')) && (CurrentCell != NULL))
|
|
np = CurrentCell;
|
|
else
|
|
np = LookupCellFile(cell, fnum);
|
|
|
|
if (np == NULL) {
|
|
Printf("Circuit '%s' not found.\n",cell);
|
|
return;
|
|
}
|
|
|
|
elementname = element;
|
|
if (*elementname == '/') elementname++;
|
|
|
|
ckto = strlen(elementname);
|
|
for (ob = np->cell; ob != NULL; ob = ob->next) {
|
|
obname = ob->name;
|
|
if (*obname == '/') obname++;
|
|
if (!strncmp(elementname, obname, ckto))
|
|
if (*(obname + ckto) == '/' || *(obname + ckto) == '\0')
|
|
break;
|
|
}
|
|
if (ob == NULL) {
|
|
Printf("Device '%s' not found in circuit '%s'.\n", elementname, cell);
|
|
return;
|
|
}
|
|
|
|
Printf("Device '%s' Pins:\n", elementname);
|
|
for (; ob != NULL; ob = ob->next) {
|
|
obname = ob->name;
|
|
if (*obname == '/') obname++;
|
|
if (!strncmp(elementname, obname, ckto)) {
|
|
if (*(obname + ckto) != '/' && *(obname + ckto) != '\0')
|
|
continue;
|
|
|
|
Printf(" ");
|
|
PrintObjectType(ob->type);
|
|
Printf(" (%s)", obname + ckto + 1);
|
|
for (nob = np->cell; nob != NULL; nob = nob->next) {
|
|
if (nob->node == ob->node) {
|
|
if (nob->type == NODE) {
|
|
Printf(" = %s", nob->name);
|
|
break;
|
|
}
|
|
else if (nob->type == PORT) {
|
|
Printf(" = %s (port of %s)", nob->name, cell);
|
|
break;
|
|
}
|
|
else if (nob->type == GLOBAL) {
|
|
Printf(" = %s (global)", nob->name);
|
|
break;
|
|
}
|
|
else if (nob->type == UNIQUEGLOBAL) {
|
|
Printf(" = %s (unique global)", nob->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Printf("\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif /* TCL_NETGEN */
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Find all nodes by name or wildcard pattern match, in the scope of */
|
|
/* the cell "cellname", and change them to the type given by "type". */
|
|
/* Changes are only allowed among nodes of type NODE, GLOBAL or */
|
|
/* UNIQUEGLOBAL. */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
int ChangeScopeCurrent(char *pattern, int typefrom, int typeto)
|
|
{
|
|
/* Note that List() is supposed to operate on CurrentCell */
|
|
/* but we want to be able to change scope on any cell. */
|
|
|
|
struct objlist *plist, *psrch;
|
|
struct nlist *tp;
|
|
int numchanged = 0;
|
|
|
|
plist = List(pattern);
|
|
while (plist != NULL) {
|
|
if (plist->type == typefrom) {
|
|
for (psrch = CurrentCell->cell; psrch != NULL; psrch = psrch->next) {
|
|
if (psrch->type == typefrom && (*matchfunc)(psrch->name, plist->name)) {
|
|
psrch->type = typeto;
|
|
Printf("Cell %s: Net %s changed to %s\n",
|
|
CurrentCell->name, psrch->name, psrch->type == NODE ?
|
|
"local" : psrch->type == GLOBAL ? "global" :
|
|
psrch->type == UNIQUEGLOBAL ? "unique global" : "unknown");
|
|
numchanged++;
|
|
}
|
|
}
|
|
}
|
|
plist = plist->next;
|
|
}
|
|
|
|
/* Recursively search descendants, if they exist */
|
|
|
|
if (CurrentCell != NULL) {
|
|
for (psrch = CurrentCell->cell; psrch != NULL; psrch = psrch->next) {
|
|
if (psrch->type == FIRSTPIN) {
|
|
numchanged += ChangeScope(CurrentCell->file, psrch->model.class,
|
|
pattern, typefrom, typeto);
|
|
}
|
|
}
|
|
}
|
|
|
|
return numchanged;
|
|
}
|
|
|
|
/* Structure to pass information to doglobalscope() */
|
|
|
|
typedef struct _gsd {
|
|
int fnum;
|
|
char *pattern;
|
|
int typefrom;
|
|
int typeto;
|
|
int *numchanged;
|
|
} gsdata;
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Function called by hash search on cells, to call ChangeScopeCurrent */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
struct nlist *doglobalscope(struct hashlist *p, void *clientdata)
|
|
{
|
|
struct nlist *ptr;
|
|
struct objlist *ob, *lob, *nob;
|
|
int file, numchanged;
|
|
gsdata *gsd = (gsdata *)clientdata;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
file = gsd->fnum;
|
|
|
|
if ((file != -1) && (ptr->file != file)) return NULL;
|
|
|
|
CurrentCell = ptr;
|
|
numchanged = ChangeScopeCurrent(gsd->pattern, gsd->typefrom, gsd->typeto);
|
|
*(gsd->numchanged) += numchanged;
|
|
|
|
return ptr;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
/* Wrapper for ChangeScopeCurrent(). Takes a cellname as an */
|
|
/* argument, instead of assuming the existance of a valid CurrentCell */
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
int ChangeScope(int fnum, char *cellname, char *pattern, int typefrom, int typeto)
|
|
{
|
|
struct nlist *SaveCell, *tp;
|
|
int numchanged = 0;
|
|
|
|
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
numchanged += ChangeScope(Circuit1->file, cellname,
|
|
pattern, typefrom, typeto);
|
|
numchanged += ChangeScope(Circuit2->file, cellname,
|
|
pattern, typefrom, typeto);
|
|
return numchanged;
|
|
}
|
|
|
|
SaveCell = CurrentCell;
|
|
|
|
if (cellname == NULL) {
|
|
/* No cellname given, so search all cells in file fnum */
|
|
gsdata locdata;
|
|
|
|
locdata.fnum = fnum;
|
|
locdata.pattern = pattern;
|
|
locdata.typefrom = typefrom;
|
|
locdata.typeto = typeto;
|
|
locdata.numchanged = &numchanged;
|
|
|
|
RecurseCellHashTable2(doglobalscope, (void *)(&locdata));
|
|
}
|
|
else {
|
|
|
|
CurrentCell = LookupCellFile(cellname, fnum);
|
|
if (CurrentCell != NULL) {
|
|
numchanged = ChangeScopeCurrent(pattern, typefrom, typeto);
|
|
}
|
|
else {
|
|
Printf("No circuit '%s' found.\n", cellname);
|
|
}
|
|
}
|
|
CurrentCell = SaveCell;
|
|
return numchanged;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
/* print all nodes in cell 'name', together with their connectivities */
|
|
/*--------------------------------------------------------------------*/
|
|
|
|
typedef struct _noderecord {
|
|
char *name; /* node name */
|
|
int uniqueglobal, global, port, node, pin; /* counts */
|
|
} noderecord;
|
|
|
|
void PrintNodes(char *name, int filenum)
|
|
{
|
|
int nodenum, nodemax;
|
|
struct nlist *tp;
|
|
struct objlist *ob;
|
|
int maxnamelen;
|
|
noderecord *nodelist;
|
|
int uniqueglobals, globals, ports, nodes, pins;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintNodes(name, Circuit1->file);
|
|
PrintNodes(name, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
tp = LookupCellFile(name, filenum);
|
|
|
|
if (tp == NULL) {
|
|
Printf ("No circuit '%s' found.\n",name);
|
|
return;
|
|
}
|
|
Printf("Circuit: '%s'\n",tp->name);
|
|
|
|
nodemax = 0;
|
|
maxnamelen = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
int len = strlen(NodeAlias(tp, ob));
|
|
if (len > maxnamelen) maxnamelen = len;
|
|
if (ob->node > nodemax) nodemax = ob->node;
|
|
}
|
|
nodelist = (noderecord *) CALLOC((nodemax + 1), sizeof(noderecord));
|
|
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
nodenum = ob->node;
|
|
if (nodenum < 0) continue;
|
|
/* repeat bits of objlist.c here for speed */
|
|
if (tp->nodename_cache != NULL) {
|
|
nodelist[nodenum].name = tp->nodename_cache[nodenum]->name;
|
|
}
|
|
else {
|
|
/* Overwrite name, in order of precedence */
|
|
if ((nodelist[nodenum].port == 0) &&
|
|
((IsPort(ob)) ||
|
|
((nodelist[nodenum].node == 0) &&
|
|
((ob->type == NODE) ||
|
|
((nodelist[nodenum].uniqueglobal == 0) &&
|
|
((ob->type == UNIQUEGLOBAL) ||
|
|
((nodelist[nodenum].global == 0) &&
|
|
((ob->type == GLOBAL) ||
|
|
((nodelist[nodenum].pin == 0) &&
|
|
(ob->type >= FIRSTPIN))))))))))
|
|
nodelist[nodenum].name = ob->name;
|
|
|
|
}
|
|
switch (ob->type) {
|
|
case UNIQUEGLOBAL:
|
|
nodelist[nodenum].uniqueglobal++; break;
|
|
case GLOBAL:
|
|
nodelist[nodenum].global++; break;
|
|
case PORT:
|
|
nodelist[nodenum].port++; break;
|
|
case NODE:
|
|
nodelist[nodenum].node++; break;
|
|
case PROPERTY:
|
|
break;
|
|
default:
|
|
nodelist[nodenum].pin++; break;
|
|
}
|
|
}
|
|
|
|
for (nodenum = 0; nodenum <= nodemax; nodenum++) {
|
|
if (nodelist[nodenum].name == NULL) continue;
|
|
|
|
pins = nodelist[nodenum].pin;
|
|
ports = nodelist[nodenum].port;
|
|
globals = nodelist[nodenum].global;
|
|
uniqueglobals = nodelist[nodenum].uniqueglobal;
|
|
nodes = nodelist[nodenum].node;
|
|
|
|
Printf("Net %d (%s):", nodenum, nodelist[nodenum].name);
|
|
Ftab(NULL, maxnamelen + 15);
|
|
Printf("Total = %d,", pins + ports + nodes + globals + uniqueglobals);
|
|
if (ports)
|
|
Printf(" Ports = %d,", ports);
|
|
Ftab(NULL, maxnamelen + 40);
|
|
if (pins)
|
|
Printf("Pins = %d,", pins);
|
|
Ftab(NULL, maxnamelen + 52);
|
|
if (nodes)
|
|
Printf("Nets = %d,", nodes);
|
|
Ftab(NULL, maxnamelen + 63);
|
|
if (globals)
|
|
Printf("Globals = %d,", globals);
|
|
Ftab(NULL, maxnamelen + 80);
|
|
if (uniqueglobals)
|
|
Printf("UniqueGlobals = %d", uniqueglobals);
|
|
Printf("\n");
|
|
}
|
|
|
|
FREE(nodelist);
|
|
}
|
|
|
|
void PrintCell(char *name, int fnum)
|
|
{
|
|
struct nlist *tp;
|
|
struct objlist *ob;
|
|
int maxnamelen;
|
|
|
|
if ((fnum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintCell(name, Circuit1->file);
|
|
PrintCell(name, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
tp = LookupCellFile(name, fnum);
|
|
if (tp == NULL) {
|
|
Printf ("No circuit '%s' found.\n",name);
|
|
return;
|
|
}
|
|
maxnamelen = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
int len;
|
|
if ((len = strlen(ob->name)) > maxnamelen) maxnamelen = len;
|
|
}
|
|
|
|
Printf("Circuit: '%s'\n", tp->name);
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
Printf ("%s ", ob->name[1] == ':' ? ob->name : ob->name);
|
|
Ftab(NULL, maxnamelen + 2);
|
|
switch (ob->type) {
|
|
case UNIQUEGLOBAL:
|
|
Printf("unique global"); break;
|
|
case GLOBAL:
|
|
Printf("global"); break;
|
|
case PORT:
|
|
Printf("port"); break;
|
|
case NODE:
|
|
Printf("node"); break;
|
|
case PROPERTY:
|
|
Printf("properties"); break;
|
|
default:
|
|
Printf("pin %d", ob->type);
|
|
break;
|
|
}
|
|
Ftab(NULL, 40);
|
|
if (ob->type != PROPERTY)
|
|
Printf(" Net #: %d", ob->node);
|
|
Printf("\n");
|
|
}
|
|
}
|
|
|
|
void PrintInstances(char *name, int filenum)
|
|
{
|
|
struct nlist *tp;
|
|
struct objlist *ob;
|
|
int instancecount;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintInstances(name, Circuit1->file);
|
|
PrintInstances(name, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
tp = LookupCellFile(name, filenum);
|
|
if (tp == NULL) {
|
|
Printf ("No circuit '%s' found.\n",name);
|
|
return;
|
|
}
|
|
Printf("Circuit: '%s'\n",tp->name);
|
|
instancecount = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
struct objlist *ob2;
|
|
int port, node, global, uniqueglobal, pin;
|
|
int ports, nodes, globals, uniqueglobals, pins;
|
|
|
|
port = node = global = uniqueglobal = pin = 0;
|
|
instancecount++;
|
|
|
|
ob2 = ob;
|
|
do {
|
|
struct objlist *ob3;
|
|
|
|
ports = nodes = globals = uniqueglobals = pins = 0;
|
|
for (ob3 = tp->cell; ob3 != NULL; ob3 = ob3->next)
|
|
if (ob3->node == ob2->node)
|
|
switch (ob3->type) {
|
|
case UNIQUEGLOBAL: uniqueglobals++; break;
|
|
case GLOBAL: globals++; break;
|
|
case PORT: ports++; break;
|
|
case NODE: nodes++; break;
|
|
case PROPERTY: break;
|
|
default: pins++; break;
|
|
}
|
|
pin++;
|
|
if (uniqueglobals) uniqueglobal++;
|
|
else if (globals) global++;
|
|
else if (ports) port++;
|
|
else if (nodes) node++;
|
|
ob2 = ob2->next;
|
|
} while (ob2 != NULL && ob2->type > FIRSTPIN);
|
|
/* Fflush(stdout); */
|
|
Printf("%s (class: %s)", ob->instance, ob->model.class);
|
|
Ftab(NULL,35);
|
|
Printf("%2d pins ->",pin);
|
|
if (port) Printf("%2d ports,",port);
|
|
Ftab(NULL,55);
|
|
if (node) Printf("%2d nodes,",node);
|
|
Ftab(NULL,65);
|
|
if (global) Printf("%2d globals,",global);
|
|
Ftab(NULL,75);
|
|
if (uniqueglobal) Printf("%2d ug",uniqueglobal);
|
|
Printf("\n");
|
|
}
|
|
}
|
|
Printf("Cell %s contains %d instances.\n",name,instancecount);
|
|
}
|
|
|
|
void DescribeInstance(char *name, int file)
|
|
{
|
|
struct nlist *tp, *tp2;
|
|
struct objlist *ob;
|
|
unsigned char *instlist;
|
|
int nodemax, nodenum;
|
|
|
|
int instancecount;
|
|
int node, nodenumber, morenodes, disconnectednodes;
|
|
|
|
if ((file == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
DescribeInstance(name, Circuit1->file);
|
|
DescribeInstance(name, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
tp = LookupCellFile(name, file);
|
|
|
|
if (tp == NULL) {
|
|
Printf("No circuit '%s' found.\n",name);
|
|
return;
|
|
}
|
|
Printf("Circuit: '%s'\n",tp->name);
|
|
|
|
/* First pass counts total number of entries */
|
|
nodemax = 0;
|
|
disconnectednodes = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
{
|
|
if (ob->node > nodemax) nodemax = ob->node;
|
|
else if ((ob->node == -1) && (ob->model.port != PROXY)) {
|
|
|
|
/* All black-box modules and placeholders by definition have all */
|
|
/* disconnected pins, so don't report those. */
|
|
|
|
if (!(tp->flags & CELL_PLACEHOLDER) && (tp->class != CLASS_MODULE))
|
|
{
|
|
//if (disconnectednodes == 0) Fprintf(stderr, "\n");
|
|
disconnectednodes++;
|
|
Fprintf(stderr, "Cell %s(%d) disconnected node: %s\n", tp->name, tp->file, ob->name);
|
|
}
|
|
}
|
|
}
|
|
instlist = (unsigned char *) CALLOC((nodemax + 1), sizeof(unsigned char));
|
|
|
|
/* Second pass finds total number of unique nodes */
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next)
|
|
if (ob->node > 0)
|
|
instlist[ob->node] = (unsigned char) 1;
|
|
|
|
/* Now add them all together */
|
|
nodenumber = 0;
|
|
for (nodenum = 1; nodenum <= nodemax; nodenum++)
|
|
if (instlist[nodenum] == (unsigned char) 1) nodenumber++;
|
|
|
|
/* And we're done with this record */
|
|
FREE(instlist);
|
|
|
|
/* Now collect the relevant information per node */
|
|
|
|
ClearDumpedList();
|
|
|
|
instancecount = 0;
|
|
for (ob = tp->cell; ob != NULL; ob = ob->next) {
|
|
if (ob->type == FIRSTPIN) {
|
|
instancecount++;
|
|
tp2 = LookupCellFile(ob->model.class, tp->file);
|
|
tp2->dumped++;
|
|
}
|
|
}
|
|
Printf("Circuit %s contains %d device instances.\n", name, instancecount);
|
|
|
|
/* print out results */
|
|
tp = FirstCell();
|
|
while (tp != NULL) {
|
|
if (tp->dumped) {
|
|
Printf(" Class: %s", tp->name);
|
|
Ftab(NULL, 30);
|
|
Printf(" instances: %3d\n", tp->dumped);
|
|
}
|
|
tp = NextCell();
|
|
}
|
|
Printf("Circuit contains %d nets", nodenumber);
|
|
if (disconnectednodes)
|
|
Printf(", and %d disconnected pin%s", disconnectednodes,
|
|
((disconnectednodes == 1) ? "" : "s"));
|
|
Printf(".\n");
|
|
|
|
}
|
|
|
|
void PrintPortsInCell(char *cellname, int filenum)
|
|
{
|
|
struct nlist *np;
|
|
struct objlist *ob;
|
|
int portcount;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintPortsInCell(cellname, Circuit1->file);
|
|
PrintPortsInCell(cellname, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
np = LookupCellFile(cellname, filenum);
|
|
if (np == NULL) {
|
|
Printf("No circuit: %s\n",cellname);
|
|
return;
|
|
}
|
|
portcount = 0;
|
|
for (ob = np->cell; ob != NULL; ob = ob->next)
|
|
if (IsPort(ob)) {
|
|
portcount++;
|
|
Printf("%s\n", ob->name);
|
|
}
|
|
Printf("Cell %s contains %d ports.\n",cellname, portcount);
|
|
}
|
|
|
|
void PrintLeavesInCell(char *cellname, int filenum)
|
|
{
|
|
struct nlist *np;
|
|
struct objlist *ob;
|
|
int am_a_leaf;
|
|
|
|
if ((filenum == -1) && (Circuit1 != NULL) && (Circuit2 != NULL)) {
|
|
PrintLeavesInCell(cellname, Circuit1->file);
|
|
PrintLeavesInCell(cellname, Circuit2->file);
|
|
return;
|
|
}
|
|
|
|
np = LookupCellFile(cellname, filenum);
|
|
if (np == NULL) {
|
|
Printf("No circuit: %s\n",cellname);
|
|
return;
|
|
}
|
|
if (np->dumped) return;
|
|
np->dumped = 1;
|
|
|
|
if (np->class != CLASS_SUBCKT) {
|
|
Printf("%s; %d ports; Primitive.\n", cellname, NumberOfPorts(cellname, filenum));
|
|
return;
|
|
}
|
|
|
|
/* otherwise, consider it's children */
|
|
am_a_leaf = 1; /* assume I am a leaf */
|
|
for (ob = np->cell; ob != NULL; ob = ob->next)
|
|
if (ob->type == FIRSTPIN) {
|
|
/* if I contain an instance, I cannot be a leaf */
|
|
PrintLeavesInCell(ob->model.class, filenum);
|
|
am_a_leaf = 0;
|
|
}
|
|
|
|
if (am_a_leaf) Printf("%s; %d ports\n", cellname, NumberOfPorts(cellname, filenum));
|
|
return;
|
|
}
|
|
|
|
static int PrintLeavesInCellHash(struct hashlist *p)
|
|
/* print leaves in hash table that are INSTANCED by other cells */
|
|
{
|
|
struct nlist *ptr;
|
|
|
|
ptr = (struct nlist *)(p->ptr);
|
|
if ((ptr->class == CLASS_SUBCKT)) PrintLeavesInCell(ptr->name, ptr->file);
|
|
return(0);
|
|
}
|
|
|
|
void PrintAllLeaves(void)
|
|
{
|
|
ClearDumpedList();
|
|
RecurseCellHashTable(PrintLeavesInCellHash);
|
|
}
|
|
|
|
static jmp_buf jmpenv;
|
|
|
|
/* static void handler(void) */
|
|
static void handler(int sig)
|
|
{
|
|
fprintf(stderr,"\nInterrupt (%d)!!\n", sig);
|
|
fflush(stderr);
|
|
longjmp(jmpenv,1);
|
|
}
|
|
|
|
#ifndef TCL_NETGEN
|
|
|
|
void Query(void)
|
|
{
|
|
/* little interactive debugger */
|
|
char reply;
|
|
char repstr[100];
|
|
char repstr2[100];
|
|
float StartTime; /* for elapsed CPU times */
|
|
int Timing; /* if true, print times of each command */
|
|
int filenum = -1;
|
|
|
|
if (!SuppressPrompts)
|
|
Printf("Netgen %s.%s: %s, %s, %s\n", NETGEN_VERSION, NETGEN_REVISION,
|
|
NETGEN_COPYRIGHT, NETGEN_AUTHOR, NETGEN_DEVELOPER);
|
|
setjmp(jmpenv);
|
|
signal(SIGINT,handler);
|
|
Timing = 0;
|
|
StartTime = 0.0;
|
|
do {
|
|
promptstring("NETGEN command: ",repstr);
|
|
if (Timing) StartTime = CPUTime();
|
|
reply = repstr[0];
|
|
switch (reply) {
|
|
case 'h' : PrintCellHashTable(0, filenum); break;
|
|
case 'H' : PrintCellHashTable(1, filenum); break;
|
|
case 'N' :
|
|
promptstring("Enter circuit name: ", repstr);
|
|
PrintNodes(repstr, -1);
|
|
break;
|
|
case 'n' :
|
|
promptstring("Enter element name: ", repstr);
|
|
if (CurrentCell == NULL)
|
|
promptstring("Enter circuit name: ", repstr2);
|
|
else strcpy(repstr2, CurrentCell->name);
|
|
Fanout(repstr2, repstr, ALLOBJECTS);
|
|
break;
|
|
case 'e' :
|
|
promptstring("Enter element name: ", repstr);
|
|
if (CurrentCell == NULL)
|
|
promptstring("Enter circuit name: ", repstr2);
|
|
else strcpy(repstr2, CurrentCell->name);
|
|
PrintElement(repstr2,repstr);
|
|
break;
|
|
case 'c' :
|
|
promptstring("Enter circuit name: ", repstr);
|
|
PrintCell(repstr, filenum);
|
|
break;
|
|
case 'i' :
|
|
promptstring("Enter circuit name: ", repstr);
|
|
PrintInstances(repstr, filenum);
|
|
break;
|
|
case 'd':
|
|
promptstring("Enter circuit name: ", repstr);
|
|
DescribeInstance(repstr, filenum);
|
|
break;
|
|
/* output file formats */
|
|
case 'k' :
|
|
promptstring("Write NTK: Enter circuit name: ", repstr);
|
|
Ntk(repstr,"");
|
|
break;
|
|
case 'x' :
|
|
promptstring("Write EXT: Enter circuit name: ", repstr);
|
|
Ext(repstr, filenum);
|
|
break;
|
|
case 'z' :
|
|
promptstring("Write SIM: Enter circuit name: ", repstr);
|
|
Sim(repstr, filenum);
|
|
break;
|
|
case 'w' :
|
|
promptstring("Write WOMBAT: circuit name: ", repstr);
|
|
Wombat(repstr,NULL);
|
|
break;
|
|
case 'a' :
|
|
promptstring("Write ACTEL: circuit name: ", repstr);
|
|
Actel(repstr,"");
|
|
break;
|
|
case 's':
|
|
promptstring("Write SPICE: circuit name: ", repstr);
|
|
SpiceCell(repstr, filenum, "");
|
|
break;
|
|
case 'v':
|
|
promptstring("Write Verilog: circuit name: ", repstr);
|
|
VerilogTop(repstr, filenum, "");
|
|
break;
|
|
case 'E':
|
|
promptstring("Write ESACAP: circuit name: ", repstr);
|
|
EsacapCell(repstr,"");
|
|
break;
|
|
case 'g':
|
|
promptstring("Write NETGEN: circuit name: ", repstr);
|
|
WriteNetgenFile(repstr,"");
|
|
break;
|
|
case 'C':
|
|
promptstring("Write C code: circuit name: ", repstr);
|
|
Ccode(repstr,"");
|
|
break;
|
|
/* input file formats */
|
|
case 'r':
|
|
case 'R':
|
|
promptstring("Read file: ",repstr);
|
|
ReadNetlist(repstr, &filenum);
|
|
break;
|
|
case 'X' :
|
|
promptstring("Read EXT: Enter file name: ", repstr);
|
|
ReadExtHier(repstr, &filenum);
|
|
break;
|
|
case 'Z' :
|
|
promptstring("Read SIM: Enter file name: ", repstr);
|
|
ReadSim(repstr, &filenum);
|
|
break;
|
|
case 'K' :
|
|
promptstring("Read NTK: file? ", repstr);
|
|
ReadNtk(repstr, &filenum);
|
|
break;
|
|
case 'A' :
|
|
printf("Reading ACTEL library.\n");
|
|
ActelLib();
|
|
break;
|
|
case 'S':
|
|
promptstring("Read SPICE (.spice) file? ", repstr);
|
|
ReadSpice(repstr, &filenum);
|
|
break;
|
|
case 'V':
|
|
promptstring("Read Verilog (.v) file? ", repstr);
|
|
ReadVerilog(repstr, &filenum);
|
|
break;
|
|
case 'G' :
|
|
promptstring("Read NETGEN: file? ", repstr);
|
|
ReadNetgenFile(repstr, &filenum);
|
|
break;
|
|
|
|
case 'f' :
|
|
promptstring("Enter circuit name to flatten: ", repstr);
|
|
Flatten(repstr, filenum);
|
|
break;
|
|
case 'F' :
|
|
promptstring("Enter class of circuit to flatten: ", repstr);
|
|
FlattenInstancesOf(repstr, filenum);
|
|
break;
|
|
case 'p' :
|
|
promptstring("Enter circuit name: ", repstr);
|
|
PrintPortsInCell(repstr, filenum);
|
|
break;
|
|
case 'T' :
|
|
NETCOMP();
|
|
break;
|
|
case 'l' :
|
|
ClearDumpedList();
|
|
promptstring("Enter circuit name: ", repstr);
|
|
PrintLeavesInCell(repstr, filenum);
|
|
break;
|
|
case 'L' :
|
|
printf("List of all leaf circuits:\n");
|
|
PrintAllLeaves();
|
|
break;
|
|
case 'D' : Debug = !Debug;
|
|
printf("Debug mode is %s\n", Debug?"ON":"OFF");
|
|
break;
|
|
case 't': StartTime = CPUTime();
|
|
Timing = !Timing;
|
|
printf("Timing of commands %s.\n", Timing?"enabled":"disabled");
|
|
break;
|
|
case '#':
|
|
if (strlen(repstr) > 1) {
|
|
char *command;
|
|
command = repstr+1;
|
|
DBUG_PUSH(command);
|
|
}
|
|
else {
|
|
promptstring("Dbug command? ",repstr);
|
|
DBUG_PUSH(repstr);
|
|
}
|
|
break;
|
|
case '!':
|
|
#ifdef IBMPC
|
|
system("");
|
|
#else
|
|
system("/bin/csh");
|
|
#endif
|
|
break;
|
|
case 'I' : Initialize(); break;
|
|
case 'q' : break;
|
|
case 'Q' : exit(0);
|
|
case 'P' : PROTOCHIP(); break;
|
|
#ifdef HAVE_MALLINFO
|
|
case 'm': PrintMemoryStats(); break;
|
|
#endif
|
|
case '<' :
|
|
{
|
|
FILE *oldfile;
|
|
promptstring("Read from file? ",repstr);
|
|
oldfile = promptstring_infile;
|
|
promptstring_infile = fopen(repstr,"r");
|
|
if (promptstring_infile == NULL)
|
|
Printf("Unable to open command file: %s\n", repstr);
|
|
else {
|
|
Query();
|
|
fclose(promptstring_infile);
|
|
}
|
|
promptstring_infile = oldfile;
|
|
break;
|
|
}
|
|
default :
|
|
fflush(stdout);
|
|
if (strlen(repstr)) fprintf(stderr,"Unknown command: %c\n",reply);
|
|
fflush(stderr);
|
|
printf(
|
|
"WRITE: nt(k),e(x)t,sim (z),(w)ombat,(a)ctel,net(g)en,(s)pice,(E)sacap,(C)\n");
|
|
printf(
|
|
"READ: (r)ead, nt(K), e(X)t, sim (Z), (A)ctel library, net(G)en, (S)pice\n");
|
|
printf(
|
|
"Print LISTS: (d)escribe circuit (c)ontents, (i)nstances, (p)orts, (l)eaves (L)\n"
|
|
);
|
|
printf(
|
|
"PRINT: (n)ode connectivity (N), (e)lement list, (h)ash table entries (H))\n");
|
|
printf(
|
|
"CELL OPS: (f)latten circuit, (F)latten instance, make circuit primiti(v)e (V=all)\n"
|
|
);
|
|
|
|
printf("toggle (D)ebug, (t)ime commands, embed (P)rotochip, ne(T)cmp\n");
|
|
printf("(!) push shell, (<) read input file");
|
|
|
|
#ifdef HAVE_MALLINFO
|
|
printf(", show (m)emory usage");
|
|
#endif
|
|
printf("\n");
|
|
printf("(q)uit, (Q)uit immediately, re-(I)nitialize \n");
|
|
break;
|
|
}
|
|
if (Timing) {
|
|
printf("CPU time used by last operation = %0.2f s\n",
|
|
ElapsedCPUTime(StartTime));
|
|
fflush(stdout); /* just in case we have been redirected */
|
|
}
|
|
} while (reply != 'q');
|
|
signal(SIGINT,SIG_DFL);
|
|
}
|
|
|
|
#endif
|