/**
 * Author: Jason Luu
 * Date: May 2009
 * 
 * Read a circuit netlist in XML format and populate the netlist data structures for VPR
 */

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "util.h"
#include "hash.h"
#include "vpr_types.h"
#include "vpr_utils.h"
#include "ReadLine.h"
#include "globals.h"
#include "ezxml.h"
#include "read_xml_util.h"
#include "read_netlist.h"
#include "pb_type_graph.h"
#include "cluster_legality.h"
#include "token.h"
#include "rr_graph.h"

static void processPorts(INOUTP ezxml_t Parent, INOUTP t_pb* pb, INOUTP t_rr_node *rr_graph,
					   INOUTP int *ncount, INOUTP struct s_hash **nhash);


static void processPb(INOUTP ezxml_t Parent, INOUTP t_pb* pb, INOUTP t_rr_node *rr_graph,
					   INOUTP int *ncount, INOUTP struct s_hash **nhash, INOUTP int *num_primitives);
static void processComplexBlock(INOUTP ezxml_t Parent, INOUTP t_block *cb, INP int index, 
					   INOUTP int *ncount, INOUTP struct s_hash **nhash, 
					   INOUTP int *num_primitives, INP const t_arch *arch);
static struct s_net *alloc_and_init_netlist_from_hash(INP int ncount, INOUTP struct s_hash **nhash);

static int add_net_to_hash(INOUTP struct s_hash **nhash, INP char *net_name, INOUTP int *ncount);

static void load_external_nets_and_cb (INP int num_blocks,
										INP struct s_block block_list[],
										INP int ncount,
										INP struct s_net nlist[],
										OUTP int *ext_ncount, 
										OUTP struct s_net **ext_nets,
										INP char **circuit_globals);

static void load_internal_cb_nets(INOUTP t_pb *top_level, INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph, INOUTP int * curr_net);

static void alloc_internal_cb_nets(INOUTP t_pb *top_level, INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph, INP int pass);

static void load_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node, INP t_rr_node * rr_graph, INOUTP struct s_net * nets, INOUTP int * curr_net, INOUTP int * curr_sink);

static void count_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node, INP t_rr_node * rr_graph);

static void mark_constant_generators(INP int num_blocks,
									INP struct s_block block_list[],
									INP int ncount,
									INOUTP struct s_net nlist[]);

static void mark_constant_generators_rec(INP t_pb *pb,
										 INP t_rr_node *rr_graph,
										INOUTP struct s_net nlist[]);


static void restore_logical_block_from_saved_block(INP int iblk, INP t_pb *pb);

/**
 * Initializes the block_list with info from a netlist 
 * net_file - Name of the netlist file to read
 * num_blocks - number of CLBs in netlist 
 * block_list - array of blocks in netlist [0..num_blocks - 1]
 * num_nets - number of nets in netlist
 * net_list - nets in netlist [0..num_nets - 1]
 */
void
read_netlist(INP const char *net_file,
			 INP const t_arch *arch,
			 OUTP int *num_blocks,
			 OUTP struct s_block *block_list[],
			 OUTP int *num_nets,
			 OUTP struct s_net *net_list[])
{
	ezxml_t Cur, Prev, Top;
	int i, j;
	const char *Prop;
    int bcount;
    struct s_block *blist;
    int ncount, ext_ncount;
    struct s_net *nlist, *ext_nlist;
	struct s_hash **nhash;
	char **circuit_inputs, **circuit_outputs, **circuit_globals;
	int Count, Len;

	int num_primitives = 0;

	/* Parse the file */ 
	printf("Begin parsing packed FPGA netlist file\n");
	Top = ezxml_parse_file(net_file);
    if(NULL == Top)
	{
	    printf(ERRTAG "Unable to load netlist file '%s'.\n", net_file);
		exit(1);
	}
	printf("Finished parsing packed FPGA netlist file\n");

	/* Root node should be block */ 
	CheckElement(Top, "block");

	/* Check top-level netlist attributes */
	Prop = FindProperty(Top, "name", TRUE);
	printf("Netlist generated from file %s\n", Prop);
	ezxml_set_attr(Top, "name", NULL);

	Prop = FindProperty(Top, "instance", TRUE);
	if(strcmp(Prop, "FPGA_packed_netlist[0]") != 0) {
		printf(ERRTAG "[Line %d] Expected instance to be \"FPGA_packed_netlist[0]\", found %s", Top->line, Prop);
		exit(1);
	}
	ezxml_set_attr(Top, "instance", NULL);

	/* Parse top-level netlist I/Os */
	Cur = FindElement(Top, "inputs", TRUE);
	circuit_inputs = GetNodeTokens(Cur);
	FreeNode(Cur);
	Cur = FindElement(Top, "outputs", TRUE);
	circuit_outputs = GetNodeTokens(Cur);
	FreeNode(Cur);
	
	Cur = FindElement(Top, "globals", TRUE);
	CountTokensInString(Cur->txt, &Count, &Len);
	if(Count > 0) {
		circuit_globals = GetNodeTokens(Cur);
	} else {
		circuit_globals = NULL;
	}
	FreeNode(Cur);
	
	/* Parse all CLB blocks and all nets*/
	bcount = CountChildren(Top, "block", 1);
	blist = my_calloc(bcount, sizeof(t_block));
	nhash = alloc_hash_table();
	ncount = 0;

	logical_block = my_calloc(num_saved_logical_blocks, sizeof(t_logical_block));
	num_logical_blocks = num_saved_logical_blocks;
		
	Cur = Top->child;
	i = 0;
	while(Cur) {
		if(0 == strcmp(Cur->name, "block"))
		{
			CheckElement(Cur, "block");
			processComplexBlock(Cur, blist, i, &ncount, nhash, &num_primitives, arch);
			Prev = Cur;
			Cur = Cur->next;
			FreeNode(Prev);
			i++;
		} else {
			Cur = Cur->next;
		}
	}
	assert(i == bcount);
	if(saved_logical_blocks != NULL) {
		assert(num_primitives == num_saved_logical_blocks);
	}
	
	nlist = alloc_and_init_netlist_from_hash(ncount, nhash);
	mark_constant_generators(bcount, blist, ncount, nlist);
	load_external_nets_and_cb(bcount, blist, ncount, nlist, &ext_ncount, &ext_nlist, circuit_globals);

	/* TODO: create this function later
		check_top_IO_matches_IO_blocks(circuit_inputs, circuit_outputs, circuit_globals, blist, bcount);
	*/

	free_hash_table(nhash);
	FreeTokens(&circuit_inputs);
	FreeTokens(&circuit_outputs);
	if(circuit_globals)
		FreeTokens(&circuit_globals);
	FreeNode(Top);

	/* load mapping between external nets and all nets */
	/* jluu TODO: Should use local variables here then assign to globals later, clean up later */
	vpack_net = nlist;
    num_logical_nets = ncount;
	clb_to_vpack_net_mapping = my_malloc(ext_ncount * sizeof(int));
	vpack_to_clb_net_mapping = my_malloc(ncount * sizeof(int));
	for(i = 0; i < ncount; i++) {
		vpack_to_clb_net_mapping[i] = OPEN;
	}
		
	for(i = 0; i < ext_ncount; i++) {
		for(j = 0; j < ncount; j++) {
			if(strcmp(ext_nlist[i].name, nlist[j].name) == 0) {
				clb_to_vpack_net_mapping[i] = j;
				vpack_to_clb_net_mapping[j] = i;
				break;
			}
		}
		assert(j != ncount);
	}

	if(saved_logical_blocks != NULL) {
		free(saved_logical_blocks);
		saved_logical_blocks = NULL;
		free(saved_logical_nets);
		saved_logical_nets = NULL;
	}

	/* Return blocks and nets */
	*num_blocks = bcount;
	*block_list = blist;
	*num_nets = ext_ncount;
	*net_list = ext_nlist;
}


/**
 * XML parser to populate CLB info and to update nets with the nets of this CLB 
 * Parent - XML tag for this CLB
 * clb - Array of CLBs in the netlist
 * index - index of the CLB to allocate and load information into
 * ncount - number of nets recorded thus far in all CLBs
 * nhash - hashtable of all nets recorded thus far in all CLBs
 */
static void processComplexBlock(INOUTP ezxml_t Parent, INOUTP t_block *cb, INP int index, 
					   INOUTP int *ncount, INOUTP struct s_hash **nhash, 
					   INOUTP int *num_primitives, INP const t_arch *arch) {

	const char *Prop;
	boolean found;
	int num_tokens = 0;
	t_token *tokens;
	int i;
	const t_pb_type * pb_type = NULL;

	/* parse cb attributes */
	cb[index].pb = my_calloc(1, sizeof(t_pb));

	Prop = FindProperty(Parent, "name", TRUE);
	cb[index].name = my_strdup(Prop);
	cb[index].pb->name = my_strdup(Prop);
	ezxml_set_attr(Parent, "name", NULL);

	Prop = FindProperty(Parent, "instance", TRUE);
	tokens = GetTokensFromString(Prop, &num_tokens);
	ezxml_set_attr(Parent, "instance", NULL);
	if(num_tokens != 4 ||
		tokens[0].type != TOKEN_STRING ||
		tokens[1].type != TOKEN_OPEN_SQUARE_BRACKET ||
		tokens[2].type != TOKEN_INT ||
		tokens[3].type != TOKEN_CLOSE_SQUARE_BRACKET) {
		printf(ERRTAG "[Line %d] Unknown syntax for instance %s in %s.  Expected pb_type[instance_number]\n",
			Parent->line, Prop, Parent->name);
		exit(1);
	}
	assert(my_atoi(tokens[2].data) == index);
	found = FALSE;
	for(i = 0; i < num_types; i++) {
		if(strcmp(type_descriptors[i].name, tokens[0].data) == 0) {
			cb[index].type = &type_descriptors[i];
			pb_type = cb[index].type->pb_type;
			found = TRUE;
			break;
		}
	}
	if(!found) {
		printf(ERRTAG "[Line %d] Unknown cb type %s for cb %s #%d\n", Parent->line, Prop, cb[index].name, index);
		exit(1);
	}
		
	/* Parse all pbs and CB internal nets*/
	cb[index].pb->logical_block = OPEN;
	cb[index].pb->pb_graph_node = cb[index].type->pb_graph_head;
	rr_node = my_calloc(cb[index].type->pb_graph_head->total_pb_pins, sizeof(t_rr_node));
	alloc_and_load_rr_graph_for_pb_graph_node(cb[index].pb->pb_graph_node, arch, 0);
	cb[index].pb->rr_graph = rr_node;
	num_rr_nodes = cb[index].pb->pb_graph_node->total_pb_pins;
	
	Prop = FindProperty(Parent, "mode", TRUE);
	ezxml_set_attr(Parent, "mode", NULL);

	found = FALSE;
	for(i = 0; i < pb_type->num_modes; i++) {
		if(strcmp(Prop, pb_type->modes[i].name) == 0) {
			cb[index].pb->mode = i;
			found = TRUE;
		}
	}
	if(!found) {
		printf(ERRTAG "[Line %d] Unknown mode %s for cb %s #%d\n", Parent->line, Prop, cb[index].name, index);
		exit(1);
	}

	processPb(Parent, cb[index].pb, cb[index].pb->rr_graph, ncount, nhash, num_primitives);

	cb[index].nets = my_malloc(cb[index].type->num_pins * sizeof(int));
	for(i = 0; i < cb[index].type->num_pins; i++ ) {
		cb[index].nets[i] = OPEN;
	}
	alloc_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node, cb[index].pb->rr_graph, 1);
	alloc_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node, cb[index].pb->rr_graph, 2);
	i = 0;
	load_internal_cb_nets(cb[index].pb, cb[index].pb->pb_graph_node, cb[index].pb->rr_graph, &i);
	freeTokens(tokens, num_tokens);
#if 0
	/* TODO: may not need local nets anymore, reminder to remove if not used, keeping this around just in case we need to use it in the future */
	for(i = 0; i < cb[index].pb->num_local_nets; i++) {
		printf("local net %s: ", cb[index].pb->name);
		for(j = 0; j <= cb[index].pb->local_nets[i].num_sinks; j++) {
			printf("%d ", cb[index].pb->local_nets[i].node_block[j]);
		}
		printf("\n");
	}
#endif
}


/**
 * XML parser to populate pb info and to update internal nets of the parent CLB
 * Parent - XML tag for this pb_type
 * pb - physical block to use
 * ncount - number of all internal subblock nets recorded thus far in this CLB
 * nhash - hashtable of all internal subblock nets recorded thus far in this CLBs
 */
static void processPb(INOUTP ezxml_t Parent, INOUTP t_pb* pb, INOUTP t_rr_node *rr_graph,
					   INOUTP int *ncount, INOUTP struct s_hash **nhash, INOUTP int *num_primitives) {
	ezxml_t Cur, Prev, lookahead;
	const char *Prop;
	const char *instance_type;
	int i, j, pb_index;
	boolean found;
	const t_pb_type *pb_type;
	t_token *tokens;
	int num_tokens;

	Cur = FindElement(Parent, "inputs", TRUE);
	processPorts(Cur, pb, rr_graph, ncount, nhash);
	FreeNode(Cur);
	Cur = FindElement(Parent, "outputs", TRUE);
	processPorts(Cur, pb, rr_graph, ncount, nhash);
	FreeNode(Cur);
	Cur = FindElement(Parent, "globals", TRUE);
	processPorts(Cur, pb, rr_graph, ncount, nhash);
	FreeNode(Cur);
	

	pb_type = pb->pb_graph_node->pb_type;
	if(pb_type->num_modes == 0) {
		pb->logical_block = *num_primitives;
		/* TODO: This info is not yet used.  Intention was to use later for error checking */
		if(saved_logical_blocks != NULL) {
			restore_logical_block_from_saved_block(*num_primitives, pb);
		}
		(*num_primitives)++;		
	} else {
		/* process children of child if exists */
	
		pb->child_pbs = my_calloc(pb_type->modes[pb->mode].num_pb_type_children, sizeof(t_pb*));
		for(i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) {
			pb->child_pbs[i] = my_calloc(pb_type->modes[pb->mode].pb_type_children[i].num_pb, sizeof(t_pb));
		}

		/* Populate info for each physical block  */
		Cur = Parent->child;
		while(Cur) {
			if(0 == strcmp(Cur->name, "block"))
			{
				CheckElement(Cur, "block");

				instance_type = FindProperty(Cur, "instance", TRUE);
				tokens = GetTokensFromString(instance_type, &num_tokens);
				ezxml_set_attr(Cur, "instance", NULL);
				if(num_tokens != 4 ||
					tokens[0].type != TOKEN_STRING ||
					tokens[1].type != TOKEN_OPEN_SQUARE_BRACKET ||
					tokens[2].type != TOKEN_INT ||
					tokens[3].type != TOKEN_CLOSE_SQUARE_BRACKET) {
					printf(ERRTAG "[Line %d] Unknown syntax for instance %s in %s.  Expected pb_type[instance_number]\n",
						Cur->line, instance_type, Cur->name);
					exit(1);
				}
				
				found = FALSE;
				pb_index = OPEN;
				for(i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) {
					if(strcmp(pb_type->modes[pb->mode].pb_type_children[i].name, tokens[0].data) == 0) {
						if(my_atoi(tokens[2].data) >= pb_type->modes[pb->mode].pb_type_children[i].num_pb) {
							printf(ERRTAG "[Line %d] Instance number exceeds # of pb available for instance %s in %s.\n",
								Cur->line, instance_type, Cur->name);
							exit(1);
						}
						pb_index = my_atoi(tokens[2].data);
						if(pb->child_pbs[i][pb_index].pb_graph_node != NULL) {
							printf(ERRTAG "[Line %d] node is used by two different blocks %s and %s\n", Cur->line, instance_type, pb->child_pbs[i][pb_index].name);
							exit(1);
						}
						pb->child_pbs[i][pb_index].pb_graph_node = &pb->pb_graph_node->child_pb_graph_nodes[pb->mode][i][pb_index];
						found = TRUE;
						break;
					}
				}
				if(!found) {
					printf(ERRTAG "[Line %d] Unknown pb type %s\n", Cur->line, instance_type);
					exit(1);
				}
					
				Prop = FindProperty(Cur, "name", TRUE);
				ezxml_set_attr(Cur, "name", NULL);
				if(0 != strcmp(Prop, "open")) {
					pb->child_pbs[i][pb_index].name = my_strdup(Prop);
					
					/* Parse all pbs and CB internal nets*/
					pb->child_pbs[i][pb_index].logical_block = OPEN;
					
					Prop = FindProperty(Cur, "mode", FALSE);
					if(Prop) {
						ezxml_set_attr(Cur, "mode", NULL);
					}
					pb->child_pbs[i][pb_index].mode = 0;
					found = FALSE;
					for(j = 0; j < pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes; j++) {
						if(strcmp(Prop, pb->child_pbs[i][pb_index].pb_graph_node->pb_type->modes[j].name) == 0) {
							pb->child_pbs[i][pb_index].mode = j;
							found = TRUE;
						}
					}
					if(!found && pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes != 0) {
						printf(ERRTAG "[Line %d] Unknown mode %s for cb %s #%d\n", Cur->line, Prop, pb->child_pbs[i][pb_index].name, pb_index);
						exit(1);
					}
					pb->child_pbs[i][pb_index].parent_pb = pb;
					pb->child_pbs[i][pb_index].rr_graph = pb->rr_graph;
				
					processPb(Cur, &pb->child_pbs[i][pb_index], rr_graph, ncount, nhash, num_primitives);
				} else {
					/* physical block has no used primitives but it may have used routing */
					pb->child_pbs[i][pb_index].name = NULL;
					pb->child_pbs[i][pb_index].logical_block = OPEN;
					lookahead = FindElement(Cur, "outputs", FALSE);
					if(lookahead != NULL) {
						lookahead = FindElement(lookahead, "port", TRUE);
						Prop = FindProperty(Cur, "mode", FALSE);
						if(Prop) {
							ezxml_set_attr(Cur, "mode", NULL);
						}
						pb->child_pbs[i][pb_index].mode = 0;
						found = FALSE;
						for(j = 0; j < pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes; j++) {
							if(strcmp(Prop, pb->child_pbs[i][pb_index].pb_graph_node->pb_type->modes[j].name) == 0) {
								pb->child_pbs[i][pb_index].mode = j;
								found = TRUE;
							}
						}
						if(!found && pb->child_pbs[i][pb_index].pb_graph_node->pb_type->num_modes != 0) {
							printf(ERRTAG "[Line %d] Unknown mode %s for cb %s #%d\n", Cur->line, Prop, pb->child_pbs[i][pb_index].name, pb_index);
							exit(1);
						}
						pb->child_pbs[i][pb_index].parent_pb = pb;
						pb->child_pbs[i][pb_index].rr_graph = pb->rr_graph;
						processPb(Cur, &pb->child_pbs[i][pb_index], rr_graph, ncount, nhash, num_primitives);				
					}
				}
				Prev = Cur;
				Cur = Cur->next;
				FreeNode(Prev);
				freeTokens(tokens, num_tokens);
			} else {
				Cur = Cur->next;
			}
		}
	}
}



/**
 * Allocates memory for nets and loads the name of the net so that it can be identified and loaded with
 * more complete information later
 * ncount - number of nets in the hashtable of nets
 * nhash - hashtable of nets
 * returns array of nets stored in hashtable
 */
static struct s_net *alloc_and_init_netlist_from_hash(INP int ncount, INOUTP struct s_hash **nhash) {
	struct s_net *nlist;
	struct s_hash_iterator hash_iter;
	struct s_hash *curr_net;
	int i;
	
	nlist = my_calloc(ncount, sizeof(struct s_net));

	hash_iter = start_hash_table_iterator();
	curr_net = get_next_hash(nhash, &hash_iter);	
	while(curr_net != NULL) {
		assert(nlist[curr_net->index].name == NULL);
		nlist[curr_net->index].name = my_strdup(curr_net->name);
		nlist[curr_net->index].num_sinks = curr_net->count - 1;

		nlist[curr_net->index].node_block = my_malloc(curr_net->count * sizeof(int));
		nlist[curr_net->index].node_block_pin = my_malloc(curr_net->count * sizeof(int));
		nlist[curr_net->index].is_global = FALSE;
		for(i = 0; i < curr_net->count; i++) {
			nlist[curr_net->index].node_block[i] = OPEN;
			nlist[curr_net->index].node_block_pin[i] = OPEN;
		}
		curr_net = get_next_hash(nhash, &hash_iter);
	}
	return nlist;
}

/**
 * Adds net to hashtable of nets.  If the net is "open", then this is a keyword so do not add it.  
 * If the net already exists, increase the count on that net 
 */
static int add_net_to_hash(INOUTP struct s_hash **nhash, INP char *net_name, INOUTP int *ncount) {
	struct s_hash *hash_value;

	if(strcmp(net_name, "open") == 0) {
		return OPEN;
	}

	hash_value = insert_in_hash_table(nhash, net_name, *ncount);
	if(hash_value->count == 1) {
		assert(*ncount == hash_value->index);
		(*ncount)++;
	}
	return hash_value->index;
}

static void processPorts(INOUTP ezxml_t Parent, INOUTP t_pb* pb, t_rr_node *rr_graph,
					   INOUTP int *ncount, INOUTP struct s_hash **nhash) {

	int i, j, in_port, out_port, clock_port, num_tokens;
	ezxml_t Cur, Prev;
	const char *Prop;
	char **pins;
	char *port_name, *interconnect_name;
	int rr_node_index;
	t_pb_graph_pin *** pin_node;
	int *num_ptrs, num_sets;
	boolean found;

	Cur = Parent->child;
	while(Cur) {
		if(0 == strcmp(Cur->name, "port"))
		{
			CheckElement(Cur, "port");

			Prop = FindProperty(Cur, "name", TRUE);
			ezxml_set_attr(Cur, "name", NULL);
			
			in_port = out_port = clock_port = 0;
			found = FALSE;
			for(i = 0; i < pb->pb_graph_node->pb_type->num_ports; i++) {
				if(0 == strcmp(pb->pb_graph_node->pb_type->ports[i].name, Prop)) {
					found = TRUE;
					break;
				}
				if(pb->pb_graph_node->pb_type->ports[i].is_clock && pb->pb_graph_node->pb_type->ports[i].type == IN_PORT) {
					clock_port++;
				} else if(!pb->pb_graph_node->pb_type->ports[i].is_clock && pb->pb_graph_node->pb_type->ports[i].type == IN_PORT) {
					in_port++;
				} else {
					assert(pb->pb_graph_node->pb_type->ports[i].type == OUT_PORT);
					out_port++;
				}
			}	
			if(!found) {
				printf(ERRTAG "[Line %d] Unknown port %s for pb %s[%d]\n", Cur->line, Prop, pb->pb_graph_node->pb_type->name, pb->pb_graph_node->placement_index);
				exit(1);
			}

			pins = GetNodeTokens(Cur);
			num_tokens = CountTokens(pins);
			if(0 == strcmp(Parent->name, "inputs")) {
				if(num_tokens != pb->pb_graph_node->num_input_pins[in_port]) {
					printf(ERRTAG "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d]\n", Cur->line, num_tokens, Prop, pb->pb_graph_node->pb_type->name, pb->pb_graph_node->placement_index);
					exit(1);
				}
			} else if(0 == strcmp(Parent->name, "outputs")) {
				if(num_tokens != pb->pb_graph_node->num_output_pins[out_port]) {
					printf(ERRTAG "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d]\n", Cur->line, num_tokens, Prop, pb->pb_graph_node->pb_type->name, pb->pb_graph_node->placement_index);
					exit(1);
				}
			} else {
				if(num_tokens != pb->pb_graph_node->num_clock_pins[clock_port]) {
					printf(ERRTAG "[Line %d] Incorrect # pins %d found for port %s for pb %s[%d]\n", Cur->line, num_tokens, Prop, pb->pb_graph_node->pb_type->name, pb->pb_graph_node->placement_index);
					exit(1);
				}
			} 
			if(0 == strcmp(Parent->name, "inputs") || 0 == strcmp(Parent->name, "globals")) {
				if(pb->parent_pb == NULL) {
					/* top-level, connections are nets to route */
					for(i = 0; i < num_tokens; i++) {
						if(0 == strcmp(Parent->name, "inputs"))
							rr_node_index = pb->pb_graph_node->input_pins[in_port][i].pin_count_in_cluster;
						else
							rr_node_index = pb->pb_graph_node->clock_pins[clock_port][i].pin_count_in_cluster;
						rr_graph[rr_node_index].net_num = add_net_to_hash(nhash, pins[i], ncount);
					}
				} else {
					for(i = 0; i < num_tokens; i++) {
						if(0 == strcmp(pins[i], "open")) {
							continue;
						}
						interconnect_name = strstr(pins[i], "->");
						*interconnect_name = '\0';
						interconnect_name += 2;
						port_name = pins[i];
						pin_node =  alloc_and_load_port_pin_ptrs_from_string(pb->pb_graph_node->parent_pb_graph_node, 
																			pb->pb_graph_node->parent_pb_graph_node->child_pb_graph_nodes[pb->parent_pb->mode],
																			port_name,
																			&num_ptrs,
																			&num_sets,
																			TRUE,
																			TRUE);
						assert(num_sets == 1 && num_ptrs[0] == 1);
						if(0 == strcmp(Parent->name, "inputs"))
							rr_node_index = pb->pb_graph_node->input_pins[in_port][i].pin_count_in_cluster;
						else
							rr_node_index = pb->pb_graph_node->clock_pins[clock_port][i].pin_count_in_cluster;
						rr_graph[rr_node_index].prev_node = pin_node[0][0]->pin_count_in_cluster;
						found = FALSE;
						for(j = 0; j < pin_node[0][0]->num_output_edges; j++) {
							if(0 == strcmp(interconnect_name, pin_node[0][0]->output_edges[j]->interconnect->name)) {
								found = TRUE;
								break;
							}
						}
						for(j = 0; j < num_sets; j++) {
							free(pin_node[j]);
						}
						free(pin_node);
						free(num_ptrs);
						if(!found) {
							printf(ERRTAG "[Line %d] Unknown interconnect %s connecting to pin %s\n", Cur->line, interconnect_name, port_name);
							exit(1);
						}
					}
				}
			}

			if(0 == strcmp(Parent->name, "outputs")) {
				if(pb->pb_graph_node->pb_type->num_modes == 0) {
					/* primitives are drivers of nets */
					for(i = 0; i < num_tokens; i++) {
						rr_node_index = pb->pb_graph_node->output_pins[out_port][i].pin_count_in_cluster;
						rr_graph[rr_node_index].net_num = add_net_to_hash(nhash, pins[i], ncount);
					}
				} else {
					for(i = 0; i < num_tokens; i++) {
						if(0 == strcmp(pins[i], "open")) {
							continue;
						}
						interconnect_name = strstr(pins[i], "->");
						*interconnect_name = '\0';
						interconnect_name += 2;
						port_name = pins[i];
						pin_node =  alloc_and_load_port_pin_ptrs_from_string(pb->pb_graph_node, 
																			pb->pb_graph_node->child_pb_graph_nodes[pb->mode],
																			port_name,
																			&num_ptrs,
																			&num_sets,
																			TRUE,
																			TRUE);
						assert(num_sets == 1 && num_ptrs[0] == 1);
						rr_node_index = pb->pb_graph_node->output_pins[out_port][i].pin_count_in_cluster;
						rr_graph[rr_node_index].prev_node = pin_node[0][0]->pin_count_in_cluster;
						found = FALSE;
						for(j = 0; j < pin_node[0][0]->num_output_edges; j++) {
							if(0 == strcmp(interconnect_name, pin_node[0][0]->output_edges[j]->interconnect->name)) {
								found = TRUE;
								rr_graph[rr_node_index].fan_in++;
								rr_graph[pin_node[0][0]->pin_count_in_cluster].edges[j] = rr_node_index;
								break;
							}
						}		
						for(j = 0; j < num_sets; j++) {
							free(pin_node[j]);
						}
						free(pin_node);
						free(num_ptrs);
						if(!found) {
							printf(ERRTAG "[Line %d] Unknown interconnect %s connecting to pin %s\n", Cur->line, interconnect_name, port_name);
							exit(1);
						}
						interconnect_name -= 2;
						*interconnect_name = '-';
					}
				}
			}

			FreeTokens(&pins);

			Prev = Cur;
			Cur = Cur->next;
			FreeNode(Prev);
		} else {
			Cur = Cur->next;
		}
	}
}


/**  
 * This function updates the nets list and the connections between that list and the complex block
*/
static void
load_external_nets_and_cb(INP int num_blocks,
		    INP struct s_block block_list[],
		    INP int ncount,
			INP struct s_net nlist[],
			OUTP int *ext_ncount, 
			OUTP struct s_net **ext_nets,
			INP char **circuit_globals)
{
    int i, j, k, ipin;
	struct s_hash **ext_nhash;
	t_rr_node *rr_graph;
	t_pb_graph_pin *pb_graph_pin;
	int *count;
	int netnum, num_tokens;


	*ext_ncount = 0;
	ext_nhash = alloc_hash_table();

	/* Assumes that complex block pins are ordered inputs, outputs, globals */

	/* Determine the external nets of complex block */
	for(i = 0; i < num_blocks; i++) {
		ipin = 0;
		if(block_list[i].type->pb_type->num_input_pins + 
			block_list[i].type->pb_type->num_output_pins + 
			block_list[i].type->pb_type->num_clock_pins != block_list[i].type->num_pins / block_list[i].type->capacity) {

			assert(0);
		}
			

		/* First determine nets external to complex blocks */
		assert(block_list[i].type->pb_type->num_input_pins + 
			block_list[i].type->pb_type->num_output_pins + 
			block_list[i].type->pb_type->num_clock_pins == block_list[i].type->num_pins / block_list[i].type->capacity);

		rr_graph = block_list[i].pb->rr_graph;
		for(j = 0; j < block_list[i].pb->pb_graph_node->num_input_ports; j++) {
			for(k = 0; k < block_list[i].pb->pb_graph_node->num_input_pins[j]; k++) {
				pb_graph_pin = &block_list[i].pb->pb_graph_node->input_pins[j][k];
				assert(pb_graph_pin->pin_count_in_cluster == ipin);
				if(rr_graph[pb_graph_pin->pin_count_in_cluster].net_num != OPEN) {
					block_list[i].nets[ipin] = add_net_to_hash(ext_nhash, nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name, ext_ncount);
				} else {
					block_list[i].nets[ipin] = OPEN;
				}				
				ipin++;
			}
		}
		for(j = 0; j < block_list[i].pb->pb_graph_node->num_output_ports; j++) {
			for(k = 0; k < block_list[i].pb->pb_graph_node->num_output_pins[j]; k++) {
				pb_graph_pin = &block_list[i].pb->pb_graph_node->output_pins[j][k];
				assert(pb_graph_pin->pin_count_in_cluster == ipin);
				if(rr_graph[pb_graph_pin->pin_count_in_cluster].net_num != OPEN) {
					block_list[i].nets[ipin] = add_net_to_hash(ext_nhash, nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name, ext_ncount);
				} else {
					block_list[i].nets[ipin] = OPEN;
				}
				ipin++;
			}
		}
		for(j = 0; j < block_list[i].pb->pb_graph_node->num_clock_ports; j++) {
			for(k = 0; k < block_list[i].pb->pb_graph_node->num_clock_pins[j]; k++) {
				pb_graph_pin = &block_list[i].pb->pb_graph_node->clock_pins[j][k];
				assert(pb_graph_pin->pin_count_in_cluster == ipin);
				if(rr_graph[pb_graph_pin->pin_count_in_cluster].net_num != OPEN) {
					block_list[i].nets[ipin] = add_net_to_hash(ext_nhash, nlist[rr_graph[pb_graph_pin->pin_count_in_cluster].net_num].name, ext_ncount);
				} else {
					block_list[i].nets[ipin] = OPEN;
				}
				ipin++;
			}
		}
		for(j = ipin; j < block_list[i].type->num_pins; j++) {
			block_list[i].nets[ipin] = OPEN;
		}
	}

	/* alloc and partially load the list of external nets */
	(*ext_nets) = alloc_and_init_netlist_from_hash(*ext_ncount, ext_nhash);
	/* Load global nets */
	num_tokens = CountTokens(circuit_globals);
	for(i = 0; i < *ext_ncount; i++) {
		for(j = 0; j < num_tokens; j++) {
			if(strcmp(circuit_globals[j], (*ext_nets)[i].name) == 0) {
				(*ext_nets)[i].is_global = TRUE;
			}
		}
	}
	count = my_calloc(*ext_ncount, sizeof(int));

    /* complete load of external nets so that each net points back to the blocks */
	for(i = 0; i < num_blocks; i++) {
		ipin = 0;
		rr_graph = block_list[i].pb->rr_graph;
		for(j = 0; j < block_list[i].type->num_pins; j++) {
			netnum = block_list[i].nets[j];
			if(netnum != OPEN) {
				if(RECEIVER == block_list[i].type->class_inf[block_list[i].type->pin_class[j]].type)
				{
					count[netnum]++;
					assert(count[netnum] <= (*ext_nets)[netnum].num_sinks);
				    (*ext_nets)[netnum].node_block[count[netnum]] = i;
				    (*ext_nets)[netnum].node_block_pin[count[netnum]] = j;

					if((*ext_nets)[netnum].is_global != block_list[i].type->is_global_pin[j]) {
						if((*ext_nets)[netnum].is_global) {
							printf(ERRTAG "Netlist attempts to connect global net %s to non global pin %d of block %s #%d\n",
								(*ext_nets)[netnum].name, j, block_list[i].name, j);
						} else {
							printf(ERRTAG "Netlist attempts to connect non-global net %s to global pin %d of block %s #%d\n",
								(*ext_nets)[netnum].name, j, block_list[i].name, j);
						}
						exit(1);
					}
				}
			    else
				{
				    assert(DRIVER == block_list[i].type->class_inf[block_list[i].type->pin_class[j]].type);
					assert((*ext_nets)[netnum].node_block[0] == OPEN);
				    (*ext_nets)[netnum].node_block[0] = i;
				    (*ext_nets)[netnum].node_block_pin[0] = j;
				}
			}			
		}
	}
	free(count);
	free_hash_table(ext_nhash);
}

/* Recursive function that fills rr_graph of cb with net numbers starting at the given rr_node */
static int count_sinks_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node, INP t_rr_node * rr_graph) {
	int i;
	int count = 0;

	for(i = 0; i < cur_rr_node->num_edges; i++) {
		if(&rr_graph[rr_graph[cur_rr_node->edges[i]].prev_node] == cur_rr_node) {
			assert(rr_graph[cur_rr_node->edges[i]].net_num == OPEN || rr_graph[cur_rr_node->edges[i]].net_num == cur_rr_node->net_num);
			count += count_sinks_internal_cb_rr_graph_net_nums(&rr_graph[cur_rr_node->edges[i]], rr_graph);
		}
	}
	if(count == 0) {
		return 1; /* terminal node */
	} else {
		return count;
	}
}

/* Recursive function that fills rr_graph of cb with net numbers starting at the given rr_node */
static void load_internal_cb_rr_graph_net_nums(INP t_rr_node * cur_rr_node, INP t_rr_node * rr_graph, INOUTP struct s_net * nets, INOUTP int * curr_net, INOUTP int * curr_sink) {
	int i;

	boolean terminal;
	terminal = TRUE;

	for(i = 0; i < cur_rr_node->num_edges; i++) {
		if(&rr_graph[rr_graph[cur_rr_node->edges[i]].prev_node] == cur_rr_node) {
			/* TODO: If multiple edges to same node (should not happen in reasonable design) this always
			selects the last edge, need to be smart about it in future (ie. select fastest edge */
			assert(rr_graph[cur_rr_node->edges[i]].net_num == OPEN || rr_graph[cur_rr_node->edges[i]].net_num == cur_rr_node->net_num);
			rr_graph[cur_rr_node->edges[i]].net_num = cur_rr_node->net_num;
			rr_graph[cur_rr_node->edges[i]].prev_edge = i;
			load_internal_cb_rr_graph_net_nums(&rr_graph[cur_rr_node->edges[i]], rr_graph, nets, curr_net, curr_sink);
			terminal = FALSE;
		}
	}
	if(terminal == TRUE) {
		/* Since the routing node index is known, assign that instead of the more obscure node block */
		nets[*curr_net].node_block[*curr_sink] = cur_rr_node->pb_graph_pin->pin_count_in_cluster;
		nets[*curr_net].node_block_pin[*curr_sink] = OPEN;
		nets[*curr_net].node_block_port[*curr_sink] = OPEN;
		(*curr_sink)++;
	}
}

/* Load internal cb nets and fill rr_graph of cb with net numbers */
static void load_internal_cb_nets(INOUTP t_pb *top_level, INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph, INOUTP int * curr_net) {
	int i, j, k;
	const t_pb_type *pb_type;
	int temp, size;
	struct s_net * nets;

	pb_type = pb_graph_node->pb_type;

	nets = top_level->local_nets;

	temp = 0;

	if(pb_graph_node->parent_pb_graph_node == NULL) { /* determine nets driven from inputs at top level */
		*curr_net = 0;
		for(i = 0; i < pb_graph_node->num_input_ports; i++) {
			for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) {
				if(rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					load_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster], rr_graph, nets, curr_net, &temp);
					assert(temp == nets[*curr_net].num_sinks);
					temp = 0;
					size = strlen(pb_graph_node->pb_type->name) + 
						pb_graph_node->placement_index/10 + 
						i / 10 + j /10 + pb_graph_node->input_pins[i][j].pin_count_in_cluster/10 + 
						26;
					nets[*curr_net].name = my_calloc(size, sizeof(char));
					sprintf(nets[*curr_net].name, "%s[%d].input[%d][%d].pin[%d]", pb_graph_node->pb_type->name,
						pb_graph_node->placement_index, i, j, pb_graph_node->input_pins[i][j].pin_count_in_cluster);
					(*curr_net)++;
				}
			}
		}
		for(i = 0; i < pb_graph_node->num_clock_ports; i++) {
			for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) {
				if(rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					load_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster], rr_graph, nets, curr_net, &temp);
					assert(temp == nets[*curr_net].num_sinks);
					temp = 0;
					nets[*curr_net].is_global = TRUE;
					size = strlen(pb_graph_node->pb_type->name) + 
						pb_graph_node->placement_index/10 + 
						i / 10 + j /10 + pb_graph_node->clock_pins[i][j].pin_count_in_cluster/10 + 
						26;
					nets[*curr_net].name = my_calloc(size, sizeof(char));
					sprintf(nets[*curr_net].name, "%s[%d].clock[%d][%d].pin[%d]", pb_graph_node->pb_type->name,
						pb_graph_node->placement_index, i, j, pb_graph_node->clock_pins[i][j].pin_count_in_cluster);
					(*curr_net)++;
				}
			}
		}
	}

	if(pb_type->blif_model != NULL) {
		/* This is a terminal node so it might drive nets, find and map the rr_graph path for those nets */
		for(i = 0; i < pb_graph_node->num_output_ports; i++) {
			for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) {
				if(rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					load_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster], rr_graph, nets, curr_net, &temp);
					assert(temp == nets[*curr_net].num_sinks);
					temp = 0;
					size = strlen(pb_graph_node->pb_type->name) + 
						pb_graph_node->placement_index/10 + 
						i / 10 + j /10 + pb_graph_node->output_pins[i][j].pin_count_in_cluster/10 + 
						26;
					nets[*curr_net].name = my_calloc(size, sizeof(char));
					sprintf(nets[*curr_net].name, "%s[%d].output[%d][%d].pin[%d]", pb_graph_node->pb_type->name,
						pb_graph_node->placement_index, i, j, pb_graph_node->output_pins[i][j].pin_count_in_cluster);
					(*curr_net)++;
				}
			}
		}
	} else {
		/* Recurse down to primitives */
		for(i = 0; i < pb_type->num_modes; i++) {
			for(j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
				for(k = 0; k < pb_type->modes[i].pb_type_children[j].num_pb; k++) {
					load_internal_cb_nets(top_level, &pb_graph_node->child_pb_graph_nodes[i][j][k], rr_graph, curr_net);
				}
			}
		}
	}

	if(pb_graph_node->parent_pb_graph_node == NULL) { /* at top level */
		assert(*curr_net == top_level->num_local_nets);
	}
}

/* allocate space to store nets internal to cb 
   two pass algorithm, pass 1 count and allocate # nets, pass 2 determine # sinks
*/
static void alloc_internal_cb_nets(INOUTP t_pb *top_level, INP t_pb_graph_node *pb_graph_node, INOUTP t_rr_node *rr_graph, INP int pass) {
	int i, j, k;
	const t_pb_type *pb_type;
	int num_sinks;
	
	pb_type = pb_graph_node->pb_type;

	if(pb_graph_node->parent_pb_graph_node == NULL) { /* determine nets driven from inputs at top level */
		top_level->num_local_nets = 0;
		if(pass == 1)
			top_level->local_nets = NULL;
		for(i = 0; i < pb_graph_node->num_input_ports; i++) {
			for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) {
				if(rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					if(pass == 2) {
						num_sinks = count_sinks_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster], rr_graph);
						top_level->local_nets[top_level->num_local_nets].num_sinks = num_sinks;
						top_level->local_nets[top_level->num_local_nets].node_block = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_port = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_pin = my_calloc(num_sinks, sizeof(int));
					}
					top_level->num_local_nets++;
				}
			}
		}
		for(i = 0; i < pb_graph_node->num_clock_ports; i++) {
			for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) {
				if(rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					if(pass == 2) {
						num_sinks = count_sinks_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster], rr_graph);
						top_level->local_nets[top_level->num_local_nets].num_sinks = num_sinks;
						top_level->local_nets[top_level->num_local_nets].node_block = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_port = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_pin = my_calloc(num_sinks, sizeof(int));
					}
					top_level->num_local_nets++;
				}
			}
		}
	}

	if(pb_type->blif_model != NULL) {
		/* This is a terminal node so it might drive nets, find and map the rr_graph path for those nets */
		for(i = 0; i < pb_graph_node->num_output_ports; i++) {
			for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) {
				if(rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					if(pass == 2) {
						num_sinks = count_sinks_internal_cb_rr_graph_net_nums(&rr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster], rr_graph);
						top_level->local_nets[top_level->num_local_nets].num_sinks = num_sinks;
						top_level->local_nets[top_level->num_local_nets].node_block = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_port = my_calloc(num_sinks, sizeof(int));
						top_level->local_nets[top_level->num_local_nets].node_block_pin = my_calloc(num_sinks, sizeof(int));
					}
					top_level->num_local_nets++;
				}
			}
		}
	} else {
		/* Recurse down to primitives */
		for(i = 0; i < pb_type->num_modes; i++) {
			for(j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
				for(k = 0; k < pb_type->modes[i].pb_type_children[j].num_pb; k++) {
					alloc_internal_cb_nets(top_level, &pb_graph_node->child_pb_graph_nodes[i][j][k], rr_graph, pass);
				}
			}
		}
	}

	if(pb_graph_node->parent_pb_graph_node == NULL) { /* at top level */
		if(pass == 1) {
			top_level->local_nets = my_calloc(top_level->num_local_nets, sizeof(struct s_net));
		}
	}
}

static void mark_constant_generators(INP int num_blocks,
									INP struct s_block block_list[],
									INP int ncount,
									INOUTP struct s_net nlist[]) {								
	int i;
	for(i = 0; i < num_blocks; i++) {
		mark_constant_generators_rec(block_list[i].pb, block_list[i].pb->rr_graph, nlist);
	}
}


static void mark_constant_generators_rec(INP t_pb *pb,
										 INP t_rr_node *rr_graph,
										 INOUTP struct s_net nlist[]) {
	int i, j;
	t_pb_type *pb_type;
	boolean const_gen;
	if(pb->pb_graph_node->pb_type->blif_model == NULL) {
		for(i = 0; i < pb->pb_graph_node->pb_type->modes[pb->mode].num_pb_type_children; i++) {
			pb_type = &(pb->pb_graph_node->pb_type->modes[pb->mode].pb_type_children[i]);
			for(j = 0; j < pb_type->num_pb; j++) {
				if(pb->child_pbs[i][j].name != NULL) {
					mark_constant_generators_rec(&(pb->child_pbs[i][j]), rr_graph, nlist);
				}
			}
		}
	} else if( strcmp(pb->pb_graph_node->pb_type->name, "inpad") != 0) {
		const_gen = TRUE;
		for(i = 0; i < pb->pb_graph_node->num_input_ports && const_gen == TRUE; i++) {
			for(j = 0; j < pb->pb_graph_node->num_input_pins[i] && const_gen == TRUE; j++) {
				if(rr_graph[pb->pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					const_gen = FALSE;
				}
			}
		}
		for(i = 0; i < pb->pb_graph_node->num_clock_ports && const_gen == TRUE; i++) {
			for(j = 0; j < pb->pb_graph_node->num_clock_pins[i] && const_gen == TRUE; j++) {
				if(rr_graph[pb->pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
					const_gen = FALSE;
				}
			}
		}
		if(const_gen == TRUE) {
			printf("%s is a constant generator \n", pb->name);
			for(i = 0; i < pb->pb_graph_node->num_output_ports; i++) {
				for(j = 0; j < pb->pb_graph_node->num_output_pins[i]; j++) {
					if(rr_graph[pb->pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num != OPEN) {
						nlist[rr_graph[pb->pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num].is_const_gen = TRUE;
					}
				}
			}
		}
	}
}

/* create logical block properties from saved block */
static void restore_logical_block_from_saved_block(INP int iblk, INP t_pb *pb) {
	int i;
	for(i = 0; i < num_saved_logical_blocks; i++) {
		if(pb->pb_graph_node->pb_type->model == saved_logical_blocks[i].model) {
			if(saved_logical_blocks[i].name != NULL && strcmp(pb->name, saved_logical_blocks[i].name) == 0) {
				break;
			}
		}
	}
	assert(i != num_saved_logical_blocks);
	logical_block[iblk].name = saved_logical_blocks[i].name;
	logical_block[iblk].clb_index = UNDEFINED;
	logical_block[iblk].clock_net = saved_logical_blocks[i].clock_net;
	logical_block[iblk].clock_net_tnode = NULL;
	logical_block[iblk].index = iblk;
	logical_block[iblk].input_net_tnodes = saved_logical_blocks[i].input_net_tnodes;
	logical_block[iblk].input_nets = saved_logical_blocks[i].input_nets;
	logical_block[iblk].model = saved_logical_blocks[i].model;
	logical_block[iblk].output_net_tnodes = saved_logical_blocks[i].output_net_tnodes;
	logical_block[iblk].output_nets = saved_logical_blocks[i].output_nets;
	logical_block[iblk].pb = pb;
	logical_block[iblk].truth_table = saved_logical_blocks[i].truth_table;
	logical_block[iblk].type = saved_logical_blocks[i].type;
	logical_block[iblk].used_input_pins = saved_logical_blocks[i].used_input_pins;

	saved_logical_blocks[i].name = NULL;
	saved_logical_blocks[i].input_net_tnodes = NULL;
	saved_logical_blocks[i].input_nets = NULL;
	saved_logical_blocks[i].output_net_tnodes = NULL;
	saved_logical_blocks[i].output_nets = NULL;
	saved_logical_blocks[i].truth_table = NULL;
}


/* Free logical blocks of netlist */
void free_logical_blocks() {
	int iblk, i;
	t_model_ports *port;
	struct s_linked_vptr *tvptr, *next;

	for (iblk=0;iblk<num_logical_blocks;iblk++) {
		port = logical_block[iblk].model->inputs;
		i = 0;
		while(port) {
			if(!port->is_clock) {
				free(logical_block[iblk].input_nets[i]);
				if(logical_block[iblk].input_net_tnodes) {
					if(logical_block[iblk].input_net_tnodes[i])
						free(logical_block[iblk].input_net_tnodes[i]);
				}
				i++;
			}
			port = port->next;
		}
		if(logical_block[iblk].input_net_tnodes) {
			free(logical_block[iblk].input_net_tnodes);
		}
		free(logical_block[iblk].input_nets);
		port = logical_block[iblk].model->outputs;
		i = 0;
		while(port) {
			free(logical_block[iblk].output_nets[i]);
			if(logical_block[iblk].output_net_tnodes) {
				if(logical_block[iblk].output_net_tnodes[i])
					free(logical_block[iblk].output_net_tnodes[i]);
			}
			i++;
			port = port->next;
		}
		if(logical_block[iblk].output_net_tnodes) {
			free(logical_block[iblk].output_net_tnodes);
		}
		free(logical_block[iblk].output_nets);
		free(logical_block[iblk].name);
		tvptr = logical_block[iblk].truth_table;
		while(tvptr != NULL) {
			if(tvptr->data_vptr)
				free(tvptr->data_vptr);
			next = tvptr->next;
			free(tvptr);
			tvptr = next;
		}
	}
	free(logical_block);
}

/* Free  logical blocks of netlist */
void free_logical_nets() {
	int inet;
	
	for (inet=0;inet<num_logical_nets;inet++) {
		free(vpack_net[inet].name);
		free(vpack_net[inet].node_block);
		free(vpack_net[inet].node_block_port);
		free(vpack_net[inet].node_block_pin);
	}
	free(vpack_net);
}

