netgen-1.5.133/0000755000175000001440000000000013547302601011655 5ustar timusersnetgen-1.5.133/netgen/0000755000175000001440000000000013445241451013137 5ustar timusersnetgen-1.5.133/netgen/symbol.map0000644000175000001440000000007013445241451015140 0ustar timusersNETGEN_1.1 { global: Tclnetgen_Init; local: *; }; netgen-1.5.133/netgen/inetcomp.c0000644000175000001440000000241213445241451015120 0ustar timusers/* "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. */ /* inetcomp.c -- a simple wrapper to the NETCOMP() function */ #include #include "netgen.h" #ifdef HAVE_GETOPT #include #endif /* HAVE_GETOPT */ void STRCPY(char *dest, char *source) { while ((*dest++ = *source++) != '\0') ; } int main(int argc, char *argv[]) { char cell1[200], cell2[200]; Debug = 0; if (argc != 1) { printf ("usage: inetcomp\n"); return (-1); } Initialize(); NETCOMP(); return(1); } netgen-1.5.133/netgen/ntk2xnf.c0000644000175000001440000000333513445241451014701 0ustar timusers/* "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. Xilinx generator extensions Copyright (C) 1995, Ingo Cyliax, EZComm Consulting 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. */ /* ntk2xnf.c -- a simple wrapper to translate .ntk to Xilinx XNF format */ #include #include "netgen.h" #ifdef HAVE_X11 /* the following two X procedures are to permit linking with netgen.a even if HAVE_X11 has been enabled */ void X_display_line(char *buf) { printf("%s", buf); } void X_display_refresh(void) { fflush(stdout); } #endif void STRCPY(char *dest, char *source) { while ((*dest++ = *source++) != '\0') ; } int main(int argc, char *argv[]) { char cellname[200]; int filenum = -1; Debug = 0; if (argc < 2 || argc > 3) { printf ("usage: ntk2xnf []\n"); return (-1); } Initialize(); XilinxLib(); STRCPY(cellname, ReadNetlist(argv[1], &filenum)); if (argc == 3) STRCPY(cellname, argv[2]); Xilinx(cellname, NULL); return(0); } netgen-1.5.133/netgen/netcomp.c0000644000175000001440000000636713445241451014764 0ustar timusers/* "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. */ /* netcomp.c -- a simple wrapper to provide netlist comparison functionality */ #include #include "netgen.h" #ifdef HAVE_GETOPT #include #endif /* HAVE_GETOPT */ #ifndef HAVE_X11 /* the following two procedures need to be defined to * permit linking with netgen.o even if HAVE_X11 has * been disabled */ void X_display_line(char *buf) { printf("%s", buf); } void X_display_refresh(void) { fflush(stdout); } #endif void STRCPY(char *dest, char *source) { while ((*dest++ = *source++) != '\0') ; } int main(int argc, char *argv[]) { #ifndef HAVE_GETOPT char cell1[200], cell2[200]; int filenum = -1; Debug = 0; if (argc < 3 || argc > 5) { printf ("usage: netcomp [ []] \n"); return (-1); } Initialize(); STRCPY(cell1, ReadNetlist(argv[1], &filenum)); if (argc >= 4) STRCPY(cell1, argv[3]); /* if explicit cell name specified */ STRCPY(cell2, ReadNetlist(argv[2], &filenum)); if (argc == 5) STRCPY(cell2, argv[4]); /* if explicit cell name specified */ #else char cell1[200], cell2[200]; int usage = 0; int args; int c; Debug = 0; VerboseOutput = 0; IgnoreRC = 0; while ((c = getopt(argc, argv, "ivq")) != EOF) { switch (c) { case 'i': IgnoreRC = 1; break; case 'v': VerboseOutput = 1; break; case 'q': NoOutput = 1; break; default: printf("Unknown flag: -%c\n", (char)c); usage = 1; } } args = argc - optind; if (args < 2 || args > 4) { printf("Wrong number of file/cell name arguments.\n"); usage = 1; } if (usage) { printf ("usage: netcomp [-i] [-v] [-q] [ []]\n"); printf (" -i = don't try to match resistances and capacitances\n"); printf (" -v = verbose output\n"); printf (" -q = no output (only results and return code)\n"); return (-1); } Initialize(); /* NoDisconnectedNodes = 1; we now do this in Compare(), AFTER reading cells */ STRCPY(cell1, ReadNetlist(argv[optind])); if (args >= 3) STRCPY(cell1, argv[optind + 2]); STRCPY(cell2, ReadNetlist(argv[optind + 1])); if (args == 4) STRCPY(cell2, argv[optind + 3]); #endif printf("Comparing cells: %s (circuit 1) and %s (circuit2).\n\n", cell1, cell2); Flatten(cell1, -1); Flatten(cell2, -1); if (Compare(cell1, cell2)) { printf("Cells are identical.\n"); return(0); } printf("Cells are different.\n"); return(1); } netgen-1.5.133/netgen/Makefile0000644000175000001440000000353213445241451014602 0ustar timusersMODULE = netgen NETGENDIR = .. SRCS = netgen_main.c include ${NETGENDIR}/defs.mak EXTRA_LIBS = ${NETGENDIR}/base/libbase.o \ ${MAIN_EXTRA_LIBS} DFLAGS += ${GR_DFLAGS} DFLAGS += -DNETGEN_DATE="\"`date`\"" LIBS += ${GR_LIBS} -lm CFLAGS += ${GR_CFLAGS} -I${NETGENDIR}/base CLEANS += netgen netcomp ntk2adl inetcomp ntk2xnf main: netgen netcomp inetcomp ntk2adl ntk2xnf tcl-main: tclnetgen${SHDLIB_EXT} tclnetgen${SHDLIB_EXT}: ${EXTRA_LIBS} @echo --- making netgen Tcl library \(tclnetgen${SHDLIB_EXT}\) ${RM} tclnetgen${SHDLIB_EXT} ${CC} ${CFLAGS} ${CPPFLAGS} -o $@ ${LDDL_FLAGS} \ ${LD_RUN_PATH} ${EXTRA_LIBS} ${LD_EXTRA_LIBS} \ -lc ${LIBS} ${LIB_SPECS} ${LDFLAGS} $(DESTDIR)${BINDIR}/netgen: ${RM} $@ ${CP} netgen $@ $(DESTDIR)${BINDIR}/ntk2adl: ${RM} $@ ${CP} ntk2adl $@ $(DESTDIR)${BINDIR}/ntk2xnf: ${RM} $@ ${CP} ntk2xnf $@ $(DESTDIR)${BINDIR}/netcomp: ${RM} $@ ${CP} netcomp $@ $(DESTDIR)${BINDIR}/inetcomp: ${RM} $@ ${CP} inetcomp $@ netcomp: netcomp.c ${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} netcomp.c -o $@ ${EXTRA_LIBS} \ ${LIBS} ${LDFLAGS} inetcomp: inetcomp.c ${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} inetcomp.c -o $@ ${EXTRA_LIBS} \ ${LIBS} ${LDFLAGS} ntk2adl: ntk2adl.c ${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} ntk2adl.c -o $@ ${EXTRA_LIBS} \ ${LIBS} ${LDFLAGS} ntk2xnf: ntk2xnf.c ${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} ntk2xnf.c -o $@ ${EXTRA_LIBS} \ ${LIBS} ${LDFLAGS} install: $(DESTDIR)${BINDIR}/netgen $(DESTDIR)${BINDIR}/netcomp \ $(DESTDIR)${BINDIR}/inetcomp $(DESTDIR)${BINDIR}/ntk2adl \ $(DESTDIR)${BINDIR}/ntk2xnf install-tcl: $(DESTDIR)${TCLDIR} $(DESTDIR)${TCLDIR}/tclnetgen${SHDLIB_EXT} $(DESTDIR)${TCLDIR}/tclnetgen${SHDLIB_EXT}: tclnetgen${SHDLIB_EXT} ${RM} $(DESTDIR)${TCLDIR}/tclnetgen${SHDLIB_EXT} ${CP} tclnetgen${SHDLIB_EXT} $(DESTDIR)${TCLDIR}/tclnetgen${SHDLIB_EXT} include ${NETGENDIR}/rules.mak netgen-1.5.133/netgen/netgen_main.c0000644000175000001440000000227213445241451015572 0ustar timusers/* "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. */ /* netgen_main.c -- top-level (main) routine */ #include #ifdef ANSI_LIBRARY #include /* for getenv */ #endif #include "netgen.h" int main(int argc, char **argv) { Finsert(stderr); InitializeCommandLine(argc, argv); #ifdef HAVE_X11 X_main_loop(argc, argv); /* does not return, if really running X */ #else Query(); #endif return(0); } netgen-1.5.133/netgen/Depend0000644000175000001440000000010013445241451014250 0ustar timusersnetgen_main.o: netgen_main.c ../base/netgen.h ../base/objlist.h netgen-1.5.133/netgen/ntk2adl.c0000644000175000001440000000317213445241451014645 0ustar timusers/* "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. */ /* ntk2adl.c -- a simple wrapper to translate .ntk to ACTEL format */ #include #include "netgen.h" #ifndef HAVE_X11 /* the following two procedures permit linking * with netgen.a even if HAVE_X11 has been disabled */ void X_display_line(char *buf) { printf("%s", buf); } void X_display_refresh(void) { fflush(stdout); } #endif void STRCPY(char *dest, char *source) { while ((*dest++ = *source++) != '\0') ; } int main(int argc, char *argv[]) { char cellname[200]; int filenum = -1; Debug = 0; if (argc < 2 || argc > 3) { printf ("usage: ntk2adl []\n"); return (-1); } Initialize(); ActelLib(); STRCPY(cellname, ReadNetlist(argv[1], &filenum)); if (argc == 3) STRCPY(cellname, argv[2]); Actel(cellname, NULL); return(0); } netgen-1.5.133/VERSION0000644000175000001440000000001013547302570012721 0ustar timusers1.5.133 netgen-1.5.133/tcltk/0000755000175000001440000000000013521501204012766 5ustar timusersnetgen-1.5.133/tcltk/console.tcl0000644000175000001440000000024213445241451015144 0ustar timusers# Tcl commands to run in the console before netgen is initialized # puts stdout "Running NetGen Console Functions" bind .text {netgen::interrupt} netgen-1.5.133/tcltk/tclnetgen.c0000644000175000001440000034755013501364377015153 0ustar timusers/* "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. */ /* tclnetgen.c --- Tcl interpreter interface for using netgen */ #include #include /* for getenv */ #include #include #include "config.h" #include "netgen.h" #include "objlist.h" #include "netcmp.h" #include "dbug.h" #include "print.h" #include "query.h" /* for ElementNodes() */ #include "hash.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /*-----------------------*/ /* Tcl 8.4 compatibility */ /*-----------------------*/ #ifndef CONST84 #define CONST84 #endif Tcl_Interp *netgeninterp; Tcl_Interp *consoleinterp; int ColumnBase = 0; char *LogFileName = NULL; extern int PropertyErrorDetected; /* Function prototypes for all Tcl command callbacks */ int _netgen_readnet(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_readlib(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_canonical(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_writenet(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_flatten(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_nodes(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_elements(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_debug(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_protochip(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_instances(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_contents(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_describe(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_cells(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_ports(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_model(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_leaves(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_quit(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_reinit(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netgen_log(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); #ifdef HAVE_MALLINFO int _netgen_printmem(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); #endif int _netgen_help(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_matching(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_compare(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_iterate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_summary(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_print(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_run(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_verify(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_automorphs(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_equate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_ignore(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_permute(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_property(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_exhaustive(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_restart(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_global(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); int _netcmp_convert(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST objv[]); typedef struct _Cmd { char *name; int (*handler)(); char *helptext; } Command; /*------------------------------------------------------*/ /* All netgen commands under Tcl are defined here */ /*------------------------------------------------------*/ Command netgen_cmds[] = { {"readnet", _netgen_readnet, "[] []\n " "read a netlist file (default format=auto)"}, {"readlib", _netgen_readlib, " []\n " "read a format library"}, {"canonical", _netgen_canonical, "\n " "return top-level cellname and file number"}, {"writenet", _netgen_writenet, " \n " "write a netlist file"}, {"flatten", _netgen_flatten, "[class] [] \n " "flatten a hierarchical cell"}, {"nodes", _netgen_nodes, "[] \n " "print nodes of an element or cell"}, {"elements", _netgen_elements, "[] \n " "print elements of a node or cell"}, {"debug", _netgen_debug, "on|off|\n " "turn debugging on or off or debug a command"}, {"protochip", _netgen_protochip, "\n " "embed protochip structure"}, {"instances", _netgen_instances, "\n " "list instances of the cell"}, {"contents", _netgen_contents, "\n " "list contents of the cell"}, {"describe", _netgen_describe, "\n " "describe the cell"}, {"cells", _netgen_cells, "[list] [-all | -top | filename]\n " "print known cells, optionally from filename only\n " "-all: print all cells, including primitives\n " "-top: print all top-level cells"}, {"ports", _netgen_ports, "\n " "print ports of the cell"}, {"model", _netgen_model, " \n " "equate a model name with a device class"}, {"leaves", _netgen_leaves, "[]\n " "print leaves of the cell"}, {"quit", _netgen_quit, "\n " "exit netgen and Tcl"}, {"reinitialize", _netgen_reinit, "\n " "reintialize netgen data structures"}, {"log", _netgen_log, "[file |start|end|reset|suspend|resume|echo]\n " "enable or disable output log to file"}, #ifdef HAVE_MALLINFO {"memory", _netgen_printmem, "\n " "print memory statistics"}, #endif {"help", _netgen_help, "\n " "print this help information"}, NULL }; Command netcmp_cmds[] = { {"compare", _netcmp_compare, " \n " "declare two cells for netcomp netlist comparison"}, {"global", _netcmp_global, " \n " "declare a node (with possible wildcards) in the\n " "hierarchy of to be of global scope"}, {"convert", _netcmp_convert, "\n " "convert global nodes to local nodes and pins\n " "in cell "}, {"iterate", _netcmp_iterate, "\n " "do one netcomp iteration"}, {"summary", _netcmp_summary, "[elements|nodes]\n " "summarize netcomp internal data structure"}, {"print", _netcmp_print, "\n " "print netcomp internal data structure"}, {"run", _netcmp_run, "[converge|resolve]\n " "converge: run netcomp to completion (convergence)\n " "resolve: run to completion and resolve symmetries"}, {"verify", _netcmp_verify, "[elements|nodes|only|equivalent|unique]\n " "verify results"}, {"symmetries", _netcmp_automorphs, "\n " "print symmetries"}, {"equate", _netcmp_equate, "elements [] [] \n " "nodes [] [] \n " "pins [[] [] ]\n " "classes [] []\n " "elements: equate two elements\n " "nodes: equate two nodes\n " "classes: equate two device classes\n " "pins: match pins between two cells"}, {"ignore", _netcmp_ignore, "class \n " "class: ignore any instances of class named "}, {"permute", _netcmp_permute, "[transistors|resistors|capacitors|]\n " ": permute named pins on device model\n " "resistor: enable resistor permutations\n " "capacitor: enable capacitor permutations\n " "transistor: enable transistor permutations\n " "(none): enable transistor and resistor permutations"}, {"property", _netcmp_property, "default: apply property defaults\n " "| [...]\n " ": name of a device type (capacitor, etc.)\n " ": name of a device model\n " ": name of the property to compare"}, {"exhaustive", _netcmp_exhaustive, "\n " "toggle exhaustive subdivision"}, {"restart", _netcmp_restart, "\n " "start over (reset data structures)"}, {"matching", _netcmp_matching, "[element|node] \n " "return the corresponding node or element name\n " "in the compared cell"}, NULL }; /*------------------------------------------------------*/ /* Given a file number, need to find the top-level cell */ /*------------------------------------------------------*/ struct nlist * GetTopCell(int fnum) { struct nlist *tp; tp = FirstCell(); while (tp != NULL) { if (tp->flags & CELL_TOP) if (tp->file == fnum) break; tp = NextCell(); } return tp; } /*------------------------------------------------------*/ /* Common function to parse a Tcl object as either a */ /* netlist file name or a file number. */ /*------------------------------------------------------*/ int CommonGetFilenameOrFile(Tcl_Interp *interp, Tcl_Obj *fobj, int *fnumptr) { int result, llen; int fnum, ftest; char *filename; struct nlist *tp; result = Tcl_GetIntFromObj(interp, fobj, &ftest); if (result != TCL_OK) { Tcl_ResetResult(interp); filename = Tcl_GetString(fobj); tp = LookupCell(filename); if (tp == NULL) { Tcl_SetResult(interp, "No such file.\n", NULL); return TCL_ERROR; } else if (!(tp->flags & CELL_TOP)) { Tcl_SetResult(interp, "Name is not a file.\n", NULL); return TCL_ERROR; } else fnum = tp->file; } else { fnum = ftest; } *fnumptr = fnum; return TCL_OK; } /*------------------------------------------------------*/ /* Common function to parse a cell name. This allows */ /* several variants on the syntax: */ /* */ /* (1) */ /* Assumes cellname is unique and finds the cell */ /* and file number. */ /* */ /* (2) { } */ /* Finds the cell, given the name and file number */ /* as a list of length 2. */ /* */ /* (3) { } */ /* Finds the cell, given the name and filename of */ /* the file containing the cell. */ /* */ /* (4) { } */ /* is also allowed and is backwards-compatible */ /* with the arguments for "lvs". */ /* */ /* (5) { } */ /* likewise. */ /* */ /* (6) */ /* refers to the top-level cell of file */ /* */ /* (7) -circuit1 */ /* the first circuit being compared, after the */ /* "compare" command has been issued. */ /* */ /* (8) -circuit2 */ /* the first circuit being compared, after the */ /* "compare" command has been issued. */ /* */ /* (9) -current */ /* the most recent circuit/file to be read, */ /* after a "readnet" or "readlib" has been issued. */ /* */ /* Note that is equivalent to the top-level */ /* cellname. That allows the order of elements to be */ /* arbitrary. */ /* */ /* Function returns a Tcl result, and fills in a */ /* pointer to the cell structure, and the file number */ /* (which is a copy of , if provided as an */ /* argument). */ /* */ /* == -1 or "*" is (theoretically) treated by */ /* all commands as a wildcard matching all netlists. */ /*------------------------------------------------------*/ int CommonParseCell(Tcl_Interp *interp, Tcl_Obj *objv, struct nlist **tpr, int *fnumptr) { Tcl_Obj *tobj, *fobj; int result, llen; int fnum, ftest, index; char *filename, *cellname; struct nlist *tp, *tp2; char *suboptions[] = { "-circuit1", "-circuit2", "-current", "*", NULL }; enum SubOptionIdx { CIRCUIT1_IDX, CIRCUIT2_IDX, CURRENT_IDX, WILDCARD_IDX }; result = Tcl_ListObjLength(interp, objv, &llen); if (result != TCL_OK) return TCL_ERROR; if (llen == 2) { fnum = -1; result = Tcl_ListObjIndex(interp, objv, 0, &tobj); if (result != TCL_OK) return TCL_ERROR; /* Is 1st argument an integer? */ result = Tcl_GetIntFromObj(interp, tobj, &ftest); if (result != TCL_OK) { Tcl_ResetResult(interp); /* Is 1st argument a special keyword? */ if (Tcl_GetIndexFromObj(interp, tobj, (CONST84 char **)suboptions, "special", 0, &index) == TCL_OK) { switch (index) { case CIRCUIT1_IDX: if (Circuit1 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } fnum = Circuit1->file; result = Tcl_ListObjIndex(interp, objv, 1, &tobj); if (result != TCL_OK) return TCL_ERROR; break; case CIRCUIT2_IDX: if (Circuit2 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } fnum = Circuit2->file; result = Tcl_ListObjIndex(interp, objv, 1, &tobj); if (result != TCL_OK) return TCL_ERROR; break; case CURRENT_IDX: if (CurrentCell == NULL) { Tcl_SetResult(interp, "No current cell\n", NULL); return TCL_ERROR; } fnum = CurrentCell->file; result = Tcl_ListObjIndex(interp, objv, 1, &tobj); if (result != TCL_OK) return TCL_ERROR; break; case WILDCARD_IDX: fnum = -2; result = Tcl_ListObjIndex(interp, objv, 1, &tobj); if (result != TCL_OK) return TCL_ERROR; break; } } else { Tcl_ResetResult(interp); fnum = -1; } /* Is 2nd argument an integer? */ if (fnum == -1) { result = Tcl_ListObjIndex(interp, objv, 1, &fobj); if (result != TCL_OK) return TCL_ERROR; result = Tcl_GetIntFromObj(interp, fobj, &ftest); if (result != TCL_OK) { Tcl_ResetResult(interp); /* Check if 2nd item is a reserved keyword */ if (Tcl_GetIndexFromObj(interp, fobj, (CONST84 char **)suboptions, "special", 0, &index) == TCL_OK) { switch (index) { case CIRCUIT1_IDX: if (Circuit1 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } fnum = Circuit1->file; break; case CIRCUIT2_IDX: if (Circuit2 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } fnum = Circuit2->file; break; case CURRENT_IDX: if (CurrentCell == NULL) { Tcl_SetResult(interp, "No current cell\n", NULL); return TCL_ERROR; } fnum = CurrentCell->file; break; case WILDCARD_IDX: filename = NULL; fnum = -1; break; } } else { Tcl_ResetResult(interp); filename = Tcl_GetString(fobj); } /* Okay, neither argument is an integer, so */ /* parse both as cell names and figure out */ /* which one is the same as the top level, */ /* and call that the filename. */ } else { filename = NULL; fnum = ftest; } } else if (fnum == -2) { filename = NULL; fnum = -1; } else /* Both file numbers have been provided, so a */ /* filename is not required. */ filename = NULL; } else { filename = NULL; fnum = ftest; result = Tcl_ListObjIndex(interp, objv, 1, &tobj); if (result != TCL_OK) return TCL_ERROR; } cellname = Tcl_GetString(tobj); if (fnum == -1) { /* If fnum is a wildcard, then we insist that there */ /* must be at least one cell matching the cellname, */ /* although the routines should be applied to all */ /* cells of the given name in all netlists. */ tp = LookupCell(cellname); if (tp == NULL) { Tcl_SetResult(interp, "No such cellname!\n", NULL); return TCL_ERROR; } if (filename != NULL) { tp2 = LookupCell(filename); if (tp2 == NULL) { Tcl_SetResult(interp, "No such cellname!\n", NULL); return TCL_ERROR; } } else tp2 = NULL; if (!(tp->flags & CELL_TOP)) { if ((tp2 != NULL) && !(tp2->flags & CELL_TOP)) { // Error: Neither name is a file! Tcl_SetResult(interp, "No filename in list!\n", NULL); return TCL_ERROR; } else if (tp2 != NULL) { // tp2 is file top, tp is cell fnum = tp2->file; tp = LookupCellFile(cellname, fnum); if (tp == NULL) { Tcl_SetResult(interp, "Cell is not in file!\n", NULL); return TCL_ERROR; } } } else { // Arguments are reversed fnum = tp->file; tp = LookupCellFile(filename, fnum); if (tp == NULL) { Tcl_SetResult(interp, "Cell is not in file!\n", NULL); return TCL_ERROR; } } } else { /* File number was given, so just plug it in */ tp = LookupCellFile(cellname, fnum); if (tp == NULL) { Tcl_SetResult(interp, "No such cell or bad file number!\n", NULL); return TCL_ERROR; } } } else { /* Only one name given; check if it matches subOption */ if (Tcl_GetIndexFromObj(interp, objv, (CONST84 char **)suboptions, "special", 0, &index) == TCL_OK) { switch (index) { case CIRCUIT1_IDX: if (Circuit1 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } tp = Circuit1; fnum = Circuit1->file; break; case CIRCUIT2_IDX: if (Circuit2 == NULL) { Tcl_SetResult(interp, "No circuit has been" " declared for comparison\n", NULL); return TCL_ERROR; } tp = Circuit2; fnum = Circuit2->file; break; case CURRENT_IDX: if (CurrentCell == NULL) { Tcl_SetResult(interp, "No current cell\n", NULL); return TCL_ERROR; } tp = CurrentCell; fnum = CurrentCell->file; break; case WILDCARD_IDX: Tcl_SetResult(interp, "Wildcards must be used with " "a valid cellname\n", NULL); return TCL_ERROR; } } else { Tcl_ResetResult(interp); /* Check if it is a file number */ result = Tcl_GetIntFromObj(interp, objv, &fnum); if (result != TCL_OK) { Tcl_ResetResult(interp); /* Only one name, which is a cellname. If not a */ /* top-level cell, then it should be a unique name. */ filename = Tcl_GetString(objv); tp = LookupCell(filename); if (tp == NULL) { Tcl_SetResult(interp, "No such cell!\n", NULL); return TCL_ERROR; } if (tp->flags & CELL_TOP) fnum = tp->file; else fnum = -1; // Use wildcard } else { /* Given a file number, need to find the top-level cell */ tp = GetTopCell(fnum); if (tp == NULL) { Tcl_SetResult(interp, "No such file number!\n", NULL); return TCL_ERROR; } } } } *tpr = tp; *fnumptr = fnum; return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_canonical */ /* Syntax: netgen::canonical */ /* Formerly: (none) */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_canonical(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { int result; struct nlist *np; int filenum; Tcl_Obj *lobj; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_filename"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &filenum); if (result != TCL_OK) return result; lobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(np->name, -1)); Tcl_ListObjAppendElement(interp, lobj, Tcl_NewIntObj(filenum)); Tcl_SetObjResult(interp, lobj); return TCL_OK; } /*------------------------------------------------------*/ /* The following code breaks up the Query() command */ /* from query.c into individual functions w/arguments */ /*------------------------------------------------------*/ /*------------------------------------------------------*/ /* Function name: _netgen_readnet */ /* Syntax: netgen::readnet [format] [] */ /* Formerly: read r, K, Z, G, and S */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_readnet(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *formats[] = { "automatic", "ext", "extflat", "sim", "ntk", "spice", "verilog", "netgen", "actel", "xilinx", NULL }; enum FormatIdx { AUTO_IDX, EXT_IDX, EXTFLAT_IDX, SIM_IDX, NTK_IDX, SPICE_IDX, VERILOG_IDX, NETGEN_IDX, ACTEL_IDX, XILINX_IDX }; struct nlist *tc; int result, index, filenum = -1; char *retstr = NULL, *savstr = NULL; if (objc > 1) { /* If last argument is a number, then force file to belong to */ /* the same netlist as everything else in "filenum". */ if (Tcl_GetIntFromObj(interp, objv[objc - 1], &filenum) != TCL_OK) { Tcl_ResetResult(interp); filenum = -1; } else if (filenum < 0) { Tcl_SetResult(interp, "Cannot use negative file number!", NULL); return TCL_ERROR; } else { objc--; } } if (objc == 1 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "?format? file ?filenum?"); return TCL_ERROR; } else if (objc > 1) { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)formats, "format", 0, &index) != TCL_OK) { if (objc == 3) return TCL_ERROR; else { Tcl_ResetResult(interp); index = AUTO_IDX; } } } switch (index) { case ACTEL_IDX: case XILINX_IDX: if (objc != 2) { Fprintf(stderr, "Warning: argument \"%s\" ignored. Reading %s library.\n", Tcl_GetString(objv[2]), formats[index]); } break; case AUTO_IDX: if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "file"); return TCL_ERROR; } retstr = Tcl_GetString(objv[1]); break; default: if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "format file"); return TCL_ERROR; } retstr = Tcl_GetString(objv[2]); break; } if (retstr) savstr = STRDUP(retstr); // Check if the file is already loaded. tc = LookupCell(savstr); if (tc != NULL) { if ((filenum != -1) && (filenum != tc->file)) { Tcl_SetResult(interp, "File is already loaded as a" " different file number.", NULL); return TCL_ERROR; } filenum = tc->file; } else { switch(index) { case AUTO_IDX: retstr = ReadNetlist(savstr, &filenum); break; case EXT_IDX: retstr = ReadExtHier(savstr, &filenum); break; case EXTFLAT_IDX: retstr = ReadExtFlat(savstr, &filenum); break; case SIM_IDX: retstr = ReadSim(savstr, &filenum); break; case NTK_IDX: retstr = ReadNtk(savstr, &filenum); break; case SPICE_IDX: retstr = ReadSpice(savstr, &filenum); break; case VERILOG_IDX: retstr = ReadVerilog(savstr, &filenum); break; case NETGEN_IDX: retstr = ReadNetgenFile(savstr, &filenum); break; case ACTEL_IDX: ActelLib(); retstr = formats[index]; break; case XILINX_IDX: XilinxLib(); retstr = formats[index]; break; } } /* Return the file number to the interpreter */ Tcl_SetObjResult(interp, Tcl_NewIntObj(filenum)); if (savstr) FREE(savstr); return (retstr == NULL) ? TCL_ERROR : TCL_OK; } /*--------------------------------------------------------*/ /* Function name: _netgen_readlib */ /* Syntax: netgen::readlib [] [] */ /* Formerly: read X, A */ /* Results: */ /* Side Effects: */ /*--------------------------------------------------------*/ int _netgen_readlib(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *formats[] = { "actel", "spice", "xilinx", NULL }; enum FormatIdx { ACTEL_IDX, SPICE_IDX, XILINX_IDX }; int result, index, fnum = -1; char *repstr; if (objc > 1) { /* If last argument is a number, then force file to belong to */ /* the same netlist as everything else in "fnum". */ if (Tcl_GetIntFromObj(interp, objv[objc - 1], &fnum) != TCL_OK) { Tcl_ResetResult(interp); fnum = -1; } else if (fnum < 0) { Tcl_SetResult(interp, "Cannot use negative file number!", NULL); return TCL_ERROR; } else { objc--; } } if (objc == 1 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "format [file]"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)formats, "format", 0, &index) != TCL_OK) { return TCL_ERROR; } switch(index) { case ACTEL_IDX: case XILINX_IDX: if (objc == 3) { Tcl_WrongNumArgs(interp, 1, objv, "actel | xilinx"); return TCL_ERROR; } break; case SPICE_IDX: if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "spice file"); return TCL_ERROR; } break; } switch(index) { case ACTEL_IDX: ActelLib(); break; case SPICE_IDX: repstr = Tcl_GetString(objv[2]); ReadSpiceLib(repstr, &fnum); break; case XILINX_IDX: XilinxLib(); break; } Tcl_SetObjResult(interp, Tcl_NewIntObj(fnum)); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_writenet */ /* Syntax: netgen::write format cellname [filenum] */ /* Formerly: k, x, z, w, o, g, s, E, and C */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_writenet(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *formats[] = { "ext", "sim", "ntk", "actel", "spice", "verilog", "wombat", "esacap", "netgen", "ccode", "xilinx", NULL }; enum FormatIdx { EXT_IDX, SIM_IDX, NTK_IDX, ACTEL_IDX, SPICE_IDX, VERILOG_IDX, WOMBAT_IDX, ESACAP_IDX, NETGEN_IDX, CCODE_IDX, XILINX_IDX }; int result, index, filenum; char *repstr; if (objc != 3 && objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "format file"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)formats, "format", 0, &index) != TCL_OK) { return TCL_ERROR; } repstr = Tcl_GetString(objv[2]); if (objc == 4) { result = Tcl_GetIntFromObj(interp, objv[3], &filenum); if (result != TCL_OK) return result; } else filenum = -1; switch(index) { case EXT_IDX: Ext(repstr, filenum); break; case SIM_IDX: Sim(repstr, filenum); break; case NTK_IDX: Ntk(repstr,""); break; case ACTEL_IDX: if (ActelLibPresent() == 0) { Fprintf(stderr, "Warning: Actel library was not loaded.\n"); Fprintf(stderr, "Try \"readlib actel\" before reading the netlist.\n"); } Actel(repstr,""); break; case SPICE_IDX: SpiceCell(repstr, filenum, ""); break; case VERILOG_IDX: VerilogModule(repstr, filenum, ""); break; case WOMBAT_IDX: Wombat(repstr,NULL); break; case ESACAP_IDX: EsacapCell(repstr,""); break; case NETGEN_IDX: WriteNetgenFile(repstr,""); break; case CCODE_IDX: Ccode(repstr,""); break; case XILINX_IDX: if (XilinxLibPresent() == 0) { Fprintf(stderr, "Warning: Xilinx library was not loaded.\n"); Fprintf(stderr, "Try \"readlib xilinx\" before reading the netlist.\n"); } Xilinx(repstr,""); break; } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_flatten */ /* Syntax: netgen::flatten mode */ /* Formerly: f and F */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_flatten(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr, *file; int result, llen, filenum; struct nlist *tp, *tp2; if ((objc < 2) || (objc > 4)) { Tcl_WrongNumArgs(interp, 1, objv, "?class? valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[objc - 1], &tp, &filenum); if (result != TCL_OK) return result; repstr = tp->name; if (objc >= 3) { char *argv = Tcl_GetString(objv[1]); if (!strcmp(argv, "class")) { tp = GetTopCell(filenum); if (objc == 4) { int numflat; tp2 = LookupCellFile(Tcl_GetString(objv[2]), filenum); if (tp2 == NULL) { Tcl_SetResult(interp, "No such cell.", NULL); return TCL_ERROR; } else { Printf("Flattening instances of %s in cell %s within file %s\n", repstr, tp2->name, tp->name); numflat = flattenInstancesOf(tp2->name, filenum, repstr); if (numflat == 0) { Tcl_SetResult(interp, "No instances found to flatten.", NULL); return TCL_ERROR; } } } else { Printf("Flattening instances of %s in file %s\n", repstr, tp->name); FlattenInstancesOf(repstr, filenum); } } else { Tcl_WrongNumArgs(interp, 1, objv, "class valid_cellname"); return TCL_ERROR; } } else { Printf("Flattening contents of cell %s\n", repstr); Flatten(repstr, filenum); } return TCL_OK; } /*--------------------------------------------------------------*/ /* Function name: _netgen_nodes */ /* Syntax: netgen::nodes [-list ] [] */ /* Formerly: n and N */ /* Results: */ /* Side Effects: */ /*--------------------------------------------------------------*/ int _netgen_nodes(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *estr = NULL, *istr = NULL, *cstr, *fstr; char *optstart; int dolist = 0; int fnum, result; struct nlist *np = NULL; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if ((objc < 1 || objc > 3) || (objc == 2)) { Tcl_WrongNumArgs(interp, 1, objv, "?element? ?cell file?"); return TCL_ERROR; } if (objc == 1) { if (CurrentCell == NULL) { Tcl_WrongNumArgs(interp, 1, objv, "(cell name required)"); return TCL_ERROR; } cstr = CurrentCell->name; fnum = CurrentCell->file; } else { result = CommonParseCell(interp, objv[objc - 1], &np, &fnum); if (result != TCL_OK) return result; cstr = np->name; // If element was specified: if (objc == 3) estr = Tcl_GetString(objv[objc - 2]); } if (estr) { if (*estr != '/') { istr = (char *)Tcl_Alloc(strlen(estr) + 2); sprintf(istr, "/%s", estr); estr = istr; } } if (estr) { if (dolist) { struct objlist *ob, *nob; Tcl_Obj *lobj, *pobj; int ckto; if (np == NULL) np = LookupCellFile(cstr, fnum); if (np == NULL) { Tcl_SetResult(interp, "No such cell.", NULL); if (istr) Tcl_Free(istr); return TCL_ERROR; } ckto = strlen(estr); for (ob = np->cell; ob != NULL; ob = ob->next) { if (!strncmp(estr, ob->name, ckto)) { if (*(ob->name + ckto) == '/' || *(ob->name + ckto) == '\0') break; } } if (ob == NULL) { Tcl_SetResult(interp, "No such element.", NULL); if (istr) Tcl_Free(istr); return TCL_ERROR; } lobj = Tcl_NewListObj(0, NULL); for (; ob != NULL; ob = ob->next) { if (!strncmp(estr, ob->name, ckto)) { if (*(ob->name + ckto) != '/' && *(ob->name + ckto) != '\0') continue; pobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, pobj, Tcl_NewStringObj(ob->name + ckto + 1, -1)); for (nob = np->cell; nob != NULL; nob = nob->next) { if (nob->node == ob->node) { if (nob->type < FIRSTPIN) { Tcl_ListObjAppendElement(interp, pobj, Tcl_NewStringObj(nob->name, -1)); break; } } } Tcl_ListObjAppendElement(interp, lobj, pobj); } } Tcl_SetObjResult(interp, lobj); } else ElementNodes(cstr, estr, fnum); } else PrintNodes(cstr, fnum); if (istr) Tcl_Free(istr); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_elements */ /* Syntax: netgen::elements [-list ] [] */ /* Formerly: e */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_elements(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *nstr = NULL, *cstr; struct objlist * (*ListSave)(); char *optstart; int dolist = 0; int fnum = -1; int result; struct nlist *np = NULL; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if (objc < 1 || objc > 2) { Tcl_WrongNumArgs(interp, 1, objv, "?node? valid_cellname"); return TCL_ERROR; } if (objc == 1) { if (CurrentCell == NULL) { Tcl_WrongNumArgs(interp, 1, objv, "(cell name required)"); return TCL_ERROR; } cstr = CurrentCell->name; } else { result = CommonParseCell(interp, objv[objc - 1], &np, &fnum); if (result != TCL_OK) return result; cstr = np->name; if (objc == 3) nstr = Tcl_GetString(objv[1]); } if (nstr) { if (dolist) { struct objlist *ob; Tcl_Obj *lobj; int nodenum; if (np == NULL) np = LookupCellFile(cstr, fnum); if (np == NULL) { Tcl_SetResult(interp, "No such cell.", NULL); return TCL_ERROR; } for (ob = np->cell; ob != NULL; ob = ob->next) { if (match(nstr, ob->name)) { nodenum = ob->node; break; } } if (ob == NULL) { Tcl_SetResult(interp, "No such node.", NULL); return TCL_ERROR; } lobj = Tcl_NewListObj(0, NULL); for (ob = np->cell; ob != NULL; ob = ob->next) { if (ob->node == nodenum && ob->type >= FIRSTPIN) { char *obname = ob->name; if (*obname == '/') obname++; Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(obname, -1)); } } Tcl_SetObjResult(interp, lobj); } else Fanout(cstr, nstr, ALLELEMENTS); } else { PrintAllElements(cstr, fnum); } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_debug */ /* Syntax: netgen::debug [on|off] or debug command */ /* Formerly: D */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_debug(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *yesno[] = { "on", "off", NULL }; enum OptionIdx { YES_IDX, NO_IDX, CMD_IDX }; int result, index; char *command; if (objc == 1) index = YES_IDX; else { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)yesno, "option", 0, &index) != TCL_OK) { index = CMD_IDX; } } switch(index) { case YES_IDX: Debug = TRUE; break; case NO_IDX: Debug = FALSE; break; case CMD_IDX: /* Need to redefine DBUG_PUSH! */ command = Tcl_GetString(objv[1]); DBUG_PUSH(command); } if (index != CMD_IDX) Printf("Debug mode is %s\n", Debug?"ON":"OFF"); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_protochip */ /* Syntax: netgen::protochip */ /* Formerly: P */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_protochip(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } PROTOCHIP(); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_instances */ /* Syntax: netgen::instances valid_cellname */ /* Formerly: i */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_instances(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr; int result; int fnum = -1; struct nlist *np = NULL; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &fnum); if (result != TCL_OK) return result; repstr = np->name; PrintInstances(repstr, fnum); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_contents */ /* Syntax: netgen::contents valid_cellname */ /* Formerly: c */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_contents(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr; int result; int fnum = -1; struct nlist *np = NULL; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &fnum); if (result != TCL_OK) return result; repstr = np->name; PrintCell(repstr, fnum); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_describe */ /* Syntax: netgen::describe valid_cellname */ /* Formerly: d */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_describe(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr; int file = -1; int result; struct nlist *np = NULL; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &file); if (result != TCL_OK) return result; repstr = np->name; DescribeInstance(repstr, file); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_cells */ /* Syntax: netgen::cells [list|all] [valid_filename] */ /* Formerly: h and H */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_cells(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr, *filename = NULL; char *optstart; int filenum = -1; struct nlist *np = NULL; int result, printopt, dolist = 0, doall = 0, dotop = 0; while (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } else if (!strcmp(optstart, "all")) { doall = 1; objv++; objc--; } else if (!strcmp(optstart, "top")) { dotop = 1; objv++; objc--; } else { result = CommonParseCell(interp, objv[1], &np, &filenum); if (result != TCL_OK) return result; objv++; objc--; } } if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "[list] [-top] [-all] [valid_filename]"); return TCL_ERROR; } else { Tcl_Obj *lobj; if (dotop) { if (dolist) lobj = Tcl_NewListObj(0, NULL); else Fprintf(stdout, "Top level cells: "); np = FirstCell(); while (np != NULL) { if ((np->flags & CELL_TOP) && ((filenum == -1) || (np->file == filenum))) { if (dolist) Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(np->name, -1)); else Fprintf(stdout, "%s ", np->name); } np = NextCell(); } if (dolist) Tcl_SetObjResult(interp, lobj); else Fprintf(stdout, "\n"); return TCL_OK; } else { if (dolist) printopt = (doall) ? 3 : 2; else printopt = (doall) ? 1 : 0; PrintCellHashTable(printopt, filenum); } } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_model */ /* Syntax: netgen::model valid_cellname class */ /* Formerly: (nothing) */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_model(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { struct nlist *tp; char *model, *retclass; unsigned char class; int fnum = -1; int result, index, nports; char *modelclasses[] = { "undefined", "nmos", "pmos", "pnp", "npn", "resistor", "capacitor", "diode", "inductor", "module", "blackbox", "xline", "moscap", "mosfet", "bjt", "subcircuit", NULL }; enum OptionIdx { UNDEF_IDX, NMOS_IDX, PMOS_IDX, PNP_IDX, NPN_IDX, RES_IDX, CAP_IDX, DIODE_IDX, INDUCT_IDX, MODULE_IDX, BLACKBOX_IDX, XLINE_IDX, MOSCAP_IDX, MOSFET_IDX, BJT_IDX, SUBCKT_IDX }; if (objc != 3 && objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname [class]"); return TCL_ERROR; } /* Check for "model blackbox on|off" */ /* Behavior is to treat empty subcircuits as blackbox cells */ if ((objc > 1) && !strcmp(Tcl_GetString(objv[1]), "blackbox")) { if ((objc > 2) && !strcmp(Tcl_GetString(objv[2]), "on")) { auto_blackbox = TRUE; return TCL_OK; } else if ((objc > 2) && !strcmp(Tcl_GetString(objv[2]), "off")) { auto_blackbox = FALSE; return TCL_OK; } else if (objc == 2) { Tcl_SetObjResult(interp, Tcl_NewBooleanObj(auto_blackbox)); return TCL_OK; } } result = CommonParseCell(interp, objv[1], &tp, &fnum); if (result != TCL_OK) return result; if (objc == 3) { model = Tcl_GetString(objv[2]); nports = NumberOfPorts(model); if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)modelclasses, "class", 0, &index) != TCL_OK) { return TCL_ERROR; } switch (index) { case UNDEF_IDX: class = CLASS_UNDEF; break; case NMOS_IDX: if (nports != 4 && nports != 3) goto wrongNumPorts; class = (nports == 4) ? CLASS_NMOS4 : CLASS_NMOS; break; case PMOS_IDX: if (nports != 4 && nports != 3) goto wrongNumPorts; class = (nports == 4) ? CLASS_PMOS4 : CLASS_PMOS; break; case PNP_IDX: if (nports != 3) goto wrongNumPorts; class = CLASS_PNP; break; case NPN_IDX: if (nports != 3) goto wrongNumPorts; class = CLASS_NPN; break; case RES_IDX: if (nports != 2 && nports != 3) goto wrongNumPorts; class = (nports == 2) ? CLASS_RES : CLASS_RES3; break; case CAP_IDX: if (nports != 2 && nports != 3) goto wrongNumPorts; class = (nports == 2) ? CLASS_CAP : CLASS_CAP3; break; case DIODE_IDX: if (nports != 2) goto wrongNumPorts; class = CLASS_DIODE; break; case INDUCT_IDX: if (nports != 2) goto wrongNumPorts; class = CLASS_INDUCTOR; break; case XLINE_IDX: if (nports != 4) goto wrongNumPorts; class = CLASS_XLINE; break; case BJT_IDX: if (nports != 3) goto wrongNumPorts; class = CLASS_BJT; break; case MOSFET_IDX: if (nports != 4 && nports != 3) goto wrongNumPorts; class = (nports == 4) ? CLASS_FET4 : CLASS_FET; break; case MOSCAP_IDX: if (nports != 3) goto wrongNumPorts; class = CLASS_ECAP; break; case MODULE_IDX: case BLACKBOX_IDX: class = CLASS_MODULE; break; case SUBCKT_IDX: class = CLASS_SUBCKT; break; } tp->class = class; } else { class = tp->class; switch (class) { case CLASS_NMOS: case CLASS_NMOS4: retclass = modelclasses[NMOS_IDX]; break; case CLASS_PMOS: case CLASS_PMOS4: retclass = modelclasses[PMOS_IDX]; break; case CLASS_FET3: case CLASS_FET4: case CLASS_FET: retclass = "mosfet"; break; case CLASS_BJT: retclass = "bipolar"; break; case CLASS_NPN: retclass = modelclasses[NPN_IDX]; break; case CLASS_PNP: retclass = modelclasses[PNP_IDX]; break; case CLASS_RES: case CLASS_RES3: retclass = modelclasses[RES_IDX]; break; case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3: retclass = modelclasses[CAP_IDX]; break; case CLASS_SUBCKT: retclass = modelclasses[SUBCKT_IDX]; break; case CLASS_MODULE: if (auto_blackbox) retclass = modelclasses[BLACKBOX_IDX]; else retclass = modelclasses[MODULE_IDX]; break; default: /* (includes case CLASS_UNDEF) */ retclass = modelclasses[UNDEF_IDX]; break; } Tcl_SetResult(interp, retclass, NULL); } return TCL_OK; wrongNumPorts: Tcl_SetResult(interp, "Wrong number of ports for device", NULL); return TCL_ERROR; } /*------------------------------------------------------*/ /* Function name: _netgen_ports */ /* Syntax: netgen::ports cell */ /* Formerly: p */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_ports(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr; int result; struct nlist *np; int filenum = -1; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &filenum); if (result != TCL_OK) return result; repstr = np->name; PrintPortsInCell(repstr, filenum); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_leaves */ /* Syntax: netgen::leaves [valid_cellname] */ /* Formerly: l and L */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_leaves(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *repstr; int result; int filenum = -1; struct nlist *np; if (objc != 1 && objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "[valid_cellname]"); return TCL_ERROR; } if (objc == 1) { Printf("List of all leaf cells:\n"); PrintAllLeaves(); } else { result = CommonParseCell(interp, objv[1], &np, &filenum); if (result != TCL_OK) return result; repstr = np->name; ClearDumpedList(); PrintLeavesInCell(repstr, filenum); } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_quit */ /* Syntax: netgen::quit */ /* Formerly: q and Q */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_quit(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } /* Call tkcon's exit routine, which will make sure */ /* the history file is updated before final exit. */ if (consoleinterp == interp) Tcl_Exit(TCL_OK); else Tcl_Eval(interp, "catch {tkcon eval exit}\n"); return TCL_OK; /* Not reached */ } /*------------------------------------------------------*/ /* Function name: _netgen_reinit */ /* Syntax: netgen::reinitialize */ /* Formerly: I */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_reinit(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } Initialize(); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netgen_log */ /* Syntax: netgen::log [option...] */ /* Formerly: (xnetgen command only) */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_log(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *yesno[] = { "start", "end", "reset", "suspend", "resume", "file", "echo", "put", NULL }; enum OptionIdx { START_IDX, END_IDX, RESET_IDX, SUSPEND_IDX, RESUME_IDX, FILE_IDX, ECHO_IDX, PUT_IDX }; int result, index, i; char *tmpstr; FILE *file; if (objc == 1) { index = (LoggingFile) ? RESUME_IDX : START_IDX; } else { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)yesno, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } } switch(index) { case START_IDX: case RESUME_IDX: if (LoggingFile) { Tcl_SetResult(interp, "Already logging output.", NULL); return TCL_ERROR; } break; case END_IDX: case RESET_IDX: case SUSPEND_IDX: if (!LoggingFile) { Tcl_SetResult(interp, "Not logging data.", NULL); return TCL_ERROR; } /* Don't leave echo off if we're stopping the log */ if (NoOutput) NoOutput = FALSE; break; } switch(index) { case START_IDX: case RESUME_IDX: case RESET_IDX: if (LogFileName == NULL) { Tcl_SetResult(interp, "No log file declared. " "Use \"log file \"", NULL); return TCL_ERROR; } break; } switch(index) { case START_IDX: LoggingFile = fopen(LogFileName, "w"); break; case RESUME_IDX: LoggingFile = fopen(LogFileName, "a"); break; case END_IDX: fclose(LoggingFile); LoggingFile = FALSE; break; case RESET_IDX: fclose(LoggingFile); LoggingFile = fopen(LogFileName, "w"); break; case SUSPEND_IDX: fclose(LoggingFile); LoggingFile = FALSE; break; case FILE_IDX: if (objc == 2) Tcl_SetResult(interp, LogFileName, NULL); else { if (LoggingFile) { fclose(LoggingFile); LoggingFile = FALSE; Printf("Closed old log file \"%s\".\n", LogFileName); } tmpstr = Tcl_GetString(objv[2]); if (LogFileName) Tcl_Free(LogFileName); LogFileName = (char *)Tcl_Alloc(1 + strlen(tmpstr)); strcpy(LogFileName, tmpstr); } break; case PUT_IDX: // All arguments after "log put" get sent to stdout through Tcl, // and also to the logfile, if the logfile is enabled. for (i = 2; i < objc; i++) { Fprintf(stdout, Tcl_GetString(objv[i])); } if (!NoOutput) Printf("\n"); return TCL_OK; case ECHO_IDX: if (objc == 2) { Tcl_SetResult(interp, (NoOutput) ? "off" : "on", NULL); } else { int bval; result = Tcl_GetBooleanFromObj(interp, objv[2], &bval); if (result == TCL_OK) NoOutput = (bval) ? FALSE : TRUE; else return result; } if (Debug) Printf("Echoing log file \"%s\" output to console %s\n", LogFileName, (NoOutput) ? "disabled" : "enabled"); return TCL_OK; } if ((index != FILE_IDX) && (index != ECHO_IDX)) Printf("Logging to file \"%s\" %s\n", LogFileName, (LoggingFile) ? "enabled" : "disabled"); return TCL_OK; } #ifdef HAVE_MALLINFO /*------------------------------------------------------*/ /* Function name: _netgen_printmem */ /* Syntax: netgen::memory */ /* Formerly: m */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netgen_printmem(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } PrintMemoryStats(); return TCL_OK; } #endif /*------------------------------------------------------*/ /* The following code breaks up the NETCOMP() command */ /* from netcmp.c into individual functions w/arguments */ /*------------------------------------------------------*/ /*------------------------------------------------------*/ /* Function name: _netcmp_compare */ /* Syntax: */ /* netgen::compare valid_cellname1 valid_cellname2 */ /* Formerly: c */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_compare(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *name1, *name2, *file1, *file2, *optstart; int fnum1, fnum2, dolist = 0; int dohierarchy = FALSE; int assignonly = FALSE; int argstart = 1, qresult, llen, result; struct Correspond *nextcomp; struct nlist *tp; Tcl_Obj *flist = NULL; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if (objc > 1) { if (!strncmp(Tcl_GetString(objv[argstart]), "assign", 6)) { assignonly = TRUE; argstart++; } else if (!strncmp(Tcl_GetString(objv[argstart]), "hier", 4)) { dohierarchy = TRUE; argstart++; } } fnum1 = -1; fnum2 = -1; if (((objc - argstart) == 2) || ((dohierarchy && ((objc - argstart) == 0)))) { if (dohierarchy && ((objc - argstart) == 0)) { qresult = GetCompareQueueTop(&name1, &fnum1, &name2, &fnum2); if (qresult == -1) { Tcl_Obj *lobj; // When queue is empty, return a null list lobj = Tcl_NewListObj(0, NULL); Tcl_SetObjResult(interp, lobj); return TCL_OK; } } else if ((objc - argstart) == 2) { result = CommonParseCell(interp, objv[argstart], &tp, &fnum1); if (result != TCL_OK) return TCL_ERROR; else if (fnum1 == -1) { Tcl_SetResult(interp, "Cannot use wildcard with compare command.\n", NULL); return TCL_ERROR; } name1 = tp->name; argstart++; result = CommonParseCell(interp, objv[argstart], &tp, &fnum2); if (result != TCL_OK) return TCL_ERROR; else if (fnum2 == -1) { Tcl_SetResult(interp, "Cannot use wildcard with compare command.\n", NULL); return TCL_ERROR; } name2 = tp->name; if (dohierarchy) { RemoveCompareQueue(); qresult = CreateCompareQueue(name1, fnum1, name2, fnum2); if (qresult != 0) { Tcl_AppendResult(interp, "No such cell ", (qresult == 1) ? name1 : name2, NULL); return TCL_ERROR; } GetCompareQueueTop(&name1, &fnum1, &name2, &fnum2); } else if (assignonly) { AssignCircuits(name1, fnum1, name2, fnum2); return TCL_OK; } } } else { Tcl_WrongNumArgs(interp, 1, objv, "[hierarchical] valid_cellname1 valid_cellname2"); return TCL_ERROR; } if (fnum1 == fnum2) { Tcl_SetResult(interp, "Cannot compare two cells in the same netlist.", NULL); return TCL_ERROR; } UniquePins(name1, fnum1); // Check for and remove duplicate pins UniquePins(name2, fnum2); // Check for and remove duplicate pins // Resolve global nodes into local nodes and ports if (dohierarchy) { ConvertGlobals(name1, fnum1); ConvertGlobals(name2, fnum2); } CreateTwoLists(name1, fnum1, name2, fnum2, dolist); while (PrematchLists(name1, fnum1, name2, fnum2) > 0) { Fprintf(stdout, "Making another compare attempt.\n"); CreateTwoLists(name1, fnum1, name2, fnum2, dolist); } // Return the names of the two cells being compared, if doing "compare // hierarchical". If "-list" was specified, then append the output // to the end of the list. if (dohierarchy) { Tcl_Obj *lobj; lobj = Tcl_NewListObj(0, NULL); Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(name1, -1)); Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(name2, -1)); Tcl_SetObjResult(interp, lobj); } #ifdef DEBUG_ALLOC PrintCoreStats(); #endif /* Arrange properties in the two compared cells */ /* ResolveProperties(name1, fnum1, name2, fnum2); */ Permute(); /* Apply permutations */ return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_iterate */ /* Syntax: netgen::iterate */ /* Formerly: i */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_iterate(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } if (!Iterate()) Printf("Please iterate again.\n"); else Printf("No fractures made: we're done.\n"); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_summary */ /* Syntax: netgen::summary [elements|nodes] */ /* Formerly: s */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_summary(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "nodes", "elements", NULL }; enum OptionIdx { NODE_IDX, ELEM_IDX }; int result, index = -1; if (objc != 1 && objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "?nodes|elements?"); return TCL_ERROR; } if (objc == 2) { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } } if (objc == 1 || index == ELEM_IDX) SummarizeElementClasses(ElementClasses); if (objc == 1 || index == NODE_IDX) SummarizeNodeClasses(NodeClasses); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_print */ /* Syntax: netgen::print [elements|nodes|queue] */ /* [legal|illegal] */ /* Formerly: P */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_print(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "nodes", "elements", "queue", NULL }; enum OptionIdx { NODE_IDX, ELEM_IDX, QUEUE_IDX }; /* Note: The order is such that the type passed to PrintElementClasses() * or PrintNodeClasses() is -1 for all, 0 for legal, and 1 for illegal */ char *classes[] = { "legal", "illegal", NULL }; enum ClassIdx { LEGAL_IDX, ILLEGAL_IDX }; int result, index = -1, class = -1, dolist = 0; int fnum1, fnum2; char *optstart; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if (objc < 1 || objc > 3) { Tcl_WrongNumArgs(interp, 1, objv, "?nodes|elements|queue? ?legal|illegal?"); return TCL_ERROR; } if (objc >= 2) { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) != TCL_OK) { if ((objc == 2) && (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)classes, "class", 0, &class) != TCL_OK)) { return TCL_ERROR; } } } if (objc == 3 && index != QUEUE_IDX) { if (Tcl_GetIndexFromObj(interp, objv[2], (CONST84 char **)classes, "class", 0, &class) != TCL_OK) { return TCL_ERROR; } } else if (objc == 3) { Tcl_WrongNumArgs(interp, 1, objv, "queue [no arguments]"); return TCL_ERROR; } enable_interrupt(); if (objc == 1 || index == NODE_IDX) PrintNodeClasses(NodeClasses, class, dolist); if (objc == 1 || index == ELEM_IDX) PrintElementClasses(ElementClasses, class, dolist); if (objc == 2 && index == QUEUE_IDX) { char *name1, *name2; Tcl_Obj *lobj; result = PeekCompareQueueTop(&name1, &fnum1, &name2, &fnum2); lobj = Tcl_NewListObj(0, NULL); if (result != -1) { Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(name1, -1)); Tcl_ListObjAppendElement(interp, lobj, Tcl_NewStringObj(name2, -1)); } Tcl_SetObjResult(interp, lobj); } disable_interrupt(); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_run */ /* Syntax: netgen::run [converge|resolve] */ /* Formerly: r and R */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_run(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "converge", "resolve", NULL }; enum OptionIdx { CONVERGE_IDX, RESOLVE_IDX }; int result, index; int automorphisms; char *optstart; int dolist; dolist = 0; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if (objc == 1) index = RESOLVE_IDX; else { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } } switch(index) { case CONVERGE_IDX: if (ElementClasses == NULL || NodeClasses == NULL) { return TCL_OK; } else { enable_interrupt(); while (!Iterate() && !InterruptPending); if (dolist) { result = _netcmp_verify(clientData, interp, 2, objv - 1); } else result = _netcmp_verify(clientData, interp, 1, NULL); disable_interrupt(); if (result != TCL_OK) return result; } break; case RESOLVE_IDX: if (ElementClasses == NULL || NodeClasses == NULL) { // Printf("Must initialize data structures first.\n"); // return TCL_ERROR; return TCL_OK; } else { enable_interrupt(); while (!Iterate() && !InterruptPending); automorphisms = VerifyMatching(); if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); else if (automorphisms == 0) Fprintf(stdout, "Netlists match uniquely.\n"); else { // First try to resolve automorphisms uniquely using // property matching automorphisms = ResolveAutomorphsByProperty(); if (automorphisms == 0) Fprintf(stdout, "Netlists match uniquely.\n"); else { // Next, attempt to resolve automorphisms uniquely by // using the pin names automorphisms = ResolveAutomorphsByPin(); } if (automorphisms == 0) Fprintf(stdout, "Netlists match uniquely.\n"); else // Anything left is truly indistinguishable Fprintf(stdout, "Netlists match with %d symmetr%s.\n", automorphisms, (automorphisms == 1) ? "y" : "ies"); while ((automorphisms = ResolveAutomorphisms()) > 0); if (automorphisms == -1) Fprintf(stdout, "Netlists do not match.\n"); else Fprintf(stdout, "Circuits match correctly.\n"); } if (PropertyErrorDetected) { Fprintf(stdout, "There were property errors.\n"); PrintPropertyResults(dolist); } disable_interrupt(); } break; } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_verify */ /* Syntax: netgen::verify [option] */ /* options: nodes, elements, only, all, */ /* equivalent, or unique. */ /* option "-list" may be used with nodes, elements */ /* all, or no option. */ /* Formerly: v */ /* Results: */ /* For only, equivalent, unique: Return 1 if */ /* verified, zero if not. */ /* Side Effects: */ /* For options elements, nodes, and all without */ /* option -list: Write output to log file. */ /* For -list options, append list to global */ /* variable "lvs_out", if it exists. */ /*------------------------------------------------------*/ int _netcmp_verify(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "nodes", "elements", "properties", "only", "all", "equivalent", "unique", NULL }; enum OptionIdx { NODE_IDX, ELEM_IDX, PROP_IDX, ONLY_IDX, ALL_IDX, EQUIV_IDX, UNIQUE_IDX }; char *optstart; int result, index = -1; int automorphisms; int dolist = 0; Tcl_Obj *egood, *ebad, *ngood, *nbad; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; egood = ngood = NULL; ebad = nbad = NULL; objv++; objc--; } } if (objc != 1 && objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "?nodes|elements|only|all|equivalent|unique?"); return TCL_ERROR; } if (objc == 2) { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } } if (ElementClasses == NULL || NodeClasses == NULL) { if (index == EQUIV_IDX || index == UNIQUE_IDX) Tcl_SetObjResult(interp, Tcl_NewIntObj(-1)); else if (CurrentCell != NULL) Fprintf(stdout, "Verify: cell %s has no elements and/or nodes." " Not checked.\n", CurrentCell->name); else Fprintf(stdout, "Verify: no current cell to verify.\n"); return TCL_OK; } else { automorphisms = VerifyMatching(); if (automorphisms == -1) { enable_interrupt(); if (objc == 1 || index == NODE_IDX || index == ALL_IDX) { if (Debug == TRUE) PrintIllegalNodeClasses(); // Old style else { FormatIllegalNodeClasses(); // Side-by-side, to log file if (dolist) { nbad = ListNodeClasses(FALSE); // As Tcl nested list #if 0 ngood = ListNodeClasses(TRUE); // As Tcl nested list #endif } } } if (objc == 1 || index == ELEM_IDX || index == ALL_IDX) { if (Debug == TRUE) PrintIllegalElementClasses(); // Old style else { FormatIllegalElementClasses(); // Side-by-side, to log file if (dolist) { ebad = ListElementClasses(FALSE); // As Tcl nested list #if 0 egood = ListElementClasses(TRUE); // As Tcl nested list #endif } } } disable_interrupt(); if (index == EQUIV_IDX || index == UNIQUE_IDX) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); else Fprintf(stdout, "Netlists do not match.\n"); } else { if (automorphisms) { if (index == EQUIV_IDX) Tcl_SetObjResult(interp, Tcl_NewIntObj((int)automorphisms)); else if (index == UNIQUE_IDX) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); else Printf("Circuits match with %d symmetr%s.\n", automorphisms, (automorphisms == 1) ? "y" : "ies"); } else { if ((index == EQUIV_IDX) || (index == UNIQUE_IDX)) { if (PropertyErrorDetected == 0) Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); else Tcl_SetObjResult(interp, Tcl_NewIntObj(2)); } else { Fprintf(stdout, "Circuits match uniquely.\n"); if (PropertyErrorDetected != 0) Fprintf(stdout, "Property errors were found.\n"); } } #if 0 if (dolist) { ngood = ListNodeClasses(TRUE); // As Tcl nested list egood = ListElementClasses(TRUE); // As Tcl nested list } #endif if ((index == PROP_IDX) && (PropertyErrorDetected != 0)) { PrintPropertyResults(dolist); } } } /* If "dolist" has been specified, then return the */ /* list-formatted output. For "verify nodes" or */ /* "verify elements", return the associated list. */ /* For "verify" or "verify all", return a nested */ /* list of {node list, element list}. */ if (dolist) { if (objc == 1 || index == NODE_IDX || index == ALL_IDX) { if (nbad == NULL) nbad = Tcl_NewListObj(0, NULL); Tcl_SetVar2Ex(interp, "lvs_out", NULL, Tcl_NewStringObj("badnets", -1), TCL_APPEND_VALUE | TCL_LIST_ELEMENT); Tcl_SetVar2Ex(interp, "lvs_out", NULL, nbad, TCL_APPEND_VALUE | TCL_LIST_ELEMENT); #if 0 if (ngood == NULL) ngood = Tcl_NewListObj(0, NULL); Tcl_SetVar2Ex(interp, "lvs_out", NULL, Tcl_NewStringObj("goodnets", -1), TCL_APPEND_VALUE | TCL_LIST_ELEMENT); Tcl_SetVar2Ex(interp, "lvs_out", NULL, ngood, TCL_APPEND_VALUE | TCL_LIST_ELEMENT); #endif } if (objc == 1 || index == ELEM_IDX || index == ALL_IDX) { if (ebad == NULL) ebad = Tcl_NewListObj(0, NULL); Tcl_SetVar2Ex(interp, "lvs_out", NULL, Tcl_NewStringObj("badelements", -1), TCL_APPEND_VALUE | TCL_LIST_ELEMENT); Tcl_SetVar2Ex(interp, "lvs_out", NULL, ebad, TCL_APPEND_VALUE | TCL_LIST_ELEMENT); #if 0 if (egood == NULL) egood = Tcl_NewListObj(0, NULL); Tcl_SetVar2Ex(interp, "lvs_out", NULL, Tcl_NewStringObj("goodelements", -1), TCL_APPEND_VALUE | TCL_LIST_ELEMENT); Tcl_SetVar2Ex(interp, "lvs_out", NULL, egood, TCL_APPEND_VALUE | TCL_LIST_ELEMENT); #endif } } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_automorphs */ /* Syntax: netgen::automorphisms */ /* Formerly: a */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_automorphs(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, "(no arguments)"); return TCL_ERROR; } PrintAutomorphisms(); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_convert */ /* Syntax: netgen::convert */ /* Formerly: nonexistant function */ /* Results: none */ /* Side Effects: one or more global nodes changed to */ /* local scope and ports. */ /*------------------------------------------------------*/ int _netcmp_convert(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *cellname; int filenum = -1; int result; struct nlist *np; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname"); return TCL_ERROR; } result = CommonParseCell(interp, objv[1], &np, &filenum); if (result != TCL_OK) return result; cellname = np->name; ConvertGlobals(cellname, filenum); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_global */ /* Syntax: netgen::global */ /* Formerly: nonexistant function */ /* Results: returns number of matching nets found */ /* Side Effects: one or more nodes changed to global */ /* scope. */ /*------------------------------------------------------*/ int _netcmp_global(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *filename, *cellname, *pattern; int numchanged = 0, p, fnum, llen, result; struct nlist *tp; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, " [...]"); return TCL_ERROR; } /* Check if first argument is a file number */ result = CommonParseCell(interp, objv[1], &tp, &fnum); if (result != TCL_OK) return result; cellname = tp->name; for (p = 2; p < objc; p++) { pattern = Tcl_GetString(objv[p]); numchanged += ChangeScope(fnum, cellname, pattern, NODE, GLOBAL); } Tcl_SetObjResult(interp, Tcl_NewIntObj(numchanged)); return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_ignore */ /* Syntax: netgen::ignore [class] */ /* Formerly: no such command */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_ignore(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "class", "shorted", NULL }; enum OptionIdx { CLASS_IDX, SHORTED_IDX }; int result, index; int file = -1; struct nlist *np; char *name = NULL, *name2 = NULL; if (objc >= 3) { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) == TCL_OK) { objc--; objv++; } result = CommonParseCell(interp, objv[1], &np, &file); if (result != TCL_OK) return result; name = np->name; } else { Tcl_WrongNumArgs(interp, 1, objv, "[class] valid_cellname"); return TCL_ERROR; } switch (index) { case CLASS_IDX: IgnoreClass(name, file, IGNORE_CLASS); break; case SHORTED_IDX: IgnoreClass(name, file, IGNORE_SHORTED); break; } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_equate */ /* Syntax: netgen::equate [elements|nodes|classes|pins] */ /* Formerly: e and n */ /* Results: */ /* Side Effects: */ /*------------------------------------------------------*/ int _netcmp_equate(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { char *options[] = { "nodes", "elements", "classes", "pins", NULL }; enum OptionIdx { NODE_IDX, ELEM_IDX, CLASS_IDX, PINS_IDX }; int result, index; char *name1 = NULL, *name2 = NULL, *optstart; struct nlist *tp1, *tp2, *SaveC1, *SaveC2; struct objlist *ob1, *ob2; int file1, file2; int i, l1, l2, ltest, lent, dolist = 0; Tcl_Obj *tobj1, *tobj2, *tobj3; if (objc > 1) { optstart = Tcl_GetString(objv[1]); if (*optstart == '-') optstart++; if (!strcmp(optstart, "list")) { dolist = 1; objv++; objc--; } } if ((objc != 2) && (objc != 4) && (objc != 6)) { Tcl_WrongNumArgs(interp, 1, objv, "?nodes|elements|classes|pins? name1 name2"); return TCL_ERROR; } else { if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **)options, "option", 0, &index) != TCL_OK) { return TCL_ERROR; } } /* Something is going on here. . . */ if (index > PINS_IDX) index = PINS_IDX; /* 4-argument form only available for "equate classes", or for other */ /* options if Circuit1 and Circuit2 have been declared. */ if ((objc == 2) && (index == PINS_IDX)) { if (Circuit1 == NULL || Circuit2 == NULL) { Tcl_SetResult(interp, "Circuits not being compared, must specify netlists.", NULL); return TCL_ERROR; } tp1 = Circuit1; file1 = Circuit1->file; tp2 = Circuit2; file2 = Circuit2->file; name1 = tp1->name; name2 = tp2->name; } else if ((objc == 4) && (index != CLASS_IDX) && (index != PINS_IDX)) { if (Circuit1 == NULL || Circuit2 == NULL) { Tcl_SetResult(interp, "Circuits not being compared, must specify netlists.", NULL); return TCL_ERROR; } tp1 = Circuit1; file1 = Circuit1->file; tp2 = Circuit2; file2 = Circuit2->file; name1 = Tcl_GetString(objv[2]); name2 = Tcl_GetString(objv[3]); } else if ((objc == 4) && ((index == CLASS_IDX) || (index == PINS_IDX))) { result = CommonParseCell(interp, objv[2], &tp1, &file1); if (result != TCL_OK) { if (index == CLASS_IDX) { Fprintf(stdout, "Cell to equate does not exist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return result; } Tcl_SetResult(interp, "No such object.", NULL); return TCL_ERROR; } result = CommonParseCell(interp, objv[3], &tp2, &file2); if (result != TCL_OK) { if (index == CLASS_IDX) { Fprintf(stdout, "Cell to equate does not exist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return result; } Tcl_SetResult(interp, "No such object.", NULL); return TCL_ERROR; } if (file1 == file2) { if (index == CLASS_IDX) { Fprintf(stdout, "Cells to equate are in the same netlist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_ERROR; } Tcl_SetResult(interp, "Objects in the same netlist cannot be equated.", NULL); return TCL_ERROR; } name1 = tp1->name; name2 = tp2->name; } else if (objc == 6) { result = CommonParseCell(interp, objv[2], &tp1, &file1); if (result != TCL_OK) { if (index == CLASS_IDX) { Fprintf(stdout, "Cell to equate does not exist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); } return result; } result = CommonParseCell(interp, objv[4], &tp2, &file2); if (result != TCL_OK) { if (index == CLASS_IDX) { Fprintf(stdout, "Cell to equate does not exist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); } return result; } if (file1 == file2) { if (index == CLASS_IDX) { Tcl_ResetResult(interp); Fprintf(stdout, "Cells to equate are in the same netlist.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_ERROR; } Tcl_SetResult(interp, "Cannot equate within the same netlist!\n", NULL); return TCL_ERROR; } name1 = Tcl_GetString(objv[3]); name2 = Tcl_GetString(objv[5]); } else { Tcl_WrongNumArgs(interp, 1, objv, "?nodes|elements|classes|pins? name1 name2"); return TCL_ERROR; } switch(index) { case NODE_IDX: if (NodeClasses == NULL) { Fprintf(stderr, "Cell has no nodes.\n"); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_OK; } if (EquivalenceNodes(name1, file1, name2, file2)) { Fprintf(stdout, "Nodes %s and %s are equivalent.\n", name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); } else { Fprintf(stderr, "Unable to equate nodes %s and %s.\n",name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); } break; case ELEM_IDX: if (ElementClasses == NULL) { if (CurrentCell == NULL) Fprintf(stderr, "Equate elements: no current cell.\n"); Fprintf(stderr, "Equate elements: cell %s and/or %s has no elements.\n", name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_OK; } if (EquivalenceElements(name1, file1, name2, file2)) { Fprintf(stdout, "Elements %s and %s are equivalent.\n", name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); } else { Fprintf(stderr, "Unable to equate elements %s and %s.\n",name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); } break; case PINS_IDX: if ((ElementClasses == NULL) && (auto_blackbox == FALSE)) { if (CurrentCell == NULL) Fprintf(stderr, "Equate elements: no current cell.\n"); Fprintf(stderr, "Equate pins: cell %s and/or %s has no elements.\n", name1, name2); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_OK; } else if (ElementClasses == NULL) { /* This has been called outside of a netlist compare, */ /* probably to force name matching of pins on black-box */ /* devices. But MatchPins only works if tp1 == Circuit1 */ /* and tp2 == Circuit2, so preserve these values and */ /* recover afterward (what a hack). */ SaveC1 = Circuit1; SaveC2 = Circuit2; Circuit1 = tp1; Circuit2 = tp2; } // Check for and remove duplicate pins. Normally this is called // from "compare", but since "equate pins" may be called outside // of and before "compare", pin uniqueness needs to be ensured. UniquePins(tp1->name, tp1->file); UniquePins(tp2->name, tp2->file); result = MatchPins(tp1, tp2, dolist); if (result == 2) { Fprintf(stdout, "Cells have no pins; pin matching not needed.\n"); } else if (result > 0) { Fprintf(stdout, "Cell pin lists are equivalent.\n"); } else { Fprintf(stdout, "Cell pin lists for %s and %s altered to match.\n", name1, name2); } Tcl_SetObjResult(interp, Tcl_NewIntObj(result)); if (ElementClasses == NULL) { /* Recover temporarily set global variables (see above) */ Circuit1 = SaveC1; Circuit2 = SaveC2; } break; case CLASS_IDX: if (objc == 6) { /* Apply additional matching of pins */ /* Objects must be CLASS_MODULE or CLASS_SUBCKT */ if (tp1->class != CLASS_MODULE && tp1->class != CLASS_SUBCKT) { Tcl_SetResult(interp, "Device class is not black box" " or subcircuit!", NULL); return TCL_ERROR; } if (tp2->class != tp1->class) { Tcl_SetResult(interp, "Device classes are different," " cannot match pins!", NULL); return TCL_ERROR; } /* Count the list items, and the number of ports in each cell */ result = Tcl_ListObjLength(interp, objv[3], &l1); if (result != TCL_OK) return TCL_ERROR; result = Tcl_ListObjLength(interp, objv[5], &l2); if (result != TCL_OK) return TCL_ERROR; if (l1 != l2) { Tcl_SetResult(interp, "Pin lists are different length," " cannot match pins!", NULL); return TCL_ERROR; } ltest = 0; for (ob1 = tp1->cell; ob1 != NULL; ob1 = ob1->next) { if (ob1->type == PORT) ltest++; } if (ltest != l1) { Tcl_SetResult(interp, "List length does not match " " number of pins in cell.", NULL); return TCL_ERROR; } ltest = 0; for (ob2 = tp2->cell; ob2 != NULL; ob2 = ob2->next) { if (ob2->type == PORT) ltest++; } if (ltest != l2) { Tcl_SetResult(interp, "List length does not match " " number of pins in cell.", NULL); return TCL_ERROR; } /* 1st pin list: Check that all list items have 1 or 2 */ /* entries, and that all of them have the same number */ result = Tcl_ListObjIndex(interp, objv[3], 0, &tobj1); if (result != TCL_OK) return result; result = Tcl_ListObjLength(interp, tobj1, &lent); if (result != TCL_OK) return result; if (lent > 2) { Tcl_SetResult(interp, "All list items must have one" " or two entries.", NULL); return TCL_ERROR; } for (i = 1; i < l1; i++) { result = Tcl_ListObjIndex(interp, objv[3], i, &tobj1); if (result != TCL_OK) return result; result = Tcl_ListObjLength(interp, tobj1, <est); if (result != TCL_OK) return result; if (ltest != lent) { Tcl_SetResult(interp, "All list items must have the" " same number of entries.", NULL); return TCL_ERROR; } } /* If the first pin is a list of 2, then all items */ /* must be lists of two. If the cell is a */ /* placeholder, then match the pin number against */ /* the 2nd list item, and rename the pin. */ if (lent == 2) { for (i = 0; i < l1; i++) { result = Tcl_ListObjIndex(interp, objv[3], i, &tobj1); result = Tcl_ListObjIndex(interp, tobj1, 0, &tobj2); if (result != TCL_OK) return result; result = Tcl_ListObjIndex(interp, tobj1, 1, &tobj3); if (result != TCL_OK) return result; for (ob1 = tp1->cell; ob1 != NULL; ob1 = ob1->next) { if (ob1->type == PORT) { if ((*matchfunc)(ob1->name, Tcl_GetString(tobj3))) { FREE(ob1->name); ob1->name = strsave(Tcl_GetString(tobj2)); Tcl_GetIntFromObj(interp, tobj3, &ob1->model.port); break; } } } } } /* If the first pin is a list of 1, then all items */ /* must be single items. If the cell is a */ /* placeholder, then flag an error; relying on */ /* numerical order would be ambiguous. */ else { /* lent == 1 */ if (tp1->flags & CELL_PLACEHOLDER) { Tcl_SetResult(interp, "No pin order information " " for the cell.", NULL); return TCL_ERROR; } /* else nothing to do here. . . need to parse */ /* the second list before we can do anything. */ } /* 2st pin list: Check that all list items have 1 or 2 */ /* entries, and that all of them have the same number */ result = Tcl_ListObjIndex(interp, objv[5], 0, &tobj2); if (result != TCL_OK) return result; result = Tcl_ListObjLength(interp, tobj2, &lent); if (result != TCL_OK) return result; if (lent > 2) { Tcl_SetResult(interp, "All list items must have one" " or two entries.", NULL); return TCL_ERROR; } for (i = 1; i < l2; i++) { result = Tcl_ListObjIndex(interp, objv[5], i, &tobj2); if (result != TCL_OK) return result; result = Tcl_ListObjLength(interp, tobj2, <est); if (result != TCL_OK) return result; if (ltest != lent) { Tcl_SetResult(interp, "All list items must have the" " same number of entries.", NULL); return TCL_ERROR; } } /* Repeat for the 2nd cell: If the first pin is a */ /* list of 2, then all items must be lists of two. */ /* If the cell is a placeholder, then match the pin */ /* number against the 2nd list item, and rename the */ /* pin. */ if (lent == 2) { for (i = 0; i < l2; i++) { result = Tcl_ListObjIndex(interp, objv[5], i, &tobj1); result = Tcl_ListObjIndex(interp, tobj1, 0, &tobj2); if (result != TCL_OK) return result; result = Tcl_ListObjIndex(interp, tobj1, 1, &tobj3); if (result != TCL_OK) return result; for (ob2 = tp2->cell; ob2 != NULL; ob2 = ob2->next) { if (ob2->type == PORT) { if ((*matchfunc)(ob2->name, Tcl_GetString(tobj3))) { FREE(ob2->name); ob2->name = strsave(Tcl_GetString(tobj2)); Tcl_GetIntFromObj(interp, tobj3, &ob2->model.port); break; } } } } } /* On the 2nd cell, if the first pin is a list of */ /* 1, and the cell is a placeholder, then we have */ /* no idea how to order the pins and must flag an */ /* error. */ else { /* lent == 1 */ if (tp2->flags & CELL_PLACEHOLDER) { Tcl_SetResult(interp, "No pin order information " " for the cell.", NULL); return TCL_ERROR; } } /* Now that all pins are assigned by name, reorder */ /* the pin lists of the 2nd cell to match the */ /* order of the 1st. */ /* Reorder the pin lists of instances of the 2nd */ /* cell to match the order of the 1st. */ // pindata.cell2 = tp2; // RecurseCellHashTable2(pinorder, (void *)(&pindata)); } if (EquivalenceClasses(tp1->name, file1, tp2->name, file2)) { Fprintf(stdout, "Device classes %s and %s are equivalent.\n", tp1->name, tp2->name); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); } else { Fprintf(stderr, "Unable to equate device classes %s and %s.\n", tp1->name, tp2->name); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); } break; } return TCL_OK; } /*------------------------------------------------------*/ /* Function name: _netcmp_property */ /* Syntax: netgen::property | [