2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-10-29 21:49:29 +01:00
|
|
|
from direction import direction
|
2018-10-26 18:25:10 +02:00
|
|
|
from pin_layout import pin_layout
|
|
|
|
|
from vector import vector
|
2018-10-25 22:36:35 +02:00
|
|
|
import debug
|
|
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
class pin_group:
|
|
|
|
|
"""
|
2019-11-07 17:33:13 +01:00
|
|
|
A class to represent a group of rectangular design pin.
|
|
|
|
|
It requires a router to define the track widths and blockages which
|
|
|
|
|
determine how pin shapes get mapped to tracks.
|
2018-11-01 19:31:24 +01:00
|
|
|
It is initially constructed with a single set of (touching) pins.
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-11-01 19:31:24 +01:00
|
|
|
def __init__(self, name, pin_set, router):
|
2018-10-25 22:36:35 +02:00
|
|
|
self.name = name
|
|
|
|
|
# Flag for when it is routed
|
|
|
|
|
self.routed = False
|
2018-10-30 20:24:13 +01:00
|
|
|
# Flag for when it is enclosed
|
|
|
|
|
self.enclosed = False
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-11-01 19:31:24 +01:00
|
|
|
# Remove any redundant pins (i.e. contained in other pins)
|
|
|
|
|
irredundant_pin_set = self.remove_redundant_shapes(list(pin_set))
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# This is a list because we can have a pin
|
|
|
|
|
# group of disconnected sets of pins
|
2018-10-26 18:25:10 +02:00
|
|
|
# and these are represented by separate lists
|
2019-04-24 19:54:22 +02:00
|
|
|
self.pins = set(irredundant_pin_set)
|
2018-11-01 19:31:24 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
self.router = router
|
|
|
|
|
# These are the corresponding pin grids for each pin group.
|
|
|
|
|
self.grids = set()
|
2019-11-07 17:33:13 +01:00
|
|
|
# These are the secondary grids that could
|
|
|
|
|
# or could not be part of the pin
|
2018-10-30 20:24:13 +01:00
|
|
|
self.secondary_grids = set()
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
# The corresponding set of partially blocked grids for each pin group.
|
2019-11-07 17:33:13 +01:00
|
|
|
# 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.
|
2018-10-25 22:36:35 +02:00
|
|
|
self.blockages = set()
|
|
|
|
|
|
2018-11-01 19:31:24 +01:00
|
|
|
# This is a set of pin_layout shapes to cover the grids
|
|
|
|
|
self.enclosures = set()
|
|
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
def __str__(self):
|
|
|
|
|
""" override print function output """
|
|
|
|
|
total_string = "(pg {} ".format(self.name)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
pin_string = "\n pins={}".format(self.pins)
|
|
|
|
|
total_string += pin_string
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
grids_string = "\n grids={}".format(self.grids)
|
|
|
|
|
total_string += grids_string
|
|
|
|
|
|
|
|
|
|
grids_string = "\n secondary={}".format(self.secondary_grids)
|
|
|
|
|
total_string += grids_string
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
if self.enclosed:
|
2019-04-24 19:54:22 +02:00
|
|
|
enclosure_string = "\n enclose={}".format(self.enclosures)
|
2018-10-30 20:24:13 +01:00
|
|
|
total_string += enclosure_string
|
|
|
|
|
|
|
|
|
|
total_string += ")"
|
|
|
|
|
return total_string
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
""" override repr function output """
|
|
|
|
|
return str(self)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
def size(self):
|
|
|
|
|
return len(self.grids)
|
2018-11-02 19:11:32 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
def set_routed(self, value=True):
|
|
|
|
|
self.routed = value
|
|
|
|
|
|
|
|
|
|
def is_routed(self):
|
|
|
|
|
return self.routed
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
def remove_redundant_shapes(self, pin_list):
|
|
|
|
|
"""
|
|
|
|
|
Remove any pin layout that is contained within another.
|
2018-11-01 19:31:24 +01:00
|
|
|
Returns a new list without modifying pin_list.
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
2018-10-29 21:49:29 +01:00
|
|
|
local_debug = False
|
2018-10-25 22:36:35 +02:00
|
|
|
if local_debug:
|
2019-11-07 17:33:13 +01:00
|
|
|
debug.info(0, "INITIAL: {}".format(pin_list))
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
# Make a copy of the list to start
|
|
|
|
|
new_pin_list = pin_list.copy()
|
2018-10-29 19:18:12 +01:00
|
|
|
|
|
|
|
|
remove_indices = set()
|
2018-10-25 22:36:35 +02:00
|
|
|
# This is n^2, but the number is small
|
2019-11-07 17:33:13 +01:00
|
|
|
for index1, pin1 in enumerate(pin_list):
|
2018-10-29 21:49:29 +01:00
|
|
|
# If we remove this pin, it can't contain other pins
|
2018-10-29 19:18:12 +01:00
|
|
|
if index1 in remove_indices:
|
|
|
|
|
continue
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
for index2, pin2 in enumerate(pin_list):
|
|
|
|
|
# Can't contain yourself,
|
|
|
|
|
# but compare the indices and not the pins
|
2018-10-31 00:52:11 +01:00
|
|
|
# so you can remove duplicate copies.
|
2019-11-07 17:33:13 +01:00
|
|
|
if index1 == index2:
|
2018-10-25 22:36:35 +02:00
|
|
|
continue
|
2018-10-29 21:49:29 +01:00
|
|
|
# If we already removed it, can't remove it again...
|
2018-10-29 19:18:12 +01:00
|
|
|
if index2 in remove_indices:
|
|
|
|
|
continue
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
if pin1.contains(pin2):
|
2018-10-26 19:40:43 +02:00
|
|
|
if local_debug:
|
2019-11-07 17:33:13 +01:00
|
|
|
debug.info(0, "{0} contains {1}".format(pin1, pin2))
|
2018-10-31 23:35:39 +01:00
|
|
|
remove_indices.add(index2)
|
2018-10-29 21:49:29 +01:00
|
|
|
|
2018-10-29 19:18:12 +01:00
|
|
|
# Remove them in decreasing order to not invalidate the indices
|
|
|
|
|
for i in sorted(remove_indices, reverse=True):
|
|
|
|
|
del new_pin_list[i]
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
if local_debug:
|
2019-11-07 17:33:13 +01:00
|
|
|
debug.info(0, "FINAL : {}".format(new_pin_list))
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
return new_pin_list
|
|
|
|
|
|
|
|
|
|
def compute_enclosures(self):
|
|
|
|
|
"""
|
|
|
|
|
Find the minimum rectangle enclosures of the given tracks.
|
|
|
|
|
"""
|
|
|
|
|
# Enumerate every possible enclosure
|
|
|
|
|
pin_list = []
|
|
|
|
|
for seed in self.grids:
|
2019-11-07 17:33:13 +01:00
|
|
|
(ll, ur) = self.enclose_pin_grids(seed,
|
|
|
|
|
direction.NORTH,
|
|
|
|
|
direction.EAST)
|
2018-10-29 21:49:29 +01:00
|
|
|
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
|
|
|
|
|
pin_list.append(enclosure)
|
|
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
(ll, ur) = self.enclose_pin_grids(seed,
|
|
|
|
|
direction.EAST,
|
|
|
|
|
direction.NORTH)
|
2018-10-25 22:36:35 +02:00
|
|
|
enclosure = self.router.compute_pin_enclosure(ll, ur, ll.z)
|
|
|
|
|
pin_list.append(enclosure)
|
|
|
|
|
|
2019-11-14 19:17:20 +01:00
|
|
|
debug.check(len(pin_list) > 0,
|
|
|
|
|
"Did not find any enclosures.")
|
|
|
|
|
|
|
|
|
|
# Now simplify the enclosure list
|
2018-11-01 19:31:24 +01:00
|
|
|
new_pin_list = self.remove_redundant_shapes(pin_list)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
|
|
|
|
debug.check(len(new_pin_list) > 0,
|
|
|
|
|
"Did not find any enclosures.")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-11-14 19:17:20 +01:00
|
|
|
return new_pin_list
|
|
|
|
|
|
2018-10-29 21:49:29 +01:00
|
|
|
def compute_connector(self, pin, enclosure):
|
2019-11-07 17:33:13 +01:00
|
|
|
"""
|
|
|
|
|
Compute a shape to connect the pin to the enclosure shape.
|
2018-10-25 22:36:35 +02:00
|
|
|
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()
|
2019-11-07 17:33:13 +01:00
|
|
|
# erc = enclosure.rc()
|
|
|
|
|
ymin = min(plc.y, elc.y)
|
|
|
|
|
ymax = max(plc.y, elc.y)
|
2018-10-25 22:36:35 +02:00
|
|
|
ll = vector(plc.x, ymin)
|
|
|
|
|
ur = vector(prc.x, ymax)
|
|
|
|
|
elif pin.yoverlaps(enclosure):
|
|
|
|
|
# Is it horizontal overlap, extend pin shape to enclosure
|
|
|
|
|
pbc = pin.bc()
|
|
|
|
|
puc = pin.uc()
|
|
|
|
|
ebc = enclosure.bc()
|
2019-11-07 17:33:13 +01:00
|
|
|
# euc = enclosure.uc()
|
|
|
|
|
xmin = min(pbc.x, ebc.x)
|
|
|
|
|
xmax = max(pbc.x, ebc.x)
|
2018-10-25 22:36:35 +02:00
|
|
|
ll = vector(xmin, pbc.y)
|
|
|
|
|
ur = vector(xmax, puc.y)
|
|
|
|
|
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)
|
|
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
if ll.x == ur.x or ll.y == ur.y:
|
2018-12-04 17:41:57 +01:00
|
|
|
return None
|
|
|
|
|
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
2018-10-25 22:36:35 +02:00
|
|
|
return p
|
|
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
def find_above_connector(self, pin, enclosures):
|
2020-11-03 15:29:17 +01:00
|
|
|
"""
|
2018-10-31 23:35:39 +01:00
|
|
|
Find the enclosure that is to above the pin
|
|
|
|
|
and make a connector to it's upper edge.
|
|
|
|
|
"""
|
|
|
|
|
# Create the list of shapes that contain the pin edge
|
|
|
|
|
edge_list = []
|
|
|
|
|
for shape in enclosures:
|
|
|
|
|
if shape.xcontains(pin):
|
|
|
|
|
edge_list.append(shape)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Sort them by their bottom edge
|
|
|
|
|
edge_list.sort(key=lambda x: x.by(), reverse=True)
|
|
|
|
|
|
|
|
|
|
# Find the bottom edge that is next to the pin's top edge
|
|
|
|
|
above_item = None
|
|
|
|
|
for item in edge_list:
|
2019-11-07 17:33:13 +01:00
|
|
|
if item.by() >= pin.uy():
|
2018-10-31 23:35:39 +01:00
|
|
|
above_item = item
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# There was nothing
|
|
|
|
|
if not above_item:
|
2018-10-31 23:35:39 +01:00
|
|
|
return None
|
|
|
|
|
# If it already overlaps, no connector needed
|
|
|
|
|
if above_item.overlaps(pin):
|
|
|
|
|
return None
|
2018-12-04 17:41:57 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Otherwise, make a connector to the item
|
|
|
|
|
p = self.compute_connector(pin, above_item)
|
|
|
|
|
return p
|
|
|
|
|
|
|
|
|
|
def find_below_connector(self, pin, enclosures):
|
2019-11-07 17:33:13 +01:00
|
|
|
"""
|
2018-10-31 23:35:39 +01:00
|
|
|
Find the enclosure that is below the pin
|
|
|
|
|
and make a connector to it's upper edge.
|
|
|
|
|
"""
|
|
|
|
|
# Create the list of shapes that contain the pin edge
|
|
|
|
|
edge_list = []
|
|
|
|
|
for shape in enclosures:
|
|
|
|
|
if shape.xcontains(pin):
|
|
|
|
|
edge_list.append(shape)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Sort them by their upper edge
|
|
|
|
|
edge_list.sort(key=lambda x: x.uy())
|
|
|
|
|
|
|
|
|
|
# Find the upper edge that is next to the pin's bottom edge
|
|
|
|
|
bottom_item = None
|
|
|
|
|
for item in edge_list:
|
2019-11-07 17:33:13 +01:00
|
|
|
if item.uy() <= pin.by():
|
2018-10-31 23:35:39 +01:00
|
|
|
bottom_item = item
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# There was nothing to the left
|
2019-11-07 17:33:13 +01:00
|
|
|
if not bottom_item:
|
2018-10-31 23:35:39 +01:00
|
|
|
return None
|
|
|
|
|
# If it already overlaps, no connector needed
|
|
|
|
|
if bottom_item.overlaps(pin):
|
|
|
|
|
return None
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Otherwise, make a connector to the item
|
|
|
|
|
p = self.compute_connector(pin, bottom_item)
|
|
|
|
|
return p
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
def find_left_connector(self, pin, enclosures):
|
2019-11-14 19:17:20 +01:00
|
|
|
"""
|
2018-10-31 23:35:39 +01:00
|
|
|
Find the enclosure that is to the left of the pin
|
|
|
|
|
and make a connector to it's right edge.
|
|
|
|
|
"""
|
|
|
|
|
# Create the list of shapes that contain the pin edge
|
|
|
|
|
edge_list = []
|
|
|
|
|
for shape in enclosures:
|
|
|
|
|
if shape.ycontains(pin):
|
|
|
|
|
edge_list.append(shape)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Sort them by their right edge
|
|
|
|
|
edge_list.sort(key=lambda x: x.rx())
|
|
|
|
|
|
|
|
|
|
# Find the right edge that is to the pin's left edge
|
|
|
|
|
left_item = None
|
|
|
|
|
for item in edge_list:
|
2019-11-14 19:17:20 +01:00
|
|
|
if item.rx() <= pin.lx():
|
2018-10-31 23:35:39 +01:00
|
|
|
left_item = item
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# There was nothing to the left
|
2019-11-07 17:33:13 +01:00
|
|
|
if not left_item:
|
2018-10-31 23:35:39 +01:00
|
|
|
return None
|
|
|
|
|
# If it already overlaps, no connector needed
|
|
|
|
|
if left_item.overlaps(pin):
|
|
|
|
|
return None
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Otherwise, make a connector to the item
|
|
|
|
|
p = self.compute_connector(pin, left_item)
|
|
|
|
|
return p
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
def find_right_connector(self, pin, enclosures):
|
2019-11-14 19:17:20 +01:00
|
|
|
"""
|
2018-10-31 23:35:39 +01:00
|
|
|
Find the enclosure that is to the right of the pin
|
|
|
|
|
and make a connector to it's left edge.
|
|
|
|
|
"""
|
|
|
|
|
# Create the list of shapes that contain the pin edge
|
|
|
|
|
edge_list = []
|
|
|
|
|
for shape in enclosures:
|
|
|
|
|
if shape.ycontains(pin):
|
|
|
|
|
edge_list.append(shape)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Sort them by their right edge
|
|
|
|
|
edge_list.sort(key=lambda x: x.lx(), reverse=True)
|
|
|
|
|
|
|
|
|
|
# Find the left edge that is next to the pin's right edge
|
|
|
|
|
right_item = None
|
|
|
|
|
for item in edge_list:
|
2019-11-07 17:33:13 +01:00
|
|
|
if item.lx() >= pin.rx():
|
2018-10-31 23:35:39 +01:00
|
|
|
right_item = item
|
|
|
|
|
else:
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
# There was nothing to the right
|
2019-11-07 17:33:13 +01:00
|
|
|
if not right_item:
|
2018-10-31 23:35:39 +01:00
|
|
|
return None
|
|
|
|
|
# If it already overlaps, no connector needed
|
|
|
|
|
if right_item.overlaps(pin):
|
|
|
|
|
return None
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 23:35:39 +01:00
|
|
|
# Otherwise, make a connector to the item
|
|
|
|
|
p = self.compute_connector(pin, right_item)
|
|
|
|
|
return p
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-31 01:41:29 +01:00
|
|
|
def find_smallest_connector(self, pin_list, shape_list):
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
2019-11-07 17:33:13 +01:00
|
|
|
Compute all of the connectors between the overlapping
|
|
|
|
|
pins and enclosure shape list.
|
2018-10-25 22:36:35 +02:00
|
|
|
Return the smallest.
|
|
|
|
|
"""
|
|
|
|
|
smallest = None
|
2018-10-31 01:41:29 +01:00
|
|
|
for pin in pin_list:
|
|
|
|
|
for enclosure in shape_list:
|
|
|
|
|
new_enclosure = self.compute_connector(pin, enclosure)
|
2019-11-07 17:33:13 +01:00
|
|
|
if not smallest or new_enclosure.area() < smallest.area():
|
2018-10-31 01:41:29 +01:00
|
|
|
smallest = new_enclosure
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
return smallest
|
|
|
|
|
|
2018-10-26 18:25:10 +02:00
|
|
|
def find_smallest_overlapping(self, pin_list, shape_list):
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
2019-11-07 17:33:13 +01:00
|
|
|
Find the smallest area shape in shape_list that overlaps with any
|
2018-10-25 22:36:35 +02:00
|
|
|
pin in pin_list by a min width.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
smallest_shape = None
|
2018-10-26 18:25:10 +02:00
|
|
|
for pin in pin_list:
|
2019-11-07 17:33:13 +01:00
|
|
|
overlap_shape = self.find_smallest_overlapping_pin(pin, shape_list)
|
2018-10-26 18:25:10 +02:00
|
|
|
if overlap_shape:
|
2019-11-07 17:33:13 +01:00
|
|
|
# overlap_length = pin.overlap_length(overlap_shape)
|
|
|
|
|
if not smallest_shape or overlap_shape.area() < smallest_shape.area():
|
2018-10-26 18:25:10 +02:00
|
|
|
smallest_shape = overlap_shape
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-26 18:25:10 +02:00
|
|
|
return smallest_shape
|
2018-10-25 22:36:35 +02:00
|
|
|
|
2018-10-26 18:25:10 +02:00
|
|
|
def find_smallest_overlapping_pin(self, pin, shape_list):
|
|
|
|
|
"""
|
2019-11-07 17:33:13 +01:00
|
|
|
Find the smallest area shape in shape_list that overlaps with any
|
2018-10-26 18:25:10 +02:00
|
|
|
pin in pin_list by a min width.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
smallest_shape = None
|
2019-11-08 16:45:25 +01:00
|
|
|
zindex = self.router.get_zindex(pin.lpp[0])
|
2019-11-07 17:33:13 +01:00
|
|
|
(min_width, min_space) = self.router.get_layer_width_space(zindex)
|
2018-10-26 18:25:10 +02:00
|
|
|
|
|
|
|
|
# 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:
|
2019-11-07 17:33:13 +01:00
|
|
|
if not smallest_shape or other.area() < smallest_shape.area():
|
2018-10-26 18:25:10 +02:00
|
|
|
smallest_shape = other
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
return smallest_shape
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
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):
|
2019-11-07 17:33:13 +01:00
|
|
|
"""
|
2018-10-25 22:36:35 +02:00
|
|
|
Return the max area pin_layout
|
|
|
|
|
"""
|
|
|
|
|
biggest = pin_list[0]
|
|
|
|
|
for pin in pin_list:
|
|
|
|
|
if pin.area() > biggest.area():
|
|
|
|
|
biggest = pin
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
return pin
|
|
|
|
|
|
2018-10-29 21:49:29 +01:00
|
|
|
def enclose_pin_grids(self, ll, dir1=direction.NORTH, dir2=direction.EAST):
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
|
|
|
|
This encloses a single pin component with a rectangle
|
2019-04-24 19:54:22 +02:00
|
|
|
starting with the seed and expanding dir1 until blocked
|
|
|
|
|
and then dir2 until blocked.
|
2018-10-29 21:49:29 +01:00
|
|
|
dir1 and dir2 should be two orthogonal directions.
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
|
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
offset1 = direction.get_offset(dir1)
|
|
|
|
|
offset2 = direction.get_offset(dir2)
|
2018-10-29 21:49:29 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
# We may have started with an empty set
|
2019-11-14 19:17:20 +01:00
|
|
|
debug.check(len(self.grids) > 0, "Cannot seed an grid empty set.")
|
2018-10-25 22:36:35 +02:00
|
|
|
|
|
|
|
|
# Start with the ll and make the widest row
|
|
|
|
|
row = [ll]
|
2018-10-29 21:49:29 +01:00
|
|
|
# Move in dir1 while we can
|
2018-10-25 22:36:35 +02:00
|
|
|
while True:
|
2018-10-29 21:49:29 +01:00
|
|
|
next_cell = row[-1] + offset1
|
2019-11-07 17:33:13 +01:00
|
|
|
# Can't move if not in the pin shape
|
2018-10-29 21:49:29 +01:00
|
|
|
if next_cell in self.grids and next_cell not in self.router.blocked_grids:
|
|
|
|
|
row.append(next_cell)
|
2018-10-25 22:36:35 +02:00
|
|
|
else:
|
|
|
|
|
break
|
2018-10-29 21:49:29 +01:00
|
|
|
# Move in dir2 while we can
|
2018-10-25 22:36:35 +02:00
|
|
|
while True:
|
2018-10-29 21:49:29 +01:00
|
|
|
next_row = [x+offset2 for x in row]
|
2018-10-25 22:36:35 +02:00
|
|
|
for cell in next_row:
|
2019-11-07 17:33:13 +01:00
|
|
|
# Can't move if any cell is not in the pin shape
|
2018-10-25 22:36:35 +02:00
|
|
|
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]
|
2019-11-07 17:33:13 +01:00
|
|
|
return (ll, ur)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
def enclose_pin(self):
|
|
|
|
|
"""
|
2019-11-07 17:33:13 +01:00
|
|
|
If there is one set of connected pin shapes,
|
|
|
|
|
this will find the smallest rectangle enclosure that
|
|
|
|
|
overlaps with any pin.
|
2018-10-29 19:18:12 +01:00
|
|
|
If there is not, it simply returns all the enclosures.
|
2018-10-25 22:36:35 +02:00
|
|
|
"""
|
2018-10-30 20:24:13 +01:00
|
|
|
self.enclosed = True
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
# Compute the enclosure pin_layout list of the set of tracks
|
2018-11-01 19:31:24 +01:00
|
|
|
self.enclosures = self.compute_enclosures()
|
2018-10-31 23:35:39 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
# Find a connector to every pin and add it to the enclosures
|
|
|
|
|
for pin in self.pins:
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
# If it is contained, it won't need a connector
|
|
|
|
|
if pin.contained_by_any(self.enclosures):
|
|
|
|
|
continue
|
2018-11-19 18:28:29 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
# Find a connector in the cardinal directions
|
2019-11-07 17:33:13 +01:00
|
|
|
# If there is overlap, but it isn't contained,
|
|
|
|
|
# these could all be None
|
|
|
|
|
# These could also be none if the pin is
|
|
|
|
|
# diagonal from the enclosure
|
2019-04-24 19:54:22 +02:00
|
|
|
left_connector = self.find_left_connector(pin, self.enclosures)
|
|
|
|
|
right_connector = self.find_right_connector(pin, self.enclosures)
|
|
|
|
|
above_connector = self.find_above_connector(pin, self.enclosures)
|
|
|
|
|
below_connector = self.find_below_connector(pin, self.enclosures)
|
2019-11-07 17:33:13 +01:00
|
|
|
connector_list = [left_connector,
|
|
|
|
|
right_connector,
|
|
|
|
|
above_connector,
|
|
|
|
|
below_connector]
|
2019-11-14 19:17:20 +01:00
|
|
|
filtered_list = list(filter(lambda x: x != None, connector_list))
|
2019-11-07 17:33:13 +01:00
|
|
|
if (len(filtered_list) > 0):
|
2019-04-24 19:54:22 +02:00
|
|
|
import copy
|
2019-11-07 17:33:13 +01:00
|
|
|
bbox_connector = copy.copy(pin)
|
2019-04-24 19:54:22 +02:00
|
|
|
bbox_connector.bbox(filtered_list)
|
|
|
|
|
self.enclosures.append(bbox_connector)
|
2018-11-20 02:32:55 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# Now, make sure each pin touches an enclosure.
|
|
|
|
|
# If not, add another (diagonal) connector.
|
|
|
|
|
# This could only happen when there was no enclosure
|
|
|
|
|
# in any cardinal direction from a pin
|
2019-04-24 19:54:22 +02:00
|
|
|
if not self.overlap_any_shape(self.pins, self.enclosures):
|
2019-11-07 17:33:13 +01:00
|
|
|
connector = self.find_smallest_connector(self.pins,
|
|
|
|
|
self.enclosures)
|
|
|
|
|
if not connector:
|
|
|
|
|
debug.error("Could not find a connector for {} with {}".format(self.pins,
|
|
|
|
|
self.enclosures))
|
2019-04-24 19:54:22 +02:00
|
|
|
self.router.write_debug_gds("no_connector.gds")
|
2019-11-14 19:17:20 +01:00
|
|
|
import pdb; pdb.set_trace()
|
2019-04-24 19:54:22 +02:00
|
|
|
self.enclosures.append(connector)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# At this point, the pins are overlapping,
|
|
|
|
|
# but there might be more than one!
|
2019-04-24 19:54:22 +02:00
|
|
|
overlap_set = set()
|
|
|
|
|
for pin in self.pins:
|
|
|
|
|
overlap_set.update(self.transitive_overlap(pin, self.enclosures))
|
2019-11-07 17:33:13 +01:00
|
|
|
# Use the new enclosures and recompute the grids
|
|
|
|
|
# that correspond to them
|
|
|
|
|
if len(overlap_set) < len(self.enclosures):
|
2019-04-24 19:54:22 +02:00
|
|
|
self.enclosures = overlap_set
|
2019-11-07 17:33:13 +01:00
|
|
|
self.grids = set()
|
|
|
|
|
# Also update the grid locations with the new
|
|
|
|
|
# (possibly pruned) enclosures
|
2019-04-24 19:54:22 +02:00
|
|
|
for enclosure in self.enclosures:
|
2019-11-07 17:33:13 +01:00
|
|
|
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
|
|
|
|
enclosure)
|
2019-04-24 19:54:22 +02:00
|
|
|
self.grids.update(sufficient)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
|
|
|
|
debug.info(3, "Computed enclosure(s) {0}\n {1}\n {2}\n {3}".format(self.name,
|
|
|
|
|
self.pins,
|
|
|
|
|
self.grids,
|
|
|
|
|
self.enclosures))
|
2018-10-25 23:25:52 +02:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
def transitive_overlap(self, shape, shape_list):
|
2018-10-30 20:24:13 +01:00
|
|
|
"""
|
2019-04-24 19:54:22 +02:00
|
|
|
Given shape, find the elements in shape_list that overlap transitively.
|
2019-11-07 17:33:13 +01:00
|
|
|
I.e. if shape overlaps A and A overlaps B, return both A and B.
|
2018-10-30 20:24:13 +01:00
|
|
|
"""
|
|
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
augmented_shape_list = set(shape_list)
|
|
|
|
|
old_connected_set = set()
|
|
|
|
|
connected_set = set([shape])
|
|
|
|
|
# Repeat as long as we expand the set
|
|
|
|
|
while len(connected_set) > len(old_connected_set):
|
|
|
|
|
old_connected_set = connected_set
|
|
|
|
|
connected_set = set([shape])
|
|
|
|
|
for old_shape in old_connected_set:
|
|
|
|
|
for cur_shape in augmented_shape_list:
|
|
|
|
|
if old_shape.overlaps(cur_shape):
|
|
|
|
|
connected_set.add(cur_shape)
|
|
|
|
|
|
|
|
|
|
# Remove the original shape
|
|
|
|
|
connected_set.remove(shape)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
# if len(connected_set)<len(shape_list):
|
|
|
|
|
# import pprint
|
|
|
|
|
# print("S: ",shape)
|
|
|
|
|
# pprint.pprint(shape_list)
|
|
|
|
|
# pprint.pprint(connected_set)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
return connected_set
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 22:36:35 +02:00
|
|
|
def add_enclosure(self, cell):
|
|
|
|
|
"""
|
|
|
|
|
Add the enclosure shape to the given cell.
|
|
|
|
|
"""
|
2018-10-26 18:25:10 +02:00
|
|
|
for enclosure in self.enclosures:
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, "Adding enclosure {0} {1}".format(self.name,
|
2019-11-14 19:17:20 +01:00
|
|
|
enclosure))
|
2018-10-26 18:25:10 +02:00
|
|
|
cell.add_rect(layer=enclosure.layer,
|
|
|
|
|
offset=enclosure.ll(),
|
|
|
|
|
width=enclosure.width(),
|
|
|
|
|
height=enclosure.height())
|
2018-10-25 22:36:35 +02:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
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)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
return perimeter_set
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 23:25:52 +02:00
|
|
|
def adjacent(self, other):
|
2019-11-07 17:33:13 +01:00
|
|
|
"""
|
2018-10-25 23:25:52 +02:00
|
|
|
Chck if the two pin groups have at least one adjacent pin grid.
|
|
|
|
|
"""
|
|
|
|
|
# We could optimize this to just check the boundaries
|
2018-10-30 20:24:13 +01:00
|
|
|
for g1 in self.perimeter_grids():
|
|
|
|
|
for g2 in other.perimeter_grids():
|
2018-10-25 23:25:52 +02:00
|
|
|
if g1.adjacent(g2):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
return False
|
|
|
|
|
|
2018-10-30 20:24:13 +01:00
|
|
|
def adjacent_grids(self, other, separation):
|
2019-11-07 17:33:13 +01:00
|
|
|
"""
|
2018-10-30 20:24:13 +01:00
|
|
|
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
|
2018-12-05 01:53:04 +01:00
|
|
|
adj_grids = set()
|
2018-10-30 20:24:13 +01:00
|
|
|
for g1 in self.grids:
|
|
|
|
|
for g2 in other.grids:
|
|
|
|
|
if g1.distance(g2) <= separation:
|
2018-12-05 01:53:04 +01:00
|
|
|
adj_grids.add(g1)
|
2018-10-30 20:24:13 +01:00
|
|
|
|
2018-12-05 01:53:04 +01:00
|
|
|
return adj_grids
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-11-02 19:11:32 +01:00
|
|
|
def convert_pin(self):
|
2018-10-30 20:24:13 +01:00
|
|
|
"""
|
|
|
|
|
Convert the list of pin shapes into sets of routing grids.
|
2019-11-14 19:17:20 +01:00
|
|
|
The secondary set of grids are "optional" pin shapes that
|
2018-10-30 20:24:13 +01:00
|
|
|
should be either blocked or part of the pin.
|
|
|
|
|
"""
|
2018-10-25 23:25:52 +02:00
|
|
|
pin_set = set()
|
2018-12-05 01:53:04 +01:00
|
|
|
partial_set = set()
|
2018-10-25 23:25:52 +02:00
|
|
|
blockage_set = set()
|
2018-10-29 21:49:29 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
for pin in self.pins:
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, " Converting {0}".format(pin))
|
2019-11-07 17:33:13 +01:00
|
|
|
# Determine which tracks the pin overlaps
|
2019-11-14 19:17:20 +01:00
|
|
|
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
|
|
|
|
pin)
|
2019-04-24 19:54:22 +02:00
|
|
|
pin_set.update(sufficient)
|
|
|
|
|
partial_set.update(insufficient)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# Blockages will be a super-set of pins since
|
|
|
|
|
# it uses the inflated pin shape.
|
|
|
|
|
blockage_in_tracks = self.router.convert_blockage(pin)
|
2019-04-24 19:54:22 +02:00
|
|
|
blockage_set.update(blockage_in_tracks)
|
2018-10-31 00:52:11 +01:00
|
|
|
|
|
|
|
|
# If we have a blockage, we must remove the grids
|
|
|
|
|
# Remember, this excludes the pin blockages already
|
2018-11-02 19:11:32 +01:00
|
|
|
shared_set = pin_set & self.router.blocked_grids
|
2019-11-07 17:33:13 +01:00
|
|
|
if len(shared_set) > 0:
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, "Removing pins {}".format(shared_set))
|
2018-11-09 23:25:10 +01:00
|
|
|
pin_set.difference_update(shared_set)
|
2018-12-05 01:53:04 +01:00
|
|
|
shared_set = partial_set & self.router.blocked_grids
|
2019-11-07 17:33:13 +01:00
|
|
|
if len(shared_set) > 0:
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, "Removing pins {}".format(shared_set))
|
2018-12-05 01:53:04 +01:00
|
|
|
partial_set.difference_update(shared_set)
|
2018-11-02 19:11:32 +01:00
|
|
|
shared_set = blockage_set & self.router.blocked_grids
|
2019-11-07 17:33:13 +01:00
|
|
|
if len(shared_set) > 0:
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, "Removing blocks {}".format(shared_set))
|
2018-11-09 23:25:10 +01:00
|
|
|
blockage_set.difference_update(shared_set)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2018-10-25 23:25:52 +02:00
|
|
|
# At least one of the groups must have some valid tracks
|
2019-11-07 17:33:13 +01:00
|
|
|
if (len(pin_set) == 0 and len(partial_set) == 0 and len(blockage_set) == 0):
|
|
|
|
|
# debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-04-24 19:54:22 +02:00
|
|
|
for pin in self.pins:
|
|
|
|
|
debug.warning(" Expanding conversion {0}".format(pin))
|
2019-11-07 17:33:13 +01:00
|
|
|
# Determine which tracks the pin overlaps
|
2019-11-14 19:17:20 +01:00
|
|
|
(sufficient, insufficient) = self.router.convert_pin_to_tracks(self.name,
|
|
|
|
|
pin,
|
|
|
|
|
expansion=1)
|
2019-04-24 19:54:22 +02:00
|
|
|
pin_set.update(sufficient)
|
|
|
|
|
partial_set.update(insufficient)
|
2019-11-14 19:17:20 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
if len(pin_set) == 0 and len(partial_set) == 0:
|
|
|
|
|
debug.error("Unable to find unblocked pin {} {}".format(self.name,
|
|
|
|
|
self.pins))
|
2018-11-09 23:25:10 +01:00
|
|
|
self.router.write_debug_gds("blocked_pin.gds")
|
2018-10-25 23:25:52 +02:00
|
|
|
|
2018-12-05 01:53:04 +01:00
|
|
|
# Consider all the grids that would be blocked
|
2019-11-07 17:33:13 +01:00
|
|
|
self.grids = pin_set | partial_set
|
2019-11-14 19:17:20 +01:00
|
|
|
if len(self.grids) < 0:
|
|
|
|
|
debug.error("Did not find any unblocked grids: {}".format(str(self.pins)))
|
|
|
|
|
self.router.write_debug_gds("blocked_pin.gds")
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-11-07 17:33:13 +01:00
|
|
|
# Remember the secondary grids for removing adjacent pins
|
|
|
|
|
self.secondary_grids = partial_set
|
2018-10-25 23:25:52 +02:00
|
|
|
|
2020-10-23 18:22:59 +02:00
|
|
|
debug.info(4, " pins {}".format(self.grids))
|
|
|
|
|
debug.info(4, " secondary {}".format(self.secondary_grids))
|