def get_bipartition( self, direction: Dir, max_usage_ratio: float, max_search_time: int, ) -> Optional[Dict[Vertex, Slot]]: """ bi-partition all the current slots """ m = get_mip_model_silent() v2var = self._create_ilp_vars(m) self._add_opt_goal(m, v2var, direction) # area constraints for each child slot self._add_area_constraints(m, v2var, direction, max_usage_ratio) self._add_pre_assignment(m, v2var, direction) self._add_grouping_constraints(m, v2var) m.optimize(max_seconds=max_search_time) next_v2s = self._get_partition_result(m, v2var, direction) if not next_v2s: _logger.debug( f'bi-partioning failed with usage ratio {max_usage_ratio}') return next_v2s
def route_design(self, routing_usage_limit: float = 0.6, detour_path_limit: int = 4) -> Dict[Edge, List[Slot]]: while 1: if routing_usage_limit > 1: _logger.error(f'Global routing failed') exit(1) _logger.info( f'Global routing attempt with routing usage limit {routing_usage_limit}' ) m = get_mip_model_silent() fifo_to_paths = self.get_fifo_to_candidate_paths( routing_usage_limit, detour_path_limit) path_to_var = self.get_path_to_var(m, fifo_to_paths) routing_edge_to_paths = self.get_routing_edge_to_passing_paths( fifo_to_paths) _logger.info(f'there are {len(fifo_to_paths)} dataflow edges') _logger.info( f'there are {len(path_to_var)} potential paths to select from') self.constrain_fifo_to_one_path(m, fifo_to_paths, path_to_var) self.constrain_routing_edge_capacity(m, path_to_var, routing_edge_to_paths) self.add_opt_goal(m, fifo_to_paths, path_to_var) status = m.optimize() if status == OptimizationStatus.OPTIMAL: _logger.warning( f'Succeeded: global routing attempt with routing usage limit {routing_usage_limit}' ) break else: _logger.warning( f'Failed: global routing attempt with routing usage limit {routing_usage_limit}' ) routing_usage_limit += 0.03 # extract results fifo_to_selected_path, routing_edge_to_selected_paths = \ self.get_routing_results(fifo_to_paths, path_to_var, routing_edge_to_paths) # _logger and analysis self.analyze_routing_results(fifo_to_paths, fifo_to_selected_path, routing_edge_to_selected_paths) fifo_to_slots = { fifo: path.get_slots_in_path() for fifo, path in fifo_to_selected_path.items() } return fifo_to_slots
def eight_way_partition( init_v2s: Dict[Vertex, Slot], grouping_constraints: List[List[Vertex]], pre_assignments: Dict[Vertex, Slot], slot_manager: SlotManager, max_usage_ratio: float, slr_width_limit: int, max_search_time: int, hbm_port_v_list: List[Vertex] = [], ) -> Dict[Vertex, Slot]: m = get_mip_model_silent() v_list = list(init_v2s.keys()) # three variables could determine the location of a module # y = y1 *2 + y2 (four slots) # x = x (each SLR is divided by half) v2var_x, v2var_y1, v2var_y2 = dict(), dict(), dict() for v in v_list: v2var_x[v] = m.add_var(var_type=BINARY, name=f'{v.name}_x') v2var_y1[v] = m.add_var(var_type=BINARY, name=f'{v.name}_y1') v2var_y2[v] = m.add_var(var_type=BINARY, name=f'{v.name}_y2') func_get_slot_by_idx = _get_slot_by_idx_closure(slot_manager) slot_to_idx = _get_slot_to_idx(func_get_slot_by_idx) _add_area_constraints(m, v_list, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2, func_get_slot_by_idx=func_get_slot_by_idx, max_usage_ratio=max_usage_ratio) _add_pre_assignment(m, v_list, slot_to_idx, pre_assignments, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2) _add_hbm_port_constraints(m, hbm_port_v_list, slot_to_idx, pre_assignments, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2) add_slr_0_1_crossing_constraint(m, v_list, v2var_y1, v2var_y2, slr_width_limit) add_slr_1_2_crossing_constraint(m, v_list, v2var_y1, slr_width_limit) add_slr_2_3_crossing_constraint(m, v_list, v2var_y1, v2var_y2, slr_width_limit) _add_grouping_constraints(m, grouping_constraints, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2) _add_opt_goal(m, v_list, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2) _logger.debug(f'Start ILP solver with max usage ratio {max_usage_ratio} and max search time {max_search_time}s') m.optimize(max_seconds=max_search_time) next_v2s = _get_results(m, v_list, func_get_slot_by_idx, v2var_x=v2var_x, v2var_y1=v2var_y1, v2var_y2=v2var_y2) return next_v2s
def latency_balancing(graph, fifo_to_path) -> Dict[str, int]: name_to_edge: Dict[str, Edge] = graph.getNameToEdgeMap() name_to_vertex: Dict[str, Vertex] = graph.getNameToVertexMap() m = get_mip_model_silent() # map Vertex -> "arrival time" vertex_to_var = {} for name, v in name_to_vertex.items(): vertex_to_var[v] = m.add_var(var_type=INTEGER, name=name) # differential constraint for each edge for e_name, e in name_to_edge.items(): # +1 because each FIFO by itself has 1 unit of latency # in case the original design is not balanced # note that additional pipelining for full_n will not lead to additional latency # we only need to increase the grace period of almost full FIFOs by 1 # [update]: we skip the +1 for the orginial FIFO. We only take care of our own modifications m += vertex_to_var[e.src] >= vertex_to_var[e.dst] + get_latency( fifo_to_path[e]) m.objective = minimize( xsum(e.width * (vertex_to_var[e.src] - vertex_to_var[e.dst]) for e in name_to_edge.values())) status = m.optimize(max_seconds=120) if status != OptimizationStatus.OPTIMAL and status != OptimizationStatus.FEASIBLE: cli_logger.warning( 'Failed to balance reconvergent paths at loop level. Most likely there is a loop of streams.' ) return {} # get result fifo_name_to_depth = {} for e_name, e in name_to_edge.items(): e.added_depth_for_rebalance = int( vertex_to_var[e.src].x - vertex_to_var[e.dst].x) - e.pipeline_level fifo_name_to_depth[e_name] = e.depth + e.added_depth_for_rebalance assert e.added_depth_for_rebalance >= 0 # logging for e_name, e in name_to_edge.items(): _logger.info( f'{e_name}: pipeline_level: {e.pipeline_level}, original depth: {e.depth}, added_depth_for_rebalance: {e.added_depth_for_rebalance}, width: {e.width} ' ) return fifo_name_to_depth
def get_legalized_v2s(orig_v2s: Dict[Vertex, Slot], grouping_list: List[List[Vertex]], all_slot_list: List[Slot], pre_assignments: Dict[Vertex, Slot], resource_usage_limit: int) -> Dict[Vertex, Slot]: """ adjust the floorplanning to satisfy the area requirement """ _logger.debug( f'Begin legalizing the floorplan results, target resource usage limit: {resource_usage_limit}' ) m = get_mip_model_silent() v_list = list(orig_v2s.keys()) s_list = all_slot_list v_to_s_to_var, s_to_v_to_var = _create_ilp_vars(m, v_list, s_list) v_to_s_to_cost = _get_v_to_s_to_cost(v_list, s_list, orig_v2s) _add_area_constraints(m, s_to_v_to_var, resource_usage_limit) _add_pre_assignments(m, v_to_s_to_var, pre_assignments, all_slot_list) _add_unique_assign_constraints(m, v_to_s_to_var) _add_grouping_constraints(m, grouping_list, v_to_s_to_var, s_list) _add_opt_goal(m, v_to_s_to_cost, v_to_s_to_var) status = m.optimize() if status != OptimizationStatus.OPTIMAL: _logger.debug( f'Fail to legalize the floorplan under target ratio {resource_usage_limit}' ) return {} new_v2s, new_s2v = _get_ilp_results(v_to_s_to_var) _log_results(new_v2s, orig_v2s) _logger.info('Finish legalizing the floorplan results.') return new_v2s