From 4d30f214dad49e121e84a37d942005c545f28793 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Fri, 2 Nov 2018 11:11:32 -0700 Subject: [PATCH] Add expanded blockages for paths an enclosures to handle wide metal spacing rules. --- compiler/router/direction.py | 27 +++++- compiler/router/grid_utils.py | 144 ++++++++++++++++++++----------- compiler/router/pin_group.py | 75 ++++++++++++++-- compiler/router/router.py | 38 ++++---- compiler/router/supply_router.py | 82 ++++-------------- 5 files changed, 219 insertions(+), 147 deletions(-) diff --git a/compiler/router/direction.py b/compiler/router/direction.py index ab5873b4..52d4207f 100644 --- a/compiler/router/direction.py +++ b/compiler/router/direction.py @@ -8,6 +8,10 @@ class direction(Enum): WEST = 4 UP = 5 DOWN = 6 + NORTHEAST = 7 + NORTHWEST = 8 + SOUTHEAST = 9 + SOUTHWEST = 10 def get_offset(direct): @@ -26,8 +30,16 @@ class direction(Enum): offset = vector3d(0,0,1) elif direct==direction.DOWN: offset = vector3d(0,0,-1) + elif direct==direction.NORTHEAST: + offset = vector3d(1,1,0) + elif direct==direction.NORTHWEST: + offset = vector3d(-1,1,0) + elif direct==direction.SOUTHEAST: + offset = vector3d(1,-1,0) + elif direct==direction.SOUTHWEST: + offset = vector3d(-1,-1,0) else: - debug.error("Invalid direction {}".format(dirct)) + debug.error("Invalid direction {}".format(direct)) return offset @@ -37,3 +49,16 @@ class direction(Enum): def cardinal_offsets(): return [direction.get_offset(d) for d in direction.cardinal_directions()] + def all_directions(): + return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST, + direction.NORTHEAST, direction.NORTHWEST, direction.SOUTHEAST, direction.SOUTHWEST] + + def all_offsets(): + return [direction.get_offset(d) for d in direction.all_directions()] + + def all_neighbors(cell): + return [cell+x for x in direction.all_offsets()] + + def cardinal_neighbors(cell): + return [cell+x for x in direction.cardinal_offsets()] + diff --git a/compiler/router/grid_utils.py b/compiler/router/grid_utils.py index 748933b6..23fe23ea 100644 --- a/compiler/router/grid_utils.py +++ b/compiler/router/grid_utils.py @@ -8,17 +8,18 @@ from vector3d import vector3d def increment_set(curset, direct): - """ - Return the cells incremented in given direction - """ - offset = direction.get_offset(direct) + """ + Return the cells incremented in given direction + """ + offset = direction.get_offset(direct) + + newset = set() + for c in curset: + newc = c+offset + newset.add(newc) + + return newset - newset = set() - for c in curset: - newc = c+offset - newset.add(newc) - - return newset def remove_border(curset, direct): """ @@ -26,7 +27,7 @@ def remove_border(curset, direct): """ border = get_border(curset, direct) curset.difference_update(border) - + def get_upper_right(curset): ur = None @@ -43,48 +44,48 @@ def get_lower_left(curset): return ll def get_border( curset, direct): - """ - Return the furthest cell(s) in a given direction. - """ - - # find direction-most cell(s) - maxc = [] - if direct==direction.NORTH: - for c in curset: - if len(maxc)==0 or c.y>maxc[0].y: - maxc = [c] - elif c.y==maxc[0].y: - maxc.append(c) - elif direct==direct.SOUTH: - for c in curset: - if len(maxc)==0 or c.ymaxc[0].x: - maxc = [c] - elif c.x==maxc[0].x: - maxc.append(c) - elif direct==direct.WEST: - for c in curset: - if len(maxc)==0 or c.xmaxc[0].y: + maxc = [c] + elif c.y==maxc[0].y: + maxc.append(c) + elif direct==direct.SOUTH: + for c in curset: + if len(maxc)==0 or c.ymaxc[0].x: + maxc = [c] + elif c.x==maxc[0].x: + maxc.append(c) + elif direct==direct.WEST: + for c in curset: + if len(maxc)==0 or c.x0: debug.info(2,"Removing pins {}".format(shared_set)) - pin_set.difference_update(router.blocked_grids) + pin_set.difference_update(self.router.blocked_grids) - shared_set = blockage_set & router.blocked_grids + shared_set = blockage_set & self.router.blocked_grids if len(shared_set)>0: debug.info(2,"Removing blocks {}".format(shared_set)) - blockage_set.difference_update(router.blocked_grids) + blockage_set.difference_update(self.router.blocked_grids) # At least one of the groups must have some valid tracks if (len(pin_set)==0 and len(blockage_set)==0): @@ -596,3 +598,60 @@ class pin_group: debug.info(2," pins {}".format(self.grids)) debug.info(2," secondary {}".format(self.secondary_grids)) + def recurse_simple_overlap_enclosure(self, start_set, direct): + """ + Recursive function to return set of tracks that connects to + the actual supply rail wire in a given direction (or terminating + when any track is no longer in the supply rail. + """ + next_set = grid_utils.expand_border(start_set, direct) + + supply_tracks = self.router.supply_rail_tracks[self.name] + supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name] + + supply_overlap = next_set & supply_tracks + wire_overlap = next_set & supply_wire_tracks + + # If the rail overlap is the same, we are done, since we connected to the actual wire + if len(wire_overlap)==len(start_set): + new_set = start_set | wire_overlap + # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region + elif len(supply_overlap)==len(start_set): + recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct) + new_set = start_set | supply_overlap | recurse_set + else: + # If we got no next set, we are done, can't expand! + new_set = set() + + return new_set + + def create_simple_overlap_enclosure(self, start_set): + """ + This takes a set of tracks that overlap a supply rail and creates an enclosure + that is ensured to overlap the supply rail wire. + It then adds rectangle(s) for the enclosure. + """ + + additional_set = set() + # Check the layer of any element in the pin to determine which direction to route it + e = next(iter(start_set)) + new_set = start_set.copy() + if e.z==0: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH) + if not new_set: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH) + else: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST) + if not new_set: + new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST) + + # Expand the pin grid set to include some extra grids that connect the supply rail + self.grids.update(new_set) + + # Add the inflated set so we don't get wide metal spacing issues (if it exists) + self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width)) + + # Add the polygon enclosures and set this pin group as routed + self.set_routed() + self.enclosures = self.compute_enclosures() + diff --git a/compiler/router/router.py b/compiler/router/router.py index f2cb6f22..2432e8c2 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -59,6 +59,8 @@ class router(router_tech): ### The routed data structures # A list of paths that have been "routed" self.paths = [] + # A list of path blockages (they might be expanded for wide metal DRC) + self.path_blockages = [] # The boundary will determine the limits to the size of the routing grid self.boundary = self.layout.measureBoundary(self.top_name) @@ -326,10 +328,16 @@ class router(router_tech): self.set_supply_rail_blocked(True) # Block all of the pin components (some will be unblocked if they're a source/target) + # Also block the previous routes for name in self.pin_groups.keys(): blockage_grids = {y for x in self.pin_groups[name] for y in x.grids} self.set_blockages(blockage_grids,True) + blockage_grids = {y for x in self.pin_groups[name] for y in x.blockages} + self.set_blockages(blockage_grids,True) + # FIXME: These duplicate a bit of work + # These are the paths that have already been routed. + self.set_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 @@ -337,8 +345,6 @@ class router(router_tech): blockage_grids = {y for x in self.pin_groups[pin_name] for y in x.grids} self.set_blockages(blockage_grids,False) - # These are the paths that have already been routed. - self.set_path_blockages() # def translate_coordinates(self, coord, mirr, angle, xyShift): # """ @@ -387,16 +393,6 @@ class router(router_tech): # z direction return 2 - - def add_path_blockages(self): - """ - Go through all of the past paths and add them as blockages. - This is so we don't have to write/reload the GDS. - """ - for path in self.paths: - for grid in path: - self.rg.set_blocked(grid) - def clear_blockages(self): """ Clear all blockages on the grid. @@ -411,9 +407,9 @@ class router(router_tech): def set_path_blockages(self,value=True): """ Flag the paths as blockages """ # These are the paths that have already been routed. - # This adds the initial blockges of the design - for p in self.paths: - p.set_blocked(value) + for path_set in self.path_blockages: + for c in path_set: + self.rg.set_blocked(c,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)) @@ -696,7 +692,7 @@ class router(router_tech): Convert the pin groups into pin tracks and blockage tracks. """ for pg in self.pin_groups[pin_name]: - pg.convert_pin(self) + pg.convert_pin() @@ -918,13 +914,6 @@ class router(router_tech): return newpath - def add_path_blockages(self): - """ - Go through all of the past paths and add them as blockages. - This is so we don't have to write/reload the GDS. - """ - for path in self.paths: - self.rg.block_path(path) def run_router(self, detour_scale): """ @@ -936,6 +925,9 @@ class router(router_tech): debug.info(2,"Found path: cost={0} ".format(cost)) debug.info(3,str(path)) self.paths.append(path) + path_set = grid_utils.flatten_set(path) + inflated_path = grid_utils.inflate_set(path_set,self.supply_rail_space_width) + self.path_blockages.append(inflated_path) self.add_route(path) else: self.write_debug_gds("failed_route.gds") diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 1e13fd12..1a7a77cf 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -99,7 +99,7 @@ class supply_router(router): self.route_pins_to_rails(gnd_name) #self.write_debug_gds("debug_pin_routes.gds",stop_program=True) - #self.write_debug_gds("final.gds") + #self.write_debug_gds("final.gds",False) return True @@ -110,77 +110,31 @@ class supply_router(router): This checks for simple cases where a pin component already overlaps a supply rail. It will add an enclosure to ensure the overlap in wide DRC rule cases. """ + debug.info(1,"Routing simple overlap pins for {0}".format(pin_name)) + + # These are the wire tracks + wire_tracks = self.supply_rail_wire_tracks[pin_name] + # These are the wire and space tracks supply_tracks = self.supply_rail_tracks[pin_name] + for pg in self.pin_groups[pin_name]: if pg.is_routed(): continue - common_set = supply_tracks & pg.grids - - if len(common_set)>0: - self.create_simple_overlap_enclosure(pin_name, common_set) + # First, check if we just overlap, if so, we are done. + overlap_grids = wire_tracks & pg.grids + if len(overlap_grids)>0: pg.set_routed() + continue + + # Else, if we overlap some of the space track, we can patch it with an enclosure + common_set = supply_tracks & pg.grids + if len(common_set)>0: + pg.create_simple_overlap_enclosure(common_set) + pg.add_enclosure(self.cell) - def recurse_simple_overlap_enclosure(self, pin_name, start_set, direct): - """ - Recursive function to return set of tracks that connects to - the actual supply rail wire in a given direction (or terminating - when any track is no longer in the supply rail. - """ - next_set = grid_utils.expand_border(start_set, direct) - - supply_tracks = self.supply_rail_tracks[pin_name] - supply_wire_tracks = self.supply_rail_wire_tracks[pin_name] - - supply_overlap = next_set & supply_tracks - wire_overlap = next_set & supply_wire_tracks - - # If the rail overlap is the same, we are done, since we connected to the actual wire - if len(wire_overlap)==len(start_set): - new_set = start_set | wire_overlap - # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region - elif len(supply_overlap)==len(start_set): - recurse_set = self.recurse_simple_overlap_enclosure(pin_name, supply_overlap, direct) - new_set = start_set | supply_overlap | recurse_set - else: - # If we got no next set, we are done, can't expand! - new_set = set() - - return new_set - - def create_simple_overlap_enclosure(self, pin_name, start_set): - """ - This takes a set of tracks that overlap a supply rail and creates an enclosure - that is ensured to overlap the supply rail wire. - It then adds rectangle(s) for the enclosure. - """ - additional_set = set() - # Check the layer of any element in the pin to determine which direction to route it - e = next(iter(start_set)) - new_set = start_set.copy() - if e.z==0: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.NORTH) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.SOUTH) - else: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.EAST) - if not new_set: - new_set = self.recurse_simple_overlap_enclosure(pin_name, start_set, direction.WEST) - - pg = pin_group(name=pin_name, pin_set=[], router=self) - pg.grids=new_set - enclosure_list = pg.compute_enclosures() - for pin in enclosure_list: - debug.info(2,"Adding simple overlap enclosure {0} {1}".format(pin_name, pin)) - self.cell.add_rect(layer=pin.layer, - offset=pin.ll(), - width=pin.width(), - height=pin.height()) - - - def finalize_supply_rails(self, name): """ @@ -478,6 +432,7 @@ class supply_router(router): for index,pg in enumerate(self.pin_groups[pin_name]): if pg.is_routed(): continue + debug.info(2,"Routing component {0} {1}".format(pin_name, index)) # Clear everything in the routing grid. @@ -498,7 +453,6 @@ class supply_router(router): # Actually run the A* router if not self.run_router(detour_scale=5): self.write_debug_gds() - def add_supply_rail_target(self, pin_name):