diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/DEF2Verilog.c | 482 | ||||
-rw-r--r-- | src/Makefile.in | 87 | ||||
-rw-r--r-- | src/addspacers.c | 2026 | ||||
-rw-r--r-- | src/blif2BSpice.c | 4 | ||||
-rw-r--r-- | src/blif2Verilog.c | 38 | ||||
-rw-r--r-- | src/blifFanout.c | 86 | ||||
-rw-r--r-- | src/hash.c | 134 | ||||
-rw-r--r-- | src/hash.h | 51 | ||||
-rw-r--r-- | src/lef.h | 239 | ||||
-rw-r--r-- | src/rc2dly.c | 71 | ||||
-rw-r--r-- | src/readdef.c | 2091 | ||||
-rw-r--r-- | src/readdef.h | 31 | ||||
-rw-r--r-- | src/readlef.c | 3436 | ||||
-rw-r--r-- | src/readlef.h | 220 | ||||
-rw-r--r-- | src/readliberty.c | 91 | ||||
-rw-r--r-- | src/readliberty.h | 2 | ||||
-rw-r--r-- | src/readverilog.c | 1989 | ||||
-rw-r--r-- | src/readverilog.h | 118 | ||||
-rw-r--r-- | src/spice2delay.c | 28 | ||||
-rw-r--r-- | src/vesta.c | 593 | ||||
-rw-r--r-- | src/vlog2Cel.c | 571 | ||||
-rw-r--r-- | src/vlog2Def.c | 480 | ||||
-rw-r--r-- | src/vlog2Spice.c | 636 | ||||
-rw-r--r-- | src/vlog2Verilog.c | 1018 | ||||
-rw-r--r-- | src/vlogFanout.c | 1735 |
25 files changed, 15751 insertions, 506 deletions
diff --git a/src/DEF2Verilog.c b/src/DEF2Verilog.c new file mode 100644 index 0000000..f65d079 --- /dev/null +++ b/src/DEF2Verilog.c @@ -0,0 +1,482 @@ +//---------------------------------------------------------------- +// DEF2Verilog +//---------------------------------------------------------------- +// Generate a structural verilog netlist from a DEF file. This is +// used by qflow to get netlists after all modifications have been +// made to the DEF file, including (but not necessarily limited to) +// insertion of clock trees, insertion of fill, decap, and antenna +// cells; and routing to antenna taps. The final DEF is presumed +// to represent the expected post-routing netlist, so this netlist +// is compared against the layout extracted netlist to check for +// possible errors introduced by the router. +// +// Revision 0, 2018-12-1: First release by R. Timothy Edwards. +// +// This program is written in ISO C99. +//---------------------------------------------------------------- +// Update 1/21/2019: Note that the DEF format does not preserve +// verilog backslash escape names, so ensure that backslash names +// include a space at the end of the name. +// +// Update 5/4/2019: Fixed handling of 2-dimensional arrays +//---------------------------------------------------------------- + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> /* For getopt() */ +#include <math.h> +#include <ctype.h> +#include <float.h> + +#include "hash.h" +#include "readverilog.h" +#include "readlef.h" +#include "readdef.h" + +void write_output(struct cellrec *top, char *vlogoutname); +void helpmessage(FILE *outf); + +char *VddNet = NULL; +char *GndNet = NULL; + +int nccount = 0; /* Count of global unconnected nets */ + +/*--------------------------------------------------------------*/ + +int main (int argc, char *argv[]) +{ + int i, result; + unsigned char Flags; + float rscale; + struct cellrec *topcell; + + char *definname = NULL; + char *vloginname = NULL; + char *vlogoutname = NULL; + + while ((i = getopt(argc, argv, "hHv:o:l:p:g:")) != EOF) { + switch( i ) { + case 'h': + case 'H': + helpmessage(stdout); + exit(0); + break; + case 'v': + vloginname = strdup(optarg); + break; + case 'l': + LefRead(optarg); /* Can be called multiple times */ + break; + case 'p': + VddNet = strdup(optarg); + break; + case 'g': + GndNet = strdup(optarg); + break; + case 'o': + vlogoutname = strdup(optarg); + break; + default: + fprintf(stderr,"Bad switch \"%c\"\n", (char)i); + helpmessage(stderr); + return 1; + } + } + + if (optind < argc) { + definname = strdup(argv[optind]); + optind++; + } + else { + fprintf(stderr, "Couldn't find a filename for DEF input file.\n"); + helpmessage(stderr); + return 1; + } + optind++; + + if (vloginname) + topcell = ReadVerilog(vloginname); + else { + fprintf(stderr, "No verilog file specified (not yet handled).\n"); + return 1; + } + result = DefRead(definname, &rscale); + write_output(topcell, vlogoutname); + return 0; +} + +/* Structure to hold a pointer to a net record and an array bound pair */ + +struct busData { + NET net; + int start; + int end; +}; + +/*----------------------------------------------------------------------*/ +/* Recursion callback function for each item in the NetTable hash */ +/*----------------------------------------------------------------------*/ + +struct nlist *hash_nets(struct hashlist *p, void *cptr) +{ + struct hashtable *NetHash = (struct hashtable *)cptr; + NET net; + char *dptr, *sptr, *bptr; + struct busData *bdata; + int aidx; + + /* Get gate from hash record */ + net = (NET)(p->ptr); + + /* Check for array delimiters. */ + dptr = strrchr(net->netname, '['); + if (dptr != NULL) { + *dptr = '\0'; + sscanf(dptr + 1, "%d", &aidx); + } + else aidx = -1; + + /* Check for backslash-escape names modified by other tools */ + /* (e.g., vlog2Cel) which replace the trailing space with a */ + /* backslash, making the name verilog-incompatible. */ + + if (*net->netname == '\\') { + sptr = strchr(net->netname, ' '); + if (sptr == NULL) { + bptr = strrchr(net->netname + 1, '\\'); + if (bptr != NULL) *bptr = ' '; + } + } + + /* Check if record already exists */ + bdata = HashLookup(net->netname, NetHash); + if (bdata == NULL) { + /* Allocate record */ + bdata = (struct busData *)malloc(sizeof(struct busData)); + bdata->start = bdata->end = aidx; + bdata->net = net; + HashPtrInstall(net->netname, bdata, NetHash); + } + else { + if (aidx != -1) { + if (aidx > bdata->start) bdata->start = aidx; + if (aidx < bdata->end) bdata->end = aidx; + } + } + if (dptr != NULL) *dptr = '['; + return NULL; +} + +/*--------------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec nets hash table */ +/*--------------------------------------------------------------------------*/ + +struct nlist *output_wires(struct hashlist *p, void *cptr) +{ + struct busData *bdata; + FILE *outf = (FILE *)cptr; + + bdata = (struct busData *)(p->ptr); + + fprintf(outf, "wire "); + if (bdata->start >= 0 && bdata->end >= 0) { + fprintf(outf, "[%d:%d] ", bdata->start, bdata->end); + } + fprintf(outf, "%s", p->name); + + // Ensure backslash escaped names end in a space character per + // verilog syntax. + if (*(p->name) == '\\') + if (*(p->name + strlen(p->name) - 1) != ' ') + fprintf(outf, " "); + + /* NOTE: The output format is fixed with power and ground */ + /* specified as wires and set to binary values. May want */ + /* additional command line options for various forms; otherwise, */ + /* vlog2Verilog can translate between forms. */ + + if (VddNet && (!strcmp(p->name, VddNet))) + fprintf(outf, " = 1'b1"); + else if (GndNet && (!strcmp(p->name, GndNet))) + fprintf(outf, " = 1'b0"); + + fprintf(outf, " ;\n"); + return NULL; +} + +/*----------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec properties */ +/* hash table */ +/*----------------------------------------------------------------------*/ + +struct nlist *output_props(struct hashlist *p, void *cptr) +{ + char *propval = (char *)(p->ptr); + FILE *outf = (FILE *)cptr; + + fprintf(outf, ".%s(%s),\n", p->name, propval); + return NULL; +} + +/*----------------------------------------------------------------------*/ +/* Recursion callback function for each item in InstanceTable */ +/* Output all module instances. */ +/*----------------------------------------------------------------------*/ + +struct nlist *output_instances(struct hashlist *p, void *cptr) +{ + GATE gate = (GATE)(p->ptr); + FILE *outf = (FILE *)cptr; + NODE node; + BUS bus; + int i, j, lastidx, nbus; + char ***net_array = NULL; + + /* Ignore pins which are recorded as gates */ + if (gate->gatetype == PinMacro) return NULL; + + fprintf(outf, "%s ", gate->gatetype->gatename); + fprintf(outf, "%s (\n", gate->gatename); + + /* In case power/ground pins are at the end of the list, find the */ + /* index of the last valid output line. Consider any input pin */ + /* that is not marked use POWER or GROUND to be automatically valid */ + /* even if it is not connected (e.g., unconnected antenna cell */ + /* input pins). */ + + for (lastidx = gate->nodes - 1; lastidx >= 0; lastidx--) { + node = gate->noderec[lastidx]; + if (node) break; + else if ((gate->gatetype->direction[lastidx] == PORT_CLASS_INPUT) + && (gate->gatetype->use[lastidx] != PORT_USE_POWER) + && (gate->gatetype->use[lastidx] != PORT_USE_GROUND)) + break; + } + + /* If the gate defines pin buses, then prepare a set of arrays for */ + /* each bus and its connections. */ + if (gate->gatetype->bus != NULL) { + for (bus = gate->gatetype->bus, nbus = 0; bus; bus = bus->next) nbus++; + net_array = (char ***)malloc(nbus * sizeof(char **)); + for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) { + net_array[i] = (char **)calloc((bus->high - bus->low + 1), sizeof(char *)); + } + } + + /* Write each port and net connection */ + for (i = 0; i <= lastidx; i++) { + int is_nc_input; + char *netname; + node = gate->noderec[i]; + + /* node may be NULL if power or ground. Currently not handling */ + /* this in expectation that the output is standard functional */ + /* verilog without power and ground. Should have runtime */ + /* options for handling power and ground in various ways. */ + /* */ + /* node may also be NULL if not connected, in which case it is */ + /* optional to output it in verilog if it is an output. If it */ + /* is an input, such as an unconnected antenna cell, then */ + /* generate an arbitrary net name to represent it. */ + + is_nc_input = 0; + if ((node == NULL) && + (gate->gatetype->direction[i] == PORT_CLASS_INPUT) && + (gate->gatetype->use[i] != PORT_USE_POWER) && + (gate->gatetype->use[i] != PORT_USE_GROUND)) { + is_nc_input = 1; + netname = (char *)malloc(32 * sizeof(char)); + sprintf(netname, "_proxy_no_connect_%d_", nccount++); + } + else if (node) netname = node->netname; + + if (node || is_nc_input) { + char *sptr, *eptr; + int k; + + /* If the gate defines pin buses, then ignore individual */ + /* pins and print out a single bus instead. */ + if (gate->gatetype->bus != NULL) { + if ((sptr = strchr(gate->node[i], '[')) != NULL) { + if (sscanf(sptr + 1, "%d", &k) == 1) { + *sptr = '\0'; + for (bus = gate->gatetype->bus, j = 0; bus; + bus = bus->next, j++) { + if (!strcmp(bus->busname, gate->node[i])) { + net_array[j][k] = strdup(netname); + break; + } + } + *sptr = '['; + if (bus != NULL) continue; + } + } + } + + fprintf(outf, " .%s(%s)", gate->node[i], netname); + if ((i != lastidx) || (gate->bus != NULL)) fprintf(outf, ","); + fprintf(outf, "\n"); + } + if (is_nc_input) free(netname); + } + + /* Write out bus connections */ + for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) { + int defnets = 0, samenet = 0; + char *dptr, *d0ptr = NULL; + + fprintf(outf, " .%s(", bus->busname); + for (j = bus->low; j <= bus->high; j++) { + if (net_array[i][j] != NULL) { + defnets++; + if ((dptr = strrchr(net_array[i][j], '[')) != NULL) { + *dptr = '\0'; + if (j == bus->low) { + d0ptr = dptr; + } + else { + if (!strcmp(net_array[i][j], net_array[i][bus->low])) samenet++; + *dptr = '['; + } + } + } + } + if (d0ptr != NULL) *d0ptr = '['; + if ((d0ptr != NULL) && (samenet == (bus->high - bus->low))) { + *d0ptr = '\0'; + fprintf(outf, "%s", net_array[i][bus->low]); + *d0ptr = '['; + } + else if (defnets > 0) { + fprintf(outf, "{"); + for (j = bus->high; j >= bus->low; j--) { + if (net_array[i][j] != NULL) { + fprintf(outf, "%s", net_array[i][j]); + } + if (j > bus->low) fprintf(outf, ","); + } + fprintf(outf, "}"); + } + fprintf(outf, ")"); + if (bus->next != NULL) fprintf(outf, ","); + fprintf(outf, "\n"); + } + + fprintf(outf, ");\n\n"); + + if (gate->gatetype->bus != NULL) { + /* Free memory allocated to net arrays */ + for (bus = gate->gatetype->bus, i = 0; bus; bus = bus->next, i++) { + for (j = bus->low; j <= bus->high; j++) + if (net_array[i][j]) free(net_array[i][j]); + free(net_array[i]); + } + free(net_array); + } + + return NULL; +} + +/*--------------------------------------------------------------*/ +/* write_output */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void write_output(struct cellrec *topcell, char *vlogoutname) +{ + FILE *outfptr = stdout; + + struct netrec *net; + struct portrec *port; + struct instance *inst; + GATE gate; + + struct hashtable NetHash; + + if (vlogoutname != NULL) { + outfptr = fopen(vlogoutname, "w"); + if (outfptr == NULL) { + fprintf(stderr, "Error: Failed to open file %s for writing netlist output\n", + vlogoutname); + return; + } + } + + InitializeHashTable(&NetHash, LARGEHASHSIZE); + + /* Write output module header */ + fprintf(outfptr, "/* Verilog module written by DEF2Verilog (qflow) */\n"); + fprintf(outfptr, "module %s (\n", topcell->name); + + /* Output the verilog netlist verbatim through the list of ports. */ + + for (port = topcell->portlist; port; port = port->next) { + if (port->name == NULL) continue; + switch(port->direction) { + case PORT_INPUT: + fprintf(outfptr, " input "); + break; + case PORT_OUTPUT: + fprintf(outfptr, " output "); + break; + case PORT_INOUT: + fprintf(outfptr, " inout "); + break; + } + net = HashLookup(port->name, &topcell->nets); + if (net && net->start >= 0 && net->end >= 0) { + fprintf(outfptr, "[%d:%d] ", net->start, net->end); + } + fprintf(outfptr, "%s", port->name); + if (port->next) fprintf(outfptr, ","); + fprintf(outfptr, "\n"); + } + fprintf(outfptr, ");\n\n"); + + /* Find all wires and create a hash table */ + /* (To do: cross check against topcell->nets to ensure correct array bounds) */ + + RecurseHashTablePointer(&NetTable, hash_nets, &NetHash); + RecurseHashTablePointer(&NetHash, output_wires, outfptr); + fprintf(outfptr, "\n"); + + /* Write instances in the order found in the DEF file */ + RecurseHashTablePointer(&InstanceTable, output_instances, outfptr); + + /* End the module */ + fprintf(outfptr, "endmodule\n"); + + if (vlogoutname != NULL) fclose(outfptr); + + fflush(stdout); +} + +/*--------------------------------------------------------------*/ +/* C helpmessage - tell user how to use the program */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *outf) +{ + fprintf(outf, "DEF2Verilog [-options] <netlist>\n"); + fprintf(outf, "\n"); + fprintf(outf, "DEF2Verilog converts a DEF file to a verilog structural\n"); + fprintf(outf, "netlist. Output on stdout.\n"); + fprintf(outf, "\n"); + fprintf(outf, "options:\n"); + fprintf(outf, " -v <path> Path to verilog file (for I/O list)\n"); + fprintf(outf, " -l <path> Path to standard cell LEF file (for macro list)\n"); + fprintf(outf, " -p <name> Name of power net\n"); + fprintf(outf, " -g <name> Name of ground net\n"); + fprintf(outf, "\n"); + fprintf(outf, " -h Print this message\n"); + +} /* helpmessage() */ + diff --git a/src/Makefile.in b/src/Makefile.in index 501bdf9..8d1b8ff 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -12,26 +12,34 @@ DEFS = @DEFS@ -DQFLOW_VERSION=\"@VERSION@\" -DQFLOW_REVISION=\"@REVISION@\" QFLOW_LIB_DIR = /usr/lib/qflow QFLOW_GRAYWOLF_PATH = @QFLOW_GRAYWOLF_PATH@ +QFLOW_REPLACE_PATH = @QFLOW_REPLACE_PATH@ +QFLOW_NTUPLACE3_PATH = @QFLOW_NTUPLACE3_PATH@ +QFLOW_NTUPLACE4_PATH = @QFLOW_NTUPLACE4_PATH@ QFLOW_QROUTER_PATH = @QFLOW_QROUTER_PATH@ -QFLOW_ABC_PATH = @QFLOW_ABC_PATH@ -QFLOW_ODIN_PATH = @QFLOW_ODIN_PATH@ QFLOW_MAGIC_PATH = @QFLOW_MAGIC_PATH@ QFLOW_NETGEN_PATH = @QFLOW_NETGEN_PATH@ QFLOW_YOSYS_PATH = @QFLOW_YOSYS_PATH@ QFLOW_OPENTIMER_PATH = @QFLOW_OPENTIMER_PATH@ QFLOW_OPENSTA_PATH = @QFLOW_OPENSTA_PATH@ -HAVE_ABC = @HAVE_ABC@ HAVE_YOSYS = @HAVE_YOSYS@ -HAVE_ODIN_II = @HAVE_ODIN_II@ HAVE_MAGIC = @HAVE_MAGIC@ HAVE_NETGEN = @HAVE_NETGEN@ HAVE_OPENTIMER = @HAVE_OPENTIMER@ HAVE_OPENSTA = @HAVE_OPENSTA@ - -OBJECTS = blif2BSpice.o blif2Verilog.o blifFanout.o vesta.o spice2delay.o rc2dly.o +HAVE_GRAYWOLF = @HAVE_GRAYWOLF@ +HAVE_REPLACE = @HAVE_REPLACE@ +HAVE_QROUTER = @HAVE_QROUTER@ + +OBJECTS = vlog2Spice.o vlog2Verilog.o vlog2Def.o vlog2Cel.o vlogFanout.o +OBJECTS += DEF2Verilog.o addspacers.o +OBJECTS += vesta.o spice2delay.o rc2dly.o +OBJECTS += blif2BSpice.o blif2Verilog.o blifFanout.o HASHLIB = hash.o LIBERTYLIB = readliberty.o +VERILOGLIB = readverilog.o +LEFLIB = readlef.o +DEFLIB = readdef.o SOURCES := $(patsubst %.o,%.c,$(OBJECTS)) TARGETS := $(patsubst %.o,%$(EXEEXT),$(OBJECTS)) @@ -39,40 +47,73 @@ BININSTALL = ${QFLOW_LIB_DIR}/bin all: $(TARGETS) +vlog2Spice$(EXEEXT): vlog2Spice.o $(HASHLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vlog2Spice.o $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS) + +vlog2Verilog$(EXEEXT): vlog2Verilog.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vlog2Verilog.o $(HASHLIB) $(VERILOGLIB) $(LEFLIB) \ + -o $@ $(LIBS) + +vlog2Cel$(EXEEXT): vlog2Cel.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vlog2Cel.o $(HASHLIB) $(VERILOGLIB) $(LEFLIB) \ + -o $@ $(LIBS) + +vlog2Def$(EXEEXT): vlog2Def.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vlog2Def.o $(HASHLIB) $(LEFLIB) $(VERILOGLIB) -o $@ $(LIBS) -lm + +vlogFanout$(EXEEXT): vlogFanout.o $(HASHLIB) $(LIBERTYLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vlogFanout.o $(HASHLIB) $(VERILOGLIB) $(LIBERTYLIB) \ + -DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \ + $(DEPENDS) -o $@ $(LIBS) -lm + +DEF2Verilog$(EXEEXT): DEF2Verilog.o $(HASHLIB) $(VERILOGLIB) $(DEFLIB) $(LEFLIB) + $(CC) $(LDFLAGS) DEF2Verilog.o $(HASHLIB) $(VERILOGLIB) $(DEFLIB) $(LEFLIB) \ + -DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \ + $(DEPENDS) -o $@ $(LIBS) -lm + +addspacers$(EXEEXT): addspacers.o $(HASHLIB) $(LEFLIB) $(DEFLIB) + $(CC) $(LDFLAGS) addspacers.o $(HASHLIB) $(LEFLIB) $(DEFLIB) -o $@ $(LIBS) -lm + blif2BSpice$(EXEEXT): blif2BSpice.o $(CC) $(LDFLAGS) blif2BSpice.o -o $@ $(LIBS) +blif2Verilog$(EXEEXT): blif2Verilog.o + $(CC) $(LDFLAGS) blif2Verilog.o -o $@ $(LIBS) + blifFanout$(EXEEXT): blifFanout.o $(HASHLIB) $(LIBERTYLIB) $(CC) $(LDFLAGS) blifFanout.o $(HASHLIB) $(LIBERTYLIB) \ -DQFLOW_VERSION=\"VERSION\" -DQFLOW_REVISION=\"REVISION\" \ $(DEPENDS) -o $@ $(LIBS) -lm -blif2Verilog$(EXEEXT): blif2Verilog.o - $(CC) $(LDFLAGS) blif2Verilog.o -o $@ $(LIBS) - -vesta$(EXEEXT): vesta.o $(HASHLIB) - $(CC) $(LDFLAGS) vesta.o $(HASHLIB) -o $@ $(LIBS) +vesta$(EXEEXT): vesta.o $(HASHLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) vesta.o $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS) spice2delay$(EXEEXT): spice2delay.o $(HASHLIB) $(LIBERTYLIB) $(CC) $(LDFLAGS) spice2delay.o $(HASHLIB) $(LIBERTYLIB) -o $@ $(LIBS) -rc2dly$(EXEEXT): rc2dly.o $(LIBERTYLIB) - $(CC) $(LDFLAGS) rc2dly.o $(LIBERTYLIB) -o $@ $(LIBS) +rc2dly$(EXEEXT): rc2dly.o $(LIBERTYLIB) $(HASHLIB) $(VERILOGLIB) + $(CC) $(LDFLAGS) rc2dly.o $(LIBERTYLIB) $(HASHLIB) $(VERILOGLIB) -o $@ $(LIBS) install: $(TARGETS) - @echo "Installing verilog and BDNET file format handlers" + @echo "Installing verilog, SPICE, etc. file format handlers" $(INSTALL) -d $(DESTDIR)${BININSTALL} @for target in $(TARGETS); do \ $(INSTALL) $$target $(DESTDIR)${BININSTALL} ;\ done - @echo "Installing links to graywolf, qrouter, odin_ii, and abc" - (cd $(DESTDIR)${BININSTALL}; $(RM) -f graywolf; ln -s $(QFLOW_GRAYWOLF_PATH) graywolf) - (cd $(DESTDIR)${BININSTALL}; $(RM) -f qrouter; ln -s $(QFLOW_QROUTER_PATH) qrouter) - @if test "${HAVE_ABC}" = "1"; then \ - (cd $(DESTDIR)${BININSTALL}; $(RM) -f abc; ln -s $(QFLOW_ABC_PATH) abc); \ + @echo "Installing links to third-party synthesis flow tool executables" + @if test "${HAVE_GRAYWOLF}" = "1"; then \ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f graywolf; ln -s $(QFLOW_GRAYWOLF_PATH) graywolf) ;\ + fi + @if test "${HAVE_REPLACE}" = "1"; then \ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f RePlAce; ln -s $(QFLOW_REPLACE_PATH) RePlAce) ;\ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f ntuplace3; ln -s $(QFLOW_NTUPLACE3_PATH) ntuplace3) ;\ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f ntuplace4h; ln -s $(QFLOW_NTUPLACE4_PATH) ntuplace4h) ;\ + fi + @if test "${HAVE_QROUTER}" = "1"; then \ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f qrouter; ln -s $(QFLOW_QROUTER_PATH) qrouter) ;\ fi @if test "${HAVE_MAGIC}" = "1"; then \ - (cd $(DESTDIR)${BININSTALL}; $(RM) -f magic; ln -s $(QFLOW_MAGIC_PATH) magic); \ + (cd $(DESTDIR)${BININSTALL}; $(RM) -f magic; ln -s $(QFLOW_MAGIC_PATH) magic) ;\ fi @if test "${HAVE_NETGEN}" = "1"; then \ (cd $(DESTDIR)${BININSTALL}; $(RM) -f netgen; ln -s $(QFLOW_NETGEN_PATH) netgen); \ @@ -92,15 +133,15 @@ uninstall: $(RM) -rf ${BININSTALL} clean: - $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) + $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB) $(RM) -f $(TARGETS) veryclean: - $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) + $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB) $(RM) -f $(TARGETS) distclean: - $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) + $(RM) -f $(OBJECTS) $(HASHLIB) $(LIBERTYLIB) $(LEFLIB) $(DEFLIB) $(VERILOGLIB) $(RM) -f $(TARGETS) .c.o: diff --git a/src/addspacers.c b/src/addspacers.c new file mode 100644 index 0000000..b0d211d --- /dev/null +++ b/src/addspacers.c @@ -0,0 +1,2026 @@ +/*------------------------------------------------------*/ +/* addspacers --- */ +/* */ +/* Tool for adding fill cells and other post-placement */ +/* cells and data into a DEF format layout. */ +/* */ +/* Primary functions are: */ +/* */ +/* 1) Fill the core area out to the bounding box with */ +/* fill cells to make the edges straight and fill */ +/* any gaps. */ +/* */ +/* 2) Create power stripes and power posts, adding */ +/* additional fill under (or near, as available) */ +/* the power stripes if requested. */ +/* */ +/* 3) Adjust pin positions to match any stretching of */ +/* the cell done in (2) */ +/* */ +/* 4) Adjust obstruction layers to match any */ +/* stretching of the cell done in (2) */ +/* */ +/* 5) Write the modified DEF layout */ +/* */ +/* 6) Write the modified obstruction file (if */ +/* modified). */ +/* */ +/*------------------------------------------------------*/ +/* */ +/* This file previously addspacers.tcl until it became */ +/* painfully obvious that the amount of calculation */ +/* involved made it a very poor choice to be done by */ +/* an interpreter. */ +/* */ +/*------------------------------------------------------*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> /* For getopt() */ +#include <math.h> +#include <ctype.h> +#include <float.h> + +#include "hash.h" +#include "readlef.h" +#include "readdef.h" + +/* Flags fields */ + +#define NOSTRETCH 1 +#define OBSTRUCT 2 +#define VERBOSE 4 +#define FILLWARNED 8 + +/* Structure used to contain core area bounding box. Also records */ +/* core site height and width. */ + +typedef struct _corebbox *COREBBOX; + +typedef struct _corebbox { + int llx; /* Layout bounds */ + int lly; + int urx; + int ury; + int urx_exp; /* Expanded core urx */ + int sitew; /* Core site width */ + int siteh; /* Core site height */ + int fillmin; /* Minimum fill cell width */ + int orient; /* Orientation of the first (lowest) row */ +} corebbox; + +/* Structure used to hold the final calculated pitch and width of stripes */ + +typedef struct _stripeinfo *SINFO; + +typedef struct _stripeinfo { + int width; + int pitch; + int offset; + int stretch; /* Maximum amount of layout stretching */ + int number; /* Total number of stripes */ +} stripeinfo; + +/* List of gates (used to sort fill cells) */ +typedef struct filllist_ *FILLLIST; + +struct filllist_ { + FILLLIST next; + FILLLIST last; + GATE gate; + int width; +}; + +/* Structures for defining a power stripe. */ + +typedef struct _powerpost *PPOST; + +typedef struct _powerpost { + PPOST next; + DSEG strut; + LefList viagen; +} powerpost; + +typedef struct _powerstripe *PSTRIPE; + +typedef struct _powerstripe { + PSTRIPE next; // One stripe definition centered on X = 0 + PPOST posts; // All posts for one stripe, centered on X = 0 + DSEG stripe; + int offset; // Horizontal start of posts + int num; // Number of stripes + int pitch; // Spacing between stripes + char *name; // Net name of power rail +} powerstripe; + +/* Hash table of instances hashed by (X, Y) position */ +struct hashtable CellPosTable; + +/* Forward declarations */ +unsigned char check_overcell_capable(unsigned char Flags); +FILLLIST generate_fill(char *fillcellname, float rscale, COREBBOX corearea, + unsigned char Flags); +SINFO generate_stripefill(char *VddNet, char *GndNet, + char *stripepat, float stripewidth_t, float stripepitch_t, + char *fillcellname, float scale, FILLLIST fillcells, + COREBBOX corearea, unsigned char Flags); +void fix_obstructions(char *definname, SINFO stripevals, float scale, + unsigned char Flags); +PSTRIPE generate_stripes(SINFO stripevals, FILLLIST fillcells, + COREBBOX corearea, char *stripepat, char *VddNet, char *GndNet, + float scale, unsigned char Flags); +void write_output(char *definname, char *defoutname, float scale, + COREBBOX corearea, SINFO stripevals, PSTRIPE rails, + char *VddNet, char *GndNet, unsigned char Flags); +void helpmessage(FILE *outf); + +/*--------------------------------------------------------------*/ +/*--------------------------------------------------------------*/ + +int main (int argc, char *argv[]) +{ + int i, result; + unsigned char Flags; + float rscale; + struct cellrec *topcell; + COREBBOX corearea; + FILLLIST fillcells; + SINFO stripevals; + PSTRIPE rails; + + char *definname = NULL; + char *fillcellname = NULL; + char *defoutname = NULL; + + float stripewidth_t, stripepitch_t; + static char* default_stripepat = "PG"; + char *stripepat = default_stripepat; + + char *VddNet = NULL; + char *GndNet = NULL; + + Flags = 0; + stripewidth_t = stripepitch_t = 0.0; + + while ((i = getopt(argc, argv, "hHvOn:o:l:p:g:f:w:P:s:")) != EOF) { + switch( i ) { + case 'v': + Flags |= VERBOSE; + /* Also set global variable used by readlef.c */ + Verbose = 1; + break; + case 'h': + case 'H': + helpmessage(stdout); + exit(0); + break; + case 'n': + Flags |= NOSTRETCH; + break; + case 'O': + Flags |= OBSTRUCT; + break; + case 'w': + if (sscanf(optarg, "%g", &stripewidth_t) != 1) { + fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg); + } + break; + case 'P': + if (sscanf(optarg, "%g", &stripepitch_t) != 1) { + fprintf(stderr, "Cannot read numeric value from \"%s\"\n", optarg); + } + break; + case 'o': + defoutname = strdup(optarg); + break; + case 'f': + fillcellname = strdup(optarg); + break; + case 's': + if (!strcmp(optarg, "tripe")) { + /* Accept the argument "-stripe <width> <pitch> <pattern>" */ + /* that was the option for the old addspacers.tcl script. */ + /* Combines width, pitch, and pattern in one argument. */ + if (sscanf(argv[optind], "%g", &stripewidth_t) != 1) { + fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]); + } + optind++; + if (sscanf(argv[optind], "%g", &stripepitch_t) != 1) { + fprintf(stderr, "Cannot read numeric value from \"%s\"\n", argv[optind]); + } + optind++; + stripepat = strdup(argv[optind]); + optind++; + } + else { + stripepat = strdup(optarg); + } + break; + case 'l': + LefRead(optarg); /* Can be called multiple times */ + break; + case 'p': + VddNet = strdup(optarg); + break; + case 'g': + GndNet = strdup(optarg); + break; + default: + fprintf(stderr,"Bad switch \"%c\"\n", (char)i); + helpmessage(stderr); + return 1; + } + } + + if (optind < argc) { + char *pptr; + + definname = (char *)malloc(strlen(argv[optind]) + 5); + strcpy(definname, argv[optind]); + pptr = strrchr(argv[optind], '.'); + if (pptr == NULL) strcat(definname, ".def"); + optind++; + } + else { + fprintf(stderr, "Couldn't find a filename for DEF input file.\n"); + helpmessage(stderr); + return 1; + } + optind++; + + result = DefRead(definname, &rscale); + + corearea = (COREBBOX)malloc(sizeof(corebbox)); + + Flags |= check_overcell_capable(Flags); + fillcells = generate_fill(fillcellname, rscale, corearea, Flags); + if (fillcells == NULL) { + fprintf(stderr, "Failed to parse any fill cells from the standard cell library.\n"); + return 1; + } + stripevals = generate_stripefill(VddNet, GndNet, stripepat, + stripewidth_t, stripepitch_t, fillcellname, rscale, fillcells, + corearea, Flags); + fix_obstructions(definname, stripevals, rscale, Flags); + rails = generate_stripes(stripevals, fillcells, corearea, stripepat, + VddNet, GndNet, rscale, Flags); + write_output(definname, defoutname, rscale, corearea, stripevals, + rails, VddNet, GndNet, Flags); + + return 0; +} + +/*--------------------------------------------------------------*/ +/* Find empty spaces in the DEF layout and insert fill macros */ +/*--------------------------------------------------------------*/ + +FILLLIST +generate_fill(char *fillcellname, float scale, COREBBOX corearea, unsigned char Flags) +{ + GATE gate, newfillinst; + ROW row; + int isfill, orient; + int corew = 1, coreh = 0, testh; + int instx, insty, instw, insth, fnamelen; + int x, y, dx, nx, fillmin; + char posname[32]; + + int corellx = 0; + int corelly = 0; + int coreurx = 0; + int coreury = 0; + + FILLLIST fillcells = NULL; + FILLLIST newfill, testfill; + + /* Parse library macros and find CORE SITE definition */ + + for (gate = GateInfo; gate; gate = gate->next) + { + if (!strncmp(gate->gatename, "site_", 5)) { + if (gate->gateclass == MACRO_CLASS_CORE) { + corew = (int)(roundf(gate->width * scale)); + testh = (int)(roundf(gate->height * scale)); + // Sometimes there are multiple-height core sites. . . + if (coreh == 0 || (testh < coreh)) coreh = testh; + } + } + } + if (corew == 0) { + fprintf(stderr, "Warning: failed to find any core site.\n"); + /* Use route pitch for step size */ + corew = (int)(roundf(LefGetRoutePitch(0) * scale)); + } + if (corew == 0) { + fprintf(stderr, "Error: failed to find any core site or route pitch.\n"); + return NULL; + } + + /* Parse library macros and find fill cells. Use "fillcellname" as a */ + /* prefix, if given; if not, find spacer cells by class. */ + + fnamelen = (fillcellname) ? strlen(fillcellname) : 0; + + for (gate = GateInfo; gate; gate = gate->next) + { + isfill = FALSE; + if (fillcellname) { + if (!strncmp(gate->gatename, fillcellname, fnamelen)) + isfill = TRUE; + } + else if (gate->gatesubclass == MACRO_SUBCLASS_SPACER) + isfill = TRUE; + + if (isfill == TRUE) { + newfill = (FILLLIST)malloc(sizeof(struct filllist_)); + newfill->gate = gate; + newfill->width = (int)(roundf(gate->width * scale)); + + /* Insert in fill list in width order, high to low */ + if (fillcells == NULL) { + newfill->next = newfill->last = NULL; + fillcells = newfill; + } + else { + for (testfill = fillcells; testfill; testfill = testfill->next) { + if (testfill->width < newfill->width) { + /* insert before */ + newfill->next = testfill; + newfill->last = testfill->last; + if (testfill->last == NULL) + fillcells = newfill; + else + testfill->last->next = newfill; + testfill->last = newfill; + break; + } + else if (testfill->next == NULL) { + /* put at end */ + newfill->next = NULL; + testfill->next = newfill; + newfill->last = testfill; + break; + } + } + } + } + } + + if (fillcells == NULL) { + fprintf(stderr, "Error: No fill cells have been specified or found.\n"); + return NULL; + } + + if (fillcells) { + testh = (int)(roundf(fillcells->gate->height * scale)); + if ((coreh == 0) || (coreh < testh)) { + /* Use fill cell height for core height */ + coreh = testh; + } + } + if (coreh == 0) { + fprintf(stderr, "Error: failed to find any core site or standard cell height.\n"); + return NULL; + } + if (Flags & VERBOSE) + fprintf(stdout, "Core site is %g x %g um\n", + (float)corew / scale, (float)coreh / scale); + + + /* Rehash all instances by position */ + /* Find minimum and maximum bounds, and record cell in lower left position */ + + InitializeHashTable(&CellPosTable, LARGEHASHSIZE); + + for (gate = Nlgates; gate; gate = gate->next) + { + /* Do not evaluate pins, only core cells */ + if (gate->gatetype == NULL) continue; + + instx = (int)(roundf(gate->placedX * scale)); + insty = (int)(roundf(gate->placedY * scale)); + instw = (int)(roundf(gate->width * scale)); + insth = (int)(roundf(gate->height * scale)); + sprintf(posname, "%dx%d", instx, insty); + HashPtrInstall(posname, gate, &CellPosTable); + + if (corellx == coreurx) { + corellx = instx; + coreurx = instx + instw; + corelly = insty; + coreury = insty + insth; + } + else { + if (instx < corellx) corellx = instx; + else if (instx + instw > coreurx) coreurx = instx + instw; + + if (insty < corelly) corelly = insty; + else if (insty + insth > coreury) coreury = insty + insth; + } + } + + fprintf(stdout, "Initial core layout: (%d %d) to (%d %d) (scale um * %d)\n", + corellx, corelly, coreurx, coreury, (int)scale); + + if (Flags & VERBOSE) fprintf(stdout, "Adding fill cells.\n"); + + /* Find the orientation of the first row and record this */ + /* in corearea. NOTE: This is simpler if the DEF file records */ + /* ROWs, something that needs to be done in place2def. */ + + row = DefFindRow(corelly); + if (row) { + corearea->orient = row->orient & (RN | RS); + } + else { + corearea->orient = RN; + y = corelly; + x = corellx; + while (x < coreurx) { + sprintf(posname, "%dx%d", x, y); + gate = (GATE)HashLookup(posname, &CellPosTable); + if (gate != NULL) { + corearea->orient = gate->orient & (RN | RS); + break; + } + x += testfill->width; + } + } + orient = corearea->orient; + + /* Starting from the lower-left corner, find gate at each site */ + /* position. If there is no gate at the site position, then */ + /* find the next gate forward, and add fill. */ + + /* NOTE: This routine does not account for obstruction areas */ + /* used to define a non-rectangular core area (to be done) */ + + for (y = corelly; y < coreury; y += coreh) { + x = corellx; + while (x < coreurx) { + sprintf(posname, "%dx%d", x, y); + gate = (GATE)HashLookup(posname, &CellPosTable); + if (gate == NULL) { + for (nx = x + corew; nx < coreurx; nx += corew) { + sprintf(posname, "%dx%d", nx, y); + gate = (GATE)HashLookup(posname, &CellPosTable); + if (gate != NULL) break; + } + if (Flags & VERBOSE) + fprintf(stdout, "Add fill from (%d %d) to (%d %d)\n", + x, y, nx, y); + dx = nx - x; + + while (dx > 0) { + for (testfill = fillcells; testfill; testfill = testfill->next) { + if (testfill->width <= dx) break; + } + if (testfill == NULL) { + if (nx == coreurx) { + if (!(Flags & FILLWARNED)) { + fprintf(stderr, "Notice: Right edge of layout" + " cannot be cleanly aligned due to\n"); + fprintf(stderr, "limited fill cell widths.\n"); + } + Flags |= FILLWARNED; // Do not repeat this message + x = nx; + dx = 0; + break; + } + else { + fprintf(stderr, "Error: Empty slot at (%g, %g) is smaller" + " than any available fill cell.\n", + (float)x / scale, (float)y / scale); + } + } + + /* Create new fill instance */ + newfillinst = (GATE)malloc(sizeof(struct gate_)); + if (testfill) + newfillinst->gatetype = testfill->gate; + else + newfillinst->gatetype = NULL; /* placeholder */ + sprintf(posname, "FILL%dx%d", x, y); + newfillinst->gatename = strdup(posname); + newfillinst->placedX = (double)x / (double)scale; + newfillinst->placedY = (double)y / (double)scale; + newfillinst->clientdata = (void *)NULL; + row = DefFindRow(y); + newfillinst->orient = (row) ? row->orient : orient; + DefAddGateInstance(newfillinst); + + /* Hash the new instance position */ + sprintf(posname, "%dx%d", x, y); + HashPtrInstall(posname, newfillinst, &CellPosTable); + + if (testfill) { + x += testfill->width; + dx -= testfill->width; + } + else { + newfillinst->width = dx; + x += dx; + dx = 0; + } + } + } + else { + x += (int)(roundf(gate->width * scale)); + } + } + /* Flip orientation each row (NOTE: This is not needed if ROW */ + /* statements are in the DEF file! */ + orient = (orient == RN) ? RS : RN; + } + + if (fillcells) { + for (testfill = fillcells; testfill->next; testfill = testfill->next); + fillmin = testfill->width; + } + else fillmin = 0; + + corearea->llx = corellx; + corearea->lly = corelly; + corearea->urx = coreurx; + corearea->urx_exp = coreurx; + corearea->ury = coreury; + corearea->sitew = corew; + corearea->siteh = coreh; + corearea->fillmin = fillmin; + + return fillcells; +} + +/*--------------------------------------------------------------*/ +/* Check if there are not enough metal layers to route power */ +/* over the cell. If not, then force the NOSTRETCH option. */ +/*--------------------------------------------------------------*/ + +unsigned char check_overcell_capable(unsigned char Flags) +{ + int ltop; + + if (!(Flags & NOSTRETCH)) { + ltop = LefGetMaxRouteLayer() - 1; + if (LefGetRouteOrientation(ltop) == 1) ltop--; + if (ltop < 3) { + fprintf(stderr, "Warning: Stretching requested, but not applicable.\n"); + return (unsigned char)NOSTRETCH; + } + } + return (unsigned char)0; +} + +/*--------------------------------------------------------------*/ +/* Generate power stripes */ +/*--------------------------------------------------------------*/ + +SINFO +generate_stripefill(char *VddNet, char *GndNet, char *stripepat, + float stripewidth_t, float stripepitch_t, + char *fillcellname, float scale, FILLLIST fillcells, + COREBBOX corearea, unsigned char Flags) +{ + int numstripes; + int minstripes; + int corew, tw, tp, tr, dx, x, y, nx; + int stripepitch_i, stripewidth_i, stripeoffset_i; + int stripepitch_f, stripewidth_f, stripeoffset_f; + int totalw; + int orient; + int nextx, totalfx; + FILLLIST fillseries, testfill, newfill, sfill; + SINFO stripevals; + char posname[32]; + GATE gate, newfillinst; + ROW row; + + stripevals = (SINFO)malloc(sizeof(stripeinfo)); + stripevals->pitch = 0; + stripevals->width = 0; + stripevals->offset = 0; + + corew = corearea->urx - corearea->llx; + + if (stripewidth_t <= 0.0 || stripepitch_t <= 0.0) { + fprintf(stdout, "No stripe information provided; no power stripes added.\n"); + return stripevals; + } + + minstripes = strlen(stripepat); + + /* Scale stripe width and pitch from microns to DEF database units */ + stripewidth_i = (int)(roundf(stripewidth_t * scale)); + stripepitch_i = (int)(roundf(stripepitch_t * scale)); + + /* Adjust stripewidth to the nearest core site unit width. */ + /* If stretching is specified and the minimum fill cell width */ + /* is greater than the core site width, then adjust to the */ + /* nearest multiple of the minimum fill cell width. */ + + if ((!(Flags & NOSTRETCH)) && (corearea->fillmin > corearea->sitew)) { + tw = stripewidth_i / corearea->fillmin; + tr = stripewidth_i % corearea->fillmin; + stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->fillmin; + } + else { + tw = stripewidth_i / corearea->sitew; + tr = stripewidth_i % corearea->sitew; + stripewidth_f = (tw + ((tr == 0) ? 0 : 1)) * corearea->sitew; + } + + if (!(Flags & NOSTRETCH)) + { + /* Find a series of fill cell macros to match the stripe width */ + fillseries = NULL; + dx = stripewidth_f; + while (dx > 0) { + int diff; + + for (testfill = fillcells; testfill; testfill = testfill->next) { + if (testfill->width <= dx) break; + } + if (testfill == NULL) { + /* This can happen if there is no fill cell that is the */ + /* same width as the minimum site pitch. If so, find */ + /* the first non-minimum-size fill cell and change it */ + /* to minimum size, then continue. */ + for (testfill = fillcells; testfill && testfill->next; + testfill = testfill->next); + for (sfill = fillseries; sfill; sfill = sfill->next) + if (sfill->gate != testfill->gate) break; + if (sfill == NULL) { + fprintf(stderr, "Warning: failed to find fill cell series matching" + " the requested stripe width.\n"); + fprintf(stderr, "Stripe width will be modified as needed.\n"); + dx = 0; + break; + } + diff = sfill->width - testfill->width; + sfill->gate = testfill->gate; + sfill->width = testfill->width; + dx += diff; + } + newfill = (FILLLIST)malloc(sizeof(struct filllist_)); + newfill->gate = testfill->gate; + newfill->width = testfill->width; + newfill->next = fillseries; + fillseries = newfill; + dx -= newfill->width; + } + + /* In case the fill series width does not equal the expected */ + /* stripe width, set the stripe width to the fill series width. */ + + stripewidth_f = 0; + for (sfill = fillseries; sfill; sfill = sfill->next) + stripewidth_f += sfill->width; + } + + /* Adjust stripepitch to the nearest core site unit width */ + tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew); + stripepitch_f = (tp * corearea->sitew); + + if (stripepitch_f < stripewidth_f * 2) { + fprintf(stderr, "Error: Stripe pitch is too small (pitch = %g, width = %g)!\n", + (float)stripepitch_f / (float)scale, + (float)stripewidth_f / (float)scale); + return stripevals; + } + if ((fillcells == NULL) && (!(Flags & NOSTRETCH))) { + fprintf(stderr, "No fill cells defined. Not stretching layout.\n"); + Flags |= NOSTRETCH; + } + + if (stripepitch_f != stripepitch_i) { + fprintf(stderr, "Stripe pitch requested = %g, stripe pitch used = %g\n", + stripepitch_t, (float)stripepitch_f / (float)scale); + } + if (stripewidth_f != stripewidth_i) { + fprintf(stderr, "Stripe width requested = %g, stripe width used = %g\n", + stripewidth_t, (float)stripewidth_f / (float)scale); + } + + if (!(Flags & NOSTRETCH)) + { + /* Cell will be stretched, so compute the amount to be */ + /* streteched per stripe, which is the stripewidth */ + /* adjusted to the nearest unit site width. */ + + numstripes = corew / (stripepitch_f - stripewidth_f); + + if (numstripes < minstripes) { + numstripes = minstripes; + + /* Recompute stripe pitch */ + stripepitch_f = corew / numstripes; + tp = (int)(0.5 + (float)stripepitch_f / (float)corearea->sitew); + stripepitch_f = (tp * corearea->sitew); + + fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n", + stripepitch_t, (float)stripepitch_f / (float)scale); + } + totalw = corew + numstripes * stripewidth_f; + + /* Find offset to center of 1st power stripe that results in */ + /* centering the stripes on the layout. */ + + stripeoffset_i = (totalw - (numstripes - 1) * stripepitch_f) / 2; + tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew); + stripeoffset_f = (tp * corearea->sitew); + + /* Add fill cells (approximately) under stripe positions. */ + /* Note that this is independent of drawing the stripes and */ + /* power posts. There is no requirement that the stretch fill */ + /* must be directly under the stripe, only that the total cell */ + /* width is increased by the total width of all stripes, and */ + /* the extra fill is added as close to each stripe as possible. */ + + orient = corearea->orient; + for (y = corearea->lly; y < corearea->ury; y += corearea->siteh) { + nextx = corearea->llx + stripeoffset_f - stripewidth_f / 2; + totalfx = 0; + + x = corearea->llx; + + sprintf(posname, "%dx%d", x, y); + gate = (GATE)HashLookup(posname, &CellPosTable); + + while (x < corearea->urx) { + while (x < nextx) { + nx = x + (int)(roundf(gate->width * scale)); + + /* If next position is larger than nextx but is also */ + /* farther from the stripe centerline than the current */ + /* position, then break here instead. */ + if ((nx > nextx) && ((nextx - x) < (nx - nextx))) break; + + gate->placedX += (double)totalfx / (double)scale; + + sprintf(posname, "%dx%d", nx, y); + gate = (GATE)HashLookup(posname, &CellPosTable); + x = nx; + + if ((x >= corearea->urx) || (gate == NULL)) break; + } + if ((x >= corearea->urx) || (gate == NULL)) break; + + if (Flags & VERBOSE) + fprintf(stdout, "Add fill under stripe from (%d %d) to (%d %d)\n", + x, y, x + stripewidth_f, y); + + for (testfill = fillseries; testfill; testfill = testfill->next) { + /* Create new fill instance */ + newfillinst = (GATE)malloc(sizeof(struct gate_)); + newfillinst->gatetype = testfill->gate; + sprintf(posname, "SFILL%dx%d", x + totalfx, y); + newfillinst->gatename = strdup(posname); + newfillinst->placedX = (double)(x + totalfx) / (double)scale; + newfillinst->placedY = (double)y / (double)scale; + newfillinst->clientdata = (void *)NULL; + row = DefFindRow(y); + newfillinst->orient = (row) ? row->orient : orient; + DefAddGateInstance(newfillinst); + + /* Position will not be revisited, so no need to */ + /* add to the position hash. */ + + totalfx += testfill->width; + } + nextx += stripepitch_f - stripewidth_f; + } + orient = (orient == RN) ? RS : RN; + } + + /* Adjust pins */ + + for (gate = Nlgates; gate; gate = gate->next) { + if (gate->gatetype == NULL) { + int px, po, pitches; + + px = (int)(roundf(gate->placedX * scale)); + po = px - stripeoffset_f - (stripewidth_f / 2); + if (po > 0) + pitches = 1 + po / (stripepitch_f - stripewidth_f); + else + pitches = -1; + if (pitches <= 0) continue; + + px += pitches * stripewidth_f; + gate->placedX = (float)(px) / scale; + } + } + + if (Flags & VERBOSE) fprintf(stdout, "Layout stretched by %g um\n", + (double)totalfx / (double)scale); + } + else + { + /* Stripes are overlaid on core without stretching */ + numstripes = corew / stripepitch_f; + if (numstripes < minstripes) { + numstripes = minstripes; + + /* Recompute stripe pitch */ + stripepitch_f = corew / numstripes; + tp = (int)(0.5 + (float)stripepitch_i / (float)corearea->sitew); + stripepitch_f = (tp * corearea->sitew); + + fprintf(stdout, "Stripe pitch reduced from %g to %g to fit in layout\n", + stripepitch_t, (float)stripepitch_f / (float)scale); + } + totalw = corew; + + /* Find offset to center of 1st power stripe that results in */ + /* centering the stripes on the layout. */ + + stripeoffset_i = (totalw - (numstripes - 1) * stripewidth_f) / 2; + tp = (int)(0.5 + (float)stripeoffset_i / (float)corearea->sitew); + stripeoffset_f = (tp * corearea->sitew); + } + + /* Record expanded area */ + corearea->urx_exp = totalw + corearea->llx; + + /* Record and return final calculated power stripe pitch and width */ + stripevals->pitch = stripepitch_f; + stripevals->width = stripewidth_f; + stripevals->offset = stripeoffset_f; + stripevals->stretch = totalfx; + stripevals->number = numstripes; + return stripevals; +} + + +/*--------------------------------------------------------------*/ +/* Adjust obstructions */ +/* If OBSTRUCT flag is set, then obstructions are to be read */ +/* from the file <rootname>.obs, modified, and written to file */ +/* <rootname>.obsx. */ +/*--------------------------------------------------------------*/ + +void +fix_obstructions(char *definname, SINFO stripevals, float scale, + unsigned char Flags) +{ + FILE *fobsin, *fobsout; + char *filename, *pptr; + char line[256]; + char layer[32]; + float fllx, flly, furx, fury; + int illx, iurx, pitches, po; + + /* If no layout stretching was done, then nothing needs to be modified */ + if (Flags & NOSTRETCH) return; + + /* Only handle obstruction layer file if the -O switch was specified */ + /* (to do: Handle obstructions via the DEF file BLOCKAGES records) */ + + if (!(Flags & OBSTRUCT)) return; + + filename = (char *)malloc(strlen(definname) + 6); + strcpy(filename, definname); + pptr = strrchr(filename, '.'); + if (pptr == NULL) + strcat(filename, ".obs"); + else + sprintf(pptr, ".obs"); + + fobsin = fopen(filename, "r"); + if (fobsin == NULL) { + fprintf(stderr, "Cannot open obstruction file %s for reading\n", filename); + free(filename); + return; + } + + pptr = strrchr(filename, '.'); + sprintf(pptr, ".obsx"); + + fobsout = fopen(filename, "w"); + if (fobsout == NULL) { + fprintf(stderr, "Cannot open obstruction file %s for writing\n", filename); + fclose(fobsin); + free(filename); + return; + } + + if (Flags & VERBOSE) fprintf(stdout, "Modifying obstruction positions.\n"); + + while (1) { + if (fgets(line, 256, fobsin) == NULL) break; + + if (!strncmp(line, "obstruction", 11)) { + sscanf(line + 11, "%g %g %g %g %s", &fllx, &flly, &furx, &fury, layer); + + if (Flags & VERBOSE) + fprintf(stdout, "In: %g %g %g %g\n", fllx, flly, furx, fury); + + illx = (int)(roundf(fllx * scale)); + iurx = (int)(roundf(furx * scale)); + + po = illx - stripevals->offset - (stripevals->width / 2); + if (po > 0) { + pitches = 1 + po / (stripevals->pitch - stripevals->width); + illx += pitches * stripevals->width; + } + po = iurx - stripevals->offset - (stripevals->width / 2); + if (po > 0) { + pitches = 1 + po / (stripevals->pitch - stripevals->width); + iurx += pitches * stripevals->width; + } + + fllx = (float)illx / scale; + furx = (float)iurx / scale; + + fprintf(fobsout, "obstruction %g %g %g %g %s\n", + fllx, flly, furx, fury, layer); + + if (Flags & VERBOSE) + fprintf(stdout, "Out: %g %g %g %g\n", fllx, flly, furx, fury); + } + } + + free(filename); + fclose(fobsin); + fclose(fobsout); +} + +/*--------------------------------------------------------------*/ +/* Create a new VIA record from a VIA or VIARULE record, with */ +/* total width and height as given. */ +/*--------------------------------------------------------------*/ + +void +via_make_generated(LefList viagen, LefList lefl, int lbot, int lcut, + int width, int height, float scale) +{ + float cutsizex, cutsizey; + float bboundx, bboundy; + float tboundx, tboundy; + + int xcuts, ycuts; + char vianame[128]; + DSEG newseg; + LefList cutrec; + + float borderx, bordery, spacingx, spacingy; + float fwidth, fheight; + float x, y; + int i, j; + + int ltop = lbot + 1; + + /* Convert width and height to microns */ + fwidth = (float)width / scale; + fheight = (float)height / scale; + + sprintf(vianame, "%s_post", lefl->lefName); + viagen->lefName = strdup(vianame); + + /* Determine number of cuts in X and Y */ + + cutsizex = LefGetViaWidth(lefl, lcut, 0); + cutsizey = LefGetViaWidth(lefl, lcut, 1); + bboundx = LefGetViaWidth(lefl, lbot, 0); + bboundy = LefGetViaWidth(lefl, lbot, 1); + tboundx = LefGetViaWidth(lefl, ltop, 0); + tboundy = LefGetViaWidth(lefl, ltop, 1); + + /* Calculate number of cuts to fit */ + + borderx = (((tboundx > bboundx) ? tboundx : bboundx) - cutsizex) / 2; + bordery = (((tboundy > bboundy) ? tboundy : bboundy) - cutsizey) / 2; + + /* If there is a SPACING record in the via, use it. If not, see if */ + /* there is a SPACING record in the record for the via cut. If */ + /* not, then assume spacing is twice the border width. */ + + cutrec = lefl; + if (cutrec->info.via.spacing == NULL) cutrec = LefFindLayerByNum(lcut); + if (cutrec && cutrec->info.via.spacing) { + spacingx = cutrec->info.via.spacing->spacing; + if (cutrec->info.via.spacing->next) + spacingy = cutrec->info.via.spacing->next->spacing; + else + spacingy = spacingx; + } + else { + spacingx = 2 * borderx; + spacingy = 2 * bordery; + } + + xcuts = 1 + (int)((fwidth - 2 * borderx) - cutsizex) / (cutsizex + spacingx); + ycuts = 1 + (int)((fheight - 2 * bordery) - cutsizey) / (cutsizey + spacingy); + + /* Ensure at least one cut! */ + if (xcuts < 1) xcuts = 1; + if (ycuts < 1) ycuts = 1; + + /* Make sure that width and height are enough to pass DRC. Height */ + /* in particular is taken from the width of the power bus and may */ + /* not be wide enough for the topmost contacts. */ + + fwidth = (xcuts * cutsizex) + ((float)(xcuts - 1) * spacingx) + (2 * borderx); + fheight = (ycuts * cutsizey) + ((float)(ycuts - 1) * spacingy) + (2 * bordery); + + viagen->info.via.area.layer = lbot; + viagen->info.via.area.x1 = -fwidth / 2; + viagen->info.via.area.x2 = fwidth / 2; + viagen->info.via.area.y1 = -fheight / 2; + viagen->info.via.area.y2 = fheight / 2; + + newseg = (DSEG)malloc(sizeof(struct dseg_)); + newseg->layer = ltop; + newseg->x1 = -fwidth / 2; + newseg->x2 = fwidth / 2; + newseg->y1 = -fheight / 2; + newseg->y2 = fheight / 2; + newseg->next = NULL; + viagen->info.via.lr = newseg; + + x = (-fwidth / 2) + borderx + (cutsizex / 2); + for (i = 0; i < xcuts; i++) { + y = (-fheight / 2) + bordery + (cutsizey / 2); + for (j = 0; j < ycuts; j++) { + newseg = (DSEG)malloc(sizeof(struct dseg_)); + newseg->layer = lcut; + newseg->x1 = x - (cutsizex / 2); + newseg->x2 = x + (cutsizex / 2); + newseg->y1 = y - (cutsizey / 2); + newseg->y2 = y + (cutsizey / 2); + newseg->next = viagen->info.via.lr; + viagen->info.via.lr = newseg; + y += (cutsizey + spacingy); + } + x += (cutsizex + spacingy); + } +} + +/*--------------------------------------------------------------*/ +/* Routine to check if a LefList entry is a valid via record */ +/* for a via between metal layers "l" (ell) and "l + 1". */ +/* Since the LefList record drops metal and cut layers more or */ +/* less randomly into the area, lr, and lr->next records, all */ +/* combinations of layers need to be checked. */ +/* */ +/* Since the metal layers are known and the cut layer is not */ +/* necessarily known, return the cut layer number if the via */ +/* record is valid. If not, return -1. */ +/*--------------------------------------------------------------*/ + +int +check_valid_via(LefList lefl, int l) +{ + int cutlayer = -1; + + if (lefl->info.via.area.layer == l) { + if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) { + if (lefl->info.via.lr->next) + cutlayer = lefl->info.via.lr->next->layer; + } + else if (lefl->info.via.lr && lefl->info.via.lr->next && + lefl->info.via.lr->next->layer == l + 1) { + cutlayer = lefl->info.via.lr->layer; + } + } + else if (lefl->info.via.area.layer == l + 1) { + if (lefl->info.via.lr && lefl->info.via.lr->layer == l) { + if (lefl->info.via.lr->next) + cutlayer = lefl->info.via.lr->next->layer; + } + else if (lefl->info.via.lr && lefl->info.via.lr->next && + lefl->info.via.lr->next->layer == l) { + cutlayer = lefl->info.via.lr->layer; + } + } + else if (lefl->info.via.lr && lefl->info.via.lr->layer == l) { + if (lefl->info.via.lr && lefl->info.via.lr->next && + lefl->info.via.lr->next->layer == l + 1) + cutlayer = lefl->info.via.area.layer; + } + else if (lefl->info.via.lr && lefl->info.via.lr->layer == l + 1) { + if (lefl->info.via.lr && lefl->info.via.lr->next && + lefl->info.via.lr->next->layer == l) + cutlayer = lefl->info.via.area.layer; + } + return cutlayer; +} + +/*--------------------------------------------------------------*/ +/* Generate stripe contact posts and metal */ +/*--------------------------------------------------------------*/ + +PSTRIPE +generate_stripes(SINFO stripevals, FILLLIST fillcells, + COREBBOX corearea, char *pattern, + char *VddNet, char *GndNet, float scale, unsigned char Flags) +{ + int i, j, l, p, n, y, hh; + int testuse; + int ltop, lbot; + float syt, syb; + double vw, vh; + int cutlayer, lcut; + int gnd_ymin, gnd_ymax, vdd_ymin, vdd_ymax, gate_ymin; + int gnd_xmin, vdd_xmin, tmp; + char *powername, *groundname; + int corew; + + PSTRIPE rails = NULL, prail; + PPOST post; + LefList lefl, *vialist, topvia, vvalid; + DSEG lr; + + /* Pick the first fill cell and parse it for the POWER and */ + /* GROUND pins as marked by pin USE. If no pins are marked */ + /* as use POWER or GROUND then look for pins with names */ + /* matching the power and ground nets. */ + + GATE fillgate = fillcells->gate; + DSEG r; + ROW row; + + lbot = 0; + for (i = 0; i < fillgate->nodes; i++) { + testuse = fillgate->use[i]; + if (testuse == PORT_USE_POWER) { + powername = fillgate->node[i]; + break; + } + } + if (i == fillgate->nodes) { + for (i = 0; i < fillgate->nodes; i++) { + if (!strcmp(fillgate->node[i], VddNet)) { + powername = VddNet; + lbot = fillgate->taps[i]->layer; + break; + } + } + if (i == fillgate->nodes) { + fprintf(stderr, "Failed to find power net pin in cell macro.\n"); + return NULL; + } + } + /* NOTE: Need to parse all taps; find one that crosses the whole */ + /* cell. If none, then find one that touches or overlaps the cell */ + /* bottom or top. */ + + r = fillcells->gate->taps[i]; + vdd_ymin = (int)(roundf(r->y1 * scale)); + vdd_ymax = (int)(roundf(r->y2 * scale)); + vdd_xmin = (int)(roundf(r->x1 * scale)); + + for (j = 0; j < fillgate->nodes; j++) { + testuse = fillgate->use[j]; + if (testuse == PORT_USE_GROUND) { + groundname = fillgate->node[j]; + break; + } + } + if (j == fillgate->nodes) { + for (j = 0; j < fillgate->nodes; j++) { + if (!strcmp(fillgate->node[j], GndNet)) { + groundname = GndNet; + lbot = fillgate->taps[i]->layer; + break; + } + } + if (j == fillgate->nodes) { + fprintf(stderr, "Failed to find ground net pin in cell macro.\n"); + return NULL; + } + } + r = fillcells->gate->taps[j]; + gnd_ymin = (int)(roundf(r->y1 * scale)); + gnd_ymax = (int)(roundf(r->y2 * scale)); + gnd_xmin = (int)(roundf(r->x1 * scale)); + + /* If the first row is inverted then the ymin/ymax values need to be */ + /* adjusted. */ + + row = DefLowestRow(); /* Try this first */ + if (row) { + if (row->orient & RS) { + gnd_ymax = corearea->siteh - gnd_ymax; + gnd_ymin = corearea->siteh - gnd_ymin; + vdd_ymax = corearea->siteh - vdd_ymax; + vdd_ymin = corearea->siteh - vdd_ymin; + + tmp = gnd_ymax; + gnd_ymax = gnd_ymin; + gnd_ymin = tmp; + + tmp = vdd_ymax; + vdd_ymax = vdd_ymin; + vdd_ymin = tmp; + } + } + else { + if (corearea->orient & RS) { + gnd_ymax = corearea->siteh - gnd_ymax; + gnd_ymin = corearea->siteh - gnd_ymin; + vdd_ymax = corearea->siteh - vdd_ymax; + vdd_ymin = corearea->siteh - vdd_ymin; + + tmp = gnd_ymax; + gnd_ymax = gnd_ymin; + gnd_ymin = tmp; + + tmp = vdd_ymax; + vdd_ymax = vdd_ymin; + vdd_ymin = tmp; + } + } + + n = strlen(pattern); + + /* Find the highest metal layer that is oriented vertically */ + + ltop = LefGetMaxRouteLayer() - 1; + if (LefGetRouteOrientation(ltop) == 1) ltop--; + if (ltop < 3) { + int mspace; + + fprintf(stderr, "Will not generate over-cell power stripes due to lack " + "of route layers\n"); + fprintf(stderr, "Generating comb structures instead.\n"); + + /* Generate comb structures in metal1 on either side to connect */ + /* power and ground. */ + + /* Get wide spacing rule relative to stripe width */ + mspace = (int)(roundf(LefGetRouteWideSpacing(lbot, + (float)(stripevals->width) / scale) * scale)); + + /* Account for ground or power bus extending beyond the cell */ + /* bounding box. Assumes that the extension is symmetric on */ + /* left and right, and the same for power and ground. */ + + if (gnd_xmin < 0) mspace -= gnd_xmin; + else if (vdd_xmin < 0) mspace -= vdd_xmin; + corew = corearea->sitew; + + /* Generate power comb on left */ + + prail = (PSTRIPE)malloc(sizeof(struct _powerstripe)); + prail->next = rails; + rails = prail; + + prail->offset = -mspace - stripevals->width / 2; + prail->pitch = corearea->urx_exp; + prail->num = 1; + if ((n < 1) || (pattern[0] == 'P')) { + prail->name = VddNet; + y = corearea->lly + (vdd_ymax + vdd_ymin) / 2; + hh = (vdd_ymax - vdd_ymin) / 2; + } + else { + prail->name = GndNet; + y = corearea->lly + (gnd_ymax + gnd_ymin) / 2; + hh = (gnd_ymax - gnd_ymin) / 2; + } + + prail->stripe = (DSEG)malloc(sizeof(struct dseg_)); + prail->stripe->layer = lbot; + prail->stripe->next = NULL; + prail->stripe->x1 = -(float)(stripevals->width / 2) / scale; + prail->stripe->x2 = (float)(stripevals->width / 2) / scale; + prail->stripe->y1 = (float)(corearea->lly - hh) / scale; + prail->stripe->y2 = (float)(corearea->ury + hh) / scale; + prail->posts = NULL; + + /* Create all posts */ + + for (; y <= corearea->ury; y += 2 * corearea->siteh) { + PPOST newpost = (PPOST)malloc(sizeof(powerpost)); + newpost->strut = (DSEG)malloc(sizeof(struct dseg_)); + newpost->viagen = NULL; + newpost->strut->layer = lbot; + newpost->strut->next = NULL; + newpost->strut->x1 = 0.0; + newpost->strut->x2 = (float)(-prail->offset + corew) / scale; + newpost->strut->y1 = (float)(y - hh) / scale; + newpost->strut->y2 = (float)(y + hh) / scale; + newpost->next = prail->posts; + prail->posts = newpost; + } + prail->offset += corearea->llx; + + /* Generate ground comb on right */ + + prail = (PSTRIPE)malloc(sizeof(struct _powerstripe)); + prail->next = rails; + rails = prail; + + prail->offset = mspace + stripevals->width / 2; + prail->pitch = corearea->urx_exp; + prail->num = 1; + if ((n < 2) || (pattern[1] == 'G')) { + prail->name = GndNet; + y = corearea->lly + (gnd_ymax + gnd_ymin) / 2; + hh = (gnd_ymax - gnd_ymin) / 2; + } + else { + prail->name = VddNet; + y = corearea->lly + (vdd_ymax + vdd_ymin) / 2; + hh = (vdd_ymax - vdd_ymin) / 2; + } + + prail->stripe = (DSEG)malloc(sizeof(struct dseg_)); + prail->stripe->layer = lbot; + prail->stripe->next = NULL; + prail->stripe->x1 = -(float)(stripevals->width / 2) / scale; + prail->stripe->x2 = (float)(stripevals->width / 2) / scale; + prail->stripe->y1 = (float)(corearea->lly - hh) / scale; + prail->stripe->y2 = (float)(corearea->ury + hh) / scale; + prail->posts = NULL; + + /* Create all posts */ + + for (; y <= corearea->ury; y += 2 * corearea->siteh) { + PPOST newpost = (PPOST)malloc(sizeof(powerpost)); + newpost->strut = (DSEG)malloc(sizeof(struct dseg_)); + newpost->viagen = NULL; + newpost->strut->layer = lbot; + newpost->strut->next = NULL; + newpost->strut->x1 = (float)(-prail->offset - corew) / scale; + newpost->strut->x2 = 0.0; + newpost->strut->y1 = (float)(y - hh) / scale; + newpost->strut->y2 = (float)(y + hh) / scale; + newpost->next = prail->posts; + prail->posts = newpost; + } + prail->offset += corearea->urx_exp; + return rails; + } + + /* Generate vias for posts */ + /* NOTE: This assumes that the power and ground rails are the same */ + /* height. If not, need to have two vialist records, one for each rail */ + + vialist = (LefList *)malloc((ltop - lbot) * sizeof(LefList)); + for (l = lbot; l < ltop; l++) vialist[l] = (LefList)NULL; + + /* First find any VIARULE GENERATE vias; these are preferred, as they */ + /* have all the right information for generating a new via for the */ + /* post. */ + + for (l = lbot; l < ltop; l++) { + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.generated) { + + /* Check for top and bottom layers matching (l) and (l + 1) */ + + if ((cutlayer = check_valid_via(lefl, l)) != -1) { + vialist[l] = LefNewVia(NULL); + via_make_generated(vialist[l], lefl, l, cutlayer, + stripevals->width, gnd_ymax - gnd_ymin, scale); + break; // Continue to next layer + } + } + } + } + } + + /* Next find any VIAs that have the right layers. Use this information */ + /* to create a new VIA record with the correct size for the post. */ + + for (l = lbot; l < ltop; l++) { + if (vialist[l] == NULL) { + vvalid = NULL; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass == CLASS_VIA) { + if ((lcut = check_valid_via(lefl, l)) != -1) { + /* Don't include vias that this routine has created, */ + if (strstr(lefl->lefName, "_post") != NULL) continue; + if (vvalid == NULL) { + vvalid = lefl; + vw = LefGetViaWidth(lefl, lcut, 0); + vh = LefGetViaWidth(lefl, lcut, 1); + cutlayer = lcut; + } + /* Find the smallest valid via. Note that it */ + /* is preferred to find a via designed for a */ + /* power post, if there is one (to be done). */ + else { + double tw, th; + tw = LefGetViaWidth(lefl, lcut, 0); + th = LefGetViaWidth(lefl, lcut, 1); + if ((th < vh) || ((th == vh) && (tw < vw))) { + vvalid = lefl; + vw = tw; + vh = th; + cutlayer = lcut; + } + } + } + } + } + if (vvalid) { + vialist[l] = LefNewVia(NULL); + via_make_generated(vialist[l], vvalid, l, cutlayer, + stripevals->width, gnd_ymax - gnd_ymin, scale); + } + } + } + + for (l = lbot; l < ltop; l++) { + if (vialist[l] == NULL) { + LefList ll0, ll1; + ll0 = LefFindLayerByNum(l); + ll1 = LefFindLayerByNum(l + 1); + fprintf(stderr, "Error: Failed to find a valid via record between " + "metal layers %s and %s\n", + ll0->lefName, ll1->lefName); + return NULL; + } + } + + /* Construct power stripe records */ + + for (p = 0; p < n; p++) { + + prail = (PSTRIPE)malloc(sizeof(struct _powerstripe)); + prail->next = rails; + rails = prail; + + prail->offset = stripevals->offset + p * stripevals->pitch; + prail->pitch = stripevals->pitch * n; + prail->num = 1 + (corearea->urx_exp - prail->offset) / prail->pitch; + + /* Note this is not strdup(), so can compare string pointers */ + prail->name = (pattern[p] == 'P') ? VddNet : GndNet; + + /* Find vertical dimensions of power rails on the standard cells */ + y = corearea->lly; + if (pattern[p] == 'P') { + y += (vdd_ymax + vdd_ymin) / 2; + hh = (vdd_ymax - vdd_ymin) / 2; + } + else { + y += (gnd_ymax + gnd_ymin) / 2; + hh = (gnd_ymax - gnd_ymin) / 2; + } + + /* Query the extent of the highest via layer and make sure the */ + /* power stripe covers it completely. */ + + topvia = vialist[ltop - 1]; + syb = topvia->info.via.area.y1; + syt = topvia->info.via.area.y2; + for (lr = topvia->info.via.lr; lr; lr = lr->next) { + if (lr->y1 < syb) syb = lr->y1; + if (lr->y2 > syt) syt = lr->y2; + } + + /* First two rails, extend down by one track pitch. A pin will */ + /* be added here. */ + if (p < 2) { + syb = -LefGetRoutePitch(ltop - 1); + if (syb > topvia->info.via.area.y1) + syb -= LefGetRoutePitch(ltop - 1); + } + + prail->stripe = (DSEG)malloc(sizeof(struct dseg_)); + prail->stripe->layer = ltop; + prail->stripe->next = NULL; + prail->stripe->x1 = -(float)(stripevals->width / 2) / scale; + prail->stripe->x2 = (float)(stripevals->width / 2) / scale; + prail->stripe->y1 = syb + ((float)corearea->lly / scale); + prail->stripe->y2 = syt + ((float)corearea->ury / scale); + prail->posts = NULL; + + /* Create all posts (also horizontally centered at X = 0) */ + + /* To be done: Check if rows are alternating N and S */ + /* or not. This code assumes that they always are. */ + + for (; y <= corearea->ury; y += 2 * corearea->siteh) { + for (l = lbot; l < ltop; l++) { + PPOST newpost = (PPOST)malloc(sizeof(powerpost)); + newpost->strut = (DSEG)malloc(sizeof(struct dseg_)); + newpost->viagen = vialist[l]; + newpost->strut->layer = l; + newpost->strut->next = NULL; + newpost->strut->x1 = -(float)(stripevals->width / 2) / scale; + newpost->strut->x2 = (float)(stripevals->width / 2) / scale; + newpost->strut->y1 = (float)(y - hh) / scale; + newpost->strut->y2 = (float)(y + hh) / scale; + newpost->next = prail->posts; + prail->posts = newpost; + } + } + } + + /* link via records and prepend to LefInfo */ + for (l = lbot; l < ltop - 1; l++) + vialist[l]->next = vialist[l + 1]; + + vialist[l]->next = LefInfo; + LefInfo = vialist[0]; + + free(vialist); + return rails; +} + +/*--------------------------------------------------------------*/ +/* Convert orient bitfield from GATE structure to character */ +/* string for a DEF file. */ +/*--------------------------------------------------------------*/ + +char *gate_to_orient(int orient) +{ + int oidx; + static char *orients[8] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"}; + + switch (orient & (RN | RS | RE | RW)) { + case RS: + oidx = 1; + break; + case RE: + oidx = 2; + break; + case RW: + oidx = 3; + break; + default: + oidx = 0; + break; + } + if (orient & RF) oidx += 4; + return orients[oidx]; +} + +/*--------------------------------------------------------------*/ +/*--------------------------------------------------------------*/ + +void +output_rail(FILE *outfptr, PSTRIPE rail, int x, int first, float scale) +{ + PPOST post; + LefList lefl; + char *otyp; + float fyd, fya, fxd, fxa; + int iyd, iya, ixd, ixa; + + static char *otypes[] = {"+ FIXED", " NEW"}; + + otyp = (first) ? otypes[0] : otypes[1]; + + for (post = rail->posts; post; post = post->next) { + lefl = LefFindLayerByNum(post->strut->layer); + fyd = post->strut->y2 - post->strut->y1; + fya = (post->strut->y2 + post->strut->y1) / 2; + iyd = (int)(roundf(fyd * scale)); + iya = (int)(roundf(fya * scale)); + if (post->viagen) { + fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * * ) %s", + otyp, lefl->lefName, iyd, x, iya, post->viagen->lefName); + } + else { + fxd = post->strut->x1; + ixd = x + (int)(roundf(fxd * scale)); + fxa = post->strut->x2; + ixa = x + (int)(roundf(fxa * scale)); + fprintf(outfptr, "\n%s %s %d ( %d %d ) ( %d * )", + otyp, lefl->lefName, iyd, ixd, iya, ixa); + } + otyp = otypes[1]; + } + lefl = LefFindLayerByNum(rail->stripe->layer); + fxd = rail->stripe->x2 - rail->stripe->x1; + fya = rail->stripe->y1; + fyd = rail->stripe->y2; + ixd = (int)(roundf(fxd * scale)); + iya = (int)(roundf(fya * scale)); + iyd = (int)(roundf(fyd * scale)); + fprintf(outfptr, "\n%s %s %d ( %d %d ) ( * %d )", + otyp, lefl->lefName, ixd, x, iya, iyd); +} + +/*--------------------------------------------------------------*/ +/*--------------------------------------------------------------*/ + +void +output_rails(FILE *outfptr, PSTRIPE rail, COREBBOX corearea, float scale, int first) +{ + int i, x; + + x = rail->offset; + for (i = 0; i < rail->num; i++) { + output_rail(outfptr, rail, x, first, scale); + first = FALSE; + x += rail->pitch; + if (x > corearea->urx_exp) break; + } +} + +/*--------------------------------------------------------------*/ +/* write_output --- */ +/* */ +/* write the modified DEF file to the output. */ +/*--------------------------------------------------------------*/ + +void +write_output(char *definname, char *defoutname, float scale, + COREBBOX corearea, SINFO stripevals, PSTRIPE rails, + char *VddNet, char *GndNet, unsigned char Flags) +{ + FILE *outfptr, *infptr; + static char line[LEF_LINE_MAX + 2]; + char *sptr; + int i, copyspecial = 0, numVias, foundrail[2], ltop; + double lh, ly; + + GATE gate, endgate; + NET net; + NODE node; + PPOST post; + LefList lefl, lname, lrec; + DSEG seg; + PSTRIPE rail; + + if (defoutname == NULL) + outfptr = stdout; + else { + outfptr = fopen(defoutname, "w"); + if (outfptr == NULL) { + fprintf(stderr, "Error: Failed to open file %s for writing modified output\n", + defoutname); + return; + } + } + + if (Flags & VERBOSE) fprintf(stdout, "Writing DEF file output.\n"); + + /* Find the number of (new) power rail SPECIALNETS to be written */ + /* There will normally be one record for power and one for ground */ + /* unless rails were not written. Power and ground rails are */ + /* checked separately for consistency of the output DEF. */ + + foundrail[0] = foundrail[1] = FALSE; + for (rail = rails; rail; rail = rail->next) { + if ((foundrail[0] == FALSE) && (rail->name == VddNet)) foundrail[0] = TRUE; + if ((foundrail[1] == FALSE) && (rail->name == GndNet)) foundrail[1] = TRUE; + } + numSpecial = ((foundrail[0] == TRUE) ? 1 : 0) + ((foundrail[1] == TRUE) ? 1 : 0); + + /* Write DEF header (copy input DEF file verbatim up to COMPONENTS) */ + + infptr = fopen(definname, "r"); + while (1) { + if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) { + fprintf(stderr, "Error: End of file reached before COMPONENTS.\n"); + return; + } + sptr = line; + while (isspace(*sptr)) sptr++; + + /* Assuming a typical DEF file here. . . */ + if (!strncmp(sptr, "COMPONENTS", 10)) break; + + /* Rewrite DIEAREA, ROWS, and TRACKS */ + else if (!strncmp(sptr, "DIEAREA", 7)) { + char *dptr; + int dllx, dlly, durx, dury; + + dptr = strchr(line, '('); + sscanf(dptr + 1, "%d %d", &dllx, &dlly); + dptr = strchr(dptr + 1, '('); + sscanf(dptr + 1, "%d %d", &durx, &dury); + + durx += stripevals->stretch; + + fprintf(outfptr, "DIEAREA ( %d %d ) ( %d %d ) ;\n", + dllx, dlly, durx, dury); + } + + else if (!strncmp(sptr, "ROW", 3)) { + char *dptr; + int radd, xnum, ridx, rowy; + ROW row; + char namepos[16]; + radd = stripevals->stretch / corearea->sitew; + static char *orientations[] = {"N", "S", "E", "W", "FN", "FS", "FE", "FW"}; + + dptr = sptr; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%d", &rowy); + row = DefFindRow(rowy); + + xnum = row->xnum + radd; + switch (row->orient & (RN | RS | RE | RW)) { + case RS: + ridx = 1; + break; + case RE: + ridx = 2; + break; + case RW: + ridx = 3; + break; + default: + ridx = 0; + } + if (row->orient & RF) ridx += 4; + + fprintf(outfptr, "ROW %s %s %d %d %s DO %d BY %d STEP %d %d ;\n", + row->rowname, row->sitename, row->x, row->y, + orientations[ridx], xnum, row->ynum, + row->xstep, row->ystep); + } + else if (!strncmp(sptr, "TRACKS", 6)) { + char *dptr; + char o; + char layer[64]; + int roffset, rnum, rpitch; + + dptr = sptr + 6; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%c", &o); + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%d", &roffset); + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%d", &rnum); + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%d", &rpitch); + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + while (!isspace(*dptr)) dptr++; + while (isspace(*dptr)) dptr++; + sscanf(dptr, "%s", layer); + + if (o == 'X') { + rnum += (int)(stripevals->stretch / rpitch); + if (stripevals->stretch % rpitch != 0) rnum++; + } + fprintf(outfptr, "TRACKS %c %d DO %d STEP %d LAYER %s ;\n", + o, roffset, rnum, rpitch, layer); + } + else + fprintf(outfptr, "%s", line); + } + + /* Write generated vias for posts */ + + numVias = 0; + for (lefl = LefInfo; lefl; lefl = lefl->next) + if (strstr(lefl->lefName, "_post") != NULL) + numVias++; + + fprintf(outfptr, "VIAS %d ;\n", numVias); + for (lefl = LefInfo; lefl; lefl = lefl->next) { + int llx, lly, urx, ury; + + if (strstr(lefl->lefName, "_post") != NULL) { + fprintf(outfptr, "- %s\n", lefl->lefName); + lname = LefFindLayerByNum(lefl->info.via.area.layer); + llx = (int)(roundf(lefl->info.via.area.x1 * scale)); + lly = (int)(roundf(lefl->info.via.area.y1 * scale)); + urx = (int)(roundf(lefl->info.via.area.x2 * scale)); + ury = (int)(roundf(lefl->info.via.area.y2 * scale)); + fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )", + lname->lefName, llx, lly, urx, ury); + if (lefl->info.via.lr) fprintf(outfptr, "\n"); + for (seg = lefl->info.via.lr; seg; seg = seg->next) { + lname = LefFindLayerByNum(seg->layer); + llx = (int)(roundf(seg->x1 * scale)); + lly = (int)(roundf(seg->y1 * scale)); + urx = (int)(roundf(seg->x2 * scale)); + ury = (int)(roundf(seg->y2 * scale)); + fprintf(outfptr, "+ RECT %s ( %d %d ) ( %d %d )", + lname->lefName, llx, lly, urx, ury); + if (seg->next) fprintf(outfptr, "\n"); + } + fprintf(outfptr, " ;\n"); + } + } + fprintf(outfptr, "END VIAS\n\n"); + + for (endgate = Nlgates; endgate->next; endgate = endgate->next); + + if (Numgates > 0) { + + /* Write instances (COMPONENTS) in the order read */ + fprintf(outfptr, "COMPONENTS %d ;\n", Numgates); + for (gate = endgate; gate ; gate = gate->last) { + int px, py; + if (gate->gatetype == NULL) continue; + + px = (int)(roundf(gate->placedX * scale)); + py = (int)(roundf(gate->placedY * scale)); + fprintf(outfptr, "- %s %s + PLACED ( %d %d ) %s ;\n", + gate->gatename, gate->gatetype->gatename, + px, py, gate_to_orient(gate->orient)); + } + fprintf(outfptr, "END COMPONENTS\n\n"); + } + + if (Numpins > 0) { + int llx, lly, urx, ury, px, py; + static char *pin_classes[] = { + "DEFAULT", "INPUT", "OUTPUT", "OUTPUT TRISTATE", "INOUT", "FEEDTHRU" + }; + LefList lefl; + + /* Write instances (PINS) in the order read, plus power pins */ + + fprintf(outfptr, "PINS %d ;\n", Numpins + numSpecial); + + if (foundrail[0]) { + for (rail = rails; rail; rail = rail->next) + if (rail->name == VddNet) break; + + ltop = rail->stripe->layer; + lrec = LefFindLayerByNum(ltop); + lh = LefGetRoutePitch(ltop - 1) / 4; + ly = rail->stripe->y1 + lh; + + /* NOTE: The over-simplified "arrangepins" Tcl script expects */ + /* LAYER and PLACED records to be on separate lines. This will */ + /* eventually be replaced by a C coded executable with a more */ + /* rigorous parser, like addspacers uses. */ + + fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", VddNet, VddNet); + fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n", + lrec->lefName, + (int)(roundf(rail->stripe->x1 * scale)), + (int)(roundf(-lh * scale)), + (int)(roundf(rail->stripe->x2 * scale)), + (int)(roundf(lh * scale))); + fprintf(outfptr, " + PLACED ( %d %d ) N ;\n", + rail->offset, (int)(roundf(ly * scale))); + } + if (foundrail[1]) { + for (rail = rails; rail; rail = rail->next) + if (rail->name == GndNet) break; + + ltop = rail->stripe->layer; + lrec = LefFindLayerByNum(ltop); + lh = LefGetRoutePitch(ltop - 1) / 4; + ly = rail->stripe->y1 + lh; + + fprintf(outfptr, "- %s + NET %s + DIRECTION INOUT\n", GndNet, GndNet); + fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n", + lrec->lefName, + (int)(roundf(rail->stripe->x1 * scale)), + (int)(roundf(-lh * scale)), + (int)(roundf(rail->stripe->x2 * scale)), + (int)(roundf(lh * scale))); + fprintf(outfptr, " + PLACED ( %d %d ) N ;\n", + rail->offset, (int)(roundf(ly * scale))); + } + + for (gate = endgate; gate ; gate = gate->last) { + int dir; + + if (gate->gatetype != NULL) continue; + + fprintf(outfptr, "- %s + NET %s", + gate->gatename, gate->node[0]); + + if (gate->direction[0] != 0) + fprintf(outfptr, " + DIRECTION %s", + pin_classes[gate->direction[0]]); + + fprintf(outfptr, "\n"); + + lefl = LefFindLayerByNum(gate->taps[0]->layer); + urx = (int)(roundf((gate->taps[0]->x2 - gate->taps[0]->x1) * scale) / 2.0); + ury = (int)(roundf((gate->taps[0]->y2 - gate->taps[0]->y1) * scale) / 2.0); + llx = -urx; + lly = -ury; + px = (int)(roundf(gate->placedX * scale)); + py = (int)(roundf(gate->placedY * scale)); + + fprintf(outfptr, " + LAYER %s ( %d %d ) ( %d %d )\n", + lefl->lefName, llx, lly, urx, ury); + fprintf(outfptr, " + PLACED ( %d %d ) %s ;\n", + px, py, gate_to_orient(gate->orient)); + } + fprintf(outfptr, "END PINS\n\n"); + } + + while (1) { + if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) { + fprintf(stderr, "Error: End of file reached before NETS.\n"); + return; + } + sptr = line; + while (isspace(*sptr)) sptr++; + + /* Assuming a typical DEF file here. . . */ + if (!strncmp(sptr, "NETS", 4)) break; + + } + fprintf(outfptr, "%s", line); + while (1) { + if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) { + fprintf(stderr, "Error: End of file reached before END NETS.\n"); + return; + } + sptr = line; + while (isspace(*sptr)) sptr++; + + /* Assuming a typical DEF file here. . . */ + if (!strncmp(sptr, "SPECIALNETS", 11)) { + sscanf(sptr + 11, "%d", ©special); + break; + } + else if (!strncmp(sptr, "END DESIGN", 10)) { + break; + } + fprintf(outfptr, "%s", line); + } + + /* Rewrite SPECIALNETS line with updated number */ + + if (copyspecial + numSpecial > 0) + fprintf(outfptr, "SPECIALNETS %d ;\n", numSpecial + copyspecial); + + if (numSpecial > 0) { + /* Write power bus stripes (SPECIALNETS) */ + int i, first = TRUE; + char *railnames[2] = {GndNet, VddNet}; + + for (i = 0; i < 2; i++) { + fprintf(outfptr, "- %s", railnames[i]); + for (rail = rails; rail; rail = rail->next) { + if (rail->name == railnames[i]) { + output_rails(outfptr, rail, corearea, scale, first); + first = FALSE; + } + } + fprintf(outfptr, " ;\n"); + first = TRUE; + } + } + + /* If there were previously no SPECIALNETS then add the ending line */ + if (numSpecial > 0 && copyspecial == 0) + fprintf(outfptr, "END SPECIALNETS\n\n"); + + /* Copy the remainder of the file verbatim */ + + while (1) { + if (fgets(line, LEF_LINE_MAX + 1, infptr) == NULL) break; + sptr = line; + while (isspace(*sptr)) sptr++; + + if (!strncmp(sptr, "END DESIGN", 10)) { + break; + } + fprintf(outfptr, "%s", line); + } + fprintf(outfptr, "END DESIGN\n"); + fclose(infptr); + + if (defoutname != NULL) fclose(outfptr); + fflush(stdout); +} + +/*--------------------------------------------------------------*/ +/* helpmessage - tell user how to use the program */ +/* */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *outf) +{ + fprintf(outf, "addspacers [-options] <netlist>\n"); + fprintf(outf, "\n"); + fprintf(outf, "addspacers adds fill cells and power buses to a layout.\n"); + fprintf(outf, "Output on stdout unless redirected with -o option.\n"); + fprintf(outf, "\n"); + fprintf(outf, "options:\n"); + fprintf(outf, " -o <path> Output file path and name\n"); + fprintf(outf, " -l <path> Path to standard cell LEF file (for macro list)\n"); + fprintf(outf, " -p <name> Name of power net\n"); + fprintf(outf, " -g <name> Name of ground net\n"); + fprintf(outf, " -f <name> Name of fill cell (or prefix)\n"); + fprintf(outf, " -w <width> Power bus stripe width\n"); + fprintf(outf, " -P <pitch> Power bus stripe pitch\n"); + fprintf(outf, " -s <pattern> Power bus stripe pattern (default \"PG\") \n"); + fprintf(outf, " -n Do not stretch layout under power buses.\n"); + fprintf(outf, " -O Handle obstruction areas in separate .obs file\n"); + fprintf(outf, "\n"); + fprintf(outf, " -v Verbose output\n"); + fprintf(outf, " -h Print this message\n"); + +} /* helpmessage() */ + diff --git a/src/blif2BSpice.c b/src/blif2BSpice.c index e1418f7..95414d6 100644 --- a/src/blif2BSpice.c +++ b/src/blif2BSpice.c @@ -279,7 +279,7 @@ void ReadNetlistAndConvert(FILE *netfile, FILE *libfile, char *libname, CleanupString(MainSubcktName); fprintf(outfile, "*SPICE netlist created from BLIF module " "%s by blif2BSpice\n", MainSubcktName); - fprintf(outfile, ""); + fprintf(outfile, "\n"); /* If doinclude == 1 then dump the contents of the */ /* libraray. If 0, then just write a .include line. */ @@ -291,7 +291,7 @@ void ReadNetlistAndConvert(FILE *netfile, FILE *libfile, char *libname, while (loc_getline(line, sizeof(line), libfile) > 0) fputs(line, outfile); fclose(libfile); - fprintf(outfile, ""); + fprintf(outfile, "\n"); } } else { diff --git a/src/blif2Verilog.c b/src/blif2Verilog.c index 0464a06..78df6cb 100644 --- a/src/blif2Verilog.c +++ b/src/blif2Verilog.c @@ -31,14 +31,12 @@ extern char *optarg; #define INPUT 0 #define OUTPUT 1 -#define UNKNOWN 2 struct Vect { struct Vect *next; char *name; char direction; /* INPUT or OUTPUT */ int Max; - int Min; }; void ReadNetlistAndConvert(FILE *, FILE *, unsigned char); @@ -191,6 +189,7 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) First = TRUE; Vector = VectorAlloc(); + Vector->next = NULL; while (loc_getline(line, sizeof(line), NETFILE) > 0) { lptr = line; while (isspace(*lptr)) lptr++; @@ -220,8 +219,6 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) if (strcmp(VectorPresent->name, InputName) == 0) { VectorPresent->Max = (VectorPresent->Max > VectorIndex) ? VectorPresent->Max : VectorIndex; - VectorPresent->Min = (VectorPresent->Min < VectorIndex) ? - VectorPresent->Min : VectorIndex; Found = TRUE; } VectorPresent = VectorPresent->next; @@ -229,8 +226,9 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) if (!Found) { VectorPresent->name = strdup(InputName); VectorPresent->direction = INPUT; - VectorPresent->Max = VectorPresent->Min = VectorIndex; + VectorPresent->Max = VectorIndex; VectorPresent->next = VectorAlloc(); + VectorPresent->next->next = NULL; } } if (PrintIt || !Found) { // Should print vectors in module statement @@ -283,8 +281,6 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) if (strcmp(VectorPresent->name, OutputName) == 0) { VectorPresent->Max = (VectorPresent->Max > VectorIndex) ? VectorPresent->Max : VectorIndex; - VectorPresent->Min = (VectorPresent->Min < VectorIndex) ? - VectorPresent->Min : VectorIndex; Found = TRUE; } VectorPresent = VectorPresent->next; @@ -292,8 +288,9 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) if (!Found) { VectorPresent->name = strdup(OutputName); VectorPresent->direction = OUTPUT; - VectorPresent->Max = VectorPresent->Min = VectorIndex; + VectorPresent->Max = VectorIndex; VectorPresent->next = VectorAlloc(); + VectorPresent->next->next = NULL; } } if (PrintIt || !Found) { @@ -335,11 +332,10 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) VectorPresent = Vector; while (VectorPresent->next != NULL) { - fprintf(OUT, "%s [%d:%d] %s;\n", + fprintf(OUT, "%s [%d:0] %s;\n", (VectorPresent->direction == INPUT) ? "input" : "output", VectorPresent->Max, - VectorPresent->Min, VectorPresent->name); VectorPresent = VectorPresent->next; } @@ -412,12 +408,15 @@ void ReadNetlistAndConvert(FILE *NETFILE, FILE *OUT, unsigned char Flags) } } if (!ItIsAnInput && !ItIsAnOutput) { - while ((Weirdpnt = strchr(InstancePortWire,'[')) != NULL) + if ((Weirdpnt = strchr(InstancePortWire,'[')) != NULL) { *Weirdpnt = '_'; - while ((Weirdpnt = strchr(InstancePortWire,']')) != NULL) - *Weirdpnt = '_'; - while ((Weirdpnt = strchr(InstancePortWire, '$')) != NULL) + if ((Weirdpnt = strchr(InstancePortWire,']')) != NULL) + *Weirdpnt = '_'; + } + while ((Weirdpnt = strchr(InstancePortWire, '$')) != NULL) { *Weirdpnt = '_'; + Weirdpnt++; + } } @@ -617,16 +616,7 @@ int loc_getline( char s[], int lim, FILE *fp) struct Vect *VectorAlloc(void) { - struct Vect *newvector; - - newvector = (struct Vect *) malloc(sizeof(struct Vect)); - newvector->next = NULL; - newvector->name = NULL; - newvector->direction = UNKNOWN; - newvector->Max = 0; - newvector->Min = 0; - - return newvector; + return (struct Vect *) malloc(sizeof(struct Vect)); } diff --git a/src/blifFanout.c b/src/blifFanout.c index 6215bb6..910d484 100644 --- a/src/blifFanout.c +++ b/src/blifFanout.c @@ -125,7 +125,7 @@ struct Gatelist { double strength; } Gatelist_; -struct hashlist *Gatehash[OBJHASHSIZE]; +struct hashtable Gatehash; struct Nodelist { char ignore; @@ -142,7 +142,7 @@ struct Nodelist { int curcount; // Active count for fanout buffering trees } Nodelist_; -struct hashlist *Nodehash[OBJHASHSIZE]; +struct hashtable Nodehash; struct Drivelist { char *Separator; // Separator (e.g., "X") @@ -151,7 +151,7 @@ struct Drivelist { int NgatesOut; // Number of gates with this suffix in output } Drivelist_; -struct hashlist *Drivehash[OBJHASHSIZE]; +struct hashtable Drivehash; struct Baselist { char *BaseName; // gate base name (e.g., "INV") @@ -159,7 +159,7 @@ struct Baselist { struct Gatelist **gates; // list of pointers to gates with } Baselist_; -struct hashlist *Basehash[OBJHASHSIZE]; +struct hashtable Basehash; enum states_ {NONE, INPUTS, OUTPUTS, GATENAME, PINNAME, INPUTNODE, CLOCKNODE, OUTPUTNODE, ENDMODEL, ERROR}; @@ -288,10 +288,10 @@ int main (int argc, char *argv[]) hashfunc = hash; matchfunc = match; - InitializeHashTable(Nodehash); - InitializeHashTable(Drivehash); - InitializeHashTable(Gatehash); - InitializeHashTable(Basehash); + InitializeHashTable(&Nodehash, LARGEHASHSIZE); + InitializeHashTable(&Drivehash, LARGEHASHSIZE); + InitializeHashTable(&Gatehash, LARGEHASHSIZE); + InitializeHashTable(&Basehash, LARGEHASHSIZE); fprintf(stdout, "blifFanout for qflow " QFLOW_VERSION "." QFLOW_REVISION "\n"); @@ -440,9 +440,9 @@ int main (int argc, char *argv[]) // Determine if suffix is numeric or alphabetic if (gatecount > 0) { char *suffix; - gl = (struct Gatelist *)HashFirst(Gatehash); + gl = (struct Gatelist *)HashFirst(&Gatehash); while (gl && gl->suffix == NULL) - gl = (struct Gatelist *)HashNext(Gatehash); + gl = (struct Gatelist *)HashNext(&Gatehash); if (gl && gl->suffix && !isdigit(*gl->suffix)) SuffixIsNumeric = FALSE; } @@ -462,7 +462,7 @@ int main (int argc, char *argv[]) gl = (struct Gatelist *)NULL; if (Buffername != NULL) { - gl = (struct Gatelist *)HashLookup(Buffername, Gatehash); + gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash); if (gl == NULL) { fprintf(stderr, "No buffer \"%s\" found in gate list\n", Buffername); fprintf(stderr, "Searching gate list for suitable buffer.\n"); @@ -471,7 +471,7 @@ int main (int argc, char *argv[]) if ((gl == NULL) || (Buffername == NULL)) { // Find a suitable buffer - gl = (struct Gatelist *)HashFirst(Gatehash); + gl = (struct Gatelist *)HashFirst(&Gatehash); while (gl != NULL) { Cell *ctest; @@ -501,11 +501,11 @@ int main (int argc, char *argv[]) } } } - gl = (struct Gatelist *)HashNext(Gatehash); + gl = (struct Gatelist *)HashNext(&Gatehash); } } else - gl = (struct Gatelist *)HashLookup(Buffername, Gatehash); + gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash); if (gl == NULL) { if (Buffername == NULL) @@ -553,7 +553,7 @@ int main (int argc, char *argv[]) while (t) { switch (state) { case GATENAME: - gl = (struct Gatelist *)HashLookup(t, Gatehash); + gl = (struct Gatelist *)HashLookup(t, &Gatehash); if (gl != NULL) { if (VerboseFlag) printf("\n\n%s", t); gateinputs = gl->num_inputs; @@ -650,7 +650,7 @@ int main (int argc, char *argv[]) /* Show top fanout gate */ nlmax = NULL; nlimax = NULL; - nl = (struct Nodelist *)HashFirst(Nodehash); + nl = (struct Nodelist *)HashFirst(&Nodehash); while (nl != NULL) { if (nl->outputgatestrength != 0.0) { nl->ratio = nl->total_load / nl->outputgatestrength; @@ -674,7 +674,7 @@ int main (int argc, char *argv[]) Inputload = nl->total_load; } } - nl = (struct Nodelist *)HashNext(Nodehash); + nl = (struct Nodelist *)HashNext(&Nodehash); } if (VerboseFlag) printf("\n"); @@ -699,7 +699,7 @@ int main (int argc, char *argv[]) if (doFanout && ((Topfanout > MaxFanout) || (Inputfanout > MaxFanout))) { /* Insert buffer trees */ - nl = (struct Nodelist *)HashFirst(Nodehash); + nl = (struct Nodelist *)HashFirst(&Nodehash); while (nl != NULL) { if (nl->ignore == FALSE) { @@ -735,7 +735,7 @@ int main (int argc, char *argv[]) Buffer_count += n; } } - nl = (struct Nodelist *)HashNext(Nodehash); + nl = (struct Nodelist *)HashNext(&Nodehash); } } write_output(doLoadBalance, infptr, outfptr); @@ -745,14 +745,14 @@ int main (int argc, char *argv[]) fprintf(stderr, "%d gates were changed.\n", Changed_count); fprintf(stderr, "\nGate counts by drive strength:\n\n"); - dl = (struct Drivelist *)HashFirst(Drivehash); + dl = (struct Drivelist *)HashFirst(&Drivehash); while (dl != NULL) { if (dl->NgatesIn > 0) { fprintf(stderr, "\t\"%s%s\" gates\tIn: %d \tOut: %d \t%+d\n", dl->Separator, dl->DriveType, dl->NgatesIn, dl->NgatesOut, (dl->NgatesOut - dl->NgatesIn)); } - dl = (struct Drivelist *)HashNext(Drivehash); + dl = (struct Drivelist *)HashNext(&Drivehash); } fprintf(stderr, "\n"); @@ -795,7 +795,7 @@ void read_ignore_file(char *ignore_file_name) while (*sp != '\0' && *sp != '\n' && !isspace(*sp)) sp++; *sp = '\0'; - nl = (struct Nodelist *)HashLookup(s, Nodehash); + nl = (struct Nodelist *)HashLookup(s, &Nodehash); if (nl != NULL) { nl->ignore = (char)1; } @@ -853,7 +853,7 @@ int read_gate_file(char *gate_file_name, char *separator) // gl->gatename, gl->gatecell->name, gl->gatecell->function); gl->strength = MaxLatency / gl->delay; - HashPtrInstall(gl->gatename, gl, Gatehash); + HashPtrInstall(gl->gatename, gl, &Gatehash); gatecount++; /* Install prefix in Basehash. Note that prefix contains the */ @@ -866,10 +866,10 @@ int read_gate_file(char *gate_file_name, char *separator) ssave = gl->gatename[ind]; gl->gatename[ind] = '\0'; - bl = (struct Baselist *)HashLookup(gl->gatename, Basehash); + bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash); if (bl == NULL) { bl = BaselistAlloc(); - HashPtrInstall(gl->gatename, bl, Basehash); + HashPtrInstall(gl->gatename, bl, &Basehash); bl->BaseName = strdup(gl->gatename); } gl->gatename[ind] = ssave; @@ -975,7 +975,7 @@ void showgatelist(void) Pin *curpin; double pincap; - gl = (struct Gatelist *)HashFirst(Gatehash); + gl = (struct Gatelist *)HashFirst(&Gatehash); while (gl != NULL) { printf("\n\ngate: %s with %d inputs and %g drive strength\n", @@ -989,7 +989,7 @@ void showgatelist(void) printf("%g ", pincap); } } - gl = (struct Gatelist *)HashNext(Gatehash); + gl = (struct Gatelist *)HashNext(&Gatehash); } } @@ -1003,13 +1003,13 @@ void registernode(char *nodename, int type, struct Gatelist *gl, char *pinname) struct Nodelist *nl; double pincap; - nl = (struct Nodelist *)HashLookup(nodename, Nodehash); + nl = (struct Nodelist *)HashLookup(nodename, &Nodehash); if (nl == NULL) { nl = NodelistAlloc(); nl->nodename = strdup(nodename); if (type == OUTPUT) nl->outputgate = NULL; - HashPtrInstall(nodename, nl, Nodehash); + HashPtrInstall(nodename, nl, &Nodehash); nl->type = type; } @@ -1050,13 +1050,13 @@ void count_gatetype(struct Gatelist *gl, int num_in, int num_out) if ((s = gl->suffix) == NULL) return; - dl = (struct Drivelist *)HashLookup(s, Drivehash); + dl = (struct Drivelist *)HashLookup(s, &Drivehash); if (dl == NULL) { // New drive type found dl = DrivelistAlloc(); - HashPtrInstall(s, dl, Drivehash); + HashPtrInstall(s, dl, &Drivehash); dl->DriveType = strdup(s); dl->Separator = gl->separator; } @@ -1075,13 +1075,13 @@ void shownodes(void) struct Nodelist *nl; int i; - nl = (struct Nodelist *)HashFirst(Nodehash); + nl = (struct Nodelist *)HashFirst(&Nodehash); while (nl != NULL) { printf("\n\nnode: %s with %d fanout and %g fF cap", nl->nodename, nl->num_inputs, nl->total_load); printf("\ndriven by %s, with %g strength.\n", nl->outputgate->gatename, nl->outputgatestrength); - nl = (struct Nodelist *)HashNext(Nodehash); + nl = (struct Nodelist *)HashNext(&Nodehash); } } @@ -1124,7 +1124,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) hasended = 0; // Find the gate record corresponding to the buffer name - glbuf = (struct Gatelist *)HashLookup(Buffername, Gatehash); + glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash); rewind(infptr); @@ -1155,7 +1155,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) /* Insert any added buffers (before 1st gate) */ - nl = (struct Nodelist *)HashFirst(Nodehash); + nl = (struct Nodelist *)HashFirst(&Nodehash); while (nl != NULL) { for (i = nl->num_buf - 1; i >= 0; i--) { hier = 0; @@ -1189,7 +1189,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) /* the buffer tree is even deeper, incrementing */ /* M until the name is unique. */ - nltest = (struct Nodelist *)HashLookup(nodename, Nodehash); + nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash); if (nltest != NULL) { sprintf(nodename, "%s_hier%d", nl->nodename, hier); hier++; @@ -1206,11 +1206,11 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) nl->nodename, buf_out_pin, nodename); } - nl = (struct Nodelist *)HashNext(Nodehash); + nl = (struct Nodelist *)HashNext(&Nodehash); } firstseen = 1; } - gl = (struct Gatelist *)HashLookup(t, Gatehash); + gl = (struct Gatelist *)HashLookup(t, &Gatehash); if (gl == NULL) { fprintf(stderr, "Error: Gate \"%s\" is used in source " "but has no liberty file definition.\n", t); @@ -1249,7 +1249,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) case INPUTNODE: case CLOCKNODE: if (VerboseFlag) printf("\nInput node %s", t); - nl = (struct Nodelist *)HashLookup(t, Nodehash); + nl = (struct Nodelist *)HashLookup(t, &Nodehash); if (nl->num_buf > 0) { hier = 0; nltest = nl; @@ -1282,7 +1282,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) /* the buffer tree is even deeper, incrementing M */ /* until the name is unique. */ - nltest = (struct Nodelist *)HashLookup(nodename, Nodehash); + nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash); if (nltest != NULL) { sprintf(nodename, "%s_hier%d", nl->nodename, hier); hier++; @@ -1307,7 +1307,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) case OUTPUTNODE: if (VerboseFlag) printf("\nOutput node %s", t); - nl = (struct Nodelist *)HashLookup(t, Nodehash); + nl = (struct Nodelist *)HashLookup(t, &Nodehash); if (doLoadBalance && (nl != NULL)) { if ((nl->ignore == FALSE) && (nl->ratio > 1.0)) { if (VerboseFlag) @@ -1394,7 +1394,7 @@ void write_output(int doLoadBalance, FILE *infptr, FILE *outfptr) fprintf(stderr, "May need to add information to gate.cfg file\n"); } - dl = (struct Drivelist *)HashLookup(bbest->suffix, Drivehash); + dl = (struct Drivelist *)HashLookup(bbest->suffix, &Drivehash); if (dl != NULL) dl->NgatesOut++; /* Recompute size of the gate driving the buffer */ @@ -1499,7 +1499,7 @@ struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload) ssave = gl->gatename[ind]; gl->gatename[ind] = '\0'; - bl = (struct Baselist *)HashLookup(gl->gatename, Basehash); + bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash); gl->gatename[ind] = ssave; for (i = 0; bl && (i < bl->Ndrives); i++) { @@ -26,8 +26,8 @@ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #endif #include "hash.h" -unsigned long (*hashfunc)(char *) = NULL; -int (*matchfunc)(char *, char *) = NULL; +unsigned long (*hashfunc)(char *, int) = hash; +int (*matchfunc)(char *, char *) = match; int (*matchintfunc)(char *, char *, int, int) = NULL; /* Add match functions: These are just strcmp() and */ @@ -45,13 +45,17 @@ int matchnocase(char *st1, char *st2) else return 0; } -void InitializeHashTable(struct hashlist **tab) +void InitializeHashTable(struct hashtable *table, int hashsize) { int i; - for (i = 0; i < OBJHASHSIZE; i++) tab[i] = NULL; + table->hashsize = hashsize; + table->hashfirstindex = 0; + table->hashfirstptr = NULL; + table->hashtab = (struct hashlist **)malloc(hashsize * sizeof(struct hashlist *)); + for (i = 0; i < hashsize; i++) table->hashtab[i] = NULL; } -int RecurseHashTable(struct hashlist **hashtab, +int RecurseHashTable(struct hashtable *table, int (*func)(struct hashlist *elem)) /* returns the sum of the return values of (*func) */ { @@ -59,8 +63,8 @@ int RecurseHashTable(struct hashlist **hashtab, struct hashlist *p; sum = 0; - for (i = 0; i < OBJHASHSIZE; i++) - for (p = hashtab[i]; p != NULL; p = p->next) + for (i = 0; i < table->hashsize; i++) + for (p = table->hashtab[i]; p != NULL; p = p->next) sum += (*func)(p); return(sum); } @@ -69,15 +73,15 @@ int RecurseHashTable(struct hashlist **hashtab, * type int value to the function. */ -int RecurseHashTableValue(struct hashlist **hashtab, +int RecurseHashTableValue(struct hashtable *table, int (*func)(struct hashlist *elem, int), int value) { int i, sum; struct hashlist *p; sum = 0; - for (i = 0; i < OBJHASHSIZE; i++) - for (p = hashtab[i]; p != NULL; p = p->next) + for (i = 0; i < table->hashsize; i++) + for (p = table->hashtab[i]; p != NULL; p = p->next) sum += (*func)(p, value); return(sum); } @@ -88,7 +92,7 @@ int RecurseHashTableValue(struct hashlist **hashtab, * function through that structure. */ -struct nlist *RecurseHashTablePointer(struct hashlist **hashtab, +struct nlist *RecurseHashTablePointer(struct hashtable *table, struct nlist *(*func)(struct hashlist *elem, void *), void *pointer) { @@ -96,8 +100,8 @@ struct nlist *RecurseHashTablePointer(struct hashlist **hashtab, struct hashlist *p; struct nlist *tp; - for (i = 0; i < OBJHASHSIZE; i++) { - for (p = hashtab[i]; p != NULL; p = p->next) { + for (i = 0; i < table->hashsize; i++) { + for (p = table->hashtab[i]; p != NULL; p = p->next) { tp = (*func)(p, pointer); if (tp != NULL) return tp; } @@ -142,23 +146,23 @@ static unsigned char uppercase[] = { // have exactly the same hash result. Lousy for binning and even // lousier for generating class magic numbers. -unsigned long hashnocase(char *s) +unsigned long hashnocase(char *s, int hashsize) { unsigned long hashval; for (hashval = 0; *s != '\0'; ) hashval = uppercase[*s++] + (hashval << 6) + (hashval << 16) - hashval; - return (hashval % OBJHASHSIZE); + return (hashval % hashsize); } -unsigned long hash(char *s) +unsigned long hash(char *s, int hashsize) { unsigned long hashval; for (hashval = 0; *s != '\0'; ) hashval = (*s++) + (hashval << 6) + (hashval << 16) - hashval; - return (hashval % OBJHASHSIZE); + return (hashval % hashsize); } /*----------------------------------------------------------------------*/ @@ -166,14 +170,14 @@ unsigned long hash(char *s) /* return the 'ptr' field of the hash table entry, or NULL if not found */ /*----------------------------------------------------------------------*/ -void *HashLookup(char *s, struct hashlist **hashtab) +void *HashLookup(char *s, struct hashtable *table) { struct hashlist *np; unsigned long hashval; - hashval = (*hashfunc)(s); + hashval = (*hashfunc)(s, table->hashsize); - for (np = hashtab[hashval]; np != NULL; np = np->next) + for (np = table->hashtab[hashval]; np != NULL; np = np->next) if ((*matchfunc)(s, np->name)) return (np->ptr); /* correct match */ return (NULL); /* not found */ @@ -186,14 +190,14 @@ void *HashLookup(char *s, struct hashlist **hashtab) /* passed integer value i */ /*----------------------------------------------------------------------*/ -void *HashIntLookup(char *s, int i, struct hashlist **hashtab) +void *HashIntLookup(char *s, int i, struct hashtable *table) { struct hashlist *np; unsigned long hashval; - hashval = (*hashfunc)(s); + hashval = (*hashfunc)(s, table->hashsize); - for (np = hashtab[hashval]; np != NULL; np = np->next) { + for (np = table->hashtab[hashval]; np != NULL; np = np->next) { if (np->ptr == NULL) { if ((*matchintfunc)(s, np->name, i, -1)) return NULL; @@ -210,14 +214,13 @@ void *HashIntLookup(char *s, int i, struct hashlist **hashtab) /* return the hashlist entry, after (re)initializing its 'ptr' field */ /*----------------------------------------------------------------------*/ -struct hashlist *HashPtrInstall(char *name, void *ptr, - struct hashlist **hashtab) +struct hashlist *HashPtrInstall(char *name, void *ptr, struct hashtable *table) { struct hashlist *np; unsigned long hashval; - hashval = (*hashfunc)(name); - for (np = hashtab[hashval]; np != NULL; np = np->next) + hashval = (*hashfunc)(name, table->hashsize); + for (np = table->hashtab[hashval]; np != NULL; np = np->next) if ((*matchfunc)(name, np->name)) { np->ptr = ptr; return (np); /* match found in hash table */ @@ -229,8 +232,8 @@ struct hashlist *HashPtrInstall(char *name, void *ptr, if ((np->name = strdup(name)) == NULL) return (NULL); np->ptr = ptr; - np->next = hashtab[hashval]; - return(hashtab[hashval] = np); + np->next = table->hashtab[hashval]; + return(table->hashtab[hashval] = np); } /*----------------------------------------------------------------------*/ @@ -239,13 +242,13 @@ struct hashlist *HashPtrInstall(char *name, void *ptr, /*----------------------------------------------------------------------*/ struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr, - struct hashlist **hashtab) + struct hashtable *table) { struct hashlist *np; unsigned long hashval; - hashval = (*hashfunc)(name); - for (np = hashtab[hashval]; np != NULL; np = np->next) + hashval = (*hashfunc)(name, table->hashsize); + for (np = table->hashtab[hashval]; np != NULL; np = np->next) if ((*matchintfunc)(name, np->name, value, (int)(*((int *)np->ptr)))) { np->ptr = ptr; return (np); /* match found in hash table */ @@ -256,27 +259,29 @@ struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr, return (NULL); if ((np->name = strdup(name)) == NULL) return (NULL); np->ptr = ptr; - np->next = hashtab[hashval]; - return(hashtab[hashval] = np); + np->next = table->hashtab[hashval]; + return(table->hashtab[hashval] = np); } /*----------------------------------------------------------------------*/ /* destroy a hash table, freeing associated memory */ /*----------------------------------------------------------------------*/ -void HashKill(struct hashlist **hashtab) +void HashKill(struct hashtable *table) { struct hashlist *np, *p; int i; - for (i = 0; i < OBJHASHSIZE; i++) { - for (p = hashtab[i]; p != NULL; ) { + for (i = 0; i < table->hashsize; i++) { + for (p = table->hashtab[i]; p != NULL; ) { np = p->next; free(p->name); free(p); p = np; } } + free(table->hashtab); + table->hashtab = NULL; } /*----------------------------------------------------------------------*/ @@ -284,13 +289,13 @@ void HashKill(struct hashlist **hashtab) /* to the new hash entry. */ /*----------------------------------------------------------------------*/ -struct hashlist *HashInstall(char *name, struct hashlist **hashtab) +struct hashlist *HashInstall(char *name, struct hashtable *table) { struct hashlist *np; unsigned long hashval; - hashval = (*hashfunc)(name); - for (np = hashtab[hashval]; np != NULL; np = np->next) + hashval = (*hashfunc)(name, table->hashsize); + for (np = table->hashtab[hashval]; np != NULL; np = np->next) if ((*matchfunc)(name, np->name)) return (np); /* match found in hash table */ /* not in table, so install it */ @@ -298,27 +303,27 @@ struct hashlist *HashInstall(char *name, struct hashlist **hashtab) return (NULL); if ((np->name = strdup(name)) == NULL) return (NULL); np->ptr = NULL; - np->next = hashtab[hashval]; - return(hashtab[hashval] = np); + np->next = table->hashtab[hashval]; + return(table->hashtab[hashval] = np); } /*----------------------------------------------------------------------*/ /* frees a hash table entry, (but not the 'ptr' field) */ /*----------------------------------------------------------------------*/ -void HashDelete(char *name, struct hashlist **hashtab) +void HashDelete(char *name, struct hashtable *table) { unsigned long hashval; struct hashlist *np; struct hashlist *np2; - hashval = (*hashfunc)(name); - np = hashtab[hashval]; + hashval = (*hashfunc)(name, table->hashsize); + np = table->hashtab[hashval]; if (np == NULL) return; if ((*matchfunc)(name, np->name)) { /* it is the first element in the list */ - hashtab[hashval] = np->next; + table->hashtab[hashval] = np->next; free(np->name); free(np); return; @@ -341,19 +346,19 @@ void HashDelete(char *name, struct hashlist **hashtab) /* HashDelete with additional integer value matching */ /*----------------------------------------------------------------------*/ -void HashIntDelete(char *name, int value, struct hashlist **hashtab) +void HashIntDelete(char *name, int value, struct hashtable *table) { unsigned long hashval; struct hashlist *np; struct hashlist *np2; - hashval = (*hashfunc)(name); - np = hashtab[hashval]; + hashval = (*hashfunc)(name, table->hashsize); + np = table->hashtab[hashval]; if (np == NULL) return; if ((*matchintfunc)(name, np->name, value, (int)(*((int *)np->ptr)))) { /* it is the first element in the list */ - hashtab[hashval] = np->next; + table->hashtab[hashval] = np->next; free(np->name); free(np); return; @@ -375,31 +380,28 @@ void HashIntDelete(char *name, int value, struct hashlist **hashtab) /*----------------------------------------------------------------------*/ -static int hashfirstindex; /* was long */ -static struct hashlist *hashfirstptr; - -void *HashNext(struct hashlist **hashtab) +void *HashNext(struct hashtable *table) /* returns 'ptr' field of next element, NULL when done */ { - if (hashfirstptr != NULL && hashfirstptr->next != NULL) { - hashfirstptr = hashfirstptr->next; - return(hashfirstptr->ptr); + if (table->hashfirstptr != NULL && table->hashfirstptr->next != NULL) { + table->hashfirstptr = table->hashfirstptr->next; + return(table->hashfirstptr->ptr); } - while (hashfirstindex < OBJHASHSIZE) { - if ((hashfirstptr = hashtab[hashfirstindex++]) != NULL) { - return(hashfirstptr->ptr); + while (table->hashfirstindex < table->hashsize) { + if ((table->hashfirstptr = table->hashtab[table->hashfirstindex++]) != NULL) { + return(table->hashfirstptr->ptr); } } - hashfirstindex = 0; - hashfirstptr = NULL; + table->hashfirstindex = 0; + table->hashfirstptr = NULL; return(NULL); } -void *HashFirst(struct hashlist **hashtab) +void *HashFirst(struct hashtable *table) { - hashfirstindex = 0; - hashfirstptr = NULL; - return(HashNext(hashtab)); + table->hashfirstindex = 0; + table->hashfirstptr = NULL; + return(HashNext(table)); } @@ -1,8 +1,9 @@ #ifndef _HASH_H #define _HASH_H -// #define OBJHASHSIZE 997 -#define OBJHASHSIZE 99997 +#define TINYHASHSIZE 17 +#define SMALLHASHSIZE 997 +#define LARGEHASHSIZE 99997 struct hashlist { char *name; @@ -10,43 +11,49 @@ struct hashlist { struct hashlist *next; }; -extern void InitializeHashTable(struct hashlist **tab); -extern int RecurseHashTable(struct hashlist **hashtab, +struct hashtable { + int hashsize; + int hashfirstindex; /* for iterating through table */ + struct hashlist *hashfirstptr; /* ditto */ + struct hashlist **hashtab; /* this is the actual table */ +}; + +extern void InitializeHashTable(struct hashtable *table, int hashsize); +extern int RecurseHashTable(struct hashtable *table, int (*func)(struct hashlist *elem)); -extern int RecurseHashTableValue(struct hashlist **hashtab, +extern int RecurseHashTableValue(struct hashtable *table, int (*func)(struct hashlist *elem, int), int); -extern struct nlist *RecurseHashTablePointer(struct hashlist **hashtab, - struct nlist *(*func)(struct hashlist *elem, +extern struct nlist *RecurseHashTablePointer(struct hashtable *table, + struct nlist *(*func)(struct hashlist *elem, void *), void *pointer); - extern int CountHashTableEntries(struct hashlist *p); extern int CountHashTableBinsUsed(struct hashlist *p); -extern void HashDelete(char *name, struct hashlist **hashtab); -extern void HashIntDelete(char *name, int value, struct hashlist **hashtab); -extern void HashKill(struct hashlist **hashtab); +extern void HashDelete(char *name, struct hashtable *table); +extern void HashIntDelete(char *name, int value, struct hashtable *table); +extern void HashKill(struct hashtable *table); /* these functions return a pointer to a hash list element */ -extern struct hashlist *HashInstall(char *name, struct hashlist **hashtab); +extern struct hashlist *HashInstall(char *name, struct hashtable *table); extern struct hashlist *HashPtrInstall(char *name, void *ptr, - struct hashlist **hashtab); + struct hashtable *table); extern struct hashlist *HashIntPtrInstall(char *name, int value, void *ptr, - struct hashlist **hashtab); + struct hashtable *table); -/* these functions return the ->ptr field of a struct hashlist */ -extern void *HashLookup(char *s, struct hashlist **hashtab); -extern void *HashIntLookup(char *s, int i, struct hashlist **hashtab); -extern void *HashFirst(struct hashlist **hashtab); -extern void *HashNext(struct hashlist **hashtab); +/* these functions return the ->ptr field of a struct hashtable */ +extern void *HashLookup(char *s, struct hashtable *table); +extern void *HashIntLookup(char *s, int i, struct hashtable *table); +extern void *HashFirst(struct hashtable *table); +extern void *HashNext(struct hashtable *table); -extern unsigned long hashnocase(char *s); -extern unsigned long hash(char *s); +extern unsigned long hashnocase(char *s, int); +extern unsigned long hash(char *s, int); extern int (*matchfunc)(char *, char *); /* matchintfunc() compares based on the name and the first */ /* entry of the pointer value, which is cast as an integer */ extern int (*matchintfunc)(char *, char *, int, int); -extern unsigned long (*hashfunc)(char *); +extern unsigned long (*hashfunc)(char *, int); /* the matching functions themselves */ extern int match(char *s1, char *s2); diff --git a/src/lef.h b/src/lef.h new file mode 100644 index 0000000..bed50c1 --- /dev/null +++ b/src/lef.h @@ -0,0 +1,239 @@ +/*--------------------------------------------------------------*/ +/* lef.h -- */ +/*--------------------------------------------------------------*/ + +#ifndef _LEF_H +#define _LEF_H + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef _SYS_TYPES_H +#ifndef u_char +typedef unsigned char u_char; +#endif +#ifndef u_short +typedef unsigned short u_short; +#endif +#ifndef u_int +typedef unsigned int u_int; +#endif +#ifndef u_long +typedef unsigned long u_long; +#endif +#endif /* _SYS_TYPES_H */ + +/* Compare functions aren't defined in the Mac's standard library */ +#if defined(__APPLE__) +typedef int (*__compar_fn_t)(const void*, const void*); +#endif + +/* Maximum number of route layers */ +#define MAX_LAYERS 12 + +/* Maximum number of all defined layers. Since masterslice and */ +/* overlap types are ignored, this just includes all the cuts. */ +#define MAX_TYPES (MAX_LAYERS * 2 - 1) + +/* Cell name (and other names) max length */ +#define MAX_NAME_LEN 1024 + +/* Max reasonable line length */ +#define MAX_LINE_LEN 2048 + +// define possible gate orientations +// Basic definitions for flipping in X and Y +#define MNONE 0x00 +#define MX 0x01 +#define MY 0x02 + +// Complete definition from value in DEF file +#define RN 0x04 +#define RS 0x08 +#define RE 0x10 +#define RW 0x20 +#define RF 0x40 + +// linked list structure for holding a list of char * strings + +typedef struct linkedstring_ *LinkedStringPtr; + +typedef struct linkedstring_ { + char *name; + LinkedStringPtr next; +} LinkedString; + +// structure holding input and output scalefactors + +typedef struct scalerec_ { + int iscale; + int mscale; + double oscale; +} ScaleRec; + +// Linked string list + +typedef struct string_ *STRING; + +struct string_ { + STRING next; + char *name; +}; + +/* DSEG is used for gate node and obstruction positions. */ + +typedef struct dseg_ *DSEG; + +struct dseg_ { + DSEG next; + int layer; + double x1, y1, x2, y2; +}; + +/* POINT is an integer point in three dimensions (layer giving the */ +/* vertical dimension). */ + +typedef struct point_ *POINT; + +struct point_ { + POINT next; + int layer; + int x1, y1; +}; + +/* DPOINT is a point location with coordinates given *both* as an */ +/* integer (for the grid-based routing) and as a physical dimension */ +/* (microns). */ + +typedef struct dpoint_ *DPOINT; + +struct dpoint_ { + DPOINT next; + int layer; + double x, y; + int gridx, gridy; +}; + +/* BUS is used for keep information about pins that are array components */ + +typedef struct bus_ *BUS; + +struct bus_ { + BUS next; + char *busname; + int low; + int high; +}; + +typedef struct node_ *NODE; + +struct node_ { + NODE next; + int nodenum; // node ordering within its net + DPOINT taps; // point position for node taps + DPOINT extend; // point position within halo of the tap + char *netname; // name of net this node belongs to + u_char numtaps; // number of actual reachable taps + int netnum; // number of net this node belongs to + int numnodes; // number of nodes on this net + int branchx; // position of the node branch in x + int branchy; // position of the node branch in y +}; + +// these are instances of gates in the netlist. The description of a +// given gate (the macro) is held in GateInfo. The same structure is +// used for both the macro and the instance records. + +typedef struct gate_ *GATE; + +struct gate_ { + GATE next; + GATE last; // For double-linked list + char *gatename; // Name of instance + GATE gatetype; // Pointer to macro record + u_char gateclass; // LEF class of gate (CORE, PAD, etc.) + u_char gatesubclass; // LEF sub-class of gate (SPACER, TIELOW, etc.) + int nodes; // number of nodes on this gate + char **node; // names of the pins on this gate + int *netnum; // net number connected to each pin + NODE *noderec; // node record for each pin + float *area; // gate area for each pin + u_char *direction; // port direction (input, output, etc.) + u_char *use; // pin use (power, ground, etc.) + DSEG *taps; // list of gate node locations and layers + DSEG obs; // list of obstructions in gate + BUS bus; // linked list of buses in the pin list + double width, height; + double placedX; + double placedY; + int orient; + void *clientdata; // This space for rent +}; + +// Define record holding information pointing to a gate and the +// index into a specific node of that gate. + +typedef struct gatenode_ *GATENODE; + +struct gatenode_ { + GATE gate; + int idx; +}; + +// Structure for a network + +typedef struct net_ *NET; + +struct net_ { + int netnum; // a unique number for this net + char *netname; + NODE netnodes; // list of nodes connected to the net + int numnodes; // number of nodes connected to the net + char Flags; // See flag field definitions, below +}; + +// Flag definitions for nets + +#define NET_SPECIAL 1 // Indicates a net read from SPECIALNETS + +// List of nets + +typedef struct netlist_ *NETLIST; + +struct netlist_ { + NETLIST next; + NET net; +}; + +// Structure for a row definition + +typedef struct row_ *ROW; + +struct row_ { + char *rowname; + char *sitename; + int x; + int y; + int orient; + int xnum; + int ynum; + int xstep; + int ystep; +}; + +/* external references to global variables */ + +extern GATE GateInfo; // standard cell macro information +extern GATE PinMacro; // macro definition for a pin +extern GATE Nlgates; +extern NET *Nlnets; +extern int Numnets; +extern u_char Verbose; + +/* Function prototypes */ + +#endif /* _LEF_H */ diff --git a/src/rc2dly.c b/src/rc2dly.c index 52025cf..7936870 100644 --- a/src/rc2dly.c +++ b/src/rc2dly.c @@ -42,7 +42,9 @@ #include <getopt.h> #include <time.h> +#include "hash.h" #include "readliberty.h" /* liberty file database */ +#include "readverilog.h" /* verilog netlist reader */ #define SRC 0x01 // node is a driver #define SNK 0x02 // node is a receiver @@ -56,6 +58,10 @@ #define VISIT_CAP 1 #define VISIT_RES 2 +/* Hash tables */ +struct hashtable LibHash; +struct hashtable InstHash; + typedef struct _r *rptr; typedef struct _node *nodeptr; @@ -86,6 +92,8 @@ typedef struct _node { short visited; } node; +/*----------------------------------------------------------*/ + void print_node (nodeptr node) { printf("Name: %s\n", node->name); printf("Type: %d\n", node->type); @@ -128,16 +136,24 @@ typedef struct _elmdly_item { void print_help () { printf("NAME\n"); - printf(" rc2dly - convert qrouter RC output file to Vesta delay file\n\n"); + printf(" rc2dly - convert qrouter RC output file to delay file\n\n"); printf("SYNOPSIS\n"); - printf(" rc2dly -r <rc_file_name> -l <stdcell_liberty_file_name> -o <output_delay_file_name>\n"); + printf(" rc2dly -r <rc_file_name> -l <stdcell_liberty_file_name> -d <output_delay_file_name>\n"); + printf(" -V <verilog_netlist_name>\n"); printf("\n"); printf("DESCRIPTION\n"); - printf(" TBD\n"); + printf(" Converts output of qrouter to one of three different delay formats,\n"); + printf(" depending on the file extension of the -d argument. If the extension\n"); + printf(" is .spef, then SPEF format is used. If the extension is .sdf, then\n"); + printf(" SDF format is used. Otherwise, the input format used by vesta is\n"); + printf(" generated.\n"); + printf("\n"); printf("Required Arguments\n"); printf(" -r <rc_file_name>\n"); printf(" -l <stdcell_liberty_file_name\n"); - printf("OPTIONS\n"); + printf(" -V <verilog_netlist_name\n"); + printf("\n"); + printf("Optional Arguments\n"); printf(" -d <output_delay_file_name>\n"); printf(" -c <module_pin_capacitance_in_pF>\n"); printf("\n"); @@ -474,6 +490,8 @@ int main (int argc, char* argv[]) { FILE* libfile = NULL; FILE* rcfile = NULL; + struct cellrec *topcell = NULL; + int verbose = 0; double modulePinCapacitance = 0; @@ -515,6 +533,7 @@ int main (int argc, char* argv[]) { static struct option long_options[] = { {"rc-file" , required_argument , 0, 'r'}, {"liberty-file" , required_argument , 0, 'l'}, + {"verilog-file" , required_argument , 0, 'V'}, {"delay-file" , required_argument , 0, 'd'}, {"pin-capacitance" , required_argument , 0, 'c'}, {"delimiter" , required_argument , 0, 'D'}, @@ -526,7 +545,7 @@ int main (int argc, char* argv[]) { /* getopt_long stores the option index here. */ int option_index = 0; - c = getopt_long (argc, argv, "hv:r:l:d:D:", long_options, &option_index); + c = getopt_long (argc, argv, "hv:r:l:d:D:V:", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) @@ -560,6 +579,10 @@ int main (int argc, char* argv[]) { optarg, strerror(errno)); break; + case 'V': + topcell = ReadVerilog(optarg); + break; + case 'l': libfile = fopen(optarg, "r"); if (libfilename) free(libfilename); @@ -631,6 +654,11 @@ int main (int argc, char* argv[]) { return 1; } + if (topcell == NULL) { + fprintf(stderr, "ERROR: Must specify input verilog netlist.\n"); + return 1; + } + if (cells == NULL) { fprintf(stderr, "ERROR: No cells were read from Liberty timing files.\n"); return 5; @@ -658,6 +686,25 @@ int main (int argc, char* argv[]) { char **tokens; int num_toks = 0; + struct instance *inst; + + InitializeHashTable(&LibHash, SMALLHASHSIZE); + InitializeHashTable(&InstHash, LARGEHASHSIZE); + + /* Hash the liberty cell records */ + + for (newcell = cells; newcell; newcell = newcell->next) + HashPtrInstall(newcell->name, newcell, &LibHash); + + /* Hash the verilog instances by liberty cell record */ + + for (inst = topcell->instlist; inst; inst = inst->next) { + newcell = (Cell *)HashLookup(inst->cellname, &LibHash); + HashPtrInstall(inst->instname, newcell, &InstHash); + } + + /* LibHash is no longer needed */ + HashKill(&LibHash); if (format == FORMAT_SPEF) { char outstr[200]; @@ -868,14 +915,14 @@ int main (int argc, char* argv[]) { if (!strncmp(tokens[2], "PIN/", 4)) snprintf(currnode->mapped, 12, "*%d", net_idx); else { - pname = strrchr(tokens[2], '/'); + pname = strrchr(tokens[2], '/') + 1; if (pname) snprintf(currnode->mapped, 12, "*%d%c%s", nid++, - delimiter, ++pname); + delimiter, pname); else - /* Node name is probably hosed, but don't crash */ + /* Node name is hosed but don't crash the program */ snprintf(currnode->mapped, 12, "*%d%c%s", nid++, - delimiter, tokens[2]); + delimiter, tokens[2]); } if (verbose > 1) print_node(currnode); @@ -979,7 +1026,7 @@ int main (int argc, char* argv[]) { snprintf(currnode->mapped, 12, "*%d%c%s", nid++, delimiter, ++pname); else - /* Node name is probably hosed, but don't crash */ + /* Node name is hosed but don't crash the program */ snprintf(currnode->mapped, 12, "*%d%c%s", nid++, delimiter, tokens[t]); } @@ -1027,7 +1074,7 @@ int main (int argc, char* argv[]) { //fprintf(stdout, "Found pin as receiver: %s\n", tokens[t]); } else { - cell = get_cell_by_name(cells, cellName); + cell = (Cell *)HashLookup(cellName, &InstHash); Pin *tmpPin = NULL; if (cell != NULL) { @@ -1085,7 +1132,7 @@ int main (int argc, char* argv[]) { if (verbose > 3) print_node(last_driver->node); elmdlyptr currElm = calloc(1, sizeof(elmdly)); - currElm->name = calloc(1, sizeof(char) * strlen(tokens[0])); + currElm->name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 1)); // name the Elmore Delay after the net strcpy(currElm->name, tokens[0]); currElm->src = last_driver->node; diff --git a/src/readdef.c b/src/readdef.c new file mode 100644 index 0000000..6d4c3fa --- /dev/null +++ b/src/readdef.c @@ -0,0 +1,2091 @@ +/* + * readdef.c -- + * + * This module incorporates the LEF/DEF format for standard-cell place and + * route. + * + * Version 0.1 (September 26, 2003): DEF input of designs. + * + * Written by Tim Edwards, Open Circuit Design + * Modified April 2013 for use with qrouter + * Modified December 2018 for use with qflow (DEF2Verilog, back-annotate verilog + * netlist from DEF output). + * + * It is assumed that the LEF files have been read in prior to this, and + * layer information is already known. The DEF file should have information + * primarily on die are, track placement, pins, components, and nets. + * + * Routed nets have their routes dropped into track obstructions, and the + * nets are ignored. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <math.h> /* for roundf() function, if std=c99 */ + +#include "readlef.h" +#include "readdef.h" +#include "hash.h" + +int numSpecial = 0; /* Tracks number of specialnets */ +int Numnets = 0; /* Total nets, including special nets */ +int Numgates = 0; /* Number of components */ +int Numpins = 0; /* Number of pins */ + +/* These hash tables speed up DEF file reading */ + +struct hashtable InstanceTable; +struct hashtable NetTable; +struct hashtable RowTable; +char *DEFDesignName; + +DSEG UserObs = NULL; +double Xlowerbound = 0, Xupperbound = 0, Ylowerbound = 0, Yupperbound = 0; +double PitchX = 1.0, PitchY = 1.0; +NET *Nlnets = NULL; +GATE Nlgates = NULL; +GATE PinMacro; + +/*--------------------------------------------------------------*/ +/* Cell macro lookup based on the hash table */ +/*--------------------------------------------------------------*/ + +static void +DefHashInit(void) +{ + /* Initialize the macro hash table */ + + InitializeHashTable(&InstanceTable, LARGEHASHSIZE); + InitializeHashTable(&NetTable, LARGEHASHSIZE); + InitializeHashTable(&RowTable, TINYHASHSIZE); +} + +GATE +DefFindGate(char *name) +{ + GATE ginst; + + ginst = (GATE)HashLookup(name, &InstanceTable); + return ginst; +} + +ROW +DefFindRow(int yval) +{ + ROW row; + char namepos[32]; + + sprintf(namepos, "%d", yval); + row = (ROW)HashLookup(namepos, &RowTable); + return row; +} + +/*--------------------------------------------------------------*/ +/* Find the lowest row and return the row record. */ +/*--------------------------------------------------------------*/ + +struct nlist *rowfindlowest(struct hashlist *p, void *clientdata) +{ + ROW row = (ROW)(p->ptr); + ROW *lrow = (ROW *)clientdata; + + if ((*lrow == NULL) || (row->y < (*lrow)->y)) + *lrow = row; + + return NULL; +} + +/*--------------------------------------------------------------*/ + +ROW +DefLowestRow() +{ + ROW row = NULL; + + RecurseHashTablePointer(&RowTable, rowfindlowest, (void *)(&row)); + + return row; +} + +/*--------------------------------------------------------------*/ + +NET +DefFindNet(char *name) +{ + NET net; + + // Guard against calls to find nets before DEF file is read + if (Numnets == 0) return NULL; + + net = (NET)HashLookup(name, &NetTable); + return net; +} + +/*--------------------------------------------------------------*/ +/* Cell macro hash table generation */ +/* Given an instance record, create an entry in the hash table */ +/* for the instance name, with the record entry pointing to the */ +/* instance record. */ +/*--------------------------------------------------------------*/ + +static void +DefHashInstance(GATE gateginfo) +{ + HashPtrInstall(gateginfo->gatename, gateginfo, &InstanceTable); +} + +/*--------------------------------------------------------------*/ + +char * +DefDesign() +{ + return DEFDesignName; +} + +/*--------------------------------------------------------------*/ +/* Net hash table generation */ +/* Given a net record, create an entry in the hash table for */ +/* the net name, with the record entry pointing to the net */ +/* record. */ +/*--------------------------------------------------------------*/ + +static void +DefHashNet(NET net) +{ + HashPtrInstall(net->netname, net, &NetTable); +} + +/* + *------------------------------------------------------------ + * + * DefAddRoutes -- + * + * Parse a network route statement from the DEF file. + * If "special" is 1, then, add the geometry to the + * list of obstructions. If "special" is 0, then read + * the geometry into a route structure for the net. + * + * Results: + * Returns the last token encountered. + * + * Side Effects: + * Reads from input stream; + * Adds information to the layout database. + * + *------------------------------------------------------------ + */ + +static char * +DefAddRoutes(FILE *f, float oscale, NET net, char special) +{ + char *token; + DSEG lr, drect; + struct point_ refp; + char valid = FALSE; /* is there a valid reference point? */ + char noobstruct; + char initial = TRUE; + struct dseg_ locarea; + double x, y, lx, ly, w, hw, s; + int routeLayer = -1, paintLayer; + LefList lefl; + + refp.x1 = 0; + refp.y1 = 0; + + /* Don't create obstructions or routes on routed specialnets inputs */ + noobstruct = (special == (char)1) ? TRUE : FALSE; + + while (initial || (token = LefNextToken(f, TRUE)) != NULL) + { + /* Get next point, token "NEW", or via name */ + if (initial || !strcmp(token, "NEW") || !strcmp(token, "new")) + { + /* initial pass is like a NEW record, but has no NEW keyword */ + initial = FALSE; + + /* invalidate reference point */ + valid = FALSE; + + token = LefNextToken(f, TRUE); + routeLayer = LefFindLayerNum(token); + + if (routeLayer < 0) + { + LefError(DEF_ERROR, "Unknown layer type \"%s\" for NEW route\n", token); + continue; + } + paintLayer = routeLayer; + + if (special == (char)1) + { + /* SPECIALNETS has the additional width */ + token = LefNextToken(f, TRUE); + if (sscanf(token, "%lg", &w) != 1) + { + LefError(DEF_ERROR, "Bad width in special net\n"); + continue; + } + if (w != 0) + w /= oscale; + else + w = LefGetRouteWidth(paintLayer); + } + else + w = LefGetRouteWidth(paintLayer); + + } + else if (*token != '(') /* via name */ + { + /* A '+' or ';' record ends the route */ + if (*token == ';' || *token == '+') + break; + + else if (valid == FALSE) + { + LefError(DEF_ERROR, "Route has via name \"%s\" but no points!\n", token); + continue; + } + lefl = LefFindLayer(token); + if (lefl != NULL) + { + /* The area to paint is derived from the via definitions. */ + + if (lefl != NULL) + { + if (lefl->lefClass == CLASS_VIA) { + + // Note: layers may be defined in any order, metal or cut. + // Check both via.area and via.lr layers, and reject those + // that exceed the number of metal layers (those are cuts). + + paintLayer = 100; + routeLayer = -1; + routeLayer = lefl->info.via.area.layer; + if (routeLayer < paintLayer) paintLayer = routeLayer; + if ((routeLayer >= 0) && (special == (char)1) && + (valid == TRUE) && (noobstruct == FALSE)) { + s = LefGetRouteSpacing(routeLayer); + drect = (DSEG)malloc(sizeof(struct dseg_)); + drect->x1 = x + (lefl->info.via.area.x1 / 2.0) - s; + drect->x2 = x + (lefl->info.via.area.x2 / 2.0) + s; + drect->y1 = y + (lefl->info.via.area.y1 / 2.0) - s; + drect->y2 = y + (lefl->info.via.area.y2 / 2.0) + s; + drect->layer = routeLayer; + drect->next = UserObs; + UserObs = drect; + } + for (lr = lefl->info.via.lr; lr; lr = lr->next) { + routeLayer = lr->layer; + if (routeLayer < paintLayer) paintLayer = routeLayer; + if ((routeLayer >= 0) && (special == (char)1) && + (valid == TRUE) && (noobstruct == FALSE)) { + s = LefGetRouteSpacing(routeLayer); + drect = (DSEG)malloc(sizeof(struct dseg_)); + drect->x1 = x + (lr->x1 / 2.0) - s; + drect->x2 = x + (lr->x2 / 2.0) + s; + drect->y1 = y + (lr->y1 / 2.0) - s; + drect->y2 = y + (lr->y2 / 2.0) + s; + drect->layer = routeLayer; + drect->next = UserObs; + UserObs = drect; + } + } + if (routeLayer == -1) paintLayer = lefl->type; + } + else { + paintLayer = lefl->type; + if (special == (char)1) + s = LefGetRouteSpacing(paintLayer); + } + } + else + { + LefError(DEF_ERROR, "Error: Via \"%s\" named but undefined.\n", token); + paintLayer = routeLayer; + } + } + else + LefError(DEF_ERROR, "Via name \"%s\" unknown in route.\n", token); + } + else + { + /* Revert to the routing layer type, in case we painted a via */ + paintLayer = routeLayer; + + /* Record current reference point */ + locarea.x1 = refp.x1; + locarea.y1 = refp.y1; + lx = x; + ly = y; + + /* Read an (X Y) point */ + token = LefNextToken(f, TRUE); /* read X */ + if (*token == '*') + { + if (valid == FALSE) + { + LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n"); + goto endCoord; + } + } + else if (sscanf(token, "%lg", &x) == 1) + { + x /= oscale; // In microns + /* Note: offsets and stubs are always less than half a pitch, */ + /* so round to the nearest integer grid point. */ + refp.x1 = (int)(0.5 + ((x - Xlowerbound) / PitchX)); + } + else + { + LefError(DEF_ERROR, "Cannot parse X coordinate.\n"); + goto endCoord; + } + token = LefNextToken(f, TRUE); /* read Y */ + if (*token == '*') + { + if (valid == FALSE) + { + LefError(DEF_ERROR, "No reference point for \"*\" wildcard\n"); + goto endCoord; + } + } + else if (sscanf(token, "%lg", &y) == 1) + { + y /= oscale; // In microns + refp.y1 = (int)(0.5 + ((y - Ylowerbound) / PitchY)); + } + else + { + LefError(DEF_ERROR, "Cannot parse Y coordinate.\n"); + goto endCoord; + } + + /* Indicate that we have a valid reference point */ + + if (valid == FALSE) + { + valid = TRUE; + } + else if ((locarea.x1 != refp.x1) && (locarea.y1 != refp.y1)) + { + /* Skip over nonmanhattan segments, reset the reference */ + /* point, and output a warning. */ + + LefError(DEF_ERROR, "Can't deal with nonmanhattan geometry in route.\n"); + locarea.x1 = refp.x1; + locarea.y1 = refp.y1; + lx = x; + ly = y; + } + else + { + locarea.x2 = refp.x1; + locarea.y2 = refp.y1; + + if (special != (char)0) { + if ((valid == TRUE) && (noobstruct == FALSE)) { + s = LefGetRouteSpacing(routeLayer); + hw = w / 2; + drect = (DSEG)malloc(sizeof(struct dseg_)); + if (lx > x) { + drect->x1 = x - s; + drect->x2 = lx + s; + } + else if (lx < x) { + drect->x1 = lx - s; + drect->x2 = x + s; + } + else { + drect->x1 = x - hw - s; + drect->x2 = x + hw + s; + } + if (ly > y) { + drect->y1 = y - s; + drect->y2 = ly + s; + } + else if (ly < y) { + drect->y1 = ly - s; + drect->y2 = y + s; + } + else { + drect->y1 = y - hw - s; + drect->y2 = y + hw + s; + } + drect->layer = routeLayer; + drect->next = UserObs; + UserObs = drect; + } + } + } + +endCoord: + /* Find the closing parenthesis for the coordinate pair */ + while (*token != ')') + token = LefNextToken(f, TRUE); + } + } + + return token; /* Pass back the last token found */ +} + +/* + *------------------------------------------------------------ + * + * DefReadGatePin --- + * + * Given a gate name and a pin name in a net from the + * DEF file NETS section, find the position of the + * gate, then the position of the pin within the gate, + * and add pin and obstruction information to the grid + * network. + * + *------------------------------------------------------------ + */ + +static void +DefReadGatePin(NET net, NODE node, char *instname, char *pinname) +{ + int i; + GATE gateginfo; + DSEG drect; + GATE g; + double dx, dy; + int gridx, gridy; + DPOINT dp; + + g = DefFindGate(instname); + if (g) { + + gateginfo = g->gatetype; + + if (!gateginfo) { + // Instances marked "<net>/pin have a NULL gatetype; this is okay. + if (strcmp(pinname, "pin")) + LefError(DEF_ERROR, "Endpoint %s/%s of net %s not found\n", + instname, pinname, net->netname); + return; + } + for (i = 0; i < gateginfo->nodes; i++) { + if (!strcasecmp(gateginfo->node[i], pinname)) { + node->taps = (DPOINT)NULL; + node->extend = (DPOINT)NULL; + + for (drect = g->taps[i]; drect; drect = drect->next) { + + // Add all routing gridpoints that fall inside + // the rectangle. Much to do here: + // (1) routable area should extend 1/2 route width + // to each side, as spacing to obstructions allows. + // (2) terminals that are wide enough to route to + // but not centered on gridpoints should be marked + // in some way, and handled appropriately. + + gridx = (int)((drect->x1 - Xlowerbound) / PitchX) - 1; + + if (gridx < 0) gridx = 0; + while (1) { + dx = (gridx * PitchX) + Xlowerbound; + if (dx > drect->x2) break; + if (dx < drect->x1) { + gridx++; + continue; + } + gridy = (int)((drect->y1 - Ylowerbound) / PitchY) - 1; + + if (gridy < 0) gridy = 0; + while (1) { + dy = (gridy * PitchY) + Ylowerbound; + if (dy > drect->y2) break; + if (dy < drect->y1) { + gridy++; + continue; + } + + // Routing grid point is an interior point + // of a gate port. Record the position + + dp = (DPOINT)malloc(sizeof(struct dpoint_)); + dp->layer = drect->layer; + dp->x = dx; + dp->y = dy; + dp->gridx = gridx; + dp->gridy = gridy; + + if ((dy >= drect->y1) && + (dx >= drect->x1) && + (dy <= drect->y2) && + (dx <= drect->x2)) { + dp->next = node->taps; + node->taps = dp; + } + else { + dp->next = node->extend; + node->extend = dp; + } + gridy++; + } + gridx++; + } + } + node->netnum = net->netnum; + g->netnum[i] = net->netnum; + g->noderec[i] = node; + node->netname = net->netname; + node->next = net->netnodes; + net->netnodes = node; + break; + } + } + if (i < gateginfo->nodes) return; + } +} + +/* + *------------------------------------------------------------ + * + * DefReadNets -- + * + * Read a NETS or SPECIALNETS section from a DEF file. + * + * Results: + * Return the total number of fixed or cover nets, + * excluding power and ground nets. This gives the + * base number of nets to be copied verbatim from + * input to output (used only for SPECIALNETS, as + * regular nets are tracked with the NET_IGNORED flag). + * + * Side Effects: + * Many. Networks are created, and geometry may be + * painted into the database top-level cell. + * + *------------------------------------------------------------ + */ + +enum def_net_keys {DEF_NET_START = 0, DEF_NET_END}; +enum def_netprop_keys { + DEF_NETPROP_USE = 0, DEF_NETPROP_ROUTED, DEF_NETPROP_FIXED, + DEF_NETPROP_COVER, DEF_NETPROP_SHAPE, DEF_NETPROP_SOURCE, + DEF_NETPROP_WEIGHT, DEF_NETPROP_PROPERTY}; + +static int +DefReadNets(FILE *f, char *sname, float oscale, char special, int total) +{ + char *token; + int keyword, subkey; + int i, processed = 0; + int nodeidx; + int fixed = 0; + char instname[MAX_NAME_LEN], pinname[MAX_NAME_LEN]; + u_char is_new; + + NET net; + int netidx; + NODE node; + + static char *net_keys[] = { + "-", + "END", + NULL + }; + + static char *net_property_keys[] = { + "USE", + "ROUTED", + "FIXED", + "COVER", + "SHAPE", + "SOURCE", + "WEIGHT", + "PROPERTY", + NULL + }; + + if (Numnets == 0) + { + // Initialize net and node records + netidx = 0; + Nlnets = (NET *)malloc(total * sizeof(NET)); + for (i = 0; i < total; i++) Nlnets[i] = NULL; + } + else { + netidx = Numnets; + Nlnets = (NET *)realloc(Nlnets, (Numnets + total) * sizeof(NET)); + for (i = Numnets; i < (Numnets + total); i++) Nlnets[i] = NULL; + } + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, net_keys); + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in NET " + "definition; ignoring.\n", token); + LefEndStatement(f); + continue; + } + + switch (keyword) + { + case DEF_NET_START: + + /* Get net name */ + token = LefNextToken(f, TRUE); + net = DefFindNet(token); + + if (net == NULL) { + net = (NET)malloc(sizeof(struct net_)); + Nlnets[Numnets++] = net; + net->numnodes = 0; + net->netname = strdup(token); + net->netnodes = (NODE)NULL; + net->Flags = (special) ? NET_SPECIAL : 0; + + /* Check for backslash-escape names modified by other tools */ + /* (e.g., vlog2Cel) which replace the trailing space with a */ + /* backslash, making the name verilog-incompatible. */ + + if (*net->netname == '\\') { + char *sptr, *bptr; + sptr = strchr(net->netname, ' '); + if (sptr == NULL) { + bptr = strrchr(net->netname + 1, '\\'); + if (bptr != NULL) *bptr = ' '; + } + } + + net->netnum = netidx++; + DefHashNet(net); + + nodeidx = 0; + is_new = TRUE; + } + else { + nodeidx = net->numnodes; + is_new = FALSE; + } + + /* Update the record of the number of nets processed */ + /* and spit out a message for every 5% finished. */ + + processed++; + + /* Get next token; will be '(' if this is a netlist */ + token = LefNextToken(f, TRUE); + + /* Process all properties */ + while (token && (*token != ';')) + { + /* Find connections for the net */ + if (*token == '(') + { + token = LefNextToken(f, TRUE); /* get pin or gate */ + strcpy(instname, token); + token = LefNextToken(f, TRUE); /* get node name */ + + if (!strcasecmp(instname, "pin")) { + strcpy(instname, token); + strcpy(pinname, "pin"); + } + else + strcpy(pinname, token); + + node = (NODE)calloc(1, sizeof(struct node_)); + node->nodenum = nodeidx++; + DefReadGatePin(net, node, instname, pinname); + + token = LefNextToken(f, TRUE); /* should be ')' */ + + continue; + } + else if (*token != '+') + { + token = LefNextToken(f, TRUE); /* Not a property */ + continue; /* Ignore it, whatever it is */ + } + else + token = LefNextToken(f, TRUE); + + subkey = Lookup(token, net_property_keys); + if (subkey < 0) + { + LefError(DEF_WARNING, "Unknown net property \"%s\" in " + "NET definition; ignoring.\n", token); + continue; + } + switch (subkey) + { + case DEF_NETPROP_USE: + /* Presently, we ignore this */ + break; + case DEF_NETPROP_SHAPE: + /* Ignore this too, along with the next keyword */ + token = LefNextToken(f, TRUE); + break; + case DEF_NETPROP_FIXED: + case DEF_NETPROP_COVER: + /* Read in fixed nets like regular nets but mark + * them as NET_IGNORED. HOWEVER, if the net + * already exists and is not marked NET_IGNORED, + * then don't force it to be ignored. That is + * particularly an issue for a net like power or + * ground, which may need to be routed like a + * regular net but also has fixed portions. */ + if (is_new) { + fixed++; + } + // fall through + case DEF_NETPROP_ROUTED: + // Read in the route; qrouter now takes + // responsibility for this route. + while (token && (*token != ';')) + token = DefAddRoutes(f, oscale, net, special); + // Treat power and ground nets in specialnets as fixed + if (subkey == DEF_NETPROP_ROUTED && special == (char)1) + fixed++; + break; + } + } + break; + + case DEF_NET_END: + if (!LefParseEndStatement(f, sname)) + { + LefError(DEF_ERROR, "Net END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == DEF_NET_END) break; + } + + // Set the number of nodes per net for each node on the net + + if (special == FALSE) { + + // Fill in the netnodes list for each net, needed for checking + // for isolated routed groups within a net. + + for (i = 0; i < Numnets; i++) { + net = Nlnets[i]; + for (node = net->netnodes; node; node = node->next) + net->numnodes++; + for (node = net->netnodes; node; node = node->next) + node->numnodes = net->numnodes; + } + } + + if (processed == total) { + if (Verbose > 0) + fprintf(stdout, " Processed %d%s nets total.\n", processed, + (special) ? " special" : ""); + } + else + LefError(DEF_WARNING, "Warning: Number of nets read (%d) does not match " + "the number declared (%d).\n", processed, total); + return fixed; +} + +/* + *------------------------------------------------------------ + * + * DefReadUseLocation -- + * + * Read location and orientation of a cell use + * Syntax: ( X Y ) O + * + * Results: + * 0 on success, -1 on failure + * + * Side Effects: + * GATE definition for the use has the placedX, placedY, + * and orient values filled. + *------------------------------------------------------------ + */ +enum def_orient {DEF_NORTH, DEF_SOUTH, DEF_EAST, DEF_WEST, + DEF_FLIPPED_NORTH, DEF_FLIPPED_SOUTH, DEF_FLIPPED_EAST, + DEF_FLIPPED_WEST}; + +static int +DefReadLocation(gate, f, oscale) + GATE gate; + FILE *f; + float oscale; +{ + int keyword; + char *token; + float x, y; + char mxflag, myflag; + + static char *orientations[] = { + "N", "S", "E", "W", "FN", "FS", "FE", "FW" + }; + static int oflags[] = { + RN, RS, RE, RW, RN | RF, RS | RF, RE | RF, RW | RF + }; + + token = LefNextToken(f, TRUE); + if (*token != '(') goto parse_error; + token = LefNextToken(f, TRUE); + if (sscanf(token, "%f", &x) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (sscanf(token, "%f", &y) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (*token != ')') goto parse_error; + token = LefNextToken(f, TRUE); + + keyword = Lookup(token, orientations); + if (keyword < 0) + { + LefError(DEF_ERROR, "Unknown macro orientation \"%s\".\n", token); + return -1; + } + + mxflag = myflag = (char)0; + + switch (keyword) + { + case DEF_NORTH: + break; + case DEF_SOUTH: + mxflag = 1; + myflag = 1; + break; + case DEF_FLIPPED_NORTH: + mxflag = 1; + break; + case DEF_FLIPPED_SOUTH: + myflag = 1; + break; + } + + if (gate) { + gate->placedX = x / oscale; + gate->placedY = y / oscale; + gate->orient = MNONE; + if (mxflag) gate->orient |= MX; + if (myflag) gate->orient |= MY; + gate->orient |= oflags[keyword]; + } + return 0; + +parse_error: + LefError(DEF_ERROR, "Cannot parse location: must be ( X Y ) orient\n"); + return -1; +} + +/* + *------------------------------------------------------------ + * + * DefReadPins -- + * + * Read a PINS section from a DEF file. + * + * Results: + * None. + * + * Side Effects: + * Generates paint and labels in the layout. + * + *------------------------------------------------------------ + */ + +enum def_pins_keys {DEF_PINS_START = 0, DEF_PINS_END}; +enum def_pins_prop_keys { + DEF_PINS_PROP_NET = 0, DEF_PINS_PROP_DIR, + DEF_PINS_PROP_LAYER, DEF_PINS_PROP_PLACED, + DEF_PINS_PROP_USE, DEF_PINS_PROP_FIXED, + DEF_PINS_PROP_COVER}; + +static void +DefReadPins(FILE *f, char *sname, float oscale, int total) +{ + char *token; + char pinname[MAX_NAME_LEN]; + int keyword, subkey; + int processed = 0; + DSEG currect, drect; + GATE gate; + int curlayer; + double hwidth; + u_char pin_use; + + static char *pin_keys[] = { + "-", + "END", + NULL + }; + + static char *pin_property_keys[] = { + "NET", + "DIRECTION", + "LAYER", + "PLACED", + "USE", + "FIXED", + "COVER", + NULL + }; + + static char *pin_classes[] = { + "DEFAULT", + "INPUT", + "OUTPUT TRISTATE", + "OUTPUT", + "INOUT", + "FEEDTHRU", + NULL + }; + + static char *pin_uses[] = { + "DEFAULT", + "SIGNAL", + "ANALOG", + "POWER", + "GROUND", + "CLOCK", + "TIEOFF", + "SCAN", + "RESET", + NULL + }; + + pin_use = PORT_USE_DEFAULT; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, pin_keys); + + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in PINS " + "definition; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case DEF_PINS_START: /* "-" keyword */ + + /* Update the record of the number of pins */ + /* processed and spit out a message for every 5% done. */ + + processed++; + + /* Get pin name */ + token = LefNextToken(f, TRUE); + if (sscanf(token, "%2047s", pinname) != 1) + { + LefError(DEF_ERROR, "Bad pin statement: Need pin name\n"); + LefEndStatement(f); + break; + } + + /* Create the pin record */ + gate = (GATE)malloc(sizeof(struct gate_)); + gate->gatetype = PinMacro; + gate->gatename = NULL; /* Use NET, but if none, use */ + /* the pin name, set at end. */ + gate->width = gate->height = 0; + curlayer = -1; + + /* Pin record has one node; allocate memory for it */ + gate->taps = (DSEG *)malloc(sizeof(DSEG)); + gate->noderec = (NODE *)malloc(sizeof(NODE)); + gate->direction = (u_char *)malloc(sizeof(u_char)); + gate->area = (float *)malloc(sizeof(float)); + gate->netnum = (int *)malloc(sizeof(int)); + gate->node = (char **)malloc(sizeof(char *)); + gate->taps[0] = NULL; + gate->noderec[0] = NULL; + gate->netnum[0] = -1; + gate->node[0] = NULL; + gate->direction[0] = PORT_CLASS_DEFAULT; + gate->area[0] = 0.0; + gate->clientdata = (void *)NULL; + + /* Now do a search through the line for "+" entries */ + /* And process each. */ + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + if (*token == ';') break; + if (*token != '+') continue; + + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_property_keys); + if (subkey < 0) + { + LefError(DEF_WARNING, "Unknown pin property \"%s\" in " + "PINS definition; ignoring.\n", token); + continue; + } + switch (subkey) + { + case DEF_PINS_PROP_NET: + /* Get the net name */ + token = LefNextToken(f, TRUE); + gate->gatename = strdup(token); + gate->node[0] = strdup(token); + break; + case DEF_PINS_PROP_DIR: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_classes); + if (subkey < 0) + LefError(DEF_ERROR, "Unknown pin class %s\n", token); + else + gate->direction[0] = subkey; + break; + case DEF_PINS_PROP_LAYER: + curlayer = LefReadLayer(f, FALSE); + currect = LefReadRect(f, curlayer, oscale); + /* Warn if pin is on layer above routing layer limit? */ + if (currect) { + gate->width = currect->x2 - currect->x1; + gate->height = currect->y2 - currect->y1; + } + break; + case DEF_PINS_PROP_USE: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_uses); + if (subkey < 0) + LefError(DEF_ERROR, "Unknown pin use %s\n", token); + else + pin_use = subkey; + break; + case DEF_PINS_PROP_PLACED: + case DEF_PINS_PROP_FIXED: + case DEF_PINS_PROP_COVER: + DefReadLocation(gate, f, oscale); + break; + } + } + + /* If no NET was declared for pin, use pinname */ + if (gate->gatename == NULL) + gate->gatename = strdup(pinname); + + /* Make sure pin is at least the size of the route layer */ + drect = (DSEG)malloc(sizeof(struct dseg_)); + gate->taps[0] = drect; + drect->next = (DSEG)NULL; + + hwidth = LefGetRouteWidth(curlayer); + if (gate->width < hwidth) gate->width = hwidth; + if (gate->height < hwidth) gate->height = hwidth; + hwidth /= 2.0; + drect->x1 = gate->placedX - hwidth; + drect->y1 = gate->placedY - hwidth; + drect->x2 = gate->placedX + hwidth; + drect->y2 = gate->placedY + hwidth; + drect->layer = curlayer; + gate->obs = (DSEG)NULL; + gate->nodes = 1; + gate->next = Nlgates; + gate->last = (GATE)NULL; + if (Nlgates) Nlgates->last = gate; + Nlgates = gate; + Numpins++; + + // Used by Tcl version of qrouter + DefHashInstance(gate); + + break; + + case DEF_PINS_END: + if (!LefParseEndStatement(f, sname)) + { + LefError(DEF_ERROR, "Pins END statement missing.\n"); + keyword = -1; + } + if (pin_use != PORT_USE_DEFAULT && gate->direction[0] == + PORT_CLASS_DEFAULT) + { + /* Derive pin use from pin class, if needed */ + switch (pin_use) { + case PORT_USE_SIGNAL: + case PORT_USE_RESET: + case PORT_USE_CLOCK: + case PORT_USE_SCAN: + gate->direction[0] = PORT_CLASS_INPUT; + break; + + case PORT_USE_POWER: + case PORT_USE_GROUND: + case PORT_USE_TIEOFF: + case PORT_USE_ANALOG: + gate->direction[0] = PORT_CLASS_BIDIRECTIONAL; + break; + } + } + break; + } + if (keyword == DEF_PINS_END) break; + } + + if (processed == total) { + if (Verbose > 0) + fprintf(stdout, " Processed %d pins total.\n", processed); + } + else + LefError(DEF_WARNING, "Warning: Number of pins read (%d) does not match " + "the number declared (%d).\n", processed, total); +} + +/* + *------------------------------------------------------------ + * + * DefReadVias -- + * + * Read a VIAS section from a DEF file. + * + * Results: + * None. + * + * Side Effects: + * Technically, this routine should be creating a cell for + * each defined via. For now, it just computes the bounding + * rectangle and layer. + * + *------------------------------------------------------------ + */ + +enum def_vias_keys {DEF_VIAS_START = 0, DEF_VIAS_END}; +enum def_vias_prop_keys { + DEF_VIAS_PROP_RECT = 0}; + +static void +DefReadVias(f, sname, oscale, total) + FILE *f; + char *sname; + float oscale; + int total; +{ + char *token; + char vianame[LEF_LINE_MAX]; + int keyword, subkey; + int processed = 0; + int curlayer; + LefList lefl; + + static char *via_keys[] = { + "-", + "END", + NULL + }; + + static char *via_property_keys[] = { + "RECT", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, via_keys); + + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in VIAS " + "definition; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case DEF_VIAS_START: /* "-" keyword */ + + /* Update the record of the number of vias */ + /* processed and spit out a message for every 5% done. */ + + processed++; + + /* Get via name */ + token = LefNextToken(f, TRUE); + if (sscanf(token, "%2047s", vianame) != 1) + { + LefError(DEF_ERROR, "Bad via statement: Need via name\n"); + LefEndStatement(f); + break; + } + lefl = LefFindLayer(token); + if (lefl == NULL) + { + lefl = (LefList)calloc(1, sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_VIA; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + /* Note: "generated" flag only refers to vias that */ + /* are internally generated by qrouter. All others */ + /* in the DEF file are read/written verbatim. */ + lefl->info.via.generated = FALSE; + lefl->lefName = strdup(token); + + lefl->next = LefInfo; + LefInfo = lefl; + } + else + { + LefError(DEF_WARNING, "Warning: Composite via \"%s\" " + "redefined.\n", vianame); + lefl = LefRedefined(lefl, vianame); + } + + /* Now do a search through the line for "+" entries */ + /* And process each. */ + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + if (*token == ';') break; + if (*token != '+') continue; + + token = LefNextToken(f, TRUE); + subkey = Lookup(token, via_property_keys); + if (subkey < 0) + { + LefError(DEF_WARNING, "Unknown via property \"%s\" in " + "VIAS definition; ignoring.\n", token); + continue; + } + switch (subkey) + { + case DEF_VIAS_PROP_RECT: + curlayer = LefReadLayer(f, FALSE); + LefAddViaGeometry(f, lefl, curlayer, oscale); + break; + } + } + break; + + case DEF_VIAS_END: + if (!LefParseEndStatement(f, sname)) + { + LefError(DEF_ERROR, "Vias END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == DEF_VIAS_END) break; + } + + if (processed == total) { + if (Verbose > 0) + fprintf(stdout, " Processed %d vias total.\n", processed); + } + else + LefError(DEF_WARNING, "Warning: Number of vias read (%d) does not match " + "the number declared (%d).\n", processed, total); +} + +/* + *------------------------------------------------------------ + * + * DefReadBlockages -- + * + * Read a BLOCKAGES section from a DEF file. + * + * Results: + * None. + * + * Side Effects: + * UserObs list is updated with the additional + * obstructions. + * + *------------------------------------------------------------ + */ + +enum def_block_keys {DEF_BLOCK_START = 0, DEF_BLOCK_END}; + +static void +DefReadBlockages(FILE *f, char *sname, float oscale, int total) +{ + char *token; + int keyword; + int processed = 0; + DSEG drect, rsrch; + LefList lefl; + + static char *blockage_keys[] = { + "-", + "END", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, blockage_keys); + + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in BLOCKAGE " + "definition; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case DEF_BLOCK_START: /* "-" keyword */ + + /* Update the record of the number of components */ + /* processed and spit out a message for every 5% done. */ + + processed++; + + /* Get layer name */ + token = LefNextToken(f, TRUE); + lefl = LefFindLayer(token); + if (lefl != NULL) + { + drect = LefReadGeometry(NULL, f, oscale); + if (UserObs == NULL) + UserObs = drect; + else { + for (rsrch = UserObs; rsrch->next; rsrch = rsrch->next); + rsrch->next = drect; + } + } + else + { + LefError(DEF_ERROR, "Bad blockage statement: Need layer name\n"); + LefEndStatement(f); + break; + } + break; + + case DEF_BLOCK_END: + if (!LefParseEndStatement(f, sname)) + { + LefError(DEF_ERROR, "Blockage END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == DEF_BLOCK_END) break; + } + + if (processed == total) { + if (Verbose > 0) + fprintf(stdout, " Processed %d blockages total.\n", processed); + } + else + LefError(DEF_WARNING, "Warning: Number of blockages read (%d) does not match " + "the number declared (%d).\n", processed, total); +} + +/* + *------------------------------------------------------------ + * + * DefAddGateInstance -- + * + * Add a gate instance to the list of instances and + * instance hash table. The instance is assumed to + * have records gatename, gatetype, placedX, and + * placedY already set. The gate macro is found from + * the gatetype record, and all information about the + * cell macro is copied to the instance record, with + * positions adjusted for the instance. + * + * Results: + * None. + * + * Side Effects: + * Many. Cell instances are created and added to + * the database. + * + *------------------------------------------------------------ + */ + +void +DefAddGateInstance(GATE gate) +{ + GATE gateginfo; + int i; + DSEG drect, newrect; + double tmp; + + if (gate == NULL) return; + gateginfo = gate->gatetype; + if (gateginfo == NULL) return; + + /* Process the gate */ + gate->width = gateginfo->width; + gate->height = gateginfo->height; + gate->nodes = gateginfo->nodes; + gate->obs = (DSEG)NULL; + + gate->taps = (DSEG *)malloc(gate->nodes * sizeof(DSEG)); + gate->noderec = (NODE *)malloc(gate->nodes * sizeof(NODE)); + gate->direction = (u_char *)malloc(gate->nodes * sizeof(u_char)); + gate->area = (float *)malloc(gate->nodes * sizeof(float)); + gate->netnum = (int *)malloc(gate->nodes * sizeof(int)); + gate->node = (char **)malloc(gate->nodes * sizeof(char *)); + + /* Let the node names point to the master cell; */ + /* this is just diagnostic; allows us, for */ + /* instance, to identify vdd and gnd nodes, so */ + /* we don't complain about them being */ + /* disconnected. */ + + for (i = 0; i < gate->nodes; i++) { + gate->node[i] = gateginfo->node[i]; /* copy pointer */ + gate->direction[i] = gateginfo->direction[i]; /* copy */ + gate->area[i] = gateginfo->area[i]; + gate->taps[i] = (DSEG)NULL; + gate->netnum[i] = 0; /* Until we read NETS */ + gate->noderec[i] = NULL; + + /* Make a copy of the gate nodes and adjust for */ + /* instance position and number of layers */ + + for (drect = gateginfo->taps[i]; drect; drect = drect->next) { + newrect = (DSEG)malloc(sizeof(struct dseg_)); + *newrect = *drect; + newrect->next = gate->taps[i]; + gate->taps[i] = newrect; + } + + for (drect = gate->taps[i]; drect; drect = drect->next) { + // handle offset from gate origin + drect->x1 -= gateginfo->placedX; + drect->x2 -= gateginfo->placedX; + drect->y1 -= gateginfo->placedY; + drect->y2 -= gateginfo->placedY; + + // handle rotations and orientations here + if (gate->orient & MX) { + tmp = drect->x1; + drect->x1 = -drect->x2; + drect->x1 += gate->placedX + gateginfo->width; + drect->x2 = -tmp; + drect->x2 += gate->placedX + gateginfo->width; + } + else { + drect->x1 += gate->placedX; + drect->x2 += gate->placedX; + } + if (gate->orient & MY) { + tmp = drect->y1; + drect->y1 = -drect->y2; + drect->y1 += gate->placedY + gateginfo->height; + drect->y2 = -tmp; + drect->y2 += gate->placedY + gateginfo->height; + } + else { + drect->y1 += gate->placedY; + drect->y2 += gate->placedY; + } + } + } + + /* Make a copy of the gate obstructions and adjust */ + /* for instance position */ + for (drect = gateginfo->obs; drect; drect = drect->next) { + newrect = (DSEG)malloc(sizeof(struct dseg_)); + *newrect = *drect; + newrect->next = gate->obs; + gate->obs = newrect; + } + + for (drect = gate->obs; drect; drect = drect->next) { + drect->x1 -= gateginfo->placedX; + drect->x2 -= gateginfo->placedX; + drect->y1 -= gateginfo->placedY; + drect->y2 -= gateginfo->placedY; + + // handle rotations and orientations here + if (gate->orient & MX) { + tmp = drect->x1; + drect->x1 = -drect->x2; + drect->x1 += gate->placedX + gateginfo->width; + drect->x2 = -tmp; + drect->x2 += gate->placedX + gateginfo->width; + } + else { + drect->x1 += gate->placedX; + drect->x2 += gate->placedX; + } + if (gate->orient & MY) { + tmp = drect->y1; + drect->y1 = -drect->y2; + drect->y1 += gate->placedY + gateginfo->height; + drect->y2 = -tmp; + drect->y2 += gate->placedY + gateginfo->height; + } + else { + drect->y1 += gate->placedY; + drect->y2 += gate->placedY; + } + } + gate->next = Nlgates; + gate->last = (GATE)NULL; + if (Nlgates) Nlgates->last = gate; + Nlgates = gate; + Numgates++; + + // Used by Tcl version of qrouter + DefHashInstance(gate); +} + +/* + *------------------------------------------------------------ + * + * DefReadComponents -- + * + * Read a COMPONENTS section from a DEF file. + * + * Results: + * 0 on success, 1 on fatal error. + * + * Side Effects: + * Many. Cell instances are created and added to + * the database. + * + *------------------------------------------------------------ + */ + +enum def_comp_keys {DEF_COMP_START = 0, DEF_COMP_END}; +enum def_prop_keys { + DEF_PROP_FIXED = 0, DEF_PROP_COVER, + DEF_PROP_PLACED, DEF_PROP_UNPLACED, + DEF_PROP_SOURCE, DEF_PROP_WEIGHT, DEF_PROP_FOREIGN, + DEF_PROP_REGION, DEF_PROP_GENERATE, DEF_PROP_PROPERTY, + DEF_PROP_EEQMASTER}; + +static int +DefReadComponents(FILE *f, char *sname, float oscale, int total) +{ + GATE gateginfo; + GATE gate = NULL; + char *token; + char usename[512]; + int keyword, subkey, i; + int processed = 0; + char OK; + int err_fatal = 0; + + static char *component_keys[] = { + "-", + "END", + NULL + }; + + static char *property_keys[] = { + "FIXED", + "COVER", + "PLACED", + "UNPLACED", + "SOURCE", + "WEIGHT", + "FOREIGN", + "REGION", + "GENERATE", + "PROPERTY", + "EEQMASTER", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, component_keys); + + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in COMPONENT " + "definition; ignoring.\n", token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case DEF_COMP_START: /* "-" keyword */ + + /* Update the record of the number of components */ + /* processed and spit out a message for every 5% done. */ + + processed++; + + /* Get use and macro names */ + token = LefNextToken(f, TRUE); + if (sscanf(token, "%511s", usename) != 1) + { + LefError(DEF_ERROR, "Bad component statement: Need use " + "and macro names\n"); + LefEndStatement(f); + err_fatal++; + break; + } + token = LefNextToken(f, TRUE); + + /* Find the corresponding macro */ + OK = 0; + for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next) { + if (!strcasecmp(gateginfo->gatename, token)) { + OK = 1; + break; + } + } + if (!OK) { + LefError(DEF_ERROR, "Could not find a macro definition for \"%s\"\n", + token); + gate = NULL; + err_fatal++; + } + else { + gate = (GATE)malloc(sizeof(struct gate_)); + gate->gatename = strdup(usename); + gate->gatetype = gateginfo; + gate->clientdata = (void *)NULL; + } + + /* Now do a search through the line for "+" entries */ + /* And process each. */ + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + if (*token == ';') break; + if (*token != '+') continue; + + token = LefNextToken(f, TRUE); + subkey = Lookup(token, property_keys); + if (subkey < 0) + { + LefError(DEF_WARNING, "Unknown component property \"%s\" in " + "COMPONENT definition; ignoring.\n", token); + continue; + } + switch (subkey) + { + case DEF_PROP_PLACED: + case DEF_PROP_UNPLACED: + case DEF_PROP_FIXED: + case DEF_PROP_COVER: + DefReadLocation(gate, f, oscale); + break; + case DEF_PROP_SOURCE: + case DEF_PROP_WEIGHT: + case DEF_PROP_FOREIGN: + case DEF_PROP_REGION: + case DEF_PROP_GENERATE: + case DEF_PROP_PROPERTY: + case DEF_PROP_EEQMASTER: + token = LefNextToken(f, TRUE); + break; + } + } + DefAddGateInstance(gate); + break; + + case DEF_COMP_END: + if (!LefParseEndStatement(f, sname)) + { + LefError(DEF_ERROR, "Component END statement missing.\n"); + keyword = -1; + err_fatal++; + } + + /* Finish final call by placing the cell use */ + if ((total > 0) && (gate != NULL)) + { + // Nothing to do. . . gate has already been placed in list. + gate = NULL; + } + break; + } + if (keyword == DEF_COMP_END) break; + } + + if (processed == total) { + if (Verbose > 0) + fprintf(stdout, " Processed %d subcell instances total.\n", processed); + } + else + LefError(DEF_WARNING, "Warning: Number of subcells read (%d) does not match " + "the number declared (%d).\n", processed, total); + return err_fatal; +} + +/* + *------------------------------------------------------------ + * + * DefRead -- + * + * Read a .def file and parse die area, track positions, + * components, pins, and nets. + * + * Results: + * Returns the units scale, so the routed output can be + * scaled to match the DEF file header. + * + * Side Effects: + * Many. + * + *------------------------------------------------------------ + */ + +/* Enumeration of sections defined in DEF files */ + +enum def_sections {DEF_VERSION = 0, DEF_NAMESCASESENSITIVE, + DEF_UNITS, DEF_DESIGN, DEF_REGIONS, DEF_ROW, DEF_TRACKS, + DEF_GCELLGRID, DEF_DIVIDERCHAR, DEF_BUSBITCHARS, + DEF_PROPERTYDEFINITIONS, DEF_DEFAULTCAP, DEF_TECHNOLOGY, + DEF_HISTORY, DEF_DIEAREA, DEF_COMPONENTS, DEF_VIAS, + DEF_PINS, DEF_PINPROPERTIES, DEF_SPECIALNETS, + DEF_NETS, DEF_IOTIMINGS, DEF_SCANCHAINS, DEF_BLOCKAGES, + DEF_CONSTRAINTS, DEF_GROUPS, DEF_EXTENSION, + DEF_END}; + +int +DefRead(char *inName, float *retscale) +{ + FILE *f; + char filename[256]; + char namepos[32]; + char *token; + int keyword, dscale, total; + int curlayer = -1, channels; + int i; + int err_fatal = 0; + float oscale; + double start, step; + double llx, lly, urx, ury, locpitch; + double dXlowerbound, dYlowerbound, dXupperbound, dYupperbound; + char corient = '.'; + DSEG diearea; + ROW newrow; + + static char *orientations[] = { + "N", "S", "E", "W", "FN", "FS", "FE", "FW" + }; + static int oflags[] = { + RN, RS, RE, RW, RN | RF, RS | RF, RE | RF, RW | RF + }; + + static char *sections[] = { + "VERSION", + "NAMESCASESENSITIVE", + "UNITS", + "DESIGN", + "REGIONS", + "ROW", + "TRACKS", + "GCELLGRID", + "DIVIDERCHAR", + "BUSBITCHARS", + "PROPERTYDEFINITIONS", + "DEFAULTCAP", + "TECHNOLOGY", + "HISTORY", + "DIEAREA", + "COMPONENTS", + "VIAS", + "PINS", + "PINPROPERTIES", + "SPECIALNETS", + "NETS", + "IOTIMINGS", + "SCANCHAINS", + "BLOCKAGES", + "CONSTRAINTS", + "GROUPS", + "BEGINEXT", + "END", + NULL + }; + + if (!strrchr(inName, '.')) + sprintf(filename, "%s.def", inName); + else + strcpy(filename, inName); + + f = fopen(filename, "r"); + + if (f == NULL) + { + fprintf(stderr, "Cannot open input file: "); + perror(filename); + *retscale = (float)0.0; + return 1; + } + + /* Initialize */ + + if (Verbose > 0) { + fprintf(stdout, "Reading DEF data from file %s.\n", filename); + fflush(stdout); + } + + oscale = 1; + lefCurrentLine = 0; + + DefHashInit(); + + /* Read file contents */ + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, sections); + if (keyword < 0) + { + LefError(DEF_WARNING, "Unknown keyword \"%s\" in DEF file; " + "ignoring.\n", token); + LefEndStatement(f); + continue; + } + if (keyword != DEF_TRACKS) corient = '.'; + + switch (keyword) + { + case DEF_VERSION: + LefEndStatement(f); + break; + case DEF_NAMESCASESENSITIVE: + LefEndStatement(f); + break; + case DEF_TECHNOLOGY: + token = LefNextToken(f, TRUE); + if (Verbose > 0) + fprintf(stdout, "Diagnostic: DEF file technology: \"%s\"\n", + token); + LefEndStatement(f); + break; + case DEF_REGIONS: + LefSkipSection(f, sections[DEF_REGIONS]); + break; + case DEF_DESIGN: + token = LefNextToken(f, TRUE); + if (Verbose > 0) + fprintf(stdout, "Diagnostic: Design name: \"%s\"\n", token); + DEFDesignName = strdup(token); + LefEndStatement(f); + break; + case DEF_UNITS: + token = LefNextToken(f, TRUE); + token = LefNextToken(f, TRUE); + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &dscale) != 1) + { + LefError(DEF_ERROR, "Invalid syntax for UNITS statement.\n"); + LefError(DEF_WARNING, "Assuming default value of 100\n"); + dscale = 100; + } + /* We don't care if the scale is 100, 200, 1000, or 2000. */ + /* Do we need to deal with numeric roundoff issues? */ + oscale *= (float)dscale; + LefEndStatement(f); + break; + case DEF_ROW: + newrow = (ROW)malloc(sizeof(struct row_)); + token = LefNextToken(f, TRUE); + newrow->rowname = strdup(token); + token = LefNextToken(f, TRUE); + newrow->sitename = strdup(token); + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->x); + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->y); + token = LefNextToken(f, TRUE); + keyword = Lookup(token, orientations); + if (keyword < 0) + newrow->orient = 0; + else + newrow->orient = oflags[keyword]; + token = LefNextToken(f, TRUE); /* skip "DO" */ + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->xnum); + token = LefNextToken(f, TRUE); /* skip "BY" */ + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->ynum); + token = LefNextToken(f, TRUE); /* skip "STEP" */ + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->xstep); + token = LefNextToken(f, TRUE); + sscanf(token, "%d", &newrow->ystep); + sprintf(namepos, "%d", newrow->y); + HashPtrInstall(namepos, newrow, &RowTable); + LefEndStatement(f); + break; + case DEF_TRACKS: + token = LefNextToken(f, TRUE); + if (strlen(token) != 1) { + LefError(DEF_ERROR, "Problem parsing track orientation (X or Y).\n"); + } + corient = tolower(token[0]); // X or Y + token = LefNextToken(f, TRUE); + if (sscanf(token, "%lg", &start) != 1) { + LefError(DEF_ERROR, "Problem parsing track start position.\n"); + err_fatal++; + } + token = LefNextToken(f, TRUE); + if (strcmp(token, "DO")) { + LefError(DEF_ERROR, "TRACKS missing DO loop.\n"); + err_fatal++; + } + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &channels) != 1) { + LefError(DEF_ERROR, "Problem parsing number of track channels.\n"); + err_fatal++; + } + token = LefNextToken(f, TRUE); + if (strcmp(token, "STEP")) { + LefError(DEF_ERROR, "TRACKS missing STEP size.\n"); + err_fatal++; + } + token = LefNextToken(f, TRUE); + if (sscanf(token, "%lg", &step) != 1) { + LefError(DEF_ERROR, "Problem parsing track step size.\n"); + err_fatal++; + } + token = LefNextToken(f, TRUE); + if (!strcmp(token, "LAYER")) { + curlayer = LefReadLayer(f, FALSE); + } + if (corient == 'x') { + locpitch = step / oscale; + if ((PitchX == 0.0) || ((locpitch < PitchX) && (locpitch != 0))) + PitchX = locpitch; + llx = start; + urx = start + step * channels; + if ((llx / oscale) < Xlowerbound) + Xlowerbound = llx / oscale; + if ((urx / oscale) > Xupperbound) + Xupperbound = urx / oscale; + } + else { + locpitch = step / oscale; + if ((PitchY == 0.0) || ((locpitch < PitchY) && (locpitch != 0))) + PitchY = locpitch; + lly = start; + ury = start + step * channels; + if ((lly / oscale) < Ylowerbound) + Ylowerbound = lly / oscale; + if ((ury / oscale) > Yupperbound) + Yupperbound = ury / oscale; + } + LefEndStatement(f); + break; + case DEF_GCELLGRID: + LefEndStatement(f); + break; + case DEF_DIVIDERCHAR: + LefEndStatement(f); + break; + case DEF_BUSBITCHARS: + LefEndStatement(f); + break; + case DEF_HISTORY: + LefEndStatement(f); + break; + case DEF_DIEAREA: + diearea = LefReadRect(f, 0, oscale); // no current layer, use 0 + dXlowerbound = diearea->x1; + dYlowerbound = diearea->y1; + dXupperbound = diearea->x2; + dYupperbound = diearea->y2; + /* Seed actual lower/upper bounds with the midpoint */ + Xlowerbound = (diearea->x1 + diearea->x2) / 2; + Ylowerbound = (diearea->y1 + diearea->y2) / 2; + Xupperbound = Xlowerbound; + Yupperbound = Ylowerbound; + LefEndStatement(f); + break; + case DEF_PROPERTYDEFINITIONS: + LefSkipSection(f, sections[DEF_PROPERTYDEFINITIONS]); + break; + case DEF_DEFAULTCAP: + LefSkipSection(f, sections[DEF_DEFAULTCAP]); + break; + case DEF_COMPONENTS: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + err_fatal += DefReadComponents(f, sections[DEF_COMPONENTS], oscale, total); + break; + case DEF_BLOCKAGES: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + DefReadBlockages(f, sections[DEF_BLOCKAGES], oscale, total); + break; + case DEF_VIAS: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + DefReadVias(f, sections[DEF_VIAS], oscale, total); + break; + case DEF_PINS: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + DefReadPins(f, sections[DEF_PINS], oscale, total); + break; + case DEF_PINPROPERTIES: + LefSkipSection(f, sections[DEF_PINPROPERTIES]); + break; + case DEF_SPECIALNETS: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + numSpecial = DefReadNets(f, sections[DEF_SPECIALNETS], oscale, TRUE, + total); + break; + case DEF_NETS: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%d", &total) != 1) total = 0; + LefEndStatement(f); + DefReadNets(f, sections[DEF_NETS], oscale, FALSE, total); + break; + case DEF_IOTIMINGS: + LefSkipSection(f, sections[DEF_IOTIMINGS]); + break; + case DEF_SCANCHAINS: + LefSkipSection(f, sections[DEF_SCANCHAINS]); + break; + case DEF_CONSTRAINTS: + LefSkipSection(f, sections[DEF_CONSTRAINTS]); + break; + case DEF_GROUPS: + LefSkipSection(f, sections[DEF_GROUPS]); + break; + case DEF_EXTENSION: + LefSkipSection(f, sections[DEF_EXTENSION]); + break; + case DEF_END: + if (!LefParseEndStatement(f, "DESIGN")) + { + LefError(DEF_ERROR, "END statement out of context.\n"); + keyword = -1; + } + break; + } + if (keyword == DEF_END) break; + } + if (Verbose > 0) + fprintf(stdout, "DEF read: Processed %d lines.\n", lefCurrentLine); + LefError(DEF_ERROR, NULL); /* print statement of errors, if any, and reset */ + + /* If there were no TRACKS statements, then use the DIEAREA */ + if (Xlowerbound == Xupperbound) { + Xlowerbound = dXlowerbound; + Xupperbound = dXupperbound; + } + if (Ylowerbound == Yupperbound) { + Ylowerbound = dYlowerbound; + Yupperbound = dYupperbound; + } + + /* Cleanup */ + + if (f != NULL) fclose(f); + *retscale = oscale; + return err_fatal; +} diff --git a/src/readdef.h b/src/readdef.h new file mode 100644 index 0000000..2a1da5d --- /dev/null +++ b/src/readdef.h @@ -0,0 +1,31 @@ +/* + * def.h -- + * + * This file includes the DEF I/O functions + * + */ + +#ifndef _DEFINT_H +#define _DEFINT_H + +#include "lef.h" + +extern int DefRead(char *inName, float *); + +extern int Numnets; +extern int Numgates; +extern int Numpins; +extern int numSpecial; + +extern GATE DefFindGate(char *name); +extern NET DefFindNet(char *name); +extern ROW DefFindRow(int yval); +extern ROW DefLowestRow(); +extern void DefAddGateInstance(GATE gate); +extern char *DefDesign(); + +/* External access to hash tables for recursion functions */ +extern struct hashtable InstanceTable; +extern struct hashtable NetTable; + +#endif /* _DEFINT_H */ diff --git a/src/readlef.c b/src/readlef.c new file mode 100644 index 0000000..21a8b1d --- /dev/null +++ b/src/readlef.c @@ -0,0 +1,3436 @@ +/* + * lef.c -- + * + * This module incorporates the LEF/DEF format for standard-cell routing + * route. + * + * Version 0.1 (September 26, 2003): LEF input handling. Includes creation + * of cells from macro statements, handling of pins, ports, obstructions, and + * associated geometry. + * + * Written by Tim Edwards, Open Circuit Design + * Modified June 2011 for use with qrouter. + * Modified November 2018 for use with qflow. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/time.h> +#include <math.h> + +#include "hash.h" +#include "readlef.h" + +/*----------------------------------------------------------------------*/ +/* Global variables */ +/*----------------------------------------------------------------------*/ + +GATE GateInfo = NULL; // standard cell macro information +u_char Verbose = 0; +char delimiter; // opening bus delimiter; + +struct hashtable MacroTable; + +/*----------------------------------------------------------------------*/ + +/* Current line number for reading */ +int lefCurrentLine = 0; + +/* Information about routing layers */ +LefList LefInfo = NULL; + +/* Information about what vias to use */ +LinkedStringPtr AllowedVias = NULL; + +/* Gate information is in the linked list GateInfo, imported */ + +/* + *--------------------------------------------------------- + * Initialize hash table of cells + *--------------------------------------------------------- + */ + +static void +LefHashInit(void) +{ + /* Initialize the macro hash table */ + InitializeHashTable(&MacroTable, SMALLHASHSIZE); +} + +/* + *--------------------------------------------------------- + * Add macro to Lef cell macro hash table + *--------------------------------------------------------- + */ + +static void +LefHashMacro(GATE gateginfo) +{ + HashPtrInstall(gateginfo->gatename, gateginfo, &MacroTable); +} + +/*--------------------------------------------------------- + * Lookup -- + * Searches a table of strings to find one that matches a given + * string. It's useful mostly for command lookup. + * + * Only the portion of a string in the table up to the first + * blank character is considered significant for matching. + * + * Results: + * If str is the same as + * or an unambiguous abbreviation for one of the entries + * in table, then the index of the matching entry is returned. + * If str is not the same as any entry in the table, but + * an abbreviation for more than one entry, + * then -1 is returned. If str doesn't match any entry, then + * -2 is returned. Case differences are ignored. + * + * NOTE: + * Table entries need no longer be in alphabetical order + * and they need not be lower case. The irouter command parsing + * depends on these features. + * + * Side Effects: + * None. + *--------------------------------------------------------- + */ + +int +Lookup(str, table) + char *str; /* Pointer to a string to be looked up */ + char *(table[]); /* Pointer to an array of string pointers + * which are the valid commands. + * The end of + * the table is indicated by a NULL string. + */ +{ + int match = -2; /* result, initialized to -2 = no match */ + int pos; + int ststart = 0; + + /* search for match */ + for (pos=0; table[pos] != NULL; pos++) + { + char *tabc = table[pos]; + char *strc = &(str[ststart]); + while (*strc!='\0' && *tabc!=' ' && + ((*tabc==*strc) || + (isupper(*tabc) && islower(*strc) && (tolower(*tabc)== *strc))|| + (islower(*tabc) && isupper(*strc) && (toupper(*tabc)== *strc)) )) + { + strc++; + tabc++; + } + + + if (*strc=='\0') + { + /* entry matches */ + if(*tabc==' ' || *tabc=='\0') + { + /* exact match - record it and terminate search */ + match = pos; + break; + } + else if (match == -2) + { + /* inexact match and no previous match - record this one + * and continue search */ + match = pos; + } + else + { + /* previous match, so string is ambiguous unless exact + * match exists. Mark ambiguous for now, and continue + * search. + */ + match = -1; + } + } + } + return(match); +} + +/* + * ---------------------------------------------------------------------------- + * LookupFull -- + * + * Look up a string in a table of pointers to strings. The last + * entry in the string table must be a NULL pointer. + * This is much simpler than Lookup() in that it does not + * allow abbreviations. It does, however, ignore case. + * + * Results: + * Index of the name supplied in the table, or -1 if the name + * is not found. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------------- + */ + +int +LookupFull(name, table) + char *name; + char **table; +{ + char **tp; + + for (tp = table; *tp; tp++) + { + if (strcmp(name, *tp) == 0) + return (tp - table); + else + { + char *sptr, *tptr; + for (sptr = name, tptr = *tp; ((*sptr != '\0') && (*tptr != '\0')); + sptr++, tptr++) + if (toupper(*sptr) != toupper(*tptr)) + break; + if ((*sptr == '\0') && (*tptr == '\0')) + return (tp - table); + } + } + + return (-1); +} + + +/* + *------------------------------------------------------------ + * + * LefNextToken -- + * + * Move to the next token in the stream input. + * If "ignore_eol" is FALSE, then the end-of-line character + * "\n" will be returned as a token when encountered. + * Otherwise, end-of-line will be ignored. + * + * Results: + * Pointer to next token to parse + * + * Side Effects: + * May read a new line from the specified file. + * + * Warnings: + * The return result of LefNextToken will be overwritten by + * subsequent calls to LefNextToken if more than one line of + * input is parsed. + * + *------------------------------------------------------------ + */ + +char * +LefNextToken(FILE *f, u_char ignore_eol) +{ + static char line[LEF_LINE_MAX + 2]; /* input buffer */ + static char *nexttoken = NULL; /* pointer to next token */ + static char *curtoken; /* pointer to current token */ + static char eol_token='\n'; + + /* Read a new line if necessary */ + + if (nexttoken == NULL) + { + for(;;) + { + if (fgets(line, LEF_LINE_MAX + 1, f) == NULL) return NULL; + lefCurrentLine++; + curtoken = line; + while (isspace(*curtoken) && (*curtoken != '\n') && (*curtoken != '\0')) + curtoken++; /* skip leading whitespace */ + + if ((*curtoken != '#') && (*curtoken != '\n') && (*curtoken != '\0')) + { + nexttoken = curtoken; + break; + } + } + if (!ignore_eol) + return &eol_token; + } + else + curtoken = nexttoken; + + /* Find the next token; set to NULL if none (end-of-line). */ + /* Treat quoted material as a single token */ + + if (*nexttoken == '\"') { + nexttoken++; + while (((*nexttoken != '\"') || (*(nexttoken - 1) == '\\')) && + (*nexttoken != '\0')) { + if (*nexttoken == '\n') { + if (fgets(nexttoken + 1, LEF_LINE_MAX - + (size_t)(nexttoken - line), f) == NULL) + return NULL; + } + nexttoken++; /* skip all in quotes (move past current token) */ + } + if (*nexttoken == '\"') + nexttoken++; + } + else { + while (!isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n')) + nexttoken++; /* skip non-whitespace (move past current token) */ + } + + /* Terminate the current token */ + if (*nexttoken != '\0') *nexttoken++ = '\0'; + + while (isspace(*nexttoken) && (*nexttoken != '\0') && (*nexttoken != '\n')) + nexttoken++; /* skip any whitespace */ + + if ((*nexttoken == '#') || (*nexttoken == '\n') || (*nexttoken == '\0')) + nexttoken = NULL; + + return curtoken; +} + +/* + *------------------------------------------------------------ + * + * LefError -- + * + * Print an error message (via fprintf) giving the line + * number of the input file on which the error occurred. + * + * "type" should be either LEF_ERROR or LEF_WARNING (or DEF_*). + * + * Results: + * None. + * + * Side Effects: + * Prints to the output (stderr). + * + *------------------------------------------------------------ + */ + +void +LefError(int type, char *fmt, ...) +{ + static int fatal = 0; + static int nonfatal = 0; + char lefordef = 'L'; + int errors; + va_list args; + + if (Verbose == 0) return; + + if ((type == DEF_WARNING) || (type == DEF_ERROR)) lefordef = 'D'; + + errors = fatal + nonfatal; + if (fmt == NULL) /* Special case: report any errors and reset */ + { + if (errors > 0) + { + fprintf(stdout, "%cEF Read: encountered %d error%s and %d warning%s total.\n", + lefordef, + fatal, (fatal == 1) ? "" : "s", + nonfatal, (nonfatal == 1) ? "" : "s"); + fatal = 0; + nonfatal = 0; + } + return; + } + + if (errors < LEF_MAX_ERRORS) + { + fprintf(stderr, "%cEF Read, Line %d: ", lefordef, lefCurrentLine); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fflush(stderr); + } + else if (errors == LEF_MAX_ERRORS) + fprintf(stderr, "%cEF Read: Further errors/warnings will not be reported.\n", + lefordef); + + if ((type == LEF_ERROR) || (type == DEF_ERROR)) + fatal++; + else if ((type == LEF_WARNING) || (type == DEF_WARNING)) + nonfatal++; +} + +/* + *------------------------------------------------------------ + * + * LefParseEndStatement -- + * + * Check if the string passed in "lineptr" contains the + * appropriate matching keyword. Sections in LEF files + * should end with "END (keyword)" or "END". To check + * against the latter case, "match" should be NULL. + * + * Results: + * TRUE if the line matches the expected end statement, + * FALSE if not. + * + * Side effects: + * None. + * + *------------------------------------------------------------ + */ + +u_char +LefParseEndStatement(FILE *f, char *match) +{ + char *token; + int keyword; + char *match_name[2]; + + match_name[0] = match; + match_name[1] = NULL; + + token = LefNextToken(f, (match == NULL) ? FALSE : TRUE); + if (token == NULL) + { + LefError(LEF_ERROR, "Bad file read while looking for END statement\n"); + return FALSE; + } + + /* END or ENDEXT */ + if ((*token == '\n') && (match == NULL)) return TRUE; + + /* END <section_name> */ + else { + keyword = LookupFull(token, match_name); + if (keyword == 0) + return TRUE; + else + return FALSE; + } +} + +/* + *------------------------------------------------------------ + * + * LefSkipSection -- + * + * Skip to the "END" record of a LEF input section + * String "section" must follow the "END" statement in + * the file to be considered a match; however, if + * section = NULL, then "END" must have no strings + * following. + * + * Results: + * None. + * + * Side Effects: + * Reads input from the specified file. Prints an + * error message if the expected END record cannot + * be found. + * + *------------------------------------------------------------ + */ + +void +LefSkipSection(FILE *f, char *section) +{ + char *token; + int keyword; + static char *end_section[] = { + "END", + "ENDEXT", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + if ((keyword = Lookup(token, end_section)) == 0) + { + if (LefParseEndStatement(f, section)) + return; + } + else if (keyword == 1) + { + if (!strcmp(section, "BEGINEXT")) + return; + } + } + + LefError(LEF_ERROR, "Section %s has no END record!\n", section); + return; +} + +/* + *------------------------------------------------------------ + * + * lefFindCell -- + * + * "name" is the name of the cell to search for. + * Returns the GATE entry for the cell from the GateInfo + * hash table. + * + *------------------------------------------------------------ + */ + +GATE +lefFindCell(char *name) +{ + GATE gateginfo; + + gateginfo = (GATE)HashLookup(name, &MacroTable); + return gateginfo; +} + +/* + *------------------------------------------------------------ + * + * LefLower -- + * + * Convert a token in a LEF or DEF file to all-lowercase. + * + *------------------------------------------------------------ + */ + +char * +LefLower(char *token) +{ + char *tptr; + + for (tptr = token; *tptr != '\0'; tptr++) + *tptr = tolower(*tptr); + + return token; +} + +LefList +LefRedefined(LefList lefl, char *redefname) +{ + LefList slef, newlefl; + char *altName; + int records; + DSEG drect; + + /* check if more than one entry points to the same */ + /* lefLayer record. If so, we will also record the */ + /* name of the first type that is not the same as */ + /* "redefname". */ + + records = 0; + altName = NULL; + + for (slef = LefInfo; slef; slef = slef->next) { + if (slef == lefl) + records++; + if (altName == NULL) + if (strcmp(slef->lefName, redefname)) + altName = (char *)slef->lefName; + } + if (records == 1) + { + /* Only one name associated with the record, so */ + /* just clear all the allocated information. */ + + while (lefl->info.via.lr) { + drect = lefl->info.via.lr->next; + free(lefl->info.via.lr); + lefl->info.via.lr = drect; + } + newlefl = lefl; + } + else + { + slef = LefFindLayer(redefname); + + newlefl = (LefList)malloc(sizeof(lefLayer)); + newlefl->lefName = strdup(newlefl->lefName); + + newlefl->next = LefInfo; + LefInfo = newlefl; + + /* If the canonical name of the original entry */ + /* is "redefname", then change it. */ + + if (!strcmp(slef->lefName, redefname)) + if (altName != NULL) + slef->lefName = altName; + } + newlefl->type = -1; + newlefl->obsType = -1; + newlefl->info.via.area.x1 = 0.0; + newlefl->info.via.area.x2 = 0.0; + newlefl->info.via.area.y1 = 0.0; + newlefl->info.via.area.y2 = 0.0; + newlefl->info.via.area.layer = -1; + newlefl->info.via.cell = (GATE)NULL; + newlefl->info.via.lr = (DSEG)NULL; + + return newlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers + *------------------------------------------------------------ + */ + +LefList +LefFindLayer(char *token) +{ + LefList lefl, rlefl; + + if (token == NULL) return NULL; + rlefl = (LefList)NULL; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (!strcmp(lefl->lefName, token)) { + rlefl = lefl; + break; + } + } + return rlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers, by layer number + *------------------------------------------------------------ + */ + +LefList +LefFindLayerByNum(int layer) +{ + LefList lefl, rlefl; + + rlefl = (LefList)NULL; + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->type == layer) { + rlefl = lefl; + break; + } + } + return rlefl; +} + +/* + *------------------------------------------------------------ + * Find a layer record in the list of layers, and return the + * layer number. + *------------------------------------------------------------ + */ + +int +LefFindLayerNum(char *token) +{ + LefList lefl; + + lefl = LefFindLayer(token); + if (lefl) + return lefl->type; + else + return -1; +} + +/* + *--------------------------------------------------------------- + * Find the maximum layer number defined by the LEF file + * This includes layer numbers assigned to both routes and + * via cut layers. + *--------------------------------------------------------------- + */ + +int +LefGetMaxLayer(void) +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->type > maxlayer) + maxlayer = lefl->type; + } + return (maxlayer + 1); +} + +/* + *--------------------------------------------------------------- + * Find the maximum routing layer number defined by the LEF file + * Note that as defined, this returns value (layer_index + 1). + *--------------------------------------------------------------- + */ + +int +LefGetMaxRouteLayer(void) +{ + int maxlayer = -1; + LefList lefl; + + for (lefl = LefInfo; lefl; lefl = lefl->next) { + if (lefl->lefClass != CLASS_ROUTE) continue; + if (lefl->type > maxlayer) + maxlayer = lefl->type; + } + return (maxlayer + 1); +} + +/* + *------------------------------------------------------------ + * Return the route keepout area, defined as the route space + * plus 1/2 the route width. This is the distance outward + * from an obstruction edge within which one cannot place a + * route. + * + * If no route layer is defined, then we pick up the value + * from information in the route.cfg file (if any). Here + * we define it as the route pitch less 1/2 the route width, + * which is the same as above if the route pitch has been + * chosen for minimum spacing. + * + * If all else fails, return zero. + *------------------------------------------------------------ + */ + +double +LefGetRouteKeepout(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.width / 2.0 + + lefl->info.route.spacing->spacing; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Similar routine to the above. Return the route width for + * a route layer. Return value in microns. If there is no + * LEF file information about the route width, then return + * half of the minimum route pitch. + *------------------------------------------------------------ + */ + +double +LefGetRouteWidth(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.width; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Similar to the above, return the width of a via. Arguments + * are the via record, the layer to check the width of, and + * direction "dir" = 1 for height and 0 for width. + *------------------------------------------------------------ + */ + +double +LefGetViaWidth(LefList lefl, int layer, int dir) +{ + double width, maxwidth; + DSEG lrect; + + maxwidth = 0.0; + + if (lefl->lefClass == CLASS_VIA) { + if (lefl->info.via.area.layer == layer) { + if (dir) + width = lefl->info.via.area.y2 - lefl->info.via.area.y1; + else + width = lefl->info.via.area.x2 - lefl->info.via.area.x1; + if (width > maxwidth) maxwidth = width; + } + for (lrect = lefl->info.via.lr; lrect; lrect = lrect->next) { + if (lrect->layer == layer) { + if (dir) + width = lrect->y2 - lrect->y1; + else + width = lrect->x2 - lrect->x1; + if (width > maxwidth) maxwidth = width; + } + } + } + return maxwidth / 2; +} + +/* + *------------------------------------------------------------ + * Similar routine to the above. Return the route offset for + * a route layer. Return value in microns. If there is no + * LEF file information about the route offset, then return + * half of the minimum route pitch. + *------------------------------------------------------------ + */ + +double +LefGetRouteOffset(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + o = lefl->info.route.hdirection; + if (o == TRUE) + return lefl->info.route.offsety; + else + return lefl->info.route.offsetx; + } + } + return 0.0; +} + +double +LefGetRouteOffsetX(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.offsetx; + } + } + return 0.0; +} + +double +LefGetRouteOffsetY(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.offsety; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Find and return the minimum metal area requirement for a + * route layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteMinArea(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.minarea; + } + } + return 0.0; /* Assume no minimum area requirement */ +} + +/* + *------------------------------------------------------------ + * Get route spacing rule (minimum width) + *------------------------------------------------------------ + */ + +double +LefGetRouteSpacing(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + if (lefl->info.route.spacing) + return lefl->info.route.spacing->spacing; + else + return 0.0; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Find route spacing to a metal layer of specific width + *------------------------------------------------------------ + */ + +double +LefGetRouteWideSpacing(int layer, double width) +{ + LefList lefl; + lefSpacingRule *srule; + double spacing; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + // Prepare a default in case of bad values + spacing = lefl->info.route.spacing->spacing; + for (srule = lefl->info.route.spacing; srule; srule = srule->next) { + if (srule->width > width) break; + spacing = srule->spacing; + } + return spacing; + } + } + return 0.0; +} + +/* + *----------------------------------------------------------------- + * Get the route pitch in the preferred direction for a given layer + *----------------------------------------------------------------- + */ + +double +LefGetRoutePitch(int layer) +{ + LefList lefl; + u_char o; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + o = lefl->info.route.hdirection; + if (o == TRUE) + return lefl->info.route.pitchy; + else + return lefl->info.route.pitchx; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the route pitch in X for a given layer + *------------------------------------------------------------ + */ + +double +LefGetRoutePitchX(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.pitchx; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the route pitch in Y for a given layer + *------------------------------------------------------------ + */ + +double +LefGetRoutePitchY(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.pitchy; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Set the route pitch in X for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchX(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchx = value; + } + } +} + +/* + *------------------------------------------------------------ + * Set the route pitch in Y for a given layer + *------------------------------------------------------------ + */ + +void +LefSetRoutePitchY(int layer, double value) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + lefl->info.route.pitchy = value; + } + } +} + +/* + *------------------------------------------------------------ + * Get the route name for a given layer + *------------------------------------------------------------ + */ + +char * +LefGetRouteName(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->lefName; + } + } + return NULL; +} + +/* + *------------------------------------------------------------ + * Get the route orientation for the given layer, + * where the result is 1 for horizontal, 0 for vertical, and + * -1 if the layer is not found. + *------------------------------------------------------------ + */ + +int +LefGetRouteOrientation(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return (int)lefl->info.route.hdirection; + } + } + return -1; +} + +/* + *------------------------------------------------------------ + * Get the route resistance and capacitance information. + * Fill in the pointer values with the relevant information. + * Return 0 on success, -1 if the layer is not found. + *------------------------------------------------------------ + */ + +int +LefGetRouteRCvalues(int layer, double *areacap, double *edgecap, + double *respersq) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + *areacap = (double)lefl->info.route.areacap; + *edgecap = (double)lefl->info.route.edgecap; + *respersq = (double)lefl->info.route.respersq; + return 0; + } + } + return -1; +} + +/* + *------------------------------------------------------------ + * Get the antenna violation area ratio for the given layer. + *------------------------------------------------------------ + */ + +double +LefGetRouteAreaRatio(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.antenna; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * Get the antenna violation area calculation method for the + * given layer. + *------------------------------------------------------------ + */ + +u_char +LefGetRouteAntennaMethod(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.method; + } + } + return CALC_NONE; +} + +/* + *------------------------------------------------------------ + * Get the route metal layer thickness (if any is defined) + *------------------------------------------------------------ + */ + +double +LefGetRouteThickness(int layer) +{ + LefList lefl; + + lefl = LefFindLayerByNum(layer); + if (lefl) { + if (lefl->lefClass == CLASS_ROUTE) { + return lefl->info.route.thick; + } + } + return 0.0; +} + +/* + *------------------------------------------------------------ + * LefReadLayers -- + * + * Read a LEF "LAYER" record from the file. + * If "obstruct" is TRUE, returns the layer mapping + * for obstruction geometry as defined in the + * technology file (if it exists), and up to two + * types are returned (the second in the 3rd argument + * pointer). + * + * Results: + * Returns layer number or -1 if no matching type is found. + * + * Side Effects: + * Reads input from file f; + * + *------------------------------------------------------------ + */ + +int +LefReadLayers(f, obstruct, lreturn) + FILE *f; + u_char obstruct; + int *lreturn; +{ + char *token; + int curlayer = -1; + LefList lefl = NULL; + + token = LefNextToken(f, TRUE); + if (*token == ';') + { + LefError(LEF_ERROR, "Bad Layer statement\n"); + return -1; + } + else + { + lefl = LefFindLayer(token); + if (lefl) + { + if (obstruct) + { + /* Use the obstruction type, if it is defined */ + curlayer = lefl->obsType; + if ((curlayer < 0) && (lefl->lefClass != CLASS_IGNORE)) + curlayer = lefl->type; + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) + if (lreturn) *lreturn = lefl->info.via.obsType; + } + else + { + if (lefl->lefClass != CLASS_IGNORE) + curlayer = lefl->type; + } + } + if ((curlayer < 0) && ((!lefl) || (lefl->lefClass != CLASS_IGNORE))) + { + /* CLASS_VIA in lefl record is a cut, and the layer */ + /* geometry is ignored for the purpose of routing. */ + + if (lefl && (lefl->lefClass == CLASS_CUT)) { + int cuttype; + + /* By the time a cut layer is being requested, */ + /* presumably from a VIA definition, the route */ + /* layers should all be defined, so start */ + /* assigning layers to cuts. */ + + /* If a cut layer is found with an unassigned number, */ + /* then assign it here. */ + cuttype = LefGetMaxLayer(); + if (cuttype < MAX_TYPES) { + lefl->type = cuttype; + curlayer = cuttype; + } + else + LefError(LEF_WARNING, "Too many cut types; type \"%s\" ignored.\n", + token); + } + else if ((!lefl) || (lefl->lefClass != CLASS_VIA)) + LefError(LEF_ERROR, "Don't know how to parse layer \"%s\"\n", token); + } + } + return curlayer; +} + +/* + *------------------------------------------------------------ + * LefReadLayer -- + * + * Read a LEF "LAYER" record from the file. + * If "obstruct" is TRUE, returns the layer mapping + * for obstruction geometry as defined in the + * technology file (if it exists). + * + * Results: + * Returns a layer number or -1 if no match is found. + * + * Side Effects: + * Reads input from file f; + * + *------------------------------------------------------------ + */ + +int +LefReadLayer(FILE *f, u_char obstruct) +{ + return LefReadLayers(f, obstruct, (int *)NULL); +} + +/* + *------------------------------------------------------------ + * LefReadRect -- + * + * Read a LEF "RECT" record from the file, and + * return a Rect in micron coordinates. + * + * Results: + * Returns a pointer to a Rect containing the micron + * coordinates, or NULL if an error occurred. + * + * Side Effects: + * Reads input from file f; + * + * Note: + * LEF/DEF does NOT define a RECT record as having (...) + * pairs, only routes. However, at least one DEF file + * contains this syntax, so it is checked. + * + *------------------------------------------------------------ + */ + +DSEG +LefReadRect(FILE *f, int curlayer, float oscale) +{ + char *token; + float llx, lly, urx, ury; + static struct dseg_ paintrect; + u_char needMatch = FALSE; + + token = LefNextToken(f, TRUE); + if (*token == '(') + { + token = LefNextToken(f, TRUE); + needMatch = TRUE; + } + if (!token || sscanf(token, "%f", &llx) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &lly) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (needMatch) + { + if (*token != ')') goto parse_error; + else token = LefNextToken(f, TRUE); + needMatch = FALSE; + } + if (*token == '(') + { + token = LefNextToken(f, TRUE); + needMatch = TRUE; + } + if (!token || sscanf(token, "%f", &urx) != 1) goto parse_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &ury) != 1) goto parse_error; + if (needMatch) + { + token = LefNextToken(f, TRUE); + if (*token != ')') goto parse_error; + } + if (curlayer < 0) { + /* Issue warning but keep geometry with negative layer number */ + LefError(LEF_WARNING, "No layer defined for RECT.\n"); + } + + /* Scale coordinates (microns to centimicrons) */ + + paintrect.x1 = llx / oscale; + paintrect.y1 = lly / oscale; + paintrect.x2 = urx / oscale; + paintrect.y2 = ury / oscale; + paintrect.layer = curlayer; + return (&paintrect); + +parse_error: + LefError(LEF_ERROR, "Bad port geometry: RECT requires 4 values.\n"); + return (DSEG)NULL; +} + +/* + *------------------------------------------------------------ + * LefReadEnclosure -- + * + * Read a LEF "ENCLOSURE" record from the file, and + * return a Rect in micron coordinates, representing + * the bounding box of the stated enclosure dimensions + * in both directions, centered on the origin. + * + * Results: + * Returns a pointer to a Rect containing the micron + * coordinates, or NULL if an error occurred. + * + * Side Effects: + * Reads input from file f + * + *------------------------------------------------------------ + */ + +DSEG +LefReadEnclosure(FILE *f, int curlayer, float oscale) +{ + char *token; + float x, y, scale; + static struct dseg_ paintrect; + + token = LefNextToken(f, TRUE); + if (!token) goto enc_parse_error; + + if (sscanf(token, "%f", &x) != 1) { + if (!strcmp(token, "BELOW") || !strcmp(token, "ABOVE")) + /* NOTE: This creates two records but fails to differentiate */ + /* between the layers, both of which will be -1 if this is a */ + /* cut layer. Needs to be handled properly. */ + + token = LefNextToken(f, TRUE); + else + goto enc_parse_error; + } + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto enc_parse_error; + + if (curlayer < 0) { + /* Issue warning but keep geometry with negative layer number */ + LefError(LEF_ERROR, "No layer defined for RECT.\n"); + } + + /* Scale coordinates (microns to centimicrons) (doubled) */ + + scale = oscale / 2.0; + + paintrect.x1 = -x / scale; + paintrect.y1 = -y / scale; + paintrect.x2 = x / scale; + paintrect.y2 = y / scale; + paintrect.layer = curlayer; + return (&paintrect); + +enc_parse_error: + LefError(LEF_ERROR, "Bad enclosure geometry: ENCLOSURE requires 2 values.\n"); + return (DSEG)NULL; +} + +/* + *------------------------------------------------------------ + * Support routines for polygon reading + *------------------------------------------------------------ + */ + +#define HEDGE 0 /* Horizontal edge */ +#define REDGE 1 /* Rising edge */ +#define FEDGE -1 /* Falling edge */ + +/* + *------------------------------------------------------------ + * lefLowX --- + * + * Sort routine to find the lowest X coordinate between + * two DPOINT structures passed from qsort() + *------------------------------------------------------------ + */ + +int +lefLowX(DPOINT *a, DPOINT *b) +{ + DPOINT p = *a; + DPOINT q = *b; + + if (p->x < q->x) + return (-1); + if (p->x > q->x) + return (1); + return (0); +} + +/* + *------------------------------------------------------------ + * lefLowY --- + * + * Sort routine to find the lowest Y coordinate between + * two DPOINT structures passed from qsort() + *------------------------------------------------------------ + */ + +int +lefLowY(DPOINT *a, DPOINT *b) +{ + DPOINT p = *a; + DPOINT q = *b; + + if (p->y < q->y) + return (-1); + if (p->y > q->y) + return (1); + return (0); +} + +/* + *------------------------------------------------------------ + * lefOrient --- + * + * Assign a direction to each of the edges in a polygon. + * + * Note that edges have been sorted, but retain the original + * linked list pointers, from which we can determine the + * path orientation + * + *------------------------------------------------------------ + */ + +char +lefOrient(DPOINT *edges, int nedges, int *dir) +{ + int n; + DPOINT p, q; + + for (n = 0; n < nedges; n++) + { + p = edges[n]; + q = edges[n]->next; + + if (p->y == q->y) + { + dir[n] = HEDGE; + continue; + } + if (p->x == q->x) + { + if (p->y < q->y) + { + dir[n] = REDGE; + continue; + } + if (p->y > q->y) + { + dir[n] = FEDGE; + continue; + } + /* Point connects to itself */ + dir[n] = HEDGE; + continue; + } + /* It's not Manhattan, folks. */ + return (FALSE); + } + return (TRUE); +} + +/* + *------------------------------------------------------------ + * lefCross --- + * + * See if an edge crosses a particular area. + * Return TRUE if edge if vertical and if it crosses the + * y-range defined by ybot and ytop. Otherwise return + * FALSE. + *------------------------------------------------------------ + */ + +char +lefCross(DPOINT edge, int dir, double ybot, double ytop) +{ + double ebot, etop; + + switch (dir) + { + case REDGE: + ebot = edge->y; + etop = edge->next->y; + return (ebot <= ybot && etop >= ytop); + + case FEDGE: + ebot = edge->next->y; + etop = edge->y; + return (ebot <= ybot && etop >= ytop); + } + return (FALSE); +} + + +/* + *------------------------------------------------------------ + * LefPolygonToRects -- + * + * Convert Geometry information from a POLYGON statement + * into rectangles. NOTE: For now, this routine + * assumes that all points are Manhattan. It will flag + * non-Manhattan geometry + * + * the DSEG pointed to by rectListPtr is updated by + * having the list of rectangles appended to it. + * + *------------------------------------------------------------ + */ + +void +LefPolygonToRects(DSEG *rectListPtr, DPOINT pointlist) +{ + DPOINT ptail, p, *pts, *edges; + DSEG rtail, rex, new; + int npts = 0; + int *dir; + int curr, wrapno, n; + double xbot, xtop, ybot, ytop; + + if (pointlist == NULL) return; + + /* Close the path by duplicating 1st point if necessary */ + + for (ptail = pointlist; ptail->next; ptail = ptail->next); + + if ((ptail->x != pointlist->x) || (ptail->y != pointlist->y)) + { + p = (DPOINT)malloc(sizeof(struct dpoint_)); + p->x = pointlist->x; + p->y = pointlist->y; + p->layer = pointlist->layer; + p->next = NULL; + ptail->next = p; + } + + // To do: Break out non-manhattan parts here. + // See CIFMakeManhattanPath in magic-8.0 + + rex = NULL; + for (p = pointlist; p->next; p = p->next, npts++); + pts = (DPOINT *)malloc(npts * sizeof(DPOINT)); + edges = (DPOINT *)malloc(npts * sizeof(DPOINT)); + dir = (int *)malloc(npts * sizeof(int)); + npts = 0; + + for (p = pointlist; p->next; p = p->next, npts++) + { + // pts and edges are two lists of pointlist entries + // that are NOT linked lists and can be shuffled + // around by qsort(). The linked list "next" pointers + // *must* be retained. + + pts[npts] = p; + edges[npts] = p; + } + + if (npts < 4) + { + LefError(LEF_ERROR, "Polygon with fewer than 4 points.\n"); + goto done; + } + + /* Sort points by low y, edges by low x */ + qsort((char *)pts, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowY); + qsort((char *)edges, npts, (int)sizeof(DPOINT), (__compar_fn_t)lefLowX); + + /* Find out which direction each edge points */ + + if (!lefOrient(edges, npts, dir)) + { + LefError(LEF_ERROR, "I can't handle non-manhattan polygons!\n"); + goto done; + } + + /* Scan the polygon from bottom to top. At each step, process + * a minimum-sized y-range of the polygon (i.e., a range such that + * there are no vertices inside the range). Use wrap numbers + * based on the edge orientations to determine how much of the + * x-range for this y-range should contain material. + */ + + for (curr = 1; curr < npts; curr++) + { + /* Find the next minimum-sized y-range. */ + + ybot = pts[curr - 1]->y; + while (ybot == pts[curr]->y) + if (++curr >= npts) goto done; + ytop = pts[curr]->y; + + /* Process all the edges that cross the y-range, from left + * to right. + */ + + for (wrapno = 0, n = 0; n < npts; n++) + { + if (wrapno == 0) xbot = edges[n]->x; + if (!lefCross(edges[n], dir[n], ybot, ytop)) + continue; + wrapno += (dir[n] == REDGE) ? 1 : -1; + if (wrapno == 0) + { + xtop = edges[n]->x; + if (xbot == xtop) continue; + new = (DSEG)malloc(sizeof(struct dseg_)); + new->x1 = xbot; + new->x2 = xtop; + new->y1 = ybot; + new->y2 = ytop; + new->layer = edges[n]->layer; + new->next = rex; + rex = new; + } + } + } + +done: + free(edges); + free(dir); + free(pts); + + if (*rectListPtr == NULL) + *rectListPtr = rex; + else + { + for (rtail = *rectListPtr; rtail->next; rtail = rtail->next); + rtail->next = rex; + } +} + +/* + *------------------------------------------------------------ + * LefReadPolygon -- + * + * Read Geometry information from a POLYGON statement + * + *------------------------------------------------------------ + */ + +DPOINT +LefReadPolygon(FILE *f, int curlayer, float oscale) +{ + DPOINT plist = NULL, newPoint; + char *token; + double px, py; + + while (1) + { + token = LefNextToken(f, TRUE); + if (token == NULL || *token == ';') break; + if (sscanf(token, "%lg", &px) != 1) + { + LefError(LEF_ERROR, "Bad X value in polygon.\n"); + LefEndStatement(f); + break; + } + + token = LefNextToken(f, TRUE); + if (token == NULL || *token == ';') + { + LefError(LEF_ERROR, "Missing Y value in polygon point!\n"); + break; + } + if (sscanf(token, "%lg", &py) != 1) + { + LefError(LEF_ERROR, "Bad Y value in polygon.\n"); + LefEndStatement(f); + break; + } + + newPoint = (DPOINT)malloc(sizeof(struct dpoint_)); + newPoint->x = px / (double)oscale; + newPoint->y = py / (double)oscale; + newPoint->layer = curlayer; + newPoint->next = plist; + plist = newPoint; + } + + return plist; +} + +/* + *------------------------------------------------------------ + * LefReadGeometry -- + * + * Read Geometry information from a LEF file. + * Used for PORT records and OBS statements. + * + * Results: + * Returns a linked list of all areas and types + * painted. + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +enum lef_geometry_keys {LEF_LAYER = 0, LEF_WIDTH, LEF_PATH, + LEF_RECT, LEF_POLYGON, LEF_VIA, LEF_PORT_CLASS, + LEF_GEOMETRY_END}; + +DSEG +LefReadGeometry(GATE lefMacro, FILE *f, float oscale) +{ + int curlayer = -1, otherlayer = -1; + + char *token; + int keyword; + DSEG rectList = (DSEG)NULL; + DSEG paintrect, newRect; + DPOINT pointlist; + + static char *geometry_keys[] = { + "LAYER", + "WIDTH", + "PATH", + "RECT", + "POLYGON", + "VIA", + "CLASS", + "END", + NULL + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, geometry_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_LAYER: + curlayer = LefReadLayers(f, FALSE, &otherlayer); + LefEndStatement(f); + break; + case LEF_WIDTH: + LefEndStatement(f); + break; + case LEF_PATH: + LefEndStatement(f); + break; + case LEF_RECT: + paintrect = (curlayer < 0) ? NULL : LefReadRect(f, curlayer, oscale); + if (paintrect) + { + /* Remember the area and layer */ + newRect = (DSEG)malloc(sizeof(struct dseg_)); + *newRect = *paintrect; + newRect->next = rectList; + rectList = newRect; + } + LefEndStatement(f); + break; + case LEF_POLYGON: + pointlist = LefReadPolygon(f, curlayer, oscale); + LefPolygonToRects(&rectList, pointlist); + break; + case LEF_VIA: + LefEndStatement(f); + break; + case LEF_PORT_CLASS: + LefEndStatement(f); + break; + case LEF_GEOMETRY_END: + if (!LefParseEndStatement(f, NULL)) + { + LefError(LEF_ERROR, "Geometry (PORT or OBS) END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_GEOMETRY_END) break; + } + return rectList; +} + +/* + *------------------------------------------------------------ + * LefReadPort -- + * + * A wrapper for LefReadGeometry, which adds a label + * to the last rectangle generated by the geometry + * parsing. + * + * Results: + * None. + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +void +LefReadPort(lefMacro, f, pinName, pinNum, pinDir, pinUse, pinArea, oscale) + GATE lefMacro; + FILE *f; + char *pinName; + int pinNum, pinDir, pinUse; + double pinArea; + float oscale; +{ + DSEG rectList, rlist; + BUS bus; + + rectList = LefReadGeometry(lefMacro, f, oscale); + + if (pinNum >= 0) { + int nodealloc, orignodes, ival; + char *aptr; + + if (lefMacro->nodes <= pinNum) { + orignodes = lefMacro->nodes; + lefMacro->nodes = (pinNum + 1); + nodealloc = lefMacro->nodes / 10; + if (nodealloc > (orignodes / 10)) { + nodealloc++; + lefMacro->taps = (DSEG *)realloc(lefMacro->taps, + nodealloc * 10 * sizeof(DSEG)); + lefMacro->noderec = (NODE *)realloc(lefMacro->noderec, + nodealloc * 10 * sizeof(NODE)); + lefMacro->direction = (u_char *)realloc(lefMacro->direction, + nodealloc * 10 * sizeof(u_char)); + lefMacro->area = (float *)realloc(lefMacro->area, + nodealloc * 10 * sizeof(float)); + lefMacro->use = (u_char *)realloc(lefMacro->use, + nodealloc * 10 * sizeof(u_char)); + lefMacro->netnum = (int *)realloc(lefMacro->netnum, + nodealloc * 10 * sizeof(int)); + lefMacro->node = (char **)realloc(lefMacro->node, + nodealloc * 10 * sizeof(char *)); + } + } + lefMacro->taps[pinNum] = rectList; + lefMacro->noderec[pinNum] = NULL; + lefMacro->area[pinNum] = 0.0; + lefMacro->direction[pinNum] = (u_char)pinDir; + lefMacro->area[pinNum] = pinArea; + lefMacro->use[pinNum] = (u_char)pinUse; + lefMacro->netnum[pinNum] = -1; + if (pinName != NULL) { + lefMacro->node[pinNum] = strdup(pinName); + + /* Check for bus delimiters */ + aptr = strrchr(pinName, delimiter); + if (aptr != NULL) { + if (sscanf(aptr + 1, "%d", &ival) == 1) { + *aptr = '\0'; + + /* Add to bus list if needed, or adjust bounds */ + for (bus = lefMacro->bus; bus; bus = bus->next) { + if (!strcmp(pinName, bus->busname)) { + if (ival > bus->high) bus->high = ival; + if (ival < bus->low) bus->low = ival; + break; + } + } + if (bus == NULL) { + bus = (BUS)malloc(sizeof(struct bus_)); + bus->busname = strdup(pinName); + bus->high = bus->low = ival; + bus->next = lefMacro->bus; + lefMacro->bus = bus; + } + *aptr = '['; + } + } + } + else + lefMacro->node[pinNum] = NULL; + } + else { + while (rectList) { + rlist = rectList->next; + free(rectList); + rectList = rlist; + } + } +} + +/* + *------------------------------------------------------------ + * LefReadPin -- + * + * Read a PIN statement from a LEF file. + * + * Results: + * 0 if the pin had a port (success), 1 if not (indicating + * an unused pin that should be ignored). + * + * Side Effects: + * Reads input from file f; + * Paints into the GATE lefMacro. + * + *------------------------------------------------------------ + */ + +enum lef_pin_keys {LEF_DIRECTION = 0, LEF_USE, LEF_PORT, LEF_CAPACITANCE, + LEF_ANTENNADIFF, LEF_ANTENNAGATE, LEF_ANTENNAMOD, + LEF_ANTENNAPAR, LEF_ANTENNAPARSIDE, LEF_ANTENNAMAX, LEF_ANTENNAMAXSIDE, + LEF_SHAPE, LEF_NETEXPR, LEF_PIN_END}; + +int +LefReadPin(lefMacro, f, pinname, pinNum, oscale) + GATE lefMacro; + FILE *f; + char *pinname; + int pinNum; + float oscale; +{ + char *token; + int keyword, subkey; + int pinDir = PORT_CLASS_DEFAULT; + int pinUse = PORT_USE_DEFAULT; + float pinArea = 0.0; + int retval = 1; + + static char *pin_keys[] = { + "DIRECTION", + "USE", + "PORT", + "CAPACITANCE", + "ANTENNADIFFAREA", + "ANTENNAGATEAREA", + "ANTENNAMODEL", + "ANTENNAPARTIALMETALAREA", + "ANTENNAPARTIALMETALSIDEAREA", + "ANTENNAMAXAREACAR", + "ANTENNAMAXSIDEAREACAR", + "SHAPE", + "NETEXPR", + "END", + NULL + }; + + static char *pin_classes[] = { + "DEFAULT", + "INPUT", + "OUTPUT", + "OUTPUT TRISTATE", + "INOUT", + "FEEDTHRU", + NULL + }; + + static int lef_class_to_bitmask[] = { + PORT_CLASS_DEFAULT, + PORT_CLASS_INPUT, + PORT_CLASS_OUTPUT, + PORT_CLASS_TRISTATE, + PORT_CLASS_BIDIRECTIONAL, + PORT_CLASS_FEEDTHROUGH + }; + + static char *pin_uses[] = { + "DEFAULT", + "SIGNAL", + "ANALOG", + "POWER", + "GROUND", + "CLOCK", + "TIEOFF", + "ANALOG", + "SCAN", + "RESET", + NULL + }; + + static int lef_use_to_bitmask[] = { + PORT_USE_DEFAULT, + PORT_USE_SIGNAL, + PORT_USE_ANALOG, + PORT_USE_POWER, + PORT_USE_GROUND, + PORT_USE_CLOCK + }; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, pin_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_DIRECTION: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_classes); + if (subkey < 0) + LefError(LEF_ERROR, "Improper DIRECTION statement\n"); + else + pinDir = lef_class_to_bitmask[subkey]; + LefEndStatement(f); + break; + case LEF_USE: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, pin_uses); + if (subkey < 0) + LefError(LEF_ERROR, "Improper USE statement\n"); + else + pinUse = lef_use_to_bitmask[subkey]; + LefEndStatement(f); + break; + case LEF_PORT: + LefReadPort(lefMacro, f, pinname, pinNum, pinDir, pinUse, pinArea, oscale); + retval = 0; + break; + case LEF_ANTENNAGATE: + /* Read off the next value as the pin's antenna gate area. */ + /* The layers or layers are not recorded. */ + token = LefNextToken(f, TRUE); + sscanf(token, "%g", &pinArea); + LefEndStatement(f); + break; + case LEF_CAPACITANCE: + case LEF_ANTENNADIFF: + case LEF_ANTENNAMOD: + case LEF_ANTENNAPAR: + case LEF_ANTENNAPARSIDE: + case LEF_ANTENNAMAX: + case LEF_ANTENNAMAXSIDE: + case LEF_NETEXPR: + case LEF_SHAPE: + LefEndStatement(f); /* Ignore. . . */ + break; + case LEF_PIN_END: + if (!LefParseEndStatement(f, pinname)) + { + LefError(LEF_ERROR, "Pin END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_PIN_END) break; + } + return retval; +} + +/* + *------------------------------------------------------------ + * LefEndStatement -- + * + * Read file input to EOF or a ';' token (end-of-statement) + * If we encounter a quote, make sure we don't terminate + * the statement on a semicolon that is part of the + * quoted material. + * + *------------------------------------------------------------ + */ + +void +LefEndStatement(FILE *f) +{ + char *token; + + while ((token = LefNextToken(f, TRUE)) != NULL) + if (*token == ';') break; +} + +/* + *------------------------------------------------------------ + * + * LefReadMacro -- + * + * Read in a MACRO section from a LEF file. + * + * Results: + * None. + * + * Side Effects: + * Creates a new cell definition in the database. + * + *------------------------------------------------------------ + */ + +enum lef_macro_keys {LEF_CLASS = 0, LEF_SIZE, LEF_ORIGIN, + LEF_SYMMETRY, LEF_SOURCE, LEF_SITE, LEF_PIN, LEF_OBS, + LEF_TIMING, LEF_FOREIGN, LEF_MACRO_END}; + +void +LefReadMacro(f, mname, oscale) + FILE *f; /* LEF file being read */ + char *mname; /* name of the macro */ + float oscale; /* scale factor to um, usually 1 */ +{ + GATE lefMacro, altMacro; + char *token, *pname, tsave[128]; + int keyword, pinNum, subkey; + float x, y; + u_char has_size, is_imported = FALSE; + struct dseg_ lefBBox; + + static char *macro_keys[] = { + "CLASS", + "SIZE", + "ORIGIN", + "SYMMETRY", + "SOURCE", + "SITE", + "PIN", + "OBS", + "TIMING", + "FOREIGN", + "END", + NULL + }; + + static char *macro_classes[] = { + "DEFAULT", + "CORE", + "BLOCK", + "PAD", + "RING", + "COVER", + "ENDCAP", + NULL + }; + + static int lef_macro_class_to_bitmask[] = { + MACRO_CLASS_DEFAULT, + MACRO_CLASS_CORE, + MACRO_CLASS_BLOCK, + MACRO_CLASS_PAD, + MACRO_CLASS_RING, + MACRO_CLASS_COVER, + MACRO_CLASS_ENDCAP + }; + + static char *macro_subclasses[] = { + ";", + "SPACER", + "ANTENNACELL", + "WELLTAP", + "TIEHIGH", + "TIELOW", + "FEEDTHRU" + }; + + static int lef_macro_subclass_to_bitmask[] = { + MACRO_SUBCLASS_NONE, + MACRO_SUBCLASS_SPACER, + MACRO_SUBCLASS_ANTENNA, + MACRO_SUBCLASS_WELLTAP, + MACRO_SUBCLASS_TIEHIGH, + MACRO_SUBCLASS_TIELOW, + MACRO_SUBCLASS_FEEDTHRU + }; + + /* Start by creating a new celldef */ + + lefMacro = (GATE)NULL; + for (altMacro = GateInfo; altMacro; altMacro = altMacro->next) + { + if (!strcmp(altMacro->gatename, mname)) { + lefMacro = altMacro; + break; + } + } + + while (lefMacro) + { + int suffix; + char newname[256]; + + altMacro = lefMacro; + for (suffix = 1; altMacro != NULL; suffix++) + { + sprintf(newname, "%250s_%d", mname, suffix); + for (altMacro = GateInfo; altMacro; altMacro = altMacro->next) + if (!strcmp(altMacro->gatename, newname)) + break; + } + LefError(LEF_WARNING, "Cell \"%s\" was already defined in this file. " + "Renaming original cell \"%s\"\n", mname, newname); + + lefMacro->gatename = strdup(newname); + lefMacro = lefFindCell(mname); + } + + // Create the new cell + lefMacro = (GATE)malloc(sizeof(struct gate_)); + lefMacro->gatename = strdup(mname); + lefMacro->gatetype = NULL; + lefMacro->gateclass = MACRO_CLASS_DEFAULT; + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + lefMacro->width = 0.0; + lefMacro->height = 0.0; + lefMacro->placedX = 0.0; + lefMacro->placedY = 0.0; + lefMacro->obs = (DSEG)NULL; + lefMacro->next = GateInfo; + lefMacro->last = (GATE)NULL; + lefMacro->nodes = 0; + lefMacro->orient = 0; + // Allocate memory for up to 10 pins initially + lefMacro->taps = (DSEG *)malloc(10 * sizeof(DSEG)); + lefMacro->noderec = (NODE *)malloc(10 * sizeof(NODE)); + lefMacro->direction = (u_char *)malloc(10 * sizeof(u_char)); + lefMacro->area = (float *)malloc(10 * sizeof(float)); + lefMacro->use = (u_char *)malloc(10 * sizeof(u_char)); + lefMacro->netnum = (int *)malloc(10 * sizeof(int)); + lefMacro->node = (char **)malloc(10 * sizeof(char *)); + // Fill in 1st entry + lefMacro->taps[0] = NULL; + lefMacro->noderec[0] = NULL; + lefMacro->area[0] = 0.0; + lefMacro->node[0] = NULL; + lefMacro->bus = NULL; + lefMacro->netnum[0] = -1; + lefMacro->clientdata = (void *)NULL; + GateInfo = lefMacro; + + /* Set gate type to the site name for site definitions */ + pname = mname; + if (!strncmp(mname, "site_", 5)) pname += 5; + + /* Initial values */ + pinNum = 0; + has_size = FALSE; + lefBBox.x2 = lefBBox.x1 = 0.0; + lefBBox.y2 = lefBBox.y1 = 0.0; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, macro_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_CLASS: + token = LefNextToken(f, TRUE); + subkey = Lookup(token, macro_classes); + if (subkey < 0) { + LefError(LEF_ERROR, "Improper macro CLASS statement\n"); + lefMacro->gateclass = MACRO_CLASS_DEFAULT; + } + else + lefMacro->gateclass = lef_macro_class_to_bitmask[subkey]; + token = LefNextToken(f, TRUE); + if (token) { + subkey = Lookup(token, macro_subclasses); + if (subkey < 0) { + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + LefEndStatement(f); + } + else if (subkey > 0) { + lefMacro->gatesubclass = lef_macro_subclass_to_bitmask[subkey]; + LefEndStatement(f); + } + else + lefMacro->gatesubclass = MACRO_SUBCLASS_NONE; + } + else + LefEndStatement(f); + break; + case LEF_SIZE: + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &x) != 1) goto size_error; + token = LefNextToken(f, TRUE); /* skip keyword "BY" */ + if (!token) goto size_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto size_error; + + lefBBox.x2 = x + lefBBox.x1; + lefBBox.y2 = y + lefBBox.y1; + has_size = TRUE; + LefEndStatement(f); + break; +size_error: + LefError(LEF_ERROR, "Bad macro SIZE; requires values X BY Y.\n"); + LefEndStatement(f); + break; + case LEF_ORIGIN: + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &x) != 1) goto origin_error; + token = LefNextToken(f, TRUE); + if (!token || sscanf(token, "%f", &y) != 1) goto origin_error; + + lefBBox.x1 = -x; + lefBBox.y1 = -y; + if (has_size) + { + lefBBox.x2 += lefBBox.x1; + lefBBox.y2 += lefBBox.y1; + } + LefEndStatement(f); + break; +origin_error: + LefError(LEF_ERROR, "Bad macro ORIGIN; requires 2 values.\n"); + LefEndStatement(f); + break; + case LEF_SYMMETRY: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsymmetry", token + strlen(token) + 1); + ; + LefEndStatement(f); + break; + case LEF_SOURCE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsource", token); + ; + LefEndStatement(f); + break; + case LEF_SITE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + // DBPropPut(lefMacro, "LEFsite", token); + ; + LefEndStatement(f); + break; + case LEF_PIN: + token = LefNextToken(f, TRUE); + /* Diagnostic */ + /* + fprintf(stdout, " Macro defines pin %s\n", token); + */ + sprintf(tsave, "%.127s", token); + if (is_imported) + LefSkipSection(f, tsave); + else + if (LefReadPin(lefMacro, f, tsave, pinNum, oscale) == 0) + pinNum++; + break; + case LEF_OBS: + /* Diagnostic */ + /* + fprintf(stdout, " Macro defines obstruction\n"); + */ + if (is_imported) + LefSkipSection(f, NULL); + else + lefMacro->obs = LefReadGeometry(lefMacro, f, oscale); + break; + case LEF_TIMING: + LefSkipSection(f, macro_keys[LEF_TIMING]); + break; + case LEF_FOREIGN: + LefEndStatement(f); + break; + case LEF_MACRO_END: + pname = mname; + if (!strncmp(mname, "site_", 5)) pname += 5; + if (!LefParseEndStatement(f, pname)) + { + LefError(LEF_ERROR, "Macro END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_MACRO_END) break; + } + + /* Finish up creating the cell */ + + if (lefMacro) { + if (has_size) { + lefMacro->width = (lefBBox.x2 - lefBBox.x1); + lefMacro->height = (lefBBox.y2 - lefBBox.y1); + + /* "placed" for macros (not instances) corresponds to the */ + /* cell origin. */ + + lefMacro->placedX = lefBBox.x1; + lefMacro->placedY = lefBBox.y1; + } + else { + LefError(LEF_ERROR, "Gate %s has no size information!\n", lefMacro->gatename); + } + } +} + +/* + *------------------------------------------------------------ + * + * LefAddViaGeometry -- + * + * Read in geometry for a VIA section from a LEF or DEF + * file. + * + * f LEF file being read + * lefl pointer to via info + * curlayer current tile type + * oscale output scaling + * + * Results: + * None. + * + * Side Effects: + * Adds to the lefLayer record for a via definition. + * + *------------------------------------------------------------ + */ + +void +LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale) +{ + DSEG currect; + DSEG viarect; + + /* Rectangles for vias are read in units of 1/2 lambda */ + currect = LefReadRect(f, curlayer, (oscale / 2)); + if (currect == NULL) return; + + /* First rect goes into info.via.area, others go into info.via.lr */ + if (lefl->info.via.area.layer < 0) + { + lefl->info.via.area = *currect; + + /* If entries exist for info.via.lr, this is a via GENERATE */ + /* statement, and metal enclosures have been parsed. Therefore */ + /* add the via dimensions to the enclosure rectangles. */ + + viarect = lefl->info.via.lr; + while (viarect != NULL) { + viarect->x1 += currect->x1; + viarect->x2 += currect->x2; + viarect->y1 += currect->y1; + viarect->y2 += currect->y2; + viarect = viarect->next; + } + } + else + { + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *currect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + } +} + +/* + *------------------------------------------------------------ + * + * LefNewRoute --- + * + * Allocate space for and fill out default records of a + * route layer. + * + *------------------------------------------------------------ + */ + +LefList LefNewRoute(char *name) +{ + LefList lefl; + + lefl = (LefList)malloc(sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_IGNORE; /* For starters */ + lefl->lefName = strdup(name); + + return lefl; +} + +/* + *------------------------------------------------------------ + * + * LefNewVia --- + * + * Allocate space for and fill out default records of a + * via definition. + * + *------------------------------------------------------------ + */ + +LefList LefNewVia(char *name) +{ + LefList lefl; + + lefl = (LefList)calloc(1, sizeof(lefLayer)); + lefl->type = -1; + lefl->obsType = -1; + lefl->lefClass = CLASS_VIA; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + lefl->info.via.generated = FALSE; + lefl->info.via.respervia = 0.0; + lefl->info.via.spacing = (lefSpacingRule *)NULL; + if (name != NULL) + lefl->lefName = strdup(name); + else + lefl->lefName = NULL; + + return lefl; +} + +/* Note: Used directly below, as it is passed to variable "mode" + * in LefReadLayerSection(). However, mainly used in LefRead(). + */ + +enum lef_sections {LEF_VERSION = 0, + LEF_BUSBITCHARS, LEF_DIVIDERCHAR, LEF_MANUFACTURINGGRID, + LEF_USEMINSPACING, LEF_CLEARANCEMEASURE, + LEF_NAMESCASESENSITIVE, LEF_PROPERTYDEFS, LEF_UNITS, + LEF_SECTION_LAYER, LEF_SECTION_VIA, LEF_SECTION_VIARULE, + LEF_SECTION_SPACING, LEF_SECTION_SITE, LEF_PROPERTY, + LEF_NOISETABLE, LEF_CORRECTIONTABLE, LEF_IRDROP, + LEF_ARRAY, LEF_SECTION_TIMING, LEF_EXTENSION, LEF_MACRO, + LEF_END}; +/* + *------------------------------------------------------------ + * + * LefReadLayerSection -- + * + * Read in a LAYER, VIA, or VIARULE section from a LEF file. + * + * Results: + * None. + * + * Side Effects: + * + *------------------------------------------------------------ + */ + +enum lef_layer_keys {LEF_LAYER_TYPE=0, LEF_LAYER_WIDTH, + LEF_LAYER_MINWIDTH, LEF_LAYER_MAXWIDTH, LEF_LAYER_AREA, + LEF_LAYER_SPACING, LEF_LAYER_SPACINGTABLE, + LEF_LAYER_PITCH, LEF_LAYER_DIRECTION, LEF_LAYER_OFFSET, + LEF_LAYER_WIREEXT, + LEF_LAYER_RES, LEF_LAYER_CAP, LEF_LAYER_EDGECAP, + LEF_LAYER_THICKNESS, LEF_LAYER_HEIGHT, + LEF_LAYER_MINDENSITY, LEF_LAYER_ANTENNA, + LEF_LAYER_ANTENNADIFF, LEF_LAYER_ANTENNASIDE, + LEF_LAYER_AGG_ANTENNA, LEF_LAYER_AGG_ANTENNADIFF, + LEF_LAYER_AGG_ANTENNASIDE, + LEF_LAYER_ACCURRENT, LEF_LAYER_DCCURRENT, + LEF_VIA_DEFAULT, LEF_VIA_LAYER, LEF_VIA_RECT, + LEF_VIA_ENCLOSURE, LEF_VIA_PREFERENCLOSURE, + LEF_VIARULE_OVERHANG, + LEF_VIARULE_METALOVERHANG, LEF_VIARULE_VIA, + LEF_VIARULE_GENERATE, LEF_LAYER_END}; + +enum lef_spacing_keys {LEF_SPACING_RANGE=0, LEF_SPACING_BY, + LEF_SPACING_ADJACENTCUTS, LEF_END_LAYER_SPACING}; + +void +LefReadLayerSection(f, lname, mode, lefl) + FILE *f; /* LEF file being read */ + char *lname; /* name of the layer */ + int mode; /* layer, via, or viarule */ + LefList lefl; /* pointer to layer info */ +{ + char *token, *tp; + int keyword, typekey = -1, entries, i; + int curlayer = -1; + double dvalue, oscale; + LefList altVia; + lefSpacingRule *newrule = NULL, *testrule; + + /* These are defined in the order of CLASS_* in lefInt.h */ + static char *layer_type_keys[] = { + "ROUTING", + "CUT", + "MASTERSLICE", + "OVERLAP", + NULL + }; + + static char *layer_keys[] = { + "TYPE", + "WIDTH", + "MINWIDTH", + "MAXWIDTH", + "AREA", + "SPACING", + "SPACINGTABLE", + "PITCH", + "DIRECTION", + "OFFSET", + "WIREEXTENSION", + "RESISTANCE", + "CAPACITANCE", + "EDGECAPACITANCE", + "THICKNESS", + "HEIGHT", + "MINIMUMDENSITY", + "ANTENNAAREARATIO", + "ANTENNADIFFAREARATIO", + "ANTENNASIDEAREARATIO", + "ANTENNACUMAREARATIO", + "ANTENNACUMDIFFAREARATIO", + "ANTENNACUMSIDEAREARATIO", + "ACCURRENTDENSITY", + "DCCURRENTDENSITY", + "DEFAULT", + "LAYER", + "RECT", + "ENCLOSURE", + "PREFERENCLOSURE", + "OVERHANG", + "METALOVERHANG", + "VIA", + "GENERATE", + "END", + NULL + }; + + static char *spacing_keys[] = { + "RANGE", + "BY", + "ADJACENTCUTS", + ";", + NULL + }; + + /* Database is assumed to be in microns. */ + /* If not, we need to parse the UNITS record, which is */ + /* currently ignored. */ + + oscale = 1; + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, layer_keys); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_LAYER_TYPE: + token = LefNextToken(f, TRUE); + if (*token != '\n') + { + typekey = Lookup(token, layer_type_keys); + if (typekey < 0) + LefError(LEF_WARNING, "Unknown layer type \"%s\" in LEF file; " + "ignoring.\n", token); + } + if (lefl->lefClass == CLASS_IGNORE) { + lefl->lefClass = typekey; + if (typekey == CLASS_ROUTE) { + + lefl->info.route.width = 0.0; + lefl->info.route.spacing = NULL; + lefl->info.route.pitchx = 0.0; + lefl->info.route.pitchy = 0.0; + // Use -1.0 as an indication that offset has not + // been specified and needs to be set to default. + lefl->info.route.offsetx = -1.0; + lefl->info.route.offsety = -1.0; + lefl->info.route.hdirection = DIR_UNKNOWN; + + lefl->info.route.minarea = 0.0; + lefl->info.route.thick = 0.0; + lefl->info.route.antenna = 0.0; + lefl->info.route.method = CALC_NONE; + + lefl->info.route.areacap = 0.0; + lefl->info.route.respersq = 0.0; + lefl->info.route.edgecap = 0.0; + + /* A routing type has been declared. Assume */ + /* this takes the name "metal1", "M1", or some */ + /* variant thereof. */ + + for (tp = lefl->lefName; *tp != '\0'; tp++) { + if (*tp >= '0' && *tp <= '9') { + sscanf(tp, "%d", &lefl->type); + + /* "metal1", e.g., is assumed to be layer #0 */ + /* This may not be a proper assumption, always */ + + lefl->type--; + break; + } + } + + /* This is probably some special non-numerical */ + /* name for a top metal layer. Take a stab at */ + /* it, defining it to be the next layer up from */ + /* whatever the previous topmost route layer */ + /* was. This should work unless the LEF file */ + /* is incorrectly written. */ + + if (lefl->type < 0) { + lefl->type = LefGetMaxRouteLayer(); + } + } + else if (typekey == CLASS_CUT || typekey == CLASS_VIA) { + lefl->info.via.spacing = NULL; + lefl->info.via.area.x1 = 0.0; + lefl->info.via.area.y1 = 0.0; + lefl->info.via.area.x2 = 0.0; + lefl->info.via.area.y2 = 0.0; + lefl->info.via.area.layer = -1; + lefl->info.via.cell = (GATE)NULL; + lefl->info.via.lr = (DSEG)NULL; + + /* Note: lefl->type not set here for cut */ + /* layers so that route layers will all be */ + /* clustered at the bottom. */ + } + } + else if (lefl->lefClass != typekey) { + LefError(LEF_ERROR, "Attempt to reclassify layer %s from %s to %s\n", + lname, layer_type_keys[lefl->lefClass], + layer_type_keys[typekey]); + } + LefEndStatement(f); + break; + case LEF_LAYER_ACCURRENT: + // Not used, but syntax is f**king stupid and has to be + // specially handled, or it breaks the parser. + token = LefNextToken(f, TRUE); /* "AVERAGE", ... */ + token = LefNextToken(f, TRUE); /* Value or "FREQUENCY" */ + LefEndStatement(f); + if (!strcmp(token, "FREQUENCY")) + { + while (TRUE) { + token = LefNextToken(f, TRUE); + LefEndStatement(f); + if (!strcmp(token, "TABLEENTRIES")) { + break; + } + } + } + break; + case LEF_LAYER_DCCURRENT: + // Not used. See comments above for ACCURRENTDENSITY + token = LefNextToken(f, TRUE); /* "AVERAGE" */ + token = LefNextToken(f, TRUE); /* Value or "WIDTH" */ + LefEndStatement(f); + if (!strcmp(token, "WIDTH")) + { + while (TRUE) { + token = LefNextToken(f, TRUE); + LefEndStatement(f); + if (!strcmp(token, "TABLEENTRIES")) { + break; + } + } + } + break; + case LEF_LAYER_MINWIDTH: + // Not handled if width is already defined. + if ((lefl->lefClass != CLASS_ROUTE) || + (lefl->info.route.width != 0)) { + LefEndStatement(f); + break; + } + /* drop through */ + case LEF_LAYER_WIDTH: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + if (lefl->lefClass == CLASS_ROUTE) + lefl->info.route.width = dvalue / (double)oscale; + else if (lefl->lefClass == CLASS_CUT) { + double baseval = (dvalue / (double)oscale) / 2.0; + lefl->info.via.area.x1 = -baseval; + lefl->info.via.area.y1 = -baseval; + lefl->info.via.area.x2 = baseval; + lefl->info.via.area.y2 = baseval; + } + LefEndStatement(f); + break; + case LEF_LAYER_MAXWIDTH: + // Not handled. + LefEndStatement(f); + break; + case LEF_LAYER_AREA: + /* Read minimum area rule value */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of area (length * length) + lefl->info.route.minarea = dvalue / (double)oscale / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_SPACING: + if ((lefl->lefClass != CLASS_ROUTE) && (lefl->lefClass != CLASS_CUT) && + (lefl->lefClass != CLASS_VIA)) { + LefEndStatement(f); + break; + } + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + token = LefNextToken(f, TRUE); + typekey = Lookup(token, spacing_keys); + + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + + // If no range specified, then the rule goes in front + if (typekey == LEF_SPACING_RANGE) { + // Get range minimum, ignore range maximum, and sort + // the spacing order. + newrule->spacing = dvalue / (double)oscale; + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + newrule->width = dvalue / (double)oscale; + for (testrule = lefl->info.route.spacing; testrule; + testrule = testrule->next) + if (testrule->next == NULL || testrule->next->width > + newrule->width) + break; + + if (!testrule) { + newrule->next = NULL; + lefl->info.route.spacing = newrule; + } + else { + newrule->next = testrule->next; + testrule->next = newrule; + } + token = LefNextToken(f, TRUE); + typekey = Lookup(token, spacing_keys); + } + else if (typekey == LEF_SPACING_BY) { + /* In info.via.spacing, save two rules; first one */ + /* is for X spacing, second is for Y spacing */ + + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0; + newrule->next = NULL; + lefl->info.via.spacing = newrule; + + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0; + newrule->next = NULL; + lefl->info.via.spacing->next = newrule; + } + else if (typekey == LEF_SPACING_ADJACENTCUTS) { + /* ADJACENTCUTS not handled here (yet) */ + /* Need this for power post generation in addspacers */ + /* (Do nothing here) */ + } + else if (typekey >= 0) { + newrule->spacing = dvalue / (double)oscale; + newrule->width = 0.0; + newrule->next = lefl->info.route.spacing; + lefl->info.route.spacing = newrule; + } + if (typekey != LEF_END_LAYER_SPACING) + LefEndStatement(f); + break; + + case LEF_LAYER_SPACINGTABLE: + // Use the values for the maximum parallel runlength + token = LefNextToken(f, TRUE); // "PARALLELRUNLENTTH" + entries = 0; + while (1) { + token = LefNextToken(f, TRUE); + if (*token == ';' || !strcmp(token, "WIDTH")) + break; + else + entries++; + } + if (*token != ';') + newrule = (lefSpacingRule *)malloc(sizeof(lefSpacingRule)); + + while (*token != ';') { + token = LefNextToken(f, TRUE); // Minimum width value + sscanf(token, "%lg", &dvalue); + newrule->width = dvalue / (double)oscale; + + for (i = 0; i < entries; i++) { + token = LefNextToken(f, TRUE); // Spacing value + } + sscanf(token, "%lg", &dvalue); + newrule->spacing = dvalue / (double)oscale; + token = LefNextToken(f, TRUE); + + for (testrule = lefl->info.route.spacing; testrule; + testrule = testrule->next) + if (testrule->next == NULL || testrule->next->width > + newrule->width) + break; + + if (!testrule) { + newrule->next = NULL; + lefl->info.route.spacing = newrule; + } + else { + newrule->next = testrule->next; + testrule->next = newrule; + } + token = LefNextToken(f, TRUE); + if (strcmp(token, "WIDTH")) break; + } + break; + case LEF_LAYER_PITCH: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.pitchx = dvalue / (double)oscale; + + token = LefNextToken(f, TRUE); + if (token && (*token != ';')) { + sscanf(token, "%lg", &dvalue); + lefl->info.route.pitchy = dvalue / (double)oscale; + LefEndStatement(f); + } + else { + lefl->info.route.pitchy = lefl->info.route.pitchx; + /* If the orientation is known, then zero the pitch */ + /* in the opposing direction. If not, then set the */ + /* direction to DIR_RESOLVE so that the pitch in */ + /* the opposing direction can be zeroed when the */ + /* direction is specified. */ + + if (lefl->info.route.hdirection == DIR_UNKNOWN) + lefl->info.route.hdirection = DIR_RESOLVE; + else if (lefl->info.route.hdirection == DIR_VERTICAL) + lefl->info.route.pitchy = 0.0; + else if (lefl->info.route.hdirection == DIR_HORIZONTAL) + lefl->info.route.pitchx = 0.0; + } + + /* Offset default is 1/2 the pitch. Offset is */ + /* intialized to -1 to tell whether or not the value */ + /* has been set by an OFFSET statement. */ + if (lefl->info.route.offsetx < 0.0) + lefl->info.route.offsetx = lefl->info.route.pitchx / 2.0; + if (lefl->info.route.offsety < 0.0) + lefl->info.route.offsety = lefl->info.route.pitchy / 2.0; + break; + case LEF_LAYER_DIRECTION: + token = LefNextToken(f, TRUE); + LefLower(token); + if (lefl->info.route.hdirection == DIR_RESOLVE) { + if (token[0] == 'h') + lefl->info.route.pitchx = 0.0; + else if (token[0] == 'v') + lefl->info.route.pitchy = 0.0; + } + lefl->info.route.hdirection = (token[0] == 'h') ? DIR_HORIZONTAL + : DIR_VERTICAL; + LefEndStatement(f); + break; + case LEF_LAYER_OFFSET: + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + lefl->info.route.offsetx = dvalue / (double)oscale; + + token = LefNextToken(f, TRUE); + if (token && (*token != ';')) { + sscanf(token, "%lg", &dvalue); + lefl->info.route.offsety = dvalue / (double)oscale; + LefEndStatement(f); + } + else { + lefl->info.route.offsety = lefl->info.route.offsetx; + } + break; + case LEF_LAYER_RES: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + if (!strcmp(token, "RPERSQ")) { + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + // Units are ohms per square + lefl->info.route.respersq = dvalue; + } + } + else if (lefl->lefClass == CLASS_VIA || lefl->lefClass == CLASS_CUT) { + sscanf(token, "%lg", &dvalue); + lefl->info.via.respervia = dvalue; // Units ohms + } + LefEndStatement(f); + break; + case LEF_LAYER_CAP: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + if (!strcmp(token, "CPERSQDIST")) { + token = LefNextToken(f, TRUE); + sscanf(token, "%lg", &dvalue); + // Units are pF per squared unit length + lefl->info.route.areacap = dvalue / + ((double)oscale * (double)oscale); + } + } + LefEndStatement(f); + break; + case LEF_LAYER_EDGECAP: + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units are pF per unit length + lefl->info.route.edgecap = dvalue / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_THICKNESS: + case LEF_LAYER_HEIGHT: + /* Assuming thickness and height are the same thing? */ + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Units of length + lefl->info.route.thick = dvalue / (double)oscale; + } + LefEndStatement(f); + break; + case LEF_LAYER_ANTENNA: + case LEF_LAYER_ANTENNASIDE: + case LEF_LAYER_AGG_ANTENNA: + case LEF_LAYER_AGG_ANTENNASIDE: + /* NOTE: Assuming that only one of these methods will */ + /* be used! If more than one is present, then only the */ + /* last one will be recorded and used. */ + + token = LefNextToken(f, TRUE); + if (lefl->lefClass == CLASS_ROUTE) { + sscanf(token, "%lg", &dvalue); + // Unitless values (ratio) + lefl->info.route.antenna = dvalue; + } + if (keyword == LEF_LAYER_ANTENNA) + lefl->info.route.method = CALC_AREA; + else if (keyword == LEF_LAYER_ANTENNASIDE) + lefl->info.route.method = CALC_SIDEAREA; + else if (keyword == LEF_LAYER_AGG_ANTENNA) + lefl->info.route.method = CALC_AGG_AREA; + else + lefl->info.route.method = CALC_AGG_SIDEAREA; + LefEndStatement(f); + break; + case LEF_LAYER_ANTENNADIFF: + case LEF_LAYER_AGG_ANTENNADIFF: + /* Not specifically handling these antenna types */ + /* (antenna ratios for antennas connected to diodes, */ + /* which can still blow gates if the diode area is */ + /* insufficiently large.) */ + LefEndStatement(f); + break; + case LEF_LAYER_MINDENSITY: + case LEF_LAYER_WIREEXT: + /* Not specifically handling these */ + LefEndStatement(f); + break; + case LEF_VIA_DEFAULT: + case LEF_VIARULE_GENERATE: + /* Do nothing; especially, don't look for end-of-statement! */ + break; + case LEF_VIA_LAYER: + curlayer = LefReadLayer(f, FALSE); + LefEndStatement(f); + break; + case LEF_VIA_RECT: + if (curlayer >= 0) + LefAddViaGeometry(f, lefl, curlayer, oscale); + LefEndStatement(f); + break; + case LEF_VIA_ENCLOSURE: + /* Defines how to draw via metal layers. */ + /* Note that values can interact with ENCLOSURE */ + /* values given for the cut layer type. This is */ + /* not being handled. */ + { + DSEG viarect, encrect; + encrect = LefReadEnclosure(f, curlayer, oscale); + viarect = (DSEG)malloc(sizeof(struct dseg_)); + *viarect = *encrect; + viarect->next = lefl->info.via.lr; + lefl->info.via.lr = viarect; + } + if (mode == LEF_SECTION_VIARULE) + lefl->info.via.generated = TRUE; + LefEndStatement(f); + break; + case LEF_VIARULE_OVERHANG: + case LEF_VIARULE_METALOVERHANG: + /* These are from older LEF definitions (e.g., 5.4) */ + /* and cannot completely specify via geometry. So if */ + /* seen, ignore the rest of the via section. Only the */ + /* explicitly defined VIA types will be used. */ + LefError(LEF_WARNING, "NOTE: Old format VIARULE ignored.\n"); + lefl->lefClass == CLASS_IGNORE; + LefEndStatement(f); + /* LefSkipSection(f, lname); */ /* Continue parsing */ + break; + case LEF_VIA_PREFERENCLOSURE: + /* Ignoring this. */ + LefEndStatement(f); + break; + case LEF_VIARULE_VIA: + LefEndStatement(f); + break; + case LEF_LAYER_END: + if (!LefParseEndStatement(f, lname)) + { + LefError(LEF_ERROR, "Layer END statement missing.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_LAYER_END) break; + } +} + +/* + *------------------------------------------------------------ + * + * LefRead -- + * + * Read a .lef file and generate all routing configuration + * structures and values from the LAYER, VIA, and MACRO sections + * + * Results: + * None. + * + * Side Effects: + * Many. Cell definitions are created and added to + * the GateInfo database. + * + *------------------------------------------------------------ + */ + +/* See above for "enum lef_sections {...}" */ + +int +LefRead(inName) + char *inName; +{ + FILE *f; + char filename[256]; + char *token; + char tsave[128]; + int keyword, layer; + int oprecis = 100; // = 1 / manufacturing grid (microns) + float oscale; + double xydiff, ogrid, minwidth; + LefList lefl; + DSEG grect; + GATE gateginfo; + + static char *sections[] = { + "VERSION", + "BUSBITCHARS", + "DIVIDERCHAR", + "MANUFACTURINGGRID", + "USEMINSPACING", + "CLEARANCEMEASURE", + "NAMESCASESENSITIVE", + "PROPERTYDEFINITIONS", + "UNITS", + "LAYER", + "VIA", + "VIARULE", + "SPACING", + "SITE", + "PROPERTY", + "NOISETABLE", + "CORRECTIONTABLE", + "IRDROP", + "ARRAY", + "TIMING", + "BEGINEXT", + "MACRO", + "END", + NULL + }; + + if (!strrchr(inName, '.')) + sprintf(filename, "%s.lef", inName); + else + strcpy(filename, inName); + + f = fopen(filename, "r"); + + if (f == NULL) + { + fprintf(stderr, "Cannot open input file: "); + perror(filename); + return 0; + } + + if (Verbose > 0) { + fprintf(stdout, "Reading LEF data from file %s.\n", filename); + fflush(stdout); + } + + oscale = 1; + + LefHashInit(); + + while ((token = LefNextToken(f, TRUE)) != NULL) + { + keyword = Lookup(token, sections); + if (keyword < 0) + { + LefError(LEF_WARNING, "Unknown keyword \"%s\" in LEF file; ignoring.\n", + token); + LefEndStatement(f); + continue; + } + switch (keyword) + { + case LEF_VERSION: + case LEF_DIVIDERCHAR: + case LEF_CLEARANCEMEASURE: + case LEF_USEMINSPACING: + case LEF_NAMESCASESENSITIVE: + LefEndStatement(f); + break; + case LEF_BUSBITCHARS: + token = LefNextToken(f, TRUE); + delimiter = (*token == '\"') ? *(token + 1) : *token; + LefEndStatement(f); + break; + case LEF_MANUFACTURINGGRID: + token = LefNextToken(f, TRUE); + if (sscanf(token, "%lg", &ogrid) == 1) + oprecis = (int)((1.0 / ogrid) + 0.5); + LefEndStatement(f); + break; + case LEF_PROPERTYDEFS: + LefSkipSection(f, sections[LEF_PROPERTYDEFS]); + break; + case LEF_UNITS: + LefSkipSection(f, sections[LEF_UNITS]); + break; + + case LEF_SECTION_VIA: + case LEF_SECTION_VIARULE: + token = LefNextToken(f, TRUE); + sprintf(tsave, "%.127s", token); + + lefl = LefFindLayer(token); + + if (keyword == LEF_SECTION_VIARULE) { + char *vianame = (char *)malloc(strlen(token) + 3); + sprintf(vianame, "%s_0", token); + + /* If the following keyword is GENERATE, then */ + /* prepare up to four contact types to represent */ + /* all possible orientations of top and bottom */ + /* metal layers. If no GENERATE keyword, ignore. */ + + token = LefNextToken(f, TRUE); + if (!strcmp(token, "GENERATE")) { + lefl = LefNewVia(vianame); + lefl->next = LefInfo; + LefInfo = lefl; + LefReadLayerSection(f, tsave, keyword, lefl); + } + else { + LefSkipSection(f, tsave); + } + free(vianame); + } + else if (lefl == NULL) + { + lefl = LefNewVia(token); + lefl->next = LefInfo; + LefInfo = lefl; + LefReadLayerSection(f, tsave, keyword, lefl); + } + else + { + LefError(LEF_WARNING, "Warning: Cut type \"%s\" redefined.\n", + token); + lefl = LefRedefined(lefl, token); + LefReadLayerSection(f, tsave, keyword, lefl); + } + break; + + case LEF_SECTION_LAYER: + token = LefNextToken(f, TRUE); + sprintf(tsave, "%.127s", token); + + lefl = LefFindLayer(token); + if (lefl == (LefList)NULL) + { + lefl = LefNewRoute(token); + lefl->next = LefInfo; + LefInfo = lefl; + } + else + { + if (lefl && lefl->type < 0) + { + LefError(LEF_ERROR, "Layer %s is only defined for" + " obstructions!\n", token); + LefSkipSection(f, tsave); + break; + } + } + LefReadLayerSection(f, tsave, keyword, lefl); + break; + + case LEF_SECTION_SPACING: + LefSkipSection(f, sections[LEF_SECTION_SPACING]); + break; + case LEF_SECTION_SITE: + token = LefNextToken(f, TRUE); + if (Verbose > 0) + fprintf(stdout, "LEF file: Defines site %s\n", token); + sprintf(tsave, "site_%.122s", token); + LefReadMacro(f, tsave, oscale); + break; + case LEF_PROPERTY: + LefSkipSection(f, NULL); + break; + case LEF_NOISETABLE: + LefSkipSection(f, sections[LEF_NOISETABLE]); + break; + case LEF_CORRECTIONTABLE: + LefSkipSection(f, sections[LEF_CORRECTIONTABLE]); + break; + case LEF_IRDROP: + LefSkipSection(f, sections[LEF_IRDROP]); + break; + case LEF_ARRAY: + LefSkipSection(f, sections[LEF_ARRAY]); + break; + case LEF_SECTION_TIMING: + LefSkipSection(f, sections[LEF_SECTION_TIMING]); + break; + case LEF_EXTENSION: + LefSkipSection(f, sections[LEF_EXTENSION]); + break; + case LEF_MACRO: + token = LefNextToken(f, TRUE); + /* Diagnostic */ + /* + fprintf(stdout, "LEF file: Defines new cell %s\n", token); + */ + sprintf(tsave, "%.127s", token); + LefReadMacro(f, tsave, oscale); + break; + case LEF_END: + if (!LefParseEndStatement(f, "LIBRARY")) + { + LefError(LEF_ERROR, "END statement out of context.\n"); + keyword = -1; + } + break; + } + if (keyword == LEF_END) break; + } + if (Verbose > 0) { + fprintf(stdout, "LEF read: Processed %d lines.\n", lefCurrentLine); + LefError(LEF_ERROR, NULL); /* print statement of errors, if any */ + } + + /* Cleanup */ + if (f != NULL) fclose(f); + + /* Make sure that the gate list has one entry called "pin" */ + + for (gateginfo = GateInfo; gateginfo; gateginfo = gateginfo->next) + if (!strcasecmp(gateginfo->gatename, "pin")) + break; + + if (!gateginfo) { + /* Add a new GateInfo entry for pseudo-gate "pin" */ + gateginfo = (GATE)malloc(sizeof(struct gate_)); + gateginfo->gatetype = NULL; + gateginfo->gateclass = MACRO_CLASS_DEFAULT; + gateginfo->gatesubclass = MACRO_SUBCLASS_NONE; + gateginfo->gatename = (char *)malloc(4); + strcpy(gateginfo->gatename, "pin"); + gateginfo->width = 0.0; + gateginfo->height = 0.0; + gateginfo->placedX = 0.0; + gateginfo->placedY = 0.0; + gateginfo->nodes = 1; + + gateginfo->taps = (DSEG *)malloc(sizeof(DSEG)); + gateginfo->noderec = (NODE *)malloc(sizeof(NODE)); + gateginfo->area = (float *)malloc(sizeof(float)); + gateginfo->direction = (u_char *)malloc(sizeof(u_char)); + gateginfo->use = (u_char *)malloc(sizeof(u_char)); + gateginfo->netnum = (int *)malloc(sizeof(int)); + gateginfo->node = (char **)malloc(sizeof(char *)); + + grect = (DSEG)malloc(sizeof(struct dseg_)); + grect->x1 = grect->x2 = 0.0; + grect->y1 = grect->y2 = 0.0; + grect->next = (DSEG)NULL; + gateginfo->obs = (DSEG)NULL; + gateginfo->next = GateInfo; + gateginfo->last = (GATE)NULL; + gateginfo->taps[0] = grect; + gateginfo->noderec[0] = NULL; + gateginfo->area[0] = 0.0; + gateginfo->netnum[0] = -1; + gateginfo->node[0] = strdup("pin"); + gateginfo->clientdata = (void *)NULL; + GateInfo = gateginfo; + + LefHashMacro(gateginfo); + } + return oprecis; +} diff --git a/src/readlef.h b/src/readlef.h new file mode 100644 index 0000000..39edd6b --- /dev/null +++ b/src/readlef.h @@ -0,0 +1,220 @@ +/* + * readlef.h -- + * + * This file defines things that are used by internal LEF routines in + * various files. + * + */ + +#ifndef _READLEF_H +#define _READLEF_H + +#include "lef.h" + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* Some constants for LEF and DEF files */ + +#define LEF_LINE_MAX 2048 /* Maximum length fixed by LEF/DEF specifications */ +#define LEF_MAX_ERRORS 100 /* Max # errors to report; limits output if */ + /* something is really wrong about the file */ + +#define DEFAULT_WIDTH 3 /* Default metal width for routes if undefined */ +#define DEFAULT_SPACING 4 /* Default spacing between metal if undefined */ + +/* Structure holding the counts of regular and special nets */ + +typedef struct +{ + int regular; + int special; + u_char has_nets; +} NetCount; + +/* Various modes for writing nets. */ +#define DO_REGULAR 0 +#define DO_SPECIAL 1 +#define ALL_SPECIAL 2 /* treat all nets as SPECIALNETS */ + +/* Types of error messages */ +enum lef_error_types {LEF_ERROR = 0, LEF_WARNING, DEF_ERROR, DEF_WARNING}; + +/* Macro classes */ +enum macro_classes {MACRO_CLASS_DEFAULT = 0, MACRO_CLASS_CORE, + MACRO_CLASS_BLOCK, MACRO_CLASS_PAD, MACRO_CLASS_RING, + MACRO_CLASS_COVER, MACRO_CLASS_ENDCAP}; + +/* Core macro subclasses */ +enum macro_subclasses {MACRO_SUBCLASS_NONE = 0, + MACRO_SUBCLASS_SPACER, MACRO_SUBCLASS_ANTENNA, + MACRO_SUBCLASS_WELLTAP, MACRO_SUBCLASS_TIEHIGH, + MACRO_SUBCLASS_TIELOW, MACRO_SUBCLASS_FEEDTHRU}; + +/* Port classes */ +enum port_classes {PORT_CLASS_DEFAULT = 0, PORT_CLASS_INPUT, + PORT_CLASS_OUTPUT, PORT_CLASS_TRISTATE, PORT_CLASS_BIDIRECTIONAL, + PORT_CLASS_FEEDTHROUGH}; + +/* Port uses */ +enum port_uses {PORT_USE_DEFAULT = 0, PORT_USE_SIGNAL, + PORT_USE_ANALOG, PORT_USE_POWER, PORT_USE_GROUND, + PORT_USE_CLOCK, PORT_USE_TIEOFF, PORT_USE_SCAN, + PORT_USE_RESET}; + +/* Structure to hold information about spacing rules */ +typedef struct _lefSpacingRule *lefSpacingPtr; +typedef struct _lefSpacingRule { + lefSpacingPtr next; + double width; /* width, in microns */ + double spacing; /* minimum spacing rule, in microns */ +} lefSpacingRule; + +/* Area calculation methods for finding antenna ratios. ANTENNA_ROUTE */ +/* is not a method but is used to indicate to the antenna search */ +/* routine that the search is for routing preparation and not for */ +/* calculating error. */ + +enum area_methods {CALC_NONE = 0, CALC_AREA, CALC_SIDEAREA, CALC_AGG_AREA, + CALC_AGG_SIDEAREA, ANTENNA_ROUTE, ANTENNA_DISABLE}; + +/* Structure used to maintain default routing information for each */ +/* routable layer type. */ + +typedef struct { + lefSpacingRule *spacing; /* spacing rules, ordered by width */ + double width; /* nominal route width, in microns */ + double pitchx; /* route X pitch, in microns */ + double pitchy; /* route Y pitch, in microns */ + double offsetx; /* route track offset from X origin, in microns */ + double offsety; /* route track offset from Y origin, in microns */ + double respersq; /* resistance per square */ + double areacap; /* area capacitance per square micron */ + double edgecap; /* edge capacitance per micron */ + double minarea; /* minimum metal area rule */ + double thick; /* metal layer thickness, if given */ + double antenna; /* antenna area ratio rule */ + u_char method; /* antenna rule calculation method */ + u_char hdirection; /* horizontal direction preferred */ +} lefRoute; + +/* These values may be used for "hdirection". initialize hdirection */ +/* with DIR_UNKNOWN. If the LEF file defines the pitch before the */ +/* direction and does not specify both X and Y pitches, then change to */ +/* DIR_RESOLVE to indicate that the pitch in the non-preferred */ +/* direction should be zeroed when the preferred direction is known. */ + +#define DIR_VERTICAL (u_char)0 +#define DIR_HORIZONTAL (u_char)1 +#define DIR_UNKNOWN (u_char)2 +#define DIR_RESOLVE (u_char)3 + +/* Structure used to maintain default generation information for each */ +/* via or viarule (contact) type. If "cell" is non-NULL, then the via */ +/* is saved in a cell (pointed to by "cell"), and "area" describes the */ +/* bounding box. Otherwise, the via is formed by magic type "type" */ +/* with a minimum area "area" for a single contact. */ + +typedef struct { + lefSpacingRule *spacing; /* spacing rules, ordered by width */ + struct dseg_ area; /* Area of single contact, or cell bbox */ + /* in units of microns */ + GATE cell; /* Cell for fixed via def, or NULL */ + DSEG lr; /* Extra information for vias with */ + /* more complicated geometry. */ + double respervia; /* resistance per via */ + int obsType; /* Secondary obstruction type */ + char generated; /* Flag indicating via from VIARULE */ +} lefVia; + +/* Defined types for "lefClass" in the lefLayer structure */ +/* Note that the first four match TYPE records in the LEF. IGNORE has */ +/* a special meaning to qrouter, and VIA is for VIA definitions. */ + +#define CLASS_ROUTE 0 /* route layer */ +#define CLASS_CUT 1 /* cut layer */ +#define CLASS_MASTER 2 /* masterslice layer */ +#define CLASS_OVERLAP 3 /* overlap layer */ +#define CLASS_IGNORE 4 /* inactive layer */ +#define CLASS_VIA 5 /* via record */ + +/* Structure defining a route or via layer and matching it to a magic */ +/* layer type. This structure is saved in the LefInfo list. */ + +typedef struct _lefLayer *LefList; + +typedef struct _lefLayer { + LefList next; /* Next layer in linked list */ + char *lefName; /* CIF name of this layer */ + int type; /* GDS layer type, or -1 for none */ + int obsType; /* GDS type to use if this is an obstruction */ + u_char lefClass; /* is this a via, route, or masterslice layer */ + union { + lefRoute route; /* for route layers */ + lefVia via; /* for contacts */ + } info; +} lefLayer; + +/* External declaration of global variables */ +extern int lefCurrentLine; +extern LefList LefInfo; +extern LinkedStringPtr AllowedVias; + +/* Forward declarations */ + +int Lookup(char *str, char*(table[])); + +u_char LefParseEndStatement(FILE *f, char *match); +void LefSkipSection(FILE *f, char *match); +void LefEndStatement(FILE *f); +GATE lefFindCell(char *name); +char *LefNextToken(FILE *f, u_char ignore_eol); +char *LefLower(char *token); +DSEG LefReadGeometry(GATE lefMacro, FILE *f, float oscale); +LefList LefRedefined(LefList lefl, char *redefname); +void LefAddViaGeometry(FILE *f, LefList lefl, int curlayer, float oscale); +DSEG LefReadRect(FILE *f, int curlayer, float oscale); +int LefReadLayer(FILE *f, u_char obstruct); +LefList LefFindLayer(char *token); +LefList LefFindLayerByNum(int layer); +LefList LefNewVia(char *name); +int LefFindLayerNum(char *token); +void LefSetRoutePitchX(int layer, double value); +void LefSetRoutePitchY(int layer, double value); +double LefGetViaWidth(LefList lefl, int layer, int dir); +double LefGetRouteKeepout(int layer); +double LefGetRouteWidth(int layer); +double LefGetRouteMinArea(int layer); +double LefGetRouteSpacing(int layer); +double LefGetRouteWideSpacing(int layer, double width); +double LefGetRoutePitch(int layer); +double LefGetRoutePitchX(int layer); +double LefGetRoutePitchY(int layer); +double LefGetRouteOffset(int layer); +double LefGetRouteThickness(int layer); +double LefGetRouteAreaRatio(int layer); +u_char LefGetRouteAntennaMethod(int layer); +int LefGetRouteRCvalues(int layer, double *areacap, double *edgecap, + double *respersq); +int LefGetViaResistance(int layer, double *respervia); +char *LefGetRouteName(int layer); +int LefGetRouteOrientation(int layer); +int LefGetMaxRouteLayer(void); +int LefGetMaxLayer(void); + +GATE LefFindInstance(char *name); +void LefHashCell(GATE gateginfo); + +int LefRead(char *inName); +void LefAssignLayerVias(); +void LefWriteGeneratedVias(FILE *f, double oscale, int defvias); + + +void LefError(int type, char *fmt, ...); /* Variable argument procedure */ + /* requires parameter list. */ + +#endif /* _READLEF_H */ diff --git a/src/readliberty.c b/src/readliberty.c index 079a644..4505480 100644 --- a/src/readliberty.c +++ b/src/readliberty.c @@ -167,6 +167,7 @@ advancetoken(FILE *flib, char delimiter) // Remove any trailing whitespace tptr = token + strlen(token) - 1; + if (tptr < token) tptr = token; while (isblank(*tptr)) { *tptr = '\0'; tptr--; @@ -650,16 +651,24 @@ read_liberty(char *libfile, char *pattern) newtable->invert = 1; } else if (!strcasecmp(token, "index_1")) { + char dnum = ','; + token = advancetoken(flib, 0); // Open parens token = advancetoken(flib, 0); // Quote if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + iptr = token; + + // Check if table is comma or space separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (newtable->invert == 1) { // Count entries - iptr = token; newtable->csize = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->csize++; } @@ -669,7 +678,7 @@ read_liberty(char *libfile, char *pattern) iptr = token; sscanf(iptr, "%lg", &newtable->caps[0]); newtable->caps[0] *= cap_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->csize++; sscanf(iptr, "%lg", @@ -679,10 +688,10 @@ read_liberty(char *libfile, char *pattern) newtable->csize++; } else { // newtable->invert = 0 + // Count entries - iptr = token; newtable->tsize = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->tsize++; } @@ -692,7 +701,7 @@ read_liberty(char *libfile, char *pattern) iptr = token; sscanf(iptr, "%lg", &newtable->times[0]); newtable->times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->tsize++; sscanf(iptr, "%lg", @@ -705,16 +714,26 @@ read_liberty(char *libfile, char *pattern) token = advancetoken(flib, ';'); // EOL semicolon } else if (!strcasecmp(token, "index_2")) { + char dnum = ','; + token = advancetoken(flib, 0); // Open parens token = advancetoken(flib, 0); // Quote if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + // Determine if array tokens are comma or space separated. + iptr = token; + + // Check if table is comma or space separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (newtable->invert == 0) { + // Count entries - iptr = token; newtable->csize = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->csize++; } @@ -724,7 +743,7 @@ read_liberty(char *libfile, char *pattern) iptr = token; sscanf(iptr, "%lg", &newtable->caps[0]); newtable->caps[0] *= cap_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->csize++; sscanf(iptr, "%lg", @@ -735,9 +754,8 @@ read_liberty(char *libfile, char *pattern) } else { // newtable->invert == 1 // Count entries - iptr = token; newtable->tsize = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->tsize++; } @@ -747,7 +765,7 @@ read_liberty(char *libfile, char *pattern) iptr = token; sscanf(iptr, "%lg", &newtable->times[0]); newtable->times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->tsize++; sscanf(iptr, "%lg", @@ -1152,6 +1170,7 @@ read_liberty(char *libfile, char *pattern) while (*token != '}') { token = advancetoken(flib, 0); if (!strcasecmp(token, "index_1")) { + char dnum = ','; // Local index values override those in the template @@ -1162,15 +1181,21 @@ read_liberty(char *libfile, char *pattern) //------------------------- + iptr = token; + + // Check if table is comma or space separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (reftable && (reftable->invert == 1)) { // Entries had better match the ref table - iptr = token; i = 0; newcell->caps = (double *)malloc(reftable->csize * sizeof(double)); sscanf(iptr, "%lg", &newcell->caps[0]); newcell->caps[0] *= cap_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &newcell->caps[i]); @@ -1178,13 +1203,12 @@ read_liberty(char *libfile, char *pattern) } } else if (reftable && (reftable->invert == 0)) { - iptr = token; i = 0; newcell->times = (double *)malloc(reftable->tsize * sizeof(double)); sscanf(iptr, "%lg", &newcell->times[0]); newcell->times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; if (i < reftable->tsize) { @@ -1201,6 +1225,7 @@ read_liberty(char *libfile, char *pattern) token = advancetoken(flib, ';'); // EOL semicolon } else if (!strcasecmp(token, "index_2")) { + char dnum = ','; // Local index values override those in the template @@ -1211,15 +1236,21 @@ read_liberty(char *libfile, char *pattern) //------------------------- + iptr = token; + + // Check if table is comma or space separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (reftable && (reftable->invert == 1)) { // Entries had better match the ref table - iptr = token; i = 0; newcell->times = (double *)malloc(reftable->tsize * sizeof(double)); sscanf(iptr, "%lg", &newcell->times[0]); newcell->times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &newcell->times[i]); @@ -1227,13 +1258,12 @@ read_liberty(char *libfile, char *pattern) } } else if (reftable && (reftable->invert == 0)) { - iptr = token; i = 0; newcell->caps = (double *)malloc(reftable->csize * sizeof(double)); sscanf(iptr, "%lg", &newcell->caps[0]); newcell->caps[0] *= cap_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; if (i < reftable->csize) { @@ -1274,6 +1304,7 @@ read_liberty(char *libfile, char *pattern) *(newcell->values + j * reftable->tsize + i) = gval * time_unit; while (*iptr != ' ' && *iptr != '\"' && + *iptr != '\0' && *iptr != ',' || *iptr == '\\') iptr++; } @@ -1390,7 +1421,12 @@ get_values(Cell *curcell, double *retdelay, double *retcap) // Calculate delay per load. Note that cap at this point should be // in fF, and trise should be in ps. // So the value of loaddelay is in ps/fF. - loaddelay = (maxtrise - mintrise) / (maxcap - mincap); + // If there is only one cap value, then nothing can be calculated; set + // loaddelay to zero so it does not blow up to infinity. + if (maxcap == mincap) + loaddelay = 0.0; + else + loaddelay = (maxtrise - mintrise) / (maxcap - mincap); curcell->slope = loaddelay; curcell->mintrans = mintrise; @@ -1472,6 +1508,7 @@ Pin * get_pin_by_name(Cell *curcell, char *pinname) { Pin *curpin; + char *dptr; for (curpin = curcell->pins; curpin; curpin = curpin->next) { if (!strcmp(curpin->name, pinname)) { @@ -1479,6 +1516,18 @@ get_pin_by_name(Cell *curcell, char *pinname) return curpin; } } + /* Check for buses */ + for (curpin = curcell->pins; curpin; curpin = curpin->next) { + dptr = strchr(curpin->name, '['); + if (dptr != NULL) { + *dptr = '\0'; + if (!strcmp(curpin->name, pinname)) { + *dptr = '['; + return curpin; + } + *dptr = '['; + } + } return NULL; } diff --git a/src/readliberty.h b/src/readliberty.h index 72add1a..d84050b 100644 --- a/src/readliberty.h +++ b/src/readliberty.h @@ -17,6 +17,8 @@ #define PIN_INPUT 0 #define PIN_CLOCK 1 #define PIN_OUTPUT 2 +#define PIN_POWER 3 +#define PIN_GROUND 4 // Function translation #define GROUPBEGIN 1 diff --git a/src/readverilog.c b/src/readverilog.c new file mode 100644 index 0000000..30b2ba4 --- /dev/null +++ b/src/readverilog.c @@ -0,0 +1,1989 @@ +/*----------------------------------------------------------------------*/ +/* readverilog.c -- Input for Verilog format (structural verilog only) */ +/* */ +/* Adapted from verilog.c in netgen-1.5 */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* The verilog input is limited to "structural verilog", that is, */ +/* verilog code that only defines inputs, outputs, local nodes (via the */ +/* "wire" statement), and instanced modules. All modules are read down */ +/* to the point where either a module (1) does not conform to the */ +/* requirements above, or (2) has no module definition, in which case */ +/* it is treated as a "black box" subcircuit and therefore becomes a */ +/* low-level device. Because in verilog syntax all instances of a */ +/* module repeat both the module pin names and the local connections, */ +/* placeholders can be built without the need to redefine pins later, */ +/* as must be done for formats like SPICE that don't declare pin names */ +/* in an instance call. */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Note that use of 1'b0 or 1'b1 and similar variants is prohibited; */ +/* the structural netlist should either declare VSS and/or VDD as */ +/* inputs, or use tie-high and tie-low standard cells. */ +/*----------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------*/ +/* Most verilog syntax has been captured below. Still to do: Handle */ +/* wires that are created on the fly using {...} notation, including */ +/* the {n{...}} concatenation method. Currently this syntax is only */ +/* handled in nets connected to instance pins. */ +/*----------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <math.h> +#include <sys/types.h> +#include <pwd.h> + +#include "hash.h" +#include "readverilog.h" + +/*------------------------------------------------------*/ +/* Global variables */ +/*------------------------------------------------------*/ + +struct filestack *OpenFiles = NULL; +int linesize = 0; /* Amount of memory allocated for line */ +int vlinenum = 0; +char *nexttok; +char *linetok; +char *line = NULL; /* The line read in */ +FILE *infile = NULL; + +struct hashtable verilogparams; +struct hashtable verilogdefs; +struct hashtable verilogvectors; + +char dictinit = FALSE; + +/*------------------------------------------------------*/ +/* Structure for stacking nested `if[n]def */ +/*------------------------------------------------------*/ + +struct ifstack { + int invert; + char *kl; + struct ifstack *next; +}; + +struct ifstack *condstack = NULL; + +/*------------------------------------------------------*/ +/* Stack handling (push and pop) */ +/*------------------------------------------------------*/ + +void PushStack(struct cellrec *cell, struct cellstack **top) +{ + struct cellstack *newstack; + + newstack = (struct cellstack *)calloc(1, sizeof(struct cellstack)); + newstack->cell = cell; + newstack->next = *top; + *top = newstack; +} + +/*------------------------------------------------------*/ + +struct cellrec *PopStack(struct cellstack **top) +{ + struct cellstack *stackptr; + struct cellrec *cellptr; + + stackptr = *top; + cellptr = stackptr->cell; + if (!stackptr) return NULL; + *top = stackptr->next; + free(stackptr); + + return cellptr; +} + +/*----------------------------------------------------------------------*/ +/* Routine that can be called to pre-install a definition into the */ +/* verilogdefs hash table. */ +/*----------------------------------------------------------------------*/ + +void VerilogDefine(char *key, char *value) +{ + if (dictinit == FALSE) { + InitializeHashTable(&verilogdefs, TINYHASHSIZE); + dictinit = TRUE; + } + HashPtrInstall(key, strdup(value), &verilogdefs); +} + +/*----------------------------------------------------------------------*/ +/* Function similar to strtok() for token parsing. The difference is */ +/* that it takes two sets of delimiters. The first is whitespace */ +/* delimiters, which separate tokens. The second is functional */ +/* delimiters, which separate tokens and have additional meaning, such */ +/* as parentheses, commas, semicolons, etc. The parser needs to know */ +/* when such tokens occur in the string, so they are returned as */ +/* individual tokens. */ +/* */ +/* Definition of delim2: String of single delimiter characters. The */ +/* special character "X" (which is never a delimiter in practice) is */ +/* used to separate single-character delimiters from two-character */ +/* delimiters (this presumably could be further extended as needed). */ +/* so ",;()" would be a valid delimiter set, but to include C-style */ +/* comments and verilog-style parameter lists, one would need */ +/* ",;()X/**///#(". */ +/*----------------------------------------------------------------------*/ + +char *strdtok(char *pstring, char *delim1, char *delim2) +{ + static char *stoken = NULL; + static char *sstring = NULL; + char *s, *s2; + char first = FALSE; + int twofer; + + if (pstring != NULL) { + /* Allocate enough memory to hold the string; tokens will be put here */ + if (sstring != NULL) free(sstring); + sstring = (char *)malloc(strlen(pstring) + 1); + stoken = pstring; + first = TRUE; + } + + /* Skip over "delim1" delimiters at the string beginning */ + for (; *stoken; stoken++) { + for (s2 = delim1; *s2; s2++) + if (*stoken == *s2) + break; + if (*s2 == '\0') break; + } + + if (*stoken == '\0') return NULL; /* Finished parsing */ + + /* "stoken" is now set. Now find the end of the current token */ + /* Check string from position stoken. If a character in "delim2" is found, */ + /* save the character in "lastdelim", null the byte at that position, and */ + /* return the token. If a character in "delim1" is found, do the same but */ + /* continue checking characters as long as there are contiguous "delim1" */ + /* characters. If the series ends in a character from "delim2", then treat */ + /* as for "delim2" above. If not, then set "lastdelim" to a null byte and */ + /* return the token. */ + + s = stoken; + + /* Special verilog rule: If a name begins with '\', then all characters */ + /* are a valid part of the name until a space character is reached. The */ + /* space character becomes part of the verilog name. The remainder of the */ + /* name is parsed according to the rules of "delim2". */ + + if (*s == '\\') { + while (*s != '\0') { + if (*s == ' ') { + s++; + break; + } + s++; + } + } + + for (; *s; s++) { + twofer = TRUE; + for (s2 = delim2; s2 && *s2; s2++) { + if (*s2 == 'X') { + twofer = FALSE; + continue; + } + if (twofer) { + if ((*s == *s2) && (*(s + 1) == *(s2 + 1))) { + if (s == stoken) { + strncpy(sstring, stoken, 2); + *(sstring + 2) = '\0'; + stoken = s + 2; + } + else { + strncpy(sstring, stoken, (int)(s - stoken)); + *(sstring + (s - stoken)) = '\0'; + stoken = s; + } + return sstring; + } + s2++; + if (*s2 == '\0') break; + } + else if (*s == *s2) { + if (s == stoken) { + strncpy(sstring, stoken, 1); + *(sstring + 1) = '\0'; + stoken = s + 1; + } + else { + strncpy(sstring, stoken, (int)(s - stoken)); + *(sstring + (s - stoken)) = '\0'; + stoken = s; + } + return sstring; + } + } + for (s2 = delim1; *s2; s2++) { + if (*s == *s2) { + strncpy(sstring, stoken, (int)(s - stoken)); + *(sstring + (s - stoken)) = '\0'; + stoken = s; + return sstring; + } + } + } + strcpy(sstring, stoken); /* Just copy to the end */ + stoken = s; + return sstring; +} + +/*----------------------------------------------------------------------*/ +/* File opening, closing, and stack handling */ +/* Return 0 on success, -1 on error. */ +/*----------------------------------------------------------------------*/ + +int OpenParseFile(char *name) +{ + /* Push filestack */ + + FILE *locfile = NULL; + struct filestack *newfile; + + locfile = fopen(name, "r"); + vlinenum = 0; + /* reset the token scanner */ + nexttok = NULL; + + if (locfile != NULL) { + if (infile != NULL) { + newfile = (struct filestack *)malloc(sizeof(struct filestack)); + newfile->file = infile; + newfile->next = OpenFiles; + OpenFiles = newfile; + } + infile = locfile; + } + return (locfile == NULL) ? -1 : 0; +} + +/*----------------------------------------------------------------------*/ + +int EndParseFile(void) +{ + return feof(infile); +} + +/*----------------------------------------------------------------------*/ + +int CloseParseFile(void) +{ + struct filestack *lastfile; + int rval; + rval = fclose(infile); + infile = (FILE *)NULL; + + /* Pop filestack if not empty */ + lastfile = OpenFiles; + if (lastfile != NULL) { + OpenFiles = lastfile->next; + infile = lastfile->file; + free(lastfile); + } + return rval; +} + +/*----------------------------------------------------------------------*/ + +void InputParseError(FILE *f) +{ + char *ch; + + fprintf(f, "line number %d = '", vlinenum); + for (ch = line; *ch != '\0'; ch++) { + if (isprint(*ch)) fprintf(f, "%c", *ch); + else if (*ch != '\n') fprintf(f, "<<%d>>", (int)(*ch)); + } + fprintf(f, "'\n"); +} + +/*----------------------------------------------------------------------*/ +/* Tokenizer routines */ +/*----------------------------------------------------------------------*/ + +#define WHITESPACE_DELIMITER " \t\n\r" + +/*----------------------------------------------------------------------*/ +/* TrimQuoted() --- */ +/* Remove spaces from inside single- or double-quoted strings. */ +/* Exclude bit values (e.g., "1'b0") */ +/*----------------------------------------------------------------------*/ + +void TrimQuoted(char *line) +{ + char *qstart, *qend, *lptr; + int slen; + int changed; + + /* Single-quoted entries */ + changed = TRUE; + lptr = line; + while (changed) + { + changed = FALSE; + qstart = strchr(lptr, '\''); + if (qstart && (qstart > lptr)) { + if (isdigit(*(qstart - 1))) { + lptr = qstart + 1; + changed = TRUE; + continue; + } + } + if (qstart) + { + qend = strrchr(qstart + 1, '\''); + if (qend && (qend > qstart)) { + slen = strlen(lptr); + for (lptr = qstart + 1; lptr < qend; lptr++) { + if (*lptr == ' ') { + memmove(lptr, lptr + 1, slen); + qend--; + changed = TRUE; + } + } + lptr++; + } + } + } + + /* Double-quoted entries */ + changed = TRUE; + lptr = line; + while (changed) + { + changed = FALSE; + qstart = strchr(lptr, '\"'); + if (qstart) + { + qend = strchr(qstart + 1, '\"'); + if (qend && (qend > qstart)) { + slen = strlen(lptr); + for (lptr = qstart + 1; lptr < qend; lptr++) { + if (*lptr == ' ') { + memmove(lptr, lptr + 1, slen); + qend--; + changed = TRUE; + } + } + lptr++; + } + } + } +} + +/*----------------------------------------------------------------------*/ +/* GetNextLineNoNewline() */ +/* */ +/* Fetch the next line, and grab the first token from the next line. */ +/* If there is no next token (next line is empty, and ends with a */ +/* newline), then place NULL in nexttok. */ +/* */ +/*----------------------------------------------------------------------*/ + +int GetNextLineNoNewline(char *delimiter) +{ + char *newbuf; + int testc; + int nested = 0; + char *s, *t, *w, e, *kl; + int len, dlen, vlen, addin; + unsigned char found; + + if (feof(infile)) return -1; + + while (1) { /* May loop indefinitely in an `if[n]def conditional */ + + // This is more reliable than feof() ... + testc = getc(infile); + if (testc == -1) return -1; + ungetc(testc, infile); + + if (linesize == 0) { + /* Allocate memory for line */ + linesize = 500; + line = (char *)malloc(linesize); + linetok = (char *)malloc(linesize); + } + fgets(line, linesize, infile); + while (strlen(line) == linesize - 1) { + newbuf = (char *)malloc(linesize + 500); + strcpy(newbuf, line); + free(line); + line = newbuf; + fgets(line + linesize - 1, 501, infile); + linesize += 500; + free(linetok); + linetok = (char *)malloc(linesize * sizeof(char)); + } + + /* Check for substitutions. Make sure linetok is large enough */ + /* to hold the entire line after substitutions. */ + + found = FALSE; + addin = 0; + for (s = line; *s != '\0'; s++) { + if (*s == '`') { + w = s + 1; + while (isalnum(*w)) w++; + e = *w; + *w = '\0'; + kl = (char *)HashLookup(s + 1, &verilogdefs); + if (kl != NULL) { + dlen = strlen(s); + vlen = strlen(kl); + addin += vlen - dlen + 1; + found = TRUE; + } + *w = e; + } + } + if (found) { + len = strlen(line); + if (len + addin > linesize) { + while (len + addin > linesize) linesize += 500; + free(linetok); + linetok = (char *)malloc(linesize); + } + } + + /* Make definition substitutions */ + + t = linetok; + for (s = line; *s != '\0'; s++) { + if (*s == '`') { + w = s + 1; + while (isalnum(*w)) w++; + e = *w; + *w = '\0'; + kl = (char *)HashLookup(s + 1, &verilogdefs); + if (kl != NULL) { + strcpy(t, kl); + t += strlen(t); + s = w - 1; + } + else *t++ = *s; + *w = e; + } + else *t++ = *s; + } + *t = '\0'; + + TrimQuoted(linetok); + vlinenum++; + + nexttok = strdtok(linetok, WHITESPACE_DELIMITER, delimiter); + if (nexttok == NULL) return 0; + + /* Handle `ifdef, `ifndef, `elsif, `else, and `endif */ + + /* If currently skipping through a section, handle conditionals differently */ + if (condstack) { + if (((condstack->invert == 0) && (condstack->kl == NULL)) + || ((condstack->invert == 1) && (condstack->kl != NULL))) { + if (match(nexttok, "`ifdef") || match(nexttok, "`ifndef")) { + nested++; + continue; + } + else if (nested > 0) { + if (match(nexttok, "`endif")) nested--; + continue; + } + else if (nexttok[0] != '`') continue; + } + } + + /* Handle conditionals (not being skipped over) */ + + if (match(nexttok, "`endif")) { + if (condstack == NULL) { + fprintf(stderr, "Error: `endif without corresponding `if[n]def\n"); + } + else { + struct ifstack *iftop = condstack; + condstack = condstack->next; + free(iftop); + } + } + + /* `if[n]def may be nested. */ + + else if (match(nexttok, "`ifdef") || match(nexttok, "`ifndef") || + match(nexttok, "`elsif") || match(nexttok, "`else")) { + + /* Every `ifdef or `ifndef increases condstack by 1 */ + if (nexttok[1] == 'i') { + struct ifstack *newif = (struct ifstack *)malloc(sizeof(struct ifstack)); + newif->next = condstack; + condstack = newif; + } + if (condstack == NULL) { + fprintf(stderr, "Error: %s without `if[n]def\n", nexttok); + break; + } + else { + if (match(nexttok, "`else")) { + /* Invert the sense of the if[n]def scope */ + condstack->invert = (condstack->invert == 1) ? 0 : 1; + } + else if (match(nexttok, "`elsif")) { + nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter); + if (nexttok == NULL) { + fprintf(stderr, "Error: `elsif with no conditional.\n"); + return 0; + } + /* Keep the same scope but redefine the parameter */ + condstack->invert = 0; + condstack->kl = (char *)HashLookup(nexttok, &verilogdefs); + } + else { + condstack->invert = (nexttok[3] == 'n') ? 1 : 0; + nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter); + if (nexttok == NULL) { + fprintf(stderr, "Error: %s with no conditional.\n", nexttok); + return 0; + } + condstack->kl = (char *)HashLookup(nexttok, &verilogdefs); + } + } + } + else if (condstack) { + if (((condstack->invert == 0) && (condstack->kl == NULL)) + || ((condstack->invert == 1) && (condstack->kl != NULL))) + continue; + else + break; + } + else + break; + } + return 0; +} + +/*----------------------------------------------------------------------*/ +/* Get the next line of input from file "infile", and find the first */ +/* valid token. */ +/*----------------------------------------------------------------------*/ + +void GetNextLine(char *delimiter) +{ + do { + if (GetNextLineNoNewline(delimiter) == -1) return; + } while (nexttok == NULL); +} + +/*----------------------------------------------------------------------*/ +/* skip to the end of the current line */ +/*----------------------------------------------------------------------*/ + +void SkipNewLine(char *delimiter) +{ + while (nexttok != NULL) + nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter); +} + +/*----------------------------------------------------------------------*/ +/* if nexttok is already NULL, force scanner to read new line */ +/*----------------------------------------------------------------------*/ + +void SkipTok(char *delimiter) +{ + if (nexttok != NULL && + (nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter))) + return; + GetNextLine(delimiter); +} + +/*----------------------------------------------------------------------*/ +/* like SkipTok, but will not fetch a new line when the buffer is empty */ +/* must be preceeded by at least one call to SkipTok() */ +/*----------------------------------------------------------------------*/ + +void SkipTokNoNewline(char *delimiter) +{ + nexttok = strdtok(NULL, WHITESPACE_DELIMITER, delimiter); +} + +/*----------------------------------------------------------------------*/ +/* Skip to the next token, ignoring any C-style comments. */ +/*----------------------------------------------------------------------*/ + +void SkipTokComments(char *delimiter) +{ + SkipTok(delimiter); + while (nexttok) { + if (!strcmp(nexttok, "//")) { + SkipNewLine(delimiter); + SkipTok(delimiter); + } + else if (!strcmp(nexttok, "/*")) { + while (nexttok && strcmp(nexttok, "*/")) + SkipTok(delimiter); + if (nexttok) SkipTok(delimiter); + } + else if (!strcmp(nexttok, "(*")) { + while (nexttok && strcmp(nexttok, "*)")) + SkipTok(delimiter); + if (nexttok) SkipTok(delimiter); + } + else break; + } +} + +/*----------------------------------------------------------------------*/ +/* Free a bus structure in the hash table during cleanup */ +/*----------------------------------------------------------------------*/ + +int freenet (struct hashlist *p) +{ + struct netrec *wb; + + wb = (struct netrec *)(p->ptr); + free(wb); + return 1; +} + +/*----------------------------------------------------------------------*/ +/* Create a new net (or bus) structure */ +/*----------------------------------------------------------------------*/ + +struct netrec *NewNet() +{ + struct netrec *wb; + + wb = (struct netrec *)calloc(1, sizeof(struct netrec)); + if (wb == NULL) fprintf(stderr, "NewNet: Core allocation error\n"); + return (wb); +} + +/*----------------------------------------------------------------------*/ +/* BusHashLookup -- */ +/* Run HashLookup() on a string, first removing any bus delimiters. */ +/*----------------------------------------------------------------------*/ + +void *BusHashLookup(char *s, struct hashtable *table) +{ + void *rval; + char *dptr = NULL; + char *sptr = s; + + if (*sptr == '\\') { + sptr = strchr(s, ' '); + if (sptr == NULL) sptr = s; + } + if ((dptr = strchr(sptr, '[')) != NULL) *dptr = '\0'; + + rval = HashLookup(s, table); + if (dptr) *dptr = '['; + return rval; +} + +/*----------------------------------------------------------------------*/ +/* BusHashPtrInstall -- */ +/* Run HashPtrInstall() on a string, first removing any bus delimiters. */ +/*----------------------------------------------------------------------*/ + +struct hashlist *BusHashPtrInstall(char *name, void *ptr, + struct hashtable *table) +{ + struct hashlist *rval; + char *dptr = NULL; + char *sptr = name; + + if (*sptr == '\\') { + sptr = strchr(name, ' '); + if (sptr == NULL) sptr = name; + } + if ((dptr = strchr(sptr, '[')) != NULL) *dptr = '\0'; + + rval = HashPtrInstall(name, ptr, table); + if (dptr) *dptr = '['; + return rval; +} + +/*------------------------------------------------------------------------------*/ +/* Get bus indexes from the notation name[a:b]. If there is only "name" */ +/* then look up the name in the bus hash list and return the index bounds. */ +/* Return 0 on success, 1 on syntax error, and -1 if signal is not a bus. */ +/* */ +/* Note that this routine relies on the delimiter characters including */ +/* "[", ":", and "]" when calling NextTok. */ +/*------------------------------------------------------------------------------*/ + +int GetBusTok(struct netrec *wb, struct hashtable *nets) +{ + int result, start, end; + char *kl; + + if (wb == NULL) return 0; + else { + wb->start = -1; + wb->end = -1; + } + + if (!strcmp(nexttok, "[")) { + SkipTokComments(VLOG_DELIMITERS); + + // Check for parameter names and substitute values if found. + result = sscanf(nexttok, "%d", &start); + if (result != 1) { + char *aptr = NULL; + char addin; + + // Check for "+/-(n)" at the end of a parameter name + aptr = strrchr(nexttok, '+'); + if (aptr == NULL) aptr = strrchr(nexttok, '-'); + if (aptr != NULL) { + addin = *aptr; + *aptr = '\0'; + } + + // Is name in the parameter list? + kl = (char *)HashLookup(nexttok, &verilogparams); + if (kl == NULL) { + fprintf(stdout, "Array value %s is not a number or a " + "parameter (line %d).\n", nexttok, vlinenum); + return 1; + } + else { + result = sscanf(kl, "%d", &start); + if (result != 1) { + fprintf(stdout, "Parameter %s has value %s that cannot be parsed" + " as an integer (line %d).\n", nexttok, kl, vlinenum); + return 1; + } + } + if (aptr != NULL) { + int addval; + *aptr = addin; + if (sscanf(aptr + 1, "%d", &addval) != 1) { + fprintf(stdout, "Unable to parse parameter increment \"%s\" " + "(line %d).\n", aptr, vlinenum); + return 1; + } + start += (addin == '+') ? addval : -addval; + } + } + + SkipTokComments(VLOG_DELIMITERS); + if (!strcmp(nexttok, "]")) { + result = 1; + end = start; // Single bit + } + else if (strcmp(nexttok, ":")) { + fprintf(stdout, "Badly formed array notation: Expected colon, " + "found %s (line %d)\n", nexttok, vlinenum); + return 1; + } + else { + SkipTokComments(VLOG_DELIMITERS); + + // Check for parameter names and substitute values if found. + result = sscanf(nexttok, "%d", &end); + if (result != 1) { + char *aptr = NULL; + char addin; + + // Check for "+/-(n)" at the end of a parameter name + aptr = strrchr(nexttok, '+'); + if (aptr == NULL) aptr = strrchr(nexttok, '-'); + if (aptr != NULL) { + addin = *aptr; + *aptr = '\0'; + } + + // Is name in the parameter list? + kl = (char *)HashLookup(nexttok, &verilogparams); + if (kl == NULL) { + fprintf(stdout, "Array value %s is not a number or a " + "parameter (line %d).\n", nexttok, vlinenum); + return 1; + } + else { + result = sscanf(kl, "%d", &end); + if (result != 1) { + fprintf(stdout, "Parameter %s has value %s that cannot" + " be parsed as an integer (line %d).\n", + nexttok, kl, vlinenum); + return 1; + } + } + if (aptr != NULL) { + int addval; + *aptr = addin; + if (sscanf(aptr + 1, "%d", &addval) != 1) { + fprintf(stdout, "Unable to parse parameter increment \"%s\" " + "(line %d).\n", aptr, vlinenum); + return 1; + } + end += (addin == '+') ? addval : -addval; + } + } + } + wb->start = start; + wb->end = end; + + while (strcmp(nexttok, "]")) { + SkipTokComments(VLOG_DELIMITERS); + if (nexttok == NULL) { + fprintf(stdout, "End of file reached while reading array bounds.\n"); + return 1; + } + else if (!strcmp(nexttok, ";")) { + // Better than reading to end-of-file, give up on end-of-statement + fprintf(stdout, "End of statement reached while reading " + "array bounds (line %d).\n", vlinenum); + return 1; + } + } + /* Move token forward to bus name */ + SkipTokComments(VLOG_DELIMITERS); + } + else if (nets) { + struct netrec *hbus; + hbus = (struct netrec *)BusHashLookup(nexttok, nets); + if (hbus != NULL) { + wb->start = hbus->start; + wb->end = hbus->end; + } + else + return -1; + } + return 0; +} + +/*----------------------------------------------------------------------*/ +/* GetBus() is similar to GetBusTok() (see above), but it parses from */ +/* a string instead of the input tokenizer. */ +/*----------------------------------------------------------------------*/ + +int GetBus(char *astr, struct netrec *wb, struct hashtable *nets) +{ + char *colonptr, *brackstart, *brackend, *sstr; + int result, start, end; + + if (wb == NULL) return 0; + else { + wb->start = -1; + wb->end = -1; + } + sstr = astr; + + // Skip to the end of verilog names bounded by '\' and ' ' + if (*sstr == '\\') + while (*sstr && *sstr != ' ') sstr++; + + brackstart = strchr(sstr, '['); + if (brackstart != NULL) { + brackend = strchr(sstr, ']'); + if (brackend == NULL) { + fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr, + vlinenum); + return 1; + } + *brackend = '\0'; + colonptr = strchr(sstr, ':'); + if (colonptr) *colonptr = '\0'; + result = sscanf(brackstart + 1, "%d", &start); + if (colonptr) *colonptr = ':'; + if (result != 1) { + fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr, + vlinenum); + *brackend = ']'; + return 1; + } + if (colonptr) + result = sscanf(colonptr + 1, "%d", &end); + else { + result = 1; + end = start; // Single bit + } + *brackend = ']'; + if (result != 1) { + fprintf(stdout, "Badly formed array notation \"%s\" (line %d)\n", astr, + vlinenum); + return 1; + } + wb->start = start; + wb->end = end; + } + else if (nets) { + struct netrec *hbus; + hbus = (struct netrec *)BusHashLookup(astr, nets); + if (hbus != NULL) { + wb->start = hbus->start; + wb->end = hbus->end; + } + else + return -1; + } + return 0; +} + +/*------------------------------------------------------*/ +/* Add net to cell database */ +/*------------------------------------------------------*/ + +struct netrec *Net(struct cellrec *cell, char *netname) +{ + struct netrec *newnet; + + newnet = NewNet(); + newnet->start = -1; + newnet->end = -1; + + /* Install net in net hash */ + BusHashPtrInstall(netname, newnet, &cell->nets); + + return newnet; +} + +/*------------------------------------------------------*/ +/* Add port to instance record */ +/*------------------------------------------------------*/ + +struct portrec *InstPort(struct instance *inst, char *portname, char *netname) +{ + struct portrec *portsrch, *newport; + + newport = (struct portrec *)malloc(sizeof(struct portrec)); + newport->name = strdup(portname); + newport->direction = PORT_NONE; + if (netname) + newport->net = strdup(netname); + else + newport->net = NULL; + newport->next = NULL; + + /* Go to end of the port list */ + if (inst->portlist == NULL) { + inst->portlist = newport; + } + else { + for (portsrch = inst->portlist; portsrch->next; portsrch = portsrch->next); + portsrch->next = newport; + } + return newport; +} + +/*------------------------------------------------------*/ +/* Add port to cell database */ +/*------------------------------------------------------*/ + +void Port(struct cellrec *cell, char *portname, struct netrec *net, int port_type) +{ + struct portrec *portsrch, *newport; + struct netrec *newnet; + + newport = (struct portrec *)malloc(sizeof(struct portrec)); + if (portname) + newport->name = strdup(portname); + else + newport->name = NULL; + newport->direction = port_type; + newport->net = NULL; + newport->next = NULL; + + /* Go to end of the port list */ + if (cell->portlist == NULL) { + cell->portlist = newport; + } + else { + for (portsrch = cell->portlist; portsrch->next; portsrch = portsrch->next); + portsrch->next = newport; + } + + /* Register the port name as a net in the cell */ + if (portname) + { + newnet = Net(cell, portname); + if (net) { + newnet->start = net->start; + newnet->end = net->end; + } + } +} + +/*------------------------------------------------------*/ +/* Create a new cell */ +/*------------------------------------------------------*/ + +struct cellrec *Cell(char *cellname) +{ + struct cellrec *new_cell; + + new_cell = (struct cellrec *)malloc(sizeof(struct cellrec)); + new_cell->name = strdup(cellname); + new_cell->portlist = NULL; + new_cell->instlist = NULL; + new_cell->lastinst = NULL; + + InitializeHashTable(&new_cell->nets, LARGEHASHSIZE); + InitializeHashTable(&new_cell->propdict, TINYHASHSIZE); + + return new_cell; +} + +/*------------------------------------------------------*/ +/* Add instance to cell database */ +/*------------------------------------------------------*/ + +struct instance *Instance(struct cellrec *cell, char *cellname, int prepend) +{ + struct instance *newinst, *instsrch; + + /* Create new instance record */ + newinst = (struct instance *)malloc(sizeof(struct instance)); + + newinst->instname = NULL; + newinst->cellname = strdup(cellname); + newinst->portlist = NULL; + newinst->next = NULL; + + InitializeHashTable(&newinst->propdict, TINYHASHSIZE); + + if (cell->instlist == NULL) { + cell->instlist = newinst; + } + else { + if (prepend == TRUE) { + newinst->next = cell->instlist; + cell->instlist = newinst; + } + else { + instsrch = (cell->lastinst != NULL) ? + cell->lastinst : cell->instlist; + + /* Go to end of the instance list */ + for (; instsrch->next; instsrch = instsrch->next); + + cell->lastinst = instsrch; + instsrch->next = newinst; + } + } + return newinst; +} + +struct instance *AppendInstance(struct cellrec *cell, char *cellname) +{ + return Instance(cell, cellname, FALSE); +} + +struct instance *PrependInstance(struct cellrec *cell, char *cellname) +{ + return Instance(cell, cellname, TRUE); +} + +/*------------------------------------------------------*/ +/* Read a verilog structural netlist */ +/*------------------------------------------------------*/ + +void ReadVerilogFile(char *fname, struct cellstack **CellStackPtr, + int blackbox) +{ + int i; + int warnings = 0, hasports, inlined_decls = 0, localcount = 1; + int port_type = PORT_NONE; + char in_module, in_param; + char *eqptr; + char pkey[256]; + + struct cellrec *top = NULL; + + in_module = (char)0; + in_param = (char)0; + + while (!EndParseFile()) { + + SkipTokComments(VLOG_DELIMITERS); /* get the next token */ + + /* Diagnostic */ + /* if (nexttok) fprintf(stdout, "Token is \"%s\"\n", nexttok); */ + + if ((EndParseFile()) && (nexttok == NULL)) break; + else if (nexttok == NULL) + break; + + /* Ignore end-of-statement markers */ + else if (!strcmp(nexttok, ";")) + continue; + + /* Ignore primitive definitions */ + else if (!strcmp(nexttok, "primitive")) { + while (1) { + SkipNewLine(VLOG_DELIMITERS); + SkipTokComments(VLOG_DELIMITERS); + if (EndParseFile()) break; + if (!strcmp(nexttok, "endprimitive")) { + in_module = 0; + break; + } + } + } + + else if (!strcmp(nexttok, "module")) { + struct netrec wb; + + SkipTokNoNewline(VLOG_DELIMITERS); + if (nexttok == NULL) { + fprintf(stderr, "Badly formed \"module\" line (line %d)\n", vlinenum); + goto skip_endmodule; + } + + if (in_module == (char)1) { + fprintf(stderr, "Missing \"endmodule\" statement on " + "subcircuit (line %d).\n", vlinenum); + InputParseError(stderr); + } + in_module = (char)1; + hasports = (char)0; + inlined_decls = (char)0; + + /* If there is an existing module, then push it */ + if (top != NULL) PushStack(top, CellStackPtr); + + /* Create new cell */ + top = Cell(nexttok); + + /* Need to support both types of I/O lists: Those */ + /* that declare names only in the module list and */ + /* follow with input/output and vector size */ + /* declarations as individual statements in the */ + /* module definition, and those which declare */ + /* everything inside the pin list. */ + + SkipTokComments(VLOG_DELIMITERS); + + // Check for parameters within #( ... ) + + if (!strcmp(nexttok, "#(")) { + SkipTokComments(VLOG_DELIMITERS); + in_param = (char)1; + } + else if (!strcmp(nexttok, "(")) { + SkipTokComments(VLOG_DELIMITERS); + } + + wb.start = wb.end = -1; + while ((nexttok != NULL) && strcmp(nexttok, ";")) { + if (in_param) { + if (!strcmp(nexttok, ")")) { + in_param = (char)0; + SkipTokComments(VLOG_DELIMITERS); + if (strcmp(nexttok, "(")) { + fprintf(stderr, "Badly formed module block parameter" + " list (line %d).\n", vlinenum); + goto skip_endmodule; + } + } + else if (!strcmp(nexttok, "=")) { + + // The parameter value is the next token. + SkipTokComments(VLOG_DELIMITERS); /* get the next token */ + HashPtrInstall(pkey, strdup(nexttok), &top->propdict); + } + else { + /* Assume this is a keyword and save it */ + strcpy(pkey, nexttok); + } + } + else if (strcmp(nexttok, ",")) { + if (!strcmp(nexttok, ")")) break; + // Ignore input, output, and inout keywords, and handle buses. + + if (inlined_decls == (char)0) { + if (!strcmp(nexttok, "input") || !strcmp(nexttok, "output") + || !strcmp(nexttok, "inout")) + inlined_decls = (char)1; + } + + if (inlined_decls == (char)1) { + if (!strcmp(nexttok, "input")) + port_type = PORT_INPUT; + else if (!strcmp(nexttok, "output")) + port_type = PORT_OUTPUT; + else if (!strcmp(nexttok, "inout")) + port_type = PORT_INOUT; + else if (strcmp(nexttok, "real") && strcmp(nexttok, "logic") + && strcmp(nexttok, "wire") + && strcmp(nexttok, "integer")) { + if (!strcmp(nexttok, "[")) { + if (GetBusTok(&wb, &top->nets) != 0) { + // Didn't parse as a bus, so wing it + Port(top, nexttok, NULL, port_type); + } + else + Port(top, nexttok, &wb, port_type); + } + else + Port(top, nexttok, NULL, port_type); + + hasports = 1; + } + } + } + SkipTokComments(VLOG_DELIMITERS); + if (nexttok == NULL) break; + } + if (inlined_decls == 1) { + if (hasports == 0) + // If the cell defines no ports, then create a proxy + Port(top, (char *)NULL, NULL, PORT_NONE); + + /* In the blackbox case, don't read the cell contents */ + if (blackbox) goto skip_endmodule; + } + } + else if (!strcmp(nexttok, "input") || !strcmp(nexttok, "output") + || !strcmp(nexttok, "inout")) { + struct netrec wb; + + if (!strcmp(nexttok, "input")) port_type = PORT_INPUT; + else if (!strcmp(nexttok, "output")) port_type = PORT_OUTPUT; + else if (!strcmp(nexttok, "inout")) port_type = PORT_INOUT; + else port_type = PORT_NONE; + + // Parsing of ports as statements not in the module pin list. + wb.start = wb.end = -1; + while (1) { + SkipTokComments(VLOG_DELIMITERS); + if (EndParseFile()) break; + + if (!strcmp(nexttok, ";")) { + // End of statement + break; + } + else if (!strcmp(nexttok, "[")) { + if (GetBusTok(&wb, &top->nets) != 0) { + // Didn't parse as a bus, so wing it + Port(top, nexttok, NULL, port_type); + } + else + Port(top, nexttok, &wb, port_type); + } + else if (strcmp(nexttok, ",")) { + /* Comma-separated list; use same bus limits */ + Port(top, nexttok, &wb, port_type); + } + hasports = 1; + } + } + else if (!strcmp(nexttok, "endmodule")) { + + if (in_module == (char)0) { + fprintf(stderr, "\"endmodule\" occurred outside of a " + "module (line %d)!\n", vlinenum); + InputParseError(stderr); + } + in_module = (char)0; + SkipNewLine(VLOG_DELIMITERS); + } + else if (!strcmp(nexttok, "`include")) { + char *iname, *iptr, *quotptr, *pathend, *userpath = NULL; + + SkipTokNoNewline(VLOG_DELIMITERS); + if (nexttok == NULL) continue; /* Ignore if no filename */ + + // Any file included in another Verilog file needs to be + // interpreted relative to the path of the parent Verilog file, + // unless it's an absolute pathname. + + pathend = strrchr(fname, '/'); + iptr = nexttok; + while (*iptr == '\'' || *iptr == '\"') iptr++; + if ((pathend != NULL) && (*iptr != '/') && (*iptr != '~')) { + *pathend = '\0'; + iname = (char *)malloc(strlen(fname) + strlen(iptr) + 2); + sprintf(iname, "%s/%s", fname, iptr); + *pathend = '/'; + } + else if ((*iptr == '~') && (*(iptr + 1) == '/')) { + /* For ~/<path>, substitute tilde from $HOME */ + userpath = getenv("HOME"); + iname = (char *)malloc(strlen(userpath) + strlen(iptr)); + sprintf(iname, "%s%s", userpath, iptr + 1); + } + else if (*iptr == '~') { + /* For ~<user>/<path>, substitute tilde from getpwnam() */ + struct passwd *passwd; + char *pathstart; + pathstart = strchr(iptr, '/'); + if (pathstart) *pathstart = '\0'; + passwd = getpwnam(iptr + 1); + if (passwd != NULL) { + userpath = passwd->pw_dir; + if (pathstart) { + *pathstart = '/'; + iname = (char *)malloc(strlen(userpath) + strlen(pathstart) + 1); + sprintf(iname, "%s%s", userpath, pathstart); + } + else { + /* Almost certainly an error, but make the substitution anyway */ + iname = strdup(userpath); + } + } + else { + /* Probably an error, but copy the filename verbatim */ + iname = strdup(iptr); + } + } + else + iname = strdup(iptr); + + // Eliminate any single or double quotes around the filename + iptr = iname; + quotptr = iptr; + while (*quotptr != '\'' && *quotptr != '\"' && + *quotptr != '\0' && *quotptr != '\n') quotptr++; + if (*quotptr == '\'' || *quotptr == '\"') *quotptr = '\0'; + + IncludeVerilog(iptr, CellStackPtr, blackbox); + free(iname); + SkipNewLine(VLOG_DELIMITERS); + } + else if (!strcmp(nexttok, "`define")) { + char *key; + + /* Parse for definitions used in expressions. Save */ + /* definitions in the "verilogdefs" hash table. */ + + SkipTokNoNewline(VLOG_DELIMITERS); + if ((nexttok == NULL) || (nexttok[0] == '\0')) break; + + key = strdup(nexttok); + + SkipTokNoNewline(VLOG_DELIMITERS); + if ((nexttok == NULL) || (nexttok[0] == '\0')) + /* Let "`define X" be equivalent to "`define X 1". */ + HashPtrInstall(key, strdup("1"), &verilogdefs); + else + HashPtrInstall(key, strdup(nexttok), &verilogdefs); + } + else if (!strcmp(nexttok, "parameter") || !strcmp(nexttok, "localparam")) { + char *paramkey = NULL; + char *paramval = NULL; + + // Pick up key = value pairs and store in current cell. Look only + // at the keyword before "=". Then set the definition as everything + // remaining in the line, excluding comments, until the end-of-statement + + while (nexttok != NULL) + { + /* Parse for parameters used in expressions. Save */ + /* parameters in the "verilogparams" hash table. */ + + SkipTok(VLOG_DELIMITERS); + if ((nexttok == NULL) || (nexttok[0] == '\0')) break; + if (!strcmp(nexttok, "=")) { + /* Pick up remainder of statement */ + while (nexttok != NULL) { + SkipTokNoNewline("X///**/X;,"); + if (nexttok == NULL) break; + if (!strcmp(nexttok, ";") || !strcmp(nexttok, ",")) break; + if (paramval == NULL) paramval = strdup(nexttok); + else { + char *paramlast; + /* Append nexttok to paramval */ + paramlast = paramval; + paramval = (char *)malloc(strlen(paramlast) + + strlen(nexttok) + 2); + sprintf(paramval, "%s %s", paramlast, nexttok); + free(paramlast); + } + } + HashPtrInstall(paramkey, paramval, &verilogparams); + free(paramval); + paramval = NULL; + if ((nexttok == NULL) || !strcmp(nexttok, ";")) break; + } + else { + if (paramkey != NULL) free(paramkey); + paramkey = strdup(nexttok); + } + } + if (paramval != NULL) free(paramval); + if (paramkey != NULL) free(paramkey); + } + else if (!strcmp(nexttok, "real") || !strcmp(nexttok, "integer")) { + fprintf(stdout, "Ignoring '%s' in module '%s' (line %d)\n", + nexttok, top->name, vlinenum); + while (strcmp(nexttok, ";")) SkipTok("X///**/X,;"); + continue; + } + else if (!strcmp(nexttok, "wire") || + !strcmp(nexttok, "assign")) { /* wire = node */ + struct netrec wb, *nb; + char *eptr, *wirename; + char is_assignment = FALSE; + char is_lhs_bundle = FALSE, is_rhs_bundle = FALSE; + char is_wire = (strcmp(nexttok, "wire")) ? FALSE : TRUE; + + // Several allowed uses of "assign": + // "assign a = b" joins two nets. + // "assign a = {b, c, ...}" creates a signal bundle from components. + // "assign {a, b ...} = {c, d, ...}" creates a bundle from another bundle. + // "assign" using any boolean arithmetic is not structural verilog. + + SkipTokNoNewline(VLOG_DELIMITERS); + if (!strcmp(nexttok, "real")) + SkipTokNoNewline(VLOG_DELIMITERS); + else if (!strcmp(nexttok, "logic")) + SkipTokNoNewline(VLOG_DELIMITERS); + + while (nexttok != NULL) { + if (!strcmp(nexttok, "=")) { + is_assignment = TRUE; + } + else if (!strcmp(nexttok, "{")) { + /* To be done: allow nested bundles */ + if (is_assignment == FALSE) is_lhs_bundle = TRUE; + else is_rhs_bundle = TRUE; + } + else if (!strcmp(nexttok, "}")) { + if (is_assignment == FALSE) is_lhs_bundle = FALSE; + else is_rhs_bundle = FALSE; + } + else if (!strcmp(nexttok, ",")) { + /* Do nothing; moving to the next wire or bundle component */ + } + else if (GetBusTok(&wb, &top->nets) == 0) { + if (is_wire) { + /* Handle bus notation (wires only) */ + if ((nb = BusHashLookup(nexttok, &top->nets)) == NULL) + nb = Net(top, nexttok); + if (nb->start == -1) { + nb->start = wb.start; + nb->end = wb.end; + } + else { + if (nb->start < wb.start) nb->start = wb.start; + if (nb->end > wb.end) nb->end = wb.end; + } + } + else { + /* "assign" to a bus subnet. This will be ignored */ + /* until this tool handles bus joining. If the */ + /* assignment is made on an undeclared wire, then */ + /* adjust the wire bounds. */ + if (nb && nb->start == -1) { + nb->start = wb.start; + nb->end = wb.end; + } + } + } + else { + if (is_assignment) { + char *qptr; + int idum; + + if (BusHashLookup(nexttok, &top->nets) != NULL) { + /* Join nets */ + /* (WIP) */ + } + else if ((qptr = strchr(nexttok, '\'')) != NULL) { + *qptr = '\0'; + if (sscanf(nexttok, "%d", &idum) == 1) { + if ((strchr(qptr + 1, 'x') == NULL) && + (strchr(qptr + 1, 'X') == NULL) && + (strchr(qptr + 1, 'z') == NULL) && + (strchr(qptr + 1, 'Z') == NULL)) { + // Power/Ground denoted by, e.g., "vdd = 1'b1". + // Only need to record the net, no further action + // needed. + } + else { + fprintf(stdout, "Assignment is not a net " + "(line %d).\n", vlinenum); + fprintf(stdout, "Module '%s' is not structural " + "verilog, making black-box.\n", top->name); + goto skip_endmodule; + } + } + *qptr = '\''; + } + else { + fprintf(stdout, "Assignment is not a net (line %d).\n", + vlinenum); + fprintf(stdout, "Module '%s' is not structural verilog," + " making black-box.\n", top->name); + goto skip_endmodule; + } + is_assignment = FALSE; + } + else if (BusHashLookup(nexttok, &top->nets) == NULL) + Net(top, nexttok); + } + do { + SkipTokNoNewline(VLOG_DELIMITERS); + } while (nexttok && !strcmp(nexttok, ";")); + } + } + else if (!strcmp(nexttok, "endmodule")) { + // No action---new module is started with next 'module' statement, + // if any. + SkipNewLine(VLOG_DELIMITERS); + } + else if (nexttok[0] == '`') { + // Ignore any other directive starting with a backtick + SkipNewLine(VLOG_DELIMITERS); + } + else if (!strcmp(nexttok, "reg") || !strcmp(nexttok, "always") || + !strcmp(nexttok, "specify") || !strcmp(nexttok, "initial")) { + fprintf(stdout, "Behavioral keyword '%s' found in source.\n", nexttok); + fprintf(stdout, "Module '%s' is not structural verilog, making " + "black-box.\n", top->name); + goto skip_endmodule; + } + else { /* module instances */ + char ignore; + int itype, arraymax, arraymin; + struct instance *thisinst; + + thisinst = AppendInstance(top, nexttok); + + SkipTokComments(VLOG_DELIMITERS); + +nextinst: + ignore = FALSE; + + // Next token must be '#(' (parameters) or an instance name + + if (!strcmp(nexttok, "#(")) { + + // Read the parameter list + SkipTokComments(VLOG_DELIMITERS); + + while (nexttok != NULL) { + char *paramname; + + if (!strcmp(nexttok, ")")) { + SkipTokComments(VLOG_DELIMITERS); + break; + } + else if (!strcmp(nexttok, ",")) { + SkipTokComments(VLOG_DELIMITERS); + continue; + } + + // We need to look for parameters of the type ".name(value)" + + else if (nexttok[0] == '.') { + paramname = strdup(nexttok + 1); + SkipTokComments(VLOG_DELIMITERS); + if (strcmp(nexttok, "(")) { + fprintf(stdout, "Error: Expecting parameter value, " + "got %s (line %d).\n", nexttok, vlinenum); + } + SkipTokComments(VLOG_DELIMITERS); + if (!strcmp(nexttok, ")")) { + fprintf(stdout, "Error: Parameter with no value found" + " (line %d).\n", vlinenum); + } + else { + HashPtrInstall(paramname, strdup(nexttok), + &thisinst->propdict); + SkipTokComments(VLOG_DELIMITERS); + if (strcmp(nexttok, ")")) { + fprintf(stdout, "Error: Expecting end of parameter " + "value, got %s (line %d).\n", nexttok, + vlinenum); + } + } + free(paramname); + } + SkipTokComments(VLOG_DELIMITERS); + } + if (!nexttok) { + fprintf(stdout, "Error: Still reading module, but got " + "end-of-file.\n"); + goto skip_endmodule; + } + } + + thisinst->instname = strdup(nexttok); + + /* fprintf(stdout, "Diagnostic: new instance is %s\n", */ + /* thisinst->instname); */ + SkipTokComments(VLOG_DELIMITERS); + + thisinst->arraystart = thisinst->arrayend = -1; + if (!strcmp(nexttok, "[")) { + // Handle instance array notation. + struct netrec wb; + if (GetBusTok(&wb, NULL) == 0) { + thisinst->arraystart = wb.start; + thisinst->arrayend = wb.end; + } + } + + if (!strcmp(nexttok, "(")) { + char savetok = (char)0; + struct portrec *new_port; + struct netrec *nb, wb; + char in_line = FALSE, *in_line_net = NULL; + char *ncomp, *nptr; + + // Read the pin list + while (nexttok != NULL) { + SkipTokComments(VLOG_DELIMITERS); + // NOTE: Deal with `ifdef et al. properly. Ignoring for now. + while (nexttok[0] == '`') { + SkipNewLine(VLOG_DELIMITERS); + SkipTokComments(VLOG_DELIMITERS); + } + if (!strcmp(nexttok, ")")) break; + else if (!strcmp(nexttok, ",")) continue; + + // We need to look for pins of the type ".name(value)" + + if (nexttok[0] != '.') { + fprintf(stdout, "Ignoring subcircuit with no pin names " + "at \"%s\" (line %d)\n", + nexttok, vlinenum); + while (nexttok != NULL) { + SkipTokComments(VLOG_DELIMITERS); + if (match(nexttok, ";")) break; + } + ignore = TRUE; + break; + } + else { + new_port = InstPort(thisinst, strdup(nexttok + 1), NULL); + SkipTokComments(VLOG_DELIMITERS); + if (strcmp(nexttok, "(")) { + fprintf(stdout, "Badly formed subcircuit pin line " + "at \"%s\" (line %d)\n", nexttok, vlinenum); + SkipNewLine(VLOG_DELIMITERS); + } + SkipTokComments(VLOG_PIN_CHECK_DELIMITERS); + if (!strcmp(nexttok, ")")) { + char localnet[100]; + // Empty parens, so create a new local node + savetok = (char)1; + sprintf(localnet, "_noconnect_%d_", localcount++); + new_port->net = strdup(localnet); + } + else { + + if (!strcmp(nexttok, "{")) { + char *in_line_net = (char *)malloc(1); + *in_line_net = '\0'; + /* In-line array---Read to "}" */ + while (nexttok) { + in_line_net = (char *)realloc(in_line_net, + strlen(in_line_net) + + strlen(nexttok) + 1); + strcat(in_line_net, nexttok); + if (!strcmp(nexttok, "}")) break; + SkipTokComments(VLOG_PIN_CHECK_DELIMITERS); + } + if (!nexttok) { + fprintf(stderr, "Unterminated net in pin %s " + "(line %d)\n", in_line_net, + vlinenum); + } + new_port->net = in_line_net; + } + else + new_port->net = strdup(nexttok); + + /* Read array information along with name; */ + /* will be parsed later */ + + SkipTokComments(VLOG_DELIMITERS); + if (!strcmp(nexttok, "[")) { + /* Check for space between name and array identifier */ + SkipTokComments(VLOG_PIN_NAME_DELIMITERS); + if (strcmp(nexttok, ")")) { + char *expnet; + expnet = (char *)malloc(strlen(new_port->net) + + strlen(nexttok) + 3); + sprintf(expnet, "%s [%s", new_port->net, nexttok); + free(new_port->net); + new_port->net = expnet; + } + SkipTokComments(VLOG_DELIMITERS); + } + if (strcmp(nexttok, ")")) { + fprintf(stdout, "Badly formed subcircuit pin line " + "at \"%s\" (line %d)\n", nexttok, vlinenum); + SkipNewLine(VLOG_DELIMITERS); + } + } + + /* Register wire if it has not been already, and if it */ + /* has been registered, check if this wire increases */ + /* the net bounds. If the net name is an in-line */ + /* vector, then process each component separately. */ + + ncomp = new_port->net; + while (isdigit(*ncomp)) ncomp++; + if (*ncomp == '{') ncomp++; + while (isspace(*ncomp)) ncomp++; + while (*ncomp != '\0') { + int is_esc = FALSE; + char saveptr; + + /* NOTE: This follows same rules in strdtok() */ + nptr = ncomp; + if (*nptr == '\\') is_esc = TRUE; + while (*nptr != ',' && *nptr != '}' && *nptr != '\0') { + if (*nptr == ' ') { + if (is_esc == TRUE) + is_esc = FALSE; + else + break; + } + nptr++; + } + saveptr = *nptr; + *nptr = '\0'; + + /* Parse ncomp as a net or bus */ + if ((nb = BusHashLookup(ncomp, &top->nets)) == NULL) + nb = Net(top, ncomp); + + GetBus(ncomp, &wb, &top->nets); + if (nb->start == -1) { + nb->start = wb.start; + nb->end = wb.end; + } + else { + if (nb->start < wb.start) nb->start = wb.start; + if (nb->end > wb.end) nb->end = wb.end; + } + + *nptr = saveptr; + if (*new_port->net != '{') + break; + + ncomp = nptr + 1; + /* Skip over any whitespace at the end of a name */ + while ((*ncomp != '\0') && (*ncomp == ' ')) + ncomp++; + while ((*ncomp != '\0') && (*nptr == ',' || *nptr == '}')) + ncomp++; + } + } + } + } + else { + fprintf(stdout, "Expected to find instance pin block but got " + "\"%s\" (line %d)\n", nexttok, vlinenum); + } + if (ignore == TRUE) continue; /* moving along. . . */ + + /* Verilog allows multiple instances of a single cell type to be */ + /* chained together in a comma-separated list. */ + + SkipTokComments(VLOG_DELIMITERS); + if (!strcmp(nexttok, ",")) { + goto nextinst; + } + + /* Otherwise, instance must end with a semicolon */ + + else if (strcmp(nexttok, ";")) { + fprintf(stdout, "Expected to find end of instance but got " + "\"%s\" (line %d)\n", nexttok, vlinenum); + } + } + continue; + +skip_endmodule: + /* There was an error, so skip to the end of the */ + /* subcircuit definition */ + + while (1) { + SkipNewLine(VLOG_DELIMITERS); + SkipTokComments(VLOG_DELIMITERS); + if (EndParseFile()) break; + if (!strcmp(nexttok, "endmodule")) { + in_module = 0; + break; + } + } + continue; + +baddevice: + fprintf(stderr, "Badly formed line in input (line %d).\n", vlinenum); + } + + /* Watch for bad ending syntax */ + + if (in_module == (char)1) { + fprintf(stderr, "Missing \"endmodule\" statement in module.\n"); + InputParseError(stderr); + } + + /* Make sure topmost cell is on stack before returning */ + PushStack(top, CellStackPtr); + + if (warnings) + fprintf(stderr, "File %s read with %d warning%s.\n", fname, + warnings, (warnings == 1) ? "" : "s"); +} + +/*----------------------------------------------*/ +/* Free memory associated with a cell */ +/*----------------------------------------------*/ + +/*----------------------------------------------*/ +/* Free memory associated with property hash */ +/*----------------------------------------------*/ + +int freeprop(struct hashlist *p) +{ + char *propval; + + propval = (char *)(p->ptr); + free(propval); + return 1; +} + +/*----------------------------------------------*/ +/* Top-level verilog module file read routine */ +/*----------------------------------------------*/ + +struct cellrec *ReadVerilogTop(char *fname, int blackbox) +{ + struct cellstack *CellStackPtr = NULL; + struct cellrec *top; + + if ((OpenParseFile(fname)) < 0) { + fprintf(stderr, "Error in Verilog file read: No file %s\n", fname); + return NULL; + } + + if (dictinit == FALSE) { + /* verilogdefs may be pre-initialized by calling VerilogDefine() */ + InitializeHashTable(&verilogdefs, TINYHASHSIZE); + dictinit = TRUE; + } + InitializeHashTable(&verilogparams, TINYHASHSIZE); + InitializeHashTable(&verilogvectors, TINYHASHSIZE); + + ReadVerilogFile(fname, &CellStackPtr, blackbox); + CloseParseFile(); + + RecurseHashTable(&verilogparams, freeprop); + HashKill(&verilogparams); + RecurseHashTable(&verilogdefs, freeprop); + HashKill(&verilogdefs); + + if (CellStackPtr == NULL) return NULL; + + top = CellStackPtr->cell; + free(CellStackPtr); + return top; +} + +/*--------------------------------------*/ +/* Wrappers for ReadVerilogTop() */ +/*--------------------------------------*/ + +struct cellrec *ReadVerilog(char *fname) +{ + return ReadVerilogTop(fname, 0); +} + +/*--------------------------------------*/ +/* Verilog file include routine */ +/*--------------------------------------*/ + +void IncludeVerilog(char *fname, struct cellstack **CellStackPtr, int blackbox) +{ + int filenum = -1; + char name[256]; + + name[0] = '\0'; + + /* If fname does not begin with "/", then assume that it is */ + /* in the same relative path as its parent. */ + + if (fname[0] != '/') { + char *ppath; + if (*CellStackPtr && ((*CellStackPtr)->cell != NULL)) { + strcpy(name, (*CellStackPtr)->cell->name); + ppath = strrchr(name, '/'); + if (ppath != NULL) + strcpy(ppath + 1, fname); + else + strcpy(name, fname); + filenum = OpenParseFile(name); + } + } + + /* If we failed the path relative to the parent, then try */ + /* the filename alone (relative to the path where netgen */ + /* was executed). */ + + if (filenum < 0) { + if ((filenum = OpenParseFile(fname)) < 0) { + if (filenum < 0) { + fprintf(stderr,"Error in Verilog file include: No file %s\n", + (*name == '\0') ? fname : name); + return; + } + } + } + ReadVerilogFile(fname, CellStackPtr, blackbox); + CloseParseFile(); +} + +/*------------------------------------------------------*/ +/* Free the cellrec structure created by ReadVerilog() */ +/*------------------------------------------------------*/ + +void FreeVerilog(struct cellrec *topcell) +{ + struct portrec *port, *dport; + struct instance *inst, *dinst; + + port = topcell->portlist; + while (port) { + if (port->name) free(port->name); + if (port->net) free(port->net); + dport = port->next; + free(port); + port = dport; + } + inst = topcell->instlist; + while (inst) { + if (inst->instname) free(inst->instname); + if (inst->cellname) free(inst->cellname); + port = inst->portlist; + while (port) { + if (port->name) free(port->name); + if (port->net) free(port->net); + dport = port->next; + free(port); + port = dport; + } + RecurseHashTable(&inst->propdict, freeprop); + HashKill(&inst->propdict); + dinst = inst->next; + free(inst); + inst = dinst; + } + + /* Delete nets hashtable */ + RecurseHashTable(&topcell->nets, freenet); + HashKill(&topcell->nets); + + /* Delete properties hashtable. */ + RecurseHashTable(&topcell->propdict, freeprop); + HashKill(&topcell->propdict); +} + + +// readverilog.c diff --git a/src/readverilog.h b/src/readverilog.h new file mode 100644 index 0000000..a50fde9 --- /dev/null +++ b/src/readverilog.h @@ -0,0 +1,118 @@ +/*----------------------------------------------------------------------*/ +/* readverilog.h -- Input for Verilog format (structural verilog only) */ +/*----------------------------------------------------------------------*/ + +/*------------------------------------------------------*/ +/* Definitions and structures */ +/*------------------------------------------------------*/ + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +/* 'X' is used here to separate the single-character delimiters */ +/* from the two-character delimiters. */ + +#define VLOG_DELIMITERS "///**/(**)#(X,;:(){}[]=" +#define VLOG_PIN_NAME_DELIMITERS "///**/(**)X()" +#define VLOG_PIN_CHECK_DELIMITERS "///**/(**)X(),{}" + +#define VERILOG_EXTENSION ".v" + +/*------------------------------------------------------*/ +/* Ports and instances are hashed for quick lookup but */ +/* also placed in a linked list so that they can be */ +/* output in the same order as the original file. */ +/*------------------------------------------------------*/ + +struct portrec { + char *name; + char *net; /* May be a {...} list */ + int direction; + struct portrec *next; +}; + +struct instance { /* Hashed by instance name */ + char *instname; + char *cellname; + int arraystart; /* -1 if not arrayed */ + int arrayend; /* -1 if not arrayed */ + struct portrec *portlist; + struct hashtable propdict; /* Instance properties */ + struct instance *next; +}; + +/*------------------------------------------------------*/ +/* Basic cell definition (hashed by name) */ +/*------------------------------------------------------*/ + +struct cellrec { + char *name; /* Cellname */ + + struct hashtable nets; /* Internal nets */ + struct hashtable propdict; /* Properties */ + + struct portrec *portlist; + struct instance *instlist; + struct instance *lastinst; /* Track last item in list */ +}; + +/*------------------------------------------------------*/ + +#define PORT_NONE 0 +#define PORT_INPUT 1 +#define PORT_OUTPUT 2 +#define PORT_INOUT 3 + +#define BUS_NONE -1 + +/*------------------------------------------------------*/ +/* Net structure (hashed by root name, if a bus) */ +/*------------------------------------------------------*/ + +struct netrec { + int start; /* start index, if a bus */ + int end; /* end index, if a bus */ +}; + +/*------------------------------------------------------*/ +/* Structure for stacking nested module definitions */ +/*------------------------------------------------------*/ + +struct cellstack { + struct cellrec *cell; + struct cellstack *next; +}; + +/*------------------------------------------------------*/ +/* Structure for nested "include" files */ +/*------------------------------------------------------*/ + +struct filestack { + FILE *file; + struct filestack *next; +}; + +/*------------------------------------------------------*/ +/* External variable declarations */ +/*------------------------------------------------------*/ + +extern int vlinenum; + +/*------------------------------------------------------*/ +/* External function declarations */ +/*------------------------------------------------------*/ + +extern void IncludeVerilog(char *, struct cellstack **, int); +extern struct cellrec *ReadVerilog(char *); +extern void FreeVerilog(struct cellrec *); +extern void VerilogDefine(char *, char *); +extern struct instance *AppendInstance(struct cellrec *cell, char *cellname); +extern struct instance *PrependInstance(struct cellrec *cell, char *cellname); +extern struct portrec *InstPort(struct instance *inst, char *portname, char *netname); +extern void *BusHashLookup(char *s, struct hashtable *table); + +// readverilog.h diff --git a/src/spice2delay.c b/src/spice2delay.c index 26975bc..734ff07 100644 --- a/src/spice2delay.c +++ b/src/spice2delay.c @@ -169,7 +169,7 @@ void add_ritem (ritemptr *ritem_list_ptr, rptr r) { } } -void process_subckt_inst(char **tokens, int num_toks, cell_io_ptr cell_io, struct hashlist **Nodehash, node_item_ptr **last_driver_ptr, int *numDrivers) { +void process_subckt_inst(char **tokens, int num_toks, cell_io_ptr cell_io, struct hashtable *Nodehash, node_item_ptr **last_driver_ptr, int *numDrivers) { nodeptr curr_node = NULL; node_item_ptr next_src_item = NULL; @@ -284,7 +284,7 @@ double spiceValtoD(char *string) { return rtrnVal; } -void process_r(char **tokens, int num_toks, struct hashlist **Nodehash, ritemptr *fullrlist) { +void process_r(char **tokens, int num_toks, struct hashtable *Nodehash, ritemptr *fullrlist) { // create ritem which captures the resistor and the connection between two nodes // for each node // if node does not exist, create it @@ -325,7 +325,7 @@ void process_r(char **tokens, int num_toks, struct hashlist **Nodehash, ritemptr add_ritem(fullrlist, curr_r); } -void process_c(char **tokens, int num_toks, struct hashlist **Nodehash) { +void process_c(char **tokens, int num_toks, struct hashtable *Nodehash) { // // change capacitance units to farads // @@ -491,14 +491,14 @@ int main (int argc, char* argv[]) { // list of all Rs for debugging and to easily free them at end ritemptr allrs = NULL; - struct hashlist *Nodehash[OBJHASHSIZE]; + struct hashtable Nodehash; /* See hash.c for these routines and variables */ hashfunc = hash; matchfunc = match; /* Initialize net hash table */ - InitializeHashTable(Nodehash); + InitializeHashTable(&Nodehash, LARGEHASHSIZE); // create first item in cell io list cell_io_ptr cell_io_list = NULL; @@ -582,19 +582,19 @@ int main (int argc, char* argv[]) { if (!(strncmp(line, "R", 1))) { printf("located resistor line\n"); - process_r(tokens, num_toks, Nodehash, &allrs); + process_r(tokens, num_toks, &Nodehash, &allrs); } else if (!(strncmp(line, "C", 1))) { printf("located capacitor line\n"); - process_c(tokens, num_toks, Nodehash); + process_c(tokens, num_toks, &Nodehash); } else if (!(strncmp(line, "X", 1))) { printf("located subckt instantiation line\n"); - printf("number of hash entries %d\n", RecurseHashTable(Nodehash, CountHashTableEntries)); - process_subckt_inst(tokens, num_toks, cell_io_list, Nodehash, &last_driver, &numDrivers); - printf("number of hash entries %d\n", RecurseHashTable(Nodehash, CountHashTableEntries)); + printf("number of hash entries %d\n", RecurseHashTable(&Nodehash, CountHashTableEntries)); + process_subckt_inst(tokens, num_toks, cell_io_list, &Nodehash, &last_driver, &numDrivers); + printf("number of hash entries %d\n", RecurseHashTable(&Nodehash, CountHashTableEntries)); } else if (!(strncmp(line, ".subckt", 7))) { printf("located subckt definition line\n"); @@ -652,11 +652,11 @@ int main (int argc, char* argv[]) { curr_node_item = curr_node_item->next; } - currnode = HashFirst(Nodehash); + currnode = HashFirst(&Nodehash); while (currnode != NULL) { printf("%s\t\t%f\t%f\n", currnode->name, currnode->nodeCap, currnode->totCapDownstream); - currnode = HashNext(Nodehash); + currnode = HashNext(&Nodehash); } @@ -722,7 +722,7 @@ int main (int argc, char* argv[]) { } printf("Number of Rs: %d\n", numRs); - currnode = HashFirst(Nodehash); + currnode = HashFirst(&Nodehash); int numNodes = 0; while (currnode != NULL) { @@ -739,7 +739,7 @@ int main (int argc, char* argv[]) { printf("Node %s had %d Rs attached\n", currnode->name, numRs); free(currnode->name); free(currnode); - currnode = HashNext(Nodehash); + currnode = HashNext(&Nodehash); } printf("Number of nodes: %d\n", numNodes); diff --git a/src/vesta.c b/src/vesta.c index 8dc3312..6baf447 100644 --- a/src/vesta.c +++ b/src/vesta.c @@ -47,7 +47,7 @@ /* 3: Report on verilog source file */ /* 4: Report on liberty file */ /*--------------------------------------------------------------*/ -/* (c) 2013-2018 Tim Edwards, Open Circuit Design */ +/* (c) 2013-2019 Tim Edwards, Open Circuit Design */ /* Released under GPL as part of the qflow package */ /*--------------------------------------------------------------*/ @@ -78,6 +78,7 @@ #include <sys/stat.h> // For mkdir() #include <math.h> // Temporary, for fabs() #include "hash.h" // For net hash table +#include "readverilog.h" #define LIB_LINE_MAX 65535 @@ -112,11 +113,12 @@ int fileCurrentLine; // Sections of verilog file #define MODULE 0 -#define IOLIST 1 -#define GATELIST 2 -#define INSTANCE 3 -#define INSTPIN 4 -#define PINCONN 5 +#define IOINLINE 1 +#define IOLIST 2 +#define GATELIST 3 +#define INSTANCE 4 +#define INSTPIN 5 +#define PINCONN 6 // Pin types (these are masks---e.g., a pin can be an INPUT and a CLOCK) #define INPUT 0x01 // The default @@ -381,7 +383,7 @@ unsigned char cleanup; /* Clean up net name syntax */ /*--------------------------------------------------------------*/ char * -advancetoken(FILE *flib, char delimiter) +advancetoken0(FILE *flib, char delimiter, char nocontline) { static char *token = NULL; static char line[LIB_LINE_MAX]; @@ -432,7 +434,7 @@ advancetoken(FILE *flib, char delimiter) /* Keep pulling stuff in if the line ends with a continuation character */ lptr = line; while (*lptr != '\n' && *lptr != '\0') { - if (*lptr == '\\') { + if ((*lptr == '\\') && (nocontline == 0)) { // To be considered a line continuation marker, there must be // only whitespace or newline between the backslash and the // end of the string. @@ -531,7 +533,7 @@ advancetoken(FILE *flib, char delimiter) // Final: Remove trailing whitespace tptr = token + strlen(token) - 1; - while (isblank(*tptr)) { + while ((tptr > token) && isblank(*tptr)) { *tptr = '\0'; tptr--; } @@ -539,6 +541,26 @@ advancetoken(FILE *flib, char delimiter) } /*--------------------------------------------------------------*/ +/* Wrapper for advancetoken0(): Default nocontline = 0 */ +/*--------------------------------------------------------------*/ + +char * +advancetoken(FILE *flib, char delimiter) +{ + return advancetoken0(flib, delimiter, 0); +} + +/*--------------------------------------------------------------*/ +/* Wrapper for advancetoken0(): nocontline = 1 for delay file */ +/*--------------------------------------------------------------*/ + +char * +advancetokennocont(FILE *flib, char delimiter) +{ + return advancetoken0(flib, delimiter, 1); +} + +/*--------------------------------------------------------------*/ /* Parse a pin name. Check if the cell has a pin of that name, */ /* and if not, add the pin to the cell, giving it default */ /* values. The pin name may contain quotes, parentheses, or */ @@ -665,8 +687,8 @@ double *table_collapse(lutableptr tableptr, double load) // Interpolate value at cap load for each transition value - vlow = *(tableptr->values + i * tableptr->size1 + (j - 1)); - vhigh = *(tableptr->values + i * tableptr->size1 + j); + vlow = *(tableptr->values + i * tableptr->size2 + (j - 1)); + vhigh = *(tableptr->values + i * tableptr->size2 + j); *(vector + i) = vlow + (vhigh - vlow) * cfrac; } return vector; @@ -855,12 +877,14 @@ double calc_prop_delay(double trans, connptr testconn, short sense, char minmax) if (sense != SENSE_NEGATIVE) { if (testconn->prvector) propdelayr = vector_get_value(testpin->propdelr, testconn->prvector, trans); + if (propdelayr < 0.0) propdelayr = 0.0; if (sense == SENSE_POSITIVE) return propdelayr; } if (sense != SENSE_POSITIVE) { if (testconn->pfvector) propdelayf = vector_get_value(testpin->propdelf, testconn->pfvector, trans); + if (propdelayf < 0.0) propdelayf = 0.0; if (sense == SENSE_NEGATIVE) return propdelayf; } @@ -891,12 +915,14 @@ double calc_transition(double trans, connptr testconn, short sense, char minmax) if (sense != SENSE_NEGATIVE) { if (testconn->trvector) transr = vector_get_value(testpin->transr, testconn->trvector, trans); + if (transr < 0.0) transr = 0.0; if (sense == SENSE_POSITIVE) return transr; } if (sense != SENSE_POSITIVE) { if (testconn->tfvector) transf = vector_get_value(testpin->transf, testconn->tfvector, trans); + if (transf < 0.0) transf = 0.0; if (sense == SENSE_NEGATIVE) return transf; } @@ -992,6 +1018,11 @@ double calc_setup_time(double trans, pinptr testpin, double clktrans, short sens /* clock is found, then return NULL---these endpoints are */ /* asynchronously clocked. */ /* */ +/* Modified 5/23/2019: Cascaded clocks should be traced */ +/* through a flop. i.e., if a clock is found to come from a */ +/* DFF output, then add the clock-to-Q delay and keep tracing */ +/* from the DFF clock. */ +/* */ /* If mode == 2, return TRUE if the search ended on a */ /* connection found in a mode 1 search. Otherwise, return */ /* FALSE. */ @@ -1035,13 +1066,34 @@ find_clock_source(connptr testlink, ddataptr *clocklist, btptr btrace, short dir iupstream = driver->refinst; if (iupstream == NULL) goto makehead; /* Not supposed to happen? */ - if (driver->refpin->type & DFFOUT) goto makehead; /* Reached a flop output */ - if (driver->refpin->type & LATCHOUT) goto makehead; /* Reached a latch output */ - for (iinput = iupstream->in_connects; iinput; iinput = iinput->next) { - newdir = calc_dir(iinput->refpin, dir); - result = find_clock_source(iinput, clocklist, newclock, newdir, mode); - if (result == (unsigned char)1) return result; + /* If a flop or latch output is reached, add the clock-to-output delay */ + /* and continue tracing from the DFF clock or LATCH input. */ + + if (driver->refpin->type & DFFOUT) { + for (iinput = driver->refinst->in_connects; iinput; iinput = iinput->next) { + if (iinput->refpin->type & DFFCLK) { + newdir = calc_dir(iinput->refpin, dir); + result = find_clock_source(iinput, clocklist, newclock, newdir, mode); + if (result == (unsigned char)1) return result; + } + } + } + else if (driver->refpin->type & LATCHOUT) { + for (iinput = driver->refinst->in_connects; iinput; iinput = iinput->next) { + if (iinput->refpin->type & LATCHIN) { + newdir = calc_dir(iinput->refpin, dir); + result = find_clock_source(iinput, clocklist, newclock, newdir, mode); + if (result == (unsigned char)1) return result; + } + } + } + else { + for (iinput = iupstream->in_connects; iinput; iinput = iinput->next) { + newdir = calc_dir(iinput->refpin, dir); + result = find_clock_source(iinput, clocklist, newclock, newdir, mode); + if (result == (unsigned char)1) return result; + } } return (unsigned char)0; @@ -1787,16 +1839,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) newtable->invert = 1; } else if (!strcasecmp(token, "index_1")) { + char dnum = ','; + token = advancetoken(flib, 0); // Open parens token = advancetoken(flib, 0); // Quote if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + iptr = token; + + // Check if table is space or comma separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (newtable->invert == 1) { // Count entries - iptr = token; newtable->size2 = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size2++; } @@ -1810,7 +1870,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) else newtable->idx2.caps[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size2++; sscanf(iptr, "%lg", @@ -1824,9 +1884,8 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) } else { // newtable->invert = 0 // Count entries - iptr = token; newtable->size1 = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size1++; } @@ -1836,7 +1895,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) iptr = token; sscanf(iptr, "%lg", &newtable->idx1.times[0]); newtable->idx1.times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size1++; sscanf(iptr, "%lg", @@ -1849,16 +1908,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) token = advancetoken(flib, ';'); // EOL semicolon } else if (!strcasecmp(token, "index_2")) { + char dnum = ','; + token = advancetoken(flib, 0); // Open parens token = advancetoken(flib, 0); // Quote if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + iptr = token; + + // Check if table is space or comma separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + if (newtable->invert == 0) { // Count entries - iptr = token; newtable->size2 = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size2++; } @@ -1871,7 +1938,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) newtable->idx2.caps[0] *= cap_unit; else newtable->idx2.cons[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size2++; sscanf(iptr, "%lg", @@ -1885,9 +1952,8 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) } else { // newtable->invert == 1 // Count entries - iptr = token; newtable->size1 = 1; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size1++; } @@ -1897,7 +1963,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) iptr = token; sscanf(iptr, "%lg", &newtable->idx1.times[0]); newtable->idx1.times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; newtable->size1++; sscanf(iptr, "%lg", @@ -2487,6 +2553,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) while (*token != '}') { token = advancetoken(flib, 0); if (!strcasecmp(token, "index_1")) { + char dnum = ','; // Local index values override those in the template @@ -2495,18 +2562,24 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + iptr = token; + + // Check if table is space or comma separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + //------------------------- if (reftable && (reftable->invert == 1)) { // Entries had better match the ref table - iptr = token; i = 0; sscanf(iptr, "%lg", &tableptr->idx2.caps[0]); if (tableptr->var2 == OUTPUT_CAP) tableptr->idx2.caps[0] *= cap_unit; else tableptr->idx2.cons[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &tableptr->idx2.caps[i]); @@ -2521,7 +2594,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) i = 0; sscanf(iptr, "%lg", &tableptr->idx1.times[0]); tableptr->idx1.times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &tableptr->idx1.times[i]); @@ -2533,6 +2606,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) token = advancetoken(flib, ';'); // EOL semicolon } else if (!strcasecmp(token, "index_2")) { + char dnum = ','; // Local index values override those in the template @@ -2541,15 +2615,21 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) if (!strcmp(token, "\"")) token = advancetoken(flib, '\"'); + iptr = token; + + // Check if table is space or comma separated + if (strchr(iptr, dnum) == NULL) + if (strchr(iptr, ' ') != NULL) + dnum = ' '; + //------------------------- if (reftable && (reftable->invert == 1)) { // Entries had better match the ref table - iptr = token; i = 0; sscanf(iptr, "%lg", &tableptr->idx1.times[0]); tableptr->idx1.times[0] *= time_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &tableptr->idx1.times[i]); @@ -2561,7 +2641,7 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) i = 0; sscanf(iptr, "%lg", &tableptr->idx2.caps[0]); tableptr->idx2.caps[0] *= cap_unit; - while ((iptr = strchr(iptr, ',')) != NULL) { + while ((iptr = strchr(iptr, dnum)) != NULL) { iptr++; i++; sscanf(iptr, "%lg", &tableptr->idx2.caps[i]); @@ -2686,244 +2766,166 @@ libertyRead(FILE *flib, lutable **tablelist, cell **celllist) /*--------------------------------------------------------------*/ void -verilogRead(FILE *fsrc, cell *cells, net **netlist, instance **instlist, - connect **inputlist, connect **outputlist, struct hashlist **Nethash) +verilogRead(char *filename, cell *cells, net **netlist, instance **instlist, + connect **inputlist, connect **outputlist, struct hashtable *Nethash) { - char *token; - char *modname = NULL; - int section = MODULE; + struct portrec *port; + struct instance *inst; + struct cellrec *topcell; + struct netrec *net; + connptr newconn, testconn; instptr newinst; + pinptr testpin; netptr newnet, testnet; cellptr testcell; - connptr newconn, testconn; - pinptr testpin; - int vstart, vend, vtarget, isinput; - - /* Read tokens off of the line */ - token = advancetoken(fsrc, 0); - - while (token != NULL) { - - switch (section) { - case MODULE: - if (!strcasecmp(token, "module")) { - token = advancetoken(fsrc, 0); - fprintf(stderr, "Parsing module \"%s\"\n", token); + int vstart, vtarget; - token = advancetoken(fsrc, 0); - if (strcmp(token, "(")) - fprintf(stderr, "Module not followed by pin list\n"); - else - token = advancetoken(fsrc, ')'); - token = advancetoken(fsrc, ';'); // Get end-of-line + /* Get verilog netlist structure using routines in readverilog.c */ + topcell = ReadVerilog(filename); + if (topcell && topcell->name) { + fprintf(stdout, "Parsing module \"%s\"\n", topcell->name); + } - // Ignore the pin list, go straight to the input/output declarations - section = IOLIST; - } - break; + /* Build local connection lists from returned netlist structure */ - case IOLIST: - if (!strcasecmp(token, "input") || !strcasecmp(token, "output")) { + for (port = topcell->portlist; port; port = port->next) { + testconn = NULL; - testconn = NULL; - vstart = vend = 0; + // Create a net entry for the input or output, add to the list of nets - if (!strcasecmp(token, "input")) { - isinput = 1; - } - else { // output - isinput = 0; - } + net = HashLookup(port->name, &topcell->nets); + if (net->start == 0 && net->end == 0) { + newnet = create_net(netlist); + newnet->name = strdup(port->name); + HashPtrInstall(newnet->name, newnet, Nethash); - token = advancetoken(fsrc, 0); - if (*token == '[') { - sscanf(token + 1, "%d", &vstart); - token = advancetoken(fsrc, ':'); - token = advancetoken(fsrc, ']'); // Read to end of vector - sscanf(token, "%d", &vend); - token = advancetoken(fsrc, 0); // Read signal name - } + testconn = (connptr)malloc(sizeof(connect)); + testconn->refnet = newnet; + testconn->refpin = NULL; // No associated pin + testconn->refinst = NULL; // No associated instance + testconn->tag = NULL; + testconn->metric = -1.0; + testconn->visited = (unsigned char)0; + testconn->prvector = NULL; + testconn->pfvector = NULL; + testconn->trvector = NULL; + testconn->tfvector = NULL; + + if (port->direction == PORT_INPUT) { // driver (input) + testconn->next = *inputlist; + *inputlist = testconn; + } + else { // receiver (output) + testconn->next = *outputlist; + *outputlist = testconn; + } + } + else { + vtarget = net->end + ((net->start < net->end) ? 1 : -1); + vstart = net->start; + while (vstart != vtarget) { + newnet = create_net(netlist); + newnet->name = (char *)malloc(strlen(port->name) + 6); + sprintf(newnet->name, "%s[%d]", port->name, vstart); + HashPtrInstall(newnet->name, newnet, Nethash); + + vstart += (vtarget > net->end) ? 1 : -1; + + testconn = (connptr)malloc(sizeof(connect)); + testconn->refnet = newnet; + testconn->refpin = NULL; // No associated pin + testconn->refinst = NULL; // No associated instance + testconn->tag = NULL; + testconn->metric = -1.0; + testconn->visited = (unsigned char)0; + testconn->prvector = NULL; + testconn->pfvector = NULL; + testconn->trvector = NULL; + testconn->tfvector = NULL; + + if (port->direction == PORT_INPUT) { // driver (input) + testconn->next = *inputlist; + *inputlist = testconn; + } + else { // receiver (output) + testconn->next = *outputlist; + *outputlist = testconn; + } + } + } + } - // Create a net entry for the input or output, add to the list of nets - - if (vstart == 0 && vend == 0) { - newnet = create_net(netlist); - newnet->name = strdup(token); - HashPtrInstall(newnet->name, newnet, Nethash); - - testconn = (connptr)malloc(sizeof(connect)); - testconn->refnet = newnet; - testconn->refpin = NULL; // No associated pin - testconn->refinst = NULL; // No associated instance - testconn->tag = NULL; - testconn->metric = -1.0; - testconn->visited = (unsigned char)0; - testconn->prvector = NULL; - testconn->pfvector = NULL; - testconn->trvector = NULL; - testconn->tfvector = NULL; - - if (isinput) { // driver (input) - testconn->next = *inputlist; - *inputlist = testconn; - } - else { // receiver (output) - testconn->next = *outputlist; - *outputlist = testconn; - } - } - else { - vtarget = vend + ((vstart < vend) ? 1 : -1); - while (vstart != vtarget) { - newnet = create_net(netlist); - newnet->name = (char *)malloc(strlen(token) + 6); - sprintf(newnet->name, "%s[%d]", token, vstart); - HashPtrInstall(newnet->name, newnet, Nethash); - - vstart += (vtarget > vend) ? 1 : -1; - - testconn = (connptr)malloc(sizeof(connect)); - testconn->refnet = newnet; - testconn->refpin = NULL; // No associated pin - testconn->refinst = NULL; // No associated instance - testconn->tag = NULL; - testconn->metric = -1.0; - testconn->visited = (unsigned char)0; - testconn->prvector = NULL; - testconn->pfvector = NULL; - testconn->trvector = NULL; - testconn->tfvector = NULL; - - if (isinput) { // driver (input) - testconn->next = *inputlist; - *inputlist = testconn; - } - else { // receiver (output) - testconn->next = *outputlist; - *outputlist = testconn; - } - } - } - token = advancetoken(fsrc, ';'); // Get rest of input/output entry - break; - } + for (inst = topcell->instlist; inst; inst = inst->next) { + for (testcell = cells; testcell; testcell = testcell->next) + if (!strcasecmp(testcell->name, inst->cellname)) + break; - /* Drop through on anything that isn't an input, output, or blank line */ + // NOTE: testcell may be NULL for non-functional cells like + // filler cells which have no I/O and so have no timing. Only + // report cells that are relevant to timing and do not show up + // in the liberty database (portlist is non-NULL). - case GATELIST: + if ((testcell == NULL) && (inst->portlist != NULL)) { + fprintf(stderr, "Cell \"%s\" was not in the liberty database!\n", + inst->cellname); + continue; + } - if (!strcasecmp(token, "endmodule")) { - section = MODULE; + newinst = (instptr)malloc(sizeof(instance)); + newinst->next = *instlist; + *instlist = newinst; + newinst->refcell = testcell; + newinst->in_connects = NULL; + newinst->out_connects = NULL; + newinst->name = strdup(inst->instname); + + for (port = inst->portlist; port; port = port->next) { + newconn = (connptr)malloc(sizeof(connect)); + for (testpin = testcell->pins; testpin; testpin = testpin->next) { + if (!strcmp(testpin->name, port->name)) break; - } - - /* Confirm that the token is a known cell, and continue parsing line if so */ - /* Otherwise, parse to semicolon line end and continue */ - - for (testcell = cells; testcell; testcell = testcell->next) - if (!strcasecmp(testcell->name, token)) - break; - - if (testcell != NULL) { - section = INSTANCE; - newinst = (instptr)malloc(sizeof(instance)); - newinst->next = *instlist; - *instlist = newinst; - newinst->refcell = testcell; - newinst->in_connects = NULL; - newinst->out_connects = NULL; + } + // Sanity check + if (testpin == NULL) { + fprintf(stderr, "No such pin \"%s\" in cell \"%s\"!\n", + port->name, testcell->name); + } + else { + if (testpin->type & OUTPUT) { + newconn->next = newinst->out_connects; + newinst->out_connects = newconn; } else { - /* Ignore all wire and assign statements */ - /* Qflow does not generate these, but other */ - /* synthesis tools may. */ - - if (!strcasecmp(token, "assign") && (verbose > 1)) { - fprintf(stdout, "Wire assignments are not handled!\n"); - } - else if (strcasecmp(token, "wire") && (verbose > 1)) { - fprintf(stdout, "Unknown cell \"%s\" instanced.\n", - token); - } - token = advancetoken(fsrc, ';'); // Get rest of entry, and ignore + newconn->next = newinst->in_connects; + newinst->in_connects = newconn; } - break; - - case INSTANCE: - newinst->name = strdup(token); - token = advancetoken(fsrc, '('); // Find beginning of pin list - section = INSTPIN; - break; - - case INSTPIN: - if (*token == '.') { - newconn = (connptr)malloc(sizeof(connect)); - // Pin name is in (token + 1) - for (testpin = testcell->pins; testpin; testpin = testpin->next) { - if (!strcmp(testpin->name, token + 1)) - break; - } - // Sanity check - if (testpin == NULL) { - fprintf(stderr, "No such pin \"%s\" in cell \"%s\"!\n", - token + 1, testcell->name); - } - else { - if (testpin->type & OUTPUT) { - newconn->next = newinst->out_connects; - newinst->out_connects = newconn; - } - else { - newconn->next = newinst->in_connects; - newinst->in_connects = newconn; - } - } - newconn->refinst = newinst; - newconn->refpin = testpin; - newconn->refnet = NULL; - newconn->tag = NULL; - newconn->metric = -1.0; - newconn->visited = (unsigned char)0; - newconn->prvector = NULL; - newconn->pfvector = NULL; - newconn->trvector = NULL; - newconn->tfvector = NULL; - token = advancetoken(fsrc, '('); // Read to beginning of pin name - section = PINCONN; - } - else if (*token == ';') { - // End of instance record - section = GATELIST; - } - else if (*token != ',' && *token != ')') { - fprintf(stderr, "Unexpected entry in instance pin connection list!\n"); - token = advancetoken(fsrc, ';'); // Read to end-of-line - section = GATELIST; - } - break; - - case PINCONN: - // Token is net name - testnet = (netptr)HashLookup(token, Nethash); - if (testnet == NULL) { - // This is a new net, and we need to record it - newnet = create_net(netlist); - newnet->name = strdup(token); - HashPtrInstall(newnet->name, newnet, Nethash); - newconn->refnet = newnet; - } - else - newconn->refnet = testnet; - section = INSTPIN; - break; - } - if (section == PINCONN) - token = advancetoken(fsrc, ')'); // Name token parsing - else - token = advancetoken(fsrc, 0); + } + newconn->refinst = newinst; + newconn->refpin = testpin; + newconn->refnet = NULL; + newconn->tag = NULL; + newconn->metric = -1.0; + newconn->visited = (unsigned char)0; + newconn->prvector = NULL; + newconn->pfvector = NULL; + newconn->trvector = NULL; + newconn->tfvector = NULL; + + testnet = (netptr)HashLookup(port->net, Nethash); + if (testnet == NULL) { + // This is a new net, and we need to record it + newnet = create_net(netlist); + newnet->name = strdup(port->net); + HashPtrInstall(newnet->name, newnet, Nethash); + newconn->refnet = newnet; + } + else + newconn->refnet = testnet; + } } + FreeVerilog(topcell); // All structures transferred } /*--------------------------------------------------------------*/ @@ -3012,6 +3014,8 @@ int assign_net_types(netptr netlist, connlistptr *clockedlist) numterms = 0; for (testnet = netlist; testnet; testnet = testnet->next) { + /* Nets with no fanout are by definition module outputs */ + if (testnet->fanout == 0) testnet->type |= OUTTERM; for (i = 0; i < testnet->fanout; i++) { testrcvr = testnet->receivers[i]; testpin = testrcvr->refpin; @@ -3139,17 +3143,18 @@ compdelay(ddataptr *a, ddataptr *b) } void -delayRead(FILE *fdly, struct hashlist **Nethash) +delayRead(FILE *fdly, struct hashtable *Nethash) { char c[128]; char d[128]; char *token; + char *result; + char *tokencopy = NULL; netptr newnet, testnet; connptr testconn; pinptr testpin; int i; - char *result; int numRxers; if (debug == 1) @@ -3159,7 +3164,7 @@ delayRead(FILE *fdly, struct hashlist **Nethash) /* be a standard delimiter, breaking up certain yosys-generated */ /* net names. */ - token = advancetoken(fdly, '\n'); + token = advancetokennocont(fdly, '\n'); result = token; while (token != NULL) { @@ -3175,28 +3180,54 @@ delayRead(FILE *fdly, struct hashlist **Nethash) if ((testnet == NULL) && cleanup) { char *mchr, *dchr; - if ((mchr = strrchr(token, '<')) != NULL) { - if ((dchr = strrchr(token, '>')) != NULL) { - if (mchr < dchr) { - *mchr = '['; - *dchr = ']'; + /* Handle the insane backslash-escape names in verilog. + * To make these compatible with SPICE, qflow opts to + * replace the ending space character with another + * backslash. The 2nd backslash has to be replaced by + * the original space character to match the original + * verilog net name. + */ + + if (*token == '\\') { + if ((mchr = strchr(token + 1, '\\')) != NULL) { + dchr = strchr(token + 1, ' '); + if ((dchr == NULL) || (dchr > mchr)) *mchr = ' '; + } + } + testnet = (netptr)HashLookup(token, Nethash); + + /* Other, legacy stuff. */ + if (testnet == NULL) { + tokencopy = strdup(token); + if ((mchr = strrchr(tokencopy, '<')) != NULL) { + if ((dchr = strrchr(tokencopy, '>')) != NULL) { + if (mchr < dchr) { + *mchr = '['; + *dchr = ']'; + } } } + testnet = (netptr)HashLookup(tokencopy, Nethash); } - for (mchr = token; *mchr != '\0'; mchr++) { - if ((*mchr == ':') || (*mchr == '.') || (*mchr == '$') + + if (testnet == NULL) { + for (mchr = tokencopy; *mchr != '\0'; mchr++) { + if ((*mchr == ':') || (*mchr == '.') || (*mchr == '$') || (*mchr == '<') || (*mchr == '>')) - *mchr = '_'; + *mchr = '_'; + } + testnet = (netptr)HashLookup(tokencopy, Nethash); } - testnet = (netptr)HashLookup(token, Nethash); + if (testnet == NULL) { - for (mchr = token; *mchr != '\0'; mchr++) { + for (mchr = tokencopy; *mchr != '\0'; mchr++) { if ((*mchr == '[') || (*mchr == ']')) *mchr = '_'; } + testnet = (netptr)HashLookup(tokencopy, Nethash); } - testnet = (netptr)HashLookup(token, Nethash); } + if (tokencopy != NULL) free(tokencopy); if (testnet == NULL) { fprintf(stderr, "ERROR: Net %s not found in hash table\n", token); @@ -3270,18 +3301,20 @@ delayRead(FILE *fdly, struct hashlist **Nethash) if (result == NULL) break; numRxers += 1; } - if (result == NULL) break; + if (result == NULL) break; if (numRxers != testnet->fanout) { - fprintf(stderr, "ERROR: Net %s only had %d receivers in delay file, " + if (numRxers != 1 || testnet->fanout > 0 || testnet->type != OUTTERM) + fprintf(stderr, "ERROR: Net %s had %d receiver%s in delay file, " " but expected a fanout of %d\n", testnet->name, - numRxers, testnet->fanout); + numRxers, (numRxers == 1) ? "" : "s", + testnet->fanout); } - token = advancetoken(fdly, '\n'); + token = advancetokennocont(fdly, '\n'); } if (result == NULL) { - fprintf(stderr, "ERROR: Unexpected end-of-file while reading delay file.\n"); + fprintf(stderr, "ERROR: Unexpected end-of-file while reading delay file.\n"); } } @@ -3702,7 +3735,7 @@ main(int objc, char *argv[]) double slack; // Net name hash table - struct hashlist *Nethash[OBJHASHSIZE]; + struct hashtable Nethash; verbose = 0; exhaustive = 0; @@ -3803,6 +3836,7 @@ main(int objc, char *argv[]) fprintf(stderr, "Cannot open %s for reading\n", argv[firstarg]); exit (1); } + fclose(fsrc); /*------------------------------------------------------------------*/ /* Generate one table template for the "scalar" case */ @@ -3885,11 +3919,12 @@ main(int objc, char *argv[]) matchfunc = match; /* Initialize net hash table */ - InitializeHashTable(Nethash); + InitializeHashTable(&Nethash, LARGEHASHSIZE); fileCurrentLine = 0; - verilogRead(fsrc, cells, &netlist, &instlist, &inputlist, &outputlist, Nethash); + verilogRead(argv[firstarg], cells, &netlist, &instlist, &inputlist, &outputlist, + &Nethash); if (delayfile != NULL) { fdly = fopen(delayfile, "r"); @@ -3903,8 +3938,7 @@ main(int objc, char *argv[]) fdly = NULL; fflush(stdout); - fprintf(stdout, "Verilog netlist read: Processed %d lines.\n", fileCurrentLine); - if (fsrc != NULL) fclose(fsrc); + fprintf(stdout, "Verilog netlist read: Processed %d lines.\n", vlinenum); /*--------------------------------------------------*/ /* Debug: Print summary of verilog source */ @@ -3963,12 +3997,12 @@ main(int objc, char *argv[]) /*--------------------------------------------------*/ if (fdly != NULL) { - delayRead(fdly, Nethash); + delayRead(fdly, &Nethash); fclose(fdly); } /* Hash table no longer needed */ - HashKill(Nethash); + HashKill(&Nethash); computeLoads(netlist, instlist, outLoad); @@ -4101,7 +4135,7 @@ main(int objc, char *argv[]) if (fsum) fprintf(fsum, "Design meets timing requirements.\n"); } } - else if (orderedpaths[0] != NULL) { + else if ((numpaths > 0) && (orderedpaths[0] != NULL)) { fprintf(stdout, "Computed maximum clock frequency (zero margin) = %g MHz\n", (1.0E6 / orderedpaths[0]->delay)); if (fsum) fprintf(fsum, "Computed maximum clock frequency " @@ -4211,6 +4245,7 @@ main(int objc, char *argv[]) if (longFormat) print_path(testddata->backtrace, stdout); if (fsum) print_path(testddata->backtrace, fsum); + /* Print skew and hold unless destination is a pin */ if (testddata->backtrace->receiver->refinst != NULL) { if (longFormat) { fprintf(stdout, " clock skew at destination = %g\n", testddata->skew); diff --git a/src/vlog2Cel.c b/src/vlog2Cel.c new file mode 100644 index 0000000..3829b88 --- /dev/null +++ b/src/vlog2Cel.c @@ -0,0 +1,571 @@ +//---------------------------------------------------------------- +// vlog2Cel +//---------------------------------------------------------------- +// Generate a .cel file for the GrayWolf placement tool from a +// verilog source, plus LEF files for the technology, standard +// cells, and (if present) hard macros. +// +// Revision 0, 2018-12-1: First release by R. Timothy Edwards. +// +// This program is written in ISO C99. +//---------------------------------------------------------------- + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> /* for getopt() */ +#include <math.h> +#include <ctype.h> +#include <float.h> + +#include "hash.h" +#include "readverilog.h" +#include "readlef.h" + +int write_output(struct cellrec *, int, char *); +void helpmessage(FILE *outf); + +char *VddNet = NULL; +char *GndNet = NULL; + +struct hashtable LEFhash; + +/*--------------------------------------------------------------*/ + +int main (int argc, char *argv[]) +{ + int i, units, result; + + char *outfile = NULL; + char *vlogname = NULL; + struct cellrec *topcell; + + VddNet = strdup("VDD"); + GndNet = strdup("VSS"); + + InitializeHashTable(&LEFhash, SMALLHASHSIZE); + units = 100; /* Default value is centimicrons */ + + while ((i = getopt(argc, argv, "hHu:l:o:")) != EOF) { + switch (i) { + case 'h': + case 'H': + helpmessage(stdout); + return 0; + case 'u': + if (sscanf(optarg, "%d", &units) != 1) { + fprintf(stderr, "Cannot read integer units from \"%s\"\n", optarg); + } + break; + case 'l': + LefRead(optarg); /* Can be called multiple times */ + break; + case 'o': + outfile = strdup(optarg); + break; + default: + fprintf(stderr, "Bad switch \"%c\"\n", (char)i); + helpmessage(stderr); + return 1; + } + } + + if (optind < argc) { + vlogname = strdup(argv[optind]); + optind++; + } + else { + fprintf(stderr, "Couldn't find a filename as input\n"); + helpmessage(stderr); + return 1; + } + optind++; + + /* If any LEF files were read, hash the GateInfo list */ + if (GateInfo != NULL) { + GATE gate; + for (gate = GateInfo; gate; gate = gate->next) { + HashPtrInstall(gate->gatename, gate, &LEFhash); + } + } + + topcell = ReadVerilog(vlogname); + result = write_output(topcell, units, outfile); + return result; +} + +/*--------------------------------------------------------------*/ +/* write_output: Generate the .cel file output. */ +/* */ +/* ARGS: */ +/* RETURNS: 0 on success, 1 on error */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +int write_output(struct cellrec *topcell, int units, char *outfile) +{ + FILE *outfptr = stdout; + int result = 0; + + struct netrec *net; + struct portrec *port; + struct instance *inst; + + GATE gateginfo; + + int i, j, layers, feedx, cellidx, kidx; + int llx, lly, cllx, clly, curx, cury; + int urx, ury, width, height, px, py; + int *pitchx, *pitchy; + int lvert = -1; + int arrayidx; + char *netsptr; + + if (outfile != NULL) { + outfptr = fopen(outfile, "w"); + if (outfptr == NULL) { + fprintf(stderr, "Error: Failed to open file %s for output\n", + outfile); + return 1; + } + } + + /* Count route layers (just need a maximum for memory allocation) */ + layers = LefGetMaxRouteLayer(); + + pitchx = (int *)calloc((layers + 1), sizeof(int)); + pitchy = (int *)calloc((layers + 1), sizeof(int)); + + /* Pull pitch information from LEF database */ + + for (i = 0; i <= layers; i++) { + pitchx[i] = (int)(LefGetRoutePitchX(i) * (double)units + 0.5); + pitchy[i] = (int)(LefGetRoutePitchY(i) * (double)units + 0.5); + } + + /* Find first vertical route that is not route 0 */ + for (i = 1; i <= layers; i++) { + if (LefGetRouteOrientation(i) == 0) { + lvert = i; + break; + } + } + + /* If X pitch on layer lvert is zero, then infinite loops happen */ + if (lvert < 0) { + fprintf(stderr, "Error: Failed to get layer information; cannot continue.\n"); + return 1; + } + else if (pitchx[lvert] <= 0) { + fprintf(stderr, "Error: Bad value %d for X pitch on vertical route" + " layer %d; cannot continue.\n", + pitchx[lvert], lvert); + return 1; + } + + /* Write instances in the order of the input file */ + + cellidx = 1; + kidx = 1; + for (inst = topcell->instlist; inst; inst = inst->next) { + if (inst->cellname) + gateginfo = HashLookup(inst->cellname, &LEFhash); + else + gateginfo = NULL; + + if (gateginfo == NULL) { + fprintf(stderr, "Error: Cell \"%s\" of instance \"%s\" not found" + " in LEF databases!\n", inst->cellname, inst->instname); + result = 1; // Set error result but continue output. + continue; + } + + width = (int)(gateginfo->width * (double)units + 0.5); + height = (int)(gateginfo->height * (double)units + 0.5); + + cllx = -(width >> 1); + clly = -(height >> 1); + curx = width + cllx; + cury = height + clly; + + arrayidx = inst->arraystart; + while (1) { + + if (arrayidx != -1) + fprintf(outfptr, "cell %d %s:%s[%d]\n", cellidx, inst->cellname, + inst->instname, arrayidx); + else + fprintf(outfptr, "cell %d %s:%s\n", cellidx, inst->cellname, + inst->instname); + fprintf(outfptr, "left %d right %d bottom %d top %d\n", + cllx, curx, clly, cury); + cellidx++; + + /* Generate implicit feedthroughs to satisfy global routing, as */ + /* many as will fit on the vertical track pitch. */ + + feedx = cllx + pitchx[lvert] / 2 + pitchx[lvert]; + kidx = 1; + while (feedx < curx) { + fprintf(outfptr, "pin name twfeed%d signal TW_PASS_THRU layer %d %d %d\n", + kidx, lvert, feedx, clly); + fprintf(outfptr, " equiv name twfeed%d layer %d %d %d\n", + kidx, lvert, feedx, cury); + feedx += pitchx[lvert]; + kidx++; + } + + /* Write each port and net connection */ + for (port = inst->portlist; port; port = port->next) { + + /* Any one of these can be a bus: the port, the net, */ + /* or the instance. */ + + int is_port_bus = FALSE; + int is_net_bus = FALSE; + int is_inst_bus = (arrayidx == -1) ? FALSE : TRUE; + + /* Bus (array) cases: + * instance port net + * 1) single single single : simple case + * 2) single single array : not legal unless bus is 1-bit + * 3) single array single : copy net to each port bit + * 4) single array array : bit-wise matching net to port + * 5) array single single : copy net to each instance + * 6) array single array : bit-wise matching net to instance + * 7) array array single : copy net in both dimensions + * 8) array array array : bit-wise matching net to port, then + * copy to each instance. + */ + + /* Verilog backslash-escaped names have spaces that */ + /* break pretty much every other format, so replace */ + /* the space with the (much more sensible) second */ + /* backslash. This can be detected and changed */ + /* back by programs converting the syntax back into */ + /* verilog. */ + + netsptr = port->net; + if (*port->net == '\\') { + netsptr = strchr(port->net, ' '); + if (netsptr != NULL) *netsptr = '\\'; + } + + /* Find the port name in the gate pin list */ + for (j = 0; j < gateginfo->nodes; j++) { + if (!strcmp(port->name, gateginfo->node[j])) break; + } + if (j == gateginfo->nodes) { + /* Is this a bus? */ + for (j = 0; j < gateginfo->nodes; j++) { + char *delim = strrchr(gateginfo->node[j], '['); + if (delim != NULL) { + *delim = '\0'; + if (!strcmp(port->name, gateginfo->node[j])) + is_port_bus = TRUE; + *delim = '['; + if (is_port_bus) break; + } + } + } + + /* Check if the net itself is an array */ + net = HashLookup(port->net, &topcell->nets); + if (net && (net->start != -1)) { + char *sptr, *dptr, *cptr; + + is_net_bus = TRUE; + + /* However, if net name is a 1-bit bus subnet, then */ + /* it is not considered to be a bus. Note that */ + /* brackets inside a verilog backslash-escaped name */ + /* are not array indicators. */ + + dptr = strchr(netsptr, '['); + if (dptr) { + cptr = strchr(dptr + 1, ':'); + if (!cptr) { + is_net_bus = FALSE; + } + } + } + + if (j == gateginfo->nodes) { + fprintf(stderr, "Error: Pin \"%s\" not found in LEF macro \"%s\"!\n", + port->name, gateginfo->gatename); + result = 1; // Set error result but continue output + } + else if (is_net_bus == FALSE) { + /* Pull pin position from first rectangle in taps list. This */ + /* does not have to be accurate; just representative. */ + int bufidx; + char *sigptr; + DSEG tap = gateginfo->taps[j]; + + /* If LEF file failed to specify pin geometry, then use cell center */ + if (tap == NULL) { + px = cllx; + py = clly; + } + else { + llx = (int)(tap->x1 * (double)units + 0.5); + lly = (int)(tap->y1 * (double)units + 0.5); + urx = (int)(tap->x2 * (double)units + 0.5); + ury = (int)(tap->y2 * (double)units + 0.5); + px = cllx + ((llx + urx) / 2); + py = clly + ((lly + ury) / 2); + } + + if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) && + ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) && + (gateginfo->direction[j] == PORT_CLASS_INPUT)) { + fprintf(outfptr, "pin_group\n"); + *sigptr = '\0'; + fprintf(outfptr, "pin name %s_bF$pin/%s ", + port->net, port->name); + *sigptr = '_'; + fprintf(outfptr, "signal %s layer %d %d %d\n", + port->net, lvert, px, py); + fprintf(outfptr, "end_pin_group\n"); + } + else { + fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n", + port->name, port->net, lvert, px, py); + } + } + else { /* Handle arrays */ + char *apin, *anet, *dptr, *cptr; + int a, pidx, armax, armin; + + if ((is_inst_bus == TRUE) && (is_port_bus == FALSE) && + (is_net_bus == TRUE)) { + armax = armin = arrayidx; + } + else { + armax = armin = 0; + for (j = 0; j < gateginfo->nodes; j++) { + char *delim, *sptr; + + sptr = gateginfo->node[j]; + if (*sptr == '\\') sptr = strchr(sptr, ' '); + if (sptr == NULL) sptr = gateginfo->node[j]; + delim = strrchr(sptr, '['); + if (delim != NULL) { + *delim = '\0'; + if (!strcmp(port->name, gateginfo->node[j])) { + if (sscanf(delim + 1, "%d", &pidx) == 1) { + if (pidx > armax) armax = pidx; + if (pidx < armin) armin = pidx; + } + } + *delim = '['; + } + } + } + + /* To do: Need to check if array is high-to-low or low-to-high */ + /* Presently assuming arrays are always defined high-to-low */ + + apin = (char *)malloc(strlen(port->name) + 15); + for (a = armax; a >= armin; a--) { + if (is_port_bus) + sprintf(apin, "%s[%d]", port->name, a); + else + sprintf(apin, "%s", port->name); + + /* If net is not delimited by {...} then it is also */ + /* an array. Otherwise, find the nth element in */ + /* the brace-enclosed set. */ + + /* To do: if any component of the array is a vector */ + /* then we need to count bits in that vector. */ + + if (*port->net == '{') { + int aidx; + char *sptr, ssave; + char *pptr = port->net + 1; + for (aidx = 0; aidx < (armax - a); aidx++) { + sptr = pptr; + while (*sptr != ',' && *sptr != '}') sptr++; + pptr = sptr + 1; + } + sptr = pptr; + if (*sptr != '\0') { + while (*sptr != ',' && *sptr != '}') sptr++; + ssave = *sptr; + *sptr = '\0'; + anet = (char *)malloc(strlen(pptr) + 1); + sprintf(anet, "%s", pptr); + *sptr = ssave; + } + else { + anet = NULL; /* Must handle this error! */ + } + } + else if (((dptr = strrchr(netsptr, '[')) != NULL) && + ((cptr = strrchr(netsptr, ':')) != NULL)) { + int fhigh, flow, fidx; + sscanf(dptr + 1, "%d", &fhigh); + sscanf(cptr + 1, "%d", &flow); + if (fhigh > flow) fidx = fhigh - (armax - a); + else fidx = flow + (armax - a); + anet = (char *)malloc(strlen(port->net) + 15); + *dptr = '\0'; + sprintf(anet, "%s[%d]", port->net, fidx); + *dptr = '['; + } + else { + anet = (char *)malloc(strlen(port->net) + 15); + sprintf(anet, "%s[%d]", port->net, a); + } + + /* Find the corresponding port bit */ + for (j = 0; j < gateginfo->nodes; j++) { + if (anet == NULL) break; + if (!strcmp(apin, gateginfo->node[j])) { + + /* Pull pin position from first rectangle in taps */ + /* list. This does not have to be accurate; just */ + /* representative. */ + int bufidx; + char *sigptr; + DSEG tap = gateginfo->taps[j]; + + /* If LEF file failed to specify pin geometry, then */ + /* use cell center */ + if (tap == NULL) { + px = cllx; + py = clly; + } + else { + llx = (int)(tap->x1 * (double)units + 0.5); + lly = (int)(tap->y1 * (double)units + 0.5); + urx = (int)(tap->x2 * (double)units + 0.5); + ury = (int)(tap->y2 * (double)units + 0.5); + px = cllx + ((llx + urx) / 2); + py = clly + ((lly + ury) / 2); + } + + if (((sigptr = strstr(port->net, "_bF$buf")) != NULL) && + ((sscanf(sigptr + 7, "%d", &bufidx)) == 1) && + (gateginfo->direction[j] == PORT_CLASS_INPUT)) { + fprintf(outfptr, "pin_group\n"); + *sigptr = '\0'; + fprintf(outfptr, "pin name %s_bF$pin/%s ", + anet, apin); + *sigptr = '_'; + fprintf(outfptr, "signal %s layer %d %d %d\n", + anet, lvert, px, py); + fprintf(outfptr, "end_pin_group\n"); + } + else { + fprintf(outfptr, "pin name %s signal %s layer %d %d %d\n", + apin, anet, lvert, px, py); + } + break; + } + } + free(anet); + if (j == gateginfo->nodes) { + fprintf(stderr, "Error: Failed to find port %s in cell %s" + " port list!\n", port->name, inst->cellname); + } + } + free(apin); + } + } + if (inst->arraystart < inst->arrayend) { + if (++arrayidx > inst->arrayend) break; + } + else { + if (--arrayidx < inst->arrayend) break; + } + } + } + + /* Compute size of a pin as the route pitch (px and py are half sizes). */ + /* This prevents pins from being spaced tighter than the route pitches. */ + + px = pitchx[lvert] >> 1; + py = pitchy[lvert] >> 1; + + /* Various attempts to get a valid value for the Y pitch. */ + if (py == 0) py = pitchy[lvert - 1]; + if (py == 0) py = pitchy[lvert + 1]; + if (py == 0) py = pitchx[lvert]; + + /* Output pins from the verilog input/output list */ + + kidx = 1; + for (port = topcell->portlist; port; port = port->next) { + if (port->name == NULL) continue; + net = HashLookup(port->name, &topcell->nets); + if (net && net->start >= 0 && net->end >= 0) { + if (net->start > net->end) { + for (i = net->start; i >= net->end; i--) { + fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx, + port->name, i); + fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n", + -px, -py, -px, py, px, py, px, -py); + fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n", + port->name, i, port->name, i, lvert); + kidx++; + } + } + else { + for (i = net->start; i <= net->end; i++) { + fprintf(outfptr, "pad %d name twpin_%s[%d]\n", kidx, + port->name, i); + fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n", + -px, -py, -px, py, px, py, px, -py); + fprintf(outfptr, "pin name %s[%d] signal %s[%d] layer %d 0 0\n", + port->name, i, port->name, i, lvert); + kidx++; + } + } + } + else { + fprintf(outfptr, "pad %d name twpin_%s\n", kidx, port->name); + fprintf(outfptr, "corners 4 %d %d %d %d %d %d %d %d\n", + -px, -py, -px, py, px, py, px, -py); + fprintf(outfptr, "pin name %s signal %s layer %d 0 0\n", + port->name, port->name, lvert); + kidx++; + } + fprintf(outfptr, "\n"); + + } + if (outfile != NULL) fclose(outfptr); + + fflush(stdout); + return result; +} + +/*--------------------------------------------------------------*/ +/* C helpmessage - tell user how to use the program */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *outf) +{ + fprintf(outf, "vlog2Cel [-options] <netlist>\n"); + fprintf(outf, "\n"); + fprintf(outf, "vlog2Cel converts a netlist in verilog to a .cel file\n"); + fprintf(outf, "for the GrayWolf placement tool. Output on stdout. LEF\n"); + fprintf(outf, "files are required for determining cell dimensions.\n"); + fprintf(outf, "\n"); + fprintf(outf, "options:\n"); + fprintf(outf, "\n"); + fprintf(outf, " -h Print this message\n"); + fprintf(outf, " -o <path> Output filename is <path>, otherwise output" + " on stdout.\n"); + fprintf(outf, " -u <value> Use scale <value> for units (default " + "100 = centimicrons)\n"); + fprintf(outf, " -l <path> Read LEF file from <path> (may be called multiple" + " times)\n"); + +} /* helpmessage() */ + diff --git a/src/vlog2Def.c b/src/vlog2Def.c new file mode 100644 index 0000000..57fc96a --- /dev/null +++ b/src/vlog2Def.c @@ -0,0 +1,480 @@ +//---------------------------------------------------------------- +// vlog2Def +//---------------------------------------------------------------- +// Convert from verilog netlist to a pre-placement DEF file +//---------------------------------------------------------------- + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> /* for getopt() */ +#include <math.h> /* For sqrt() and ceil() */ + +#include "hash.h" +#include "readverilog.h" +#include "readlef.h" + +int write_output(struct cellrec *, int hasmacros, float aspect, float density, + int units, GATE coresite, char *outname); +void helpmessage(FILE *outf); + +/* Linked list for nets */ + +typedef struct _linkedNet *linkedNetPtr; + +typedef struct _linkedNet { + char *instname; + char *pinname; + linkedNetPtr next; +} linkedNet; + +/* Hash table of LEF macros */ +struct hashtable LEFhash; + +/*--------------------------------------------------------------*/ + +int main (int argc, char *argv[]) +{ + int i, result = 0, hasmacros = FALSE; + int units = 100; + float aspect = 1.0; + float density = 1.0; + struct cellrec *topcell; + GATE coresite = NULL; + char *defoutname = NULL; + + InitializeHashTable(&LEFhash, SMALLHASHSIZE); + + while ((i = getopt(argc, argv, "hHl:a:d:u:o:")) != EOF) { + switch (i) { + case 'h': + case 'H': + helpmessage(stdout); + return 0; + case 'l': + result = LefRead(optarg); /* Can be called multiple times */ + if (result == 0) { + helpmessage(stderr); + return 1; + } + break; + case 'o': + defoutname = strdup(optarg); + break; + case 'a': + if (sscanf(optarg, "%f", &aspect) != 1) { + fprintf(stderr, "Could not read aspect value from \"-a %s\"\n", + optarg); + helpmessage(stderr); + return 1; + } + break; + case 'd': + if (sscanf(optarg, "%f", &density) != 1) { + fprintf(stderr, "Could not read density value from \"-d %s\"\n", + optarg); + helpmessage(stderr); + return 1; + } + if (density < 0.0 || density > 1.0) { + fprintf(stderr, "Illegal density value \"-d %s\"\n", optarg); + helpmessage(stderr); + return 1; + } + break; + case 'u': + if (sscanf(optarg, "%d", &units) != 1) { + fprintf(stderr, "Could not read units value from \"-u %s\"\n", + optarg); + helpmessage(stderr); + return 1; + } + break; + default: + fprintf(stderr, "Bad option switch \"%c\"\n", (char)i); + helpmessage(stderr); + return 1; + } + } + + if (optind >= argc) { + fprintf(stderr, "Couldn't find a filename as input\n"); + helpmessage(stderr); + return 1; + } + + /* If any LEF files were read, hash the GateInfo list */ + if (GateInfo != NULL) { + GATE gate; + for (gate = GateInfo; gate; gate = gate->next) { + HashPtrInstall(gate->gatename, gate, &LEFhash); + if (!strncmp(gate->gatename, "site_", 5)) + if (gate->gateclass == MACRO_CLASS_CORE) + coresite = gate; + } + hasmacros = TRUE; + } + + topcell = ReadVerilog(argv[optind]); + result = write_output(topcell, hasmacros, aspect, density, + units, coresite, defoutname); + return result; +} + +/*--------------------------------------------------------------*/ +/* output_nets: */ +/* Recursion callback function for each item in Nodehash */ +/*--------------------------------------------------------------*/ + +struct nlist *output_nets(struct hashlist *p, void *cptr) +{ + struct netrec *net; + char *sptr = NULL; + FILE *outf = (FILE *)cptr; + linkedNetPtr nlink, nsrch; + + nlink = (linkedNetPtr)(p->ptr); + + // Verilog backslash-escaped names are decidedly not SPICE + // compatible, so replace the mandatory trailing space character + // with another backslash. + + if (*p->name == '\\') { + sptr = strchr(p->name, ' '); + if (sptr != NULL) *sptr = '\\'; + } + + fprintf(outf, "- %s\n", p->name); + + for (nsrch = nlink; nsrch; nsrch = nsrch->next) { + fprintf(outf, " ( %s %s )", nsrch->instname, nsrch->pinname); + if (nsrch->next == NULL) + fprintf(outf, " ;"); + fprintf(outf, "\n"); + } + + if (sptr != NULL) *sptr = ' '; + return NULL; +} + +/*--------------------------------------------------------------*/ +/* port_output_specs */ +/* */ +/* Write information to an entry in the DEF PINS section */ +/*--------------------------------------------------------------*/ + +void port_output_specs(FILE *outfptr, struct portrec *port, + LefList *routelayer, int units) +{ + char *layername; + int x, y, ll, ur, w, hw; + LefList pinlayer; + + /* This string array much match the port definitions in readverilog.h */ + static char *portdirs[] = {"", "INPUT", "OUTPUT", "INOUT"}; + + x = y = 0; /* To be done (need constraints?) */ + + /* To be done: Layer depends on position and orientation */ + pinlayer = routelayer[2]; + + layername = pinlayer->lefName; + w = (int)(0.5 + pinlayer->info.route.width * (float)units); + hw = w >> 1; + ll = -hw; + ur = w - hw; + + if (port->direction > PORT_NONE) + fprintf(outfptr, "\n + DIRECTION %s", portdirs[port->direction]); + fprintf(outfptr, "\n + LAYER %s ( %d %d ) ( %d %d )", layername, + ll, ll, ur, ur); + fprintf(outfptr, "\n + PLACED ( %d %d ) N ;\n", x, y); +} + +/*--------------------------------------------------------------*/ +/* write_output */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +int write_output(struct cellrec *topcell, int hasmacros, float aspect, + float density, int units, GATE coresite, char *outname) +{ + FILE *outfptr = stdout; + int ncomp, npin, nnet, start, end, i, result = 0; + int totalwidth, totalheight, rowwidth, rowheight, numrows; + int sitewidth, siteheight, numsites; + char portnet[512]; + + struct netrec *net; + struct portrec *port; + struct instance *inst; + + struct hashtable Nodehash; + + /* Static string "PIN" for ports */ + static char pinname[] = "PIN"; + + linkedNetPtr nlink, nsrch; + LefList slef; + LefList routelayer[3]; + + /* Open the output file (unless name is NULL, in which case use stdout) */ + if (outname != NULL) { + outfptr = fopen(outname, "w"); + if (outfptr == NULL) { + fprintf(stderr, "Error: Cannot open file %s for writing.\n", outname); + return 1; + } + } + + /* Hash the nets */ + + InitializeHashTable(&Nodehash, LARGEHASHSIZE); + nnet = 0; + for (port = topcell->portlist; port; port = port->next) { + if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) { + start = net->start; + end = net->end; + } + else start = end = -1; + + if (start > end) { + int tmp; + tmp = start; + start = end; + end = tmp; + } + for (i = start; i <= end; i++) { + nlink = (linkedNetPtr)malloc(sizeof(linkedNet)); + nlink->instname = pinname; + + if (start == -1) + nlink->pinname = port->name; + else { + sprintf(portnet, "%s[%d]", port->name, i); + nlink->pinname = strdup(portnet); + } + nlink->next = NULL; + if ((nsrch = HashLookup(nlink->pinname, &Nodehash)) != NULL) { + while (nsrch->next) nsrch = nsrch->next; + nsrch->next = nlink; + } + else { + HashPtrInstall(nlink->pinname, nlink, &Nodehash); + nnet++; + } + } + } + totalwidth = 0; + for (inst = topcell->instlist; inst; inst = inst->next) { + for (port = inst->portlist; port; port = port->next) { + + nlink = (linkedNetPtr)malloc(sizeof(linkedNet)); + nlink->instname = inst->instname; + nlink->pinname = port->name; + nlink->next = NULL; + + if ((nsrch = HashLookup(port->net, &Nodehash)) != NULL) { + while (nsrch->next) nsrch = nsrch->next; + nsrch->next = nlink; + } + else { + HashPtrInstall(port->net, nlink, &Nodehash); + nnet++; + } + } + + if (hasmacros) { + GATE gate; + + gate = HashLookup(inst->cellname, &LEFhash); + if (gate) { + /* Make sure this is a core cell */ + if (gate->gateclass == MACRO_CLASS_CORE) { + totalwidth += (int)(gate->width * (float)units); + rowheight = (int)(gate->height * (float)units); + } + /* To do: Handle non-core cell records */ + /* (specifically PAD and BLOCK). */ + } + } + } + + /* For pin placement, find the 2nd and 3rd route layer LEF names. */ + /* NOTE: This only ensures that the output is valid LEF; it does */ + /* not do anything about applying pin constraints. */ + + for (i = 0; i < 3; i++) + routelayer[i] = (LefList)NULL; + + for (slef = LefInfo; slef; slef = slef->next) + if (slef->lefClass == CLASS_ROUTE) + if ((slef->type < 3) && (slef->type >= 0)) + routelayer[slef->type] = slef; + + /* Write output DEF header */ + fprintf(outfptr, "VERSION 5.6 ;\n"); + /* fprintf(outfptr, "NAMESCASESENSITIVE ON ;\n"); */ + fprintf(outfptr, "DIVIDERCHAR \"/\" ;\n"); + fprintf(outfptr, "BUSBITCHARS \"[]\" ;\n"); + fprintf(outfptr, "DESIGN %s ;\n", topcell->name); + fprintf(outfptr, "UNITS DISTANCE MICRONS %d ;\n", units); + fprintf(outfptr, "\n"); + + /* Calculate pre-placement die area, rows, and tracks, and output the same, */ + /* depending on what has been read in from LEF files. */ + + if (hasmacros) { + /* NOTE: Use a prorated density that is slightly lower than the target */ + /* or else the placement can fail due to fewer sites available then */ + /* cell area to place, after accounting for density. */ + + int efftotalwidth = (int)ceilf((float)totalwidth / (density * 0.95)); + + numrows = (int)ceilf(sqrtf(efftotalwidth / (aspect * rowheight))); + rowwidth = (int)ceilf(efftotalwidth / numrows); + totalheight = (int)ceilf(rowheight * numrows); + sitewidth = (int)ceilf(coresite->width * units); + siteheight = (int)ceilf(coresite->height * units); + + /* Diagnostic */ + fprintf(stdout, "Diagnostic:\n"); + fprintf(stdout, "Total width of all cells = %gum\n", (float)totalwidth / (float)units); + fprintf(stdout, "Effective total width after density planning = %gum\n", (float)efftotalwidth / (float)units); + fprintf(stdout, "Site size = (%gum, %gum)\n", (float)sitewidth / (float)units, siteheight / (float)units); + fprintf(stdout, "Row height = %gum\n", (float)rowheight / (float)units); + fprintf(stdout, "Row width = %gum\n", (float)rowwidth / (float)units); + fprintf(stdout, "Total height = %gum\n", (float)totalheight / (float)units); + + /* To do: compute additional area for pins */ + + fprintf(outfptr, "DIEAREA ( 0 0 ) ( %d %d ) ;\n", rowwidth, totalheight); + fprintf(outfptr, "\n"); + + /* Compute site placement and generate ROW statements */ + + numsites = (int)ceilf((float)rowwidth / (float)sitewidth); + for (i = 0; i < numrows; i++) { + fprintf(outfptr, "ROW ROW_%d %s 0 %d %c DO %d BY 1 STEP %d 0 ;\n", + i + 1, coresite->gatename + 5, i * siteheight, + ((i % 2) == 0) ? 'N' : 'S', numsites, sitewidth); + } + fprintf(outfptr, "\n"); + } + + /* Write components in the order of the input file */ + + ncomp = 0; + for (inst = topcell->instlist; inst; inst = inst->next) { + ncomp++; + if (inst->arraystart != -1) { + int arrayw = inst->arraystart - inst->arrayend; + ncomp += (arrayw < 0) ? -arrayw : arrayw; + } + } + fprintf(outfptr, "COMPONENTS %d ;\n", ncomp); + + for (inst = topcell->instlist; inst; inst = inst->next) { + if (inst->arraystart != -1) { + int ahigh, alow, j; + if (inst->arraystart > inst->arrayend) { + ahigh = inst->arraystart; + alow = inst->arrayend; + } + else { + ahigh = inst->arrayend; + alow = inst->arraystart; + } + for (j = ahigh; j >= alow; j--) { + fprintf(outfptr, "- %s[%d] %s ;\n", inst->instname, j, inst->cellname); + } + } + else + fprintf(outfptr, "- %s %s ;\n", inst->instname, inst->cellname); + } + fprintf(outfptr, "END COMPONENTS\n\n"); + + npin = 0; + for (port = topcell->portlist; port; port = port->next) { + if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) { + int btot = net->start - net->end; + if (btot < 0) btot = -btot; + npin += btot + 1; + } + else + npin++; + } + fprintf(outfptr, "PINS %d ;\n", npin); + + for (port = topcell->portlist; port; port = port->next) { + if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) { + if (net->start == -1) { + fprintf(outfptr, "- %s + NET %s", port->name, port->name); + port_output_specs(outfptr, port, routelayer, units); + } + else if (net->start > net->end) { + for (i = net->start; i >= net->end; i--) { + fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i, + port->name, i); + port_output_specs(outfptr, port, routelayer, units); + } + } + else { + for (i = net->start; i <= net->end; i++) { + fprintf(outfptr, "- %s[%d] + NET %s[%d]", port->name, i, + port->name, i); + port_output_specs(outfptr, port, routelayer, units); + } + } + } + else { + fprintf(outfptr, "- %s + NET %s", port->name, port->name); + port_output_specs(outfptr, port, routelayer, units); + } + } + + fprintf(outfptr, "END PINS\n\n"); + + fprintf(outfptr, "NETS %d ;\n", nnet); + RecurseHashTablePointer(&Nodehash, output_nets, outfptr); + fprintf(outfptr, "END NETS\n\n"); + + /* End the design */ + fprintf(outfptr, "END DESIGN\n"); + + if (outname != NULL) fclose(outfptr); + + fflush(stdout); + return result; + +} /* write_output */ + +/*--------------------------------------------------------------*/ +/* C helpmessage - tell user how to use the program */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *outf) +{ + fprintf(outf,"vlog2Def <netlist>\n"); + fprintf(outf,"\n"); + fprintf(outf,"vlog2Def converts a verilog netlist to a pre-placement DEF file.\n"); + fprintf(outf,"\n"); + fprintf(outf,"options:\n"); + fprintf(outf,"\n"); + fprintf(outf," -h Print this message\n"); + fprintf(outf," -o <path> Set output filename (otherwise output is on stdout).\n"); + fprintf(outf," -l <path> Read LEF file from <path> (may be called multiple" + " times)\n"); + fprintf(outf," -a <value> Set aspect ratio to <value> (default 1.0)\n"); + fprintf(outf," -d <value> Set density to <value> (default 1.0)\n"); + fprintf(outf," -u <value> Set units-per-micron to <value) (default 100)\n"); + +} /* helpmessage() */ + diff --git a/src/vlog2Spice.c b/src/vlog2Spice.c new file mode 100644 index 0000000..5b70297 --- /dev/null +++ b/src/vlog2Spice.c @@ -0,0 +1,636 @@ +//-------------------------------------------------------------- +// vlog2Spice +// +// Convert a structural verilog netlist (with power and ground +// nets!) into a SPICE netlist. +// +// Revision 0, 2006-11-11: First release by R. Timothy Edwards. +// Revision 1, 2009-07-13: Minor cleanups by Philipp Klaus Krause. +// Revision 2, 2013-05-10: Modified to take a library of subcell +// definitions to use for determining port order. +// Revision 3, 2013-10-09: Changed from BDnet2BSpice to +// blif2BSpice +// Revision 4, 2018-11-28: Changed from blif2BSpice to vlog2Spice +// +//-------------------------------------------------------------- + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <math.h> +#include <ctype.h> +#include <float.h> + +#include "hash.h" +#include "readverilog.h" + +#define LengthOfLine 16384 + +#define DO_INCLUDE 0x01 +#define DO_DELIMITER 0x02 + +/* Linked list of names of SPICE libraries to read */ +typedef struct _linkedstring *LinkedStringPtr; + +typedef struct _linkedstring { + char *string; + LinkedStringPtr next; +} LinkedString; + +/* Function prototypes */ +int write_output(struct cellrec *, LinkedStringPtr, char *, int); +int loc_getline(char s[], int lim, FILE *fp); +void helpmessage(FILE *); + +//-------------------------------------------------------- + +int main (int argc, char *argv[]) +{ + int i, result = 0; + int flags = 0; + char *eptr; + + char *vloginname = NULL; + char *spclibname = NULL; + char *spcoutname = NULL; + + LinkedStringPtr spicelibs = NULL, newspicelib; + + + struct cellrec *topcell = NULL; + + while ((i = getopt(argc, argv, "hHidD:l:s:o:")) != EOF) { + switch (i) { + case 'l': + newspicelib = (LinkedStringPtr)malloc(sizeof(LinkedString)); + newspicelib->string = strdup(optarg); + newspicelib->next = spicelibs; + spicelibs = newspicelib; + break; + case 'o': + spcoutname = strdup(optarg); + break; + case 'i': + flags |= DO_INCLUDE; + break; + case 'd': + flags |= DO_DELIMITER; + break; + case 'h': + case 'H': + helpmessage(stdout); + exit(0); + break; + case 'D': + eptr = strchr(optarg, '='); + if (eptr != NULL) { + *eptr = '\0'; + VerilogDefine(optarg, eptr + 1); + *eptr = '='; + } + else + VerilogDefine(optarg, "1"); + break; + default: + fprintf(stderr, "Unknown switch %c\n", (char)i); + helpmessage(stderr); + exit(1); + break; + } + } + + if (optind < argc) { + vloginname = strdup(argv[optind]); + optind++; + } + else { + fprintf(stderr, "Couldn't find a filename as input\n"); + helpmessage(stderr); + exit(1); + } + optind++; + + topcell = ReadVerilog(vloginname); + if (topcell != NULL) + result = write_output(topcell, spicelibs, spcoutname, flags); + else + result = 1; /* Return error code */ + + return result; +} + +/*--------------------------------------------------------------*/ +/* Verilog backslash notation, which is the most absurd syntax */ +/* in the known universe, is fundamentally incompatible with */ +/* SPICE. The ad-hoc solution used by qflow is to replace */ +/* the trailing space with another backslash such that the */ +/* name is SPICE-compatible and the original syntax can be */ +/* recovered when needed. */ +/*--------------------------------------------------------------*/ + +void backslash_fix(char *netname) +{ + char *sptr; + + if (*netname == '\\') + if ((sptr = strchr(netname, ' ')) != NULL) + *sptr = '\\'; +} + +/*--------------------------------------------------------------*/ +/* write_output --- Write the SPICE netlist output */ +/* */ +/* ARGS: */ +/* RETURNS: 0 on success, 1 on error. */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +int write_output(struct cellrec *topcell, LinkedStringPtr spicelibs, + char *outname, int flags) +{ + FILE *libfile; + FILE *outfile; + char *libname; + LinkedStringPtr curspicelib; + + struct netrec *net; + struct instance *inst; + struct portrec *port; + struct portrec *newport, *portlist, *lastport; + + int i, j, k, start, end, pcount = 1; + int result = 0; + int instidx, insti; + + char *lptr; + char *sp, *sp2; + char line[LengthOfLine]; + + struct hashtable Libhash; + + if (outname != NULL) { + outfile = fopen(outname, "w"); + if (outfile == NULL) { + fprintf(stderr, "Error: Couldn't open file %s for writing\n", outname); + return 1; + } + } + else + outfile = stdout; + + /* Initialize SPICE library hash table */ + InitializeHashTable(&Libhash, SMALLHASHSIZE); + + // Read one or more SPICE libraries of subcircuits and use them to define + // the order of pins that were read from LEF (which is not necessarily in + // SPICE pin order). + + for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) { + libname = curspicelib->string; + + libfile = fopen(libname, "r"); + if (libfile == NULL) { + fprintf(stderr, "Couldn't open %s for reading\n", libname); + continue; + } + + /* Read SPICE library of subcircuits, if one is specified. */ + /* Retain the name and order of ports passed to each */ + /* subcircuit. */ + + j = 0; + while (loc_getline(line, sizeof(line), libfile) > 0) { + if (!strncasecmp(line, ".subckt", 7)) { + char *cellname; + lastport = NULL; + portlist = NULL; + + /* Read cellname */ + sp = line + 7; + while (isspace(*sp) && (*sp != '\n')) sp++; + sp2 = sp; + while (!isspace(*sp2) && (*sp2 != '\n')) sp2++; + *sp2 = '\0'; + + /* Keep a record of the cellname until we generate the */ + /* hash entry for it */ + cellname = strdup(sp); + + /* Now fill out the ordered port list */ + sp = sp2 + 1; + while (isspace(*sp) && (*sp != '\n') && (*sp != '\0')) sp++; + while (sp) { + + /* Move string pointer to next port name */ + + if (*sp == '\n' || *sp == '\0') { + loc_getline(line, sizeof(line), libfile); + if (*line == '+') + sp = line + 1; + else + break; + } + while (isspace(*sp) && (*sp != '\n')) sp++; + + /* Terminate port name and advance pointer */ + sp2 = sp; + while (!isspace(*sp2) && (*sp2 != '\n') && (*sp2 != '\0')) sp2++; + *sp2 = '\0'; + + /* Add port to list (in forward order) */ + + newport = (struct portrec *)malloc(sizeof(struct portrec)); + if (portlist == NULL) + portlist = newport; + else + lastport->next = newport; + lastport = newport; + newport->name = strdup(sp); + newport->net = NULL; + newport->direction = 0; + newport->next = NULL; + + sp = sp2 + 1; + } + + /* Read input to end of subcircuit */ + + if (strncasecmp(line, ".ends", 4)) { + while (loc_getline(line, sizeof(line), libfile) > 0) + if (!strncasecmp(line, ".ends", 4)) + break; + } + + /* Hash the new port record by cellname */ + HashPtrInstall(cellname, portlist, &Libhash); + free(cellname); + } + } + fclose(libfile); + } + + /* Write output header */ + fprintf(outfile, "*SPICE netlist created from verilog structural netlist module " + "%s by vlog2Spice (qflow)\n", topcell->name); + fprintf(outfile, "*This file may contain array delimiters, not for use in simulation.\n"); + fprintf(outfile, "\n"); + + /* If flags has DO_INCLUDE then dump the contents of the */ + /* libraries. If 0, then just write a .include line. */ + + for (curspicelib = spicelibs; curspicelib; curspicelib = curspicelib->next) { + libname = curspicelib->string; + + if (flags & DO_INCLUDE) { + libfile = fopen(libname, "r"); + if (libname != NULL) { + fprintf(outfile, "** Start of included library %s\n", libname); + /* Write out the subcircuit library file verbatim */ + while (loc_getline(line, sizeof(line), libfile) > 0) + fputs(line, outfile); + fprintf(outfile, "** End of included library %s\n", libname); + fclose(libfile); + } + } + else { + fprintf(outfile, ".include %s\n", libname); + } + } + fprintf(outfile, "\n"); + + /* Generate the subcircuit definition, adding power and ground nets */ + + fprintf(outfile, ".subckt %s ", topcell->name); + + for (port = topcell->portlist; port; port = port->next) { + if ((net = BusHashLookup(port->name, &topcell->nets)) != NULL) { + start = net->start; + end = net->end; + } + else start = end = -1; + + if (start > end) { + int tmp; + tmp = start; + start = end; + end = tmp; + } + if (start == end) { + fprintf(outfile, "%s", port->name); + if (pcount++ % 8 == 7) { + pcount = 0; + fprintf(outfile, "\n+"); + } + fprintf(outfile, " "); + } + else { + for (i = start; i <= end; i++) { + /* Note that use of brackets is not legal SPICE syntax */ + /* but suffices for LVS and such. Output should be */ + /* post-processed before using in simulation. */ + if (flags & DO_DELIMITER) + fprintf(outfile, "%s<%d>", port->name, i); + else + fprintf(outfile, "%s[%d]", port->name, i); + + if (pcount++ % 8 == 7) { + pcount = 0; + fprintf(outfile, "\n+"); + } + fprintf(outfile, " "); + } + } + } + fprintf(outfile, "\n\n"); + + /* Output instances */ + + instidx = -1; + for (inst = topcell->instlist; inst; ) { + int argcnt; + struct portrec *libport; + + /* Check if the instance is an array */ + if (inst->arraystart != -1) { + if (instidx == -1) { + instidx = inst->arraystart; + insti = 0; + } + else if (inst->arraystart > inst->arrayend) { + instidx--; + insti++; + } + else if (inst->arraystart < inst->arrayend) { + instidx++; + insti++; + } + } + + if (inst->arraystart == -1) + fprintf(outfile, "X%s ", inst->instname); + else + fprintf(outfile, "X%s[%d] ", inst->instname, instidx); + pcount = 1; + + /* Search library records for subcircuit */ + + portlist = (struct portrec *)HashLookup(inst->cellname, &Libhash); + + /* If no library entry exists, complain about arbitrary port */ + /* order, then use the instance's port names to create a port */ + /* record entry. */ + + if (portlist == NULL) { + fprintf(stderr, "Warning: No SPICE subcircuit for %s. Pin" + " order will be arbitrary.\n", inst->cellname); + lastport = portlist = NULL; + for (libport = inst->portlist; libport; libport = libport->next) { + newport = (struct portrec *)malloc(sizeof(struct portrec)); + if (portlist == NULL) + portlist = newport; + else + lastport->next = newport; + lastport = newport; + newport->name = strdup(libport->name); + newport->net = NULL; + newport->direction = libport->direction; + newport->next = NULL; + } + HashPtrInstall(inst->cellname, portlist, &Libhash); + } + + /* Output pin connections in the order of the LEF record, which */ + /* has been forced to match the port order of the SPICE library */ + /* If there is no SPICE library record, output in the order of */ + /* the instance, which may or may not be correct. In such a */ + /* case, flag a warning. */ + + argcnt = 0; + for (libport = portlist; ; libport = libport->next) { + char *dptr, dsave; + int idx = 0, is_array = FALSE, match = FALSE; + + if (portlist != NULL && libport == NULL) break; + + argcnt++; + argcnt %= 8; + if (argcnt == 7) + fprintf(outfile, "\n+ "); + + dptr = NULL; + if (libport) { + for (dptr = libport->name; *dptr != '\0'; dptr++) { + if (*dptr == '[') { + is_array = TRUE; + dsave = *dptr; + *dptr = '\0'; + sscanf(dptr + 1, "%d", &idx); + break; + } + } + } + + /* Treat arrayed instances like a bit-blasted port */ + if ((is_array == FALSE) && (inst->arraystart != -1)) { + is_array = TRUE; + idx = insti; + } + + for (port = inst->portlist; port; port = port->next) { + /* Find the port name in the instance which matches */ + /* the port name in the macro definition. */ + + if (libport) { + if (!strcasecmp(libport->name, port->name)) { + match = TRUE; + break; + } + } + else { + match = TRUE; + break; + } + } + if (!match) { + char *gptr; + + /* Deal with annoying use of "!" as a global indicator, */ + /* which is sometimes used. . . */ + gptr = libport->name + strlen(libport->name) - 1; + if (*gptr == '!') { + *gptr = '\0'; + for (port = inst->portlist; port; port = port->next) { + if (!strcasecmp(libport->name, port->name)) { + match = TRUE; + break; + } + } + *gptr = '!'; + } + else { + for (port = inst->portlist; port; port = port->next) { + gptr = port->name + strlen(port->name) - 1; + if (*gptr == '!') { + *gptr = '\0'; + if (!strcasecmp(libport->name, port->name)) match = TRUE; + *gptr = '!'; + if (match == TRUE) break; + } + } + } + } + if (!match) { + fprintf(stderr, "Error: Instance %s has no port %s!\n", + inst->instname, libport->name); + } + else { + if (flags & DO_DELIMITER) { + char *d1ptr, *d2ptr; + if ((d1ptr = strchr(port->net, '[')) != NULL) { + if ((d2ptr = strchr(d1ptr + 1, ']')) != NULL) { + *d1ptr = '<'; + *d2ptr = '>'; + } + } + } + if (is_array) { + char *portname = port->net; + if (*portname == '{') { + char *epos, ssave; + int k; + + /* Bus notation "{a, b, c, ... }" */ + /* Go to the end and count bits backwards. */ + /* until reaching the idx'th position. */ + + /* To be done: Move GetIndexedNet() from */ + /* vlog2Verilog.c to readverilog.c and call */ + /* it from here. It is more complete than */ + /* this implementation. */ + + while (*portname != '}' && *portname != '\0') portname++; + for (k = 0; k < idx; k++) { + epos = portname; + portname--; + while (*portname != ',' && portname > port->net) + portname--; + } + if (*portname == ',') portname++; + ssave = *epos; + *epos = '\0'; + backslash_fix(portname); + fprintf(outfile, "%s", portname); + *epos = ssave; + } + else { + struct netrec wb; + + GetBus(portname, &wb, &topcell->nets); + + if (wb.start < 0) { + /* portname is not a bus */ + backslash_fix(portname); + fprintf(outfile, "%s", portname); + } + else { + int lidx; + if (wb.start < wb.end) + lidx = wb.start + idx; + else + lidx = wb.start - idx; + /* portname is a partial or full bus */ + dptr = strrchr(portname, '['); + if (dptr) *dptr = '\0'; + backslash_fix(portname); + if (flags & DO_DELIMITER) + fprintf(outfile, "%s<%d>", portname, lidx); + else + fprintf(outfile, "%s[%d]", portname, lidx); + if (dptr) *dptr = '['; + } + } + } + else { + backslash_fix(port->net); + fprintf(outfile, "%s", port->net); + } + + if (pcount++ % 8 == 7) { + pcount = 0; + fprintf(outfile, "\n+"); + } + fprintf(outfile, " "); + } + + if (portlist == NULL) { + fprintf(stdout, "Warning: No defined subcircuit %s for " + "instance %s!\n", inst->cellname, inst->instname); + fprintf(stdout, "Pins will be output in arbitrary order.\n"); + break; + } + if (dptr != NULL) *dptr = '['; + } + fprintf(outfile, "%s\n", inst->cellname); + + if ((inst->arraystart != -1) && (instidx != inst->arrayend)) continue; + instidx = -1; + inst = inst->next; + } + fprintf(outfile, "\n.ends\n"); + fprintf(outfile, ".end\n"); + + if (outname != NULL) fclose(outfile); + + return result; +} + +/*--------------------------------------------------------------*/ +/*C loc_getline: read a line, return length */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +int loc_getline(char *s, int lim, FILE *fp) +{ + int c, i; + + i = 0; + while(--lim > 0 && (c = getc(fp)) != EOF && c != '\n') + s[i++] = c; + if (c == '\n') + s[i++] = c; + s[i] = '\0'; + if (c == EOF) i = 0; + return i; +} + +/*--------------------------------------------------------------*/ +/*C helpmessage - tell user how to use the program */ +/* */ +/* ARGS: error code (0 = success, 1 = error) */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *fout) +{ + fprintf(fout, "vlog2Spice [-options] netlist \n"); + fprintf(fout, "\n"); + fprintf(fout, "vlog2Spice converts a netlist in verilog format \n"); + fprintf(fout, "to Spice subcircuit format. Output on stdout unless -o option used.\n"); + fprintf(fout, "Input file must be a structural verilog netlist with power and ground.\n"); + fprintf(fout, "\n"); + fprintf(fout, "Options:\n"); + fprintf(fout, " -h Print this message\n"); + fprintf(fout, " -i Generate include statement for library, not a dump.\n"); + fprintf(fout, " -d Convert array delimiter brackets to angle brackets.\n"); + fprintf(fout, " -D <key>=<value> Preregister a verilog definition.\n"); + fprintf(fout, " -l <path> Specify path to SPICE library of standard cells.\n"); + fprintf(fout, " -o <path> Specify path to output SPICE file.\n"); + fprintf(fout, "\n"); + +} /* helpmessage() */ + diff --git a/src/vlog2Verilog.c b/src/vlog2Verilog.c new file mode 100644 index 0000000..1b0ebf8 --- /dev/null +++ b/src/vlog2Verilog.c @@ -0,0 +1,1018 @@ +//---------------------------------------------------------------- +// vlog2Verilog +//---------------------------------------------------------------- +// Convert between verilog styles. +// Options include bit-blasting vectors and adding power +// supply connections. +// +// Revision 0, 2018-11-29: First release by R. Timothy Edwards. +// +// This program is written in ISO C99. + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> /* For getopt() */ +#include <math.h> +#include <ctype.h> +#include <float.h> + +#include "hash.h" +#include "readverilog.h" +#include "readlef.h" + +int write_output(struct cellrec *, unsigned char, char *); +void helpmessage(FILE *outf); +void cleanup_string(char *); +int is_pwr_name(char *); + +char *VddNet = NULL; +char *GndNet = NULL; +char *AntennaCell = NULL; + +struct hashtable Lefhash; + +/* Define option flags */ + +#define IMPLICIT_POWER (unsigned char)0x01 +#define MAINTAIN_CASE (unsigned char)0x02 +#define BIT_BLAST (unsigned char)0x04 +#define NONAME_POWER (unsigned char)0x08 +#define ADD_ANTENNA (unsigned char)0x10 + +/*--------------------------------------------------------------*/ + +int main (int argc, char *argv[]) +{ + int i, result; + unsigned char Flags; + + char *vloginname = NULL; + char *vlogoutname = NULL; + struct cellrec *topcell; + + Flags = (unsigned char)IMPLICIT_POWER; + + VddNet = strdup("VDD"); + GndNet = strdup("VSS"); + + InitializeHashTable(&Lefhash, SMALLHASHSIZE); + + while ((i = getopt(argc, argv, "pbchnHv:g:l:o:a:")) != EOF) { + switch( i ) { + case 'p': + Flags &= ~IMPLICIT_POWER; + break; + case 'b': + Flags |= BIT_BLAST; + break; + case 'c': + Flags |= MAINTAIN_CASE; + break; + case 'a': + Flags |= ADD_ANTENNA; + if (AntennaCell != NULL) free(AntennaCell); + AntennaCell = strdup(optarg); + break; + case 'n': + Flags |= NONAME_POWER; + break; + case 'h': + case 'H': + helpmessage(stdout); + exit(0); + break; + case 'l': + LefRead(optarg); /* Can be called multiple times */ + break; + case 'v': + free(VddNet); + VddNet = strdup(optarg); + cleanup_string(VddNet); + break; + case 'o': + vlogoutname = strdup(optarg); + break; + case 'g': + free(GndNet); + GndNet = strdup(optarg); + cleanup_string(GndNet); + break; + default: + fprintf(stderr,"Bad switch \"%c\"\n", (char)i); + helpmessage(stderr); + return 1; + } + } + + if (optind < argc) { + vloginname = strdup(argv[optind]); + optind++; + } + else { + fprintf(stderr, "Couldn't find a filename as input\n"); + helpmessage(stderr); + return 1; + } + optind++; + + /* If any LEF files were read, hash the GateInfo list */ + if (GateInfo != NULL) { + GATE gate; + for (gate = GateInfo; gate; gate = gate->next) { + HashPtrInstall(gate->gatename, gate, &Lefhash); + } + } + + topcell = ReadVerilog(vloginname); + result = write_output(topcell, Flags, vlogoutname); + return result; +} + +/*--------------------------------------------------------------*/ +/* Name compare for VddNet and GndNet. Remove any trailing "!" */ +/* (global reference) from the net name. */ +/*--------------------------------------------------------------*/ + +int is_pwr_name(char *text) +{ + int n = strlen(text); + + if (*(text + n - 1) == '!') *(text + n - 1) = '\0'; + + if (!strcmp(text, VddNet)) return 0; + if (!strcmp(text, GndNet)) return 0; + return 1; +} + +/*--------------------------------------------------------------*/ +/* String input cleanup (mainly strip quoted text) */ +/*--------------------------------------------------------------*/ + +void cleanup_string(char *text) +{ + int i; + char *sptr, *wptr; + + /* Remove quotes from quoted strings */ + + sptr = strchr(text, '"'); + if (sptr != NULL) { + i = 0; + while (sptr[i + 1] != '"') { + sptr[i] = sptr[i + 1]; + i++; + } + sptr[i] = '\0'; + } + + /* Remove any trailing "!" used as a global identifier */ + if ((sptr > text) && (*(sptr - 1) == '!')) { + *(sptr - 1) = '\0'; + } +} + +/*--------------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec nets hash table */ +/*--------------------------------------------------------------------------*/ + +struct nlist *output_wires(struct hashlist *p, void *cptr) +{ + struct netrec *net; + FILE *outf = (FILE *)cptr; + + net = (struct netrec *)(p->ptr); + + /* Ignore the power and ground nets; these have already been output */ + /* This also extends to any net in the form <digit><single quote> */ + + if (p->name[0] == '\'') return NULL; + if (isdigit(p->name[0])) { + char c, *dptr; + + dptr = p->name; + while (isdigit(*dptr)) dptr++; + if (*dptr == '\0') return NULL; + else if (*dptr == '\'') { + c = *(dptr + 1); + if (c == 'b' || c == 'h' || c == 'd' || c == 'o') + return NULL; + } + } + else if (is_pwr_name(p->name)) return NULL; + + fprintf(outf, "wire "); + if (net->start >= 0 && net->end >= 0) { + fprintf(outf, "[%d:%d] ", net->start, net->end); + } + fprintf(outf, "%s ;\n", p->name); + return NULL; +} + +/*----------------------------------------------------------------------*/ +/* Convert a verilog number into a binary string. The target string */ +/* "bitstring" is assumed to be at least (bits + 1) bytes in length. */ +/* "c" is a conversion type ('h', 'o', or 'd'). */ +/*----------------------------------------------------------------------*/ +/* hexidecimal to binary */ + +char *hex2binary(char *uval, int bits) +{ + int first, dval, hexc; + char *bitstring = (char *)malloc(1 + bits); + char *hptr, *bptr, *hstring; + char binhex[5]; + + first = bits % 4; + hexc = ((first == 0) ? 0 : 1) + (bits / 4); /* Number of hex characters */ + + hstring = (char *)malloc(hexc + 1); + strncpy(hstring, uval, hexc); + *(hstring + hexc) = '\0'; + for (hptr = hstring; *hptr != '\0'; hptr++) { + + /* Catch 'x', 'X', etc., and convert to zero. */ + /* Keep 'z'/'Z' as this must be handled differently */ + + if ((*hptr == 'z') || (*hptr == 'Z')) *hptr = 'Z'; + else if ((*hptr == 'x') || (*hptr == 'X')) *hptr = '0'; + } + + hptr = hstring; + bptr = bitstring; + while (*hptr != '\0') { + switch(*hptr) { + case 'Z': + strcpy(binhex, "ZZZZ"); + break; + case '0': + strcpy(binhex, "0000"); + break; + case '1': + strcpy(binhex, "0001"); + break; + case '2': + strcpy(binhex, "0010"); + break; + case '3': + strcpy(binhex, "0011"); + break; + case '4': + strcpy(binhex, "0100"); + break; + case '5': + strcpy(binhex, "0101"); + break; + case '6': + strcpy(binhex, "0110"); + break; + case '7': + strcpy(binhex, "0111"); + break; + case '8': + strcpy(binhex, "1000"); + break; + case '9': + strcpy(binhex, "1001"); + break; + case 'a': + strcpy(binhex, "1010"); + break; + case 'b': + strcpy(binhex, "1011"); + break; + case 'c': + strcpy(binhex, "1100"); + break; + case 'd': + strcpy(binhex, "1101"); + break; + case 'e': + strcpy(binhex, "1110"); + break; + case 'f': + strcpy(binhex, "1111"); + break; + } + if (first > 0) { + strncpy(bptr, binhex + (4 - first), first); + *(bptr + first) = '\0'; + bptr += first; + first = 0; + } + else { + strcpy(bptr, binhex); + bptr += 4; + } + hptr++; + } + return bitstring; +} + +/* octal to binary */ + +char *oct2binary(char *uval, int bits) +{ + int first, dval, octc; + char *bitstring = (char *)malloc(1 + bits); + char *optr, *bptr, *ostring; + char binoct[4]; + + first = bits % 3; + octc = ((first == 0) ? 0 : 1) + (bits / 3); /* Number of octal characters */ + ostring = (char *)malloc(1 + octc); + + ostring = (char *)malloc(octc + 1); + strncpy(ostring, uval, octc); + *(ostring + octc) = '\0'; + for (optr = ostring; *optr != '\0'; optr++) { + + /* Catch 'x', 'X', etc., and convert to zero. */ + /* Keep 'z'/'Z' as this must be handled differently */ + + if ((*optr == 'z') || (*optr == 'Z')) *optr = 'Z'; + else if ((*optr == 'x') || (*optr == 'X')) *optr = '0'; + } + + optr = ostring; + bptr = bitstring; + while (*optr != '\0') { + switch(*optr) { + case 'Z': + strcpy(binoct, "ZZZ"); + break; + case '0': + strcpy(binoct, "000"); + break; + case '1': + strcpy(binoct, "001"); + break; + case '2': + strcpy(binoct, "010"); + break; + case '3': + strcpy(binoct, "011"); + break; + case '4': + strcpy(binoct, "100"); + break; + case '5': + strcpy(binoct, "101"); + break; + case '6': + strcpy(binoct, "110"); + break; + case '7': + strcpy(binoct, "11"); + break; + } + if (first > 0) { + strncpy(bptr, binoct + (3 - first), first); + *(bptr + first) = '\0'; + bptr += first; + first = 0; + } + else { + strcpy(bptr, binoct); + bptr += 3; + } + optr++; + } + return bitstring; +} + +/* decimal to binary */ + +char *dec2binary(char *uval, int bits) +{ + int first, dval, hexc; + char *bitstring = (char *)malloc(1 + bits); + char *hptr, *bptr, *hstring, *nval; + char binhex[5]; + + first = bits % 4; + hexc = ((first == 0) ? 0 : 1) + bits >> 2; /* Number of hex characters */ + hstring = (char *)malloc(1 + hexc); + + /* Scan integer value then convert to hex */ + sscanf(uval, "%d", &dval); + sprintf(hstring, "%0*x", hexc, dval); + + hptr = hstring; + bptr = bitstring; + while (*hptr != '\0') { + switch(*hptr) { + case '0': + strcpy(binhex, "0000"); + break; + case '1': + strcpy(binhex, "0001"); + break; + case '2': + strcpy(binhex, "0010"); + break; + case '3': + strcpy(binhex, "0011"); + break; + case '4': + strcpy(binhex, "0100"); + break; + case '5': + strcpy(binhex, "0101"); + break; + case '6': + strcpy(binhex, "0110"); + break; + case '7': + strcpy(binhex, "0111"); + break; + case '8': + strcpy(binhex, "1000"); + break; + case '9': + strcpy(binhex, "1001"); + break; + case 'a': + strcpy(binhex, "1010"); + break; + case 'b': + strcpy(binhex, "1011"); + break; + case 'c': + strcpy(binhex, "1100"); + break; + case 'd': + strcpy(binhex, "1101"); + break; + case 'e': + strcpy(binhex, "1110"); + break; + case 'f': + strcpy(binhex, "1111"); + break; + } + if (first > 0) { + strncpy(bptr, binhex + (4 - first), first); + bptr += first; + first = 0; + } + else { + strcpy(bptr, binhex); + bptr += 4; + } + hptr++; + } + return bitstring; +} + +/*----------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec properties */ +/* hash table */ +/*----------------------------------------------------------------------*/ + +struct nlist *output_props(struct hashlist *p, void *cptr) +{ + char *propval = (char *)(p->ptr); + FILE *outf = (FILE *)cptr; + + fprintf(outf, ".%s(%s),\n", p->name, propval); + return NULL; +} + +/*--------------------------------------------------------------*/ +/* Find the idx'th component of a net name. This pulls the */ +/* single wire name out of an indexed or concatenated array. */ +/* */ +/* Note that this routine does not handle nested braces. */ +/*--------------------------------------------------------------*/ + +char *GetIndexedNet(char *netname, int ridx, struct cellrec *topcell) +{ + int i, alen, idx; + struct netrec wb; + char *sptr, *pptr, savc, *bptr; + static char *subname = NULL; + if (subname == NULL) subname = (char *)malloc(1); + + if (*netname == '{') { + sptr = netname + 1; + i = 0; + while (i <= ridx) { + /* Advance to next comma or close-brace */ + pptr = strchr(sptr, ','); + if (pptr == NULL) pptr = strchr(sptr, '}'); + if (pptr == NULL) pptr = sptr + strlen(sptr) - 1; + + savc = *pptr; + *pptr = '\0'; + + /* Does the array component at sptr have array bounds? */ + GetBus(sptr, &wb, &topcell->nets); + if (wb.start != -1) { + alen = (wb.start - wb.end); + if (alen < 0) alen = -alen; + if (i + alen < ridx) + i += alen; + else { + if (wb.start < wb.end) idx = wb.start + (ridx - i); + else idx = wb.start - (ridx - i); + bptr = strrchr(sptr, '['); + if (bptr != NULL) *bptr = '\0'; + subname = (char *)realloc(subname, strlen(sptr) + 10); + sprintf(subname, "%s[%d]", sptr, idx); + i = ridx; + if (bptr != NULL) *bptr = '['; + } + } + else { + if (i == ridx) { + subname = (char *)realloc(subname, strlen(sptr) + 1); + sprintf(subname, "%s", sptr); + } + i++; + } + *pptr = savc; + } + } + else { + GetBus(netname, &wb, &topcell->nets); + idx = (wb.start > wb.end) ? (wb.start - ridx) : (wb.start + ridx); + bptr = strrchr(netname, '['); + if (bptr != NULL) *bptr = '\0'; + + subname = (char *)realloc(subname, strlen(netname) + 10); + sprintf(subname, "%s[%d]", netname, idx); + if (bptr != NULL) *bptr = '['; + } + return subname; +} + +/*--------------------------------------------------------------*/ +/* write_output */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +int write_output(struct cellrec *topcell, unsigned char Flags, char *outname) +{ + FILE *outfptr = stdout; + int result = 0; + int nunconn = 0; + int arrayidx = -1; + + GATE gate = (GATE)NULL; + + struct netrec *net; + struct portrec *port; + struct instance *inst; + + if (outname != NULL) { + outfptr = fopen(outname, "w"); + if (outfptr == NULL) { + fprintf(stderr, "Error: Cannot open file %s for writing.\n", outname); + return 1; + } + } + + /* Write output module header */ + fprintf(outfptr, "/* Verilog module written by vlog2Verilog (qflow) */\n"); + + if (Flags & IMPLICIT_POWER) + fprintf(outfptr, "/* With explicit power connections */\n"); + if (!(Flags & MAINTAIN_CASE)) + fprintf(outfptr, "/* With case-insensitive names (SPICE-compatible) */\n"); + if (Flags & BIT_BLAST) + fprintf(outfptr, "/* With bit-blasted vectors */\n"); + if (Flags & NONAME_POWER) + fprintf(outfptr, "/* With power connections converted to binary 1, 0 */\n"); + fprintf(outfptr, "\n"); + + fprintf(outfptr, "module %s(\n", topcell->name); + + if (Flags & IMPLICIT_POWER) { + fprintf(outfptr, " inout %s,\n", VddNet); + fprintf(outfptr, " inout %s,\n", GndNet); + } + + for (port = topcell->portlist; port; port = port->next) { + if (port->name == NULL) continue; + switch(port->direction) { + case PORT_INPUT: + fprintf(outfptr, " input "); + break; + case PORT_OUTPUT: + fprintf(outfptr, " output "); + break; + case PORT_INOUT: + fprintf(outfptr, " inout "); + break; + } + net = HashLookup(port->name, &topcell->nets); + if (net && net->start >= 0 && net->end >= 0) { + fprintf(outfptr, "[%d:%d] ", net->start, net->end); + } + fprintf(outfptr, "%s", port->name); + if (port->next) fprintf(outfptr, ","); + fprintf(outfptr, "\n"); + } + fprintf(outfptr, ");\n\n"); + + /* Declare all wires */ + + if (!(Flags & IMPLICIT_POWER) && !(Flags & NONAME_POWER)) { + fprintf(outfptr, "wire %s = 1'b1;\n", VddNet); + fprintf(outfptr, "wire %s = 1'b0;\n\n", GndNet); + } + + RecurseHashTablePointer(&topcell->nets, output_wires, outfptr); + fprintf(outfptr, "\n"); + + /* Write instances in the order of the input file */ + + for (inst = topcell->instlist; inst; ) { + int nprops = RecurseHashTable(&inst->propdict, CountHashTableEntries); + fprintf(outfptr, "%s ", inst->cellname); + if (nprops > 0) { + fprintf(outfptr, "#(\n"); + RecurseHashTablePointer(&inst->propdict, output_props, outfptr); + fprintf(outfptr, ") "); + } + if (inst->cellname) + fprintf(outfptr, "%s", inst->instname); + else { + fprintf(outfptr, "vlog2Verilog: No cell for instance %s\n", inst->instname); + result = 1; // Set error result but continue output. + } + if (inst->arraystart != -1) { + if (Flags & BIT_BLAST) { + if (arrayidx == -1) arrayidx = inst->arraystart; + fprintf(outfptr, "[%d]", arrayidx); + } + else { + fprintf(outfptr, " [%d:%d]", inst->arraystart, inst->arrayend); + } + } + fprintf(outfptr, " (\n"); + + // If there is a gate record read from LEF, keep a pointer to it. + if (GateInfo != NULL) + gate = (GATE)HashLookup(inst->cellname, &Lefhash); + + if (Flags & IMPLICIT_POWER) { + + /* If any LEF files were read, then get the power and */ + /* ground net names from the LEF file definition. */ + + if (gate) { + int n; + u_char found = 0; + for (n = 0; n < gate->nodes; n++) { + if (gate->use[n] == PORT_USE_POWER) { + fprintf(outfptr, " .%s(%s),\n", gate->node[n], VddNet); + found++; + } + else if (gate->use[n] == PORT_USE_GROUND) { + fprintf(outfptr, " .%s(%s),\n", gate->node[n], GndNet); + found++; + } + if (found == 2) break; + } + } + else { + /* Fall back on VddNet and GndNet names */ + fprintf(outfptr, " .%s(%s),\n", GndNet, GndNet); + fprintf(outfptr, " .%s(%s),\n", VddNet, VddNet); + } + } + + /* Write each port and net connection */ + for (port = inst->portlist; port; port = port->next) { + + /* If writing explicit power net names, then watch */ + /* for power connections encoded as binary, and */ + /* convert them to the power bus names. */ + + if ((Flags & IMPLICIT_POWER) || (!(Flags & NONAME_POWER))) { + int brepeat = 0; + char is_array = FALSE, saveptr; + char *sptr = port->net, *nptr; + char *expand = (char *)malloc(1); + + *expand = '\0'; + if (*sptr == '{') { + is_array = TRUE; + sptr++; + expand = (char *)realloc(expand, 2); + strcpy(expand, "{"); + } + while ((*sptr != '}') && (*sptr != '\0')) { + int nest = 0; + + nptr = sptr + 1; + while ((*nptr != '\0') && (*nptr != ',')) { + if (*nptr == '{') nest++; + if (*nptr == '}') { + if (nest == 0) break; + else nest--; + } + nptr++; + } + saveptr = *nptr; + *nptr = '\0'; + + if (isdigit(*sptr) || (*sptr == '\'')) { + char *bptr = sptr; + if (sscanf(bptr, "%d", &brepeat) == 0) brepeat = -1; + while (isdigit(*bptr)) bptr++; + + /* Is digit followed by "'" (fixed values 1 or 0)? */ + + if (*bptr == '\'') { + char *bitstring; + bptr++; + + /* Important note: Need to check if 'x' is */ + /* on an output, in which case it should be */ + /* treated like 'z' (unconnected). */ + + /* Ports in verilog instances have no */ + /* direction information so it is necessary */ + /* to pull the information from the LEF */ + /* record of the cell. */ + + if (gate) { + int n; + for (n = 0; n < gate->nodes; n++) { + if (!strcmp(gate->node[n], port->name)) { + switch (gate->direction[n]) { + case PORT_CLASS_INPUT: + port->direction = PORT_INPUT; + break; + case PORT_CLASS_OUTPUT: + port->direction = PORT_OUTPUT; + break; + default: + port->direction = PORT_INOUT; + break; + } + break; + } + } + } + + if (port->direction != PORT_INPUT) { + char *xptr; + for (xptr = bptr; *xptr != '\0'; xptr++) { + if ((*xptr == 'X') || (*xptr == 'x')) + *xptr = 'z'; + } + } + + switch(*bptr) { + case 'd': + bitstring = dec2binary(bptr + 1, brepeat); + break; + case 'h': + bitstring = hex2binary(bptr + 1, brepeat); + break; + case 'o': + bitstring = oct2binary(bptr + 1, brepeat); + break; + default: + bitstring = strdup(bptr + 1); + break; + } + + if (brepeat < 0) brepeat = strlen(bitstring); + + if ((brepeat > 1) && (is_array == FALSE)) { + is_array = TRUE; + expand = (char *)realloc(expand, strlen(expand) + 2); + strcat(expand, "{"); + } + + bptr = bitstring; + while (*bptr != '\0') { + if (*bptr == '1') { + expand = (char *)realloc(expand, + strlen(expand) + + strlen(VddNet) + 2); + strcat(expand, VddNet); + } + else if (tolower(*bptr) == 'z') { + char unconnect[20]; + /* Unconnected node: Make a new node name. */ + /* This is a single bit, so it can be */ + /* implicitly declared. */ + sprintf(unconnect, "\\$_unconn_%d_ ", nunconn++); + expand = (char *)realloc(expand, + strlen(expand) + strlen(unconnect) + + 1); + strcat(expand, unconnect); + } + else { /* Note: If 'X', then ground it */ + expand = (char *)realloc(expand, + strlen(expand) + + strlen(GndNet) + 2); + strcat(expand, GndNet); + } + brepeat--; + if (brepeat > 0) + strcat(expand, ","); + bptr++; + if (brepeat <= 0) break; + } + if (bptr == bitstring) { + fprintf(stderr, "Warning: Cannot parse \"%s\"\n", sptr); + } + while (brepeat > 0) { + if ((bptr > bitstring) && (*(bptr - 1) == '1')) { + expand = (char *)realloc(expand, + strlen(expand) + + strlen(VddNet) + 2); + strcat(expand, VddNet); + } + else { /* Note: If 'X', then ground it */ + expand = (char *)realloc(expand, + strlen(expand) + + strlen(GndNet) + 2); + strcat(expand, GndNet); + } + brepeat--; + if (brepeat > 0) + strcat(expand, ","); + } + free(bitstring); + } + + /* Otherwise add to "expand" verbatim */ + else { + expand = (char *)realloc(expand, strlen(expand) + + strlen(sptr) + 1); + strcat(expand, sptr); + } + } + else { + /* Normal net name, add to "expand" */ + expand = (char *)realloc(expand, strlen(expand) + + strlen(sptr) + 1); + strcat(expand, sptr); + } + if (saveptr == ',') { + expand = (char *)realloc(expand, strlen(expand) + 2); + strcat(expand, ","); + } + *nptr = saveptr; + sptr = nptr; + if (saveptr != '\0') sptr++; + } + + if (is_array) { + expand = (char *)realloc(expand, strlen(expand) + 2); + strcat(expand, "}"); + } + + /* Replace port->net */ + + free(port->net); + port->net = expand; + } + fprintf(outfptr, " .%s(", port->name); + if ((Flags & BIT_BLAST) && (arrayidx != -1)) { + /* Find the index from the start and pull that item from port->net */ + int ridx; + ridx = arrayidx - inst->arraystart; + if (ridx < 0) ridx = -ridx; + fprintf(outfptr, "%s", GetIndexedNet(port->net, ridx, topcell)); + } + else + fprintf(outfptr, "%s", port->net); + fprintf(outfptr, ")"); + if (port->next) fprintf(outfptr, ","); + fprintf(outfptr, "\n"); + } + fprintf(outfptr, ");\n\n"); + + /* For bit-blasted output, output each element of an array separately. */ + if (Flags & BIT_BLAST) { + if (arrayidx == inst->arrayend) { + inst = inst->next; + arrayidx = -1; + } + else if (arrayidx < inst->arrayend) + arrayidx++; + else if (arrayidx > inst->arrayend) + arrayidx--; + } + else + inst = inst->next; + } + + if (Flags & ADD_ANTENNA) { + char *antennapin = NULL; + GATE gate, acell = NULL; + double asize; + + /* Find the cell name that matches the antenna cell */ + /* Can't use the hash table here because name may be a */ + /* prefix. If more than one cell matches by prefix, */ + /* then use the cell with the smallest area. */ + + for (gate = GateInfo; gate; gate = gate->next) { + if (!strncmp(gate->gatename, AntennaCell, strlen(AntennaCell))) { + if (!strcmp(gate->gatename, AntennaCell)) { + acell = gate; + break; + } + else if (acell == NULL) { + acell = gate; + } + else { + if (gate->width < acell->width) { + acell = gate; + } + } + } + } + + if (acell) { + int i; + /* Find the node that isn't a power pin */ + for (i = 0; i < acell->nodes; i++) { + if (acell->use[i] != PORT_USE_POWER && + acell->use[i] != PORT_USE_GROUND) { + antennapin = acell->node[i]; + break; + } + } + } + + if (antennapin) { + int antcnt = 0; + + /* Add antenna cells to all module inputs */ + + for (port = topcell->portlist; port; port = port->next) { + if (port->name == NULL) continue; + switch(port->direction) { + case PORT_INPUT: + fprintf(outfptr, "%s antenna_%d ", acell->gatename, antcnt); + + net = HashLookup(port->name, &topcell->nets); + if (net && net->start >= 0 && net->end >= 0) { + fprintf(outfptr, "[%d:%d] ", net->start, net->end); + } + + fprintf(outfptr, "(\n"); + fprintf(outfptr, " .%s(%s)\n", antennapin, port->name); + fprintf(outfptr, ");\n\n"); + antcnt++; + break; + } + } + } + } + + /* End the module */ + fprintf(outfptr, "endmodule\n"); + + if (outname != NULL) fclose(outfptr); + + fflush(stdout); + return result; +} + +/*--------------------------------------------------------------*/ +/* C helpmessage - tell user how to use the program */ +/* */ +/* ARGS: */ +/* RETURNS: 1 to OS */ +/* SIDE EFFECTS: */ +/*--------------------------------------------------------------*/ + +void helpmessage(FILE *outf) +{ + fprintf(outf, "vlog2Verilog [-options] <netlist>\n"); + fprintf(outf, "\n"); + fprintf(outf, "vlog2Verilog converts a netlist in one verilog style to\n"); + fprintf(outf, "another. LEF files may be given as inputs to determine\n"); + fprintf(outf, "power and ground net names for cells.\n"); + fprintf(outf, "\n"); + fprintf(outf, "options:\n"); + fprintf(outf, "\n"); + fprintf(outf, " -h Print this message\n"); + fprintf(outf, " -o <path> Set output filename (otherwise output is on stdout).\n"); + fprintf(outf, " -p Don't add power nodes to instances\n"); + fprintf(outf, " (only nodes present in the instance used)\n"); + fprintf(outf, " -b Remove vectors (bit-blasted)\n"); + fprintf(outf, " -c Case-insensitive output (SPICE compatible) \n"); + fprintf(outf, " -n Convert power nets to binary 1 and 0\n"); + fprintf(outf, " -a <name> Add antenna cells to module input pins.\n"); + fprintf(outf, " -l <path> Read LEF file from <path>\n"); + fprintf(outf, " -v <name> Use <name> for power net (default \"Vdd\")\n"); + fprintf(outf, " -g <name> Use <name> for ground net (default \"Gnd\")\n"); + +} /* helpmessage() */ + diff --git a/src/vlogFanout.c b/src/vlogFanout.c new file mode 100644 index 0000000..4fc4aef --- /dev/null +++ b/src/vlogFanout.c @@ -0,0 +1,1735 @@ +/* + *--------------------------------------------------------------------------- + * vlogFanout vlog_input [vlog_output] + * + * vlogFanout[.c] parses a structural verilog netlist. The fanout is analyzed, + * and fanout of each gate is counted. A value to parameterize the driving + * cell will be output. + * Fanouts exceeding a maximum are broken into (possibly hierarchical) + * buffer trees. Eventually, a critical path can be identified, and the + * gates sized to improve it. + * + * Original: fanout.c by Steve Beccue + * New: vlogFanout.c by Tim Edwards. + * changes: 1) Input and output format changed from RTL verilog to BDNET + * for compatibility with the existing digital design flow. + * 2) Gate format changed to facilitate entering IBM data + * 3) Code changed/optimized in too many ways to list here. + * + * Update 4/8/2013: + * Removing dependence upon the naming convention of the original + * technology used with this tool. Instead, the format of the naming + * convention is passed to the tool using "-s <separator>", and parsing + * the remaining name for unique identifiers. + * + * Update 10/8/2013: + * Changed input file format from BDNET to BLIF + * + * Update 10/21/2013: + * Removed most of the dependencies on fixed-length arrays + * + * Update 5/13/2015: + * Added hash functions, which have been on the "to do" list for a + * while. Greatly speeds up the processing, especially for large + * netlists. + * + * Replaced the gate.cfg file parser with the liberty file parser + * from the "liberty2tech" code, which is no longer needed. The + * gate.cfg format was essentially useless as it failed to track + * which pins in a cell are inputs and which are outputs. + * + * Moving the clock tree generator into this module, because I have + * figured out a way to do this in one stage instead of two, using + * the swap_group capability of the graywolf placement tool to find + * the optimal groupings. + * + * Update 5/7/2018: + * Separated clock buffers from other buffers. Identify clock inputs + * on flops and latches and trace back to a common clock pin or pins. + * + * Update 6/15/2018: + * Finally got around to doing dynamic string allocation on the input, + * which avoids issues of having I/O lines that are arbitrarily long + * crashing the program. + * + * Update 11/26/2018: + * Reworked the entire tool to operate on an input verilog netlist + * instead of a BLIF file, given the limitations of the BLIF format + * and the need to completely rewrite the BLIF handling scripts in + * order to maintain use of a badly outdated format. Also, modified + * the code so that iterative calls are run inside vlogFanout instead + * of requiring vlogFanout to be run multiple times, so that the + * liberty file and netlist do not have to be re-read on each pass. + * + * Update 1/17/2019: + * Added handling of pins which are buses. + *--------------------------------------------------------------------------- + * + * Revision 1.3 2008/09/09 21:24:30 steve_beccue + * changed gate strengths in code. Inserted inverters. 2nd pass on + * insert inverters does not work. + * + * Revision 1.2 2008/09/04 14:25:59 steve_beccue + * added helpmessage and id + * + *--------------------------------------------------------------------------- + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> /* for getopt() */ +#include <string.h> +#include <ctype.h> /* for isdigit() */ +#include <math.h> + +#include "hash.h" /* for hash table functions */ +#include "readliberty.h" /* liberty file database */ +#include "readverilog.h" /* verilog parser */ + +#define FALSE 0 +#define TRUE 1 +#define MAXLINE 512 + +char *Inputfname; +char *Outputfname; +char *Buffername = NULL; +char *Clkbufname = NULL; +char *buf_in_pin = NULL; +char *clkbuf_in_pin = NULL; +char *buf_out_pin = NULL; +char *clkbuf_out_pin = NULL; +char *Ignorepath = NULL; +char SuffixIsNumeric; +int GatePrintFlag = 0; +int NodePrintFlag = 0; +int VerboseFlag = 0; +int skip_eol = 0; + +int Topfanout = 0; +int Inputfanout = 0; +double Topload = 0.0; +double Inputload = 0.0; +double Topratio = 0.0; +int Changed_count = 0; // number of gates changed +int Buffer_count = 0; // number of buffers added +int stren_err_counter = 0; +double MaxOverload = 0.0; + +int MaxFanout = 16; // Maximum fanout per node allowed without + // additional buffering. +double MaxLatency = 1000.0; // Maximum variable latency (ps) for which we + // are solving. Represents the largest delay + // allowed for any gate caused by total load + // capacitance. This is empirically derived. +double MaxOutputCap = 30.0; // Maximum capacitance for an output node (fF). + // Outputs should be able to drive this much + // capacitance within MaxLatency time (ps). +double WireCap = 10.0; // Base capacitance for an output node, estimate + // of average wire capacitance (fF). + +struct Gatelist { + char *gatename; + Cell *gatecell; + char *suffix; // points to position in gatename, not allocated + char *separator; + int num_inputs; + double Cint; + double delay; + double strength; +} Gatelist_; + +struct hashtable Gatehash; + +struct Nodelist { + char ignore; + char *nodename; + struct Gatelist *outputgate; + double outputgatestrength; + int type; + int clock; + int num_inputs; + double total_load; + double ratio; // drive strength to total_load ratio + // For net buffer trees + int num_buf; // Number of buffers to split the net + int curcount; // Active count for fanout buffering trees +} Nodelist_; + +struct hashtable Nodehash; +struct hashtable Bushash; + +struct Bus { + int imax; + int imin; +} Bus_; + +struct Drivelist { + char *Separator; // Separator (e.g., "X") + char *DriveType; // Suffix name (e.g., "1") + int NgatesIn; // Number of gates with this suffix in input + int NgatesOut; // Number of gates with this suffix in output +} Drivelist_; + +struct hashtable Drivehash; + +struct Baselist { + char *BaseName; // gate base name (e.g., "INV") + int Ndrives; // number of different drive types for this gate + struct Gatelist **gates; // list of pointers to gates with +} Baselist_; + +struct hashtable Basehash; + +enum states_ {NONE, INPUTS, OUTPUTS, GATENAME, PINNAME, INPUTNODE, CLOCKNODE, + OUTPUTNODE, ENDMODEL, ERROR}; +enum nodetype_ {UNKNOWN, INPUT, CLOCK, OUTPUT, INPUTPIN, OUTPUTPIN, INOUTPIN}; + +int read_gate_file(char *gate_file_name, char *separator); +void read_ignore_file(char *ignore_file_name); +struct Gatelist *GatelistAlloc(); +struct Nodelist *NodelistAlloc(); +struct Drivelist *DrivelistAlloc(); +struct Baselist *BaselistAlloc(); +void showgatelist(void); +void helpmessage(void); +struct Nodelist *registernode(char *nodename, int type, struct Gatelist *gl, + char *pinname); +void shownodes(void); +void resize_gates(struct cellrec *topcell, int doLoadBalance, int doFanout); +void write_output(struct cellrec *topcell, FILE *outfptr, int doLoadBalance, + int doFanout); +struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload); +void count_gatetype(struct Gatelist *gl, int num_in, int num_out); + +/* + *--------------------------------------------------------------------------- + * find_suffix --- + * + * Given a gate name, return the part of the name corresponding to the + * suffix. That is, find the last occurrance of the string separator + * and return a pointer to the following character. Note that a NULL + * separator means there is no suffix, vs. an emptry string separator, + * which means that the suffix encompasses all digits at the end of + * the gate name. + *--------------------------------------------------------------------------- + */ + +char *find_suffix(char *gatename, char *separator) +{ + char *tsuf, *gptr; + char *suffix = NULL; + + if (separator == NULL) { + return NULL; + } + else if (*separator == '\0') { + suffix = gatename + strlen(gatename) - 1; + while (isdigit(*suffix)) suffix--; + suffix++; + } + else { + gptr = gatename; + while ((tsuf = strstr(gptr, separator)) != NULL) { + suffix = tsuf; + gptr = tsuf + 1; + } + if (suffix != NULL) + suffix += strlen(separator); + } + return suffix; +} + +/* + *--------------------------------------------------------------------------- + * Check if a liberty file function describes a buffer. Since this is the + * function of the output pin, it needs only be the input pin (e.g., + * func(Q) = "A"). However, some liberty files repeat the output pin (e.g., + * func(Q) = "Q = A"). Check for both styles. + *--------------------------------------------------------------------------- + */ + +int is_buffer_func(char *func_text, char *pin_in, char *pin_out) { + char *eqptr, esav, *tptr; + + if (!strcmp(func_text, pin_in)) return 1; + + else if ((eqptr = strchr(func_text, '=')) != NULL) { + tptr = eqptr + 1; + while (isspace(*tptr) && (*tptr != '\0')) tptr++; + while ((eqptr > func_text) && isspace(*(eqptr - 1))) eqptr--; + esav = *eqptr; + *eqptr = '\0'; + if (!strcmp(func_text, pin_out) && (*tptr != '\0') && + !strcmp(tptr, pin_in)) { + *eqptr = esav; + return 1; + } + *eqptr = esav; + } + return 0; +} + +typedef struct _gaterec { + char *path; /* Path to library */ + char *sep; /* Gate name separator ("-" if none) */ +} GateRec; + +/*----------------------------------------------------------------------*/ +/* Insert buffers to reduce fanout */ +/* */ +/* This is done in two passes to avoid changing the hash entries while */ +/* iterating through them. Nodes needed buffering are marked with the */ +/* number of buffers required. Then, the nodes are parsed again and */ +/* buffers are added where marked. */ +/* */ +/* Return the highest index (plus one) of any component added, so that */ +/* insert_buffers can be called subsequent times without generating the */ +/* same name for any component. This value is passed back to the */ +/* subroutine as "cidx". */ +/*----------------------------------------------------------------------*/ + +int insert_buffers(struct cellrec *topcell, int cidx) +{ + int i; + int hier; + int slen; + struct Nodelist *nl = NULL; + struct Nodelist *nltest; + char nodename[MAXLINE]; + char instname[MAXLINE]; + char *spos; + + struct Gatelist *glbuf, *clkbuf; + struct Gatelist *gl; + + struct portrec *port, *newport; + struct instance *inst, *newinst; + + // Find the gate record corresponding to the buffer name + glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash); + + // Find the gate record corresponding to the clock buffer name + clkbuf = (struct Gatelist *)HashLookup(Clkbufname, &Gatehash); + + Buffer_count = 0; + if ((Topfanout > MaxFanout) || (Inputfanout > MaxFanout)) { + + /* Mark nets for inserting buffer trees */ + + nl = (struct Nodelist *)HashFirst(&Nodehash); + while (nl != NULL) { + if (nl->ignore == FALSE) { + + // Nets with no driver must be module inputs. Mainly, + // this condition rejects power and ground nodes. + + if ((nl->num_inputs > MaxFanout) && ((nl->outputgatestrength != 0.0) + || (nl->type == INPUTPIN))) { + double d; + int stages, n, mfan; + + // Find number of hierarchical stages (plus one) needed + + mfan = nl->num_inputs; + stages = 1; + n = MaxFanout; + while (mfan > MaxFanout) { + mfan = nl->num_inputs / n; + n *= MaxFanout; + stages++; + } + + // Find the floor of the number of fanouts per buffer + + d = pow((double)nl->num_inputs, (double)(1.0 / stages)); + n = (int)((double)nl->num_inputs / d); + + // Split network into reasonably balanced groups of + // the same (or almost) fanout. + + nl->num_buf = n; + nl->curcount = n - 1; + Buffer_count += n; + } + } + nl = (struct Nodelist *)HashNext(&Nodehash); + } + } + + /* Parse all instances and adjust net names to account for buffer trees. */ + + for (inst = topcell->instlist; inst; inst = inst->next) { + /* Check each port and net connection */ + gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash); + if (gl == NULL) continue; + for (port = inst->portlist; port; port = port->next) { + if (port->direction != PORT_OUTPUT) { + + if (VerboseFlag) printf("\nInput node %s", port->net); + nl = (struct Nodelist *)HashLookup(port->net, &Nodehash); + if (nl == NULL) { + fprintf(stderr, "vlogFanout: Port net %s not in hash\n", port->net); + continue; + } + if (nl->num_buf > 0) { + hier = 0; + nltest = nl; + sprintf(nodename, "%s", nl->nodename); + while (1) { + int is_escaped; + + slen = strlen(nodename); + spos = nodename + slen - 1; + is_escaped = (*nodename == '\\') ? TRUE : FALSE; + if ((is_escaped == TRUE) && (*spos == ' ')) spos--; + if (*spos == ']') { + /* Avoid downstream problems: */ + /* recast "[X]_bF$bufN" as _X_bF$bufN" */ + char *dptr = nodename + slen - 1; + while (dptr >= nodename && *dptr != '[') dptr--; + if (dptr >= nodename) *dptr = '_'; + if (dptr > nodename && *(dptr - 1) == ' ') { + /* There was a space in front of the vector */ + /* delimiter, so move everything back one. */ + memmove(dptr - 1, dptr, strlen(dptr)); + spos--; + is_escaped = TRUE; + } + } + else { + spos++; + } + sprintf(spos, "_bF$buf%d%s", nl->curcount, + ((is_escaped == TRUE) ? " " : "")); + + /* For buffer trees of depth > 1, there will be */ + /* an existing node name wih the _bufN extension */ + /* that is in the node hash table. If so, then */ + /* add the prefix "_hierM". Test again in case */ + /* the buffer tree is even deeper, incrementing */ + /* until the name is unique. */ + + nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash); + if (nltest == NULL) break; + if (nltest->outputgate == NULL) break; + sprintf(spos, "_hier%d%s", hier, + ((is_escaped == TRUE) ? " " : "")); + hier++; + } + + /* Increment input count and load cap on new node */ + registernode(nodename, INPUT, gl, port->name); + + nl->curcount--; + if (nl->curcount < 0) nl->curcount = nl->num_buf - 1; + + /* Reassign the port's net name */ + free(port->net); + port->net = strdup(nodename); + } + } + } + } + + /* Insert any added buffers */ + + nl = (struct Nodelist *)HashFirst(&Nodehash); + while (nl != NULL) { + for (i = nl->num_buf - 1; i >= 0; i--) { + hier = 0; + nltest = nl; + sprintf(nodename, "%s", nl->nodename); + while (1) { + int is_escaped; + + slen = strlen(nodename); + spos = nodename + slen - 1; + is_escaped = (*nodename == '\\') ? TRUE : FALSE; + if ((is_escaped == TRUE) && (*spos == ' ')) spos--; + if (*spos == ']') { + /* Avoid downstream problems: */ + /* recast "[X]_bF$bufN" as _X_bF$bufN" */ + char *dptr = nodename + slen - 1; + while (dptr >= nodename && *dptr != '[') dptr--; + if (dptr >= nodename) *dptr = '_'; + if (dptr > nodename && *(dptr - 1) == ' ') { + memmove(dptr - 1, dptr, strlen(dptr)); + spos--; + } + } + else { + spos++; + } + sprintf(spos, "_bF$buf%d%s", i, + ((is_escaped == TRUE) ? " " : "")); + + /* For buffer trees of depth > 1, there will be */ + /* an existing node name wih the _bufN extension */ + /* that is in the node hash table. If so, then */ + /* add the prefix "_hierM". Test again in case */ + /* the buffer tree is even deeper, incrementing */ + /* M until the name is unique. */ + + nltest = (struct Nodelist *)HashLookup(nodename, &Nodehash); + if (nltest == NULL) break; + if (nltest->outputgate == NULL) break; + sprintf(spos, "_hier%d%s", hier, + ((is_escaped == TRUE) ? " " : "")); + hier++; + } + + if (nl->clock == TRUE) { + /* Prepend clock buffer to instance list */ + newinst = PrependInstance(topcell, Clkbufname); + sprintf(instname, "%s_insert%d", Clkbufname, cidx); + newinst->instname = strdup(instname); + newport = InstPort(newinst, clkbuf_in_pin, nl->nodename); + newport = InstPort(newinst, clkbuf_out_pin, nodename); + + /* Register the new node name */ + Net(topcell, nodename); + registernode(nodename, OUTPUT, clkbuf, clkbuf_out_pin); + } + else { + /* Prepend regular buffer to instance list */ + newinst = PrependInstance(topcell, Buffername); + sprintf(instname, "%s_insert%d", Buffername, cidx); + newinst->instname = strdup(instname); + newport = InstPort(newinst, buf_in_pin, nl->nodename); + newport = InstPort(newinst, buf_out_pin, nodename); + + /* Register the new node name */ + Net(topcell, nodename); + registernode(nodename, OUTPUT, glbuf, buf_out_pin); + } + cidx++; + } + nl->num_inputs = nl->num_buf; + nl->num_buf = 0; + nl = (struct Nodelist *)HashNext(&Nodehash); + } + return cidx; +} + +/* + *--------------------------------------------------------------------------- + * Read a file of nets for which we should ignore fanout. Typically this + * would include the power and ground nets, but may include other static + * nets with non-critical timing. + *--------------------------------------------------------------------------- + */ + +void read_ignore_file(char *ignore_file_name) +{ + struct Nodelist *nl; + FILE *ignorefptr; + char line[MAXLINE]; /* One net per line, should not need dynamic allocation */ + char *s, *sp; + + if (!(ignorefptr = fopen(ignore_file_name, "r"))) { + fprintf(stderr, "vlogFanout: Couldn't open %s as ignore file.\n", + ignore_file_name); + fflush(stderr); + return; + // This is only a warning. It will not stop execution of vlogFanout + } + + while ((s = fgets(line, MAXLINE, ignorefptr)) != NULL) { + // One net name per line + while (isspace(*s)) s++; + sp = s; + while (*sp != '\0' && *sp != '\n' && !isspace(*sp)) sp++; + *sp = '\0'; + + nl = (struct Nodelist *)HashLookup(s, &Nodehash); + if (nl != NULL) { + nl->ignore = (char)1; + } + } + fclose(ignorefptr); +} + +/* + *--------------------------------------------------------------------------- + * + * Call read_liberty() to read in cell information from a Liberty file, + * and then hash the resulting list. + * + *--------------------------------------------------------------------------- + */ + +int read_gate_file(char *gate_file_name, char *separator) +{ + int i, j, k, ind, format = -1; + int gatecount; + char *s, *t, *ss, ssave; + struct Gatelist *gl; + struct Baselist *bl; + Cell *cells, *curcell; + Pin *curpin; + + gatecount = 0; + cells = read_liberty(gate_file_name, NULL); + for (curcell = cells; curcell != NULL; curcell = curcell->next) { + if (curcell->name == NULL) continue; /* undefined/unused cell */ + + gl = GatelistAlloc(); + gl->gatename = strdup(curcell->name); + gl->suffix = find_suffix(gl->gatename, separator); + gl->separator = separator; + gl->gatecell = curcell; + + get_values(curcell, &gl->delay, &gl->Cint); + + gl->num_inputs = 0; + for (curpin = curcell->pins; curpin; curpin = curpin->next) + if (curpin->type == PIN_INPUT || curpin->type == PIN_CLOCK) + gl->num_inputs++; + + /* The "MaxLatency" is empirically derived. Since gl->delay */ + /* is in ps/fF, and strength is compared directly to total */ + /* load capacitance, MaxLatency is effectively in units of ps */ + /* and represents the maximum latency due to load capacitance */ + /* for any gate in the circuit after making gate strength */ + /* substitutions (this does not include internal, constant */ + /* delays in each gate). */ + + // (Diagnostic, for debug) + // fprintf(stdout, "Parsing cell \"%s\", \"%s\", function \"%s\"\n", + // gl->gatename, gl->gatecell->name, gl->gatecell->function); + + gl->strength = MaxLatency / gl->delay; + HashPtrInstall(gl->gatename, gl, &Gatehash); + gatecount++; + + /* Install prefix in Basehash. Note that prefix contains the */ + /* separator string, if any. */ + + if ((s = gl->suffix) == NULL) + ind = strlen(gl->gatename); + else + ind = (int)(s - gl->gatename); + + ssave = gl->gatename[ind]; + gl->gatename[ind] = '\0'; + bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash); + if (bl == NULL) { + bl = BaselistAlloc(); + HashPtrInstall(gl->gatename, bl, &Basehash); + bl->BaseName = strdup(gl->gatename); + } + gl->gatename[ind] = ssave; + + // Note: this code assumes that there are no repeats in + // the liberty file gate list (there shouldn't be). + + if (bl->Ndrives == 0) + bl->gates = (struct Gatelist **)malloc(sizeof(struct Gatelist *)); + else + bl->gates = (struct Gatelist **)realloc(bl->gates, + (bl->Ndrives + 1) * sizeof(struct Gatelist *)); + bl->gates[bl->Ndrives] = gl; + bl->Ndrives++; + } + return gatecount; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +struct Gatelist* GatelistAlloc() +{ + struct Gatelist *gl; + + gl = (struct Gatelist*)malloc(sizeof(struct Gatelist)); + gl->gatename = NULL; + gl->suffix = NULL; + gl->num_inputs = 0; + gl->Cint = 0.0; + gl->delay = 0.0; + gl->strength = 0.0; + return gl; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +struct Drivelist *DrivelistAlloc() +{ + struct Drivelist *dl; + + dl = (struct Drivelist *)malloc(sizeof(struct Drivelist)); + dl->NgatesIn = 0; + dl->NgatesOut = 0; + dl->DriveType = NULL; + dl->Separator = NULL; + return dl; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +struct Nodelist* NodelistAlloc() +{ + struct Nodelist *nl; + + nl = (struct Nodelist *)malloc(sizeof(struct Nodelist)); + nl->nodename = NULL; + nl->ignore = FALSE; + nl->outputgate = NULL; + nl->outputgatestrength = 0.0; + nl->type = UNKNOWN; + nl->total_load = 0.0; + nl->num_inputs = 0; + nl->num_buf = 0; // Tree expansion of node + nl->curcount = 0; + nl->clock = FALSE; + return nl; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +struct Baselist *BaselistAlloc() +{ + struct Baselist *bl; + + bl = (struct Baselist *)malloc(sizeof(struct Baselist)); + bl->BaseName = NULL; + bl->Ndrives = 0; + bl->gates = NULL; + return bl; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +void showgatelist(void) +{ + struct Gatelist *gl; + Cell *curcell; + Pin *curpin; + double pincap; + + gl = (struct Gatelist *)HashFirst(&Gatehash); + while (gl != NULL) { + + printf("\n\ngate: %s with %d inputs and %g drive strength\n", + gl->gatename, gl->num_inputs, gl->strength); + printf("%g ", gl->Cint); + + curcell = gl->gatecell; + for (curpin = curcell->pins; curpin; curpin = curpin->next) { + if (curpin->type == PIN_INPUT || curpin->type == PIN_CLOCK) { + get_pincap(curcell, curpin->name, &pincap); + printf("%g ", pincap); + } + } + gl = (struct Gatelist *)HashNext(&Gatehash); + } +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +struct Nodelist *registernode(char *nodename, int type, struct Gatelist *gl, + char *pinname) +{ + struct Nodelist *nl; + double pincap; + char *dptr; + + nl = (struct Nodelist *)HashLookup(nodename, &Nodehash); + + if (nl == NULL) { + nl = NodelistAlloc(); + nl->nodename = strdup(nodename); + if (type == OUTPUT) nl->outputgate = NULL; + HashPtrInstall(nodename, nl, &Nodehash); + nl->type = type; + nl->outputgate = NULL; + + if ((dptr = strrchr(nodename, '[')) != NULL) { + struct Bus *newbus = (struct Bus *)malloc(sizeof(struct Bus)); + int idx; + *dptr = '\0'; + sscanf(dptr + 1, "%d", &idx); + newbus->imax = newbus->imin = idx; + HashPtrInstall(nodename, newbus, &Bushash); + *dptr = '['; + } + } + else { + if ((dptr = strrchr(nodename, '[')) != NULL) { + struct Bus *newbus; + int idx; + *dptr = '\0'; + newbus = (struct Bus *)HashLookup(nodename, &Bushash); + sscanf(dptr + 1, "%d", &idx); + if (idx < newbus->imin) newbus->imin = idx; + if (idx > newbus->imax) newbus->imax = idx; + *dptr = '['; + } + } + + if (type == OUTPUT) { + nl->outputgate = gl; + if (gl != NULL) { + nl->outputgatestrength = gl->strength; + nl->total_load += gl->Cint; + count_gatetype(gl, 1, 1); + } + } + else if (type == INPUT || type == CLOCK) { + if (gl != NULL) { + get_pincap(gl->gatecell, pinname, &pincap); + nl->total_load += pincap; + nl->num_inputs++; + } + } + if (type == CLOCK) nl->clock = TRUE; + + if ((nl->type != INPUTPIN) && (nl->type != OUTPUTPIN) && + (nl->type != INOUTPIN) && (gl == NULL)) { + fprintf(stderr, "\nError: no output gate for net %s\n", nodename); + fflush(stderr); + } + return nl; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +void count_gatetype(struct Gatelist *gl, int num_in, int num_out) +{ + struct Drivelist *dl; + char *s, *nptr, *tsuf; + int g; + + if ((s = gl->suffix) == NULL) + return; + + dl = (struct Drivelist *)HashLookup(s, &Drivehash); + if (dl == NULL) { + + // New drive type found + + dl = DrivelistAlloc(); + HashPtrInstall(s, dl, &Drivehash); + dl->DriveType = strdup(s); + dl->Separator = gl->separator; + } + + dl->NgatesIn += num_in; // Number of these gates before processing + dl->NgatesOut += num_out; // Number of these gates after processing +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +void shownodes(void) +{ + struct Nodelist *nl; + int i; + + nl = (struct Nodelist *)HashFirst(&Nodehash); + while (nl != NULL) { + printf("\n\nnode: %s with %d fanout and %g fF cap", + nl->nodename, nl->num_inputs, nl->total_load); + printf("\ndriven by %s, with %g strength.\n", + nl->outputgate->gatename, nl->outputgatestrength); + nl = (struct Nodelist *)HashNext(&Nodehash); + } +} + +/*--------------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec nets hash table */ +/*--------------------------------------------------------------------------*/ + +struct nlist *output_wires(struct hashlist *p, void *cptr) +{ + struct netrec *net; + FILE *outf = (FILE *)cptr; + + net = (struct netrec *)(p->ptr); + + /* Ignore any net which is a hardwired 1/0 bit list */ + + if (p->name[0] == '\'') return NULL; + if (isdigit(p->name[0])) { + char c, *dptr; + + dptr = p->name; + while (isdigit(*dptr)) dptr++; + if (*dptr == '\0') return NULL; + else if (*dptr == '\'') { + c = *(dptr + 1); + if (c == 'b' || c == 'h' || c == 'd' || c == 'o') + return NULL; + } + } + + fprintf(outf, "wire "); + if (net->start >= 0 && net->end >= 0) { + fprintf(outf, "[%d:%d] ", net->start, net->end); + } + fprintf(outf, "%s ;\n", p->name); + return NULL; +} + +/*----------------------------------------------------------------------*/ +/* Recursion callback function for each item in the cellrec properties */ +/* hash table */ +/*----------------------------------------------------------------------*/ + +struct nlist *output_props(struct hashlist *p, void *cptr) +{ + char *propval = (char *)(p->ptr); + FILE *outf = (FILE *)cptr; + + fprintf(outf, ".%s(%s),\n", p->name, propval); + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * Resize gates + *--------------------------------------------------------------------------- + */ + +void resize_gates(struct cellrec *topcell, int doLoadBalance, int doFanout) +{ + char *s, *t; + char instname[MAXLINE]; + char *stren, *orig; + int gateinputs; + int pincount; + int needscorrecting; + int i; + struct Gatelist *gl; + struct Gatelist *glbest, *bbest; + struct Gatelist *glbuf; + struct Nodelist *nl; + struct Drivelist *dl; + double inv_size; + + struct netrec *net; + struct portrec *port, *newport; + struct instance *inst, *newinst; + struct cellrec *sizedcell; + int cidx; + + Changed_count = 0; + needscorrecting = 0; + + // Find the gate record corresponding to the buffer name + glbuf = (struct Gatelist *)HashLookup(Buffername, &Gatehash); + + /* If there are situations where a load is high but the fanout is low, */ + /* and insertion of a buffer will reduce the overall delay, then insert a */ + /* buffer. */ + + for (inst = topcell->instlist; inst; inst = inst->next) { + needscorrecting = FALSE; + + /* Find the gate's output node and determine if the gate is resized */ + + gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash); + if (gl == NULL) continue; + + for (port = inst->portlist; port; port = port->next) + if (port->direction == PORT_OUTPUT) + break; + + if (port) { + nl = (struct Nodelist *)HashLookup(port->net, &Nodehash); + if (doLoadBalance && (nl != NULL)) { + if ((nl->ignore == FALSE) && (nl->ratio > 1.0)) { + if (VerboseFlag) + printf("\nGate %s (%s) should be %g times stronger", + inst->instname, inst->cellname, nl->ratio); + needscorrecting = TRUE; + orig = gl->suffix; + glbest = best_size(gl, nl->total_load + WireCap, NULL); + if (glbest && VerboseFlag) + printf("\nGate changed from %s to %s\n", gl->gatename, + glbest->gatename); + inv_size = nl->total_load; + } + + // Is this node an output pin? Check required output drive. + if ((nl->ignore == FALSE) && (nl->type == OUTPUTPIN)) { + orig = gl->suffix; + glbest = best_size(gl, nl->total_load + MaxOutputCap + + WireCap, NULL); + if (glbest && (glbest != gl)) { + if (doLoadBalance) { + needscorrecting = TRUE; + if (VerboseFlag) + printf("\nOutput Gate changed from %s to %s\n", + gl->gatename, glbest->gatename); + } + } + } + // Don't attempt to correct gates for which we cannot + // find a suffix + if (orig == NULL) needscorrecting = FALSE; + } + } + + /* Write cell name, possibly modified for gate strength */ + if (needscorrecting) { + if (glbest == NULL) { // return val to insert inverters + + if (VerboseFlag) + printf("\nInsert buffers %s - %g\n", s, inv_size); + + s = strstr(port->name, nl->nodename); // get output node + s = strtok(s, " \\\t"); // strip it clean + if (*s == '[') { + char *p = strrchr(s, ']'); + if (p != NULL) + strcpy(p, "_bF$buf]\";\n"); // rename it + else + strcat(s, "_bF%buf\";\n"); + } + else + strcat(s, "_bF$buf\";\n"); // rename it + + bbest = best_size(glbuf, inv_size + WireCap, NULL); + + /* If bbest->suffix is NULL, then we will have to break */ + /* up this network. */ + /* Buffer trees will be inserted by downstream tools, */ + /* after analyzing the placement of the network. This */ + /* error needs to be passed down to those tools. . . */ + + if (bbest == NULL) { + fprintf(stderr, "Fatal error: No gates found for %s\n", + glbuf->gatename); + } + + dl = (struct Drivelist *)HashLookup(bbest->suffix, &Drivehash); + if (dl != NULL) dl->NgatesOut++; + + /* Recompute size of the gate driving the buffer */ + if (nl != NULL) { + get_pincap(bbest->gatecell, buf_in_pin, &nl->total_load); + if (gl != NULL) { + nl->total_load += gl->Cint; + } + } + orig = gl->suffix; + glbest = best_size(gl, nl->total_load + WireCap, NULL); + + /* Prepend buffer to instance list */ + newinst = PrependInstance(topcell, bbest->gatename); + sprintf(instname, "%s_insert%d", bbest->gatename, cidx); + newinst->instname = strdup(instname); + newport = InstPort(newinst, buf_in_pin, s); + newport = InstPort(newinst, buf_out_pin, nl->nodename); + cidx++; + + /* Register the new node name */ + if (HashLookup(s, &topcell->nets) == NULL) { + Net(topcell, s); + registernode(s, INPUT, bbest, buf_in_pin); + } + } + if ((gl != NULL) && (gl != glbest)) Changed_count++; + + /* Reassign the instance's cell */ + free(inst->cellname); + inst->cellname = strdup(glbest->gatename); + + /* Adjust the gate count for "in" and "out" types */ + count_gatetype(gl, 0, -1); + count_gatetype(glbest, 0, 1); + } + } +} + +/* + *--------------------------------------------------------------------------- + * Rewrite the verilog output with resized gates and clock and buffer trees + *--------------------------------------------------------------------------- + */ + +void write_output(struct cellrec *topcell, FILE *outfptr, int doLoadBalance, + int doFanout) +{ + struct netrec *net; + struct portrec *port; + struct instance *inst; + + /* Write output module header */ + fprintf(outfptr, "/* Verilog module written by vlogFanout (qflow) */\n"); + if (doFanout) + fprintf(outfptr, "/* With clock tree generation and fanout reduction */\n"); + if (doLoadBalance) + fprintf(outfptr, "/* %s gate resizing */\n", (doFanout) ? "and" : "With"); + fprintf(outfptr, "\n"); + + fprintf(outfptr, "module %s(\n", topcell->name); + for (port = topcell->portlist; port; port = port->next) { + switch(port->direction) { + case PORT_INPUT: + fprintf(outfptr, " input "); + break; + case PORT_OUTPUT: + fprintf(outfptr, " output "); + break; + case PORT_INOUT: + fprintf(outfptr, " inout "); + break; + } + net = HashLookup(port->name, &topcell->nets); + if (net && net->start >= 0 && net->end >= 0) { + fprintf(outfptr, "[%d:%d] ", net->start, net->end); + } + fprintf(outfptr, "%s", port->name); + if (port->next) fprintf(outfptr, ","); + fprintf(outfptr, "\n"); + } + fprintf(outfptr, ");\n\n"); + + /* Declare all wires */ + RecurseHashTablePointer(&topcell->nets, output_wires, outfptr); + fprintf(outfptr, "\n"); + + /* Write instances in the order of the input file */ + + for (inst = topcell->instlist; inst; inst = inst->next) { + int nprops = RecurseHashTable(&inst->propdict, CountHashTableEntries); + fprintf(outfptr, "%s ", inst->cellname); + if (nprops > 0) { + fprintf(outfptr, "#(\n"); + RecurseHashTablePointer(&inst->propdict, output_props, outfptr); + fprintf(outfptr, ") "); + } + if (inst->cellname) + fprintf(outfptr, "%s (\n", inst->instname); + else + fprintf(outfptr, "vlogFanout: No cell for instance %s\n", inst->instname); + + /* Write each port and net connection */ + for (port = inst->portlist; port; port = port->next) { + fprintf(outfptr, " .%s(%s)", port->name, port->net); + if (port->next) fprintf(outfptr, ","); + fprintf(outfptr, "\n"); + } + fprintf(outfptr, ");\n\n"); + } + + /* End the module */ + fprintf(outfptr, "endmodule\n"); + + fflush(stdout); +} + +/* + *--------------------------------------------------------------------------- + * Return a pointer to the gate with the drive strength that is the + * minimum necessary to drive the load "amount". + * + * If the load exceeds the available gate sizes, then return the maximum + * strength gate available, and set "overload" to TRUE. Otherwise, FALSE + * is returned in "overload". + *--------------------------------------------------------------------------- + */ + +struct Gatelist *best_size(struct Gatelist *gl, double amount, char *overload) +{ + char *s; + char ssave; + int ind, i; + double amax = 1.0E10; // Assuming no gate is this big! + double gmax = 0.0; + struct Gatelist *newgl, *glbest = NULL, *glsave = NULL; + struct Baselist *bl; + + if (overload) *overload = FALSE; + if ((s = gl->suffix) == NULL) return NULL; + ind = (int)(s - gl->gatename); // Compare out to and including the suffix + ssave = gl->gatename[ind]; + gl->gatename[ind] = '\0'; + + bl = (struct Baselist *)HashLookup(gl->gatename, &Basehash); + gl->gatename[ind] = ssave; + + for (i = 0; bl && (i < bl->Ndrives); i++) { + newgl = bl->gates[i]; + if (newgl->strength >= gmax) { + gmax = newgl->strength; + glsave = newgl; + } + if (amount <= newgl->strength) { + if (newgl->strength < amax) { + if (newgl->suffix) { + glbest = newgl; + amax = newgl->strength; + } + } + } + } + + if (amax == 1.0E10) { + double oratio; + + stren_err_counter++; + if (overload) *overload = TRUE; + + if (glsave != NULL) + glbest = glsave; + else + glbest = NULL; + + if (gmax > 0.0) { + oratio = (double)(amount / gmax); + if (oratio > MaxOverload) { + + fprintf(stderr, "Warning %d: load of %g is %g times greater " + "than strongest gate %s\n", + stren_err_counter, amount, oratio, glsave->gatename); + + if (MaxOverload == 0.0) + fprintf(stderr, "This warning will only be repeated for " + "larger overload ratios. Warning count reflects\n" + "the total number of overloaded nets.\n"); + + MaxOverload = oratio; + } + } + } + return glbest; +} + +/* + *--------------------------------------------------------------------------- + *--------------------------------------------------------------------------- + */ + +void helpmessage(void) +{ + printf("\nvlogFanout:\n\n"); + printf("vlogFanout looks at a synthesized BLIF netlist.\n"); + printf("Node fanout is measured, and gate size is adjusted.\n"); + printf("File \"gate.cfg\" is used to describe the RTL gates.\n\n"); + + printf("\tUsage: vlogFanout [-switches] vlog_in [vlog_out].\n\n"); + + printf("vlogFanout returns the number of gate substitutions made.\n"); + printf("Typically, it will be iterated until convergence (return value 0).\n\n"); + + printf("valid switches are:\n"); + printf("\t-f\t\tRun gate fanout buffering only (no load balancing)\n"); + printf("\t-L\t\tRun gate load balance optimization only (no fanout buffering)\n"); + printf("\t-g\t\tDebug mode: parse and print the gate.cfg table\n"); + printf("\t-n\t\tDebug mode: parse and print the node list\n"); + printf("\t-v\t\tDebug mode: verbose output\n"); + printf("\t-l latency\tSet the maximum variable latency (ps). " + "(value %g, default 1000.0)\n", MaxLatency); + printf("\t-F value\tSet the maximum fanout per node (value %d, default 16)\n", + MaxFanout); + printf("\t-b buffername\tSet the name of a buffer gate\n"); + printf("\t-i pin_name\tSet the name of the buffer gate input pin (used with -b)\n"); + printf("\t-o pin_name\tSet the name of the buffer gate output pin (used with -b)\n"); + printf("\t-s separator\tGate names have \"separator\" before drive strength\n"); + printf("\t-c value\tSet the maximum output capacitance (fF). " + "(value %g, default 30.0)\n", MaxOutputCap); + printf("\t-p filepath\tSpecify an alternate path and filename for gate.cfg\n"); + printf("\t-I filepath\tSpecify a path and filename for list of nets to ignore\n"); + printf("\t-h\t\tprint this help message\n\n"); + + printf("This will not work at all for tristate gates.\n"); + printf("Nodes with multiple outputs are assumed to be in parallel.\n"); +} + +/* + *--------------------------------------------------------------------------- + * main routine for vlogFanout + *--------------------------------------------------------------------------- + */ + +int main (int argc, char *argv[]) +{ + int i, j, k, l, iter, cidx; + int state; + int maxline, curline; + int libcount; + int inputcount; + int gateinputs; + int gatecount; + int doLoadBalance = 1; + int doFanout = 1; + int nodetype, porttype; + char netname[MAXLINE]; + char *pinname; + char *libfile, *libsep; + char *separg = NULL; + char *test; + char *s, *t, *comptr; + FILE *outfptr; + char *line; + GateRec *Gatepaths = NULL; + struct Gatelist *gl = NULL; + struct Nodelist *nl = NULL, *nlmax, *nlimax; + struct Drivelist *dl = NULL; + struct cellrec *topcell; + struct portrec *port; + struct instance *inst; + struct netrec *net; + int cur_pintype; + + SuffixIsNumeric = TRUE; // By default, assume numeric suffixes + + InitializeHashTable(&Nodehash, LARGEHASHSIZE); + InitializeHashTable(&Bushash, SMALLHASHSIZE); + InitializeHashTable(&Drivehash, SMALLHASHSIZE); + InitializeHashTable(&Gatehash, SMALLHASHSIZE); + InitializeHashTable(&Basehash, SMALLHASHSIZE); + + fprintf(stdout, "vlogFanout for qflow " QFLOW_VERSION "." QFLOW_REVISION "\n"); + + while ((i = getopt(argc, argv, "fLSgnhvl:c:b:i:o:p:s:I:F:")) != EOF) { + switch (i) { + case 'b': + /* If value is a comma-separated pair, the first is a */ + /* general purpose buffer and the second is a clock buffer. */ + Buffername = strdup(optarg); + if ((comptr = strchr(Buffername, ',')) != NULL) { + *comptr = '\0'; + Clkbufname = comptr + 1; + } + break; + case 'i': + buf_in_pin = strdup(optarg); + if ((comptr = strchr(buf_in_pin, ',')) != NULL) { + *comptr = '\0'; + clkbuf_in_pin = comptr + 1; + } + break; + case 'o': + buf_out_pin = strdup(optarg); + if ((comptr = strchr(buf_out_pin, ',')) != NULL) { + *comptr = '\0'; + clkbuf_out_pin = comptr + 1; + } + break; + case 'p': + /* Allow multiple files to be specified as space-separated */ + /* list, either by specifying all on one "-p" arguments */ + /* or by passing multiple "-p" arguments. */ + + if (Gatepaths == NULL) { + libcount = 1; + Gatepaths = (GateRec *)malloc(sizeof(GateRec)); + Gatepaths->path = strdup(optarg); + Gatepaths->sep = (separg) ? strdup(separg) : NULL; + } + else { + libcount++; + Gatepaths = (GateRec *)realloc(Gatepaths, libcount * sizeof(GateRec)); + Gatepaths[libcount - 1].path = strdup(optarg); + Gatepaths[libcount - 1].sep = (separg) ? strdup(separg) : NULL; + } + break; + case 'f': // fanout only + doLoadBalance = 0; + break; + case 'L': // load balance only + doFanout = 0; + break; + case 'I': + Ignorepath = strdup(optarg); + break; + case 'F': + MaxFanout = atoi(optarg); + break; + case 'l': + MaxLatency = atof(optarg); + break; + case 'c': + MaxOutputCap = atof(optarg); + break; + case 's': + if (!strcasecmp(optarg, "none")) { + if (separg) free(separg); + separg = NULL; + } + else if (!strcasecmp(optarg, "nullstring")) { + if (separg) free(separg); + separg = strdup(""); + } + else + separg = strdup(optarg); + break; + case 'S': + if (separg) free(separg); + separg = NULL; + break; + case 'g': + GatePrintFlag = 1; + break; + case 'n': + NodePrintFlag = 1; + break; + case 'v': + VerboseFlag = 1; + break; + case 'h': + helpmessage(); + return 3; + break; + default: + break; + } + } + if (separg) free(separg); + + /* If there is only one set of in and out pins, then assume */ + /* that the pin names apply to both regular and clock */ + /* buffer types. */ + + if (clkbuf_in_pin == NULL) { + clkbuf_in_pin = buf_in_pin; + } + if (clkbuf_out_pin == NULL) { + clkbuf_out_pin = buf_out_pin; + } + + Inputfname = Outputfname = NULL; + outfptr = stdout; + i = optind; + + if (i < argc) { + Inputfname = strdup(argv[i]); + } + i++; + if (i < argc) { + Outputfname = strdup(argv[i]); + if (!(outfptr = fopen(Outputfname, "w"))) { + fprintf(stderr, "vlogFanout: Couldn't open %s for writing.\n", Outputfname); + return 1; + } + } + i++; + + // Make sure we have a valid gate file path + if (Gatepaths == NULL) { + fprintf(stderr, "vlogFanout: No liberty file(s) specified.\n"); + return 1; + } + gatecount = 0; + for (l = 0; l < libcount; l++) { + int loccount; + libfile = Gatepaths[l].path; + libsep = Gatepaths[l].sep; + loccount = read_gate_file(libfile, libsep); + if (loccount == 0) + fprintf(stderr, "vlogFanout: Warning: No gates found in file %s!\n", + libfile); + gatecount += loccount; + } + + // Determine if suffix is numeric or alphabetic + if (gatecount > 0) { + char *suffix; + gl = (struct Gatelist *)HashFirst(&Gatehash); + while (gl && gl->suffix == NULL) + gl = (struct Gatelist *)HashNext(&Gatehash); + if (gl && gl->suffix && !isdigit(*gl->suffix)) + SuffixIsNumeric = FALSE; + } + + if (gatecount == 0) { + fprintf(stderr, "vlogFanout: No gates found in any input file!\n"); + return 1; + } + if (GatePrintFlag) { + showgatelist(); + return 0; + } + + if (buf_in_pin == NULL || buf_out_pin == NULL) { + Pin *curpin; + + gl = (struct Gatelist *)NULL; + + if (Buffername != NULL) { + gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash); + if (gl == NULL) { + fprintf(stderr, "No buffer \"%s\" found in gate list\n", Buffername); + fprintf(stderr, "Searching gate list for suitable buffer.\n"); + } + } + + if ((gl == NULL) || (Buffername == NULL)) { + // Find a suitable buffer + gl = (struct Gatelist *)HashFirst(&Gatehash); + while (gl != NULL) { + Cell *ctest; + + // Find the first gate with one input, one output, + // and a function string that matches the input pin name. + + ctest = gl->gatecell; + if (ctest->pins && ctest->pins->next && !ctest->pins->next->next) { + if (ctest->pins->type == PIN_INPUT && + ctest->pins->next->type == PIN_OUTPUT) { + if (is_buffer_func(ctest->function, ctest->pins->name, + ctest->pins->next->name)) { + fprintf(stdout, "Using cell \"%s\" for buffers.\n", + ctest->name); + Buffername = strdup(ctest->name); + break; + } + } + else if (ctest->pins->type == PIN_OUTPUT && + ctest->pins->next->type == PIN_INPUT) { + if (is_buffer_func(ctest->function, ctest->pins->next->name, + ctest->pins->name)) { + fprintf(stdout, "Using cell \"%s\" for buffers.\n", + ctest->name); + Buffername = strdup(ctest->name); + break; + } + } + } + gl = (struct Gatelist *)HashNext(&Gatehash); + } + } + else + gl = (struct Gatelist *)HashLookup(Buffername, &Gatehash); + + if (gl == NULL) { + if (Buffername == NULL) + fprintf(stderr, "vlogFanout: No suitable buffer cell in library.\n"); + else + fprintf(stderr, "vlogFanout: Buffer cell %s cannot be found.\n", + Buffername); + return 1; + } + for (curpin = gl->gatecell->pins; curpin; curpin = curpin->next) { + if (curpin->type == PIN_INPUT) { + if (buf_in_pin == NULL) + buf_in_pin = strdup(curpin->name); + } + else if (curpin->type == PIN_OUTPUT) { + if (buf_out_pin == NULL) + buf_out_pin = strdup(curpin->name); + } + } + if (buf_in_pin == NULL || buf_out_pin == NULL) { + fprintf(stderr, "vlogFanout: Could not parse I/O pins " + "of buffer cell %s.\n", Buffername); + return 1; + } + } + + /* If Clkbufname is not defined, make it the same as Buffername */ + if (Clkbufname == NULL) Clkbufname = Buffername; + + /* Read the verilog file */ + topcell = ReadVerilog(Inputfname); + + if (topcell == NULL) { + fprintf(stderr, "vlogFanout: No module found in file!\n"); + return 1; + } + + /* Transfer the contents of the verilog top cell into the local database */ + + for (port = topcell->portlist; port; port = port->next) { + int locdir; + + // Translate port direction from definitions in readverilog.h to + // the list of direction definitions in "enum nodetype_" above. + + switch (port->direction) { + case PORT_INPUT: + locdir = INPUTPIN; + break; + case PORT_OUTPUT: + locdir = OUTPUTPIN; + break; + case PORT_INOUT: + locdir = INOUTPIN; + break; + default: + locdir = UNKNOWN; + break; + } + + /* Find net corresponding to the port name and bit blast if needed */ + + net = BusHashLookup(port->name, &topcell->nets); + if (net->start > net->end) { + for (i = net->end; i <= net->start; i++) { + sprintf(netname, "%s[%d]", port->name, i); + registernode(netname, locdir, NULL, NULL); + } + } + else if (net->start < net->end) { + for (i = net->start; i <= net->end; i++) { + sprintf(netname, "%s[%d]", port->name, i); + registernode(netname, locdir, NULL, NULL); + } + } + else { + registernode(port->name, locdir, NULL, NULL); + } + } + + for (inst = topcell->instlist; inst; inst = inst->next) { + /* Match the gate record pulled from verilog to the gate list */ + /* pulled from the liberty file. */ + + if (!inst->cellname) { + fprintf(stderr, "Error: Instance %s does not name a corresponding cell!\n", + inst->instname); + continue; + } + gl = (struct Gatelist *)HashLookup(inst->cellname, &Gatehash); + if (gl != NULL) { + for (port = inst->portlist; port; port = port->next) { + cur_pintype = get_pintype(gl->gatecell, port->name); + switch(cur_pintype) { + case PIN_OUTPUT: + /* Gate output */ + nodetype = OUTPUT; + porttype = PORT_OUTPUT; + break; + case PIN_INPUT: + /* Gate input */ + nodetype = INPUT; + porttype = PORT_INPUT; + break; + case PIN_CLOCK: + /* Gate clock */ + nodetype = CLOCK; + porttype = PORT_INPUT; + break; + default: + /* Unknown */ + nodetype = UNKNOWN; + porttype = PORT_NONE; + break; + } + registernode(port->net, nodetype, gl, port->name); + port->direction = porttype; + } + } + } + + /* get list of nets to ignore, if there is one, and mark nets to ignore */ + if (Ignorepath != NULL) read_ignore_file(Ignorepath); + + if (NodePrintFlag) { + shownodes(); + return 0; + } + + /* Apply iterative buffering and resizing */ + + iter = 0; + cidx = 0; + Changed_count = 1; + while (Changed_count > 0) { + iter++; + + /* Show top fanout gate */ + nlmax = NULL; + nlimax = NULL; + nl = (struct Nodelist *)HashFirst(&Nodehash); + while (nl != NULL) { + if (nl->outputgatestrength != 0.0) { + nl->ratio = nl->total_load / nl->outputgatestrength; + } + if (nl->ignore == FALSE) { + if ((nl->num_inputs >= Topfanout) && (nl->outputgatestrength != 0.0)) { + Topfanout = nl->num_inputs; + nlmax = nl; + } + else if ((nl->num_inputs >= Inputfanout) && (nl->type == INPUTPIN)) { + Inputfanout = nl->num_inputs; + nlimax = nl; + } + if ((nl->ratio >= Topratio) && (nl->outputgatestrength != 0.0)) { + Topratio = nl->ratio; + } + if ((nl->total_load >= Topload) && (nl->outputgatestrength != 0.0)) { + Topload = nl->total_load; + } + else if ((nl->total_load >= Inputload) && (nl->type == INPUTPIN)) { + Inputload = nl->total_load; + } + } + nl = (struct Nodelist *)HashNext(&Nodehash); + } + + if (VerboseFlag) printf("\nIteration %d\n", iter); + fflush(stdout); + + if (nlmax) { + fprintf(stderr, "Top internal fanout is %d (load %g) from node %s,\n" + "driven by %s with strength %g (fF driven at latency %g)\n", + Topfanout, Topload, nlmax->nodename, + nlmax->outputgate->gatename, + nlmax->outputgatestrength, + MaxLatency); + + fprintf(stderr, "Top fanout load-to-strength ratio is %g (latency = %g ps)\n", + Topratio, MaxLatency * Topratio); + + fprintf(stderr, "Top input node fanout is %d (load %g) from node %s.\n", + Inputfanout, Inputload, nlimax->nodename); + } + + fprintf(stderr, "%d gates exceed specified minimum load.\n", stren_err_counter); + + if (doFanout) cidx = insert_buffers(topcell, cidx); + fprintf(stderr, "%d buffers were added.\n", Buffer_count); + + resize_gates(topcell, doLoadBalance, doFanout); + fprintf(stderr, "%d gates were changed.\n", Changed_count); + + fprintf(stderr, "\nGate counts by drive strength:\n\n"); + dl = (struct Drivelist *)HashFirst(&Drivehash); + while (dl != NULL) { + if (dl->NgatesIn > 0) { + fprintf(stderr, "\t\"%s%s\" gates\tIn: %d \tOut: %d \t%+d\n", + dl->Separator, dl->DriveType, dl->NgatesIn, + dl->NgatesOut, (dl->NgatesOut - dl->NgatesIn)); + } + dl = (struct Drivelist *)HashNext(&Drivehash); + } + fprintf(stderr, "\n"); + } + + write_output(topcell, outfptr, doLoadBalance, doFanout); + if (outfptr != stdout) fclose(outfptr); + + // Output number of gates changed so we can iterate until this is zero. + + fprintf(stdout, "Number of gates changed: %d\n", Changed_count + Buffer_count); + return 0; +} + +/* end of vlogFanout.c */ |