From 7f5e6dd6f8d3a7536a179c3d865e2d50f8ebb437 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 24 Apr 2019 10:54:22 -0700 Subject: [PATCH] Fix unconnected supply pin bug in supply router. Simplified some of the supply router pin groups so that it assumes each group is fully connected. When computing enclosures of the pins on the routing grid, it will remove disconnected enclosure shapes to keep things connected. --- compiler/router/pin_group.py | 224 ++++++++++++------------------- compiler/router/router.py | 142 ++++++++------------ compiler/router/supply_router.py | 24 +++- 3 files changed, 161 insertions(+), 229 deletions(-) diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 13368d12..0600561b 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -25,7 +25,7 @@ class pin_group: # This is a list because we can have a pin group of disconnected sets of pins # and these are represented by separate lists - self.pins = [set(irredundant_pin_set)] + self.pins = set(irredundant_pin_set) self.router = router # These are the corresponding pin grids for each pin group. @@ -55,7 +55,7 @@ class pin_group: total_string += grids_string if self.enclosed: - enlosure_string = "\n enclose={}".format(self.enclosures) + enclosure_string = "\n enclose={}".format(self.enclosures) total_string += enclosure_string total_string += ")" @@ -74,25 +74,6 @@ class pin_group: def is_routed(self): return self.routed - def pins_enclosed(self): - """ - Check if all of the pin shapes are enclosed. - Does not check if the DRC is correct, but just touching. - """ - for pin_list in self.pins: - pin_is_enclosed=False - for pin in pin_list: - if pin_is_enclosed: - break - for encosure in self.enclosures: - if pin.overlaps(enclosure): - pin_is_enclosed=True - break - else: - return False - - return True - def remove_redundant_shapes(self, pin_list): """ Remove any pin layout that is contained within another. @@ -135,7 +116,6 @@ class pin_group: return new_pin_list - # FIXME: This relies on some technology parameters from router which is not clean. def compute_enclosures(self): """ Find the minimum rectangle enclosures of the given tracks. @@ -406,8 +386,8 @@ class pin_group: def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST): """ This encloses a single pin component with a rectangle - starting with the seed and expanding right until blocked - and then up until blocked. + starting with the seed and expanding dir1 until blocked + and then dir2 until blocked. dir1 and dir2 should be two orthogonal directions. """ @@ -458,61 +438,86 @@ class pin_group: # Compute the enclosure pin_layout list of the set of tracks self.enclosures = self.compute_enclosures() - for pin_list in self.pins: - for pin in pin_list: + # Find a connector to every pin and add it to the enclosures + for pin in self.pins: - # If it is contained, it won't need a connector - if pin.contained_by_any(self.enclosures): - continue + # If it is contained, it won't need a connector + if pin.contained_by_any(self.enclosures): + continue - # Find a connector in the cardinal directions - # If there is overlap, but it isn't contained, these could all be None - # These could also be none if the pin is diagonal from the enclosure - left_connector = self.find_left_connector(pin, self.enclosures) - right_connector = self.find_right_connector(pin, self.enclosures) - above_connector = self.find_above_connector(pin, self.enclosures) - below_connector = self.find_below_connector(pin, self.enclosures) - connector_list = [left_connector, right_connector, above_connector, below_connector] - filtered_list = list(filter(lambda x: x!=None, connector_list)) - if (len(filtered_list)>0): - import copy - bbox_connector = copy.copy(pin) - bbox_connector.bbox(filtered_list) - self.enclosures.append(bbox_connector) + # Find a connector in the cardinal directions + # If there is overlap, but it isn't contained, these could all be None + # These could also be none if the pin is diagonal from the enclosure + left_connector = self.find_left_connector(pin, self.enclosures) + right_connector = self.find_right_connector(pin, self.enclosures) + above_connector = self.find_above_connector(pin, self.enclosures) + below_connector = self.find_below_connector(pin, self.enclosures) + connector_list = [left_connector, right_connector, above_connector, below_connector] + filtered_list = list(filter(lambda x: x!=None, connector_list)) + if (len(filtered_list)>0): + import copy + bbox_connector = copy.copy(pin) + bbox_connector.bbox(filtered_list) + self.enclosures.append(bbox_connector) # Now, make sure each pin touches an enclosure. If not, add another (diagonal) connector. # This could only happen when there was no enclosure in any cardinal direction from a pin - for pin_list in self.pins: - if not self.overlap_any_shape(pin_list, self.enclosures): - connector = self.find_smallest_connector(pin_list, self.enclosures) - if connector==None: - debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) - self.router.write_debug_gds("no_connector.gds") - self.enclosures.append(connector) - + if not self.overlap_any_shape(self.pins, self.enclosures): + connector = self.find_smallest_connector(pin_list, self.enclosures) + if connector==None: + debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures)) + self.router.write_debug_gds("no_connector.gds") + self.enclosures.append(connector) + + # At this point, the pins are overlapping, but there might be more than one! + overlap_set = set() + for pin in self.pins: + overlap_set.update(self.transitive_overlap(pin, self.enclosures)) + # Use the new enclosures and recompute the grids that correspond to them + if len(overlap_set) len(old_connected_set): + old_connected_set = connected_set + connected_set = set([shape]) + for old_shape in old_connected_set: + for cur_shape in augmented_shape_list: + if old_shape.overlaps(cur_shape): + connected_set.add(cur_shape) + + + # Remove the original shape + connected_set.remove(shape) + + # if len(connected_set) {0}\n {1}".format(combined.pins,combined.grids)) + # new_pin_groups.append(combined) + + # # Add the pin groups that weren't added to the new set + # for index in all_indices: + # new_pin_groups.append(self.pin_groups[pin_name][index]) + + # old_size = len(self.pin_groups[pin_name]) + # # Use the new pin group! + # self.pin_groups[pin_name] = new_pin_groups + # removed_pairs = old_size - len(new_pin_groups) + # debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) - # Now reconstruct the new groups - new_pin_groups = [] - for index1,index2_set in adjacent_pins.items(): - # Remove the indices if they are added to the new set - all_indices.discard(index1) - all_indices.difference_update(index2_set) - - # Create the combined group starting with the first item - combined = self.pin_groups[pin_name][index1] - # Add all of the other items that overlapped - for index2 in index2_set: - pg = self.pin_groups[pin_name][index2] - combined.add_group(pg) - debug.info(3,"Combining {0} {1}:".format(pin_name, index2)) - debug.info(3, " {0}\n {1}".format(combined.pins, pg.pins)) - debug.info(3," --> {0}\n {1}".format(combined.pins,combined.grids)) - new_pin_groups.append(combined) - - # Add the pin groups that weren't added to the new set - for index in all_indices: - new_pin_groups.append(self.pin_groups[pin_name][index]) - - old_size = len(self.pin_groups[pin_name]) - # Use the new pin group! - self.pin_groups[pin_name] = new_pin_groups - removed_pairs = old_size - len(new_pin_groups) - debug.info(1, "Combined {0} pin groups for {1}".format(removed_pairs,pin_name)) - - return removed_pairs + # return removed_pairs def separate_adjacent_pins(self, separation): @@ -748,44 +752,10 @@ class router(router_tech): if gid not in group_map: group_map[gid] = pin_group(name=pin_name, pin_set=[], router=self) # We always add it to the first set since they are touching - group_map[gid].pins[0].add(pin) + group_map[gid].pins.add(pin) self.pin_groups[pin_name] = list(group_map.values()) - # This is the old O(n^2) implementation - # def analyze_pins(self, pin_name): - # """ - # Analyze the shapes of a pin and combine them into pin_groups which are connected. - # """ - # debug.info(2,"Analyzing pin groups for {}.".format(pin_name)) - - # pin_set = self.pins[pin_name] - - # # Put each pin in an equivalence class of it's own - # equiv_classes = [set([x]) for x in pin_set] - # def combine_classes(equiv_classes): - # for class1 in equiv_classes: - # for class2 in equiv_classes: - # if class1 == class2: - # continue - # # Compare each pin in each class, - # # and if any overlap, update equiv_classes to include the combined the class - # for p1 in class1: - # for p2 in class2: - # if p1.overlaps(p2): - # combined_class = class1 | class2 - # equiv_classes.remove(class1) - # equiv_classes.remove(class2) - # equiv_classes.append(combined_class) - # return(equiv_classes) - # return(equiv_classes) - - # old_length = math.inf - # while (len(equiv_classes){2} {3}".format(name,ll,ur,pin)) + debug.info(3,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) self.cell.add_layout_pin(text=name, layer=pin.layer, offset=pin.ll(), @@ -379,7 +392,10 @@ class supply_router(router): # Actually run the A* router if not self.run_router(detour_scale=5): - self.write_debug_gds() + self.write_debug_gds("debug_route.gds",False) + + #if index==3 and pin_name=="vdd": + # self.write_debug_gds("route.gds",False) def add_supply_rail_target(self, pin_name):