| /* The XML parser processes an XML file into a tree data structure composed of * |
| * ezxml_t nodes. Each ezxml_t node represents an XML element. For example * |
| * <a> <b/> </a> will generate two ezxml_t nodes. One called "a" and its * |
| * child "b". Each ezxml_t node can contain various XML data such as attribute * |
| * information and text content. The XML parser provides several functions to * |
| * help the developer build, traverse, and free this ezxml_t tree. * |
| * * |
| * The function ezxml_parse_file reads in an architecture file. * |
| * * |
| * The function FindElement returns a child ezxml_t node for a given ezxml_t * |
| * node that matches a name provided by the developer. * |
| * * |
| * The function FreeNode frees a child ezxml_t node. All children nodes must * |
| * be freed before the parent can be freed. * |
| * * |
| * The function FindProperty is used to extract attributes from an ezxml_t node. * |
| * The corresponding ezxml_set_attr is used to then free an attribute after it * |
| * is read. We have several helper functions that perform this common * |
| * read/store/free operation in one step such as GetIntProperty and * |
| * GetFloatProperty. * |
| * * |
| * Because of how the XML tree traversal works, we free everything when we're * |
| * done reading an architecture file to make sure that there isn't some part * |
| * of the architecture file that got missed. |
| * |
| * |
| * Architecture file checks already implemented (Daniel Chen): |
| * - Duplicate pb_types, pb_type ports, models, model ports, |
| * interconnects, interconnect annotations. |
| * - Port and pin range checking (port with 4 pins can only be |
| * accessed within [0:3]. |
| * - LUT delay matrix size matches # of LUT inputs |
| * - Ensures XML tags are ordered. |
| * - Clocked primitives that have timing annotations must have a clock |
| * name matching the primitive. |
| * - Enforced VPR definition of LUT and FF must have one input port (n pins) |
| * and one output port(1 pin). |
| * - Checks file extension for blif and architecture xml file, avoid crashes if |
| * the two files are swapped on command line. |
| * |
| */ |
| |
| #include <string.h> |
| #include <assert.h> |
| #include <map> |
| #include <string> |
| #include <sstream> |
| #include "util.h" |
| #include "arch_types.h" |
| #include "ReadLine.h" |
| #include "ezxml.h" |
| #include "read_xml_arch_file.h" |
| #include "read_xml_util.h" |
| #include "parse_switchblocks.h" |
| |
| using namespace std; |
| |
| enum Fc_type { |
| FC_ABS, FC_FRAC, FC_FULL |
| }; |
| |
| /* This gives access to the architecture file name to |
| all architecture-parser functions */ |
| static const char* arch_file_name = NULL; |
| |
| /* 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 *cb_type_descriptors; |
| |
| /* Function prototypes */ |
| /* Populate data */ |
| static void SetupEmptyType(void); |
| 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, |
| e_power_estimation_method power_method); |
| static void ProcessPinToPinAnnotations(ezxml_t parent, |
| t_pin_to_pin_annotation *annotation, t_pb_type * parent_pb_type); |
| static void ProcessInterconnect(INOUTP ezxml_t Parent, t_mode * mode); |
| static void ProcessMode(INOUTP ezxml_t Parent, t_mode * mode, |
| bool * default_leakage_mode); |
| static void Process_Fc(ezxml_t Node, t_type_descriptor * Type, t_segment_inf *segments, int num_segments); |
| static void ProcessComplexBlockProps(ezxml_t Node, t_type_descriptor * Type); |
| static void ProcessSizingTimingIpinCblock(INOUTP ezxml_t Node, |
| OUTP struct s_arch *arch, INP bool timing_enabled); |
| 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 bool 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 bool timing_enabled, s_arch arch); |
| static void ProcessSwitches(INOUTP ezxml_t Node, |
| OUTP struct s_arch_switch_inf **Switches, OUTP int *NumSwitches, |
| INP bool timing_enabled); |
| static void ProcessSwitchTdel(INOUTP ezxml_t Node, INP bool timing_enabled, |
| INP int switch_index, OUTP s_arch_switch_inf *Switches); |
| static void ProcessDirects(INOUTP ezxml_t Parent, OUTP t_direct_inf **Directs, |
| OUTP int *NumDirects, INP struct s_arch_switch_inf *Switches, INP int NumSwitches, |
| INP bool timing_enabled); |
| static void ProcessSegments(INOUTP ezxml_t Parent, |
| OUTP struct s_segment_inf **Segs, OUTP int *NumSegs, |
| INP struct s_arch_switch_inf *Switches, INP int NumSwitches, |
| INP bool timing_enabled, INP bool switchblocklist_required); |
| static void ProcessSwitchblocks(INOUTP ezxml_t Parent, OUTP vector<t_switchblock_inf> *switchblocks, |
| INP t_arch_switch_inf *switches, INP int num_switches); |
| static void ProcessCB_SB(INOUTP ezxml_t Node, INOUTP bool * list, |
| INP int len); |
| static void ProcessPower( INOUTP ezxml_t parent, |
| INOUTP t_power_arch * power_arch, INP t_type_descriptor * Types, |
| INP int NumTypes); |
| |
| static void ProcessClocks(ezxml_t Parent, t_clock_arch * clocks); |
| |
| 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 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); |
| static void PrintPb_types_recPower(INP FILE * Echo, |
| INP const t_pb_type * pb_type, const char* tabs); |
| static void PrintArchInfo(INP FILE * Echo, struct s_arch *arch); |
| static void ProcessPb_TypePowerEstMethod(ezxml_t Parent, t_pb_type * pb_type); |
| static void ProcessPb_TypePort_Power(ezxml_t Parent, t_port * port, |
| e_power_estimation_method power_method); |
| e_power_estimation_method power_method_inherited( |
| e_power_estimation_method parent_power_method); |
| static void CheckXMLTagOrder(ezxml_t Parent); |
| static void primitives_annotation_clock_match( |
| t_pin_to_pin_annotation *annotation, t_pb_type * parent_pb_type); |
| |
| #ifdef INTERPOSER_BASED_ARCHITECTURE |
| int gcd(int a, int b) |
| { |
| if (b == 0) |
| return a; |
| else |
| return gcd(b, a%b); |
| } |
| int lcm(int a, int b) |
| { |
| int mygcd = gcd(a,b); |
| |
| if(mygcd==0) |
| { |
| return -1; |
| } |
| else |
| { |
| return (a*b)/mygcd; |
| } |
| } |
| #endif |
| |
| /* 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, Count, Len; |
| int capacity, pin_count; |
| int num_class; |
| const char * Prop; |
| |
| ezxml_t Cur, Prev; |
| char **Tokens, **CurTokens; |
| |
| capacity = 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Locations->line, |
| "%s is an invalid pin location pattern.\n", Prop); |
| } |
| ezxml_set_attr(Locations, "pattern", NULL); |
| |
| /* Alloc and clear pin locations */ |
| Type->pin_width = (int *) my_calloc(Type->num_pins, sizeof(int)); |
| Type->pin_height = (int *) my_calloc(Type->num_pins, sizeof(int)); |
| Type->pinloc = (int ****) my_malloc(Type->width * sizeof(int **)); |
| for (int width = 0; width < Type->width; ++width) { |
| Type->pinloc[width] = (int ***) my_malloc(Type->height * sizeof(int *)); |
| for (int height = 0; height < Type->height; ++height) { |
| Type->pinloc[width][height] = (int **) my_malloc(4 * sizeof(int *)); |
| for (int side = 0; side < 4; ++side) { |
| Type->pinloc[width][height][side] = (int *) my_malloc( |
| Type->num_pins * sizeof(int)); |
| for (int pin = 0; pin < Type->num_pins; ++pin) { |
| Type->pinloc[width][height][side][pin] = 0; |
| } |
| } |
| } |
| } |
| |
| Type->pin_loc_assignments = (char *****) my_malloc( |
| Type->width * sizeof(char ****)); |
| Type->num_pin_loc_assignments = (int ***) my_malloc( |
| Type->width * sizeof(int **)); |
| for (int width = 0; width < Type->width; ++width) { |
| Type->pin_loc_assignments[width] = (char ****) my_calloc(Type->height, |
| sizeof(char ***)); |
| Type->num_pin_loc_assignments[width] = (int **) my_calloc(Type->height, |
| sizeof(int *)); |
| for (int height = 0; height < Type->height; ++height) { |
| Type->pin_loc_assignments[width][height] = (char ***) my_calloc(4, |
| sizeof(char **)); |
| Type->num_pin_loc_assignments[width][height] = (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 (ie. height) */ |
| int height = GetIntProperty(Cur, "offset", false, 0); |
| if ((height < 0) || (height >= Type->height)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "'%d' is an invalid offset for type '%s'.\n", height, |
| Type->name); |
| } |
| |
| /* Get side */ |
| int side = 0; |
| Prop = FindProperty(Cur, "side", true); |
| if (0 == strcmp(Prop, "left")) { |
| side = LEFT; |
| } else if (0 == strcmp(Prop, "top")) { |
| side = TOP; |
| } else if (0 == strcmp(Prop, "right")) { |
| side = RIGHT; |
| } else if (0 == strcmp(Prop, "bottom")) { |
| side = BOTTOM; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "'%s' is not a valid side.\n", Prop); |
| } |
| ezxml_set_attr(Cur, "side", NULL); |
| |
| /* Check location is on perimeter */ |
| if ((TOP == side) && (height != (Type->height - 1))) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Locations are only allowed on large block perimeter. 'top' side should be at offset %d only.\n", |
| (Type->height - 1)); |
| } |
| if ((BOTTOM == side) && (height != 0)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Locations are only allowed on large block perimeter. 'bottom' side should be at offset 0 only.\n"); |
| } |
| |
| /* Go through lists of pins */ |
| CountTokensInString(Cur->txt, &Count, &Len); |
| Type->num_pin_loc_assignments[0][height][side] = Count; |
| if (Count > 0) { |
| Tokens = GetNodeTokens(Cur); |
| CurTokens = Tokens; |
| Type->pin_loc_assignments[0][height][side] = (char**) my_calloc( |
| Count, sizeof(char*)); |
| for (int pin = 0; pin < Count; ++pin) { |
| /* Store location assignment */ |
| Type->pin_loc_assignments[0][height][side][pin] = 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 = (struct s_class*) my_calloc(num_class, |
| sizeof(struct s_class)); |
| Type->num_class = num_class; |
| Type->pin_class = (int*) my_malloc(Type->num_pins * sizeof(int) * capacity); |
| Type->is_global_pin = (bool*) my_malloc( |
| Type->num_pins * sizeof(bool)* capacity); |
| for (i = 0; i < Type->num_pins * capacity; i++) { |
| Type->pin_class[i] = OPEN; |
| Type->is_global_pin[i] = true; |
| } |
| |
| 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 || |
| Type->pb_type->ports[j].is_non_clock_global; |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Another loc specified for perimeter.\n"); |
| } |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Another loc specified for fill.\n"); |
| } |
| 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Unknown grid location type '%s' for type '%s'.\n", |
| Prop, Type->name); |
| } |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "grid location property 'start' must be specified for grid location type 'col'.\n"); |
| } |
| Type->grid_loc_def[i].start_col = my_atoi(Prop); |
| ezxml_set_attr(Cur, "start", NULL); |
| } else if (Prop != NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "grid location property 'start' valid for grid location type 'col' only.\n"); |
| } |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Grid location property 'repeat' valid for grid location type 'col' only.\n"); |
| } |
| Prop = FindProperty(Cur, "pos", false); |
| if (Type->grid_loc_def[i].grid_loc_type == COL_REL) { |
| if (Prop == NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Grid location property 'pos' must be specified for grid location type 'rel'.\n"); |
| } |
| Type->grid_loc_def[i].col_rel = (float) atof(Prop); |
| ezxml_set_attr(Cur, "pos", NULL); |
| } else if (Prop != NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Grid location property 'pos' valid for grid location type 'rel' only.\n"); |
| } |
| |
| 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, t_pb_type * parent_pb_type) { |
| 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") |
| || 0 == strcmp(Parent->name, "pack_pattern")) { |
| i = 1; |
| } |
| |
| annotation->num_value_prop_pairs = i; |
| annotation->prop = (int*) my_calloc(i, sizeof(int)); |
| annotation->value = (char**) my_calloc(i, sizeof(char *)); |
| annotation->line_num = Parent->line; |
| /* Todo: This is slow, I should use a case lookup */ |
| i = 0; |
| if (0 == strcmp(Parent->name, "delay_constant")) { |
| annotation->type = 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 = 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 = 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 = 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 = 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); |
| |
| primitives_annotation_clock_match(annotation, parent_pb_type); |
| |
| } else if (0 == strcmp(Parent->name, "T_clock_to_Q")) { |
| annotation->type = 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); |
| |
| primitives_annotation_clock_match(annotation, parent_pb_type); |
| |
| } else if (0 == strcmp(Parent->name, "T_hold")) { |
| annotation->type = 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); |
| |
| primitives_annotation_clock_match(annotation, parent_pb_type); |
| |
| } else if (0 == strcmp(Parent->name, "pack_pattern")) { |
| annotation->type = E_ANNOT_PIN_TO_PIN_PACK_PATTERN; |
| annotation->format = E_ANNOT_PIN_TO_PIN_CONSTANT; |
| Prop = FindProperty(Parent, "name", true); |
| annotation->prop[i] = (int) E_ANNOT_PIN_TO_PIN_PACK_PATTERN_NAME; |
| annotation->value[i] = my_strdup(Prop); |
| ezxml_set_attr(Parent, "name", 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Unknown port type %s in %s in %s", Parent->name, |
| Parent->parent->name, Parent->parent->parent->name); |
| } |
| assert(i == annotation->num_value_prop_pairs); |
| } |
| |
| static t_port * findPortByName(const char * name, t_pb_type * pb_type, |
| int * high_index, int * low_index) { |
| t_port * port; |
| int i; |
| unsigned int high; |
| unsigned int low; |
| unsigned int bracket_pos; |
| unsigned int colon_pos; |
| |
| bracket_pos = strcspn(name, "["); |
| |
| /* Find port by name */ |
| port = NULL; |
| for (i = 0; i < pb_type->num_ports; i++) { |
| char * compare_to = pb_type->ports[i].name; |
| |
| if (strlen(compare_to) == bracket_pos |
| && strncmp(name, compare_to, bracket_pos) == 0) { |
| port = &pb_type->ports[i]; |
| break; |
| } |
| } |
| if (i >= pb_type->num_ports) { |
| return NULL; |
| } |
| |
| /* Get indices */ |
| if (strlen(name) > bracket_pos) { |
| high = atoi(&name[bracket_pos + 1]); |
| |
| colon_pos = strcspn(name, ":"); |
| |
| if (colon_pos < strlen(name)) { |
| low = atoi(&name[colon_pos + 1]); |
| } else { |
| low = high; |
| } |
| } else { |
| high = port->num_pins - 1; |
| low = 0; |
| } |
| |
| if (high_index && low_index) { |
| *high_index = high; |
| *low_index = low; |
| } |
| |
| return port; |
| } |
| |
| static void ProcessPb_TypePowerPinToggle(ezxml_t parent, t_pb_type * pb_type) { |
| ezxml_t cur, prev; |
| const char * prop; |
| t_port * port; |
| int high, low; |
| |
| cur = FindFirstElement(parent, "port", false); |
| while (cur) { |
| prop = FindProperty(cur, "name", true); |
| |
| port = findPortByName(prop, pb_type, &high, &low); |
| if (!port) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Could not find port '%s' needed for energy per toggle.", |
| prop); |
| } |
| if (high != port->num_pins - 1 || low != 0) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Pin-toggle does not support pin indices (%s)", prop); |
| } |
| |
| if (port->port_power->pin_toggle_initialized) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Duplicate pin-toggle energy for port '%s'", port->name); |
| } |
| port->port_power->pin_toggle_initialized = true; |
| ezxml_set_attr(cur, "name", NULL); |
| |
| /* Get energy per toggle */ |
| port->port_power->energy_per_toggle = GetFloatProperty(cur, |
| "energy_per_toggle", true, 0.); |
| |
| /* Get scaled by factor */ |
| bool reverse_scaled = false; |
| prop = FindProperty(cur, "scaled_by_static_prob", false); |
| if (!prop) { |
| prop = FindProperty(cur, "scaled_by_static_prob_n", false); |
| if (prop) { |
| reverse_scaled = true; |
| } |
| } |
| |
| if (prop) { |
| port->port_power->scaled_by_port = findPortByName(prop, pb_type, |
| &high, &low); |
| if (high != low) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Pin-toggle 'scaled_by_static_prob' must be a single pin (%s)", |
| prop); |
| } |
| port->port_power->scaled_by_port_pin_idx = high; |
| port->port_power->reverse_scaled = reverse_scaled; |
| } |
| ezxml_set_attr(cur, "scaled_by_static_prob", NULL); |
| ezxml_set_attr(cur, "scaled_by_static_prob_n", NULL); |
| |
| prev = cur; |
| cur = cur->next; |
| FreeNode(prev); |
| } |
| } |
| |
| static void ProcessPb_TypePower(ezxml_t Parent, t_pb_type * pb_type) { |
| ezxml_t cur, child; |
| bool require_dynamic_absolute = false; |
| bool require_static_absolute = false; |
| bool require_dynamic_C_internal = false; |
| |
| cur = FindFirstElement(Parent, "power", false); |
| if (!cur) { |
| return; |
| } |
| |
| switch (pb_type->pb_type_power->estimation_method) { |
| case POWER_METHOD_TOGGLE_PINS: |
| ProcessPb_TypePowerPinToggle(cur, pb_type); |
| require_static_absolute = true; |
| break; |
| case POWER_METHOD_C_INTERNAL: |
| require_dynamic_C_internal = true; |
| require_static_absolute = true; |
| break; |
| case POWER_METHOD_ABSOLUTE: |
| require_dynamic_absolute = true; |
| require_static_absolute = true; |
| break; |
| default: |
| break; |
| } |
| |
| if (require_static_absolute) { |
| child = FindElement(cur, "static_power", true); |
| pb_type->pb_type_power->absolute_power_per_instance.leakage = |
| GetFloatProperty(child, "power_per_instance", true, 0.); |
| FreeNode(child); |
| } |
| |
| if (require_dynamic_absolute) { |
| child = FindElement(cur, "dynamic_power", true); |
| pb_type->pb_type_power->absolute_power_per_instance.dynamic = |
| GetFloatProperty(child, "power_per_instance", true, 0.); |
| FreeNode(child); |
| } |
| |
| if (require_dynamic_C_internal) { |
| child = FindElement(cur, "dynamic_power", true); |
| pb_type->pb_type_power->C_internal = GetFloatProperty(child, |
| "C_internal", true, 0.); |
| FreeNode(child); |
| } |
| |
| if (cur) { |
| FreeNode(cur); |
| } |
| |
| } |
| |
| static void ProcessPb_TypePowerEstMethod(ezxml_t Parent, t_pb_type * pb_type) { |
| ezxml_t cur; |
| const char * prop; |
| |
| e_power_estimation_method parent_power_method; |
| |
| prop = NULL; |
| |
| cur = FindFirstElement(Parent, "power", false); |
| if (cur) { |
| prop = FindProperty(cur, "method", false); |
| } |
| |
| if (pb_type->parent_mode && pb_type->parent_mode->parent_pb_type) { |
| parent_power_method = |
| pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method; |
| } else { |
| parent_power_method = POWER_METHOD_AUTO_SIZES; |
| } |
| |
| if (!prop) { |
| /* default method is auto-size */ |
| pb_type->pb_type_power->estimation_method = power_method_inherited( |
| parent_power_method); |
| } else if (strcmp(prop, "auto-size") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_AUTO_SIZES; |
| } else if (strcmp(prop, "specify-size") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_SPECIFY_SIZES; |
| } else if (strcmp(prop, "pin-toggle") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_TOGGLE_PINS; |
| } else if (strcmp(prop, "c-internal") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_C_INTERNAL; |
| } else if (strcmp(prop, "absolute") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_ABSOLUTE; |
| } else if (strcmp(prop, "ignore") == 0) { |
| pb_type->pb_type_power->estimation_method = POWER_METHOD_IGNORE; |
| } else if (strcmp(prop, "sum-of-children") == 0) { |
| pb_type->pb_type_power->estimation_method = |
| POWER_METHOD_SUM_OF_CHILDREN; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Invalid power estimation method for pb_type '%s'", |
| pb_type->name); |
| } |
| |
| if (prop) { |
| ezxml_set_attr(cur, "method", NULL); |
| } |
| |
| } |
| |
| /* 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, k, num_annotations; |
| const char *Prop; |
| ezxml_t Cur = NULL; |
| ezxml_t Prev = NULL; |
| char* class_name; |
| /* STL maps for checking various duplicate names */ |
| map<string, int> pb_port_names; |
| map<string, int> mode_names; |
| pair<map<string, int>::iterator, bool> ret_pb_ports; |
| pair<map<string, int>::iterator, bool> ret_mode_names; |
| int num_in_ports, num_out_ports, num_clock_ports; |
| int num_delay_constant, num_delay_matrix, num_C_constant, num_C_matrix, |
| num_T_setup, num_T_cq, num_T_hold; |
| |
| 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Unknown class '%s' in pb_type '%s'\n", class_name, |
| pb_type->name); |
| } |
| 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 = num_in_ports = num_out_ports = num_clock_ports = 0; |
| num_in_ports = CountChildren(Parent, "input", 0); |
| num_out_ports = CountChildren(Parent, "output", 0); |
| num_clock_ports = CountChildren(Parent, "clock", 0); |
| num_ports = num_in_ports + num_out_ports + num_clock_ports; |
| pb_type->ports = (t_port*) my_calloc(num_ports, sizeof(t_port)); |
| pb_type->num_ports = num_ports; |
| |
| /* Enforce VPR's definition of LUT/FF by checking number of ports */ |
| if (pb_type->class_type == LUT_CLASS |
| || pb_type->class_type == LATCH_CLASS) { |
| if (num_in_ports != 1 || num_out_ports != 1) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "%s primitives must contain exactly one input port and one output port." |
| "Found '%d' input port(s) and '%d' output port(s) for '%s'", |
| (pb_type->class_type == LUT_CLASS) ? "LUT" : "Latch", |
| num_in_ports, num_out_ports, pb_type->name); |
| } |
| } |
| |
| /* Check the XML tag ordering of pb_type */ |
| CheckXMLTagOrder(Parent); |
| |
| /* Initialize Power Structure */ |
| pb_type->pb_type_power = (t_pb_type_power*) my_calloc(1, |
| sizeof(t_pb_type_power)); |
| ProcessPb_TypePowerEstMethod(Parent, pb_type); |
| |
| /* process ports */ |
| j = 0; |
| for (i = 0; i < 3; i++) { |
| if (i == 0) { |
| k = 0; |
| Cur = FindFirstElement(Parent, "input", false); |
| } else if (i == 1) { |
| k = 0; |
| Cur = FindFirstElement(Parent, "output", false); |
| } else { |
| k = 0; |
| Cur = FindFirstElement(Parent, "clock", false); |
| } |
| while (Cur != NULL) { |
| pb_type->ports[j].parent_pb_type = pb_type; |
| pb_type->ports[j].index = j; |
| pb_type->ports[j].port_index_by_type = k; |
| ProcessPb_TypePort(Cur, &pb_type->ports[j], |
| pb_type->pb_type_power->estimation_method); |
| |
| //Check port name duplicates |
| ret_pb_ports = pb_port_names.insert( |
| pair<string, int>(pb_type->ports[j].name, 0)); |
| if (!ret_pb_ports.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Duplicate port names in pb_type '%s': port '%s'\n", |
| pb_type->name, pb_type->ports[j].name); |
| } |
| |
| /* get next iteration */ |
| Prev = Cur; |
| Cur = Cur->next; |
| j++; |
| k++; |
| 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) { |
| 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_delay_constant = CountChildren(Parent, "delay_constant", 0); |
| num_delay_matrix = CountChildren(Parent, "delay_matrix", 0); |
| num_C_constant = CountChildren(Parent, "C_constant", 0); |
| num_C_matrix = CountChildren(Parent, "C_matrix", 0); |
| num_T_setup = CountChildren(Parent, "T_setup", 0); |
| num_T_cq = CountChildren(Parent, "T_clock_to_Q", 0); |
| num_T_hold = CountChildren(Parent, "T_hold", 0); |
| num_annotations = num_delay_constant + num_delay_matrix + num_C_constant |
| + num_C_matrix + num_T_setup + num_T_cq + num_T_hold; |
| |
| CheckXMLTagOrder(Parent); |
| |
| pb_type->annotations = (t_pin_to_pin_annotation*) 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], |
| pb_type); |
| |
| /* 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 { |
| bool default_leakage_mode = false; |
| |
| /* container pb_type, process modes */ |
| assert(pb_type->class_type == UNKNOWN_CLASS); |
| pb_type->num_modes = CountChildren(Parent, "mode", 0); |
| pb_type->pb_type_power->leakage_default_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 = (t_mode*) 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], &default_leakage_mode); |
| i++; |
| } else { |
| pb_type->modes = (t_mode*) 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; |
| pb_type->modes[i].index = i; |
| ProcessMode(Cur, &pb_type->modes[i], &default_leakage_mode); |
| if (default_leakage_mode) { |
| pb_type->pb_type_power->leakage_default_mode = i; |
| } |
| |
| ret_mode_names = mode_names.insert( |
| pair<string, int>(pb_type->modes[i].name, 0)); |
| if (!ret_mode_names.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Duplicate mode name: '%s' in pb_type '%s'.\n", |
| pb_type->modes[i].name, pb_type->name); |
| } |
| |
| /* get next iteration */ |
| Prev = Cur; |
| Cur = Cur->next; |
| i++; |
| FreeNode(Prev); |
| } |
| } |
| } |
| assert(i == pb_type->num_modes); |
| } |
| |
| pb_port_names.clear(); |
| mode_names.clear(); |
| ProcessPb_TypePower(Parent, pb_type); |
| |
| } |
| |
| static void ProcessPb_TypePort_Power(ezxml_t Parent, t_port * port, |
| e_power_estimation_method power_method) { |
| ezxml_t cur; |
| const char * prop; |
| bool wire_defined = false; |
| |
| port->port_power = (t_port_power*) my_calloc(1, sizeof(t_port_power)); |
| |
| //Defaults |
| if (power_method == POWER_METHOD_AUTO_SIZES) { |
| port->port_power->wire_type = POWER_WIRE_TYPE_AUTO; |
| port->port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; |
| } else if (power_method == POWER_METHOD_SPECIFY_SIZES) { |
| port->port_power->wire_type = POWER_WIRE_TYPE_IGNORED; |
| port->port_power->buffer_type = POWER_BUFFER_TYPE_NONE; |
| } |
| |
| cur = FindElement(Parent, "power", false); |
| |
| if (cur) { |
| /* Wire capacitance */ |
| |
| /* Absolute C provided */ |
| prop = FindProperty(cur, "wire_capacitance", false); |
| if (prop) { |
| if (!(power_method == POWER_METHOD_AUTO_SIZES |
| || power_method == POWER_METHOD_SPECIFY_SIZES)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Wire capacitance defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", |
| port->name, port->parent_pb_type->name); |
| } else { |
| wire_defined = true; |
| port->port_power->wire_type = POWER_WIRE_TYPE_C; |
| port->port_power->wire.C = (float) atof(prop); |
| } |
| ezxml_set_attr(cur, "wire_capacitance", NULL); |
| } |
| |
| /* Wire absolute length provided */ |
| prop = FindProperty(cur, "wire_length", false); |
| if (prop) { |
| if (!(power_method == POWER_METHOD_AUTO_SIZES |
| || power_method == POWER_METHOD_SPECIFY_SIZES)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Wire length defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", |
| port->name, port->parent_pb_type->name); |
| } else if (wire_defined) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Multiple wire properties defined for port '%s', pb_type '%s'.", |
| port->name, port->parent_pb_type->name); |
| } else if (strcmp(prop, "auto") == 0) { |
| wire_defined = true; |
| port->port_power->wire_type = POWER_WIRE_TYPE_AUTO; |
| } else { |
| wire_defined = true; |
| port->port_power->wire_type = POWER_WIRE_TYPE_ABSOLUTE_LENGTH; |
| port->port_power->wire.absolute_length = (float) atof(prop); |
| } |
| ezxml_set_attr(cur, "wire_length", NULL); |
| } |
| |
| /* Wire relative length provided */ |
| prop = FindProperty(cur, "wire_relative_length", false); |
| if (prop) { |
| if (!(power_method == POWER_METHOD_AUTO_SIZES |
| || power_method == POWER_METHOD_SPECIFY_SIZES)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Wire relative length defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", |
| port->name, port->parent_pb_type->name); |
| } else if (wire_defined) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Multiple wire properties defined for port '%s', pb_type '%s'.", |
| port->name, port->parent_pb_type->name); |
| } else { |
| wire_defined = true; |
| port->port_power->wire_type = POWER_WIRE_TYPE_RELATIVE_LENGTH; |
| port->port_power->wire.relative_length = (float) atof(prop); |
| } |
| ezxml_set_attr(cur, "wire_relative_length", NULL); |
| } |
| |
| /* Buffer Size */ |
| prop = FindProperty(cur, "buffer_size", false); |
| if (prop) { |
| if (!(power_method == POWER_METHOD_AUTO_SIZES |
| || power_method == POWER_METHOD_SPECIFY_SIZES)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, cur->line, |
| "Buffer size defined for port '%s'. This is an invalid option for the parent pb_type '%s' power estimation method.", |
| port->name, port->parent_pb_type->name); |
| } else if (strcmp(prop, "auto") == 0) { |
| port->port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; |
| } else { |
| port->port_power->buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; |
| port->port_power->buffer_size = (float) atof(prop); |
| } |
| ezxml_set_attr(cur, "buffer_size", NULL); |
| } |
| |
| FreeNode(cur); |
| } |
| } |
| |
| static void ProcessPb_TypePort(INOUTP ezxml_t Parent, t_port * port, |
| e_power_estimation_method power_method) { |
| 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); |
| |
| Prop = FindProperty(Parent, "chain", false); |
| port->chain_name = my_strdup(Prop); |
| ezxml_set_attr(Parent, "chain", NULL); |
| |
| port->equivalent = GetboolProperty(Parent, "equivalent", false, false); |
| port->num_pins = GetIntProperty(Parent, "num_pins", true, 0); |
| port->is_non_clock_global = GetboolProperty(Parent, |
| "is_non_clock_global", false, false); |
| |
| if (0 == strcmp(Parent->name, "input")) { |
| port->type = IN_PORT; |
| port->is_clock = false; |
| |
| /* Check if LUT/FF port class is lut_in/D */ |
| if (port->parent_pb_type->class_type == LUT_CLASS) { |
| if ((!port->port_class) || strcmp("lut_in", port->port_class)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Inputs to LUT primitives must have a port class named " |
| "as \"lut_in\"."); |
| } |
| } else if (port->parent_pb_type->class_type == LATCH_CLASS) { |
| if ((!port->port_class) || strcmp("D", port->port_class)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Input to flipflop primitives must have a port class named " |
| "as \"D\"."); |
| } |
| /* Only allow one input pin for FF's */ |
| if (port->num_pins != 1) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Input port of flipflop primitives must have exactly one pin. " |
| "Found %d.", port->num_pins); |
| } |
| } |
| |
| } else if (0 == strcmp(Parent->name, "output")) { |
| port->type = OUT_PORT; |
| port->is_clock = false; |
| |
| /* Check if LUT/FF port class is lut_out/Q */ |
| if (port->parent_pb_type->class_type == LUT_CLASS) { |
| if ((!port->port_class) || strcmp("lut_out", port->port_class)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Output to LUT primitives must have a port class named " |
| "as \"lut_in\"."); |
| } |
| /* Only allow one output pin for LUT's */ |
| if (port->num_pins != 1) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Output port of LUT primitives must have exactly one pin. " |
| "Found %d.", port->num_pins); |
| } |
| } else if (port->parent_pb_type->class_type == LATCH_CLASS) { |
| if ((!port->port_class) || strcmp("Q", port->port_class)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Output to flipflop primitives must have a port class named " |
| "as \"D\"."); |
| } |
| /* Only allow one output pin for FF's */ |
| if (port->num_pins != 1) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Output port of flipflop primitives must have exactly one pin. " |
| "Found %d.", port->num_pins); |
| } |
| } |
| } else if (0 == strcmp(Parent->name, "clock")) { |
| port->type = IN_PORT; |
| port->is_clock = true; |
| if (port->is_non_clock_global == true) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Port %s cannot be both a clock and a non-clock simultaneously\n", |
| Parent->name); |
| } |
| |
| if (port->parent_pb_type->class_type == LATCH_CLASS) { |
| if ((!port->port_class) || strcmp("clock", port->port_class)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Clock to flipflop primitives must have a port class named " |
| "as \"clock\"."); |
| } |
| /* Only allow one output pin for FF's */ |
| if (port->num_pins != 1) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Clock port of flipflop primitives must have exactly one pin. " |
| "Found %d.", port->num_pins); |
| } |
| } |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Parent->line, |
| "Unknown port type %s", Parent->name); |
| } |
| |
| ProcessPb_TypePort_Power(Parent, port, power_method); |
| } |
| |
| static void ProcessInterconnect(INOUTP ezxml_t Parent, t_mode * mode) { |
| int num_interconnect = 0; |
| int num_complete, num_direct, num_mux; |
| int i, j, k, L_index, num_annotations; |
| int num_delay_constant, num_delay_matrix, num_C_constant, num_C_matrix, |
| num_pack_pattern; |
| const char *Prop; |
| ezxml_t Cur, Prev; |
| ezxml_t Cur2, Prev2; |
| Cur = Cur2 = Prev = Prev2 = NULL; |
| |
| map<string, int> interc_names; |
| pair<map<string, int>::iterator, bool> ret_interc_names; |
| |
| num_complete = num_direct = num_mux = 0; |
| num_complete = CountChildren(Parent, "complete", 0); |
| num_direct = CountChildren(Parent, "direct", 0); |
| num_mux = CountChildren(Parent, "mux", 0); |
| num_interconnect = num_complete + num_direct + num_mux; |
| |
| CheckXMLTagOrder(Parent); |
| |
| mode->num_interconnect = num_interconnect; |
| mode->interconnect = (t_interconnect*) my_calloc(num_interconnect, |
| sizeof(t_interconnect)); |
| |
| i = 0; |
| for (L_index = 0; L_index < 3; L_index++) { |
| if (L_index == 0) { |
| Cur = FindFirstElement(Parent, "complete", false); |
| } else if (L_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].line_num = Cur->line; |
| |
| mode->interconnect[i].parent_mode_index = mode->index; |
| mode->interconnect[i].parent_mode = mode; |
| |
| 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); |
| |
| ret_interc_names = interc_names.insert( |
| pair<string, int>(mode->interconnect[i].name, 0)); |
| if (!ret_interc_names.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Duplicate interconnect name: '%s' in mode: '%s'.\n", |
| mode->interconnect[i].name, mode->name); |
| } |
| |
| /* Process delay and capacitance annotations */ |
| num_annotations = 0; |
| num_delay_constant = CountChildren(Cur, "delay_constant", 0); |
| num_delay_matrix = CountChildren(Cur, "delay_matrix", 0); |
| num_C_constant = CountChildren(Cur, "C_constant", 0); |
| num_C_matrix = CountChildren(Cur, "C_matrix", 0); |
| num_pack_pattern = CountChildren(Cur, "pack_pattern", 0); |
| num_annotations = num_delay_constant + num_delay_matrix |
| + num_C_constant + num_C_matrix + num_pack_pattern; |
| |
| CheckXMLTagOrder(Cur); |
| |
| mode->interconnect[i].annotations = |
| (t_pin_to_pin_annotation*) 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 < 5; 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); |
| } else if (j == 4) { |
| Cur2 = FindFirstElement(Cur, "pack_pattern", false); |
| } |
| while (Cur2 != NULL) { |
| ProcessPinToPinAnnotations(Cur2, |
| &(mode->interconnect[i].annotations[k]), NULL); |
| |
| /* get next iteration */ |
| Prev2 = Cur2; |
| Cur2 = Cur2->next; |
| k++; |
| FreeNode(Prev2); |
| } |
| } |
| assert(k == num_annotations); |
| |
| /* Power */ |
| mode->interconnect[i].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, |
| sizeof(t_interconnect_power)); |
| mode->interconnect[i].interconnect_power->port_info_initialized = |
| false; |
| |
| //ProcessInterconnectMuxArch(Cur, &mode->interconnect[i]); |
| |
| /* get next iteration */ |
| Prev = Cur; |
| Cur = Cur->next; |
| FreeNode(Prev); |
| i++; |
| } |
| } |
| |
| interc_names.clear(); |
| assert(i == num_interconnect); |
| } |
| |
| static void ProcessMode(INOUTP ezxml_t Parent, t_mode * mode, |
| bool * default_leakage_mode) { |
| int i; |
| const char *Prop; |
| ezxml_t Cur, Prev; |
| map<string, int> pb_type_names; |
| pair<map<string, int>::iterator, bool> ret_pb_types; |
| |
| 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", 0); |
| if (mode->num_pb_type_children > 0) { |
| mode->pb_type_children = (t_pb_type*) 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); |
| |
| ret_pb_types = pb_type_names.insert( |
| pair<string, int>(mode->pb_type_children[i].name, 0)); |
| if (!ret_pb_types.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Duplicate pb_type name: '%s' in mode: '%s'.\n", |
| mode->pb_type_children[i].name, mode->name); |
| } |
| |
| /* get next iteration */ |
| Prev = Cur; |
| Cur = Cur->next; |
| i++; |
| FreeNode(Prev); |
| } |
| } |
| } else { |
| mode->pb_type_children = NULL; |
| } |
| |
| /* Allocate power structure */ |
| mode->mode_power = (t_mode_power*) my_calloc(1, sizeof(t_mode_power)); |
| |
| /* Clear STL map used for duplicate checks */ |
| pb_type_names.clear(); |
| |
| 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 Node, t_type_descriptor * Type, t_segment_inf *segments, int num_segments) { |
| enum Fc_type def_type_in, def_type_out, ovr_type; |
| const char *Prop, *Prop2; |
| char *port_name; |
| float def_in_val, def_out_val, ovr_val; |
| int ipin, iclass, end_pin_index, start_pin_index, match_count; |
| int iport, iport_pin, curr_pin, port_found; |
| ezxml_t Child, Junk; |
| |
| def_type_in = FC_FRAC; |
| def_type_out = FC_FRAC; |
| def_in_val = OPEN; |
| def_out_val = OPEN; |
| |
| Type->is_Fc_frac = (bool *) my_malloc(Type->num_pins * sizeof(bool)); |
| Type->is_Fc_full_flex = (bool *) my_malloc( |
| Type->num_pins * sizeof(bool)); |
| Type->Fc = (float **) alloc_matrix(0, Type->num_pins-1, 0, num_segments-1, sizeof(float)); |
| |
| /* Load the default fc_in, if specified */ |
| Prop = FindProperty(Node, "default_in_type", false); |
| if (Prop != NULL) { |
| if (0 == strcmp(Prop, "abs")) { |
| def_type_in = FC_ABS; |
| } else if (0 == strcmp(Prop, "frac")) { |
| def_type_in = FC_FRAC; |
| } else if (0 == strcmp(Prop, "full")) { |
| def_type_in = FC_FULL; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Invalid type '%s' for Fc. Only abs, frac and full are allowed.\n", |
| Prop); |
| } |
| switch (def_type_in) { |
| case FC_FULL: |
| def_in_val = 0.0; |
| break; |
| case FC_ABS: |
| case FC_FRAC: |
| Prop2 = FindProperty(Node, "default_in_val", true); |
| def_in_val = (float) atof(Prop2); |
| ezxml_set_attr(Node, "default_in_val", NULL); |
| break; |
| default: |
| def_in_val = -1; |
| } |
| /* Release the property */ |
| ezxml_set_attr(Node, "default_in_type", NULL); |
| } |
| |
| /* Load the default fc_out, if specified */ |
| Prop = FindProperty(Node, "default_out_type", false); |
| if (Prop != NULL) { |
| if (0 == strcmp(Prop, "abs")) { |
| def_type_out = FC_ABS; |
| } else if (0 == strcmp(Prop, "frac")) { |
| def_type_out = FC_FRAC; |
| } else if (0 == strcmp(Prop, "full")) { |
| def_type_out = FC_FULL; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Invalid type '%s' for Fc. Only abs, frac and full are allowed.\n", |
| Prop); |
| } |
| switch (def_type_out) { |
| case FC_FULL: |
| def_out_val = 0.0; |
| break; |
| case FC_ABS: |
| case FC_FRAC: |
| Prop2 = FindProperty(Node, "default_out_val", true); |
| def_out_val = (float) atof(Prop2); |
| ezxml_set_attr(Node, "default_out_val", NULL); |
| break; |
| default: |
| def_out_val = -1; |
| } |
| /* Release the property */ |
| ezxml_set_attr(Node, "default_out_type", NULL); |
| } |
| |
| /* Go though all the pins and segments in Type, assign def_in_val and def_out_val |
| * to entries in Type->Fc array corresponding to input pins and output |
| * pins. Also sets up the type of fc of the pin in the bool arrays */ |
| for (ipin = 0; ipin < Type->num_pins; ipin++) { |
| iclass = Type->pin_class[ipin]; |
| for (int iseg = 0; iseg < num_segments; iseg++){ |
| if (Type->class_inf[iclass].type == DRIVER) { |
| Type->Fc[ipin][iseg] = def_out_val; |
| Type->is_Fc_full_flex[ipin] = |
| (def_type_out == FC_FULL) ? true : false; |
| Type->is_Fc_frac[ipin] = (def_type_out == FC_FRAC) ? true : false; |
| } else if (Type->class_inf[iclass].type == RECEIVER) { |
| Type->Fc[ipin][iseg] = def_in_val; |
| Type->is_Fc_full_flex[ipin] = |
| (def_type_in == FC_FULL) ? true : false; |
| Type->is_Fc_frac[ipin] = (def_type_in == FC_FRAC) ? true : false; |
| } else { |
| Type->Fc[ipin][iseg] = -1; |
| Type->is_Fc_full_flex[ipin] = false; |
| Type->is_Fc_frac[ipin] = false; |
| } |
| } |
| } |
| |
| /* pin-based fc overrides and segment-based fc overrides should not exist at the same time */ |
| if (ezxml_child(Node, "pin") != NULL && ezxml_child(Node, "segment") != NULL){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Complex block 'fc' is allowed to specify pin-based fc overrides OR segment-based fc overrides, not both.\n"); |
| } |
| |
| /* Now, check for pin-based fc override - look for pin child. */ |
| Child = ezxml_child(Node, "pin"); |
| while (Child != NULL) { |
| /* Get all the properties of the child first */ |
| Prop = FindProperty(Child, "name", true); |
| if (Prop == NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Pin child with no name is not allowed.\n"); |
| } |
| ezxml_set_attr(Child, "name", NULL); |
| |
| Prop2 = FindProperty(Child, "fc_type", true); |
| if (Prop2 != NULL) { |
| if (0 == strcmp(Prop2, "abs")) { |
| ovr_type = FC_ABS; |
| } else if (0 == strcmp(Prop2, "frac")) { |
| ovr_type = FC_FRAC; |
| } else if (0 == strcmp(Prop2, "full")) { |
| ovr_type = FC_FULL; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Invalid type '%s' for Fc. Only abs, frac and full are allowed.\n", |
| Prop2); |
| } |
| switch (ovr_type) { |
| case FC_FULL: |
| ovr_val = 0.0; |
| break; |
| case FC_ABS: |
| case FC_FRAC: |
| Prop2 = FindProperty(Child, "fc_val", true); |
| if (Prop2 == NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Pin child with no fc_val specified is not allowed.\n"); |
| } |
| ovr_val = (float) atof(Prop2); |
| ezxml_set_attr(Child, "fc_val", NULL); |
| break; |
| default: |
| ovr_val = -1; |
| } |
| /* Release the property */ |
| ezxml_set_attr(Child, "fc_type", NULL); |
| |
| port_name = NULL; |
| |
| /* Search for the child pin in Type and overwrites the default values */ |
| /* Check whether the name is in the format of "<port_name>" or * |
| * "<port_name> [start_index:end_index]" by looking for the symbol '[' */ |
| Prop2 = strstr(Prop, "["); |
| if (Prop2 == NULL) { |
| /* Format "port_name" , Prop stores the port_name */ |
| end_pin_index = start_pin_index = -1; |
| } else { |
| /* Format "port_name [start_index:end_index]" */ |
| match_count = sscanf(Prop, "%s [%d:%d]", port_name, |
| &end_pin_index, &start_pin_index); |
| Prop = port_name; |
| if (match_count != 3 |
| || (match_count != 1 && port_name == NULL)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Invalid name for pin child, name should be in the format \"port_name\" or \"port_name [end_pin_index:start_pin_index]\"," |
| "The end_pin_index and start_pin_index can be the same.\n"); |
| } |
| if (end_pin_index < 0 || start_pin_index < 0) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "The pin_index should not be a negative value.\n"); |
| } |
| if (end_pin_index < start_pin_index) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "The end_pin_index should be not be less than start_pin_index.\n"); |
| } |
| } |
| |
| /* Find the matching port_name in Type */ |
| /* TODO: Check for pins assigned more than one override fc's - right now assigning the last value specified. */ |
| iport_pin = 0; |
| port_found = false; |
| for (iport = 0; |
| ((iport < Type->pb_type->num_ports) && (port_found == false)); |
| iport++) { |
| if (strcmp(Prop, Type->pb_type->ports[iport].name) == 0) { |
| /* This is the port, the start_pin_index and end_pin_index offset starts |
| * here. The indices are inclusive. */ |
| port_found = true; |
| if (end_pin_index > Type->pb_type->ports[iport].num_pins) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "The end_pin_index for this port: %d cannot be greater than the number of pins in this port: %d.\n", |
| end_pin_index, |
| Type->pb_type->ports[iport].num_pins); |
| } |
| |
| // The pin indices is not specified - override whole port. |
| if (end_pin_index == -1 && start_pin_index == -1) { |
| start_pin_index = 0; |
| |
| // Minus one since it is going to be assessed inclusively. |
| end_pin_index = Type->pb_type->ports[iport].num_pins |
| - 1; |
| } |
| |
| /* Go through the pins in the port from start_pin_index to end_pin_index |
| * and overwrite the default fc_val and fc_type with the values parsed in |
| * from above. */ |
| for (curr_pin = start_pin_index; curr_pin <= end_pin_index; |
| curr_pin++) { |
| |
| // Check whether the value had been overwritten |
| if (ovr_val != Type->Fc[iport_pin + curr_pin][0] |
| || Type->is_Fc_full_flex[iport_pin |
| + curr_pin] |
| != (ovr_type == FC_FULL) ? true : |
| false |
| || Type->is_Fc_frac[iport_pin + curr_pin] |
| != (ovr_type == FC_FRAC) ? |
| true : false) { |
| |
| for (int iseg = 0; iseg < num_segments; iseg++){ |
| Type->Fc[iport_pin + curr_pin][iseg] = ovr_val; |
| } |
| Type->is_Fc_full_flex[iport_pin + curr_pin] = |
| (ovr_type == FC_FULL) ? true : false; |
| Type->is_Fc_frac[iport_pin + curr_pin] = |
| (ovr_type == FC_FRAC) ? true : false; |
| |
| } else { |
| |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, |
| Child->line, |
| "Multiple Fc override detected!\n"); |
| } |
| } |
| |
| } else { |
| /* This is not the matching port, move the iport_pin index forward. */ |
| iport_pin += Type->pb_type->ports[iport].num_pins; |
| } |
| } /* Finish going through all the ports in pb_type looking for the pin child's port. */ |
| |
| /* The override pin child is not in any of the ports in pb_type. */ |
| if (port_found == false) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "The port \"%s\" cannot be found.\n", Prop); |
| } |
| |
| /* End of case where fc_type of pin_child is specified. */ |
| } else { |
| /* fc_type of pin_child is not specified. Error out. */ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Pin child with no fc_type specified is not allowed.\n"); |
| } |
| |
| /* Find next child and frees up the current child. */ |
| Junk = Child; |
| Child = ezxml_next(Child); |
| FreeNode(Junk); |
| |
| } /* End of processing pin children */ |
| |
| /* now check for segment-based overrides. earlier in this function we already checked that both kinds of |
| overrides haven't been specified */ |
| Child = ezxml_child(Node, "segment"); |
| while (Child != NULL){ |
| const char *segment_name; |
| int seg_ind; |
| float in_val; |
| float out_val; |
| |
| /* get name */ |
| Prop = FindProperty(Child, "name", true); |
| ezxml_set_attr(Child, "name", NULL); |
| segment_name = Prop; |
| |
| /* get fc_in */ |
| Prop = FindProperty(Child, "in_val", true); |
| ezxml_set_attr(Child, "in_val", NULL); |
| in_val = (float) atof(Prop); |
| |
| |
| /* get fc_out */ |
| Prop = FindProperty(Child, "out_val", true); |
| ezxml_set_attr(Child, "out_val", NULL); |
| out_val = (float) atof(Prop); |
| |
| |
| /* get segment index for which Fc should be updated */ |
| seg_ind = UNDEFINED; |
| for (int iseg = 0; iseg < num_segments; iseg++){ |
| if ( strcmp(segment_name, segments[iseg].name) == 0 ){ |
| seg_ind = iseg; |
| break; |
| } |
| } |
| if (seg_ind == UNDEFINED){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Child->line, |
| "Segment-based Fc override specified segment name that cannot be found in segment list: %s\n", segment_name); |
| } |
| |
| /* update Fc for this segment across all driver/receiver pins */ |
| for (ipin = 0; ipin < Type->num_pins; ipin++){ |
| iclass = Type->pin_class[ipin]; |
| |
| if (Type->class_inf[iclass].type == DRIVER){ |
| Type->Fc[ipin][seg_ind] = out_val; |
| } else if (Type->class_inf[iclass].type == RECEIVER){ |
| Type->Fc[ipin][seg_ind] = in_val; |
| } else { |
| /* do nothing */ |
| } |
| } |
| |
| |
| /* Find next child and frees up the current child. */ |
| Junk = Child; |
| Child = ezxml_next(Child); |
| FreeNode(Junk); |
| } |
| |
| } |
| |
| /* 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->width = GetIntProperty(Node, "width", false, 1); |
| Type->height = GetIntProperty(Node, "height", false, 1); |
| Type->area = GetFloatProperty(Node, "area", false, UNDEFINED); |
| |
| if (atof(Prop) < 0) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Area for type %s must be non-negative\n", Type->name); |
| } |
| } |
| |
| /* 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 L_index; |
| /* std::maps for checking duplicates */ |
| map<string, int> model_name_map; |
| map<string, int> model_port_map; |
| pair<map<string, int>::iterator, bool> ret_map_name; |
| pair<map<string, int>::iterator, bool> ret_map_port; |
| |
| L_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 = NULL; |
| 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 = L_index; |
| L_index++; |
| |
| /* Try insert new model, check if already exist at the same time */ |
| ret_map_name = model_name_map.insert(pair<string, int>(temp->name, 0)); |
| if (!ret_map_name.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, child->line, |
| "Duplicate model name: '%s'.\n", temp->name); |
| } |
| |
| /* Process the inputs */ |
| p = ezxml_child(child, "input_ports"); |
| junkp = p; |
| if (p == NULL) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, child->line, |
| "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_non_clock_global = GetboolProperty(p, |
| "is_non_clock_global", false, false); |
| 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); |
| if (tp->is_clock == true && tp->is_non_clock_global == true) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, p->line, |
| "Signal cannot be both a clock and a non-clock signal simultaneously\n"); |
| } |
| |
| /* Try insert new port, check if already exist at the same time */ |
| ret_map_port = model_port_map.insert( |
| pair<string, int>(tp->name, 0)); |
| if (!ret_map_port.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, p->line, |
| "Duplicate model input port name: '%s'.\n", |
| tp->name); |
| } |
| |
| temp->inputs = tp; |
| junk = p; |
| p = ezxml_next(p); |
| FreeNode(junk); |
| } |
| } else /* No input ports? */ |
| { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, child->line, |
| "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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, child->line, |
| "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; |
| Prop = FindProperty(p, "is_clock", false); |
| if (Prop && my_atoi(Prop) != 0) { |
| tp->is_clock = true; |
| } |
| ezxml_set_attr(p, "is_clock", NULL); |
| if (tp->is_clock == true && tp->is_non_clock_global == true) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, p->line, |
| "Signal cannot be both a clock and a non-clock signal simultaneously\n"); |
| } |
| |
| /* Try insert new output port, check if already exist at the same time */ |
| ret_map_port = model_port_map.insert( |
| pair<string, int>(tp->name, 0)); |
| if (!ret_map_port.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, p->line, |
| "Duplicate model output port name: '%s'.\n", |
| tp->name); |
| } |
| |
| temp->outputs = tp; |
| junk = p; |
| p = ezxml_next(p); |
| FreeNode(junk); |
| } |
| } else /* No output ports? */ |
| { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, child->line, |
| "Required output ports not found for element '%s'.\n", |
| temp->name); |
| } |
| |
| FreeNode(junkp); |
| |
| /* Clear port map for next model */ |
| model_port_map.clear(); |
| /* Push new model onto model stack */ |
| temp->next = arch->models; |
| arch->models = temp; |
| /* Find next model */ |
| junk = child; |
| child = ezxml_next(child); |
| FreeNode(junk); |
| } |
| model_port_map.clear(); |
| model_name_map.clear(); |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Auto-sizing, width and height cannot be specified\n"); |
| } |
| arch->clb_grid.Aspect = (float) atof(Prop); |
| ezxml_set_attr(Node, "auto", NULL); |
| if (arch->clb_grid.Aspect <= 0) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "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 bool timing_enabled) { |
| const char *Prop; |
| ezxml_t Cur; |
| bool custom_switch_block = false; |
| |
| ProcessSizingTimingIpinCblock(Node, arch, timing_enabled); |
| |
| 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 if (strcmp(Prop, "custom") == 0) { |
| arch->SBType = CUSTOM; |
| custom_switch_block = true; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "Unknown property %s for switch block type x\n", Prop); |
| } |
| ezxml_set_attr(Cur, "type", NULL); |
| |
| arch->Fs = GetIntProperty(Cur, "fs", !custom_switch_block, 3); |
| |
| FreeNode(Cur); |
| } |
| |
| /* Processes the sizing, timing, and ipin_cblock child objects of the 'device' node. |
| We can specify an ipin cblock's info through the sizing/timing nodes (legacy), |
| OR through the ipin_cblock node which specifies the info using the index of a switch. */ |
| static void ProcessSizingTimingIpinCblock(INOUTP ezxml_t Node, |
| OUTP struct s_arch *arch, INP bool timing_enabled) { |
| |
| ezxml_t Cur; |
| |
| bool ipin_cblock_info_as_switch = false; |
| arch->ipin_cblock_switch_name = NULL; |
| arch->ipin_mux_trans_size = UNDEFINED; |
| arch->C_ipin_cblock = UNDEFINED; |
| arch->T_ipin_cblock = UNDEFINED; |
| |
| Cur = FindElement(Node, "ipin_cblock", false); |
| if (Cur) { |
| const char *switch_name; |
| /* if an ipin_cblock node exists, then ipin cblock delay/capacitance/area are specified |
| through a corresponding switch. in this case, ipin cblock info cannot be specified |
| through the timing/sizing nodes */ |
| switch_name = FindProperty(Cur, "switch", true); |
| arch->ipin_cblock_switch_name = my_strdup(switch_name); |
| ezxml_set_attr(Cur, "switch", NULL); |
| ipin_cblock_info_as_switch = true; |
| FreeNode(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); |
| if (arch->ipin_mux_trans_size && ipin_cblock_info_as_switch){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "If ipin cblock mux trans size is specified via a switch, it should not be specified again via sizing/timing nodes\n"); |
| } |
| FreeNode(Cur); |
| |
| /* currently only ipin cblock info is specified in the timing node */ |
| Cur = FindElement(Node, "timing", (bool)(timing_enabled && !ipin_cblock_info_as_switch)); |
| 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); |
| |
| if (arch->C_ipin_cblock && ipin_cblock_info_as_switch){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "If ipin cblock C is specified via a switch, it should not be specified again via sizing/timing nodes\n"); |
| } |
| if (arch->T_ipin_cblock && ipin_cblock_info_as_switch){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Cur->line, |
| "If ipin cblock T is specified via a switch, it should not be specified again via sizing/timing nodes\n"); |
| } |
| 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; |
| |
| bool 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Unknown property %s for chan_width_distr x\n", Prop); |
| } |
| 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(void) { |
| t_type_descriptor * type; |
| type = &cb_type_descriptors[EMPTY_TYPE->index]; |
| type->name = "<EMPTY>"; |
| type->num_pins = 0; |
| type->width = 1; |
| 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 = NULL; |
| type->is_Fc_full_flex = NULL; |
| type->Fc = NULL; |
| 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->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; |
| |
| /* Power */ |
| copy->pb_type_power = (t_pb_type_power*) my_calloc(1, |
| sizeof(t_pb_type_power)); |
| copy->pb_type_power->estimation_method = power_method_inherited( |
| pb_type->pb_type_power->estimation_method); |
| |
| /* Ports */ |
| copy->num_ports = pb_type->num_ports; |
| copy->ports = (t_port*) 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->ports[i].port_index_by_type = pb_type->ports[i].port_index_by_type; |
| |
| copy->ports[i].port_power = (t_port_power*) my_calloc(1, |
| sizeof(t_port_power)); |
| //Defaults |
| if (copy->pb_type_power->estimation_method == POWER_METHOD_AUTO_SIZES) { |
| copy->ports[i].port_power->wire_type = POWER_WIRE_TYPE_AUTO; |
| copy->ports[i].port_power->buffer_type = POWER_BUFFER_TYPE_AUTO; |
| } else if (copy->pb_type_power->estimation_method |
| == POWER_METHOD_SPECIFY_SIZES) { |
| copy->ports[i].port_power->wire_type = POWER_WIRE_TYPE_IGNORED; |
| copy->ports[i].port_power->buffer_type = POWER_BUFFER_TYPE_NONE; |
| } |
| } |
| |
| copy->max_internal_delay = pb_type->max_internal_delay; |
| copy->annotations = (t_pin_to_pin_annotation*) 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 = (char*) 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 = (char*) 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].line_num = pb_type->annotations[i].line_num; |
| 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 = (int*) my_malloc( |
| sizeof(int) * pb_type->annotations[i].num_value_prop_pairs); |
| copy->annotations[i].value = (char**) 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->pb_type_power->leakage_default_mode = 1; |
| lut_pb_type->modes = (t_mode*) my_calloc(lut_pb_type->num_modes, |
| sizeof(t_mode)); |
| |
| /* First mode, route_through */ |
| lut_pb_type->modes[0].name = my_strdup("wire"); |
| lut_pb_type->modes[0].parent_pb_type = lut_pb_type; |
| lut_pb_type->modes[0].index = 0; |
| lut_pb_type->modes[0].num_pb_type_children = 0; |
| lut_pb_type->modes[0].mode_power = (t_mode_power*) my_calloc(1, |
| sizeof(t_mode_power)); |
| |
| /* 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 = (t_interconnect*) my_calloc(1, |
| sizeof(t_interconnect)); |
| lut_pb_type->modes[0].interconnect[0].name = (char*) 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 = (char*) 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 = (char*) 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); |
| |
| lut_pb_type->modes[0].interconnect[0].parent_mode_index = 0; |
| lut_pb_type->modes[0].interconnect[0].parent_mode = &lut_pb_type->modes[0]; |
| lut_pb_type->modes[0].interconnect[0].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, sizeof(t_interconnect_power)); |
| |
| lut_pb_type->modes[0].interconnect[0].annotations = |
| (t_pin_to_pin_annotation*) my_calloc(lut_pb_type->num_annotations, |
| sizeof(t_pin_to_pin_annotation)); |
| lut_pb_type->modes[0].interconnect[0].num_annotations = |
| lut_pb_type->num_annotations; |
| for (i = 0; i < lut_pb_type->modes[0].interconnect[0].num_annotations; |
| i++) { |
| lut_pb_type->modes[0].interconnect[0].annotations[i].clock = my_strdup( |
| lut_pb_type->annotations[i].clock); |
| lut_pb_type->modes[0].interconnect[0].annotations[i].input_pins = |
| my_strdup(lut_pb_type->annotations[i].input_pins); |
| lut_pb_type->modes[0].interconnect[0].annotations[i].output_pins = |
| my_strdup(lut_pb_type->annotations[i].output_pins); |
| lut_pb_type->modes[0].interconnect[0].annotations[i].line_num = |
| lut_pb_type->annotations[i].line_num; |
| lut_pb_type->modes[0].interconnect[0].annotations[i].format = |
| lut_pb_type->annotations[i].format; |
| lut_pb_type->modes[0].interconnect[0].annotations[i].type = |
| lut_pb_type->annotations[i].type; |
| lut_pb_type->modes[0].interconnect[0].annotations[i].num_value_prop_pairs = |
| lut_pb_type->annotations[i].num_value_prop_pairs; |
| lut_pb_type->modes[0].interconnect[0].annotations[i].prop = |
| (int*) my_malloc( |
| sizeof(int) |
| * lut_pb_type->annotations[i].num_value_prop_pairs); |
| lut_pb_type->modes[0].interconnect[0].annotations[i].value = |
| (char**) my_malloc( |
| sizeof(char *) |
| * lut_pb_type->annotations[i].num_value_prop_pairs); |
| for (j = 0; j < lut_pb_type->annotations[i].num_value_prop_pairs; j++) { |
| lut_pb_type->modes[0].interconnect[0].annotations[i].prop[j] = |
| lut_pb_type->annotations[i].prop[j]; |
| lut_pb_type->modes[0].interconnect[0].annotations[i].value[j] = |
| my_strdup(lut_pb_type->annotations[i].value[j]); |
| } |
| } |
| |
| /* 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].index = 1; |
| lut_pb_type->modes[1].num_pb_type_children = 1; |
| lut_pb_type->modes[1].mode_power = (t_mode_power*) my_calloc(1, |
| sizeof(t_mode_power)); |
| lut_pb_type->modes[1].pb_type_children = (t_pb_type*) 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]; |
| for (i = 0; i < lut_pb_type->modes[1].pb_type_children[0].num_ports; i++) { |
| if (lut_pb_type->modes[1].pb_type_children[0].ports[i].type |
| == IN_PORT) { |
| lut_pb_type->modes[1].pb_type_children[0].ports[i].equivalent = |
| true; |
| } |
| } |
| |
| /* Process interconnect */ |
| lut_pb_type->modes[1].num_interconnect = 2; |
| lut_pb_type->modes[1].interconnect = (t_interconnect*) my_calloc(2, |
| sizeof(t_interconnect)); |
| lut_pb_type->modes[1].interconnect[0].name = (char*) my_calloc( |
| strlen(lut_pb_type->name) + 10, sizeof(char)); |
| sprintf(lut_pb_type->modes[1].interconnect[0].name, "direct:%s", |
| lut_pb_type->name); |
| lut_pb_type->modes[1].interconnect[0].type = DIRECT_INTERC; |
| lut_pb_type->modes[1].interconnect[0].input_string = (char*) 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 = (char*) 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[0].infer_annotations = true; |
| |
| lut_pb_type->modes[1].interconnect[0].parent_mode_index = 1; |
| lut_pb_type->modes[1].interconnect[0].parent_mode = &lut_pb_type->modes[1]; |
| lut_pb_type->modes[1].interconnect[0].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, sizeof(t_interconnect_power)); |
| |
| lut_pb_type->modes[1].interconnect[1].name = (char*) my_calloc( |
| strlen(lut_pb_type->name) + 11, sizeof(char)); |
| sprintf(lut_pb_type->modes[1].interconnect[1].name, "direct:%s", |
| lut_pb_type->name); |
| |
| lut_pb_type->modes[1].interconnect[1].type = DIRECT_INTERC; |
| lut_pb_type->modes[1].interconnect[1].input_string = (char*) my_calloc( |
| strlen(default_name) + strlen(out_port->name) + 4, sizeof(char)); |
| sprintf(lut_pb_type->modes[1].interconnect[1].input_string, "%s.%s", |
| default_name, out_port->name); |
| lut_pb_type->modes[1].interconnect[1].output_string = (char*) 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); |
| lut_pb_type->modes[1].interconnect[1].infer_annotations = true; |
| |
| lut_pb_type->modes[1].interconnect[1].parent_mode_index = 1; |
| lut_pb_type->modes[1].interconnect[1].parent_mode = &lut_pb_type->modes[1]; |
| lut_pb_type->modes[1].interconnect[1].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, sizeof(t_interconnect_power)); |
| |
| 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 = (t_mode*) 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; |
| mem_pb_type->modes[0].index = 0; |
| mem_pb_type->modes[0].mode_power = (t_mode_power*) my_calloc(1, |
| sizeof(t_mode_power)); |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "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); |
| } |
| } |
| } |
| |
| mem_pb_type->modes[0].num_pb_type_children = 1; |
| mem_pb_type->modes[0].pb_type_children = (t_pb_type*) 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 = (t_interconnect*) my_calloc( |
| mem_pb_type->modes[0].num_interconnect, sizeof(t_interconnect)); |
| |
| for (i = 0; i < mem_pb_type->modes[0].num_interconnect; i++) { |
| mem_pb_type->modes[0].interconnect[i].parent_mode_index = 0; |
| mem_pb_type->modes[0].interconnect[i].parent_mode = |
| &mem_pb_type->modes[0]; |
| } |
| |
| /* 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 = |
| (char*) my_calloc(i_inter / 10 + 8, sizeof(char)); |
| sprintf(mem_pb_type->modes[0].interconnect[i_inter].name, |
| "direct%d", i_inter); |
| mem_pb_type->modes[0].interconnect[i_inter].infer_annotations = true; |
| |
| 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 = |
| (char*) 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 = |
| (char*) 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 = |
| (char*) 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 = |
| (char*) 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); |
| } |
| |
| /* Allocate interconnect power structures */ |
| mem_pb_type->modes[0].interconnect[i_inter].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, |
| sizeof(t_interconnect_power)); |
| 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 = |
| (char*) 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); |
| mem_pb_type->modes[0].interconnect[i_inter].infer_annotations = true; |
| |
| 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 = |
| (char*) 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 = |
| (char*) 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 = |
| (char*) 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 = |
| (char*) 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); |
| |
| } |
| |
| /* Allocate interconnect power structures */ |
| mem_pb_type->modes[0].interconnect[i_inter].interconnect_power = |
| (t_interconnect_power*) my_calloc(1, |
| sizeof(t_interconnect_power)); |
| 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, |
| bool timing_enabled, s_arch arch) { |
| ezxml_t CurType, Prev; |
| ezxml_t Cur; |
| t_type_descriptor * Type; |
| int i; |
| map<string, int> pb_type_descriptors; |
| pair<map<string, int>::iterator, bool> ret_pb_type_descriptors; |
| /* 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)); |
| |
| cb_type_descriptors = *Types; |
| |
| EMPTY_TYPE = &cb_type_descriptors[EMPTY_TYPE_INDEX]; |
| IO_TYPE = &cb_type_descriptors[IO_TYPE_INDEX]; |
| cb_type_descriptors[EMPTY_TYPE_INDEX].index = EMPTY_TYPE_INDEX; |
| cb_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); |
| |
| ret_pb_type_descriptors = pb_type_descriptors.insert( |
| pair<string, int>(Type->name, 0)); |
| if (!ret_pb_type_descriptors.second) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, CurType->line, |
| "Duplicate pb_type descriptor name: '%s'.\n", Type->name); |
| } |
| |
| /* Load pb_type info */ |
| Type->pb_type = (t_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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, CurType->line, |
| "First complex block must be named \"io\" and define the inputs and outputs for the FPGA"); |
| } |
| } |
| 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 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); |
| |
| /* Load Fc */ |
| Cur = FindElement(CurType, "fc", true); |
| Process_Fc(Cur, Type, arch.Segments, arch.num_segments); |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "grid location type 'fill' must be specified.\n"); |
| } |
| pb_type_descriptors.clear(); |
| } |
| |
| /* Loads the given architecture file. Currently only |
| * handles type information */ |
| void XmlReadArch(INP const char *ArchFile, INP bool timing_enabled, |
| OUTP struct s_arch *arch, OUTP t_type_descriptor ** Types, |
| OUTP int *NumTypes) { |
| ezxml_t Cur = NULL, Next; |
| const char *Prop; |
| bool power_reqd; |
| |
| if (check_file_name_extension(ArchFile, ".xml") == false) { |
| vpr_printf_warning(__FILE__, __LINE__, |
| "Architecture file '%s' may be in incorrect format. " |
| "Expecting .xml format for architecture files.\n", |
| ArchFile); |
| } |
| |
| /* Parse the file */ |
| Cur = ezxml_parse_file(ArchFile); |
| if (NULL == Cur) { |
| vpr_throw(VPR_ERROR_ARCH, ArchFile, 0, |
| "Unable to find/load architecture file '%s'.\n", ArchFile); |
| } |
| |
| arch_file_name = ArchFile; |
| |
| /* 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)) { |
| vpr_printf_warning(__FILE__, __LINE__, |
| "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 switches */ |
| Next = FindElement(Cur, "switchlist", true); |
| ProcessSwitches(Next, &(arch->Switches), &(arch->num_switches), |
| timing_enabled); |
| FreeNode(Next); |
| |
| /* Process switchblocks. This depends on switches */ |
| bool switchblocklist_required = (arch->SBType == CUSTOM); //require this section only if custom switchblocks are used |
| |
| /* 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, switchblocklist_required); |
| FreeNode(Next); |
| |
| Next = FindElement(Cur, "switchblocklist", switchblocklist_required); |
| if (Next){ |
| ProcessSwitchblocks(Next, &(arch->switchblocks), arch->Switches, arch->num_switches); |
| FreeNode(Next); |
| } |
| |
| /* Process types */ |
| Next = FindElement(Cur, "complexblocklist", true); |
| ProcessComplexBlocks(Next, Types, NumTypes, timing_enabled, *arch); |
| FreeNode(Next); |
| |
| #ifdef INTERPOSER_BASED_ARCHITECTURE |
| /* find the least common multiple of block heights |
| * for interposer based architectures, a culine cannot go through a block */ |
| arch->lcm_of_block_heights = 1; |
| for(int i=0; i < *NumTypes; ++i) |
| { |
| t_type_descriptor * Type = &(*Types)[i]; |
| if(Type!=0) |
| { |
| arch->lcm_of_block_heights = lcm(arch->lcm_of_block_heights, Type->height); |
| } |
| } |
| #endif |
| |
| /* Process directs */ |
| Next = FindElement(Cur, "directlist", false); |
| if (Next) { |
| ProcessDirects(Next, &(arch->Directs), &(arch->num_directs), |
| arch->Switches, arch->num_switches, |
| timing_enabled); |
| FreeNode(Next); |
| } |
| |
| /* Process architecture power information */ |
| |
| /* If arch->power has been initialized, meaning the user has requested power estimation, |
| * then the power architecture information is required. |
| */ |
| if (arch->power) { |
| power_reqd = true; |
| } else { |
| power_reqd = false; |
| } |
| |
| Next = FindElement(Cur, "power", power_reqd); |
| if (Next) { |
| if (arch->power) { |
| ProcessPower(Next, arch->power, *Types, *NumTypes); |
| } else { |
| /* This information still needs to be read, even if it is just |
| * thrown away. |
| */ |
| t_power_arch * power_arch_fake = (t_power_arch*) my_calloc(1, |
| sizeof(t_power_arch)); |
| ProcessPower(Next, power_arch_fake, *Types, *NumTypes); |
| free(power_arch_fake); |
| } |
| FreeNode(Next); |
| } |
| |
| // Process Clocks |
| Next = FindElement(Cur, "clocks", power_reqd); |
| if (Next) { |
| if (arch->clocks) { |
| ProcessClocks(Next, arch->clocks); |
| } else { |
| /* This information still needs to be read, even if it is just |
| * thrown away. |
| */ |
| t_clock_arch * clocks_fake = (t_clock_arch*) my_calloc(1, |
| sizeof(t_clock_arch)); |
| ProcessClocks(Next, clocks_fake); |
| free(clocks_fake->clock_inf); |
| free(clocks_fake); |
| } |
| 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_arch_switch_inf *Switches, INP int NumSwitches, |
| INP bool timing_enabled, INP bool switchblocklist_required) { |
| 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 name */ |
| tmp = FindProperty(Node, "name", false); |
| if (tmp) { |
| (*Segs)[i].name = my_strdup(tmp); |
| } else { |
| /* if swich block is "custom", then you have to provide a name for segment */ |
| if (switchblocklist_required) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "No name specified for the segment #%d.\n", i); |
| |
| } |
| /* set name to default: "unnamed_segment_<segment_index>" */ |
| stringstream ss; |
| ss << "unnamed_segment_" << i; |
| string dummy = ss.str(); |
| tmp = dummy.c_str(); |
| (*Segs)[i].name = my_strdup(tmp); |
| } |
| ezxml_set_attr(Node, "name", NULL); |
| |
| /* 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 Power info */ |
| /* |
| (*Segs)[i].Cmetal_per_m = GetFloatProperty(Node, "Cmetal_per_m", false, |
| 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Invalid switch type '%s'.\n", tmp); |
| } |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, SubElem->line, |
| "'%s' is not a valid mux name.\n", tmp); |
| } |
| 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].arch_wire_switch = j; |
| (*Segs)[i].arch_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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, SubElem->line, |
| "'%s' is not a valid wire_switch name.\n", tmp); |
| } |
| (*Segs)[i].arch_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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, SubElem->line, |
| "'%s' is not a valid opin_switch name.\n", tmp); |
| } |
| (*Segs)[i].arch_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 = (bool *) my_malloc(length * sizeof(bool)); |
| 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 = (bool *) my_malloc((length + 1) * sizeof(bool)); |
| 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); |
| } |
| } |
| |
| /* Processes the switchblocklist section from the xml architecture file. |
| See vpr/SRC/route/build_switchblocks.c for a detailed description of this |
| switch block format */ |
| static void ProcessSwitchblocks(INOUTP ezxml_t Parent, OUTP vector<t_switchblock_inf> *switchblocks, |
| INP t_arch_switch_inf *switches, INP int num_switches){ |
| |
| ezxml_t Node; |
| ezxml_t SubElem; |
| const char *tmp; |
| |
| /* get the number of switchblocks */ |
| int num_switchblocks = CountChildren(Parent, "switchblock", 1); |
| switchblocks->reserve(num_switchblocks); |
| |
| /* read-in all switchblock data */ |
| for (int i_sb = 0; i_sb < num_switchblocks; i_sb++){ |
| /* use a temp variable which will be assigned to switchblocks later */ |
| t_switchblock_inf sb; |
| |
| Node = ezxml_child(Parent, "switchblock"); |
| |
| /* get name */ |
| tmp = FindProperty(Node, "name", true); |
| if (tmp){ |
| sb.name = tmp; |
| } |
| ezxml_set_attr(Node, "name", NULL); |
| |
| /* get type */ |
| tmp = FindProperty(Node, "type", true); |
| if (tmp){ |
| if (0 == strcmp(tmp, "bidir")){ |
| sb.directionality = BI_DIRECTIONAL; |
| } else if (0 == strcmp(tmp, "unidir")){ |
| sb.directionality = UNI_DIRECTIONAL; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, "Unsopported switchblock type: %s\n", tmp); |
| } |
| } |
| ezxml_set_attr(Node, "type", NULL); |
| |
| /* get the switchblock location */ |
| SubElem = ezxml_child(Node, "switchblock_location"); |
| tmp = FindProperty(SubElem, "type", true); |
| if (tmp){ |
| if (strcmp(tmp, "EVERYWHERE") == 0){ |
| sb.location = E_EVERYWHERE; |
| } else if (strcmp(tmp, "PERIMETER") == 0){ |
| sb.location = E_PERIMETER; |
| } else if (strcmp(tmp, "CORE") == 0){ |
| sb.location = E_CORE; |
| } else if (strcmp(tmp, "CORNER") == 0){ |
| sb.location = E_CORNER; |
| } else if (strcmp(tmp, "FRINGE") == 0){ |
| sb.location = E_FRINGE; |
| } else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, "unrecognized switchblock location: %s\n", tmp); |
| } |
| } |
| ezxml_set_attr(SubElem, "type", NULL); |
| FreeNode(SubElem); |
| |
| /* get switchblock permutation functions */ |
| SubElem = ezxml_child(Node, "switchfuncs"); |
| read_sb_switchfuncs(SubElem, &(sb)); |
| FreeNode(SubElem); |
| |
| read_sb_wireconns(switches, num_switches, Node, &(sb)); |
| |
| /* assign the sb to the switchblocks vector */ |
| switchblocks->push_back(sb); |
| |
| /* run error checks on switch blocks */ |
| check_switchblock(&sb); |
| |
| FreeNode(Node); |
| } |
| |
| return; |
| } |
| |
| |
| static void ProcessCB_SB(INOUTP ezxml_t Node, INOUTP bool * 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 ' ': |
| case '\t': |
| case '\n': |
| break; |
| case 'T': |
| case '1': |
| if (i >= len) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "CB or SB depopulation is too long. It should be (length) symbols for CBs and (length+1) symbols for SBs.\n"); |
| } |
| list[i] = true; |
| ++i; |
| break; |
| case 'F': |
| case '0': |
| if (i >= len) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "CB or SB depopulation is too long. It should be (length) symbols for CBs and (length+1) symbols for SBs.\n"); |
| } |
| list[i] = false; |
| ++i; |
| break; |
| default: |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Invalid character %c in CB or SB depopulation list.\n", |
| *tmp); |
| } |
| ++tmp; |
| } |
| if (i < len) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "CB or SB depopulation is too short. It should be (length) symbols for CBs and (length+1) symbols for SBs.\n"); |
| } |
| |
| /* Free content string */ |
| ezxml_set_txt(Node, ""); |
| } |
| |
| else { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "'%s' is not a valid type for specifying cb and sb depopulation.\n", |
| tmp); |
| } |
| ezxml_set_attr(Node, "type", NULL); |
| } |
| |
| static void ProcessSwitches(INOUTP ezxml_t Parent, |
| OUTP struct s_arch_switch_inf **Switches, OUTP int *NumSwitches, |
| INP bool timing_enabled) { |
| int i, j; |
| const char *type_name; |
| const char *switch_name; |
| const char *buf_size; |
| |
| bool 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) = new s_arch_switch_inf[(*NumSwitches)]; |
| } |
| |
| /* 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)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Two switches with the same name '%s' were found.\n", |
| switch_name); |
| } |
| } |
| (*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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Invalid switch type '%s'.\n", type_name); |
| } |
| 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); |
| ProcessSwitchTdel(Node, timing_enabled, i, (*Switches)); |
| (*Switches)[i].buf_size = GetFloatProperty(Node, "buf_size", |
| has_buf_size, 0); |
| (*Switches)[i].mux_trans_size = GetFloatProperty(Node, "mux_trans_size", |
| false, 1); |
| |
| buf_size = FindProperty(Node, "power_buf_size", false); |
| if (buf_size == NULL) { |
| (*Switches)[i].power_buffer_type = POWER_BUFFER_TYPE_AUTO; |
| } else if (strcmp(buf_size, "auto") == 0) { |
| (*Switches)[i].power_buffer_type = POWER_BUFFER_TYPE_AUTO; |
| } else { |
| (*Switches)[i].power_buffer_type = POWER_BUFFER_TYPE_ABSOLUTE_SIZE; |
| (*Switches)[i].power_buffer_size = (float) atof(buf_size); |
| } |
| ezxml_set_attr(Node, "power_buf_size", NULL); |
| |
| /* Remove the switch element from parse tree */ |
| FreeNode(Node); |
| } |
| } |
| |
| /* Processes the switch delay. Switch delay can be specified in two ways. |
| First way: switch delay is specified as a constant via the property Tdel in the switch node. |
| Second way: switch delay is specified as a function of the switch fan-in. In this |
| case, multiple nodes in the form |
| |
| <Tdel num_inputs="1" delay="3e-11"/> |
| |
| are specified as children of the switch node. In this case, Tdel |
| is not included as a property of the switch node (first way). */ |
| static void ProcessSwitchTdel(INOUTP ezxml_t Node, INP bool timing_enabled, |
| INP int switch_index, OUTP s_arch_switch_inf *Switches){ |
| |
| float Tdel_prop_value; |
| int num_Tdel_children; |
| |
| /* check if switch node has the Tdel property */ |
| bool has_Tdel_prop = false; |
| Tdel_prop_value = GetFloatProperty(Node, "Tdel", false, UNDEFINED); |
| if (Tdel_prop_value != UNDEFINED){ |
| has_Tdel_prop = true; |
| } |
| |
| /* check if switch node has Tdel children */ |
| bool has_Tdel_children = false; |
| num_Tdel_children = CountChildren(Node, "Tdel", 0); |
| if (num_Tdel_children != 0){ |
| has_Tdel_children = true; |
| } |
| |
| /* delay should not be specified as a Tdel property AND a Tdel child */ |
| if (has_Tdel_prop && has_Tdel_children){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Switch delay should be specified as EITHER a Tdel property OR as a child of the switch node, not both"); |
| } |
| |
| /* get pointer to the switch's Tdel map, then read-in delay data into this map */ |
| std::map<int, double> *Tdel_map = &Switches[switch_index].Tdel_map; |
| if (has_Tdel_prop){ |
| /* delay specified as a constant */ |
| if (Tdel_map->count(UNDEFINED)){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "what the fuck"); |
| } else { |
| (*Tdel_map)[UNDEFINED] = Tdel_prop_value; |
| } |
| } else if (has_Tdel_children) { |
| /* Delay specified as a function of switch fan-in. |
| Go through each Tdel child, read-in num_inputs and the delay value. |
| Insert this info into the switch delay map */ |
| for (int ichild = 0; ichild < num_Tdel_children; ichild++){ |
| ezxml_t Tdel_child = ezxml_child(Node, "Tdel"); |
| |
| int num_inputs = GetIntProperty(Tdel_child, "num_inputs", true, 0); |
| float Tdel_value = GetFloatProperty(Tdel_child, "delay", true, 0.); |
| |
| if (Tdel_map->count( num_inputs ) ){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Tdel_child->line, |
| "Tdel node specified num_inputs (%d) that has already been specified by another Tdel node", num_inputs); |
| } else { |
| (*Tdel_map)[num_inputs] = Tdel_value; |
| } |
| |
| FreeNode(Tdel_child); |
| } |
| } else { |
| /* No delay info specified for switch */ |
| if (timing_enabled){ |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Switch should contain intrinsic delay information if timing is enabled"); |
| } else { |
| /* set a default value */ |
| (*Tdel_map)[UNDEFINED] = 0; |
| } |
| } |
| } |
| |
| static void ProcessDirects(INOUTP ezxml_t Parent, OUTP t_direct_inf **Directs, |
| OUTP int *NumDirects, INP struct s_arch_switch_inf *Switches, INP int NumSwitches, |
| INP bool timing_enabled) { |
| int i, j; |
| const char *direct_name; |
| const char *from_pin_name; |
| const char *to_pin_name; |
| const char *switch_name; |
| |
| ezxml_t Node; |
| |
| /* Count the children and check they are direct connections */ |
| *NumDirects = CountChildren(Parent, "direct", 1); |
| |
| /* Alloc direct list */ |
| *Directs = NULL; |
| if (*NumDirects > 0) { |
| *Directs = (t_direct_inf *) my_malloc( |
| *NumDirects * sizeof(t_direct_inf)); |
| memset(*Directs, 0, (*NumDirects * sizeof(t_direct_inf))); |
| } |
| |
| /* Load the directs. */ |
| for (i = 0; i < *NumDirects; ++i) { |
| Node = ezxml_child(Parent, "direct"); |
| |
| direct_name = FindProperty(Node, "name", true); |
| /* Check for direct name collisions */ |
| for (j = 0; j < i; ++j) { |
| if (0 == strcmp((*Directs)[j].name, direct_name)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Two directs with the same name '%s' were found.\n", |
| direct_name); |
| } |
| } |
| (*Directs)[i].name = my_strdup(direct_name); |
| ezxml_set_attr(Node, "name", NULL); |
| |
| /* Figure out the source pin and sink pin name */ |
| from_pin_name = FindProperty(Node, "from_pin", true); |
| to_pin_name = FindProperty(Node, "to_pin", true); |
| |
| /* Check that to_pin and the from_pin are not the same */ |
| if (0 == strcmp(to_pin_name, from_pin_name)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "The source pin and sink pin are the same: %s.\n", |
| to_pin_name); |
| } |
| (*Directs)[i].from_pin = my_strdup(from_pin_name); |
| (*Directs)[i].to_pin = my_strdup(to_pin_name); |
| ezxml_set_attr(Node, "from_pin", NULL); |
| ezxml_set_attr(Node, "to_pin", NULL); |
| |
| (*Directs)[i].x_offset = GetIntProperty(Node, "x_offset", true, 0); |
| (*Directs)[i].y_offset = GetIntProperty(Node, "y_offset", true, 0); |
| (*Directs)[i].z_offset = GetIntProperty(Node, "z_offset", true, 0); |
| ezxml_set_attr(Node, "x_offset", NULL); |
| ezxml_set_attr(Node, "y_offset", NULL); |
| ezxml_set_attr(Node, "z_offset", NULL); |
| |
| //Set the optional switch type |
| switch_name = FindProperty(Node, "switch_name", false); |
| if(switch_name != NULL) { |
| //Look-up the user defined switch |
| for(j = 0; j < NumSwitches; j++) { |
| if(0 == strcmp(switch_name, Switches[j].name)) { |
| break; //Found the switch |
| } |
| } |
| if(j >= NumSwitches) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "Could not find switch named '%s' in switch list.\n", switch_name); |
| |
| } |
| (*Directs)[i].switch_type = j; //Save the correct switch index |
| ezxml_set_attr(Node, "switch_name", NULL); |
| } else { |
| //If not defined, use the delayless switch by default |
| //TODO: find a better way of indicating this. Ideally, we would |
| //specify the delayless switch index here, but it does not appear |
| //to be defined at this point. |
| (*Directs)[i].switch_type = -1; |
| } |
| |
| /* Check that the direct chain connection is not zero in both direction */ |
| if ((*Directs)[i].x_offset == 0 && (*Directs)[i].y_offset == 0) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Node->line, |
| "The x_offset and y_offset are both zero, this is a length 0 direct chain connection.\n"); |
| } |
| |
| (*Directs)[i].line = Node->line; |
| /* Should I check that the direct chain offset is not greater than the chip? How? */ |
| |
| /* Remove the direct element from parse tree */ |
| FreeNode(Node); |
| } |
| } |
| |
| static void CreateModelLibrary(OUTP struct s_arch *arch) { |
| t_model* model_library; |
| |
| model_library = (t_model*) 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 = (t_model_ports*) 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 = (t_model_ports*) 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 = (t_model_ports*) 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 = (t_model_ports*) 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 = (t_model_ports*) 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 = (t_model_ports*) 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); |
| } |
| } |
| } |
| |
| 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; |
| |
| bool 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 { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "No matching model for pb_type %s\n", pb_type->blif_model); |
| } |
| |
| 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "No matching model port for port %s in pb_type %s\n", |
| pb_type->ports[p].name, pb_type->name); |
| } |
| } |
| } 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 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) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, 0, |
| "No pb_type found for model %s\n", cur_model->name); |
| } |
| 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; |
| |
| //Print all layout device switch/segment list info first |
| PrintArchInfo(Echo, arch); |
| |
| //Models |
| fprintf(Echo, "*************************************************\n"); |
| 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; |
| } |
| } |
| fprintf(Echo, "*************************************************\n\n"); |
| fprintf(Echo, "*************************************************\n"); |
| for (i = 0; i < NumTypes; ++i) { |
| fprintf(Echo, "Type: \"%s\"\n", Types[i].name); |
| fprintf(Echo, "\tcapacity: %d\n", Types[i].capacity); |
| fprintf(Echo, "\twidth: %d\n", Types[i].width); |
| fprintf(Echo, "\theight: %d\n", Types[i].height); |
| for (j = 0; j < Types[i].num_pins; j++) { |
| fprintf(Echo, "\tis_Fc_frac: \n"); |
| fprintf(Echo, "\t\tPin number %d: %s\n", j, |
| (Types[i].is_Fc_frac[j] ? "true" : "false")); |
| fprintf(Echo, "\tis_Fc_full_flex: \n"); |
| fprintf(Echo, "\t\tPin number %d: %s\n", j, |
| (Types[i].is_Fc_full_flex[j] ? "true" : "false")); |
| for (int iseg = 0; iseg < arch->num_segments; iseg++){ |
| fprintf(Echo, "\tPin: %d Segment: %d Fc: %f\n", j, iseg, Types[i].Fc[j][iseg]); |
| } |
| } |
| 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 = (char*) 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); |
| } |
| |
| if (pb_type->num_modes > 0) {/*one or more modes*/ |
| 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]); |
| } |
| //Print power info for interconnects |
| if (pb_type->modes[i].interconnect[j].interconnect_power) { |
| if (pb_type->modes[i].interconnect[j].interconnect_power->power_usage.dynamic |
| || pb_type->modes[i].interconnect[j].interconnect_power->power_usage.leakage) { |
| fprintf(Echo, "%s\t\t\tpower %e %e\n", tabs, |
| pb_type->modes[i].interconnect[j].interconnect_power->power_usage.dynamic, |
| pb_type->modes[i].interconnect[j].interconnect_power->power_usage.leakage); |
| } |
| } |
| } |
| } |
| } else {/*leaf pb with unknown model*/ |
| /*LUT(names) already handled, it naturally has 2 modes. |
| I/O has no annotations to be displayed |
| All other library or user models may have delays specificied, e.g. Tsetup and Tcq |
| Display the additional information*/ |
| if (strcmp(pb_type->model->name, "names") |
| && strcmp(pb_type->model->name, "input") |
| && strcmp(pb_type->model->name, "output")) { |
| for (k = 0; k < pb_type->num_annotations; k++) { |
| fprintf(Echo, "%s\t\t\tannotation %s %s %s %d: %s\n", tabs, |
| pb_type->annotations[k].clock, |
| pb_type->annotations[k].input_pins, |
| pb_type->annotations[k].output_pins, |
| pb_type->annotations[k].format, |
| pb_type->annotations[k].value[0]); |
| } |
| } |
| } |
| |
| if (pb_type->pb_type_power) { |
| PrintPb_types_recPower(Echo, pb_type, tabs); |
| } |
| free(tabs); |
| } |
| |
| //Added May 2013 Daniel Chen, help dump arch info after loading from XML |
| static void PrintPb_types_recPower(INP FILE * Echo, |
| INP const t_pb_type * pb_type, const char* tabs) { |
| |
| int i = 0; |
| /*Print power information for each pb if available*/ |
| switch (pb_type->pb_type_power->estimation_method) { |
| case POWER_METHOD_UNDEFINED: |
| fprintf(Echo, "%s\tpower method: undefined\n", tabs); |
| break; |
| case POWER_METHOD_IGNORE: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because of the inheritance property of auto-size*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_IGNORE) |
| break; |
| } |
| fprintf(Echo, "%s\tpower method: ignore\n", tabs); |
| break; |
| case POWER_METHOD_SUM_OF_CHILDREN: |
| fprintf(Echo, "%s\tpower method: sum-of-children\n", tabs); |
| break; |
| case POWER_METHOD_AUTO_SIZES: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because of the inheritance property of auto-size*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_AUTO_SIZES) |
| break; |
| } |
| fprintf(Echo, "%s\tpower method: auto-size\n", tabs); |
| break; |
| case POWER_METHOD_SPECIFY_SIZES: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because of the inheritance property of specify-size*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_SPECIFY_SIZES) |
| break; |
| } |
| |
| fprintf(Echo, "%s\tpower method: specify-size\n", tabs); |
| for (i = 0; i < pb_type->num_ports; i++) { |
| //Print all the power information on each port, only if available, |
| //will not print if value is 0 or NULL |
| if (pb_type->ports[i].port_power->buffer_type |
| || pb_type->ports[i].port_power->wire_type |
| || pb_type->pb_type_power->absolute_power_per_instance.leakage |
| || pb_type->pb_type_power->absolute_power_per_instance.dynamic) { |
| |
| fprintf(Echo, "%s\t\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); |
| //Buffer size |
| switch (pb_type->ports[i].port_power->buffer_type) { |
| case (POWER_BUFFER_TYPE_UNDEFINED): |
| case (POWER_BUFFER_TYPE_NONE): |
| break; |
| case (POWER_BUFFER_TYPE_AUTO): |
| fprintf(Echo, "%s\t\t\tbuffer_size %s\n", tabs, "auto"); |
| break; |
| case (POWER_BUFFER_TYPE_ABSOLUTE_SIZE): |
| fprintf(Echo, "%s\t\t\tbuffer_size %f\n", tabs, |
| pb_type->ports[i].port_power->buffer_size); |
| break; |
| default: |
| break; |
| } |
| switch (pb_type->ports[i].port_power->wire_type) { |
| case (POWER_WIRE_TYPE_UNDEFINED): |
| case (POWER_WIRE_TYPE_IGNORED): |
| break; |
| case (POWER_WIRE_TYPE_C): |
| fprintf(Echo, "%s\t\t\twire_cap: %e\n", tabs, |
| pb_type->ports[i].port_power->wire.C); |
| break; |
| case (POWER_WIRE_TYPE_ABSOLUTE_LENGTH): |
| fprintf(Echo, "%s\t\t\twire_len(abs): %e\n", tabs, |
| pb_type->ports[i].port_power->wire.absolute_length); |
| break; |
| case (POWER_WIRE_TYPE_RELATIVE_LENGTH): |
| fprintf(Echo, "%s\t\t\twire_len(rel): %f\n", tabs, |
| pb_type->ports[i].port_power->wire.relative_length); |
| break; |
| case (POWER_WIRE_TYPE_AUTO): |
| fprintf(Echo, "%s\t\t\twire_len: %s\n", tabs, "auto"); |
| break; |
| default: |
| break; |
| } |
| |
| } |
| } |
| //Output static power even if non zero |
| if (pb_type->pb_type_power->absolute_power_per_instance.leakage) |
| fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.leakage); |
| |
| if (pb_type->pb_type_power->absolute_power_per_instance.dynamic) |
| fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.dynamic); |
| break; |
| case POWER_METHOD_TOGGLE_PINS: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because once energy_per_toggle is specified at one level, |
| all children pb's are energy_per_toggle and only want to display once*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_TOGGLE_PINS) |
| break; |
| } |
| |
| fprintf(Echo, "%s\tpower method: pin-toggle\n", tabs); |
| for (i = 0; i < pb_type->num_ports; i++) { |
| /*Print all the power information on each port, only if available, |
| will not print if value is 0 or NULL*/ |
| if (pb_type->ports[i].port_power->energy_per_toggle |
| || pb_type->ports[i].port_power->scaled_by_port |
| || pb_type->pb_type_power->absolute_power_per_instance.leakage |
| || pb_type->pb_type_power->absolute_power_per_instance.dynamic) { |
| |
| fprintf(Echo, "%s\t\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); |
| //Toggle Energy |
| if (pb_type->ports[i].port_power->energy_per_toggle) { |
| fprintf(Echo, "%s\t\t\tenergy_per_toggle %e\n", tabs, |
| pb_type->ports[i].port_power->energy_per_toggle); |
| } |
| //Scaled by port (could be reversed) |
| if (pb_type->ports[i].port_power->scaled_by_port) { |
| if (pb_type->ports[i].port_power->scaled_by_port->num_pins |
| > 1) { |
| fprintf(Echo, |
| (pb_type->ports[i].port_power->reverse_scaled ? |
| "%s\t\t\tscaled_by_static_prob_n: %s[%d]\n" : |
| "%s\t\t\tscaled_by_static_prob: %s[%d]\n"), |
| tabs, |
| pb_type->ports[i].port_power->scaled_by_port->name, |
| pb_type->ports[i].port_power->scaled_by_port_pin_idx); |
| } else { |
| fprintf(Echo, |
| (pb_type->ports[i].port_power->reverse_scaled ? |
| "%s\t\t\tscaled_by_static_prob_n: %s\n" : |
| "%s\t\t\tscaled_by_static_prob: %s\n"), |
| tabs, |
| pb_type->ports[i].port_power->scaled_by_port->name); |
| } |
| } |
| } |
| } |
| //Output static power even if non zero |
| if (pb_type->pb_type_power->absolute_power_per_instance.leakage) |
| fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.leakage); |
| |
| if (pb_type->pb_type_power->absolute_power_per_instance.dynamic) |
| fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.dynamic); |
| |
| break; |
| case POWER_METHOD_C_INTERNAL: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because of values at this level includes all children pb's*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_C_INTERNAL) |
| break; |
| } |
| fprintf(Echo, "%s\tpower method: C-internal\n", tabs); |
| |
| if (pb_type->pb_type_power->absolute_power_per_instance.leakage) |
| fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.leakage); |
| |
| if (pb_type->pb_type_power->C_internal) |
| fprintf(Echo, "%s\t\tdynamic c-internal: %e \n", tabs, |
| pb_type->pb_type_power->C_internal); |
| break; |
| case POWER_METHOD_ABSOLUTE: |
| if (pb_type->parent_mode) { |
| /*if NOT top-level pb (all top-level pb has NULL parent_mode, check parent's power method |
| This is because of values at this level includes all children pb's*/ |
| if (pb_type->parent_mode->parent_pb_type->pb_type_power->estimation_method |
| == POWER_METHOD_ABSOLUTE) |
| break; |
| } |
| fprintf(Echo, "%s\tpower method: absolute\n", tabs); |
| if (pb_type->pb_type_power->absolute_power_per_instance.leakage) |
| fprintf(Echo, "%s\t\tstatic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.leakage); |
| |
| if (pb_type->pb_type_power->absolute_power_per_instance.dynamic) |
| fprintf(Echo, "%s\t\tdynamic power_per_instance: %e \n", tabs, |
| pb_type->pb_type_power->absolute_power_per_instance.dynamic); |
| break; |
| default: |
| fprintf(Echo, "%s\tpower method: error has occcured\n", tabs); |
| break; |
| } |
| } |
| //Added May 2013 Daniel Chen, help dump arch info after loading from XML |
| static void PrintArchInfo(INP FILE * Echo, struct s_arch *arch) { |
| int i, j; |
| |
| fprintf(Echo, "Printing architecture... \n\n"); |
| //Layout |
| fprintf(Echo, "*************************************************\n"); |
| if (arch->clb_grid.IsAuto) { |
| fprintf(Echo, "Layout: auto %f\n", arch->clb_grid.Aspect); |
| } else { |
| fprintf(Echo, "Layout: width %d height %d\n", arch->clb_grid.W, |
| arch->clb_grid.H); |
| } |
| fprintf(Echo, "*************************************************\n\n"); |
| //Device |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Device Info:\n"); |
| |
| fprintf(Echo, |
| "\tSizing: R_minW_nmos %e R_minW_pmos %e ipin_mux_trans_size %e\n", |
| arch->R_minW_nmos, arch->R_minW_pmos, arch->ipin_mux_trans_size); |
| |
| fprintf(Echo, "\tTiming: C_ipin_cblock %e T_ipin_cblock %e\n", |
| arch->C_ipin_cblock, arch->T_ipin_cblock); |
| |
| fprintf(Echo, "\tArea: grid_logic_tile_area %e\n", |
| arch->grid_logic_tile_area); |
| |
| fprintf(Echo, "\tChannel Width Distribution:\n"); |
| fprintf(Echo, "\t\tio: width %e\n", arch->Chans.chan_width_io); |
| |
| switch (arch->Chans.chan_x_dist.type) { |
| case (UNIFORM): |
| fprintf(Echo, "\t\tx: type uniform peak %e\n", |
| arch->Chans.chan_x_dist.peak); |
| break; |
| case (GAUSSIAN): |
| fprintf(Echo, |
| "\t\tx: type gaussian peak %e \ |
| width %e Xpeak %e dc %e\n", |
| arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.width, |
| arch->Chans.chan_x_dist.xpeak, arch->Chans.chan_x_dist.dc); |
| break; |
| case (PULSE): |
| fprintf(Echo, |
| "\t\tx: type pulse peak %e \ |
| width %e Xpeak %e dc %e\n", |
| arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.width, |
| arch->Chans.chan_x_dist.xpeak, arch->Chans.chan_x_dist.dc); |
| break; |
| case (DELTA): |
| fprintf(Echo, "\t\tx: distr dleta peak %e \ |
| Xpeak %e dc %e\n", |
| arch->Chans.chan_x_dist.peak, arch->Chans.chan_x_dist.xpeak, |
| arch->Chans.chan_x_dist.dc); |
| break; |
| default: |
| fprintf(Echo, "\t\tInvalid Distribution!\n"); |
| break; |
| } |
| |
| switch (arch->Chans.chan_y_dist.type) { |
| case (UNIFORM): |
| fprintf(Echo, "\t\ty: type uniform peak %e\n", |
| arch->Chans.chan_y_dist.peak); |
| break; |
| case (GAUSSIAN): |
| fprintf(Echo, |
| "\t\ty: type gaussian peak %e \ |
| width %e Xpeak %e dc %e\n", |
| arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.width, |
| arch->Chans.chan_y_dist.xpeak, arch->Chans.chan_y_dist.dc); |
| break; |
| case (PULSE): |
| fprintf(Echo, |
| "\t\ty: type pulse peak %e \ |
| width %e Xpeak %e dc %e\n", |
| arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.width, |
| arch->Chans.chan_y_dist.xpeak, arch->Chans.chan_y_dist.dc); |
| break; |
| case (DELTA): |
| fprintf(Echo, "\t\ty: distr dleta peak %e \ |
| Xpeak %e dc %e\n", |
| arch->Chans.chan_y_dist.peak, arch->Chans.chan_y_dist.xpeak, |
| arch->Chans.chan_y_dist.dc); |
| break; |
| default: |
| fprintf(Echo, "\t\tInvalid Distribution!\n"); |
| break; |
| } |
| |
| switch (arch->SBType) { |
| case (WILTON): |
| fprintf(Echo, "\tSwitch Block: type wilton fs %d\n", arch->Fs); |
| break; |
| case (UNIVERSAL): |
| fprintf(Echo, "\tSwitch Block: type universal fs %d\n", arch->Fs); |
| break; |
| case (SUBSET): |
| fprintf(Echo, "\tSwitch Block: type subset fs %d\n", arch->Fs); |
| break; |
| default: |
| break; |
| } |
| fprintf(Echo, "*************************************************\n\n"); |
| //Switch list |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Switch List:\n"); |
| |
| //13 is hard coded because format of %e is always 1.123456e+12 |
| //It always consists of 10 alphanumeric digits, a decimal |
| //and a sign |
| for (i = 0; i < arch->num_switches; i++) { |
| |
| if (arch->Switches[i].buffered) { |
| fprintf(Echo, "\tSwitch[%d]: name %s type mux/buffer\n", i + 1, |
| arch->Switches[i].name); |
| } else { |
| fprintf(Echo, "\tSwitch[%d]: name %s type pass_trans\n", i + 1, |
| arch->Switches[i].name); |
| } |
| fprintf(Echo, "\t\t\t\tR %e Cin %e Cout %e\n", arch->Switches[i].R, |
| arch->Switches[i].Cin, arch->Switches[i].Cout); |
| fprintf(Echo, "\t\t\t\t#Tdel values %d buf_size %e mux_trans_size %e\n", |
| (int)arch->Switches[i].Tdel_map.size(), arch->Switches[i].buf_size, |
| arch->Switches[i].mux_trans_size); |
| if (arch->Switches[i].power_buffer_type == POWER_BUFFER_TYPE_AUTO) { |
| fprintf(Echo, "\t\t\t\tpower_buffer_size auto\n"); |
| } else { |
| fprintf(Echo, "\t\t\t\tpower_buffer_size %e\n", |
| arch->Switches[i].power_buffer_size); |
| } |
| } |
| |
| fprintf(Echo, "*************************************************\n\n"); |
| //Segment List |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Segment List:\n"); |
| for (i = 0; i < arch->num_segments; i++) { |
| fprintf(Echo, |
| "\tSegment[%d]: frequency %d length %d R_metal %e C_metal %e\n", |
| i + 1, arch->Segments[i].frequency, arch->Segments[i].length, |
| arch->Segments[i].Rmetal, arch->Segments[i].Cmetal); |
| |
| if (arch->Segments[i].directionality == UNI_DIRECTIONAL) { |
| //wire_switch == arch_opin_switch |
| fprintf(Echo, "\t\t\t\ttype unidir mux_name %s\n", |
| arch->Switches[arch->Segments[i].arch_wire_switch].name); |
| } else { //Should be bidir |
| fprintf(Echo, "\t\t\t\ttype bidir wire_switch %s arch_opin_switch %s\n", |
| arch->Switches[arch->Segments[i].arch_wire_switch].name, |
| arch->Switches[arch->Segments[i].arch_opin_switch].name); |
| } |
| |
| fprintf(Echo, "\t\t\t\tcb "); |
| for (j = 0; j < arch->Segments->cb_len; j++) { |
| if (arch->Segments->cb[j]) { |
| fprintf(Echo, "1 "); |
| } else { |
| fprintf(Echo, "0 "); |
| } |
| } |
| fprintf(Echo, "\n"); |
| |
| fprintf(Echo, "\t\t\t\tsb "); |
| for (j = 0; j < arch->Segments->sb_len; j++) { |
| if (arch->Segments->sb[j]) { |
| fprintf(Echo, "1 "); |
| } else { |
| fprintf(Echo, "0 "); |
| } |
| } |
| fprintf(Echo, "\n"); |
| } |
| fprintf(Echo, "*************************************************\n\n"); |
| //Direct List |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Direct List:\n"); |
| for (i = 0; i < arch->num_directs; i++) { |
| fprintf(Echo, "\tDirect[%d]: name %s from_pin %s to_pin %s\n", i + 1, |
| arch->Directs[i].name, arch->Directs[i].from_pin, |
| arch->Directs[i].to_pin); |
| fprintf(Echo, "\t\t\t\t x_offset %d y_offset %d z_offset %d\n", |
| arch->Directs[i].x_offset, arch->Directs[i].y_offset, |
| arch->Directs[i].z_offset); |
| } |
| fprintf(Echo, "*************************************************\n\n"); |
| |
| //Architecture Power |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Power:\n"); |
| if (arch->power) { |
| fprintf(Echo, "\tlocal_interconnect C_wire %e factor %f\n", |
| arch->power->C_wire_local, arch->power->local_interc_factor); |
| fprintf(Echo, "\tlogical_effort_factor %f trans_per_sram_bit %f\n", |
| arch->power->logical_effort_factor, |
| arch->power->transistors_per_SRAM_bit); |
| |
| } |
| |
| fprintf(Echo, "*************************************************\n\n"); |
| //Architecture Clock |
| fprintf(Echo, "*************************************************\n"); |
| fprintf(Echo, "Clock:\n"); |
| if (arch->clocks) { |
| for (i = 0; i < arch->clocks->num_global_clocks; i++) { |
| if (arch->clocks->clock_inf[i].autosize_buffer) { |
| fprintf(Echo, "\tClock[%d]: buffer_size auto C_wire %e", i + 1, |
| arch->clocks->clock_inf->C_wire); |
| } else { |
| fprintf(Echo, "\tClock[%d]: buffer_size %e C_wire %e", i + 1, |
| arch->clocks->clock_inf[i].buffer_size, |
| arch->clocks->clock_inf[i].C_wire); |
| } |
| fprintf(Echo, "\t\t\t\tstat_prob %f switch_density %f period %e", |
| arch->clocks->clock_inf[i].prob, |
| arch->clocks->clock_inf[i].dens, |
| arch->clocks->clock_inf[i].period); |
| } |
| } |
| |
| fprintf(Echo, "*************************************************\n\n"); |
| } |
| static void ProcessPower( INOUTP ezxml_t parent, |
| INOUTP t_power_arch * power_arch, INP t_type_descriptor * Types, |
| INP int NumTypes) { |
| ezxml_t Cur; |
| |
| /* Get the local interconnect capacitances */ |
| power_arch->local_interc_factor = 0.5; |
| Cur = FindElement(parent, "local_interconnect", false); |
| if (Cur) { |
| power_arch->C_wire_local = GetFloatProperty(Cur, "C_wire", false, 0.); |
| power_arch->local_interc_factor = GetFloatProperty(Cur, "factor", false, |
| 0.5); |
| FreeNode(Cur); |
| } |
| |
| /* Get segment split */ |
| /* |
| power_arch->seg_buffer_split = 1; |
| Cur = FindElement(parent, "segment_buffer_split", false); |
| if (Cur) { |
| power_arch->seg_buffer_split = GetIntProperty(Cur, "split_into", true, |
| 1); |
| FreeNode(Cur); |
| }*/ |
| |
| /* Get logical effort factor */ |
| power_arch->logical_effort_factor = 4.0; |
| Cur = FindElement(parent, "buffers", false); |
| if (Cur) { |
| power_arch->logical_effort_factor = GetFloatProperty(Cur, |
| "logical_effort_factor", true, 0); |
| FreeNode(Cur); |
| } |
| |
| /* Get SRAM Size */ |
| power_arch->transistors_per_SRAM_bit = 6.0; |
| Cur = FindElement(parent, "sram", false); |
| if (Cur) { |
| power_arch->transistors_per_SRAM_bit = GetFloatProperty(Cur, |
| "transistors_per_bit", true, 0); |
| FreeNode(Cur); |
| } |
| |
| /* Get Mux transistor size */ |
| power_arch->mux_transistor_size = 1.0; |
| Cur = FindElement(parent, "mux_transistor_size", false); |
| if (Cur) { |
| power_arch->mux_transistor_size = GetFloatProperty(Cur, |
| "mux_transistor_size", true, 0); |
| FreeNode(Cur); |
| } |
| |
| /* Get FF size */ |
| power_arch->FF_size = 1.0; |
| Cur = FindElement(parent, "FF_size", false); |
| if (Cur) { |
| power_arch->FF_size = GetFloatProperty(Cur, "FF_size", true, 0); |
| FreeNode(Cur); |
| } |
| |
| /* Get LUT transistor size */ |
| power_arch->LUT_transistor_size = 1.0; |
| Cur = FindElement(parent, "LUT_transistor_size", false); |
| if (Cur) { |
| power_arch->LUT_transistor_size = GetFloatProperty(Cur, |
| "LUT_transistor_size", true, 0); |
| FreeNode(Cur); |
| } |
| } |
| |
| /* Get the clock architcture */ |
| static void ProcessClocks(ezxml_t Parent, t_clock_arch * clocks) { |
| ezxml_t Node; |
| int i; |
| const char *tmp; |
| |
| clocks->num_global_clocks = CountChildren(Parent, "clock", 0); |
| |
| /* Alloc the clockdetails */ |
| clocks->clock_inf = NULL; |
| if (clocks->num_global_clocks > 0) { |
| clocks->clock_inf = (t_clock_network *) my_malloc( |
| clocks->num_global_clocks * sizeof(t_clock_network)); |
| memset(clocks->clock_inf, 0, |
| clocks->num_global_clocks * sizeof(t_clock_network)); |
| } |
| |
| /* Load the clock info. */ |
| for (i = 0; i < clocks->num_global_clocks; ++i) { |
| /* get the next clock item */ |
| Node = ezxml_child(Parent, "clock"); |
| |
| tmp = FindProperty(Node, "buffer_size", true); |
| if (strcmp(tmp, "auto") == 0) { |
| clocks->clock_inf[i].autosize_buffer = true; |
| } else { |
| clocks->clock_inf[i].autosize_buffer = false; |
| clocks->clock_inf[i].buffer_size = (float) atof(tmp); |
| } |
| ezxml_set_attr(Node, "buffer_size", NULL); |
| |
| clocks->clock_inf[i].C_wire = GetFloatProperty(Node, "C_wire", true, 0); |
| FreeNode(Node); |
| } |
| } |
| |
| e_power_estimation_method power_method_inherited( |
| e_power_estimation_method parent_power_method) { |
| switch (parent_power_method) { |
| case POWER_METHOD_IGNORE: |
| case POWER_METHOD_AUTO_SIZES: |
| case POWER_METHOD_SPECIFY_SIZES: |
| case POWER_METHOD_TOGGLE_PINS: |
| return parent_power_method; |
| case POWER_METHOD_C_INTERNAL: |
| case POWER_METHOD_ABSOLUTE: |
| return POWER_METHOD_IGNORE; |
| case POWER_METHOD_UNDEFINED: |
| return POWER_METHOD_UNDEFINED; |
| case POWER_METHOD_SUM_OF_CHILDREN: |
| /* Just revert to the default */ |
| return POWER_METHOD_AUTO_SIZES; |
| default: |
| assert(0); |
| return POWER_METHOD_UNDEFINED; // Should never get here, but avoids a compiler warning. |
| } |
| } |
| |
| /* Date:June 28th, 2013 |
| * Author: Daniel Chen |
| * Purpose: Checks for correctly grouped XML tag |
| * ordering, vpr_throws if incorrect |
| * Note: Used this because there is a |
| * limitation in ezxml_cut in the ezxml library |
| * which seg faults with incorrectly |
| * grouped tags |
| */ |
| static void CheckXMLTagOrder(ezxml_t Parent) { |
| |
| int i, num_tags; |
| ezxml_t Cur, Trace; |
| char* tag_name; |
| i = num_tags = 0; |
| |
| /* Start with first subtag */ |
| Cur = Parent->child; |
| |
| while (Cur) { |
| tag_name = my_strdup(Cur->name); |
| num_tags = CountChildren(Parent, tag_name, 0); |
| Trace = Cur->ordered; |
| for (i = 1; i < num_tags; i++) { //Check the next num_tags-1 tags see if grouped |
| if (Trace) { |
| if (strcmp(Trace->name, tag_name)) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, Trace->line, |
| "XML tags of type '%s' must be specified right after/before each other\n", |
| tag_name); |
| } |
| Trace = Trace->ordered; |
| } |
| } |
| Cur = Cur->sibling; // Go to next tag with a different name on the same level |
| free(tag_name); // Free the copied tag name |
| } |
| } |
| |
| /* Date:July 10th, 2013 |
| * Author: Daniel Chen |
| * Purpose: Attempts to match a clock_name specified in an |
| * timing annotation (Tsetup, Thold, Tc_to_q) with the |
| * clock_name specified in the primitive. Applies |
| * to flipflop/memory right now. |
| */ |
| static void primitives_annotation_clock_match( |
| t_pin_to_pin_annotation *annotation, t_pb_type * parent_pb_type) { |
| |
| int i_port; |
| bool clock_valid = false; //Determine if annotation's clock is same as primtive's clock |
| |
| if (!parent_pb_type || !annotation) { |
| vpr_throw(VPR_ERROR_OTHER, __FILE__, __LINE__, |
| "Annotation_clock check encouters invalid annotation or primitive.\n"); |
| } |
| |
| for (i_port = 0; i_port < parent_pb_type->num_ports; i_port++) { |
| if (parent_pb_type->ports[i_port].is_clock) { |
| if (strcmp(parent_pb_type->ports[i_port].name, annotation->clock) |
| == 0) { |
| clock_valid = true; |
| break; |
| } |
| } |
| } |
| |
| if (!clock_valid) { |
| vpr_throw(VPR_ERROR_ARCH, arch_file_name, annotation->line_num, |
| "Clock '%s' does not match any clock defined in pb_type '%s'.\n", |
| annotation->clock, parent_pb_type->name); |
| } |
| |
| } |
| |
| /* Used by functions outside read_xml_util.c to gain access to arch filename */ |
| const char* get_arch_file_name() { |
| return arch_file_name; |
| } |
| |