// rc2dly
//
// This program takes the .rc file generated by qrouter as an input,
// calculates the Elmore Delay for each interconnect, and outputs the data in
// the delay file format specified in the vesta static timing tool
//
// An Example of the rc file format follows
//
// <net_name> <num_drivers> <driver0> [driverN] <num_receivers> ( R0 C0 ...
//
// - ohms for R
// - pF for C
//
// A more concrete example demonstrating how branches of the physical
// interconnect are specified.
//
// clock<0> 1 PIN/clock<0> 3 ( 3.96667 0.0003468 ( 0 0 BUFX2_9/A ,
// ( 6.33333 0.000684 BUFX2_7/A ) , ( 0.373333 5.76e-05 BUFX2_8/A ) ) )
//
// This parses out as net name = clock<0>, one driver node, driver
// node name is "PIN/clock<0>", net has 3 endpoints, and the route
// tree structure is:
//
// driver --> --> BUFX2_9/A
//               |
//               +--> BUFX2_7/A
//               |
//               +--> BUFX2_8/A
//
// Todo
// 2) double check all read in c and r values for units
//
// Written by Russell Freisenhahn
// Added to qflow August 12, 2017
// Added alternate SPEF output format December 6, 2017 (Tim Edwards)

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <time.h>

#include "hash.h"
#include "readliberty.h"	/* liberty file database */
#include "readverilog.h"	/* verilog netlist reader */

#define SRC     0x01    // node is a driver
#define SNK     0x02    // node is a receiver
#define INT     0x03    // node is internal to an interconnect

#define FORMAT_VESTA 0
#define FORMAT_SPEF  1
#define FORMAT_SDF   2

#define VISIT_CONN  0
#define VISIT_CAP   1
#define VISIT_RES   2

/* Hash tables */
struct hashtable LibHash;
struct hashtable InstHash;

typedef struct _r *rptr;
typedef struct _node *nodeptr;

typedef struct _r {
    char       *name;
    nodeptr     node1;
    nodeptr     node2;
    double      rval;
} r;

typedef struct _ritem* ritemptr;

typedef struct _ritem {
    rptr        r;
    ritemptr    next;
} ritem;


typedef struct _node {
    char*       name;
    char	mapped[12];
    int         type;
    ritemptr    rlist;
    ritemptr    rlist_end;	/* To avoid having to find the list end */
    double      nodeCap;
    double      totCapDownstream;
    double      totCapDownstreamLessGates;
    short       visited;
} node;

/*----------------------------------------------------------*/

void print_node (nodeptr node) {
    printf("Name: %s\n", node->name);
    printf("Type: %d\n", node->type);
    printf("Cap: %.10f\n", node->nodeCap);
    printf("DownstreamCap: %.10f\n", node->totCapDownstream);
    printf("DownstreamCapLessGates: %.10f\n", node->totCapDownstreamLessGates);
    printf("\n");
}

typedef struct _node_item *node_item_ptr;

typedef struct _node_item {
    nodeptr         node;
    node_item_ptr   prev;
    node_item_ptr   next;
} node_item;

typedef struct _snk* snkptr;

typedef struct _snk {
    nodeptr     snknode;
    double      delay;
    snkptr      next;
} snk;

typedef struct _elmdly* elmdlyptr;

typedef struct _elmdly {
    char        *name;
    nodeptr     src;
    snkptr      snklist;
} elmdly;

typedef struct _elmdly_item *elmdly_item_ptr;

typedef struct _elmdly_item {
    elmdlyptr       elmdly;
    elmdly_item_ptr next;
} elmdly_item;

void print_help () {
    printf("NAME\n");
    printf("    rc2dly - convert qrouter RC output file to delay file\n\n");
    printf("SYNOPSIS\n");
    printf("    rc2dly -r <rc_file_name> -l <stdcell_liberty_file_name> -d <output_delay_file_name>\n");
    printf("		-V <verilog_netlist_name>\n");
    printf("\n");
    printf("DESCRIPTION\n");
    printf("    Converts output of qrouter to one of three different delay formats,\n");
    printf("    depending on the file extension of the -d argument.  If the extension\n");
    printf("    is .spef, then SPEF format is used.  If the extension is .sdf, then\n");
    printf("    SDF format is used.  Otherwise, the input format used by vesta is\n");
    printf("    generated.\n");
    printf("\n");
    printf("Required Arguments\n");
    printf("    -r <rc_file_name>\n");
    printf("    -l <stdcell_liberty_file_name\n");
    printf("    -V <verilog_netlist_name\n");
    printf("\n");
    printf("Optional Arguments\n");
    printf("    -d <output_delay_file_name>\n");
    printf("    -c <module_pin_capacitance_in_pF>\n");
    printf("\n");
}

char** tokenize_line (char *line, const char *delims, char*** tokens_ptr, int *num_toks) {
    int buff_sz = 4;

    char **tokens = calloc(buff_sz, sizeof(char*));

    int i = 0;

    tokens[i] = strtok(line, delims);
    i++;

    for (i = 1; tokens[i-1] != NULL; i++) {
        if (i == buff_sz) {
            buff_sz *= 2;
            tokens = realloc(tokens, sizeof(char*) * buff_sz);
        }
        *num_toks = i;
        tokens[i] = strtok(NULL, delims);
    }

    /**tokens_ptr = tokens;*/
    return tokens;
}

nodeptr create_node (char *name, int type, double nodeCap) {
    nodeptr new_node = calloc(1, sizeof(node));

    new_node->name = calloc(strlen(name) + 1, sizeof(char));
    strcpy(new_node->name, name);
    new_node->type = type;
    new_node->nodeCap = nodeCap;
    new_node->rlist = NULL;
    new_node->rlist_end = NULL;
    new_node->totCapDownstream = 0.0;
    new_node->totCapDownstreamLessGates = 0.0;
    new_node->visited = 0;
    new_node->mapped[0] = '\0';

    return new_node;
}

void add_node_item (node_item_ptr *node_item_list_ptr, nodeptr n, node_item_ptr *last_node_item) {

    node_item_ptr next = calloc(1, sizeof(node_item));
    next->node = n;

    // list has no items
    if (*node_item_list_ptr == NULL) {

        *node_item_list_ptr = next;

        if (last_node_item != NULL) {
            *last_node_item = next;
        }
    } else {

        // list has some items, we need to find the end
        if (last_node_item == NULL) {
            node_item_ptr i;

            for (i = *node_item_list_ptr; i->next != NULL; i = i->next);

            i->next = next;
            next->prev = i;
        } else {
            next->prev = *last_node_item;
            (*last_node_item)->next = next;
            *last_node_item = next;
        }
    }
}

void add_ritem (ritemptr *ritem_list_ptr, rptr r, ritemptr *ritem_last_ptr) {
    ritemptr next = calloc(1, sizeof(ritem));
    next->r = r;

    // list has no items
    if (*ritem_list_ptr == NULL) {

        *ritem_list_ptr = next;

    } else if (*ritem_last_ptr != NULL) {
	ritemptr i;
	i = *ritem_last_ptr;
	i->next = next;
	*ritem_last_ptr = next;
    } else {

    // list has some items, we need to find the end
        ritemptr i;

        for (i = *ritem_list_ptr; i->next != NULL; i = i->next);

        i->next = next;
	*ritem_last_ptr = next;
    }
}

void add_elmdly_item (elmdly_item_ptr *elmdly_item_list_ptr, elmdlyptr elmdly) {
    elmdly_item_ptr next = calloc(1, sizeof(elmdly_item));
    next->elmdly = elmdly;

    // list has no items
    if (*elmdly_item_list_ptr == NULL) {

        *elmdly_item_list_ptr = next;

    } else {

        // list has some items, we need to find the end
        elmdly_item_ptr i;

        for (i = *elmdly_item_list_ptr; i->next != NULL; i = i->next);

        i->next = next;
    }
}

// for multi-driver nets, must not recurse finding another driver
void sum_downstream_cap(nodeptr curr_node, nodeptr prev_node) {

    ritemptr curr_ritem = curr_node->rlist;

    while (curr_ritem != NULL) {
        // make sure to not backtrack to previous node
        // make sure to not recurse on the current node
        if (    (curr_ritem->r->node1 != prev_node)
            &&  (curr_ritem->r->node1 != curr_node)
           ) {

            sum_downstream_cap(curr_ritem->r->node1, curr_node);
            curr_node->totCapDownstream += (curr_ritem->r->node1->totCapDownstream + curr_ritem->r->node1->nodeCap);
	    if (curr_ritem->r->node1->type != SNK) {
		curr_node->totCapDownstreamLessGates += (curr_ritem->r->node1->totCapDownstreamLessGates + curr_ritem->r->node1->nodeCap);
	    }
        } else if (     (curr_ritem->r->node2 != prev_node)
                    &&  (curr_ritem->r->node2 != curr_node)
           ) {

            sum_downstream_cap(curr_ritem->r->node2, curr_node);
            curr_node->totCapDownstream += (curr_ritem->r->node2->totCapDownstream + curr_ritem->r->node2->nodeCap);
	    if (curr_ritem->r->node2->type != SNK) {
		curr_node->totCapDownstreamLessGates += (curr_ritem->r->node2->totCapDownstreamLessGates + curr_ritem->r->node2->nodeCap);
	    }
        }

        curr_ritem = curr_ritem->next;
    }
}

void add_snk (snkptr *snk_list_ptr, snkptr snk) {

    // list has no items
    if (*snk_list_ptr == NULL) {

        *snk_list_ptr = snk;

    } else {

        // list has some items, we need to find the end
        snkptr i;

        for (i = *snk_list_ptr; i->next != NULL; i = i->next);

        i->next = snk;
    }
}

/* Recursive routine to visit all nodes of a net */

static int nid, snid, net_idx;

void visit_nodes(
	nodeptr	curr_node,
	nodeptr	prev_node,
	int mode,
	FILE *outfile
	) {

    char type;
    ritemptr curr_ritem;

    switch (mode) {
	case VISIT_CONN:
	    type = 'I';
	    if (!strncmp(curr_node->name, "PIN/", 4)) {
		type = 'P';
	    }
	    if (curr_node->type == SNK) {
		fprintf(outfile, "*%c %s I", type, curr_node->mapped);
		if (type == 'I')
		    fprintf(outfile, " *L %g", curr_node->nodeCap);
		fprintf(outfile, "\n");
	    }
	    else if (curr_node->type == SRC) {
		char *gateend;
		char *sepptr = strrchr(curr_node->name, '/');
		fprintf(outfile, "*%c %s O", type, curr_node->mapped);
		if (sepptr != NULL) {
		    *sepptr = '\0';
		    gateend = strrchr(curr_node->name, '_');
		    *sepptr = '/';
		    if (gateend != NULL) {
			*gateend = '\0';
			fprintf(outfile, " *D %s", curr_node->name);
			*gateend = '_';
		    }
		}
		fprintf(outfile, "\n");
	    }
	    break;
	case VISIT_CAP:
	    if (curr_node->type == INT) {
		snid++;
		fprintf(outfile, "%d %s %g\n", snid,
			curr_node->mapped,
			curr_node->nodeCap);
	    }
	    break;
    }

    curr_ritem = curr_node->rlist;

    while (curr_ritem != NULL) {
	switch (mode) {
	    case VISIT_RES:
		// NOTE:  Node pairs get visited twice in succession,
		// so output only once per pair.
		snid++;
		if (snid % 2) {
		    fprintf(outfile, "%d %s %s %g\n", snid >> 1,
				curr_ritem->r->node1->mapped,
				curr_ritem->r->node2->mapped,
				curr_ritem->r->rval);
		}
		break;
	}
        if ((curr_ritem->r->node1 != prev_node) &&
			(curr_ritem->r->node1 != curr_node))
	    visit_nodes(curr_ritem->r->node1, curr_node, mode, outfile);
        if ((curr_ritem->r->node2 != prev_node) &&
			(curr_ritem->r->node2 != curr_node))
	    visit_nodes(curr_ritem->r->node2, curr_node, mode, outfile);

        curr_ritem = curr_ritem->next;
    }
}

void calculate_elmore_delay (
        nodeptr     curr_node,
        nodeptr     prev_node,
        rptr        prev_r, // the connection used to get curr_node
        elmdlyptr   curr_elmdly,
        /*snkptr      curr_snk,*/
        double      firstR,
        double      elmdly,
        int         verbose
        ) {

    // -recursively walk each branch of nodes
    // -accumulate delay on each branch
    // -append to Elmore Delay list when sink node reached

    // accumulate delay
    // -first node uses a model resistor based on typical output drive strengths
    //  of stdcell librarie
    // -subsequent nodes us the resistor that was traveled to arrive at current
    //  node

    if (verbose > 3) {
        fprintf(stdout, "INFO: node is %s with current delay of %.10f\n", curr_node->name, elmdly);
    }
    if (curr_node->type == SRC) {
        elmdly = firstR * (curr_node->nodeCap + curr_node->totCapDownstream);
        if (verbose > 3) {
            fprintf(stdout, "INFO: SRC node in elmore delay calc\n");
        }
    } else {
        if (verbose > 3) {
            fprintf(stdout, "INFO: not SRC node in elmore delay calc\n");
        }
        elmdly += prev_r->rval * (curr_node->nodeCap + curr_node->totCapDownstream);
    }

    // -if current node is an input to another cell, this is an endpoint and the
    //  current delay value needs to be saved
    // -there still might be other connections though that need to be traversed
    //  to find other endpoints
    if (curr_node->type == SNK) {

        if (verbose > 3) {
            printf("INFO: Found SNK node %s with delay to it of %.10f\n", curr_node->name, elmdly);
        }
        snkptr curr_snk = calloc(1, sizeof(snk));

        curr_snk->snknode = curr_node;
        curr_snk->delay = elmdly;

        add_snk(&curr_elmdly->snklist, curr_snk);
    }

    ritemptr curr_ritem = curr_node->rlist;

    while (curr_ritem != NULL) {
        // make sure to not backtrack to previous node
        // make sure to not recurse on the current node
        if (    (curr_ritem->r->node1 != prev_node)
            &&  (curr_ritem->r->node1 != curr_node)
           ) {

            calculate_elmore_delay(curr_ritem->r->node1, curr_node, curr_ritem->r, curr_elmdly, firstR, elmdly, verbose);
            if (verbose > 1) printf("TEST: %s %f %f\n", curr_node->name, curr_node->nodeCap, curr_node->totCapDownstream);

        } else if (     (curr_ritem->r->node2 != prev_node)
                    &&  (curr_ritem->r->node2 != curr_node)
           ) {

            calculate_elmore_delay(curr_ritem->r->node2, curr_node, curr_ritem->r, curr_elmdly, firstR, elmdly, verbose);
            if (verbose > 1) printf("TEST: %s %f %f\n", curr_node->name, curr_node->nodeCap, curr_node->totCapDownstream);

        }

        curr_ritem = curr_ritem->next;
    }
}

int main (int argc, char* argv[]) {

    FILE* outfile = stdout;
    FILE* libfile = NULL;
    FILE* rcfile = NULL;

    struct cellrec *topcell = NULL;

    int verbose = 0;

    double modulePinCapacitance = 0;

    Cell *cells = NULL, *newcell, *libcells;
    Pin *newpin;
    char* libfilename = NULL;
    char* nodenameptr;
    char* design = NULL;
    char* dotptr = NULL;
    char  delimiter = '/';

    nodeptr currnode = NULL;
    rptr    currR    = NULL;
    snkptr currSnk = NULL;

    // pointer to last node in a doubly-linked list consisting of node_items
    node_item_ptr currNodeStack = NULL;
    node_item_ptr allNodes = NULL;
    node_item_ptr lastNode = NULL;

    // -Maintain a list of all nodes that are outputs / drivers.
    // -Iterate over the list to walk each interconnect to calculate
    //  Elmore Delay
    node_item_ptr drivers = NULL;
    node_item_ptr last_driver = NULL;
    int format = FORMAT_VESTA;

    // list of all Rs for debugging and to easily free them at end
    ritemptr allrs = NULL;
    ritemptr allrs_end = NULL;

    elmdly_item_ptr delays = NULL;

    // Command-line argument parsing
    int c;

    while (1) {
        static struct option long_options[] = {
            {"rc-file"      , required_argument , 0, 'r'},
            {"liberty-file" , required_argument , 0, 'l'},
            {"verilog-file" , required_argument , 0, 'V'},
            {"delay-file"   , required_argument , 0, 'd'},
            {"pin-capacitance"   , required_argument , 0, 'c'},
            {"delimiter"    , required_argument , 0, 'D'},
            {"verbose"      , required_argument , 0, 'v'},
            {"help"         , no_argument       , 0, 'h'},
            {0, 0, 0, 0}
        };

        /* getopt_long stores the option index here. */
        int option_index = 0;

        c = getopt_long (argc, argv, "hv:r:l:d:D:V:", long_options, &option_index);

        /* Detect the end of the options. */
        if (c == -1)
            break;

        switch (c) {
            case 0:
                /* If this option set a flag, do nothing else now. */
                if (long_options[option_index].flag != 0)
                    break;
                printf ("option %s", long_options[option_index].name);
                if (optarg)
                    printf (" with arg %s", optarg);
                printf ("\n");
                break;

            case 'r':
                rcfile = fopen(optarg, "r");
		design = strdup(optarg);
		if ((dotptr = strrchr(design, '.')) != NULL) *dotptr = '\0';

                if (!rcfile) {
                    fprintf(stderr, "ERROR: Unable to open input RC file `%s': %s\n", optarg, strerror(errno));
                }
                break;

            case 'D':
		delimiter = optarg[0];
		if (optarg[1] != '\0')
		    fprintf(stderr, "ERROR: Delimiter \"%s\" must be one character: %s\n",
				optarg, strerror(errno));
                break;

	    case 'V':
		topcell = ReadVerilog(optarg);
		break;

            case 'l':
                libfile = fopen(optarg, "r");
		if (libfilename) free(libfilename);
                libfilename = strdup(optarg);

                if (!libfile) {
                    fprintf(stderr, "ERROR: Unable to open input Liberty "
				"timing file`%s': %s\n", optarg, strerror(errno));
                }
		else {
		    // Read in Liberty file
		    printf("Reading Liberty timing file %s\n", libfilename);
		    libcells = read_liberty(libfilename, 0);
		    fclose(libfile);
		    if (cells == NULL)
			cells = libcells;
		    else {
			for (newcell = cells; newcell->next; newcell = newcell->next);
			newcell->next = libcells;
		    }
		}
                break;

            case 'd':
                if (!strcmp(optarg, "-")) {
                    outfile = stdout;
                } else {
                    outfile = fopen(optarg, "w");
                }
                if (!outfile) {
                    fprintf(stderr, "ERROR: Unable to open output file `%s': "
				"%s\n", optarg, strerror(errno));
                }
		else {
		    dotptr = strrchr(optarg, '.');
		    if (dotptr != NULL)
			if (!strcmp(dotptr, ".spef"))
			    format = FORMAT_SPEF;
			else if (!strcmp(dotptr, ".sdf"))
			    format = FORMAT_SDF;
		}
                break;

            case 'c':
                modulePinCapacitance = atof(optarg);
                break;

            case 'h':
                print_help();
                break;

            case 'v':
                verbose = atoi(optarg);
                break;

            default:
                print_help();
                return 0;
        }
    }

    if (rcfile == NULL) {
        fprintf(stderr, "ERROR: Must specify input RC file.\n");
        return 1;
    }

    if (libfilename == NULL) {
        fprintf(stderr, "ERROR: Must specify at least one input Liberty timing file.\n");
        return 1;
    }

    if (topcell == NULL) {
	fprintf(stderr, "ERROR: Must specify input verilog netlist.\n");
	return 1;
    }

    if (cells == NULL) {
        fprintf(stderr, "ERROR: No cells were read from Liberty timing files.\n");
	return 5;
    }

    if (verbose > 3) {
        for (newcell = cells; newcell; newcell = newcell->next) {
	    if (newcell->name == NULL) continue;  /* "don't use" cell */
            fprintf(stdout, "Cell: %s\n", newcell->name);
            fprintf(stdout, "   Function: %s\n", newcell->function);
            for (newpin = newcell->pins; newpin; newpin = newpin->next) {
                fprintf(stdout, "   Pin: %s  cap=%g\n", newpin->name, newpin->cap);
            }
            fprintf(stdout, "\n");
        }
    }

    char *line;
    size_t nbytes = LIB_LINE_MAX;
    line = calloc(1, LIB_LINE_MAX);
    int bytesRead = 0;
    int num_net_drivers;

    const char delims[3] = " \n";

    char **tokens;
    int num_toks = 0;
    struct instance *inst;

    InitializeHashTable(&LibHash, SMALLHASHSIZE);
    InitializeHashTable(&InstHash, LARGEHASHSIZE);

    /* Hash the liberty cell records */

    for (newcell = cells; newcell; newcell = newcell->next)
	HashPtrInstall(newcell->name, newcell, &LibHash);

    /* Hash the verilog instances by liberty cell record */

    for (inst = topcell->instlist; inst; inst = inst->next) {
	newcell = (Cell *)HashLookup(inst->cellname, &LibHash);
	HashPtrInstall(inst->instname, newcell, &InstHash);
    }

    /* LibHash is no longer needed */
    HashKill(&LibHash);

    if (format == FORMAT_SPEF) {
	char outstr[200];
	time_t t;
	struct tm *tmp;
	char *dptr;

	/* Write SPEF file format output header */
	t = time(NULL);
	tmp = localtime(&t);
	strftime(outstr, 200, "%H:%M:%S %A %B %d, %Y", tmp);

	fprintf(outfile, "*SPEF \"IEEE 1481.1999\"\n");
	fprintf(outfile, "*DESIGN \"%s\"\n", design);
	fprintf(outfile, "*DATE \"%s\"\n", outstr);
	fprintf(outfile, "*VENDOR \"%s\"\n", "unknown");
	fprintf(outfile, "*PROGRAM \"%s\"\n", "qrouter");
	fprintf(outfile, "*VERSION \"%s\"\n", "unknown");
	fprintf(outfile, "*DESIGN_FLOW \"%s\"\n", "qflow");
	fprintf(outfile, "*DIVIDER %s\n", "/");
	fprintf(outfile, "*DELIMITER %c\n", delimiter);
	fprintf(outfile, "*BUS_DELIMITER %s\n", "<>");
	fprintf(outfile, "*T_UNIT 1 PS\n");
	fprintf(outfile, "*C_UNIT 1 FF\n");
	fprintf(outfile, "*R_UNIT 1 OHM\n");
	fprintf(outfile, "*L_UNIT 1 HENRY\n");
	fprintf(outfile, "\n");
	fprintf(outfile, "*NAME_MAP\n");

	/* Parse entire file once to get all node names.  These will be	*/
	/* assigned numerical values in the order seen, so they can be	*/
	/* referenced later with the same numerical value.  This avoids	*/
	/* having to recast the node name to conform to SPEF rules.	*/

        nid = 1;
	while ((bytesRead = getline(&line, &nbytes, rcfile)) > 0) {
	    if (bytesRead > 2) {
	        tokens = tokenize_line(line, delims, &tokens, &num_toks);
		// Count nodes per line.  Drivers are named nodes unless
		// they are pins (redundant name), and the third token
		// after an open parenthesis is a named node if it is not
		// another open parenthesis.  The net name is not a node
		// but gets its own name identifier.
		if ((dptr = strrchr(tokens[0], '/')) != NULL)
		    *dptr = '\0';
		// Note:  Delimiter and pin not part of mapped name, as some
		// parsers do not accept that (#$@! wishy-washy spec).
	        fprintf(outfile, "*%d %s\n", nid++, tokens[0]);
                num_net_drivers = atoi(tokens[1]);
		for (t = 2; t < num_net_drivers + 2; t++)  {
	            if (strncmp(tokens[t], "PIN/", 4)) {
			if ((dptr = strrchr(tokens[t], '/')) != NULL)
			    *dptr = '\0';
			fprintf(outfile, "*%d %s\n", nid++, tokens[t]);
		    }
		}
	        for (; t < num_toks; t++) {
	            if (!strcmp(tokens[t], "("))
	                if (strcmp(tokens[t + 3], "("))
			    if (strncmp(tokens[t + 3], "PIN/", 4)) {
				if ((dptr = strrchr(tokens[t + 3], '/')) != NULL)
				    *dptr = '\0';
				fprintf(outfile, "*%d %s\n", nid++, tokens[t + 3]);
			    }
		}
	    }
	}
	fprintf(outfile, "\n");

	/* Go back to the beginning of the file */
	rewind(rcfile);

	fprintf(outfile, "*PORTS\n");

	/* Parse entire file a second time to get all port names, which	*/
	/* are the entries ending with "/PIN".                          */

        nid = 1;
	while ((bytesRead = getline(&line, &nbytes, rcfile)) > 0) {
	    if (bytesRead > 2) {
	        tokens = tokenize_line(line, delims, &tokens, &num_toks);
		// Count nodes per line, as above.

		net_idx = nid++;
                num_net_drivers = atoi(tokens[1]);
		for (t = 2; t < 2 + num_net_drivers; t++) {
		    if (!strncmp(tokens[t], "PIN/", 4))
			fprintf(outfile, "*%d I\n", net_idx);
		    else
			nid++;
		}
	        for (; t < num_toks; t++) {
	            if (!strcmp(tokens[t], "(")) {
	                if (strcmp(tokens[t + 3], "(")) {
			    if (!strncmp(tokens[t + 3], "PIN/", 4))
	                	fprintf(outfile, "*%d O\n", net_idx);
			    else
				nid++;
			}
		    }
		}
	    }
	}
	fprintf(outfile, "\n");

	/* Go back to the beginning of the file */
	rewind(rcfile);
    }
    else if (format == FORMAT_SDF) {
	char outstr[200];
	time_t t;
	struct tm *tmp;

	/* Write SDF file format output header */
	t = time(NULL);
	tmp = localtime(&t);
	strftime(outstr, 200, "%H:%M:%S %A %B %d, %Y", tmp);

	fprintf(outfile, "(DELAYFILE\n");
	fprintf(outfile, "   (SDFVERSION \"3.0\")\n");
	fprintf(outfile, "   (DESIGN \"%s\")\n", design);
	fprintf(outfile, "   (DATE \"%s\")\n", outstr);
	fprintf(outfile, "   (VENDOR \"%s\")\n", "unknown");
	fprintf(outfile, "   (PROGRAM \"%s\")\n", "qrouter");
	fprintf(outfile, "   (VERSION \"%s\")\n", "unknown");
	fprintf(outfile, "   (DIVIDER /)\n");
	fprintf(outfile, "   (TIMESCALE 1 ps)\n");
	fprintf(outfile, "   (CELL\n");
	fprintf(outfile, "      (CELLTYPE \"%s\")\n", design);
	fprintf(outfile, "      (INSTANCE)\n");
	fprintf(outfile, "      (DELAY\n");
	fprintf(outfile, "         (ABSOLUTE\n");
    }

    bytesRead = getline(&line, &nbytes, rcfile);

    // <net> <num_net_drivers> <driver_node_0> [drive_node_n] <num_receivers> (R1 C1
    // <terminal>, R2 C2 <terminal>, ...)
    //
    // Parsing States for .rc file
    // 1) net / interconnect name
    // 2) num drivers
    // 3) process listed drivers
    // 4) num receivers
    //

    int num_rxers = 0;
    int t = 0;
    Cell *cell;
    char *pname;
    node_item_ptr tmp_nip = NULL;

    num_net_drivers = 0;
    nid = 1;
    while (bytesRead > 0) {

        // skip blank lines
        if (bytesRead > 2) {

            tokens = tokenize_line(line, delims, &tokens, &num_toks);

            t = 0;
	    net_idx = nid++;	/* net takes the next name ID */

	    if (verbose > 3)
                fprintf(stdout, "\nProcessing net %s\n", tokens[0]);

            t += 1;

            // process number of drivers
            num_net_drivers = atoi(tokens[t]);
            //fprintf(stdout, "Number of drivers is %d\n", num_net_drivers);
            t += 1;

            // process drivers
            for (; t < (2 + num_net_drivers); t++) {
	        if (verbose > 3)
                    fprintf(stdout, "TBD: process driver number %d %s\n", t-2, tokens[t]);
            }

            // no t increment is required as for loop gets us to proper index after last driver
            num_rxers = atoi(tokens[t]);
            t += 1;

            // process remaining tokens which contains R's, C's, node connections, and rxers
            int nodeNum = 0;
            int rNum = 0;
            char *name = NULL;

            while(t < num_toks) {

                if (!strcmp(tokens[t], "(")) {

                    // check if this is the first node
                    if (nodeNum == 0) {

                        // assemble node name based on interconnect name and node number
                        name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 10));

                        if (sprintf(name, "%s_n%d", tokens[0], nodeNum) < 0) {
                            fprintf(stderr, "ERROR: sprintf failed to create interconnect node name\n");
                            return 2;
                        }

                        // create a new node, this one is the first (driving) node of the interconnect
                        currnode = create_node(tokens[2], SRC, 0);
			// If driver name is a pin then the name ID is the net name ID
			if (!strncmp(tokens[2], "PIN/", 4))
			    snprintf(currnode->mapped, 12, "*%d", net_idx);
			else {
			    pname = strrchr(tokens[2], '/') + 1;
			    if (pname)
				snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
						delimiter, pname);
			    else
				/* Node name is hosed but don't crash the program */
				snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
						delimiter, tokens[2]);
			}

                        if (verbose > 1) print_node(currnode);

                        // add node to list of drivers
                        add_node_item(&drivers, currnode, &last_driver);
			num_net_drivers++;

                        // add node to current node stack
                        add_node_item(&currNodeStack, currnode, &currNodeStack);
                        add_node_item(&allNodes, currnode, &lastNode);

                        //printf("%s_n%d\n", tokens[0], nodeNum);

                        nodeNum += 1;
                    }

                    name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 10));
                    if (sprintf(name, "%s_n%d", tokens[0], nodeNum) < 0) {
                        fprintf(stderr, "ERROR: sprintf failed to create interconnect node name\n");
                        return 2;
                    }
                    // create the new node
                    currnode = create_node(name, INT, atof(tokens[t+2]));
		    snprintf(currnode->mapped, 12, "%d_%d", net_idx, nodeNum);
                    nodeNum++;

                    if (verbose > 1) {
                        print_node(currnode);
                        fprintf(stdout, "nodeCap of new node is %.10f\n", atof(tokens[t+2]));
                    }

                    name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 10));
                    if (sprintf(name, "%s_r%d", tokens[0], rNum) < 0) {
                        fprintf(stderr, "ERROR: sprintf failed to create resistor name\n");
                        return 2;
                    }
                    rNum += 1;
                    // create resistor
                    currR = calloc(1, sizeof(r));
                    currR->name = name;
                    currR->node1 = currNodeStack->node;
                    currR->node2 = currnode;
                    currR->rval = atof(tokens[t+1]);
                    // add resistor to each node's resistor list and the global list
                    add_ritem(&currNodeStack->node->rlist, currR, &currNodeStack->node->rlist_end);
                    add_ritem(&currnode->rlist, currR, &currnode->rlist_end);
                    add_ritem(&allrs, currR, &allrs_end);

                    // push the most recent node onto the nodestack
                    add_node_item(&currNodeStack, currnode, &currNodeStack);
                    add_node_item(&allNodes, currnode, &lastNode);
                    //if (verbose > 2) fprintf(stdout, "Add node %s\n", currnode->name);

                    t += 3;

                } else if (!strcmp(tokens[t], ")")) {
                    // pop the top node off the nodestack
                    if (currNodeStack != NULL) {
                        if (verbose > 2) fprintf(stdout, "Pop node %s\n", currNodeStack->node->name);
                        tmp_nip = currNodeStack;
                        currNodeStack = currNodeStack->prev;
                        currNodeStack->next = NULL;
                        free(tmp_nip);
                    } else {
                        fprintf(stderr, "ERROR: Attempt to pop an empty current node stack!\n");
                        return 3;
                    }

                    t += 1;
                } else if (!strcmp(tokens[t], ",")) {
                    // nothing to do on a comma
                    t += 1;
                } else {
		    char *uptr;
                    // located a receiver
                    // Some of the receiver nodes are not endpoints of a branch,
                    // but are branch points themselves. This complicates how
                    // to label the node as a SRC, INT, or SNK node since the
                    // Elmore Delay calculation looks at the node type to
                    // determine when it has reached an endpoint.
                    //
                    // The solution to this is to create an extra node when a
                    // receiver is found that is connected via a 0 ohm resistor.
                    // The capacitance on this node (the receiver input capacitance)
                    // will be absorbed as downstream capacitance with the 0 ohm
                    // R contributing nothing

                    // create node name
                    // name the extra node after the receiver
                    name = strdup(tokens[t]);

                    // create the new node
                    currnode = create_node(name, SNK, 0);
		    // If driver name is a pin then the name ID is the net name ID
		    if (!strncmp(tokens[t], "PIN/", 4))
			snprintf(currnode->mapped, 12, "*%d", net_idx);
		    else {
			pname = strrchr(tokens[t], '/');
			if (pname)
			    snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
					delimiter, ++pname);
			else
			    /* Node name is hosed but don't crash the program */
			    snprintf(currnode->mapped, 12, "*%d%c%s", nid++,
					delimiter, tokens[t]);
		    }

                    if (verbose > 1) print_node(currnode);
                    name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 10));
                    if (sprintf(name, "%s_r%d", tokens[0], rNum) < 0) {
                        fprintf(stderr, "ERROR: sprintf failed to create resistor name\n");
                        return 2;
                    }
                    rNum += 1;
                    // create resistor
                    currR = calloc(1, sizeof(r));
                    currR->name = name;
                    currR->node1 = currNodeStack->node;
                    currR->node2 = currnode;
                    currR->rval = 0;
                    // add resistor to each node's resistor list and the global list
                    add_ritem(&currNodeStack->node->rlist, currR, &currNodeStack->node->rlist_end);
                    add_ritem(&currnode->rlist, currR, &currnode->rlist_end);
                    add_ritem(&allrs, currR, &allrs_end);

                    // Add the receiver contributed capacitance which is either
                    // the input pin capacitance to a std cell or the user-specified
                    // capacitance of a module-level pin
                    char *cellIndex = strsep(&tokens[t], "/");
                    char *pinName = tokens[t];
                    char *cellName;

		    // (Fixed:  Do not use strsep, as cellname may have underscores
		    // in the name in addition to the one that delimits the index.)
		    uptr = strrchr(cellIndex, '_');
		    if (uptr != NULL) {
		       *uptr = '\0';
		       cellName = cellIndex;
		       cellIndex = uptr + 1;
		    }
		    else {
		       cellName = cellIndex;	/* Should not happen */
		       cellIndex = NULL;
		    }

                    if (!strcmp(cellName, "PIN")) {
                        currnode->nodeCap = modulePinCapacitance;
                        //fprintf(stdout, "Found pin as receiver: %s\n", tokens[t]);
                    } else {

			cell = (Cell *)HashLookup(cellName, &InstHash);
                        Pin *tmpPin = NULL;

                        if (cell != NULL) {
                            tmpPin = get_pin_by_name(cell, pinName);
                            // Liberty Timing File cap units are in pf for osu std cells (other possibility is ff)
                            // readliberty.c stores them and returns values as ff
                            // -> need to correct by /1000 to put back in pf
                            currnode->nodeCap = tmpPin->cap/1000;

                            if (verbose > 3) {
                                printf("cap is %f\n", tmpPin->cap);
                                fprintf(stdout, "INFO: Found cell as receiver: %s\n", cell->name);
                                fprintf(stdout, "INFO: Added cap value is %s %f\n\n", tmpPin->name, tmpPin->cap/1000);
                                print_node(currnode);
                            }
                        } else {
                            if (verbose > 3) {
                                fprintf(stdout, "INFO: Skipping lineAdded cap value is %s %f\n", tmpPin->name, tmpPin->cap/1000);
                            }
                        }
                    }

                    // The extra node created to handle termination points in the
                    // interconnect does not need to be pushed onto the stack

                    // but still add to full node list
                    //if (verbose > 2) fprintf(stdout, "Add node %s\n", currnode->name);
                    add_node_item(&allNodes, currnode, &lastNode);

                    t += 1;
                }
            }

            if (verbose > 3)
                fprintf(stdout, "INFO: Verify all nodes matched up by balancing the parens\n");
            // Verify we matched up all the nodes by popping off the driver node
            if (currNodeStack != NULL) {
                tmp_nip = currNodeStack;
                currNodeStack = currNodeStack->prev;
                free(tmp_nip);
            } else {
                fprintf(stdout, "ERROR: Attempt to pop an empty current node stack!\n");
                return 3;
            }

            if (currNodeStack != NULL) {
                fprintf(stderr, "ERROR: Net %s had unbalance parentheses!\n", tokens[0]);
                return 4;
            }

            if (verbose > 3)
                fprintf(stdout, "INFO: Sum downstream capacitance for each node\n");
            sum_downstream_cap(last_driver->node, NULL);

            if (verbose > 3) print_node(last_driver->node);

            elmdlyptr currElm = calloc(1, sizeof(elmdly));
            currElm->name = calloc(1, sizeof(char) * (strlen(tokens[0]) + 1));
            // name the Elmore Delay after the net
            strcpy(currElm->name, tokens[0]);
            currElm->src = last_driver->node;
            add_elmdly_item(&delays, currElm);

	    if (format == FORMAT_VESTA) {
                if (verbose > 3) {
                    fprintf(stdout, "INFO: Calculate Elmore Delay for each SNK\n");
                }
                calculate_elmore_delay(
                            last_driver->node,
                            NULL,
                            NULL,
                            currElm,
                            /* NULL, */
                            1,
                            0,
                            verbose);

                if (verbose > 3)
                    fprintf(stdout, "ELM: %s\t\t%s\t\t%f\n", currElm->name,
				currElm->src->name, currElm->src->nodeCap +
				currElm->src->totCapDownstream);
                fprintf(outfile, "%s\n", currElm->name);
                fprintf(outfile, "%s %f\n", currElm->src->name,
				currElm->src->totCapDownstreamLessGates);
 
                currSnk = currElm->snklist;
 
                while(currSnk != NULL) {
                    fprintf(outfile, "%s %f\n", currSnk->snknode->name, currSnk->delay);
                    currSnk = currSnk->next;
                }
 
                fprintf(outfile, "\n");
	    }
	    else if (format == FORMAT_SPEF) {
		/* Write SPEF file format output for each net */

		fprintf(outfile, "*D_NET *%d %g\n",
			net_idx, currElm->src->totCapDownstreamLessGates);

		fprintf(outfile, "*CONN\n");

		/* Visit drivers and receivers */
		visit_nodes(last_driver->node, NULL, VISIT_CONN, outfile);

		fprintf(outfile, "*CAP\n");
		snid = 0;
		/* Visit nodes of the net and output lumped parasitic caps */
		visit_nodes(last_driver->node, NULL, VISIT_CAP, outfile);

		fprintf(outfile, "*RES\n");
		snid = 1;
		/* Visit nodes of the net and output branch resistances */
		visit_nodes(last_driver->node, NULL, VISIT_RES, outfile);

		fprintf(outfile, "*END\n");
	    }
	    else {		/* (format == FORMAT_SDF) */
                calculate_elmore_delay(
                            last_driver->node,
                            NULL,
                            NULL,
                            currElm,
                            /* NULL, */
                            1,
                            0,
                            verbose);

                currSnk = currElm->snklist;
 
                while(currSnk != NULL) {
		    char *srcname, *snkname;
		    srcname = (!strncmp(currElm->src->name, "PIN/", 4)) ?
				currElm->src->name + 4 : currElm->src->name;
		    snkname = (!strncmp(currSnk->snknode->name, "PIN/", 4)) ?
				currSnk->snknode->name + 4 : currSnk->snknode->name;
		    fprintf(outfile, "            (INTERCONNECT %s %s (%g))\n",
				srcname, snkname, currSnk->delay);

                    currSnk = currSnk->next;
                }
	    }
        }

        bytesRead = getline(&line, &nbytes, rcfile);
    }

    if (format == FORMAT_SDF) {
	/* Close off all those stupid parentheses */
	fprintf(outfile, "         )\n");
	fprintf(outfile, "      )\n");
	fprintf(outfile, "   )\n");
	fprintf(outfile, ")\n");
    }

    fclose(outfile);
    // Cleanup

    free(delays);
    free(design);

    ritemptr tmp_ritem = allrs;
    rptr tmp_r = NULL;
    int numRs = 0;

    while(allrs != NULL) {
        numRs++;
        tmp_ritem = allrs->next;
        free(allrs->r->name);
        free(allrs->r);
        free(allrs);
        allrs = tmp_ritem;
    }
    printf("Number of Rs: %d\n", numRs);

    fprintf(stdout, "TBD: need to clean-up node deletion\n");
    fclose(rcfile);

    return 0;
}
