def parse_repeat(action, upper_level_binding): """ evaluates the values of repeat of a sub-component action - default value of repeat is 1 - string bindings are allowed, and bindings can be from: 1. compound attributes 2. compound action arguments (its own upper-level action) - arithemtic operations are allowed in specifying repeat value """ if 'repeat' in action and action['repeat'] is not None: if type(action['repeat']) is not int: op_type, op1, op2 = parse_expression_for_arithmetic( action['repeat'], upper_level_binding) if op_type is not None: parsed_repeat = process_arithmetic(op1, op2, op_type) else: if action['repeat'] in upper_level_binding: parsed_repeat = upper_level_binding[action['repeat']] else: parsed_repeat = None ERROR_CLEAN_EXIT( 'repeat value for primitive action cannot be parsed, ', 'no binding found in compound arguments/ attributes', action) return parsed_repeat # return the actual value if repeat is an integer return action['repeat'] # default repeat value is 1 return 1
def v02_is_component_list(name, binding_dictionary=None): """ determines if the component is a list according to its name if not, return 0; if yes, return list length it is possible that the last index of the list involves direct binding or arithmetical operations (operands could be strings that are keys in binding dictionary) """ start_idx = name.find('[') if start_idx == -1: return 0, None else: if ']' not in name: WARN(name, ': located [ but not ], typo?') else: name_base = name[:start_idx] end_idx = name.find(']') n = name[start_idx + 1:end_idx] # check if the tail involves arithmetic operations optype, op1, op2 = parse_expression_for_arithmetic( n, binding_dictionary) if optype is None: if n in binding_dictionary: # tail is a direct binding, directly retrieve the numerical value n = binding_dictionary[n] list_length = int(n) else: list_length = int(process_arithmetic(op1, op2, optype)) return list_length, name_base
def map_arg_range_bounds(self, arg_range_str, attributes_dict): """ arguments for compound actions might have ranges that are specified in terms of it attributes parses the argument ranges in the format int/str..int/str, where str can be arithmetic operation :param arg_range_str: string that decribes the range of a compound action :param attributes_dict: attribute name-value pairs of the compound component :return: parsed argument range, whether there was binding """ split_sub_string = arg_range_str.split('..') detect_arg_range_binding = False # process the start index try: start_idx = int(split_sub_string[0]) except ValueError: op_type, op1, op2 = parse_expression_for_arithmetic( split_sub_string[0], attributes_dict) if op_type is not None: start_idx = process_arithmetic(op1, op2, op_type) else: if split_sub_string[0] not in attributes_dict: ERROR_CLEAN_EXIT('cannot find mapping from', arg_range_str, 'to', attributes_dict) start_idx = attributes_dict[split_sub_string[0]] detect_arg_range_binding = True # process the end index try: end_idx = int(split_sub_string[1]) except ValueError: op_type, op1, op2 = parse_expression_for_arithmetic( split_sub_string[1], attributes_dict) if op_type is not None: end_idx = process_arithmetic(op1, op2, op_type) else: if split_sub_string[1] not in attributes_dict: ERROR_CLEAN_EXIT('cannot find mapping from', arg_range_str, 'to', attributes_dict) end_idx = attributes_dict[split_sub_string[1]] detect_arg_range_binding = True new_arg_range_str = str(start_idx) + '..' + str(end_idx) return new_arg_range_str, detect_arg_range_binding
def eval_subcomponent_action_for_ERT(self, subaction, subcomponent_info, upper_level_arguments, upper_level_attributes): subaction_copy = deepcopy( subaction) # do not want to modify the class definitions aggregated_dict = deepcopy(upper_level_attributes) if upper_level_arguments is not None: aggregated_dict.update(deepcopy(upper_level_arguments)) if 'arguments' in subaction and subaction_copy[ 'arguments'] is not None: # if there is arguments, evaluate the arguments in terms of the compound action arguments for subarg_name, subarg_info in subaction_copy['arguments'].items( ): if type(subarg_info) is str: try: subaction_copy['arguments'][ subarg_name] = aggregated_dict[subarg_info] except KeyError: op_type, op1, op2 = parse_expression_for_arithmetic( subarg_info, aggregated_dict) if op_type is not None: subaction_copy['arguments'][ subarg_name] = process_arithmetic( op1, op2, op_type) else: print( 'available compound arguments and attributes: ', aggregated_dict) print('primitive argument to for binding:', subarg_info) ERROR_CLEAN_EXIT( 'subcomponent argument name cannot be ', 'mapped to upper class arguments', subarg_info) subcomponent_info['actions'] = [subaction_copy] is_subcomponent_primitive_class = self.is_primitive_class( subcomponent_info['class']) subaction_ERT = self.generate_component_ert( subcomponent_info, is_subcomponent_primitive_class) # the call is guaranteed to produce an ERT with 'energy' and 'argument' key subaction_energy = subaction_ERT[subaction_copy['name']]['energy'] if type(subaction_energy) is not int and type( subaction_energy) is not float: ERROR_CLEAN_EXIT('Unusual estimated energy received for:', subcomponent_info, 'Energy received: %s' % subaction_energy) # parse the repeat information of the subaction (if any) # repeat can be int # binding to compound component arguments # binding to compound component attributes upper_level_binding = deepcopy(upper_level_attributes) if upper_level_arguments is not None: upper_level_binding.update(upper_level_arguments) parsed_repeat_info = EnergyReferenceTableGenerator.parse_repeat( subaction, upper_level_binding) subaction_energy *= parsed_repeat_info # print(subaction_ERT, parsed_repeat_info, subaction_energy) return round(subaction_energy, self.decimal_place)
def str_to_int(str_to_be_parsed, binding_dictionary): optype, op1, op2 = parse_expression_for_arithmetic(str_to_be_parsed, binding_dictionary) if optype is None: parsed_int = binding_dictionary[str_to_be_parsed] if str_to_be_parsed in binding_dictionary \ else int(str_to_be_parsed) else: parsed_int = int(process_arithmetic(op1, op2, optype)) return parsed_int
def v02_compound_component_constructor(self, compound_component_info): """ given the physical information of a compound component, the compound component object is constructed according to its compound classes, all compound properties are fully resolved and subcomponent definitions are included 1. compound attributes are all assigned with values 2. compound action argument ranges are all assigned values/ compound action arguments are static values 3. subcomponent attributes values are assigned, as they only depend on compound attributes - subcomponent attributes can be: 1. numbers 2. string bindings to compound component attributes 3. arithmetic operations that contain string bindings and numbers 3. subcomponent definition of the compound actions are included :param compound_component_info: physical specification of a compound component :return: fully defined compound component """ compound_component_name = compound_component_info['name'] compound_class_info = self.compound_class_description[ compound_component_info['class']] compound_component_definition = deepcopy( compound_class_info) # a copy of default class definition compound_component_definition['class'] = compound_component_info['class'] compound_component_definition['attributes'] = deepcopy( compound_component_info['attributes']) # fully defined attribute values compound_attributes = compound_component_definition['attributes'] # process subcomponent name format # if subcomponent is a list, expand the list of subcomponents (list tail index can be arithmetic operantions) # else keep the subcomponent name subcomponents = deepcopy(compound_component_definition['subcomponents']) # check if any sub-compound-component attribute is not specified in the top-level, apply defualt value specified # in the class definition list_of_new_components = [] list_of_to_remove_components = [] for subcomponent_idx in range(len(subcomponents)): subcomponent = subcomponents[subcomponent_idx] list_length, subcomponent_name_base = v02_is_component_list( subcomponent['name'], compound_attributes) if subcomponent_name_base is not None: list_of_to_remove_components.append(subcomponent) # INFO('list component name: ', subcomponent['name'], 'detected in compound class: ', compound_component_info['class']) for i in range(list_length): new_component = deepcopy(subcomponent) new_component['name'] = subcomponent_name_base + '[' + str( i) + ']' list_of_new_components.append(new_component) for comp in list_of_to_remove_components: subcomponents.remove(comp) for comp in list_of_new_components: subcomponents.append(comp) # process the subcomponent attribute values # subcomponent attributes can be: # 1. numbers # 2. string bindings to compound component attributes # 3. arithmetic operations that contain string bindings and numbers compound_component_definition['subcomponents'] = {} for subcomponent in subcomponents: subcomponent_name = subcomponent['name'] sub_component_attributes = subcomponent['attributes'] for sub_attr_name, sub_attr_val in sub_component_attributes.items(): if type(sub_attr_val) is str: # subcomponent attributes can be computed in terms of upper-level compound attributes op_type, op1, op2 = parse_expression_for_arithmetic( sub_attr_val, compound_attributes) if op_type is not None: sub_component_attributes[ sub_attr_name] = process_arithmetic(op1, op2, op_type) # INFO(compound_component_name, 'sub-attribute', sub_attr_name, 'processed as arithmetic operation') else: try: sub_component_attributes[ sub_attr_name] = compound_attributes[sub_attr_val] # INFO(compound_component_name, 'sub-attribute', sub_attr_name,'processed as binding') except KeyError: ERROR_CLEAN_EXIT( 'cannot find bindings from upper-level attribute names', '{', sub_attr_name, ':', sub_attr_val, '}') # process default sub-component-component attribute values that are not specified in the top-level # default values can be : # (1) numerical values # (2) arithmetic operations of other sub-compound-component attribute values sub_class = subcomponent['class'] if sub_class in self.compound_class_description: sub_class_description = deepcopy( self.compound_class_description[sub_class]) for attr_name, default_attr_val in sub_class_description[ 'attributes'].items(): if attr_name not in subcomponent['attributes']: if type(default_attr_val) is str: op_type, op1, op2 = parse_expression_for_arithmetic( default_attr_val, subcomponent['attributes']) if op_type is not None: default_attr_val = process_arithmetic( op1, op2, op_type) # INFO(compound_component_name, 'sub-attribute', sub_attr_name, 'processed as arithmetic operation') else: try: default_attr_val = subcomponent['attributes'][ default_attr_val] # INFO(compound_component_name, 'sub-attribute', sub_attr_name,'processed as binding') except KeyError: WARN( 'did not find bindings of the specified default attribute value for class: ', sub_class, '---> {', attr_name, ':', default_attr_val, '}, ' ' Keep the original specifications') subcomponent['attributes'][attr_name] = default_attr_val compound_component_definition['subcomponents'][ subcomponent_name] = subcomponent # check if the subcomponent name is a list # list_length, name_base = v02_is_component_list(subcomponent_name, compound_attributes) # if subcomponent_name == 'Y_memory_controller[0..total_PEs-1]': # print('----------------------', list_length, name_base) # if name_base is None: # compound_component_definition['subcomponents'][subcomponent_name] = subcomponent # else: # for item_idx in range(list_length): # new_subcomponent_name = name_base + '[' + str(item_idx) + ']' # compound_component_definition['subcomponents'][new_subcomponent_name] = deepcopy(subcomponent) # top-level compound component will not have 'actions' specified in the component info # because accelergy needs to generate the energy values for all possible actions (and arguments) # the actions in the component class description is therefore processed # action arguments can be: # 1. numbers # 2. string bindings to compound attributes (its own attributes) # action arguments cannot be arithmetic operations if 'actions' not in compound_component_info: # if there is no actions specified in the compound component info compound_actions = compound_component_definition['actions'] for c_action in compound_actions: c_action_name = c_action['name'] if 'arguments' in c_action: c_action_args = c_action['arguments'] for c_action_arg_name, c_action_arg_range in c_action_args.items( ): c_action_args[c_action_arg_name], detect_arg_range_binding = \ self.map_arg_range_bounds(c_action_arg_range, compound_attributes) if detect_arg_range_binding: INFO(compound_component_name, 'action:', c_action_name, 'arg:', c_action_arg_name, 'range interpreted as:', c_action_args[c_action_arg_name]) # low-level compound components will have 'actions' assigned, since top-level action will be interpreted as # one or more defined low-level compound action # no change should be added as the action arguments should be defined already, so the required action list # from component info is copied, with the definition of the action retrieved from class description else: compound_component_definition['actions'] = deepcopy( compound_component_info['actions']) for action in compound_component_definition['actions']: action_name = action['name'] for class_action_def in compound_class_info['actions']: if class_action_def['name'] == action_name: action['subcomponents'] = deepcopy( class_action_def['subcomponents']) return compound_component_definition
def construct_new_leaf_node_description(self, full_name, leaf_description, shared_attr_dict): """ Interprets the architecture component 1. apply the default values specified in the compound and primitive classes 2. apply shared attributes projected from upper hierarchy 3. check if there is any arithmetic expressions in the attribute bindings have to be amongst the attribute names 4. updates the architecture description data structure """ # ERROR: duplicate names in architecture if full_name in self.architecture_description: ERROR_CLEAN_EXIT( 'flattened name: ', full_name, ' is already in architecture, check duplicated names') class_name = leaf_description['class'] new_component_description = {'class': class_name} # apply the default values specified in the compound and primitive classes if not self.is_primitive_class( class_name ) and not class_name in self.compound_class_description: ERROR_CLEAN_EXIT('cannot find the class specified:', class_name) if not self.is_primitive_class(class_name): new_component_description['attributes'] = deepcopy( self.compound_class_description[class_name]['attributes']) else: new_component_description['attributes'] = deepcopy( self.primitive_class_description[class_name]['attributes']) if 'attributes' in leaf_description: new_component_description['attributes'].update( leaf_description['attributes']) # apply shared attributes projected from upper hierarchy # if redefined at the node, the attribute value will not be projected if shared_attr_dict is not None: for name, value in shared_attr_dict.items(): if 'attributes' not in leaf_description or \ name not in leaf_description['attributes'] : new_component_description['attributes'].update( {name: value}) # Info: projected shared attributes overridden if 'attributes' in leaf_description and \ name in leaf_description['attributes'] : INFO( 'Ignored shared attribute value projection: attribute ', name, ' redefined at component ', full_name, ' => ignore projected value from upper hierarchy') # check if there is any binding or arithmetic expressions in the attribute dictionary for attr_name, attr_val in new_component_description[ 'attributes'].items(): if type(attr_val) is str: op_type, op1, op2 = parse_expression_for_arithmetic\ (attr_val, new_component_description['attributes']) if not op_type is None: result = process_arithmetic(op1, op2, op_type) new_component_description['attributes'][attr_name] = result else: if attr_val in new_component_description['attributes']: new_component_description['attributes'][ attr_name] = new_component_description[ 'attributes'][attr_val] # save to data structure self.architecture_description[full_name] = deepcopy( new_component_description)