OpenRAM/compiler/router/pin_group.py

298 lines
11 KiB
Python
Raw Normal View History

from vector3d import vector3d
from tech import drc
import debug
class pin_group:
"""
A class to represent a group of touching rectangular design pin.
It requires a router to define the track widths and blockages which
determine how pin shapes get mapped to tracks.
"""
def __init__(self, name, pin_shapes, router):
self.name = name
# Flag for when it is routed
self.routed = False
self.pins = pin_shapes
self.router = router
# These are the corresponding pin grids for each pin group.
self.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 set_routed(self, value=True):
self.routed = value
def is_routed(self):
return self.routed
def remove_redundant_shapes(self, pin_list):
"""
Remove any pin layout that is contained within another.
"""
local_debug = False
if local_debug:
debug.info(0,"INITIAL:",pin_list)
# Make a copy of the list to start
new_pin_list = pin_list.copy()
# This is n^2, but the number is small
for pin1 in pin_list:
for pin2 in pin_list:
# Can't contain yourself
if pin1 == pin2:
continue
if pin2.contains(pin1):
# It may have already been removed by being enclosed in another pin
if pin1 in new_pin_list:
new_pin_list.remove(pin1)
if local_debug:
debug.info(0,"FINAL :",new_pin_list)
return new_pin_list
# FIXME: This relies on some technology parameters from router which is not clearn.
def compute_enclosures(self):
"""
Find the minimum rectangle enclosures of the given tracks.
"""
# Enumerate every possible enclosure
pin_list = []
for seed in self.grids:
(ll, ur) = self.enclose_pin_grids(seed)
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
pin_list.append(enclosure)
#return pin_list
# We used to do this, but smaller enclosures can be
return self.remove_redundant_shapes(pin_list)
def compute_enclosure(self, pin, enclosure):
"""
Compute an enclosure to connect the pin to the enclosure shape.
This assumes the shape will be the dimension of the pin.
"""
if pin.xoverlaps(enclosure):
# Is it vertical overlap, extend pin shape to enclosure
plc = pin.lc()
prc = pin.rc()
elc = enclosure.lc()
erc = enclosure.rc()
ymin = min(plc.y,elc.y)
ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc()
puc = pin.uc()
ebc = enclosure.bc()
euc = enclosure.uc()
xmin = min(pbc.x,ebc.x)
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else:
# Neither, so we must do a corner-to corner
pc = pin.center()
ec = enclosure.center()
xmin = min(pc.x, ec.x)
xmax = max(pc.x, ec.x)
ymin = min(pc.y, ec.y)
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
ur = vector(xmax, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
return p
def find_smallest_connector(self, enclosure_list):
"""
Compute all of the connectors between non-overlapping pins and enclosures.
Return the smallest.
"""
smallest = None
for pin in self.pins:
for enclosure in enclosure_list:
new_enclosure = self.compute_enclosure(pin, enclosure)
if smallest == None or new_enclosure.area()<smallest.area():
smallest = new_enclosure
return smallest
def find_smallest_overlapping(self, shape_list):
"""
Find the smallest area shape in shape_list that overlaps with any
pin in pin_list by a min width.
"""
smallest_shape = None
for pin in self.pins:
# They may not be all on the same layer... in the future.
zindex=self.router.get_zindex(pin.layer_num)
(min_width,min_space) = self.router.get_layer_width_space(zindex)
# Now compare it with every other shape to check how much they overlap
for other in shape_list:
overlap_length = pin.overlap_length(other)
if overlap_length > min_width:
if smallest_shape == None or other.area()<smallest_shape.area():
smallest_shape = other
return smallest_shape
def overlap_any_shape(self, pin_list, shape_list):
"""
Does the given pin overlap any of the shapes in the pin list.
"""
for pin in pin_list:
for other in shape_list:
if pin.overlaps(other):
return True
return False
def max_pin_layout(self, pin_list):
"""
Return the max area pin_layout
"""
biggest = pin_list[0]
for pin in pin_list:
if pin.area() > biggest.area():
biggest = pin
return pin
def enclose_pin_grids(self, seed):
"""
This encloses a single pin component with a rectangle
starting with the seed and expanding right until blocked
and then up until blocked.
"""
# We may have started with an empty set
if not self.grids:
return None
# Start with the seed
ll = seed
# Start with the ll and make the widest row
row = [ll]
# Move right while we can
while True:
right = row[-1] + vector3d(1,0,0)
# Can't move if not in the pin shape
if right in self.grids and right not in self.router.blocked_grids:
row.append(right)
else:
break
# Move up while we can
while True:
next_row = [x+vector3d(0,1,0) for x in row]
for cell in next_row:
# Can't move if any cell is not in the pin shape
if cell not in self.grids or cell in self.router.blocked_grids:
break
else:
row = next_row
# Skips the second break
continue
# Breaks from the nested break
break
# Add a shape from ll to ur
ur = row[-1]
return (ll,ur)
def enclose_pin(self):
"""
This will find the biggest rectangle enclosing some grid squares and
put a rectangle over it. It does not enclose grid squares that are blocked
by other shapes.
"""
# Compute the enclosure pin_layout list of the set of tracks
enclosure_list = self.compute_enclosures()
self.enclosure = self.find_smallest_overlapping(enclosure_list)
if not self.enclosure:
self.enclosure = self.find_smallest_connector(enclosure_list)
debug.info(2,"Computed enclosure {0}\n {1}\n {2}\n {3}".format(self.name, self.pins, self.grids, self.enclosure))
def add_enclosure(self, cell):
"""
Add the enclosure shape to the given cell.
"""
debug.info(2,"Adding enclosure {0} {1}".format(self.name, self.enclosure))
cell.add_rect(layer=self.enclosure.layer,
offset=self.enclosure.ll(),
width=self.enclosure.width(),
height=self.enclosure.height())
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:
if g1.adjacent(g2):
return True
return False
def convert_pin(self, router):
#print("PG ",pg)
# Keep the same groups for each pin
pin_set = set()
blockage_set = set()
for pin in self.pins:
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_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_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 shared_set:
debug.info(2,"Removing pins {}".format(shared_set))
shared_set = blockage_set & router.blocked_grids
if shared_set:
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):
self.write_debug_gds("blocked_pin.gds")
debug.error("Unable to find unblocked pin on grid.")
# We need to route each of the components, so don't combine the groups
self.grids = pin_set | blockage_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)