summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DEF2Verilog.c482
-rw-r--r--src/Makefile.in87
-rw-r--r--src/addspacers.c2026
-rw-r--r--src/blif2BSpice.c4
-rw-r--r--src/blif2Verilog.c38
-rw-r--r--src/blifFanout.c86
-rw-r--r--src/hash.c134
-rw-r--r--src/hash.h51
-rw-r--r--src/lef.h239
-rw-r--r--src/rc2dly.c71
-rw-r--r--src/readdef.c2091
-rw-r--r--src/readdef.h31
-rw-r--r--src/readlef.c3436
-rw-r--r--src/readlef.h220
-rw-r--r--src/readliberty.c91
-rw-r--r--src/readliberty.h2
-rw-r--r--src/readverilog.c1989
-rw-r--r--src/readverilog.h118
-rw-r--r--src/spice2delay.c28
-rw-r--r--src/vesta.c593
-rw-r--r--src/vlog2Cel.c571
-rw-r--r--src/vlog2Def.c480
-rw-r--r--src/vlog2Spice.c636
-rw-r--r--src/vlog2Verilog.c1018
-rw-r--r--src/vlogFanout.c1735
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", &copyspecial);
+ 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++) {
diff --git a/src/hash.c b/src/hash.c
index c18f9a2..70ffd07 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -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));
}
diff --git a/src/hash.h b/src/hash.h
index a7e9d86..380832d 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -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 */