summaryrefslogtreecommitdiff
path: root/src/vlogFanout.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vlogFanout.c')
-rw-r--r--src/vlogFanout.c1735
1 files changed, 1735 insertions, 0 deletions
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 */