diff --git a/compiler/router/pin_group.py b/compiler/router/pin_group.py index 474702a4..0c5221e5 100644 --- a/compiler/router/pin_group.py +++ b/compiler/router/pin_group.py @@ -41,10 +41,7 @@ class pin_group: # or could not be part of the pin self.secondary_grids = set() - # The corresponding set of partially blocked grids for each pin group. - # These are blockages for other nets but unblocked - # for routing this group. These are also blockages if we - # used a simple enclosure to route to a rail. + # The set of blocked grids due to this pin self.blockages = set() # This is a set of pin_layout shapes to cover the grids @@ -421,16 +418,16 @@ class pin_group: while True: next_cell = row[-1] + offset1 # Can't move if not in the pin shape - if next_cell in self.grids and next_cell not in self.router.blocked_grids: + if next_cell in self.grids and next_cell not in self.router.get_blocked_grids(): row.append(next_cell) else: break # Move in dir2 while we can while True: - next_row = [x+offset2 for x in row] + next_row = [x + offset2 for x in row] for cell in next_row: # Can't move if any cell is not in the pin shape - if cell not in self.grids or cell in self.router.blocked_grids: + if cell not in self.grids or cell in self.router.get_blocked_grids(): break else: row = next_row @@ -606,9 +603,10 @@ class pin_group: The secondary set of grids are "optional" pin shapes that should be either blocked or part of the pin. """ + # Set of tracks that overlap a pin pin_set = set() + # Set of track adjacent to or paritally overlap a pin (not full DRC connection) partial_set = set() - blockage_set = set() for pin in self.pins: debug.info(4, " Converting {0}".format(pin)) @@ -621,25 +619,20 @@ class pin_group: # Blockages will be a super-set of pins since # it uses the inflated pin shape. blockage_in_tracks = self.router.convert_blockage(pin) - blockage_set.update(blockage_in_tracks) + self.blockages.update(blockage_in_tracks) # If we have a blockage, we must remove the grids # Remember, this excludes the pin blockages already - shared_set = pin_set & self.router.blocked_grids + shared_set = pin_set & self.router.get_blocked_grids() if len(shared_set) > 0: debug.info(4, "Removing pins {}".format(shared_set)) pin_set.difference_update(shared_set) - shared_set = partial_set & self.router.blocked_grids + shared_set = partial_set & self.router.get_blocked_grids() if len(shared_set) > 0: debug.info(4, "Removing pins {}".format(shared_set)) - partial_set.difference_update(shared_set) - shared_set = blockage_set & self.router.blocked_grids - if len(shared_set) > 0: - debug.info(4, "Removing blocks {}".format(shared_set)) - blockage_set.difference_update(shared_set) # At least one of the groups must have some valid tracks - if (len(pin_set) == 0 and len(partial_set) == 0 and len(blockage_set) == 0): + if (len(pin_set) == 0 and len(partial_set) == 0): # debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins)) for pin in self.pins: @@ -656,8 +649,11 @@ class pin_group: self.pins)) self.router.write_debug_gds("blocked_pin.gds") - # Consider all the grids that would be blocked - self.grids = pin_set | partial_set + # Consider the fully connected set first and if not the partial set + if len(pin_set) > 0: + self.grids = pin_set + else: + self.grids = partial_set if len(self.grids) < 0: debug.error("Did not find any unblocked grids: {}".format(str(self.pins))) self.router.write_debug_gds("blocked_pin.gds") diff --git a/compiler/router/router.py b/compiler/router/router.py index d666fcb5..8cc8f5d8 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -69,9 +69,12 @@ class router(router_tech): # The blockage data structures # A list of metal shapes (using the same pin_layout structure) - # that are not pins but blockages. + # that could be blockages. + # This will include the pins above as well. self.blockages = [] - # The corresponding set of blocked grids for above pin shapes + # The corresponding set of blocked grids for above blockage pin_layout shapes + # It is a cached set of grids that *could* be blocked, but may be unblocked + # depending on which pin we are routing. self.blocked_grids = set() # The routed data structures @@ -345,15 +348,14 @@ class router(router_tech): # This is just a virtual function pass - def prepare_blockages(self, pin_name): + def prepare_blockages(self): """ Reset and add all of the blockages in the design. - Names is a list of pins to add as a blockage. """ debug.info(3, "Preparing blockages.") # Start fresh. Not the best for run-time, but simpler. - self.clear_blockages() + self.clear_all_blockages() # This adds the initial blockges of the design # print("BLOCKING:", self.blocked_grids) self.set_blockages(self.blocked_grids, True) @@ -370,22 +372,17 @@ class router(router_tech): # (some will be unblocked if they're a source/target) # Also block the previous routes for name in self.pin_groups: - blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} - self.set_blockages(blockage_grids, True) + # This should be a superset of the grids... blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} self.set_blockages(blockage_grids, True) + # But do the grids just in case + blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} + self.set_blockages(blockage_grids, True) # FIXME: These duplicate a bit of work # These are the paths that have already been routed. self.set_blockages(self.path_blockages) - # Don't mark the other components as targets since we want to route - # directly to a rail, but unblock all the source components so we can - # route over them - # 1/6/21: This would cause things that looked like loops in the supply tree router - # blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} - # self.set_blockages(blockage_grids, False) - def convert_shape_to_units(self, shape): """ Scale a shape (two vector list) to user units @@ -421,7 +418,18 @@ class router(router_tech): # z direction return 2 - def clear_blockages(self): + def clear_blockages(self, pin_name): + """ + This function clears a given pin and all of its components from being blockages. + """ + # This should be a superset of the grids... + blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.blockages} + self.set_blockages(blockage_grids, False) + # But do the grids just in case + blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} + self.set_blockages(blockage_grids, False) + + def clear_all_blockages(self): """ Clear all blockages on the grid. """ @@ -432,24 +440,24 @@ class router(router_tech): """ Flag the blockages in the grid """ self.rg.set_blocked(blockages, value) - def get_blockage_tracks(self, ll, ur, z): - debug.info(3, "Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) + def convert_to_tracks(self, ll, ur, z): + debug.info(3, "Converting ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) - block_list = [] + grid_list = [] for x in range(int(ll[0]), int(ur[0])+1): for y in range(int(ll[1]), int(ur[1])+1): - block_list.append(vector3d(x, y, z)) + grid_list.append(vector3d(x, y, z)) - return set(block_list) + return set(grid_list) def convert_blockage(self, blockage): """ Convert a pin layout blockage shape to routing grid tracks. """ # Inflate the blockage by half a spacing rule - [ll, ur] = self.convert_blockage_to_tracks(blockage.inflate()) + [ll, ur] = self.convert_shape_to_tracks(blockage.inflate()) zlayer = self.get_zindex(blockage.lpp) - blockage_tracks = self.get_blockage_tracks(ll, ur, zlayer) + blockage_tracks = self.convert_to_tracks(ll, ur, zlayer) return blockage_tracks def convert_blockages(self): @@ -460,6 +468,12 @@ class router(router_tech): blockage_list = self.convert_blockage(blockage) self.blocked_grids.update(blockage_list) + def get_blocked_grids(self): + """ + Return the blocked grids with their flag set + """ + return set([x for x in self.blocked_grids if self.rg.is_blocked(x)]) + def retrieve_blockages(self, lpp): """ Recursive find boundaries as blockages to the routing grid. @@ -473,11 +487,7 @@ class router(router_tech): new_pin = pin_layout("blockage{}".format(len(self.blockages)), rect, lpp) - - # If there is a rectangle that is the same in the pins, - # it isn't a blockage! - if new_pin not in self.all_pins: - self.blockages.append(new_pin) + self.blockages.append(new_pin) def convert_point_to_units(self, p): """ @@ -492,10 +502,10 @@ class router(router_tech): Convert a wave to a set of center points """ return [self.convert_point_to_units(i) for i in wave] - - def convert_blockage_to_tracks(self, shape): + + def convert_shape_to_tracks(self, shape): """ - Convert a rectangular blockage shape into track units. + Convert a rectangular shape into track units. """ (ll, ur) = shape ll = snap_to_grid(ll) @@ -531,8 +541,8 @@ class router(router_tech): insufficient_list = set() zindex = self.get_zindex(pin.lpp) - for x in range(int(ll[0]) + expansion, int(ur[0]) + 1 + expansion): - for y in range(int(ll[1] + expansion), int(ur[1]) + 1 + expansion): + for x in range(int(ll[0]) - expansion, int(ur[0]) + 1 + expansion): + for y in range(int(ll[1] - expansion), int(ur[1]) + 1 + expansion): (full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x, y, @@ -813,6 +823,7 @@ class router(router_tech): for pin_name in self.pin_groups: debug.info(2, "Enclosing pins for {}".format(pin_name)) for pg in self.pin_groups[pin_name]: + self.clear_blockages(pin_name) pg.enclose_pin() pg.add_enclosure(self.cell) @@ -824,6 +835,9 @@ class router(router_tech): for i in range(self.num_pin_components(pin_name)): self.add_pin_component_source(pin_name, i) + # Clearing the blockage of this pin requires the inflated pins + self.clear_blockages(pin_name) + def add_target(self, pin_name): """ This will mark the grids for all pin components as a target. @@ -832,6 +846,9 @@ class router(router_tech): for i in range(self.num_pin_components(pin_name)): self.add_pin_component_target(pin_name, i) + # Clearing the blockage of this pin requires the inflated pins + self.clear_blockages(pin_name) + def add_perimeter_target(self, side="all"): """ This will mark all the cells on the perimeter of the original layout as a target. diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 2c06d619..134a2ee3 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -62,6 +62,8 @@ class signal_escape_router(router): start_time = datetime.now() for pin_name in ordered_pin_names: self.route_signal(pin_name) + #if pin_name == "dout1[1]": + # self.write_debug_gds("postroute.gds", False) print_time("Maze routing pins",datetime.now(), start_time, 3) @@ -79,7 +81,8 @@ class signal_escape_router(router): # This is inefficient since it is non-incremental, but it was # easier to debug. - self.prepare_blockages(pin_name) + self.prepare_blockages() + self.clear_blockages(pin_name) # Add the single component of the pin as the source # which unmarks it as a blockage too @@ -88,6 +91,9 @@ class signal_escape_router(router): # Marks the grid cells all along the perimeter as a target self.add_perimeter_target(side) + #if pin_name == "dout1[1]": + # self.write_debug_gds("preroute.gds", False) + # Actually run the A* router if self.run_router(detour_scale=detour_scale): new_pin = self.get_perimeter_pin() diff --git a/compiler/router/supply_grid_router.py b/compiler/router/supply_grid_router.py index 53758d9b..6876329f 100644 --- a/compiler/router/supply_grid_router.py +++ b/compiler/router/supply_grid_router.py @@ -73,13 +73,16 @@ class supply_grid_router(router): # Add the supply rails in a mesh network and connect H/V with vias start_time = datetime.now() # Block everything - self.prepare_blockages(self.gnd_name) + self.prepare_blockages() + self.clear_blockages(self.gnd_name) + # Determine the rail locations self.route_supply_rails(self.gnd_name, 0) # Block everything - self.prepare_blockages(self.vdd_name) + self.prepare_blockages() + self.clear_blockages(self.vdd_name) # Determine the rail locations self.route_supply_rails(self.vdd_name, 1) print_time("Routing supply rails", datetime.now(), start_time, 3) diff --git a/compiler/router/supply_tree_router.py b/compiler/router/supply_tree_router.py index d2db1e29..73378b2d 100644 --- a/compiler/router/supply_tree_router.py +++ b/compiler/router/supply_tree_router.py @@ -61,12 +61,6 @@ class supply_tree_router(router): self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) print_time("Finding pins and blockages",datetime.now(), start_time, 3) - # Add the supply rails in a mesh network and connect H/V with vias - start_time = datetime.now() - # Block everything - self.prepare_blockages(self.gnd_name) - self.prepare_blockages(self.vdd_name) - # Route the supply pins to the supply rails # Route vdd first since we want it to be shorter start_time = datetime.now() @@ -136,7 +130,8 @@ class supply_tree_router(router): # This is inefficient since it is non-incremental, but it was # easier to debug. - self.prepare_blockages(pin_name) + self.prepare_blockages() + self.clear_blockages(pin_name) # Add the single component of the pin as the source # which unmarks it as a blockage too