mirror of https://github.com/VLSIDA/OpenRAM.git
Add remove adjacent feature for wide metal spacing
This commit is contained in:
parent
c4163d3401
commit
1344a8f7f1
|
|
@ -30,3 +30,10 @@ class direction(Enum):
|
|||
debug.error("Invalid direction {}".format(dirct))
|
||||
|
||||
return offset
|
||||
|
||||
def cardinal_directions():
|
||||
return [direction.NORTH, direction.EAST, direction.SOUTH, direction.WEST]
|
||||
|
||||
def cardinal_offsets():
|
||||
return [direction.get_offset(d) for d in direction.cardinal_directions()]
|
||||
|
||||
|
|
|
|||
|
|
@ -172,20 +172,7 @@ class grid_path:
|
|||
return neighbors
|
||||
|
||||
def neighbor(self, d):
|
||||
if d==direction.EAST:
|
||||
offset = vector3d(1,0,0)
|
||||
elif d==direction.WEST:
|
||||
offset = vector3d(-1,0,0)
|
||||
elif d==direction.NORTH:
|
||||
offset = vector3d(0,1,0)
|
||||
elif d==direction.SOUTH:
|
||||
offset = vector3d(0,-1,0)
|
||||
elif d==direction.UP:
|
||||
offset = vector3d(0,0,1)
|
||||
elif d==direction.DOWN:
|
||||
offset = vector3d(0,0,-1)
|
||||
else:
|
||||
debug.error("Invalid direction {}".format(d),-1)
|
||||
offset = direction.get_offset(d)
|
||||
|
||||
newwave = [point + offset for point in self.pathlist[-1]]
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ class pin_group:
|
|||
self.name = name
|
||||
# Flag for when it is routed
|
||||
self.routed = False
|
||||
# Flag for when it is enclosed
|
||||
self.enclosed = False
|
||||
|
||||
# This is a list because we can have a pin group of disconnected sets of pins
|
||||
# and these are represented by separate lists
|
||||
if pin_shapes:
|
||||
|
|
@ -25,10 +28,40 @@ class pin_group:
|
|||
self.router = router
|
||||
# These are the corresponding pin grids for each pin group.
|
||||
self.grids = set()
|
||||
# These are the secondary grids that could or could not be part of the pin
|
||||
self.secondary_grids = set()
|
||||
|
||||
# The corresponding set of partially blocked grids for each pin group.
|
||||
# These are blockages for other nets but unblocked for routing this group.
|
||||
self.blockages = set()
|
||||
|
||||
def __str__(self):
|
||||
""" override print function output """
|
||||
total_string = "(pg {} ".format(self.name)
|
||||
|
||||
pin_string = "\n pins={}".format(self.pins)
|
||||
total_string += pin_string
|
||||
|
||||
grids_string = "\n grids={}".format(self.grids)
|
||||
total_string += grids_string
|
||||
|
||||
grids_string = "\n secondary={}".format(self.secondary_grids)
|
||||
total_string += grids_string
|
||||
|
||||
if self.enclosed:
|
||||
enlosure_string = "\n enclose={}".format(self.enclosures)
|
||||
total_string += enclosure_string
|
||||
|
||||
total_string += ")"
|
||||
return total_string
|
||||
|
||||
def __repr__(self):
|
||||
""" override repr function output """
|
||||
return str(self)
|
||||
|
||||
def size(self):
|
||||
return len(self.grids)
|
||||
|
||||
def set_routed(self, value=True):
|
||||
self.routed = value
|
||||
|
||||
|
|
@ -277,22 +310,41 @@ class pin_group:
|
|||
this will find the smallest rectangle enclosure that overlaps with any pin.
|
||||
If there is not, it simply returns all the enclosures.
|
||||
"""
|
||||
self.enclosed = True
|
||||
|
||||
# Compute the enclosure pin_layout list of the set of tracks
|
||||
self.enclosures = self.compute_enclosures()
|
||||
|
||||
# A single set of connected pins is easy, so use the optimized set
|
||||
if len(self.pins)==1:
|
||||
enclosure_list = self.enclosures
|
||||
smallest = self.find_smallest_overlapping(self.pins[0],enclosure_list)
|
||||
if smallest:
|
||||
self.enclosures=[smallest]
|
||||
|
||||
debug.info(2,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
|
||||
# if len(self.pins)==1:
|
||||
# enclosure_list = self.enclosures
|
||||
# smallest = self.find_smallest_overlapping(self.pins[0],enclosure_list)
|
||||
# if smallest:
|
||||
# self.enclosures=[smallest]
|
||||
|
||||
# Save the list of all grids
|
||||
#self.all_grids = self.grids.copy()
|
||||
|
||||
# Remove the grids that are not covered by the enclosures
|
||||
# FIXME: We could probably just store what grids each enclosure overlaps when
|
||||
# it was created.
|
||||
#for enclosure in self.enclosures:
|
||||
# enclosure_in_tracks=router.convert_pin_to_tracks(self.name, enclosure)
|
||||
# self.grids.difference_update(enclosure_in_tracks)
|
||||
|
||||
debug.info(3,"Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
|
||||
self.pins,
|
||||
self.grids,
|
||||
self.enclosures))
|
||||
|
||||
|
||||
def combine_pins(self, pg1, pg2):
|
||||
"""
|
||||
Combine two pin groups into one.
|
||||
"""
|
||||
self.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins
|
||||
self.grids = pg1.grids | pg2.grids # OR the set of grid locations
|
||||
self.secondary_grids = pg1.secondary_grids | pg2.secondary_grids
|
||||
|
||||
def add_enclosure(self, cell):
|
||||
"""
|
||||
Add the enclosure shape to the given cell.
|
||||
|
|
@ -305,23 +357,57 @@ class pin_group:
|
|||
height=enclosure.height())
|
||||
|
||||
|
||||
|
||||
def perimeter_grids(self):
|
||||
"""
|
||||
Return a list of the grids on the perimeter.
|
||||
This assumes that we have a single contiguous shape.
|
||||
"""
|
||||
perimeter_set = set()
|
||||
cardinal_offsets = direction.cardinal_offsets()
|
||||
for g1 in self.grids:
|
||||
neighbor_grids = [g1 + offset for offset in cardinal_offsets]
|
||||
neighbor_count = sum([x in self.grids for x in neighbor_grids])
|
||||
# If we aren't completely enclosed, we are on the perimeter
|
||||
if neighbor_count < 4:
|
||||
perimeter_set.add(g1)
|
||||
|
||||
return perimeter_set
|
||||
|
||||
def adjacent(self, other):
|
||||
"""
|
||||
Chck if the two pin groups have at least one adjacent pin grid.
|
||||
"""
|
||||
# We could optimize this to just check the boundaries
|
||||
for g1 in self.grids:
|
||||
for g2 in other.grids:
|
||||
for g1 in self.perimeter_grids():
|
||||
for g2 in other.perimeter_grids():
|
||||
if g1.adjacent(g2):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def adjacent_grids(self, other, separation):
|
||||
"""
|
||||
Determine the sets of grids that are within a separation distance
|
||||
of any grid in the other set.
|
||||
"""
|
||||
# We could optimize this to just check the boundaries
|
||||
g1_grids = set()
|
||||
g2_grids = set()
|
||||
for g1 in self.grids:
|
||||
for g2 in other.grids:
|
||||
if g1.distance(g2) <= separation:
|
||||
g1_grids.add(g1)
|
||||
g2_grids.add(g2)
|
||||
|
||||
return g1_grids,g2_grids
|
||||
|
||||
def convert_pin(self, router):
|
||||
#print("PG ",pg)
|
||||
# Keep the same groups for each pin
|
||||
"""
|
||||
Convert the list of pin shapes into sets of routing grids.
|
||||
The secondary set of grids are "optional" pin shapes that could be
|
||||
should be either blocked or part of the pin.
|
||||
"""
|
||||
pin_set = set()
|
||||
blockage_set = set()
|
||||
|
||||
|
|
@ -334,19 +420,6 @@ class pin_group:
|
|||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||
blockage_in_tracks = 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
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing pins {}".format(shared_set))
|
||||
shared_set = blockage_set & router.blocked_grids
|
||||
if len(shared_set)>0:
|
||||
debug.info(2,"Removing blocks {}".format(shared_set))
|
||||
pin_set.difference_update(router.blocked_grids)
|
||||
blockage_set.difference_update(router.blocked_grids)
|
||||
debug.info(2," pins {}".format(pin_set))
|
||||
debug.info(2," blocks {}".format(blockage_set))
|
||||
|
||||
# At least one of the groups must have some valid tracks
|
||||
if (len(pin_set)==0 and len(blockage_set)==0):
|
||||
|
|
@ -355,14 +428,9 @@ class pin_group:
|
|||
|
||||
# We need to route each of the components, so don't combine the groups
|
||||
self.grids = pin_set | blockage_set
|
||||
# Remember the secondary grids for removing adjacent pins in wide metal spacing
|
||||
self.secondary_grids = blockage_set - pin_set
|
||||
|
||||
# Add all of the partial blocked grids to the set for the design
|
||||
# if they are not blocked by other metal
|
||||
#partial_set = blockage_set - pin_set
|
||||
#self.blockages = partial_set
|
||||
|
||||
# We should not have added the pins to the blockages,
|
||||
# but remove them just in case
|
||||
# Partial set may still be in the blockages if there were
|
||||
# other shapes disconnected from the pins that were also overlapping
|
||||
#route.blocked_grids.difference_update(pin_set)
|
||||
debug.info(2," pins {}".format(self.grids))
|
||||
debug.info(2," secondary {}".format(self.secondary_grids))
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ class router(router_tech):
|
|||
self.all_pins = set()
|
||||
|
||||
# A map of pin names to a list of pin groups
|
||||
# A pin group is a set overlapping pin shapes on the same layer.
|
||||
self.pin_groups = {}
|
||||
|
||||
### The blockage data structures
|
||||
|
|
@ -157,10 +156,14 @@ class router(router_tech):
|
|||
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
|
||||
|
||||
# Separate any adjacent grids of differing net names to prevent wide metal DRC violations
|
||||
self.separate_adjacent_pins(pin)
|
||||
|
||||
# Must be done before enclosing pins
|
||||
self.separate_adjacent_pins(self.supply_rail_space_width)
|
||||
# For debug
|
||||
#self.separate_adjacent_pins(1)
|
||||
|
||||
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
||||
self.enclose_pins()
|
||||
#self.write_debug_gds("debug_enclose_pins.gds",stop_program=True)
|
||||
|
||||
|
||||
def combine_adjacent_pins_pass(self, pin_name):
|
||||
|
|
@ -191,13 +194,13 @@ class router(router_tech):
|
|||
# Combine if at least 1 grid cell is adjacent
|
||||
if pg1.adjacent(pg2):
|
||||
combined = pin_group(pin_name, [], self)
|
||||
combined.pins = [*pg1.pins, *pg2.pins] # Join the two lists of pins
|
||||
combined.grids = pg1.grids | pg2.grids # OR the set of grid locations
|
||||
debug.info(2,"Combining {0}:\n {1}\n {2}".format(pin_name, pg1.pins, pg2.pins))
|
||||
debug.info(2," --> {0}\n {1}\n".format(combined.pins,combined.grids))
|
||||
combined.combine_pins(pg1, pg2)
|
||||
debug.info(2,"Combining {0} {1} {2}:".format(pin_name, index1, index2))
|
||||
debug.info(2, " {0}\n {1}".format(pg1.pins, pg2.pins))
|
||||
debug.info(2," --> {0}\n {1}".format(combined.pins,combined.grids))
|
||||
remove_indices.update([index1,index2])
|
||||
pin_groups.append(combined)
|
||||
|
||||
break
|
||||
|
||||
# Remove them in decreasing order to not invalidate the indices
|
||||
debug.info(2,"Removing {}".format(sorted(remove_indices)))
|
||||
|
|
@ -207,7 +210,7 @@ class router(router_tech):
|
|||
# Use the new pin group!
|
||||
self.pin_groups[pin_name] = pin_groups
|
||||
|
||||
removed_pairs = len(remove_indices)/2
|
||||
removed_pairs = int(len(remove_indices)/2)
|
||||
debug.info(1, "Combined {0} pin pairs for {1}".format(removed_pairs,pin_name))
|
||||
|
||||
return removed_pairs
|
||||
|
|
@ -229,17 +232,77 @@ class router(router_tech):
|
|||
else:
|
||||
debug.warning("Did not converge combining adjacent pins in supply router.")
|
||||
|
||||
def separate_adjacent_pins(self, pin_name, separation=1):
|
||||
def separate_adjacent_pins(self, separation):
|
||||
"""
|
||||
This will try to separate all grid pins by the supplied number of separation
|
||||
tracks (default is to prevent adjacency).
|
||||
"""
|
||||
# Commented out to debug with SCMOS
|
||||
#if separation==0:
|
||||
# return
|
||||
|
||||
pin_names = self.pin_groups.keys()
|
||||
for pin_name1 in pin_names:
|
||||
for pin_name2 in pin_names:
|
||||
if pin_name1==pin_name2:
|
||||
continue
|
||||
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
|
||||
|
||||
def separate_adjacent_pin(self, pin_name1, pin_name2, separation):
|
||||
"""
|
||||
Go through all of the pin groups and check if any other pin group is
|
||||
within a separation of it.
|
||||
If so, reduce the pin group grid to not include the adjacent grid.
|
||||
Try to do this intelligently to keep th pins enclosed.
|
||||
"""
|
||||
pass
|
||||
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
|
||||
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
|
||||
for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
|
||||
# FIXME: Use separation distance and edge grids only
|
||||
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
|
||||
# These should have the same length, so...
|
||||
if len(grids_g1)>0:
|
||||
debug.info(1,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
|
||||
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
|
||||
|
||||
def remove_adjacent_grid(self, pg1, grids1, pg2, grids2):
|
||||
"""
|
||||
Remove one of the adjacent grids in a heuristic manner.
|
||||
"""
|
||||
# Determine the bigger and smaller group
|
||||
if pg1.size()>pg2.size():
|
||||
bigger = pg1
|
||||
bigger_grids = grids1
|
||||
smaller = pg2
|
||||
smaller_grids = grids2
|
||||
else:
|
||||
bigger = pg2
|
||||
bigger_grids = grids2
|
||||
smaller = pg1
|
||||
smaller_grids = grids1
|
||||
|
||||
# First, see if we can remove grids that are in the secondary grids
|
||||
# i.e. they aren't necessary to the pin grids
|
||||
if bigger_grids.issubset(bigger.secondary_grids):
|
||||
debug.info(1,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
|
||||
bigger.grids.difference_update(bigger_grids)
|
||||
self.blocked_grids.update(bigger_grids)
|
||||
return
|
||||
elif smaller_grids.issubset(smaller.secondary_grids):
|
||||
debug.info(1,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
|
||||
smaller.grids.difference_update(smaller_grids)
|
||||
self.blocked_grids.update(smaller_grids)
|
||||
return
|
||||
|
||||
# If that fails, just randomly remove from the bigger one and give a warning.
|
||||
# This might fail later.
|
||||
debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids))
|
||||
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids))
|
||||
bigger.grids.difference_update(bigger_grids)
|
||||
self.blocked_grids.update(bigger_grids)
|
||||
|
||||
|
||||
|
||||
def prepare_blockages(self, pin_name):
|
||||
"""
|
||||
Reset and add all of the blockages in the design.
|
||||
|
|
@ -975,6 +1038,8 @@ class router(router_tech):
|
|||
if show_enclosures:
|
||||
for key in self.pin_groups.keys():
|
||||
for pg in self.pin_groups[key]:
|
||||
if not pg.enclosed:
|
||||
continue
|
||||
for pin in pg.enclosures:
|
||||
#print("enclosure: ",pin.name,pin.ll(),pin.width(),pin.height())
|
||||
self.cell.add_rect(layer="text",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ class supply_router(router):
|
|||
# Power rail width in grid units.
|
||||
self.rail_track_width = 2
|
||||
|
||||
|
||||
|
||||
def create_routing_grid(self):
|
||||
"""
|
||||
|
|
@ -69,6 +70,9 @@ class supply_router(router):
|
|||
# but this is simplest for now.
|
||||
self.create_routing_grid()
|
||||
|
||||
# Compute the grid dimensions
|
||||
self.compute_supply_rail_dimensions()
|
||||
|
||||
# Get the pin shapes
|
||||
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
||||
#self.write_debug_gds("pin_enclosures.gds",stop_program=True)
|
||||
|
|
@ -87,7 +91,7 @@ class supply_router(router):
|
|||
|
||||
self.route_simple_overlaps(vdd_name)
|
||||
self.route_simple_overlaps(gnd_name)
|
||||
#self.write_debug_gds("debug_simple_route.gds",stop_program=True)
|
||||
self.write_debug_gds("debug_simple_route.gds",stop_program=False)
|
||||
|
||||
# Route the supply pins to the supply rails
|
||||
# Route vdd first since we want it to be shorter
|
||||
|
|
@ -436,9 +440,6 @@ class supply_router(router):
|
|||
Must be done with lower left at 0,0
|
||||
"""
|
||||
|
||||
# Compute the grid dimensions
|
||||
self.compute_supply_rail_dimensions()
|
||||
|
||||
# Compute the grid locations of the supply rails
|
||||
self.compute_supply_rails(name, supply_number)
|
||||
|
||||
|
|
|
|||
|
|
@ -163,6 +163,10 @@ class vector3d():
|
|||
""" Min of both values """
|
||||
return vector3d(min(self.x,other.x),min(self.y,other.y),min(self.z,other.z))
|
||||
|
||||
def distance(self, other):
|
||||
""" Return the planar distance between two values """
|
||||
return abs(self.x-other.x)+abs(self.y-other.y)
|
||||
|
||||
|
||||
def adjacent(self, other):
|
||||
""" Is the one grid adjacent in any planar direction to the other """
|
||||
|
|
|
|||
Loading…
Reference in New Issue