#include <string.h>
#include <assert.h>
#include "util.h"
#include "arch_types.h"
#include "ReadLine.h"
#include "ezxml.h"
#include "read_xml_arch_file.h"
#include "read_xml_util.h"

/* special type indexes, necessary for initialization initialization, everything afterwards
   should use the pointers to these type indices*/ 

#define NUM_MODELS_IN_LIBRARY 4
#define EMPTY_TYPE_INDEX 0
#define IO_TYPE_INDEX 1
    enum Fc_type
{ FC_ABS, FC_FRAC, FC_FULL };

/* This identifies the t_type_ptr of an IO block */
static t_type_ptr IO_TYPE = NULL;

/* This identifies the t_type_ptr of an Empty block */
static t_type_ptr EMPTY_TYPE = NULL;

/* This identifies the t_type_ptr of the default logic block */
static t_type_ptr FILL_TYPE = NULL;

/* Describes the different types of CLBs available */
static struct s_type_descriptor *type_descriptors;

/* Function prototypes */ 
/*   Populate data */
static void ParseFc(ezxml_t Node,
		     enum Fc_type *Fc,
		     float *Val);
static void SetupEmptyType();
static void SetupPinLocationsAndPinClasses(ezxml_t Locations,
			       t_type_descriptor * Type);
static void SetupGridLocations(ezxml_t Locations,
				t_type_descriptor * Type);
#if 0
static void SetupTypeTiming(ezxml_t timing,
			     t_type_descriptor * Type);
#endif
/*    Process XML hiearchy */
static void ProcessPb_Type(INOUTP ezxml_t Parent, 
						   t_pb_type * pb_type, 
						   t_mode * mode);
static void ProcessPb_TypePort(INOUTP ezxml_t Parent, 
						   t_port * port);
static void ProcessPinToPinAnnotations(ezxml_t parent, 
						   t_pin_to_pin_annotation *annotation);
static void ProcessInterconnect(INOUTP ezxml_t Parent,
						   t_mode * mode);
static void ProcessMode(INOUTP ezxml_t Parent,
						   t_mode * mode);
static void Process_Fc(ezxml_t Fc_in_node,
			ezxml_t Fc_out_node,
			t_type_descriptor * Type);
static void ProcessComplexBlockProps(ezxml_t Node,
			      t_type_descriptor * Type);
static void ProcessChanWidthDistr(INOUTP ezxml_t Node,
				   OUTP struct s_arch *arch);
static void ProcessChanWidthDistrDir(INOUTP ezxml_t Node,
				      OUTP t_chan * chan);
static void ProcessModels(INOUTP ezxml_t Node,
			   OUTP struct s_arch *arch);
static void ProcessLayout(INOUTP ezxml_t Node,
			   OUTP struct s_arch *arch);
static void ProcessDevice(INOUTP ezxml_t Node,
			   OUTP struct s_arch *arch,
			   INP boolean timing_enabled);
static void alloc_and_load_default_child_for_pb_type(INOUTP t_pb_type *pb_type, char *new_name, t_pb_type *copy);
static void ProcessLutClass(INOUTP t_pb_type *lut_pb_type);
static void ProcessMemoryClass(INOUTP t_pb_type *mem_pb_type);
static void ProcessComplexBlocks(INOUTP ezxml_t Node,
			  OUTP t_type_descriptor ** Types,
			  OUTP int *NumTypes,
			  INP boolean timing_enabled);
static void ProcessSwitches(INOUTP ezxml_t Node,
			     OUTP struct s_switch_inf **Switches,
			     OUTP int *NumSwitches,
			     INP boolean timing_enabled);
static void ProcessSegments(INOUTP ezxml_t Parent,
			     OUTP struct s_segment_inf **Segs,
			     OUTP int *NumSegs,
			     INP struct s_switch_inf *Switches,
			     INP int NumSwitches,
			     INP boolean timing_enabled);
static void ProcessCB_SB(INOUTP ezxml_t Node,
			  INOUTP boolean * list,
			  INP int len);

static void CreateModelLibrary(OUTP struct s_arch *arch);
static void UpdateAndCheckModels(INOUTP struct s_arch *arch);
static void SyncModelsPbTypes(INOUTP struct s_arch *arch, INP t_type_descriptor * Types, INP int NumTypes);
static void AddModelsToPbTypes_rec(INOUTP t_pb_type *pb_type);
static void SyncModelsPbTypes_rec(INOUTP struct s_arch *arch, INP t_pb_type *pb_type);

static void PrintPb_types_rec(INP FILE * Echo, INP const t_pb_type * pb_type, int level);


/* Figures out the Fc type and value for the given node. Unlinks the 
 * type and value. */ 
static void
ParseFc(ezxml_t Node, enum Fc_type *Fc, float *Val)
{
    const char *Prop;

    Prop = FindProperty(Node, "type", TRUE);
    if(0 == strcmp(Prop, "abs"))
	{
	    *Fc = FC_ABS;
	}
    
    else if(0 == strcmp(Prop, "frac"))
	{
	    *Fc = FC_FRAC;
	}
    
    else if(0 == strcmp(Prop, "full"))
	{
	    *Fc = FC_FULL;
	}
    
    else
	{
	    printf(ERRTAG "[LINE %d] Invalid type '%s' for Fc. Only abs, frac " 
			"and full are allowed.\n", Node->line, Prop);
	    exit(1);
	}
    switch (*Fc)
	{
	case FC_FULL:
	    *Val = 0.0;
	    break;
	case FC_ABS:
	case FC_FRAC:
	    *Val = atof(Node->txt);
	    ezxml_set_attr(Node, "type", NULL);
	    ezxml_set_txt(Node, "");
	    break;
	default:
	    assert(0);
	}
    
	/* Release the property */ 
	ezxml_set_attr(Node, "type", NULL);
}

/* Sets up the pinloc map and pin classes for the type. Unlinks the loc nodes
 * from the XML tree.
 * Pins and pin classses must already be setup by SetupPinClasses */ 
static void
SetupPinLocationsAndPinClasses(ezxml_t Locations, t_type_descriptor * Type)
{
    int i, j, k, PinsPerSubtile, Count, Len;
	int capacity, pin_count;
	int num_class;
	const char * Prop;

    ezxml_t Cur, Prev;
    char **Tokens, **CurTokens;

	capacity = Type->capacity;

    PinsPerSubtile = Type->num_pins / Type->capacity;

	Prop = FindProperty(Locations, "pattern", TRUE);
	if(strcmp(Prop, "spread") == 0) {
		Type->pin_location_distribution = E_SPREAD_PIN_DISTR;
	} else if (strcmp(Prop,"custom") == 0) {
		Type->pin_location_distribution = E_CUSTOM_PIN_DISTR;
	} else {
		printf(ERRTAG "[LINE %d] %s is an invalid pin location pattern.\n", Locations->line, Prop);
		exit(1);
	}
	ezxml_set_attr(Locations, "pattern", NULL);
    
	/* Alloc and clear pin locations */ 
	Type->pinloc = (int ***)my_malloc(Type->height * sizeof(int **));
	Type->pin_height = (int *)my_calloc(Type->num_pins, sizeof(int));
    for(i = 0; i < Type->height; ++i)
	{
	    Type->pinloc[i] = (int **)my_malloc(4 * sizeof(int *));
	    for(j = 0; j < 4; ++j)
		{
		    Type->pinloc[i][j] =
			(int *)my_malloc(Type->num_pins * sizeof(int));
		    for(k = 0; k < Type->num_pins; ++k)
			{
			    Type->pinloc[i][j][k] = 0;
			}
		}
	}

	Type->pin_loc_assignments = (char****) my_malloc(Type->height * sizeof(char***));
	Type->num_pin_loc_assignments = (int**) my_malloc(Type->height * sizeof(int*));
	for(i = 0; i < Type->height; i++) {
		Type->pin_loc_assignments[i] = (char***) my_calloc(4, sizeof(char**));
		Type->num_pin_loc_assignments[i] = (int*) my_calloc(4, sizeof(int));	
	}
    
	/* Load the pin locations */ 
	if(Type->pin_location_distribution == E_CUSTOM_PIN_DISTR) {
		Cur = Locations->child;
		while(Cur)
		{
			CheckElement(Cur, "loc");
		    
			/* Get offset */ 
			i = GetIntProperty(Cur, "offset", FALSE, 0);
			if((i < 0) || (i >= Type->height))
			{
				printf(ERRTAG
					"[LINE %d] %d is an invalid offset for type '%s'.\n", Cur->line,
					i, Type->name);
				exit(1);
			}

			/* Get side */ 
			Prop = FindProperty(Cur, "side", TRUE);
			if(0 == strcmp(Prop, "left"))
			{
				j = LEFT;
			}
		    
			else if(0 == strcmp(Prop, "top"))
			{
				j = TOP;
			}
		    
			else if(0 == strcmp(Prop, "right"))
			{
				j = RIGHT;
			}
		    
			else if(0 == strcmp(Prop, "bottom"))
			{
				j = BOTTOM;
			}
		    
			else
			{
				printf(ERRTAG "[LINE %d] '%s' is not a valid side.\n", Cur->line, Prop);
				exit(1);
			}
			ezxml_set_attr(Cur, "side", NULL);
		    
			/* Check location is on perimeter */ 
			if((TOP == j) && (i != (Type->height - 1)))
			{
				printf(ERRTAG
					"[LINE %d] Locations are only allowed on large block " 
					"perimeter. 'top' side should be at offset %d only.\n", Cur->line,
					(Type->height - 1));
				exit(1);
			}
			if((BOTTOM == j) && (i != 0))
			{
				printf(ERRTAG
					"[LINE %d] Locations are only allowed on large block "
					"perimeter. 'bottom' side should be at offset 0 only.\n", Cur->line);
				exit(1);
			}
		    
			/* Go through lists of pins */ 
			CountTokensInString(Cur->txt, &Count, &Len);
			Type->num_pin_loc_assignments[i][j] = Count;
			if(Count > 0) 
			{
				Tokens = GetNodeTokens(Cur);
				CurTokens = Tokens;
				Type->pin_loc_assignments[i][j] = (char**) my_calloc(Count, sizeof(char*));
				for(k = 0; k < Count; k++) {
					/* Store location assignment */
					Type->pin_loc_assignments[i][j][k] = my_strdup(*CurTokens);
										    
					/* Advance through list of pins in this location */ 
					++CurTokens;
				}
				FreeTokens(&Tokens);
			}
			Prev = Cur;
			Cur = Cur->next;
			FreeNode(Prev);
		}
	}

	/* Setup pin classes */
	num_class = 0;
	for(i = 0; i < Type->pb_type->num_ports; i++) {
		if(Type->pb_type->ports[i].equivalent) {
			num_class += capacity;
		} else {
			num_class += capacity * Type->pb_type->ports[i].num_pins;
		}
	}
	Type->class_inf = my_calloc(num_class, sizeof(struct s_class));
	Type->num_class = num_class;
	Type->pin_class = my_malloc(Type->num_pins * sizeof(int) * capacity);
	Type->is_global_pin = my_malloc(Type->num_pins * sizeof(boolean) * capacity);
	for(i = 0; i < Type->num_pins * capacity; i++) {
		Type->pin_class[i] = OPEN;
		Type->is_global_pin[i] = OPEN;
	}

	pin_count = 0;

	/* Equivalent pins share the same class, non-equivalent pins belong to different pin classes */
	num_class = 0;
	for(i = 0; i < capacity; ++i)
	{
		for(j = 0; j < Type->pb_type->num_ports; ++j)
		{
			if(Type->pb_type->ports[j].equivalent) {
				Type->class_inf[num_class].num_pins = Type->pb_type->ports[j].num_pins;
				Type->class_inf[num_class].pinlist = 
					(int *)my_malloc(sizeof(int) * Type->pb_type->ports[j].num_pins);
			}
	
			for(k = 0; k < Type->pb_type->ports[j].num_pins; ++k)
			{
				if(!Type->pb_type->ports[j].equivalent) {
					Type->class_inf[num_class].num_pins = 1;
					Type->class_inf[num_class].pinlist = (int *)my_malloc(sizeof(int) * 1);
					Type->class_inf[num_class].pinlist[0] =	pin_count;
				} else {
					Type->class_inf[num_class].pinlist[k] =	pin_count;
				}

				if(Type->pb_type->ports[j].type == IN_PORT) {
					Type->class_inf[num_class].type = RECEIVER;
				} else {
					assert(Type->pb_type->ports[j].type == OUT_PORT);
					Type->class_inf[num_class].type = DRIVER;
				}
				Type->pin_class[pin_count] = num_class;
				Type->is_global_pin[pin_count] = Type->pb_type->ports[j].is_clock;
				pin_count++;

				if(!Type->pb_type->ports[j].equivalent) {
					num_class++;
				}
			}
			if(Type->pb_type->ports[j].equivalent) {
				num_class++;
			}
		}
	}
	assert(num_class == Type->num_class);
	assert(pin_count == Type->num_pins);
}

/* Sets up the grid_loc_def for the type. Unlinks the loc nodes
 * from the XML tree. */ 
static void
SetupGridLocations(ezxml_t Locations, t_type_descriptor * Type)
{
    int i;

    ezxml_t Cur, Prev;
    const char *Prop;

    Type->num_grid_loc_def = CountChildren(Locations, "loc", 1);
    Type->grid_loc_def = 
	(struct s_grid_loc_def *)my_calloc(Type->num_grid_loc_def,
					   sizeof(struct s_grid_loc_def));
    
	/* Load the pin locations */ 
	Cur = Locations->child;
    i = 0;
    while(Cur)
	{
	    CheckElement(Cur, "loc");
	    
		/* loc index */ 
		Prop = FindProperty(Cur, "type", TRUE);
	    if(Prop)
		{
			if(strcmp(Prop, "perimeter") == 0)
			{
			    if(Type->num_grid_loc_def != 1)
				{
				    printf(ERRTAG
						"[LINE %d] Another loc specified for perimeter.\n", Cur->line);
				    exit(1);
				}
			    Type->grid_loc_def[i].grid_loc_type = BOUNDARY;
			    assert(IO_TYPE == Type); /* IO goes to boundary */
			} else if(strcmp(Prop, "fill") == 0)
			{
			    if(Type->num_grid_loc_def != 1 || FILL_TYPE != NULL)
				{
				    printf(ERRTAG
						"[LINE %d] Another loc specified for fill.\n", Cur->line);
				    exit(1);
				}
			    Type->grid_loc_def[i].grid_loc_type = FILL;
			    FILL_TYPE = Type;
			}
		    else if(strcmp(Prop, "col") == 0)
			{
			    Type->grid_loc_def[i].grid_loc_type = COL_REPEAT;
			}
		    else if(strcmp(Prop, "rel") == 0)
			{
			    Type->grid_loc_def[i].grid_loc_type = COL_REL;
			}
		    else
			{
			    printf(ERRTAG
					"[LINE %d] Unknown grid location type '%s' for type '%s'.\n", Cur->line,
				    Prop, Type->name);
			    exit(1);
			}
		    ezxml_set_attr(Cur, "type", NULL);
		}
	    Prop = FindProperty(Cur, "start", FALSE);
	    if(Type->grid_loc_def[i].grid_loc_type == COL_REPEAT)
		{
		    if(Prop == NULL)
			{
			    printf(ERRTAG
				    "[LINE %d] grid location property 'start' must be specified for grid location type 'col'.\n",
					Cur->line);
			    exit(1);
			}
		    Type->grid_loc_def[i].start_col = my_atoi(Prop);
		    ezxml_set_attr(Cur, "start", NULL);
		}
	    else if(Prop != NULL)
		{
		    printf(ERRTAG
			    "[LINE %d] grid location property 'start' valid for grid location type 'col' only.\n",
				Cur->line);
		    exit(1);
		}
	    Prop = FindProperty(Cur, "repeat", FALSE);
	    if(Type->grid_loc_def[i].grid_loc_type == COL_REPEAT)
		{
		    if(Prop != NULL)
			{
			    Type->grid_loc_def[i].repeat = my_atoi(Prop);
			    ezxml_set_attr(Cur, "repeat", NULL);
			}
		}
	    else if(Prop != NULL)
		{
		    printf(ERRTAG
			    "[LINE %d] grid location property 'repeat' valid for grid location type 'col' only.\n",
				Cur->line);
		    exit(1);
		}
	    Prop = FindProperty(Cur, "pos", FALSE);
	    if(Type->grid_loc_def[i].grid_loc_type == COL_REL)
		{
		    if(Prop == NULL)
			{
			    printf(ERRTAG
				    "[LINE %d] grid location property 'pos' must be specified for grid location type 'rel'.\n",
					Cur->line);
			    exit(1);
			}
		    Type->grid_loc_def[i].col_rel = atof(Prop);
		    ezxml_set_attr(Cur, "pos", NULL);
		}
	    else if(Prop != NULL)
		{
		    printf(ERRTAG
			    "[LINE %d] grid location property 'pos' valid for grid location type 'rel' only.\n",
				Cur->line);
		    exit(1);
		}

		Type->grid_loc_def[i].priority = GetIntProperty(Cur, "priority", FALSE, 1);

	    Prev = Cur;
	    Cur = Cur->next;
	    FreeNode(Prev);
	    i++;
	}
}

static void
ProcessPinToPinAnnotations(ezxml_t Parent, t_pin_to_pin_annotation *annotation)
{
	int i = 0;
	const char *Prop;
	
	if(FindProperty(Parent, "max", FALSE)) {
		i++;
	}
	if(FindProperty(Parent, "min", FALSE)) {
		i++;
	}
	if(FindProperty(Parent, "type", FALSE)) {
		i++;
	}
	if(FindProperty(Parent, "value", FALSE)) {
		i++;
	}
	if(0 == strcmp(Parent->name, "C_constant") || 0 == strcmp(Parent->name, "C_matrix")) {
		i = 1;
	}

	annotation->num_value_prop_pairs = i;
	annotation->prop = my_calloc(i, sizeof(int));
	annotation->value = my_calloc(i, sizeof(char *));

	i = 0;
	if(0 == strcmp(Parent->name, "delay_constant")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_DELAY;
		annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT;
		Prop = FindProperty(Parent, "max", FALSE);
		if(Prop) {
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_MAX;
			annotation->value[i] = my_strdup(Prop);
			ezxml_set_attr(Parent, "max", NULL);
			i++;
		}
		Prop = FindProperty(Parent, "min", FALSE);
		if(Prop) {
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_MIN;
			annotation->value[i] = my_strdup(Prop);
			ezxml_set_attr(Parent, "min", NULL);
			i++;
		}
		Prop = FindProperty(Parent, "in_port", TRUE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "in_port", NULL);
		Prop = FindProperty(Parent, "out_port", TRUE);
		annotation->output_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "out_port", NULL);
	} else if (0 == strcmp(Parent->name, "delay_matrix")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_DELAY;
		annotation->format = E_ANNOT_PIN_TO_PIN_MATRIX;
		Prop = FindProperty(Parent, "type", TRUE);
		annotation->value[i] = my_strdup(Parent->txt);
		ezxml_set_txt(Parent, "");
		if(0 == strcmp(Prop, "max")) {
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_MAX;
		} else {
			assert(0 == strcmp(Prop, "min"));
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_MIN;
		}
		ezxml_set_attr(Parent, "type", NULL);
		i++;
		Prop = FindProperty(Parent, "in_port", TRUE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "in_port", NULL);
		Prop = FindProperty(Parent, "out_port", TRUE);
		annotation->output_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "out_port", NULL);
	} else if (0 == strcmp(Parent->name, "C_constant")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_CAPACITANCE;
		annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT;
		Prop = FindProperty(Parent, "C", TRUE);
		annotation->value[i] = my_strdup(Prop);
		ezxml_set_attr(Parent, "C", NULL);
		annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_CAPACITANCE_C;
		i++;

		Prop = FindProperty(Parent, "in_port", FALSE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "in_port", NULL);
		Prop = FindProperty(Parent, "out_port", FALSE);
		annotation->output_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "out_port", NULL);
		assert(annotation->output_pins != NULL || annotation->input_pins != NULL);
	} else if (0 == strcmp(Parent->name, "C_matrix")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_CAPACITANCE;
		annotation->format = E_ANNOT_PIN_TO_PIN_MATRIX;
		annotation->value[i] = my_strdup(Parent->txt);
		ezxml_set_txt(Parent, "");
		annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_CAPACITANCE_C;
		i++;
		Prop = FindProperty(Parent, "in_port", FALSE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "in_port", NULL);
		Prop = FindProperty(Parent, "out_port", FALSE);
		annotation->output_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "out_port", NULL);
		assert(annotation->output_pins != NULL || annotation->input_pins != NULL);
	} else if (0 == strcmp(Parent->name, "T_setup")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_DELAY;
		annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT;
		Prop = FindProperty(Parent, "value", TRUE);
		annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_TSETUP;
		annotation->value[i] = my_strdup(Prop);
		ezxml_set_attr(Parent, "value", NULL);
		i++;
		Prop = FindProperty(Parent, "port", TRUE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "port", NULL);
		Prop = FindProperty(Parent, "clock", TRUE);
		annotation->clock = my_strdup(Prop);
		ezxml_set_attr(Parent, "clock", NULL);
	} else if (0 == strcmp(Parent->name, "T_clock_to_Q")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_DELAY;
		annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT;
		Prop = FindProperty(Parent, "max", FALSE);
		if(Prop) {
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MAX;
			annotation->value[i] = my_strdup(Prop);
			ezxml_set_attr(Parent, "max", NULL);
			i++;
		}
		Prop = FindProperty(Parent, "min", FALSE);
		if(Prop) {
			annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_CLOCK_TO_Q_MIN;
			annotation->value[i] = my_strdup(Prop);
			ezxml_set_attr(Parent, "min", NULL);
			i++;
		}
		
		Prop = FindProperty(Parent, "port", TRUE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "port", NULL);
		Prop = FindProperty(Parent, "clock", TRUE);
		annotation->clock = my_strdup(Prop);
		ezxml_set_attr(Parent, "clock", NULL);
	} else if (0 == strcmp(Parent->name, "T_hold")) {
		annotation->type = (int) E_ANNOT_PIN_TO_PIN_DELAY;
		annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT;
		Prop = FindProperty(Parent, "value", TRUE);
		annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_DELAY_THOLD;
		annotation->value[i] = my_strdup(Prop);
		ezxml_set_attr(Parent, "value", NULL);
		i++;
				
		Prop = FindProperty(Parent, "port", TRUE);
		annotation->input_pins = my_strdup(Prop);
		ezxml_set_attr(Parent, "port", NULL);
		Prop = FindProperty(Parent, "clock", TRUE);
		annotation->clock = my_strdup(Prop);
		ezxml_set_attr(Parent, "clock", NULL);
	} else {
		printf(ERRTAG "[LINE %d] Unknown port type %s in %s in %s", Parent->line,
			Parent->name, Parent->parent->name, Parent->parent->parent->name);
		exit(1);
	}
	assert (i == annotation->num_value_prop_pairs);
}

/* Takes in a pb_type, allocates and loads data for it and recurses downwards */
static void ProcessPb_Type(INOUTP ezxml_t Parent, 
						   t_pb_type * pb_type, 
						   t_mode * mode) {
	int num_ports, i, j, num_annotations;
	const char *Prop;
	ezxml_t Cur, Prev;
	char* class_name;

	pb_type->parent_mode = mode;
	if(mode != NULL && mode->parent_pb_type != NULL) {
		pb_type->depth = mode->parent_pb_type->depth + 1;
		Prop = FindProperty(Parent, "name", TRUE);
		pb_type->name = my_strdup(Prop);
		ezxml_set_attr(Parent, "name", NULL);
	} else {
		pb_type->depth = 0;
		/* same name as type */
	}

	Prop = FindProperty(Parent, "blif_model", FALSE);
	pb_type->blif_model = my_strdup(Prop);
	ezxml_set_attr(Parent, "blif_model", NULL);

	pb_type->class_type = UNKNOWN_CLASS;
	Prop = FindProperty(Parent, "class", FALSE);
	class_name = my_strdup(Prop);
	
	if(class_name) {
		ezxml_set_attr(Parent, "class", NULL);
		if(0 == strcmp(class_name, "lut")) {
			pb_type->class_type = LUT_CLASS;
		} else if(0 == strcmp(class_name, "flipflop")) {
			pb_type->class_type = LATCH_CLASS;
		} else if(0 == strcmp(class_name, "memory")) {
			pb_type->class_type = MEMORY_CLASS;
		} else {
			printf("[LINE %d] Unknown class %s in pb_type %s\n", Parent->line, class_name, pb_type->name);
			exit(1);
		}
		free(class_name);
	}
	
	if(mode == NULL) {
		pb_type->num_pb = 1;
	} else {
		pb_type->num_pb = GetIntProperty(Parent, "num_pb", TRUE, 0);
	}

	assert(pb_type->num_pb > 0);
	num_ports = 0;
	num_ports += CountChildren(Parent, "input", 0);
	num_ports += CountChildren(Parent, "output", 0);
	num_ports += CountChildren(Parent, "clock", 0);
	pb_type->ports = my_calloc(num_ports, sizeof(t_port));
	pb_type->num_ports = num_ports;
	
	/* process ports */
	j = 0;
	for(i = 0; i < 3; i++) {
		if(i == 0) {
			Cur = FindFirstElement(Parent, "input", FALSE);
		} else if (i ==1) {
			Cur = FindFirstElement(Parent, "output", FALSE);
		} else {
			Cur = FindFirstElement(Parent, "clock", FALSE);
		}
		while (Cur != NULL)
		{
			ProcessPb_TypePort(Cur, &pb_type->ports[j]);
			pb_type->ports[j].parent_pb_type = pb_type;
				
			/* get next iteration */
			Prev = Cur;
			Cur = Cur->next;
			j++;
			FreeNode(Prev);
		}
	}
	assert(j == num_ports);

	/* Count stats on the number of each type of pin */
	pb_type->num_clock_pins = pb_type->num_input_pins = pb_type->num_output_pins = 0;
	for(i = 0; i < pb_type->num_ports; i++) {
		if(pb_type->ports[i].type == IN_PORT && pb_type->ports[i].is_clock == FALSE) {
			pb_type->num_input_pins += pb_type->ports[i].num_pins;
		} else if(pb_type->ports[i].type == OUT_PORT) {
			assert(pb_type->ports[i].is_clock == FALSE);
			pb_type->num_output_pins += pb_type->ports[i].num_pins;
		} else {
			assert(pb_type->ports[i].is_clock && pb_type->ports[i].type == IN_PORT);
			pb_type->num_clock_pins += pb_type->ports[i].num_pins;
		}
	}

	/* set max_internal_delay if exist */
	pb_type->max_internal_delay = UNDEFINED;
	Cur = FindElement(Parent, "max_internal_delay", FALSE);
	if(Cur) {
		pb_type->max_internal_delay = GetFloatProperty(Cur, "value", TRUE, UNDEFINED);
		FreeNode(Cur);
	}
	
	pb_type->annotations = NULL;
	pb_type->num_annotations = 0;
	i = 0;
	/* Determine if this is a leaf or container pb_type */
	if(pb_type->blif_model != NULL) {
		/* Process delay and capacitance annotations */
		num_annotations = 0;
		num_annotations += CountChildren(Parent, "delay_constant", 0);
		num_annotations += CountChildren(Parent, "delay_matrix", 0);
		num_annotations += CountChildren(Parent, "C_constant", 0);
		num_annotations += CountChildren(Parent, "C_matrix", 0);
		num_annotations += CountChildren(Parent, "T_setup", 0);
		num_annotations += CountChildren(Parent, "T_clock_to_Q", 0);
		num_annotations += CountChildren(Parent, "T_hold", 0);

		pb_type->annotations = my_calloc(num_annotations, sizeof(t_pin_to_pin_annotation));
		pb_type->num_annotations = num_annotations;

		j = 0;
		Cur = NULL;
		for(i = 0; i < 7; i++) {
			if(i == 0) {
				Cur = FindFirstElement(Parent, "delay_constant", FALSE);
			} else if (i == 1) {
				Cur = FindFirstElement(Parent, "delay_matrix", FALSE);
			} else if (i == 2) {
				Cur = FindFirstElement(Parent, "C_constant", FALSE);
			} else if (i == 3) {
				Cur = FindFirstElement(Parent, "C_matrix", FALSE);
			} else if (i == 4) {
				Cur = FindFirstElement(Parent, "T_setup", FALSE);
			} else if (i == 5) {
				Cur = FindFirstElement(Parent, "T_clock_to_Q", FALSE);
			} else if (i == 6) {
				Cur = FindFirstElement(Parent, "T_hold", FALSE);
			}
			while (Cur != NULL)
			{
				ProcessPinToPinAnnotations(Cur, &pb_type->annotations[j]);

				/* get next iteration */
				Prev = Cur;
				Cur = Cur->next;
				j++;
				FreeNode(Prev);
			}
		}
		assert(j == num_annotations);

		/* leaf pb_type, if special known class, then read class lib otherwise treat as primitive */
		if(pb_type->class_type == LUT_CLASS) {
			ProcessLutClass(pb_type);
		} else if(pb_type->class_type == MEMORY_CLASS) {
			ProcessMemoryClass(pb_type);
		} else {
			/* other leaf pb_type do not have modes */
			pb_type->num_modes = 0;
			assert(CountChildren(Parent, "mode", 0) == 0);
		}
	} else {
		/* container pb_type, process modes */
		assert(pb_type->class_type == UNKNOWN_CLASS);
		pb_type->num_modes = CountChildren(Parent, "mode", 0);
		
		if(pb_type->num_modes == 0) {
			/* The pb_type operates in an implied one mode */
			pb_type->num_modes = 1;
			pb_type->modes = my_calloc(pb_type->num_modes, sizeof(t_mode));
			pb_type->modes[i].parent_pb_type = pb_type;			
			pb_type->modes[i].index = i;
			ProcessMode(Parent, &pb_type->modes[i]);
			i++;
		} else {
			pb_type->modes = my_calloc(pb_type->num_modes, sizeof(t_mode));
		
			Cur = FindFirstElement(Parent, "mode", TRUE);
			while (Cur != NULL)
			{
				if(0 == strcmp(Cur->name, "mode")) {
					pb_type->modes[i].parent_pb_type = pb_type;
					ProcessMode(Cur, &pb_type->modes[i]);
							
					/* get next iteration */
					Prev = Cur;
					Cur = Cur->next;
					i++;
					FreeNode(Prev);
				}
			}
		}
		assert(i == pb_type->num_modes);
	}   
}

static void ProcessPb_TypePort(INOUTP ezxml_t Parent, 
							   t_port * port) {
	const char *Prop;
	Prop = FindProperty(Parent, "name", TRUE);
	port->name = my_strdup(Prop);
	ezxml_set_attr(Parent, "name", NULL);

	Prop = FindProperty(Parent, "port_class", FALSE);
	port->port_class = my_strdup(Prop);
	ezxml_set_attr(Parent, "port_class", NULL);

	port->equivalent = GetBooleanProperty(Parent, "equivalent", FALSE, FALSE);
	port->num_pins = GetIntProperty(Parent, "num_pins", TRUE, 0);
	
	if(0 == strcmp(Parent->name, "input")) {
		port->type = IN_PORT;
		port->is_clock = FALSE;
	} else if (0 == strcmp(Parent->name, "output")) {
		port->type = OUT_PORT;
		port->is_clock = FALSE;
	} else if (0 == strcmp(Parent->name, "clock")) {
		port->type = IN_PORT;
		port->is_clock = TRUE;
	} else {
		printf(ERRTAG "[LINE %d] Unknown port type %s", Parent->line, Parent->name);
		exit(1);
	}
}

static void ProcessInterconnect(INOUTP ezxml_t Parent,
								t_mode * mode) {
	int num_interconnect = 0;
	int i, j, k, index, num_annotations;
	const char *Prop;
	ezxml_t Cur, Prev;
	ezxml_t Cur2, Prev2;

	num_interconnect += CountChildren(Parent, "complete", 0);
	num_interconnect += CountChildren(Parent, "direct", 0);
	num_interconnect += CountChildren(Parent, "mux", 0);

	mode->num_interconnect = num_interconnect;
	mode->interconnect = my_calloc(num_interconnect, sizeof(t_interconnect));

	i = 0;
	for(index = 0; index < 3; index++) {
		if(index == 0) {
			Cur = FindFirstElement(Parent, "complete", FALSE);
		} else if (index == 1) {
			Cur = FindFirstElement(Parent, "direct", FALSE);
		} else { 
			Cur = FindFirstElement(Parent, "mux", FALSE);
		}
		while (Cur != NULL)
		{
			if(0 == strcmp(Cur->name, "complete")) {
				mode->interconnect[i].type = COMPLETE_INTERC;
			} else if(0 == strcmp(Cur->name, "direct")) {
				mode->interconnect[i].type = DIRECT_INTERC;
			} else {
				assert(0 == strcmp(Cur->name, "mux"));
				mode->interconnect[i].type = MUX_INTERC;
			} 

			mode->interconnect[i].parent_mode_index = mode->index;
			Prop = FindProperty(Cur, "input", TRUE);
			mode->interconnect[i].input_string = my_strdup(Prop);
			ezxml_set_attr(Cur, "input", NULL);

			Prop = FindProperty(Cur, "output", TRUE);
			mode->interconnect[i].output_string = my_strdup(Prop);
			ezxml_set_attr(Cur, "output", NULL);

			Prop = FindProperty(Cur, "name", TRUE);
			mode->interconnect[i].name = my_strdup(Prop);
			ezxml_set_attr(Cur, "name", NULL);

			/* Process delay and capacitance annotations */
			num_annotations = 0;
			num_annotations += CountChildren(Cur, "delay_constant", 0);
			num_annotations += CountChildren(Cur, "delay_matrix", 0);
			num_annotations += CountChildren(Cur, "C_constant", 0);
			num_annotations += CountChildren(Cur, "C_matrix", 0);

			mode->interconnect[i].annotations = my_calloc(num_annotations, sizeof(t_pin_to_pin_annotation));
			mode->interconnect[i].num_annotations = num_annotations;

			k = 0;
			Cur2 = NULL;
			for(j = 0; j < 4; j++) {
				if(j == 0) {
					Cur2 = FindFirstElement(Cur, "delay_constant", FALSE);
				} else if (j == 1) {
					Cur2 = FindFirstElement(Cur, "delay_matrix", FALSE);
				} else if (j == 2) {
					Cur2 = FindFirstElement(Cur, "C_constant", FALSE);
				} else if (j == 3) {
					Cur2 = FindFirstElement(Cur, "C_matrix", FALSE);
				}
				while (Cur2 != NULL)
				{
					ProcessPinToPinAnnotations(Cur2, &(mode->interconnect[i].annotations[k]));

					/* get next iteration */
					Prev2 = Cur2;
					Cur2 = Cur2->next;
					k++;
					FreeNode(Prev2);
				}
			}
			assert(k == num_annotations);
					
			/* get next iteration */
			Prev = Cur;
			Cur = Cur->next;
			FreeNode(Prev);
			i++;
		}
	}

	assert(i == num_interconnect);
}

static void ProcessMode(INOUTP ezxml_t Parent,
						t_mode * mode) {
	int i;
	const char *Prop;
	ezxml_t Cur, Prev;

	if(0 == strcmp(Parent->name, "pb_type")) {
		/* implied mode */
		mode->name = my_strdup(mode->parent_pb_type->name);
	} else {
		Prop = FindProperty(Parent, "name", TRUE);
		mode->name = my_strdup(Prop);
		ezxml_set_attr(Parent, "name", NULL);
	}

	mode->num_pb_type_children = CountChildren(Parent, "pb_type", 1);
	mode->pb_type_children = my_calloc(mode->num_pb_type_children, sizeof(t_pb_type));

	i = 0;
	Cur = FindFirstElement(Parent, "pb_type", TRUE);
	while (Cur != NULL)
	{
		if(0 == strcmp(Cur->name, "pb_type")) {
			ProcessPb_Type(Cur, &mode->pb_type_children[i], mode);
					
			/* get next iteration */
			Prev = Cur;
			Cur = Cur->next;
			i++;
			FreeNode(Prev);
		}
	}

	Cur = FindElement(Parent, "interconnect", TRUE);
	ProcessInterconnect(Cur, mode);
	FreeNode(Cur);
}

/* Takes in the node ptr for the 'fc_in' and 'fc_out' elements and initializes
 * the appropriate fields of type. Unlinks the contents of the nodes. */ 
static void
Process_Fc(ezxml_t Fc_in_node, ezxml_t Fc_out_node, t_type_descriptor * Type)
{
    enum Fc_type Type_in;
    enum Fc_type Type_out;

    ParseFc(Fc_in_node, &Type_in, &Type->Fc_in);
    ParseFc(Fc_out_node, &Type_out, &Type->Fc_out);
    if(FC_FULL == Type_in)
	{
		printf(ERRTAG "[LINE %d] 'full' Fc type isn't allowed for Fc_in.\n", Fc_in_node->line);
	    exit(1);
	}
    Type->is_Fc_out_full_flex = FALSE;
    Type->is_Fc_frac = FALSE;
    if(FC_FULL == Type_out)
	{
	    Type->is_Fc_out_full_flex = TRUE;
	}
    
    else if(Type_in != Type_out)
	{
	    printf(ERRTAG
			"[LINE %d] Fc_in and Fc_out must have same type unless Fc_out has type 'full'.\n", Fc_in_node->line);
	    exit(1);
	}
    if(FC_FRAC == Type_in)
	{
	    Type->is_Fc_frac = TRUE;
	}
}

/* Thie processes attributes of the 'type' tag and then unlinks them */ 
static void
ProcessComplexBlockProps(ezxml_t Node, t_type_descriptor * Type)
{
    const char *Prop;
    
	/* Load type name */ 
	Prop = FindProperty(Node, "name", TRUE);
    Type->name = my_strdup(Prop);
    ezxml_set_attr(Node, "name", NULL);
    
	/* Load properties */
	Type->capacity = GetIntProperty(Node, "capacity", FALSE, 1);   /* TODO: Any block with capacity > 1 that is not I/O has not been tested, must test */
	Type->height = GetIntProperty(Node, "height", FALSE, 1);    
	Type->area = GetFloatProperty(Node, "area", FALSE, UNDEFINED);
	

	if(atof(Prop) < 0) {
		printf("[LINE %d] Area for type %s must be non-negative\n", Node->line, Type->name);
		exit(1);
	}
}

/* Takes in node pointing to <models> and loads all the
 * child type objects. Unlinks the entire <models> node
 * when complete. */ 
static void
ProcessModels(INOUTP ezxml_t Node, OUTP struct s_arch *arch)
{
	const char *Prop;
	ezxml_t child;
	ezxml_t p;
	ezxml_t junk;
	ezxml_t junkp;
	t_model *temp;
	t_model_ports *tp;
	int index;

	index = NUM_MODELS_IN_LIBRARY;

	arch->models = NULL;
	child = ezxml_child(Node, "model");
	while (child != NULL)
	{
		temp = (t_model*)my_calloc(1, sizeof(t_model));
		temp->used = 0;
		temp->inputs = temp->outputs = temp->instances = NULL;
		Prop = FindProperty(child, "name", TRUE);
		temp->name = my_strdup(Prop);
		ezxml_set_attr(child, "name", NULL);
		temp->pb_types = NULL;
		temp->index = index;
		index++;

		/* Process the inputs */
		p = ezxml_child(child, "input_ports");
		junkp = p;
		if (p == NULL)
			printf(ERRTAG "Required input ports not found for element '%s'.\n", temp->name);

		p = ezxml_child(p, "port");
		if (p != NULL)
		{
			while (p != NULL)
			{
				tp = (t_model_ports*)my_calloc(1, sizeof(t_model_ports));
				Prop = FindProperty(p, "name", TRUE);
				tp->name = my_strdup(Prop);
				ezxml_set_attr(p, "name", NULL);
				tp->size = -1; /* determined later by pb_types */
				tp->min_size = -1; /* determined later by pb_types */
				tp->next = temp->inputs;
				tp->dir = IN_PORT;
				tp->is_clock = FALSE;
				Prop = FindProperty(p, "is_clock", FALSE);
				if(Prop && my_atoi(Prop) != 0) {
					tp->is_clock = TRUE;
				}
				ezxml_set_attr(p, "is_clock", NULL);
				temp->inputs = tp;
				junk = p;
				p = ezxml_next(p);
				FreeNode(junk);
			}
		}
		else /* No input ports? */
		{
			printf(ERRTAG "Required input ports not found for element '%s'.\n", temp->name);
		}
		FreeNode(junkp);

		/* Process the outputs */
		p = ezxml_child(child, "output_ports");
		junkp = p;
		if (p == NULL)
			printf(ERRTAG "Required output ports not found for element '%s'.\n", temp->name);

		p = ezxml_child(p, "port");
		if (p != NULL)
		{
			while (p != NULL)
			{
				tp = (t_model_ports*)my_calloc(1, sizeof(t_model_ports));
				Prop = FindProperty(p, "name", TRUE);
				tp->name = my_strdup(Prop);
				ezxml_set_attr(p, "name", NULL);
				tp->size = -1; /* determined later by pb_types */
				tp->min_size = -1; /* determined later by pb_types */
				tp->next = temp->outputs;
				tp->dir = OUT_PORT;
				temp->outputs = tp;
				junk = p;
				p = ezxml_next(p);
				FreeNode(junk);
			}
		}
		else /* No output ports? */
		{
			printf(ERRTAG "Required output ports not found for element '%s'.\n", temp->name);
		}
		FreeNode(junkp);

		/* Find the next model */
		temp->next = arch->models;
  		arch->models = temp;
		junk = child;
		child = ezxml_next(child);
		FreeNode(junk);
	}

	return;
}

/* Takes in node pointing to <layout> and loads all the
 * child type objects. Unlinks the entire <layout> node
 * when complete. */ 
static void
ProcessLayout(INOUTP ezxml_t Node, OUTP struct s_arch *arch)
{
    const char *Prop;

    arch->clb_grid.IsAuto = TRUE;
    
	/* Load width and height if applicable */ 
	Prop = FindProperty(Node, "width", FALSE);
    if(Prop != NULL)
	{
	    arch->clb_grid.IsAuto = FALSE;
	    arch->clb_grid.W = my_atoi(Prop);
	    ezxml_set_attr(Node, "width", NULL);

		arch->clb_grid.H = GetIntProperty(Node, "height", TRUE, UNDEFINED);
	}
    
	/* Load aspect ratio if applicable */ 
	Prop = FindProperty(Node, "auto", arch->clb_grid.IsAuto);
    if(Prop != NULL)
	{
	    if(arch->clb_grid.IsAuto == FALSE)
		{
		    printf(ERRTAG
			    "Auto-sizing, width and height cannot be specified\n");
		}
	    arch->clb_grid.Aspect = atof(Prop);
	    ezxml_set_attr(Node, "auto", NULL);
		if(arch->clb_grid.Aspect <= 0)
		{
		    printf(ERRTAG
			    "Grid aspect ratio is less than or equal to zero %g\n", arch->clb_grid.Aspect);
		}
	}
}

/* Takes in node pointing to <device> and loads all the
 * child type objects. Unlinks the entire <device> node
 * when complete. */ 
static void
ProcessDevice(INOUTP ezxml_t Node, OUTP struct s_arch *arch,
	INP boolean timing_enabled)
{
    const char *Prop;
    ezxml_t Cur;

    Cur = FindElement(Node, "sizing", TRUE);
    arch->R_minW_nmos = GetFloatProperty(Cur, "R_minW_nmos", timing_enabled, 0);
    arch->R_minW_pmos = GetFloatProperty(Cur, "R_minW_pmos", timing_enabled, 0);
    arch->ipin_mux_trans_size = GetFloatProperty(Cur, "ipin_mux_trans_size", FALSE, 0);
    FreeNode(Cur);

    Cur = FindElement(Node, "timing", timing_enabled);
    if(Cur != NULL)
	{
	    arch->C_ipin_cblock = GetFloatProperty(Cur, "C_ipin_cblock", FALSE, 0);
	    arch->T_ipin_cblock = GetFloatProperty(Cur, "T_ipin_cblock", FALSE, 0);
	    FreeNode(Cur);
	}

    Cur = FindElement(Node, "area", TRUE);
    arch->grid_logic_tile_area = GetFloatProperty(Cur, "grid_logic_tile_area", FALSE, 0);
    FreeNode(Cur);

    Cur = FindElement(Node, "chan_width_distr", FALSE);
    if(Cur != NULL)
	{
	    ProcessChanWidthDistr(Cur, arch);
	    FreeNode(Cur);
	}

    Cur = FindElement(Node, "switch_block", TRUE);
    Prop = FindProperty(Cur, "type", TRUE);
    if(strcmp(Prop, "wilton") == 0)
	{
	    arch->SBType = WILTON;
	}
    else if(strcmp(Prop, "universal") == 0)
	{
	    arch->SBType = UNIVERSAL;
	}
    else if(strcmp(Prop, "subset") == 0)
	{
	    arch->SBType = SUBSET;
	}
    else
	{
		printf(ERRTAG "[LINE %d] Unknown property %s for switch block type x\n", Cur->line,
		    Prop);
	    exit(1);
	}
    ezxml_set_attr(Cur, "type", NULL);

    arch->Fs = GetIntProperty(Cur, "fs", TRUE, 3);

    FreeNode(Cur);
}

/* Takes in node pointing to <chan_width_distr> and loads all the
 * child type objects. Unlinks the entire <chan_width_distr> node
 * when complete. */ 
static void
ProcessChanWidthDistr(INOUTP ezxml_t Node, OUTP struct s_arch *arch)
{
    ezxml_t Cur;

    Cur = FindElement(Node, "io", TRUE);
    arch->Chans.chan_width_io = GetFloatProperty(Cur, "width", TRUE, UNDEFINED);
    FreeNode(Cur);
    Cur = FindElement(Node, "x", TRUE);
    ProcessChanWidthDistrDir(Cur, &arch->Chans.chan_x_dist);
    FreeNode(Cur);
    Cur = FindElement(Node, "y", TRUE);
    ProcessChanWidthDistrDir(Cur, &arch->Chans.chan_y_dist);
    FreeNode(Cur);
}

/* Takes in node within <chan_width_distr> and loads all the
 * child type objects. Unlinks the entire node when complete. */ 
static void
ProcessChanWidthDistrDir(INOUTP ezxml_t Node, OUTP t_chan * chan)
{
    const char *Prop;

    boolean hasXpeak, hasWidth, hasDc;
    hasXpeak = hasWidth = hasDc = FALSE;
    Prop = FindProperty(Node, "distr", TRUE);
    if(strcmp(Prop, "uniform") == 0)
	{
	    chan->type = UNIFORM;
	}
    else if(strcmp(Prop, "gaussian") == 0)
	{
	    chan->type = GAUSSIAN;
	    hasXpeak = hasWidth = hasDc = TRUE;
	}
    else if(strcmp(Prop, "pulse") == 0)
	{
	    chan->type = PULSE;
	    hasXpeak = hasWidth = hasDc = TRUE;
	}
    else if(strcmp(Prop, "delta") == 0)
	{
	    hasXpeak = hasDc = TRUE;
	    chan->type = DELTA;
	}
    else
	{
		printf(ERRTAG "[LINE %d] Unknown property %s for chan_width_distr x\n", Node->line,
		    Prop);
	    exit(1);
	}
    ezxml_set_attr(Node, "distr", NULL);
    chan->peak = GetFloatProperty(Node, "peak", TRUE, UNDEFINED);
	chan->width = GetFloatProperty(Node, "width", hasWidth, 0);
    chan->xpeak = GetFloatProperty(Node, "xpeak", hasXpeak, 0);
	chan->dc = GetFloatProperty(Node, "dc", hasDc, 0);
}

static void
SetupEmptyType()
{
    t_type_descriptor * type;
    type = &type_descriptors[EMPTY_TYPE->index];
    type->name = "<EMPTY>";
    type->num_pins = 0;
    type->height = 1;
    type->capacity = 0;
    type->num_drivers = 0;
    type->num_receivers = 0;
    type->pinloc = NULL;
    type->num_class = 0;
    type->class_inf = NULL;
    type->pin_class = NULL;
    type->is_global_pin = NULL;
    type->is_Fc_frac = TRUE;
    type->is_Fc_out_full_flex = FALSE;
    type->Fc_in = 0;
    type->Fc_out = 0;
	type->pb_type = NULL;
	type->area = UNDEFINED;
	
	/* Used as lost area filler, no definition */ 
	type->grid_loc_def = NULL;
    type->num_grid_loc_def = 0;
}


static void alloc_and_load_default_child_for_pb_type(INOUTP t_pb_type *pb_type, char *new_name, t_pb_type *copy) {
	int i, j;
	char *dot;
	
	assert(pb_type->blif_model != NULL);

	copy->name = my_strdup(new_name);
	copy->blif_model = my_strdup(pb_type->blif_model);
	copy->class_type = pb_type->class_type;
	copy->depth = pb_type->depth;
	copy->model = pb_type->model;
	copy->models_contained = NULL;
	copy->modes = NULL;
	copy->num_modes = 0;
	copy->num_clock_pins = pb_type->num_clock_pins;
	copy->num_input_pins = pb_type->num_input_pins;
	copy->num_output_pins = pb_type->num_output_pins;
	copy->num_pb = 1;
	copy->num_ports = pb_type->num_ports;
	copy->ports = my_calloc(pb_type->num_ports, sizeof(t_port));
	for(i = 0; i < pb_type->num_ports; i++) {
		copy->ports[i].is_clock = pb_type->ports[i].is_clock;
		copy->ports[i].model_port = pb_type->ports[i].model_port;
		copy->ports[i].type = pb_type->ports[i].type;
		copy->ports[i].num_pins = pb_type->ports[i].num_pins;
		copy->ports[i].parent_pb_type = copy;
		copy->ports[i].name = my_strdup(pb_type->ports[i].name);
		copy->ports[i].port_class = my_strdup(pb_type->ports[i].port_class);
	}

	copy->max_internal_delay = pb_type->max_internal_delay;
	copy->annotations = my_calloc(pb_type->num_annotations, sizeof(t_pin_to_pin_annotation));
	copy->num_annotations = pb_type->num_annotations;
	for(i = 0; i < copy->num_annotations; i++) {
		copy->annotations[i].clock = my_strdup(pb_type->annotations[i].clock);
		dot = strstr(pb_type->annotations[i].input_pins, ".");
		copy->annotations[i].input_pins = my_malloc(sizeof(char) * (strlen(new_name) + strlen(dot) + 1));
		copy->annotations[i].input_pins[0] = '\0';
		strcat(copy->annotations[i].input_pins, new_name);
		strcat(copy->annotations[i].input_pins, dot);
		if(pb_type->annotations[i].output_pins != NULL) {
		dot = strstr(pb_type->annotations[i].output_pins, ".");
			copy->annotations[i].output_pins = my_malloc(sizeof(char) * (strlen(new_name) + strlen(dot) + 1));
			copy->annotations[i].output_pins[0] = '\0';
			strcat(copy->annotations[i].output_pins, new_name);
			strcat(copy->annotations[i].output_pins, dot);
		} else {
			copy->annotations[i].output_pins = NULL;
		}
		copy->annotations[i].format = pb_type->annotations[i].format;
		copy->annotations[i].type = pb_type->annotations[i].type;
		copy->annotations[i].num_value_prop_pairs = pb_type->annotations[i].num_value_prop_pairs;
		copy->annotations[i].prop = my_malloc(sizeof(int) * pb_type->annotations[i].num_value_prop_pairs);
		copy->annotations[i].value = my_malloc(sizeof(char *) * pb_type->annotations[i].num_value_prop_pairs);
		for(j = 0; j < pb_type->annotations[i].num_value_prop_pairs; j++) {
			copy->annotations[i].prop[j] = pb_type->annotations[i].prop[j];
			copy->annotations[i].value[j] = my_strdup(pb_type->annotations[i].value[j]);
		}
	}
}

/* populate special lut class */
void ProcessLutClass(INOUTP t_pb_type *lut_pb_type) {
	char *default_name;
	t_port *in_port;
	t_port *out_port;
	int i, j;
	
	if(strcmp(lut_pb_type->name, "lut") != 0) {
		default_name = my_strdup("lut");
	} else {
		default_name = my_strdup("lut_child");
	}
	
	lut_pb_type->num_modes = 2;
	lut_pb_type->modes = my_calloc(lut_pb_type->num_modes, sizeof(t_mode));
	
	/* First mode, route_through */
	lut_pb_type->modes[0].name = my_strdup(lut_pb_type->name);
	lut_pb_type->modes[0].parent_pb_type = lut_pb_type;
	lut_pb_type->modes[0].num_pb_type_children = 0;
	
	/* Process interconnect */
	/* TODO: add timing annotations to route-through */
	assert(lut_pb_type->num_ports == 2);
	if(strcmp(lut_pb_type->ports[0].port_class, "lut_in") == 0) {
		assert(strcmp(lut_pb_type->ports[1].port_class, "lut_out") == 0);
		in_port = &lut_pb_type->ports[0];
		out_port = &lut_pb_type->ports[1];
	} else {
		assert(strcmp(lut_pb_type->ports[0].port_class, "lut_out") == 0);
		assert(strcmp(lut_pb_type->ports[1].port_class, "lut_in") == 0);
		out_port = &lut_pb_type->ports[0];
		in_port = &lut_pb_type->ports[1];
	}
	lut_pb_type->modes[0].num_interconnect = 1;
	lut_pb_type->modes[0].interconnect = my_calloc(1, sizeof(t_interconnect));
	lut_pb_type->modes[0].interconnect[0].name = my_calloc(
														strlen(lut_pb_type->name) + 10,
														sizeof(char));
	sprintf(lut_pb_type->modes[0].interconnect[0].name, "complete:%s", lut_pb_type->name);
	lut_pb_type->modes[0].interconnect[0].type = COMPLETE_INTERC;
	lut_pb_type->modes[0].interconnect[0].input_string = my_calloc(
														strlen(lut_pb_type->name) + 
														strlen(in_port->name) + 2,
														sizeof(char));
	sprintf(lut_pb_type->modes[0].interconnect[0].input_string, "%s.%s", 
			lut_pb_type->name, in_port->name);
	lut_pb_type->modes[0].interconnect[0].output_string = my_calloc(
														strlen(lut_pb_type->name) + 
														strlen(out_port->name) + 2,
														sizeof(char));
	sprintf(lut_pb_type->modes[0].interconnect[0].output_string, "%s.%s", 
			lut_pb_type->name, out_port->name);

	/* Second mode, LUT */
	
	lut_pb_type->modes[1].name = my_strdup(lut_pb_type->name);
	lut_pb_type->modes[1].parent_pb_type = lut_pb_type;
	lut_pb_type->modes[1].num_pb_type_children = 1;
	lut_pb_type->modes[1].pb_type_children = my_calloc(1, sizeof(t_pb_type));
	alloc_and_load_default_child_for_pb_type(lut_pb_type, default_name, lut_pb_type->modes[1].pb_type_children);
	/* moved annotations to child so delete old annotations */
	for(i = 0; i < lut_pb_type->num_annotations; i++) {
		for(j = 0; j < lut_pb_type->annotations[i].num_value_prop_pairs; j++) {
			free(lut_pb_type->annotations[i].value[j]);
		}
		free(lut_pb_type->annotations[i].value);
		free(lut_pb_type->annotations[i].prop);
		if(lut_pb_type->annotations[i].input_pins) {
			free(lut_pb_type->annotations[i].input_pins);
		}
		if(lut_pb_type->annotations[i].output_pins) {
			free(lut_pb_type->annotations[i].output_pins);
		}
		if(lut_pb_type->annotations[i].clock) {
			free(lut_pb_type->annotations[i].clock);
		}
	}
	lut_pb_type->num_annotations = 0;
	free(lut_pb_type->annotations);
	lut_pb_type->annotations = NULL;
	lut_pb_type->modes[1].pb_type_children[0].depth = lut_pb_type->depth + 1;
	lut_pb_type->modes[1].pb_type_children[0].parent_mode = &lut_pb_type->modes[1];

	/* Process interconnect */
	lut_pb_type->modes[1].num_interconnect = 2;
	lut_pb_type->modes[1].interconnect = my_calloc(2, sizeof(t_interconnect));
	lut_pb_type->modes[1].interconnect[0].name = my_calloc(
														strlen(lut_pb_type->name) + 10,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[0].name, "complete:%s", lut_pb_type->name);
	lut_pb_type->modes[1].interconnect[0].type = COMPLETE_INTERC;
	lut_pb_type->modes[1].interconnect[0].input_string = my_calloc(
														strlen(lut_pb_type->name) + 
														strlen(in_port->name) + 2,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[0].input_string, "%s.%s", 
			lut_pb_type->name, in_port->name);
	lut_pb_type->modes[1].interconnect[0].output_string = my_calloc(
														strlen(default_name) + 
														strlen(in_port->name) + 2,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[0].output_string, "%s.%s", 
			default_name, in_port->name);

	lut_pb_type->modes[1].interconnect[1].name = my_calloc(
														strlen(lut_pb_type->name) + 11,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[1].name, "complete2:%s", lut_pb_type->name);
	
	lut_pb_type->modes[1].interconnect[1].type = COMPLETE_INTERC;
	lut_pb_type->modes[1].interconnect[1].input_string = my_calloc(
														strlen(default_name) + 
														strlen(lut_pb_type->name) +
														strlen(in_port->name) +
														strlen(out_port->name) + 4,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[1].input_string, "%s.%s %s.%s", default_name, out_port->name,
		lut_pb_type->name, in_port->name);
	lut_pb_type->modes[1].interconnect[1].output_string = my_calloc(
														strlen(lut_pb_type->name) + 
														strlen(out_port->name) + 
														strlen(in_port->name) + 2,
														sizeof(char));
	sprintf(lut_pb_type->modes[1].interconnect[1].output_string, "%s.%s", 
			lut_pb_type->name, out_port->name);

	free(default_name);

	free(lut_pb_type->blif_model);
	lut_pb_type->blif_model = NULL;
	lut_pb_type->model = NULL;
}

/* populate special memory class */ 
static void ProcessMemoryClass(INOUTP t_pb_type *mem_pb_type) {
	char *default_name;
	char *input_name, *input_port_name, *output_name, *output_port_name;
	int i, j, i_inter, num_pb;

	
	if(strcmp(mem_pb_type->name, "memory_slice") != 0) {
		default_name = my_strdup("memory_slice");
	} else {
		default_name = my_strdup("memory_slice_1bit");
	}
	
	mem_pb_type->modes = my_calloc(1, sizeof(t_mode));
	mem_pb_type->modes[0].name = my_strdup(default_name);
	mem_pb_type->modes[0].parent_pb_type = mem_pb_type;

	num_pb = OPEN;
	for(i = 0; i < mem_pb_type->num_ports; i++) {
		if(mem_pb_type->ports[i].port_class != NULL && 
		   strstr(mem_pb_type->ports[i].port_class, "data") == mem_pb_type->ports[i].port_class) {
			if(num_pb == OPEN) {
				num_pb = mem_pb_type->ports[i].num_pins;
			} else if (num_pb != mem_pb_type->ports[i].num_pins) {
				printf(ERRTAG "memory %s has inconsistent number of data bits %d and %d\n", mem_pb_type->name,
					num_pb, mem_pb_type->ports[i].num_pins);
				exit(1);
			}
		}
	}

	mem_pb_type->modes[0].num_pb_type_children = 1;
	mem_pb_type->modes[0].pb_type_children = my_calloc(1, sizeof(t_pb_type));
	alloc_and_load_default_child_for_pb_type(mem_pb_type, default_name, &mem_pb_type->modes[0].pb_type_children[0]);
	mem_pb_type->modes[0].pb_type_children[0].depth = mem_pb_type->depth + 1;
	mem_pb_type->modes[0].pb_type_children[0].parent_mode = &mem_pb_type->modes[0];
	mem_pb_type->modes[0].pb_type_children[0].num_pb = num_pb;
	
	mem_pb_type->num_modes = 1;
		
	free(mem_pb_type->blif_model);
	mem_pb_type->blif_model = NULL;
	mem_pb_type->model = NULL;

	mem_pb_type->modes[0].num_interconnect = mem_pb_type->num_ports * num_pb;
	mem_pb_type->modes[0].interconnect = my_calloc(mem_pb_type->modes[0].num_interconnect, sizeof(t_interconnect));

	/* Process interconnect */
	i_inter = 0;
	for(i = 0; i < mem_pb_type->num_ports; i++) {
		mem_pb_type->modes[0].interconnect[i_inter].type = DIRECT_INTERC;
		input_port_name = mem_pb_type->ports[i].name;
		output_port_name = mem_pb_type->ports[i].name;
	
		if(mem_pb_type->ports[i].type == IN_PORT) {
			input_name = mem_pb_type->name;
			output_name = default_name;
		} else {
			input_name = default_name;
			output_name = mem_pb_type->name;
		}

		if(mem_pb_type->ports[i].port_class != NULL && 
		   strstr(mem_pb_type->ports[i].port_class, "data") == mem_pb_type->ports[i].port_class) {

   			mem_pb_type->modes[0].interconnect[i_inter].name = my_calloc(i_inter/10 + 8, sizeof(char));
			sprintf(mem_pb_type->modes[0].interconnect[i_inter].name, "direct%d", i_inter);

	   
		   if(mem_pb_type->ports[i].type == IN_PORT) {
				/* force data pins to be one bit wide and update stats */
				mem_pb_type->modes[0].pb_type_children[0].ports[i].num_pins = 1; 
				mem_pb_type->modes[0].pb_type_children[0].num_input_pins -= (mem_pb_type->ports[i].num_pins - 1);


			   	mem_pb_type->modes[0].interconnect[i_inter].input_string = my_calloc(
																	strlen(input_name) + 
																	strlen(input_port_name) + 2,
																	sizeof(char));
				sprintf(mem_pb_type->modes[0].interconnect[i_inter].input_string, "%s.%s", 
						input_name, input_port_name);
				mem_pb_type->modes[0].interconnect[i_inter].output_string = my_calloc(
																	strlen(output_name) + 
																	strlen(output_port_name) + 2*(6 + num_pb/10),
																	sizeof(char));
				sprintf(mem_pb_type->modes[0].interconnect[i_inter].output_string, "%s[%d:0].%s", 
						output_name, num_pb - 1, output_port_name);
		   } else {
			    /* force data pins to be one bit wide and update stats */
				mem_pb_type->modes[0].pb_type_children[0].ports[i].num_pins = 1; 
				mem_pb_type->modes[0].pb_type_children[0].num_output_pins -= (mem_pb_type->ports[i].num_pins - 1);

			   	mem_pb_type->modes[0].interconnect[i_inter].input_string = my_calloc(
																	strlen(input_name) + 
																	strlen(input_port_name) + 2*(6 + num_pb/10),
																	sizeof(char));
				sprintf(mem_pb_type->modes[0].interconnect[i_inter].input_string, "%s[%d:0].%s", 
						input_name, num_pb - 1, input_port_name);
				mem_pb_type->modes[0].interconnect[i_inter].output_string = my_calloc(
																	strlen(output_name) + 
																	strlen(output_port_name) + 2,
																	sizeof(char));
				sprintf(mem_pb_type->modes[0].interconnect[i_inter].output_string, "%s.%s", 
						output_name, output_port_name);
		   }

		   i_inter++;
		} else {
			for(j = 0; j < num_pb; j++) {			
				/* Anything that is not data must be an input */
				mem_pb_type->modes[0].interconnect[i_inter].name = my_calloc(i_inter/10 + j/10 + 10, sizeof(char));
				sprintf(mem_pb_type->modes[0].interconnect[i_inter].name, "direct%d_%d", i_inter, j);

				if(mem_pb_type->ports[i].type == IN_PORT) {
					mem_pb_type->modes[0].interconnect[i_inter].type = DIRECT_INTERC;
					mem_pb_type->modes[0].interconnect[i_inter].input_string = my_calloc(
																		strlen(input_name) + 
																		strlen(input_port_name) + 2,
																		sizeof(char));
					sprintf(mem_pb_type->modes[0].interconnect[i_inter].input_string, "%s.%s", 
							input_name, input_port_name);
					mem_pb_type->modes[0].interconnect[i_inter].output_string = my_calloc(
																		strlen(output_name) + 
																		strlen(output_port_name) + 2*(6 + num_pb/10),
																		sizeof(char));
					sprintf(mem_pb_type->modes[0].interconnect[i_inter].output_string, "%s[%d:%d].%s", 
							output_name, j, j, output_port_name);
				} else {
					mem_pb_type->modes[0].interconnect[i_inter].type = DIRECT_INTERC;
					mem_pb_type->modes[0].interconnect[i_inter].input_string = my_calloc(
																		strlen(input_name) + 
																		strlen(input_port_name) + 2*(6 + num_pb/10),
																		sizeof(char));
					sprintf(mem_pb_type->modes[0].interconnect[i_inter].input_string, "%s[%d:%d].%s", 
							input_name, j, j, input_port_name);
					mem_pb_type->modes[0].interconnect[i_inter].output_string = my_calloc(
																		strlen(output_name) + 
																		strlen(output_port_name) + 2,
																		sizeof(char));
					sprintf(mem_pb_type->modes[0].interconnect[i_inter].output_string, "%s.%s", 
							output_name, output_port_name);					

				}
				i_inter++;
			}
		}
	}
	mem_pb_type->modes[0].num_interconnect = i_inter;

	free(default_name);
}

/* Takes in node pointing to <typelist> and loads all the
 * child type objects. Unlinks the entire <typelist> node
 * when complete. */ 
static void
ProcessComplexBlocks(INOUTP ezxml_t Node, OUTP t_type_descriptor ** Types,
	OUTP int *NumTypes, boolean timing_enabled)
{
    ezxml_t CurType, Prev;
    ezxml_t Cur, Cur2;
    t_type_descriptor * Type;
	int i;

    
	/* Alloc the type list. Need one additional t_type_desctiptors:
	 * 1: empty psuedo-type 
	 */ 
	*NumTypes = CountChildren(Node, "pb_type", 1) + 1;
    *Types = (t_type_descriptor *) 
	my_malloc(sizeof(t_type_descriptor) * (*NumTypes));

	type_descriptors = *Types;

    EMPTY_TYPE = &type_descriptors[EMPTY_TYPE_INDEX];
    IO_TYPE = &type_descriptors[IO_TYPE_INDEX];
    type_descriptors[EMPTY_TYPE_INDEX].index = EMPTY_TYPE_INDEX;
    type_descriptors[IO_TYPE_INDEX].index = IO_TYPE_INDEX;
    SetupEmptyType();
    
	/* Process the types */ 
	/* TODO: I should make this more flexible but release is soon and I don't have time so assert values for empty and io types*/
	assert(EMPTY_TYPE_INDEX == 0);
	assert(IO_TYPE_INDEX == 1);
	i = 1;			/* Skip over 'empty' type */
    CurType = Node->child;
    while(CurType)
	{
	    CheckElement(CurType, "pb_type");
	    
		/* Alias to current type */ 
		Type = &(*Types)[i];
	    
		/* Parses the properties fields of the type */ 
		ProcessComplexBlockProps(CurType, Type);
		
		/* Load pb_type info */
		Type->pb_type = my_malloc(sizeof(t_pb_type));
	    Type->pb_type->name = my_strdup(Type->name);
		if(i == IO_TYPE_INDEX) {
			if(strcmp(Type->name, "io") != 0) {
				printf("First complex block must be named \"io\" and define the inputs and outputs for the FPGA");
				exit(1);
			}
		}
		ProcessPb_Type(CurType, Type->pb_type, NULL);
		Type->num_pins = Type->capacity * (Type->pb_type->num_input_pins + Type->pb_type->num_output_pins + Type->pb_type->num_clock_pins);
		Type->num_receivers = Type->capacity * Type->pb_type->num_input_pins;
		Type->num_drivers = Type->capacity * Type->pb_type->num_output_pins;
		
		/* Load Fc */ 
		Cur = FindElement(CurType, "fc_in", TRUE);
	    Cur2 = FindElement(CurType, "fc_out", TRUE);
	    Process_Fc(Cur, Cur2, Type);
	    FreeNode(Cur);
	    FreeNode(Cur2);
	    
		/* Load pin names and classes and locations */ 
	    Cur = FindElement(CurType, "pinlocations", TRUE);
	    SetupPinLocationsAndPinClasses(Cur, Type);
	    FreeNode(Cur);
	    Cur = FindElement(CurType, "gridlocations", TRUE);
	    SetupGridLocations(Cur, Type);
	    FreeNode(Cur);
#if 0
	    Cur = FindElement(CurType, "timing", timing_enabled);
	    if(Cur)
		{
		    SetupTypeTiming(Cur, Type);
		    FreeNode(Cur);
		}
#endif   
	    Type->index = i;

		/* Type fully read */ 
		++i;
	    
		/* Free this node and get its next sibling node */ 
		Prev = CurType;
	    CurType = CurType->next;
	    FreeNode(Prev);

	}
    if(FILL_TYPE == NULL)
	{
	    printf(ERRTAG "grid location type 'fill' must be specified.\n");
	    exit(1);
	}
}

/* Loads the given architecture file. Currently only 
 * handles type information */ 
void
XmlReadArch(INP const char *ArchFile, INP boolean timing_enabled,
	OUTP struct s_arch *arch, OUTP t_type_descriptor ** Types,
	OUTP int *NumTypes)
{
    ezxml_t Cur, Next;
	const char *Prop;
    
	/* Parse the file */ 
	Cur = ezxml_parse_file(ArchFile);
    if(NULL == Cur)
	{
	    printf(ERRTAG "Unable to load architecture file '%s'.\n",
		    ArchFile);
		exit(1);
	}
    
	/* Root node should be architecture */ 
	CheckElement(Cur, "architecture");
	/* TODO: do version processing properly with string delimiting on the . */
	Prop = FindProperty(Cur, "version", FALSE);
    if(Prop != NULL)
	{
		if (atof(Prop) > atof(VPR_VERSION)) {
			printf(WARNTAG "This architecture version is for VPR %f while your current VPR version is " VPR_VERSION ", compatability issues may arise\n",
				atof(Prop));
		}
	    ezxml_set_attr(Cur, "version", NULL);
	}
    
	/* Process models */ 
	Next = FindElement(Cur, "models", TRUE);
	ProcessModels(Next, arch);
	FreeNode(Next);
	CreateModelLibrary(arch);
    
	/* Process layout */ 
	Next = FindElement(Cur, "layout", TRUE);
    ProcessLayout(Next, arch);
    FreeNode(Next);
    
	/* Process device */ 
	Next = FindElement(Cur, "device", TRUE);
    ProcessDevice(Next, arch, timing_enabled);
    FreeNode(Next);
    
	/* Process types */ 
	Next = FindElement(Cur, "complexblocklist", TRUE);
    ProcessComplexBlocks(Next, Types, NumTypes, timing_enabled);
    FreeNode(Next);
    
	/* Process switches */ 
	Next = FindElement(Cur, "switchlist", TRUE);
    ProcessSwitches(Next, &(arch->Switches), &(arch->num_switches),
		     timing_enabled);
    FreeNode(Next);
    
	/* Process segments. This depends on switches */ 
	Next = FindElement(Cur, "segmentlist", TRUE);
    ProcessSegments(Next, &(arch->Segments), &(arch->num_segments),
		     arch->Switches, arch->num_switches, timing_enabled);
    FreeNode(Next);

	SyncModelsPbTypes(arch, *Types, *NumTypes);
	UpdateAndCheckModels(arch);
    
	/* Release the full XML tree */ 
	FreeNode(Cur);
}

static void
ProcessSegments(INOUTP ezxml_t Parent, OUTP struct s_segment_inf **Segs,
	OUTP int *NumSegs, INP struct s_switch_inf *Switches,
	INP int NumSwitches, INP boolean timing_enabled)
{
    int i, j, length;
    const char *tmp;

    ezxml_t SubElem;
    ezxml_t Node;
    
	/* Count the number of segs and check they are in fact
	 * of segment elements. */ 
	*NumSegs = CountChildren(Parent, "segment", 1);
    
	/* Alloc segment list */ 
	*Segs = NULL;
    if(*NumSegs > 0)
	{
	    *Segs =
		(struct s_segment_inf *)my_malloc(*NumSegs *
						   sizeof(struct
							  s_segment_inf));
	    memset(*Segs, 0, (*NumSegs * sizeof(struct s_segment_inf)));
	}
    
	/* Load the segments. */ 
	for(i = 0; i < *NumSegs; ++i)
	{
	    Node = ezxml_child(Parent, "segment");
	    
		/* Get segment length */ 
		length = 1;	/* DEFAULT */
	    tmp = FindProperty(Node, "length", FALSE);
	    if(tmp)
		{
		    if(strcmp(tmp, "longline") == 0)
			{
			    (*Segs)[i].longline = TRUE;
			}
		    else
			{
			    length = my_atoi(tmp);
			}
		}
	    (*Segs)[i].length = length;
	    ezxml_set_attr(Node, "length", NULL);
	    
		/* Get the frequency */ 
		(*Segs)[i].frequency = 1;	/* DEFAULT */
	    tmp = FindProperty(Node, "freq", FALSE);
	    if(tmp)
		{
		    (*Segs)[i].frequency = (int) (atof(tmp) * MAX_CHANNEL_WIDTH);
		}
	    ezxml_set_attr(Node, "freq", NULL);
	    
		/* Get timing info */ 
		(*Segs)[i].Rmetal = GetFloatProperty(Node, "Rmetal", timing_enabled, 0);
	    (*Segs)[i].Cmetal = GetFloatProperty(Node, "Cmetal", timing_enabled, 0);
	    
		/* Get the type */ 
		tmp = FindProperty(Node, "type", TRUE);
	    if(0 == strcmp(tmp, "bidir"))
		{
		    (*Segs)[i].directionality = BI_DIRECTIONAL;
		}
	    
	    else if(0 == strcmp(tmp, "unidir"))
		{
		    (*Segs)[i].directionality = UNI_DIRECTIONAL;
		}
	    
	    else
		{
			printf(ERRTAG "[LINE %d] Invalid switch type '%s'.\n", Node->line, tmp);
		    exit(1);
		}
	    ezxml_set_attr(Node, "type", NULL);
	    
		/* Get the wire and opin switches, or mux switch if unidir */ 
		if(UNI_DIRECTIONAL == (*Segs)[i].directionality)
		{
		    SubElem = FindElement(Node, "mux", TRUE);
		    tmp = FindProperty(SubElem, "name", TRUE);
		    
			/* Match names */ 
			for(j = 0; j < NumSwitches; ++j)
			{
			    if(0 == strcmp(tmp, Switches[j].name))
				{
				    break;	/* End loop so j is where we want it */
				}
			}
		    if(j >= NumSwitches)
			{
				printf(ERRTAG "[LINE %d] '%s' is not a valid mux name.\n", SubElem->line,
				    tmp);
			    exit(1);
			}
		    ezxml_set_attr(SubElem, "name", NULL);
		    FreeNode(SubElem);
		    
			/* Unidir muxes must have the same switch
			 * for wire and opin fanin since there is 
			 * really only the mux in unidir. */ 
			(*Segs)[i].wire_switch = j;
		    (*Segs)[i].opin_switch = j;
		}
	    
	    else
		{
		    assert(BI_DIRECTIONAL == (*Segs)[i].directionality);
		    SubElem = FindElement(Node, "wire_switch", TRUE);
		    tmp = FindProperty(SubElem, "name", TRUE);
		    
			/* Match names */ 
			for(j = 0; j < NumSwitches; ++j)
			{
			    if(0 == strcmp(tmp, Switches[j].name))
				{
				    break;	/* End loop so j is where we want it */
				}
			}
		    if(j >= NumSwitches)
			{
			    printf(ERRTAG
					"[LINE %d] '%s' is not a valid wire_switch name.\n", SubElem->line,
				    tmp);
			    exit(1);
			}
		    (*Segs)[i].wire_switch = j;
		    ezxml_set_attr(SubElem, "name", NULL);
		    FreeNode(SubElem);
		    SubElem = FindElement(Node, "opin_switch", TRUE);
		    tmp = FindProperty(SubElem, "name", TRUE);
		    
			/* Match names */ 
			for(j = 0; j < NumSwitches; ++j)
			{
			    if(0 == strcmp(tmp, Switches[j].name))
				{
				    break;	/* End loop so j is where we want it */
				}
			}
		    if(j >= NumSwitches)
			{
			    printf(ERRTAG
					"[LINE %d] '%s' is not a valid opin_switch name.\n", SubElem->line, 
				    tmp);
			    exit(1);
			}
		    (*Segs)[i].opin_switch = j;
		    ezxml_set_attr(SubElem, "name", NULL);
		    FreeNode(SubElem);
		}
	    
		/* Setup the CB list if they give one, otherwise use full */ 
		(*Segs)[i].cb_len = length;
	    (*Segs)[i].cb = (boolean *) my_malloc(length * sizeof(boolean));
	    for(j = 0; j < length; ++j)
		{
		    (*Segs)[i].cb[j] = TRUE;
		}
	    SubElem = FindElement(Node, "cb", FALSE);
	    if(SubElem)
		{
		    ProcessCB_SB(SubElem, (*Segs)[i].cb, length);
		    FreeNode(SubElem);
		}
	    
		/* Setup the SB list if they give one, otherwise use full */ 
		(*Segs)[i].sb_len = (length + 1);
	    (*Segs)[i].sb =
		(boolean *) my_malloc((length + 1) * sizeof(boolean));
	    for(j = 0; j < (length + 1); ++j)
		{
		    (*Segs)[i].sb[j] = TRUE;
		}
	    SubElem = FindElement(Node, "sb", FALSE);
	    if(SubElem)
		{
		    ProcessCB_SB(SubElem, (*Segs)[i].sb, (length + 1));
		    FreeNode(SubElem);
		}
	    FreeNode(Node);
	}
}

static void
ProcessCB_SB(INOUTP ezxml_t Node, INOUTP boolean * list, INP int len)
{
    const char *tmp = NULL;
    int i;
    
	/* Check the type. We only support 'pattern' for now. 
	 * Should add frac back eventually. */ 
	tmp = FindProperty(Node, "type", TRUE);
    if(0 == strcmp(tmp, "pattern"))
	{
	    i = 0;
	    
		/* Get the content string */ 
		tmp = Node->txt;
	    while(*tmp)
		{
		    switch (*tmp)
			{
			case ' ':
			    break;
			case 'T':
			case '1':
			    if(i >= len)
				{
				    printf(ERRTAG
					    "[LINE %d] CB or SB depopulation is too long. It "
					    
					    "should be (length) symbols for CBs and (length+1) "
					     "symbols for SBs.\n", Node->line);
				    exit(1);
				}
			    list[i] = TRUE;
			    ++i;
			    break;
			case 'F':
			case '0':
			    if(i >= len)
				{
				    printf(ERRTAG
					    "[LINE %d] CB or SB depopulation is too long. It "
					    
					    "should be (length) symbols for CBs and (length+1) "
						"symbols for SBs.\n", Node->line);
				    exit(1);
				}
			    list[i] = FALSE;
			    ++i;
			    break;
			default:
			    printf(ERRTAG "[LINE %d] Invalid character %c in CB or " 
				    "SB depopulation list.\n", Node->line,
					*tmp);
			    exit(1);
			}
		    ++tmp;
		}
	    if(i < len)
		{
		    printf(ERRTAG "[LINE %d] CB or SB depopulation is too short. It " 
			    "should be (length) symbols for CBs and (length+1) "
			     "symbols for SBs.\n", Node->line);
		    exit(1);
		}
	    
		/* Free content string */ 
		ezxml_set_txt(Node, "");
	}
    
    else
	{
	    printf(ERRTAG "[LINE %d] '%s' is not a valid type for specifying " 
			"cb and sb depopulation.\n", Node->line, tmp);
	    exit(1);
	}
    ezxml_set_attr(Node, "type", NULL);
}

static void
ProcessSwitches(INOUTP ezxml_t Parent, OUTP struct s_switch_inf **Switches,
	OUTP int *NumSwitches, INP boolean timing_enabled)
{
    int i, j;
    const char *type_name;
    const char *switch_name;
    
    boolean has_buf_size;
    ezxml_t Node;
    has_buf_size = FALSE;
    
	/* Count the children and check they are switches */ 
	*NumSwitches = CountChildren(Parent, "switch", 1);
    
	/* Alloc switch list */ 
	*Switches = NULL;
    if(*NumSwitches > 0)
	{
	    *Switches =
		(struct s_switch_inf *)my_malloc(*NumSwitches *
						  sizeof(struct
							 s_switch_inf));
	    memset(*Switches, 0,
		    (*NumSwitches * sizeof(struct s_switch_inf)));
	}
    
	/* Load the switches. */ 
	for(i = 0; i < *NumSwitches; ++i)
	{
	    Node = ezxml_child(Parent, "switch");
	    switch_name = FindProperty(Node, "name", TRUE);
	    type_name = FindProperty(Node, "type", TRUE);
	    
		/* Check for switch name collisions */ 
		for(j = 0; j < i; ++j)
		{
		    if(0 == strcmp((*Switches)[j].name, switch_name))
			{
			    printf(ERRTAG
				    "[LINE %d] Two switches with the same name '%s' were "
					"found.\n", Node->line, switch_name);
			    exit(1);
			}
		}
	    (*Switches)[i].name = my_strdup(switch_name);
	    ezxml_set_attr(Node, "name", NULL);
	    
		/* Figure out the type of switch. */ 
		if(0 == strcmp(type_name, "mux"))
		{
		    (*Switches)[i].buffered = TRUE;
		    has_buf_size = TRUE;
		}
	    
	    else if(0 == strcmp(type_name, "pass_trans"))
		{
		    (*Switches)[i].buffered = FALSE;
		}
	    
	    else if(0 == strcmp(type_name, "buffer"))
		{
		    (*Switches)[i].buffered = TRUE;
		}
	    
	    else
		{
			printf(ERRTAG "[LINE %d] Invalid switch type '%s'.\n", Node->line, type_name);
		    exit(1);
		}
	    ezxml_set_attr(Node, "type", NULL);
		(*Switches)[i].R = GetFloatProperty(Node, "R", timing_enabled, 0);
	    (*Switches)[i].Cin = GetFloatProperty(Node, "Cin", timing_enabled, 0);
	    (*Switches)[i].Cout = GetFloatProperty(Node, "Cout", timing_enabled, 0);
	    (*Switches)[i].Tdel = GetFloatProperty(Node, "Tdel", timing_enabled, 0);
	    (*Switches)[i].buf_size = GetFloatProperty(Node, "buf_size", has_buf_size, 0);
	    (*Switches)[i].mux_trans_size = GetFloatProperty(Node, "mux_trans_size", FALSE, 1);

		/* Remove the switch element from parse tree */ 
		FreeNode(Node);
	}
}


static void CreateModelLibrary(OUTP struct s_arch *arch) {
	t_model* model_library;

	model_library = my_calloc(4, sizeof(t_model));
	model_library[0].name = my_strdup("input");
	model_library[0].index = 0;
	model_library[0].inputs = NULL;
	model_library[0].instances = NULL;
	model_library[0].next = &model_library[1];
	model_library[0].outputs = my_calloc(1, sizeof(t_model_ports));
	model_library[0].outputs->dir = OUT_PORT;
	model_library[0].outputs->name = my_strdup("inpad");
	model_library[0].outputs->next = NULL;
	model_library[0].outputs->size = 1;
	model_library[0].outputs->min_size = 1;
	model_library[0].outputs->index = 0;
	model_library[0].outputs->is_clock = FALSE;

	model_library[1].name = my_strdup("output");
	model_library[1].index = 1;
	model_library[1].inputs = my_calloc(1, sizeof(t_model_ports));
	model_library[1].inputs->dir = IN_PORT;
	model_library[1].inputs->name = my_strdup("outpad");
	model_library[1].inputs->next = NULL;
	model_library[1].inputs->size = 1;
	model_library[1].inputs->min_size = 1;
	model_library[1].inputs->index = 0;
	model_library[1].inputs->is_clock = FALSE;
	model_library[1].instances = NULL;
	model_library[1].next = &model_library[2];
	model_library[1].outputs = NULL;

	model_library[2].name = my_strdup("latch");
	model_library[2].index = 2;
	model_library[2].inputs = my_calloc(2, sizeof(t_model_ports));
	model_library[2].inputs[0].dir = IN_PORT;
	model_library[2].inputs[0].name = my_strdup("D");
	model_library[2].inputs[0].next = &model_library[2].inputs[1];
	model_library[2].inputs[0].size = 1;
	model_library[2].inputs[0].min_size = 1;
	model_library[2].inputs[0].index = 0;
	model_library[2].inputs[0].is_clock = FALSE;	
	model_library[2].inputs[1].dir = IN_PORT;
	model_library[2].inputs[1].name = my_strdup("clk");
	model_library[2].inputs[1].next = NULL;
	model_library[2].inputs[1].size = 1;
	model_library[2].inputs[1].min_size = 1;
	model_library[2].inputs[1].index = 0;
	model_library[2].inputs[1].is_clock = TRUE;	
	model_library[2].instances = NULL;
	model_library[2].next = &model_library[3];
	model_library[2].outputs = my_calloc(1, sizeof(t_model_ports));
	model_library[2].outputs->dir = OUT_PORT;
	model_library[2].outputs->name = my_strdup("Q");
	model_library[2].outputs->next = NULL;
	model_library[2].outputs->size = 1;
	model_library[2].outputs->min_size = 1;
	model_library[2].outputs->index = 0;
	model_library[2].outputs->is_clock = FALSE;

	model_library[3].name = my_strdup("names");
	model_library[3].index = 3;
	model_library[3].inputs = my_calloc(1, sizeof(t_model_ports));
	model_library[3].inputs->dir = IN_PORT;
	model_library[3].inputs->name = my_strdup("in");
	model_library[3].inputs->next = NULL;
	model_library[3].inputs->size = 1;
	model_library[3].inputs->min_size = 1;
	model_library[3].inputs->index = 0;
	model_library[3].inputs->is_clock = FALSE;
	model_library[3].instances = NULL;
	model_library[3].next = NULL;
	model_library[3].outputs = my_calloc(1, sizeof(t_model_ports));
	model_library[3].outputs->dir = OUT_PORT;
	model_library[3].outputs->name = my_strdup("out");
	model_library[3].outputs->next = NULL;
	model_library[3].outputs->size = 1;
	model_library[3].outputs->min_size = 1;
	model_library[3].outputs->index = 0;
	model_library[3].outputs->is_clock = FALSE;

	arch->model_library = model_library;
}

static void SyncModelsPbTypes(INOUTP struct s_arch *arch, INP t_type_descriptor * Types, INP int NumTypes) {
	int i;
	for(i = 0; i < NumTypes; i++) {
		if(Types[i].pb_type != NULL) {
			SyncModelsPbTypes_rec(arch, Types[i].pb_type);
		}
	}
	for(i = 0; i < NumTypes; i++) {
		if(Types[i].pb_type != NULL) {
			AddModelsToPbTypes_rec(Types[i].pb_type);
		}
	}
}


static void SyncModelsPbTypes_rec(INOUTP struct s_arch *arch, INOUTP t_pb_type * pb_type) {
	int i, j, p;
	t_model *model_match_prim, *cur_model;
	t_model_ports *model_port;
	struct s_linked_vptr *old;
	char* blif_model_name;
	
	boolean found;

	if(pb_type->blif_model != NULL) {		
		
		/* get actual name of subckt */
		if(strstr(pb_type->blif_model, ".subckt ") == pb_type->blif_model) {
			blif_model_name = strchr(pb_type->blif_model, ' ');
		} else {
			blif_model_name = strchr(pb_type->blif_model, '.');
		}
		if(blif_model_name) {
			blif_model_name++; /* get character after the '.' or ' ' */
		} else {
			printf("Unknown blif model %s in pb_type %s\n", pb_type->blif_model, pb_type->name);
		}

		/* There are two sets of models to consider, the standard library of models and the user defined models */
		if(	(strcmp(blif_model_name, "input") == 0)  ||
			(strcmp(blif_model_name, "output") == 0) ||
			(strcmp(blif_model_name, "names") == 0)  ||
			(strcmp(blif_model_name, "latch") == 0) ) {
			cur_model = arch->model_library;
		} else {
			cur_model = arch->models;
		}

		/* Determine the logical model to use */
		found = FALSE;	
		model_match_prim = NULL;
		while(cur_model && !found) {
			/* blif model always starts with .subckt so need to skip first 8 characters */
			if(strcmp(blif_model_name, cur_model->name) == 0) {
				found = TRUE;
				model_match_prim = cur_model;
			}
			cur_model = cur_model->next;
		}
		if(found != TRUE) {
			printf(ERRTAG "No matching model for pb_type %s\n", pb_type->blif_model);
			exit(1);
		}


		pb_type->model = model_match_prim;
		old = model_match_prim->pb_types;
		model_match_prim->pb_types = (struct s_linked_vptr*) my_malloc(sizeof(struct s_linked_vptr));
		model_match_prim->pb_types->next = old;
		model_match_prim->pb_types->data_vptr = pb_type;

		for(p = 0; p < pb_type->num_ports; p++) {
			found = FALSE;
			/* TODO: Parse error checking - check if INPUT matches INPUT and OUTPUT matches OUTPUT (not yet done) */
			model_port = model_match_prim->inputs;
			while(model_port && !found) {
				if(strcmp(model_port->name, pb_type->ports[p].name) == 0) {
					if(model_port->size < pb_type->ports[p].num_pins) {
						model_port->size = pb_type->ports[p].num_pins;
					}
					if(model_port->min_size > pb_type->ports[p].num_pins || model_port->min_size == -1) {
						model_port->min_size = pb_type->ports[p].num_pins;
					}
					pb_type->ports[p].model_port = model_port;
					assert(pb_type->ports[p].type == model_port->dir);
					assert(pb_type->ports[p].is_clock == model_port->is_clock);
					found = TRUE;
				}
				model_port = model_port->next;
			}
			model_port = model_match_prim->outputs;
			while(model_port && !found) {
				if(strcmp(model_port->name, pb_type->ports[p].name) == 0) {
					if(model_port->size < pb_type->ports[p].num_pins) {
						model_port->size = pb_type->ports[p].num_pins;						
					}
					if(model_port->min_size > pb_type->ports[p].num_pins || model_port->min_size == -1) {
						model_port->min_size = pb_type->ports[p].num_pins;
					}
					pb_type->ports[p].model_port = model_port;
					assert(pb_type->ports[p].type == model_port->dir);
					found = TRUE;
				}
				model_port = model_port->next;
			}
			if(found != TRUE) {
				printf(ERRTAG "No matching model port for port %s in pb_type %s\n", pb_type->ports[p].name, pb_type->name);
				exit(1);
			}
		}
	} else {
		for(i = 0; i < pb_type->num_modes; i++) {
			for(j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
				SyncModelsPbTypes_rec(arch, &(pb_type->modes[i].pb_type_children[j]));
			}
		}
	}
}

static void AddModelsToPbTypes_rec(INOUTP t_pb_type *pb_type) {
	int i, j;
	struct s_linked_vptr *child, *curr;

	/* Determine all logical models contained by pb_type */
	if(pb_type->num_modes == 0) {
		pb_type->models_contained = my_malloc(sizeof(struct s_linked_vptr));
		pb_type->models_contained->data_vptr = pb_type->model;
		pb_type->models_contained->next = NULL;
	} else {
		pb_type->models_contained = NULL;
		for(i = 0; i < pb_type->num_modes; i++) {
			for(j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
				AddModelsToPbTypes_rec(&pb_type->modes[i].pb_type_children[j]);
				child = pb_type->modes[i].pb_type_children[j].models_contained;
				/* find model in parent that matches with that in child, if not, add to parent */
				while(child) {
					curr = pb_type->models_contained;
					while(curr) {
						if(curr->data_vptr == child->data_vptr) {
							break;
						}
						curr = curr->next;
					}
					if(curr == NULL) {
						curr = my_malloc(sizeof(struct s_linked_vptr));
						curr->next = pb_type->models_contained;
						curr->data_vptr = child->data_vptr;
						pb_type->models_contained = curr;
					}
					child = child->next;
				}
			}
		}
	}
}

static void UpdateAndCheckModels(INOUTP struct s_arch *arch) {
	t_model * cur_model;
	t_model_ports *port;
	int i, j;
	cur_model = arch->models;
	while(cur_model) {
		if(cur_model->pb_types == NULL) {
			printf("No pb_type found for model %s\n", cur_model->name);
			exit(1);
		}
		port = cur_model->inputs;
		i = 0;
		j = 0;
		while(port) {
			if(port->is_clock) {
				port->index = i;
				i++;
			} else {
				port->index = j;
				j++;
			}
			port = port->next;
		}
		port = cur_model->outputs;
		i = 0;
		while(port) {
			port->index = i;
			i++;
			port = port->next;
		}
		cur_model = cur_model->next;
	}
}


/* Output the data from architecture data so user can verify it
 * was interpretted correctly. */ 
void
EchoArch(INP const char *EchoFile, INP const t_type_descriptor * Types,
	INP int NumTypes, struct s_arch *arch)
{
    int i, j;
    FILE * Echo;
	t_model * cur_model;
	t_model_ports * model_port;
	struct s_linked_vptr *cur_vptr;


    Echo = my_fopen(EchoFile, "w", 0);
	cur_model = NULL;

	for( j = 0; j < 2; j++ ) {
		if(j == 0) {
			fprintf(Echo, "Printing user models \n");
			cur_model = arch->models;
		} else if(j == 1) {
			fprintf(Echo, "Printing library models \n");
			cur_model = arch->model_library;
		}
		while(cur_model) {
			fprintf(Echo, "Model: \"%s\"\n", cur_model->name);
			model_port = cur_model->inputs;
			while(model_port) {
				fprintf(Echo, "\tInput Ports: \"%s\" \"%d\" min_size=\"%d\"\n", model_port->name, model_port->size, model_port->min_size);
				model_port = model_port->next;
			}
			model_port = cur_model->outputs;
			while(model_port) {
				fprintf(Echo, "\tOutput Ports: \"%s\" \"%d\" min_size=\"%d\"\n", model_port->name, model_port->size, model_port->min_size);
				model_port = model_port->next;
			}
			cur_vptr = cur_model->pb_types;
			i = 0;
			while(cur_vptr != NULL) {
				fprintf(Echo, "\tpb_type %d: \"%s\"\n", i, ((t_pb_type*)cur_vptr->data_vptr)->name);
				cur_vptr = cur_vptr->next;
				i++;
			}
			
			cur_model = cur_model->next;
		}
	}

    for(i = 0; i < NumTypes; ++i)
	{
	    fprintf(Echo, "Type: \"%s\"\n", Types[i].name);
	    fprintf(Echo, "\tcapacity: %d\n", Types[i].capacity);
	    fprintf(Echo, "\theight: %d\n", Types[i].height);
	    
	    fprintf(Echo, "\tis_Fc_frac: %s\n",
		      (Types[i].is_Fc_frac ? "TRUE" : "FALSE"));
	    fprintf(Echo, "\tis_Fc_out_full_flex: %s\n",
		     (Types[i].is_Fc_out_full_flex ? "TRUE" : "FALSE"));
	    fprintf(Echo, "\tFc_in: %f\n", Types[i].Fc_in);
	    fprintf(Echo, "\tFc_out: %f\n", Types[i].Fc_out);

		
	    fprintf(Echo, "\tnum_drivers: %d\n", Types[i].num_drivers);
	    fprintf(Echo, "\tnum_receivers: %d\n", Types[i].num_receivers);
		fprintf(Echo, "\tindex: %d\n", Types[i].index);
		if(Types[i].pb_type) {
			PrintPb_types_rec(Echo, Types[i].pb_type, 2);
		}
	    fprintf(Echo, "\n");
	}
    fclose(Echo);
}

static void
PrintPb_types_rec(INP FILE * Echo, INP const t_pb_type * pb_type, int level)
{
	int i, j, k;
	char *tabs;

	tabs = my_malloc((level + 1) * sizeof(char));
	for(i = 0; i < level; i++) {
		tabs[i] = '\t';
	}
	tabs[level] = '\0';
	
	fprintf(Echo, "%spb_type name: %s\n", tabs, pb_type->name);
	fprintf(Echo, "%s\tblif_model: %s\n", tabs, pb_type->blif_model);
	fprintf(Echo, "%s\tclass_type: %d\n", tabs, pb_type->class_type);
	fprintf(Echo, "%s\tnum_modes: %d\n", tabs, pb_type->num_modes);
	fprintf(Echo, "%s\tnum_ports: %d\n", tabs, pb_type->num_ports);
	for(i = 0; i < pb_type->num_ports; i++) {
		fprintf(Echo, "%s\tport %s type %d num_pins %d\n", tabs,
			pb_type->ports[i].name,
			pb_type->ports[i].type,
			pb_type->ports[i].num_pins);
	}
	for(i = 0; i < pb_type->num_modes; i++) {
		fprintf(Echo, "%s\tmode %s:\n", tabs, pb_type->modes[i].name);
		for(j = 0; j < pb_type->modes[i].num_pb_type_children; j++) {
			PrintPb_types_rec(Echo, &pb_type->modes[i].pb_type_children[j], level + 2);
		}
		for(j = 0; j < pb_type->modes[i].num_interconnect; j++) {
			fprintf(Echo, "%s\t\tinterconnect %d %s %s\n", tabs, pb_type->modes[i].interconnect[j].type,
				pb_type->modes[i].interconnect[j].input_string, 
				pb_type->modes[i].interconnect[j].output_string);
			for(k = 0; k < pb_type->modes[i].interconnect[j].num_annotations; k++) {
				fprintf(Echo, "%s\t\t\tannotation %s %s %d: %s\n", tabs, pb_type->modes[i].interconnect[j].annotations[k].input_pins,
					pb_type->modes[i].interconnect[j].annotations[k].output_pins, pb_type->modes[i].interconnect[j].annotations[k].format,
					pb_type->modes[i].interconnect[j].annotations[k].value[0]);
			}
		}
	}
	free(tabs);
}



