Merge branch 'supply_routing' of https://github.com/VLSIDA/PrivateRAM into supply_routing

This commit is contained in:
Matt Guthaus 2018-11-02 11:13:34 -07:00
commit 7e39150c38
5 changed files with 219 additions and 147 deletions

View File

@ -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()]

View File

@ -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.y<maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.EAST:
for c in curset:
if len(maxc)==0 or c.x>maxc[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.x<maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
"""
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.y<maxc[0].y:
maxc = [c]
elif c.y==maxc[0].y:
maxc.append(c)
elif direct==direct.EAST:
for c in curset:
if len(maxc)==0 or c.x>maxc[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.x<maxc[0].x:
maxc = [c]
elif c.x==maxc[0].x:
maxc.append(c)
newset = set(maxc)
return newset
newset = set(maxc)
return newset
def expand_border(curset, direct):
"""
Expand the current set of sells in a given direction.
Only return the contiguous cells.
"""
border_set = get_border(curset, direct)
next_border_set = increment_set(border_set, direct)
return next_border_set
"""
Expand the current set of sells in a given direction.
Only return the contiguous cells.
"""
border_set = get_border(curset, direct)
next_border_set = increment_set(border_set, direct)
return next_border_set
def expand_borders(curset):
"""
@ -94,6 +95,47 @@ def expand_borders(curset):
south_set=expand_border(curset,direction.SOUTH)
east_set=expand_border(curset,direction.EAST)
west_set=expand_border(curset,direction.WEST)
return(north_set, east_set, south_set, west_set)
def inflate_cell(cell, distance):
"""
Expand the current cell in all directions and return the set.
"""
newset = set(cell)
if distance==0:
return(newset)
# recursively call this based on the distance
for offset in direction.all_offsets():
# FIXME: If distance is large this will be inefficient, but it is like 1 or 2
newset.update(inflate_cell(cell+offset,distance-1))
return newset
def inflate_set(curset, distance):
"""
Expand the set in all directions by the given number of grids.
"""
if distance<=0:
return curset
newset = curset.copy()
# Add all my neighbors
for c in curset:
newset.update(direction.all_neighbors(c))
# Recurse with less depth
return inflate_set(newset,distance-1)
def flatten_set(curset):
"""
Flatten until we have a set of vector3d objects.
"""
newset = set()
for c in curset:
if isinstance(c,vector3d):
newset.add(c)
else:
newset.update(flatten_set(c))
return newset

View File

@ -2,6 +2,7 @@ from direction import direction
from pin_layout import pin_layout
from vector3d import vector3d
from vector import vector
import grid_utils
from tech import drc
import debug
@ -34,6 +35,7 @@ class pin_group:
# 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.
self.blockages = set()
# This is a set of pin_layout shapes to cover the grids
@ -65,7 +67,7 @@ class pin_group:
def size(self):
return len(self.grids)
def set_routed(self, value=True):
self.routed = value
@ -550,7 +552,7 @@ class pin_group:
return g1_grids,g2_grids
def convert_pin(self, router):
def convert_pin(self):
"""
Convert the list of pin shapes into sets of routing grids.
The secondary set of grids are "optional" pin shapes that could be
@ -563,25 +565,25 @@ class pin_group:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=router.convert_pin_to_tracks(self.name, pin)
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks)
# Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = router.convert_blockage(pin)
blockage_in_tracks = self.router.convert_blockage(pin)
blockage_set.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 & router.blocked_grids
shared_set = pin_set & self.router.blocked_grids
if len(shared_set)>0:
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()

View File

@ -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")

View File

@ -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):