diff options
Diffstat (limited to 'src/vlogFanout.c')
-rw-r--r-- | src/vlogFanout.c | 1735 |
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 */ |