From 849293b95b1bbac26fd85b834191433e8e7c2856 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Thu, 13 Sep 2018 09:10:29 -0700 Subject: [PATCH] Converting grid data structures to sets to reduce size. --- compiler/router/grid.py | 36 +++---- compiler/router/router.py | 171 ++++++++++++++++++------------- compiler/router/signal_router.py | 8 +- compiler/router/supply_router.py | 82 +++++++++++---- 4 files changed, 181 insertions(+), 116 deletions(-) diff --git a/compiler/router/grid.py b/compiler/router/grid.py index 8e6ed91d..8f38b7ec 100644 --- a/compiler/router/grid.py +++ b/compiler/router/grid.py @@ -37,7 +37,7 @@ class grid: self.map={} def set_blocked(self,n,value=True): - if isinstance(n,list): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: self.set_blocked(item,value) else: @@ -45,7 +45,7 @@ class grid: self.map[n].blocked=value def is_blocked(self,n): - if isinstance(n,list): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: if self.is_blocked(item): return True @@ -57,39 +57,33 @@ class grid: def set_path(self,n,value=True): - if isinstance(n,list): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: self.set_path(item,value) else: self.add_map(n) self.map[n].path=value - def set_blockages(self,block_list,value=True): - debug.info(3,"Adding blockage list={0}".format(str(block_list))) - for n in block_list: - self.set_blocked(n,value) - def clear_blockages(self): debug.info(2,"Clearing all blockages") - for n in self.map.keys(): - self.set_blocked(n,False) + self.set_blocked(set(self.map.keys()),False) - def set_source(self,n): - if isinstance(n,list): + def set_source(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: - self.set_source(item) + self.set_source(item,value) else: self.add_map(n) - self.map[n].source=True + self.map[n].source=value self.source.append(n) - def set_target(self,n): - if isinstance(n,list): + def set_target(self,n,value=True): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: - self.set_target(item) + self.set_target(item,value) else: self.add_map(n) - self.map[n].target=True + self.map[n].target=value self.target.append(n) @@ -101,11 +95,11 @@ class grid: self.set_blocked(n,False) - def add_target(self,track_list): + def set_target(self,track_list,value=True): debug.info(3,"Adding target list={0}".format(str(track_list))) for n in track_list: debug.info(4,"Adding target ={0}".format(str(n))) - self.set_target(n) + self.set_target(n,value) self.set_blocked(n,False) def is_target(self,point): @@ -118,7 +112,7 @@ class grid: """ Add a point to the map if it doesn't exist. """ - if isinstance(n,list): + if isinstance(n, (list,tuple,set,frozenset)): for item in n: self.add_map(item) else: diff --git a/compiler/router/router.py b/compiler/router/router.py index f57274dc..5b2b845f 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -40,15 +40,17 @@ class router: self.pins = {} # A set of connected pin groups self.pin_groups = {} - # The corresponding sets of grids of the groups + # The corresponding sets of grids for each pin self.pin_grids = {} - # The set of partially covered pins to avoid - self.pin_blockages = {} + # The set of partially covered pins to avoid for each pin + self.pin_partials = {} + # A set of blocked grids + self.blocked_grids = set() - # A list of blockages + # A list of pin layout shapes that are blocked self.blockages=[] - # all the paths we've routed so far (to supplement the blockages) + # A list of paths that are blocked self.paths = [] # The boundary will determine the limits to the size of the routing grid @@ -65,7 +67,7 @@ class router: self.pins = {} self.pin_groups = {} self.pin_grids = {} - self.pin_blockages = {} + self.pin_paritals = {} self.reinit() def set_top(self,top_name): @@ -174,7 +176,7 @@ class router: self.pins = {} self.pin_groups = {} self.pin_grids = {} - self.pin_blockages = {} + self.pin_partials = {} # DO NOT clear the blockages as these don't change self.rg.reinit() @@ -243,41 +245,44 @@ class router: """ self.rg.clear_blockages() - def add_pin_blockages(self, pin_name): - """ Add the blockages except the pin shapes. Also remove the pin shapes from the blockages list. """ - self.add_blockages(self.pin_blockages[pin_name]) - - def add_blockages(self, blockages=None): + def set_blockages(self, blockages, value=True): """ Flag the blockages in the grid """ - if blockages == None: - blockages = self.blockage_grids - self.rg.set_blockages(blockages) - + self.rg.set_blocked(blockages, value) + + 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) + 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)) + debug.info(4,"Converting blockage ll={0} ur={1} z={2}".format(str(ll),str(ur),z)) block_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)) - return block_list + return set(block_list) + def convert_blockage(self, blockage): + """ + Convert a pin layout blockage shape to routing grid tracks. + """ + # Inflate the blockage by spacing rule + [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) + zlayer = self.get_zindex(blockage.layer_num) + blockage_tracks = self.get_blockage_tracks(ll,ur,zlayer) + return blockage_tracks def convert_blockages(self): """ Convert blockages to grid tracks. """ - blockage_grids = [] for blockage in self.blockages: - debug.info(2,"Converting blockage {}".format(str(blockage))) - # Inflate the blockage by spacing rule - [ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) - zlayer = self.get_zindex(blockage.layer_num) - blockages = self.get_blockage_tracks(ll,ur,zlayer) - blockage_grids.extend(blockages) - - # Remember the filtered blockages - self.blockage_grids = blockage_grids + debug.info(3,"Converting blockage {}".format(str(blockage))) + blockage_list = self.convert_blockage(blockage) + self.blocked_grids.update(blockage_list) def retrieve_blockages(self, layer_num): @@ -341,7 +346,7 @@ class router: If a pin has insufficent overlap, it returns the blockage list to avoid it. """ (ll,ur) = pin.rect - debug.info(1,"Converting [ {0} , {1} ]".format(ll,ur)) + debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur)) # scale the size bigger to include neaby tracks ll=ll.scale(self.track_factor).floor() @@ -350,41 +355,36 @@ class router: # width depends on which layer it is zindex=self.get_zindex(pin.layer_num) if zindex: - width = self.vert_layer_width + width = self.vert_layer_width + spacing = self.vert_layer_spacing else: width = self.horiz_layer_width - + spacing = self.horiz_layer_spacing + track_list = [] block_list = [] - for x in range(int(ll[0]),int(ur[0])): - for y in range(int(ll[1]),int(ur[1])): - debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) + for x in range(int(ll[0]),int(ur[0])+1): + for y in range(int(ll[1]),int(ur[1])+1): + debug.info(4,"Converting [ {0} , {1} ]".format(x,y)) # however, if there is not enough overlap, then if there is any overlap at all, # we need to block it to prevent routes coming in on that grid full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) - track_area = (full_rect[1].x-full_rect[0].x)*(full_rect[1].y-full_rect[0].y) overlap_rect=self.compute_overlap(pin.rect,full_rect) - overlap_area = overlap_rect[0]*overlap_rect[1] - debug.info(1,"Check overlap: {0} {1} max={2}".format(pin.rect,overlap_rect,overlap_area)) - - # Assume if more than half the area, it is occupied - overlap_ratio = overlap_area/track_area - if overlap_ratio > 0.25: + min_overlap = min(overlap_rect) + debug.info(3,"Check overlap: {0} . {1} = {2}".format(pin.rect,full_rect,overlap_rect)) + + if min_overlap > spacing: + debug.info(3," Overlap: {0} {1} >? {2}".format(overlap_rect,min_overlap,spacing)) track_list.append(vector3d(x,y,zindex)) # otherwise, the pin may not be accessible, so block it - elif overlap_ratio > 0: + elif min_overlap > 0: + debug.info(3," Insufficient overlap: {0} {1} >? {2}".format(overlap_rect,min_overlap,spacing)) block_list.append(vector3d(x,y,zindex)) else: - debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,overlap_rect,overlap_area)) - # print("H:",x,y) - # if x>38 and x<42 and y>42 and y<45: - # print(pin) - # print(full_rect, overlap_rect, overlap_ratio) - #debug.warning("Off-grid pin for {0}.".format(str(pin))) - #debug.info(1,"Converted [ {0} , {1} ]".format(ll,ur)) - return (track_list,block_list) + debug.info(4," No overlap: {0} {1} max={2}".format(overlap_rect,min_overlap,spacing)) + return (tuple(track_list),tuple(block_list)) def compute_overlap(self, r1, r2): """ Calculate the rectangular overlap of two rectangles. """ @@ -432,8 +432,8 @@ class router: track. """ # to scale coordinates to tracks - x = track.x*self.track_width - 0.5*self.track_width - y = track.y*self.track_width - 0.5*self.track_width + x = track[0]*self.track_width - 0.5*self.track_width + y = track[1]*self.track_width - 0.5*self.track_width # offset lowest corner object to to (-track halo,-track halo) ll = snap_to_grid(vector(x,y)) ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) @@ -497,20 +497,50 @@ class router: """ Convert the pin groups into pin tracks and blockage tracks """ - self.pin_grids[pin_name] = [] - self.pin_blockages[pin_name] = [] - + try: + self.pin_grids[pin_name] + except: + self.pin_grids[pin_name] = [] + try: + self.pin_partials[pin_name] + except: + self.pin_partials[pin_name] = [] + found_pin = False for pg in self.pin_groups[pin_name]: + # Keep the same groups for each pin + self.pin_grids[pin_name].append(set()) + self.pin_partials[pin_name].append(set()) + + pin_set = set() + partial_set = set() for pin in pg: - (pin_in_tracks,blockage_in_tracks)=self.convert_pin_to_tracks(pin) + debug.info(2,"Converting {0}".format(pin)) + (pin_in_tracks,partial_in_tracks)=self.convert_pin_to_tracks(pin) + # In the blockages, what did this shape expand as? + blockage_in_tracks = self.convert_blockage(pin) # At least one of the groups must have some valid tracks if (len(pin_in_tracks)>0): found_pin = True - # We need to route each of the classes, so don't combine the groups - self.pin_grids[pin_name].append(pin_in_tracks) - # However, we can just block all of the partials, so combine the groups - self.pin_blockages[pin_name].extend(blockage_in_tracks) + pin_set.update(pin_in_tracks) + partial_set.update(partial_in_tracks) + partial_set.update(blockage_in_tracks) + + debug.info(2," grids {}".format(pin_set)) + debug.info(2," parts {}".format(partial_set)) + + # We can just block all of the partials, so combine the groups + self.pin_partials[pin_name][-1].add(frozenset(partial_set)) + # We need to route each of the classes, so don't combine the groups + self.pin_grids[pin_name][-1].add(frozenset(pin_set)) + + # This happens when a shape is covered partially by one shape and fully by another + self.pin_partials[pin_name][-1].difference_update(pin_set) + + # These will be blocked depending on what we are routing + self.blocked_grids.difference_update(pin_set) + self.blocked_grids.difference_update(partial_set) + if not found_pin: self.write_debug_gds() @@ -528,6 +558,8 @@ class router: """ This returns how many disconnected pin components there are. """ + debug.check(len(self.pin_grids[pin_name]) == len(self.pin_partials[pin_name]), + "Pin grid and partial blockage component sizes don't match.") return len(self.pin_grids[pin_name]) def add_pin_component(self, pin_name, index, is_source=False): @@ -545,6 +577,7 @@ class router: debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) self.rg.add_target(pin_in_tracks) + def add_supply_rail_target(self, pin_name): """ Add the supply rails of given name as a routing target. @@ -555,15 +588,14 @@ class router: for wave_index in range(len(rail)): pin_in_tracks = rail[wave_index] #debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) - self.rg.add_target(pin_in_tracks) + self.rg.set_target(pin_in_tracks) - def add_component_blockages(self, pin_name): + def set_component_blockages(self, pin_name, value=True): """ Block all of the pin components. """ - for comp_index in range(self.num_pin_components(pin_name)): - pin_in_tracks = self.pin_grids[pin_name][comp_index] - self.add_blockages(pin_in_tracks) + for component in self.pin_grids[pin_name]: + self.set_blockages(component, value) def write_debug_gds(self): @@ -633,12 +665,14 @@ class router: def prepare_path(self,path): """ - Prepare a path or wave for routing + Prepare a path or wave for routing ebedding. + This tracks the path, simplifies the path and marks it as a path for debug output. """ - debug.info(3,"Set path: " + str(path)) + debug.info(4,"Set path: " + str(path)) # Keep track of path for future blockages self.paths.append(path) + path.set_blocked() # This is marked for debug path.set_path() @@ -650,7 +684,7 @@ class router: # First, simplify the path for #debug.info(1,str(self.path)) contracted_path = self.contract_path(path) - debug.info(1,str(contracted_path)) + debug.info(3,"Contracted path: " + str(contracted_path)) return contracted_path @@ -659,7 +693,6 @@ class router: """ Add the current wire route to the given design instance. """ - path=self.prepare_path(path) # convert the path back to absolute units from tracks diff --git a/compiler/router/signal_router.py b/compiler/router/signal_router.py index 547eb002..7acc61a1 100644 --- a/compiler/router/signal_router.py +++ b/compiler/router/signal_router.py @@ -66,12 +66,12 @@ class signal_router(router): self.find_pins(dest) # Now add the blockages - self.add_blockages() - self.add_pin_blockages(src) - self.add_pin_blockages(dest) + self.set_blockages(self.blocked_grids,True) + self.set_blockages(self.pin_partial[src],True) + self.set_blockages(self.pin_partial[dest],True) # Add blockages from previous paths - self.add_path_blockages() + self.set_path_blockages() # Now add the src/tgt if they are not blocked by other shapes diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index c224e66b..28ecc044 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -26,7 +26,6 @@ class supply_router(router): # Power rail width in grid units. self.rail_track_width = 2 - def create_routing_grid(self): @@ -62,30 +61,57 @@ class supply_router(router): self.find_blockages() # Get the pin shapes - self.find_pins(vdd_name) - self.find_pins(gnd_name) - + self.find_pins(self.vdd_name) + self.find_pins(self.gnd_name) - self.add_blockages() - self.add_pin_blockages(vdd_name) - self.route_supply_rails(gnd_name,0) - self.connect_supply_rail(gnd_name) - self.route_pins_to_rails(gnd_name) - - # Start fresh. Not the best for run-time, but simpler. - self.clear_blockages() + # Add the supply rails in a mesh network and connect H/V with vias + self.prepare_blockages(block_names=[self.vdd_name],unblock_names=[self.gnd_name]) + self.route_supply_rails(self.gnd_name,0) - self.add_blockages() - self.add_pin_blockages(gnd_name) - self.route_supply_rails(vdd_name,1) - self.connect_supply_rail(vdd_name) - self.route_pins_to_rails(vdd_name) + self.prepare_blockages(block_names=[self.gnd_name],unblock_names=[self.vdd_name]) + self.route_supply_rails(self.vdd_name,1) + + + # Route the supply pins to the supply rails + #self.route_pins_to_rails(gnd_name) + #self.route_pins_to_rails(vdd_name) self.write_debug_gds() return False + def prepare_blockages(self, block_names=None, unblock_names=None): + """ + Reset and add all of the blockages in the design. + Names is a list of pins to add as a blockage. + """ + # Start fresh. Not the best for run-time, but simpler. + self.clear_blockages() + # This adds the initial blockges of the design + print("BLOCKING:",self.blocked_grids) + self.set_blockages(self.blocked_grids,True) + # This is conservative to prevent DRC violations on partially blocked tracks + if block_names: + for name in block_names: + # These are the partially blocked tracks around supply pins. + print("BLOCKING PARTIALS:",name,self.pin_partials[name]) + self.set_blockages(self.pin_partials[name],True) + # These are the actual supply pins + self.set_blockages(self.pin_grids[name],True) + print("BLOCKING GRIDS:",name,self.pin_grids[name]) + # This will unblock + if unblock_names: + for name in unblock_names: + # These are the partially blocked tracks around supply pins. + self.set_blockages(self.pin_partials[name],False) + print("UNBLOCKING PARTIALS:",name,self.pin_partials[name]) + # These are the actual supply pins + self.set_blockages(self.pin_grids[name],False) + print("UNBLOCKING GRIDS:",name,self.pin_grids[name]) + + # These are the paths that have already been routed. + self.set_path_blockages() - def connect_supply_rail(self, name): + def connect_supply_rails(self, name): """ Add vias between overlapping supply rails. """ @@ -100,7 +126,10 @@ class supply_router(router): for h in horizontal_paths: overlap = v.overlap(h) if overlap: - shared_areas.append(overlap) + (ll,ur) = overlap + # Only add if the overlap is wide enough + if ur.x-ll.x >= self.rail_track_width-1 and ur.y-ll.y >= self.rail_track_width-1: + shared_areas.append(overlap) for (ll,ur) in shared_areas: @@ -140,6 +169,10 @@ class supply_router(router): # Remember index of path size which is how many rails we had at the start self.num_rails = len(self.paths) + # Add teh supply rail vias + self.connect_supply_rails(name) + + def route_supply_rail(self, name, seed_wave, direct): """ This finds the first valid starting location and routes a supply rail @@ -179,20 +212,25 @@ class supply_router(router): After it is done, the cells are added to the pin blockage list. """ + # For every component for index in range(self.num_pin_components(pin_name)): + self.rg.reinit() + + self.prepare_blockages(block_names=None,unblock_names=[pin_name]) + # Block all the pin components first - self.add_component_blockages(pin_name) + self.set_component_blockages(pin_name, True) # Add the single component of the pin as the source # which unmarks it as a blockage too self.add_pin_component(pin_name,index,is_source=True) - + # Add all of the rails as targets # Don't add the other pins, but we could? self.add_supply_rail_target(pin_name) - + # Actually run the A* router self.run_router(detour_scale=5)