Converting grid data structures to sets to reduce size.

This commit is contained in:
Matt Guthaus 2018-09-13 09:10:29 -07:00
parent 2d86492d91
commit 849293b95b
4 changed files with 181 additions and 116 deletions

View File

@ -37,7 +37,7 @@ class grid:
self.map={} self.map={}
def set_blocked(self,n,value=True): def set_blocked(self,n,value=True):
if isinstance(n,list): if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
self.set_blocked(item,value) self.set_blocked(item,value)
else: else:
@ -45,7 +45,7 @@ class grid:
self.map[n].blocked=value self.map[n].blocked=value
def is_blocked(self,n): def is_blocked(self,n):
if isinstance(n,list): if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
if self.is_blocked(item): if self.is_blocked(item):
return True return True
@ -57,39 +57,33 @@ class grid:
def set_path(self,n,value=True): def set_path(self,n,value=True):
if isinstance(n,list): if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
self.set_path(item,value) self.set_path(item,value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].path=value 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): def clear_blockages(self):
debug.info(2,"Clearing all blockages") debug.info(2,"Clearing all blockages")
for n in self.map.keys(): self.set_blocked(set(self.map.keys()),False)
self.set_blocked(n,False)
def set_source(self,n): def set_source(self,n,value=True):
if isinstance(n,list): if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
self.set_source(item) self.set_source(item,value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].source=True self.map[n].source=value
self.source.append(n) self.source.append(n)
def set_target(self,n): def set_target(self,n,value=True):
if isinstance(n,list): if isinstance(n, (list,tuple,set,frozenset)):
for item in n: for item in n:
self.set_target(item) self.set_target(item,value)
else: else:
self.add_map(n) self.add_map(n)
self.map[n].target=True self.map[n].target=value
self.target.append(n) self.target.append(n)
@ -101,11 +95,11 @@ class grid:
self.set_blocked(n,False) 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))) debug.info(3,"Adding target list={0}".format(str(track_list)))
for n in track_list: for n in track_list:
debug.info(4,"Adding target ={0}".format(str(n))) debug.info(4,"Adding target ={0}".format(str(n)))
self.set_target(n) self.set_target(n,value)
self.set_blocked(n,False) self.set_blocked(n,False)
def is_target(self,point): def is_target(self,point):
@ -118,7 +112,7 @@ class grid:
""" """
Add a point to the map if it doesn't exist. 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: for item in n:
self.add_map(item) self.add_map(item)
else: else:

View File

@ -40,15 +40,17 @@ class router:
self.pins = {} self.pins = {}
# A set of connected pin groups # A set of connected pin groups
self.pin_groups = {} self.pin_groups = {}
# The corresponding sets of grids of the groups # The corresponding sets of grids for each pin
self.pin_grids = {} self.pin_grids = {}
# The set of partially covered pins to avoid # The set of partially covered pins to avoid for each pin
self.pin_blockages = {} 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=[] self.blockages=[]
# all the paths we've routed so far (to supplement the blockages) # A list of paths that are blocked
self.paths = [] self.paths = []
# The boundary will determine the limits to the size of the routing grid # The boundary will determine the limits to the size of the routing grid
@ -65,7 +67,7 @@ class router:
self.pins = {} self.pins = {}
self.pin_groups = {} self.pin_groups = {}
self.pin_grids = {} self.pin_grids = {}
self.pin_blockages = {} self.pin_paritals = {}
self.reinit() self.reinit()
def set_top(self,top_name): def set_top(self,top_name):
@ -174,7 +176,7 @@ class router:
self.pins = {} self.pins = {}
self.pin_groups = {} self.pin_groups = {}
self.pin_grids = {} self.pin_grids = {}
self.pin_blockages = {} self.pin_partials = {}
# DO NOT clear the blockages as these don't change # DO NOT clear the blockages as these don't change
self.rg.reinit() self.rg.reinit()
@ -243,41 +245,44 @@ class router:
""" """
self.rg.clear_blockages() self.rg.clear_blockages()
def add_pin_blockages(self, pin_name): def set_blockages(self, blockages, value=True):
""" 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):
""" Flag the blockages in the grid """ """ Flag the blockages in the grid """
if blockages == None: self.rg.set_blocked(blockages, value)
blockages = self.blockage_grids
self.rg.set_blockages(blockages) 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): 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 = [] block_list = []
for x in range(int(ll[0]),int(ur[0])+1): for x in range(int(ll[0]),int(ur[0])+1):
for y in range(int(ll[1]),int(ur[1])+1): for y in range(int(ll[1]),int(ur[1])+1):
block_list.append(vector3d(x,y,z)) 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): def convert_blockages(self):
""" Convert blockages to grid tracks. """ """ Convert blockages to grid tracks. """
blockage_grids = []
for blockage in self.blockages: for blockage in self.blockages:
debug.info(2,"Converting blockage {}".format(str(blockage))) debug.info(3,"Converting blockage {}".format(str(blockage)))
# Inflate the blockage by spacing rule blockage_list = self.convert_blockage(blockage)
[ll,ur]=self.convert_blockage_to_tracks(blockage.inflate()) self.blocked_grids.update(blockage_list)
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
def retrieve_blockages(self, layer_num): 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. If a pin has insufficent overlap, it returns the blockage list to avoid it.
""" """
(ll,ur) = pin.rect (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 # scale the size bigger to include neaby tracks
ll=ll.scale(self.track_factor).floor() ll=ll.scale(self.track_factor).floor()
@ -350,41 +355,36 @@ class router:
# width depends on which layer it is # width depends on which layer it is
zindex=self.get_zindex(pin.layer_num) zindex=self.get_zindex(pin.layer_num)
if zindex: if zindex:
width = self.vert_layer_width width = self.vert_layer_width
spacing = self.vert_layer_spacing
else: else:
width = self.horiz_layer_width width = self.horiz_layer_width
spacing = self.horiz_layer_spacing
track_list = [] track_list = []
block_list = [] block_list = []
for x in range(int(ll[0]),int(ur[0])): for x in range(int(ll[0]),int(ur[0])+1):
for y in range(int(ll[1]),int(ur[1])): for y in range(int(ll[1]),int(ur[1])+1):
debug.info(1,"Converting [ {0} , {1} ]".format(x,y)) debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
# however, if there is not enough overlap, then if there is any overlap at all, # 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 # we need to block it to prevent routes coming in on that grid
full_rect = self.convert_track_to_shape(vector3d(x,y,zindex)) 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_rect=self.compute_overlap(pin.rect,full_rect)
overlap_area = overlap_rect[0]*overlap_rect[1] min_overlap = min(overlap_rect)
debug.info(1,"Check overlap: {0} {1} max={2}".format(pin.rect,overlap_rect,overlap_area)) debug.info(3,"Check overlap: {0} . {1} = {2}".format(pin.rect,full_rect,overlap_rect))
# Assume if more than half the area, it is occupied if min_overlap > spacing:
overlap_ratio = overlap_area/track_area debug.info(3," Overlap: {0} {1} >? {2}".format(overlap_rect,min_overlap,spacing))
if overlap_ratio > 0.25:
track_list.append(vector3d(x,y,zindex)) track_list.append(vector3d(x,y,zindex))
# otherwise, the pin may not be accessible, so block it # 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)) block_list.append(vector3d(x,y,zindex))
else: else:
debug.info(4,"No overlap: {0} {1} max={2}".format(pin.rect,overlap_rect,overlap_area)) debug.info(4," No overlap: {0} {1} max={2}".format(overlap_rect,min_overlap,spacing))
# print("H:",x,y) return (tuple(track_list),tuple(block_list))
# 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)
def compute_overlap(self, r1, r2): def compute_overlap(self, r1, r2):
""" Calculate the rectangular overlap of two rectangles. """ """ Calculate the rectangular overlap of two rectangles. """
@ -432,8 +432,8 @@ class router:
track. track.
""" """
# to scale coordinates to tracks # to scale coordinates to tracks
x = track.x*self.track_width - 0.5*self.track_width x = track[0]*self.track_width - 0.5*self.track_width
y = track.y*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) # offset lowest corner object to to (-track halo,-track halo)
ll = snap_to_grid(vector(x,y)) ll = snap_to_grid(vector(x,y))
ur = snap_to_grid(ll + vector(self.track_width,self.track_width)) 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 Convert the pin groups into pin tracks and blockage tracks
""" """
self.pin_grids[pin_name] = [] try:
self.pin_blockages[pin_name] = [] 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 found_pin = False
for pg in self.pin_groups[pin_name]: 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: 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 # At least one of the groups must have some valid tracks
if (len(pin_in_tracks)>0): if (len(pin_in_tracks)>0):
found_pin = True found_pin = True
# We need to route each of the classes, so don't combine the groups pin_set.update(pin_in_tracks)
self.pin_grids[pin_name].append(pin_in_tracks) partial_set.update(partial_in_tracks)
# However, we can just block all of the partials, so combine the groups partial_set.update(blockage_in_tracks)
self.pin_blockages[pin_name].extend(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: if not found_pin:
self.write_debug_gds() self.write_debug_gds()
@ -528,6 +558,8 @@ class router:
""" """
This returns how many disconnected pin components there are. 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]) return len(self.pin_grids[pin_name])
def add_pin_component(self, pin_name, index, is_source=False): 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)) debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks))
self.rg.add_target(pin_in_tracks) self.rg.add_target(pin_in_tracks)
def add_supply_rail_target(self, pin_name): def add_supply_rail_target(self, pin_name):
""" """
Add the supply rails of given name as a routing target. Add the supply rails of given name as a routing target.
@ -555,15 +588,14 @@ class router:
for wave_index in range(len(rail)): for wave_index in range(len(rail)):
pin_in_tracks = rail[wave_index] pin_in_tracks = rail[wave_index]
#debug.info(1,"Set target: " + str(pin_name) + " " + str(pin_in_tracks)) #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. Block all of the pin components.
""" """
for comp_index in range(self.num_pin_components(pin_name)): for component in self.pin_grids[pin_name]:
pin_in_tracks = self.pin_grids[pin_name][comp_index] self.set_blockages(component, value)
self.add_blockages(pin_in_tracks)
def write_debug_gds(self): def write_debug_gds(self):
@ -633,12 +665,14 @@ class router:
def prepare_path(self,path): 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 # Keep track of path for future blockages
self.paths.append(path) self.paths.append(path)
path.set_blocked()
# This is marked for debug # This is marked for debug
path.set_path() path.set_path()
@ -650,7 +684,7 @@ class router:
# First, simplify the path for # First, simplify the path for
#debug.info(1,str(self.path)) #debug.info(1,str(self.path))
contracted_path = self.contract_path(path) contracted_path = self.contract_path(path)
debug.info(1,str(contracted_path)) debug.info(3,"Contracted path: " + str(contracted_path))
return contracted_path return contracted_path
@ -659,7 +693,6 @@ class router:
""" """
Add the current wire route to the given design instance. Add the current wire route to the given design instance.
""" """
path=self.prepare_path(path) path=self.prepare_path(path)
# convert the path back to absolute units from tracks # convert the path back to absolute units from tracks

View File

@ -66,12 +66,12 @@ class signal_router(router):
self.find_pins(dest) self.find_pins(dest)
# Now add the blockages # Now add the blockages
self.add_blockages() self.set_blockages(self.blocked_grids,True)
self.add_pin_blockages(src) self.set_blockages(self.pin_partial[src],True)
self.add_pin_blockages(dest) self.set_blockages(self.pin_partial[dest],True)
# Add blockages from previous paths # 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 # Now add the src/tgt if they are not blocked by other shapes

View File

@ -26,7 +26,6 @@ class supply_router(router):
# Power rail width in grid units. # Power rail width in grid units.
self.rail_track_width = 2 self.rail_track_width = 2
def create_routing_grid(self): def create_routing_grid(self):
@ -62,30 +61,57 @@ class supply_router(router):
self.find_blockages() self.find_blockages()
# Get the pin shapes # Get the pin shapes
self.find_pins(vdd_name) self.find_pins(self.vdd_name)
self.find_pins(gnd_name) self.find_pins(self.gnd_name)
self.add_blockages() # Add the supply rails in a mesh network and connect H/V with vias
self.add_pin_blockages(vdd_name) self.prepare_blockages(block_names=[self.vdd_name],unblock_names=[self.gnd_name])
self.route_supply_rails(gnd_name,0) self.route_supply_rails(self.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()
self.add_blockages() self.prepare_blockages(block_names=[self.gnd_name],unblock_names=[self.vdd_name])
self.add_pin_blockages(gnd_name) self.route_supply_rails(self.vdd_name,1)
self.route_supply_rails(vdd_name,1)
self.connect_supply_rail(vdd_name)
self.route_pins_to_rails(vdd_name) # 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() self.write_debug_gds()
return False 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. Add vias between overlapping supply rails.
""" """
@ -100,7 +126,10 @@ class supply_router(router):
for h in horizontal_paths: for h in horizontal_paths:
overlap = v.overlap(h) overlap = v.overlap(h)
if overlap: 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: 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 # Remember index of path size which is how many rails we had at the start
self.num_rails = len(self.paths) 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): def route_supply_rail(self, name, seed_wave, direct):
""" """
This finds the first valid starting location and routes a supply rail 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. After it is done, the cells are added to the pin blockage list.
""" """
# For every component # For every component
for index in range(self.num_pin_components(pin_name)): 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 # 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 # Add the single component of the pin as the source
# which unmarks it as a blockage too # which unmarks it as a blockage too
self.add_pin_component(pin_name,index,is_source=True) self.add_pin_component(pin_name,index,is_source=True)
# Add all of the rails as targets # Add all of the rails as targets
# Don't add the other pins, but we could? # Don't add the other pins, but we could?
self.add_supply_rail_target(pin_name) self.add_supply_rail_target(pin_name)
# Actually run the A* router # Actually run the A* router
self.run_router(detour_scale=5) self.run_router(detour_scale=5)