mirror of https://github.com/VLSIDA/OpenRAM.git
Add expanded blockages for paths an enclosures to handle wide metal spacing rules.
This commit is contained in:
parent
b24c8a42a1
commit
4d30f214da
|
|
@ -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()]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in New Issue