mirror of https://github.com/VLSIDA/OpenRAM.git
Split graph router class to use it for signal escaping later
This commit is contained in:
parent
7be6f2783b
commit
db2a276077
|
|
@ -260,7 +260,7 @@ class sram_1bank(design, verilog, lef):
|
|||
elif OPTS.route_supplies == "tree":
|
||||
from openram.router import supply_tree_router as router
|
||||
else:
|
||||
from openram.router import graph_router as router
|
||||
from openram.router import supply_graph_router as router
|
||||
rtr=router(layers=self.supply_stack,
|
||||
design=self,
|
||||
bbox=bbox,
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ from .signal_escape_router import *
|
|||
from .signal_router import *
|
||||
from .supply_grid_router import *
|
||||
from .supply_tree_router import *
|
||||
from .graph_router import *
|
||||
from .supply_graph_router import *
|
||||
|
|
|
|||
|
|
@ -5,25 +5,22 @@
|
|||
#
|
||||
from openram import debug
|
||||
from openram.base.vector import vector
|
||||
from openram.base.vector3d import vector3d
|
||||
from openram.gdsMill import gdsMill
|
||||
from openram.tech import GDS
|
||||
from openram.tech import drc
|
||||
from openram.tech import layer as tech_layer
|
||||
from openram import OPTS
|
||||
from .router_tech import router_tech
|
||||
from .graph import graph
|
||||
from .graph_shape import graph_shape
|
||||
from .graph_utils import snap
|
||||
|
||||
|
||||
class graph_router(router_tech):
|
||||
"""
|
||||
This is the router class that uses the Hanan grid method to route pins using
|
||||
a graph.
|
||||
This is the base class for routers that use the Hanan grid graph method.
|
||||
"""
|
||||
|
||||
def __init__(self, layers, design, bbox=None, pin_type=None):
|
||||
def __init__(self, layers, design, bbox=None):
|
||||
|
||||
# `router_tech` contains tech constants for the router
|
||||
router_tech.__init__(self, layers, route_track_width=1)
|
||||
|
|
@ -32,9 +29,6 @@ class graph_router(router_tech):
|
|||
self.layers = layers
|
||||
# This is the `hierarchy_layout` object
|
||||
self.design = design
|
||||
# Side supply pin type
|
||||
# (can be "top", "bottom", "right", "left", and "ring")
|
||||
self.pin_type = pin_type
|
||||
# Temporary GDSII file name to find pins and blockages
|
||||
self.gds_filename = OPTS.openram_temp + "temp.gds"
|
||||
# Dictionary for vdd and gnd pins
|
||||
|
|
@ -46,8 +40,6 @@ class graph_router(router_tech):
|
|||
self.blockages = []
|
||||
# This is all the vias between routing layers
|
||||
self.vias = []
|
||||
# New pins are the side supply pins
|
||||
self.new_pins = {}
|
||||
# Fake pins are imaginary pins on the side supply pins to route other
|
||||
# pins to them
|
||||
self.fake_pins = []
|
||||
|
|
@ -56,81 +48,6 @@ class graph_router(router_tech):
|
|||
self.offset = snap(self.track_wire / 2)
|
||||
|
||||
|
||||
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||
""" Route the given pins in the given order. """
|
||||
debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name))
|
||||
|
||||
# Save pin names
|
||||
self.vdd_name = vdd_name
|
||||
self.gnd_name = gnd_name
|
||||
|
||||
# Prepare gdsMill to find pins and blockages
|
||||
self.prepare_gds_reader()
|
||||
|
||||
# Find pins to be routed
|
||||
self.find_pins(vdd_name)
|
||||
self.find_pins(gnd_name)
|
||||
|
||||
# Find blockages and vias
|
||||
self.find_blockages()
|
||||
self.find_vias()
|
||||
|
||||
# Convert blockages and vias if they overlap a pin
|
||||
self.convert_vias()
|
||||
self.convert_blockages()
|
||||
|
||||
# Add side pins
|
||||
self.calculate_ring_bbox()
|
||||
if self.pin_type in ["top", "bottom", "right", "left"]:
|
||||
self.add_side_pin(vdd_name)
|
||||
self.add_side_pin(gnd_name)
|
||||
elif self.pin_type == "ring":
|
||||
self.add_ring_pin(vdd_name)
|
||||
self.add_ring_pin(gnd_name)
|
||||
else:
|
||||
debug.warning("Side supply pins aren't created.")
|
||||
|
||||
# Add vdd and gnd pins as blockages as well
|
||||
# NOTE: This is done to make vdd and gnd pins DRC-safe
|
||||
for pin in self.all_pins:
|
||||
self.blockages.append(self.inflate_shape(pin, is_pin=True))
|
||||
|
||||
# Route vdd and gnd
|
||||
for pin_name in [vdd_name, gnd_name]:
|
||||
pins = self.pins[pin_name]
|
||||
# Route closest pins according to the minimum spanning tree
|
||||
for source, target in self.get_mst_pairs(list(pins)):
|
||||
# This is the routing region scale
|
||||
scale = 1
|
||||
while True:
|
||||
# Create the graph
|
||||
g = graph(self)
|
||||
region = g.create_graph(source, target, scale)
|
||||
# Find the shortest path from source to target
|
||||
path = g.find_shortest_path()
|
||||
# If there is no path found, exponentially try again with a
|
||||
# larger routing region
|
||||
if path is None:
|
||||
rll, rur = region
|
||||
bll, bur = self.ring_bbox
|
||||
# Stop scaling the region and throw an error
|
||||
if rll.x < bll.x and rll.y < bll.y and \
|
||||
rur.x > bur.x and rur.y > bur.y:
|
||||
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
|
||||
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
|
||||
# Exponentially scale the region
|
||||
scale *= 2
|
||||
debug.info(0, "Retry routing in larger routing region with scale {}".format(scale))
|
||||
continue
|
||||
# Create the path shapes on layout
|
||||
self.add_path(path)
|
||||
# Find the recently added shapes
|
||||
self.prepare_gds_reader()
|
||||
self.find_blockages(pin_name)
|
||||
self.find_vias()
|
||||
break
|
||||
|
||||
|
||||
def prepare_gds_reader(self):
|
||||
""" Write the current layout to a temporary file to read the layout. """
|
||||
|
||||
|
|
@ -299,195 +216,6 @@ class graph_router(router_tech):
|
|||
extra_spacing=self.offset)
|
||||
|
||||
|
||||
def calculate_ring_bbox(self, num_vias=3):
|
||||
""" Calculate the ring-safe bounding box of the layout. """
|
||||
|
||||
ll, ur = self.design.get_bbox()
|
||||
# Calculate the "wideness" of a side supply pin
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
# Total wideness is used to find it any pin overlaps in this region. If
|
||||
# so, the bbox is shifted to prevent this overlap.
|
||||
total_wideness = wideness * 4
|
||||
for blockage in self.blockages:
|
||||
bll, bur = blockage.rect
|
||||
if self.get_zindex(blockage.lpp) == 1: # Vertical
|
||||
diff = ll.x + total_wideness - bll.x
|
||||
if diff > 0:
|
||||
ll = vector(ll.x - diff, ll.y)
|
||||
diff = ur.x - total_wideness - bur.x
|
||||
if diff < 0:
|
||||
ur = vector(ur.x - diff, ur.y)
|
||||
else: # Horizontal
|
||||
diff = ll.y + total_wideness - bll.y
|
||||
if diff > 0:
|
||||
ll = vector(ll.x, ll.y - diff)
|
||||
diff = ur.y - total_wideness - bur.y
|
||||
if diff < 0:
|
||||
ur = vector(ur.x, ur.y - diff)
|
||||
self.ring_bbox = [ll, ur]
|
||||
|
||||
|
||||
def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):
|
||||
""" Add supply pin to one side of the layout. """
|
||||
|
||||
ll, ur = self.ring_bbox
|
||||
vertical = side in ["left", "right"]
|
||||
inner = pin_name == self.gnd_name
|
||||
|
||||
# Calculate wires' wideness
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
|
||||
# Calculate the offset for the inner ring
|
||||
if inner:
|
||||
margin = wideness * 2
|
||||
else:
|
||||
margin = 0
|
||||
|
||||
# Calculate the lower left coordinate
|
||||
if side == "top":
|
||||
offset = vector(ll.x + margin, ur.y - wideness - margin)
|
||||
elif side == "bottom":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "left":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "right":
|
||||
offset = vector(ur.x - wideness - margin, ll.y + margin)
|
||||
|
||||
# Calculate width and height
|
||||
shape = ur - ll
|
||||
if vertical:
|
||||
shape_width = wideness
|
||||
shape_height = shape.y
|
||||
else:
|
||||
shape_width = shape.x
|
||||
shape_height = wideness
|
||||
if inner:
|
||||
if vertical:
|
||||
shape_height -= margin * 2
|
||||
else:
|
||||
shape_width -= margin * 2
|
||||
|
||||
# Add this new pin
|
||||
layer = self.get_layer(int(vertical))
|
||||
pin = self.design.add_layout_pin(text=pin_name,
|
||||
layer=layer,
|
||||
offset=offset,
|
||||
width=shape_width,
|
||||
height=shape_height)
|
||||
|
||||
# Add fake pins on this new pin evenly
|
||||
fake_pins = []
|
||||
if vertical:
|
||||
space = (shape_height - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x, offset.y + wideness)
|
||||
else:
|
||||
space = (shape_width - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x + wideness, offset.y)
|
||||
for i in range(1, num_fake_pins + 1):
|
||||
if vertical:
|
||||
offset = vector(start_offset.x, start_offset.y + i * (space + self.track_wire))
|
||||
ll = vector(offset.x, offset.y - self.track_wire)
|
||||
ur = vector(offset.x + wideness, offset.y)
|
||||
else:
|
||||
offset = vector(start_offset.x + i * (space + self.track_wire), start_offset.y)
|
||||
ll = vector(offset.x - self.track_wire, offset.y)
|
||||
ur = vector(offset.x, offset.y + wideness)
|
||||
rect = [ll, ur]
|
||||
fake_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
fake_pins.append(fake_pin)
|
||||
return pin, fake_pins
|
||||
|
||||
|
||||
def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4):
|
||||
""" Add the supply ring to the layout. """
|
||||
|
||||
# Add side pins
|
||||
new_pins = []
|
||||
for side in ["top", "bottom", "right", "left"]:
|
||||
new_shape, fake_pins = self.add_side_pin(pin_name, side, num_vias, num_fake_pins)
|
||||
ll, ur = new_shape.rect
|
||||
rect = [ll, ur]
|
||||
layer = self.get_layer(side in ["left", "right"])
|
||||
new_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
new_pins.append(new_pin)
|
||||
self.pins[pin_name].update(fake_pins)
|
||||
self.fake_pins.extend(fake_pins)
|
||||
|
||||
# Add vias to the corners
|
||||
shift = self.track_wire + self.track_space
|
||||
half_wide = self.track_wire / 2
|
||||
for i in range(4):
|
||||
ll, ur = new_pins[i].rect
|
||||
if i % 2:
|
||||
top_left = vector(ur.x - (num_vias - 1) * shift - half_wide, ll.y + (num_vias - 1) * shift + half_wide)
|
||||
else:
|
||||
top_left = vector(ll.x + half_wide, ur.y - half_wide)
|
||||
for j in range(num_vias):
|
||||
for k in range(num_vias):
|
||||
offset = vector(top_left.x + j * shift, top_left.y - k * shift)
|
||||
self.design.add_via_center(layers=self.layers,
|
||||
offset=offset)
|
||||
|
||||
# Save side pins for routing
|
||||
self.new_pins[pin_name] = new_pins
|
||||
for pin in new_pins:
|
||||
self.blockages.append(self.inflate_shape(pin, is_pin=True))
|
||||
|
||||
|
||||
def get_mst_pairs(self, pins):
|
||||
"""
|
||||
Return the pin pairs from the minimum spanning tree in a graph that
|
||||
connects all pins together.
|
||||
"""
|
||||
|
||||
pin_count = len(pins)
|
||||
|
||||
# Create an adjacency matrix that connects all pins
|
||||
edges = [[0] * pin_count for i in range(pin_count)]
|
||||
for i in range(pin_count):
|
||||
for j in range(pin_count):
|
||||
# Skip if they're the same pin
|
||||
if i == j:
|
||||
continue
|
||||
# Skip if both pins are fake
|
||||
if pins[i] in self.fake_pins and pins[j] in self.fake_pins:
|
||||
continue
|
||||
edges[i][j] = pins[i].distance(pins[j])
|
||||
|
||||
pin_connected = [False] * pin_count
|
||||
pin_connected[0] = True
|
||||
|
||||
# Add the minimum cost edge in each iteration (Prim's)
|
||||
mst_pairs = []
|
||||
for i in range(pin_count - 1):
|
||||
min_cost = float("inf")
|
||||
s = 0
|
||||
t = 0
|
||||
# Iterate over already connected pins
|
||||
for m in range(pin_count):
|
||||
# Skip if not connected
|
||||
if not pin_connected[m]:
|
||||
continue
|
||||
# Iterate over this pin's neighbors
|
||||
for n in range(pin_count):
|
||||
# Skip if already connected or isn't a neighbor
|
||||
if pin_connected[n] or edges[m][n] == 0:
|
||||
continue
|
||||
# Choose this edge if it's better the the current one
|
||||
if edges[m][n] < min_cost:
|
||||
min_cost = edges[m][n]
|
||||
s = m
|
||||
t = n
|
||||
pin_connected[t] = True
|
||||
mst_pairs.append((pins[s], pins[t]))
|
||||
|
||||
return mst_pairs
|
||||
|
||||
|
||||
def add_path(self, path):
|
||||
""" Add the route path to the layout. """
|
||||
|
||||
|
|
@ -546,12 +274,6 @@ class graph_router(router_tech):
|
|||
height=abs(diff.y) + self.track_wire)
|
||||
|
||||
|
||||
def get_new_pins(self, name):
|
||||
""" Return the new supply pins added by this router. """
|
||||
|
||||
return self.new_pins[name]
|
||||
|
||||
|
||||
def write_debug_gds(self, gds_name="debug_route.gds", g=None, source=None, target=None):
|
||||
""" Write the debug GDSII file for the router. """
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,298 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import debug
|
||||
from openram.base.vector import vector
|
||||
from openram import OPTS
|
||||
from .graph import graph
|
||||
from .graph_router import graph_router
|
||||
from .graph_shape import graph_shape
|
||||
|
||||
|
||||
class supply_graph_router(graph_router):
|
||||
"""
|
||||
This is the supply router that uses the Hanan grid graph method.
|
||||
"""
|
||||
|
||||
def __init__(self, layers, design, bbox=None, pin_type=None):
|
||||
|
||||
# `router_tech` contains tech constants for the router
|
||||
graph_router.__init__(self, layers, design, bbox)
|
||||
|
||||
# Side supply pin type
|
||||
# (can be "top", "bottom", "right", "left", and "ring")
|
||||
self.pin_type = pin_type
|
||||
# New pins are the side supply pins
|
||||
self.new_pins = {}
|
||||
|
||||
|
||||
def route(self, vdd_name="vdd", gnd_name="gnd"):
|
||||
""" Route the given pins in the given order. """
|
||||
debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name))
|
||||
|
||||
# Save pin names
|
||||
self.vdd_name = vdd_name
|
||||
self.gnd_name = gnd_name
|
||||
|
||||
# Prepare gdsMill to find pins and blockages
|
||||
self.prepare_gds_reader()
|
||||
|
||||
# Find pins to be routed
|
||||
self.find_pins(vdd_name)
|
||||
self.find_pins(gnd_name)
|
||||
|
||||
# Find blockages and vias
|
||||
self.find_blockages()
|
||||
self.find_vias()
|
||||
|
||||
# Convert blockages and vias if they overlap a pin
|
||||
self.convert_vias()
|
||||
self.convert_blockages()
|
||||
|
||||
# Add side pins
|
||||
self.calculate_ring_bbox()
|
||||
if self.pin_type in ["top", "bottom", "right", "left"]:
|
||||
self.add_side_pin(vdd_name)
|
||||
self.add_side_pin(gnd_name)
|
||||
elif self.pin_type == "ring":
|
||||
self.add_ring_pin(vdd_name)
|
||||
self.add_ring_pin(gnd_name)
|
||||
else:
|
||||
debug.warning("Side supply pins aren't created.")
|
||||
|
||||
# Add vdd and gnd pins as blockages as well
|
||||
# NOTE: This is done to make vdd and gnd pins DRC-safe
|
||||
for pin in self.all_pins:
|
||||
self.blockages.append(self.inflate_shape(pin, is_pin=True))
|
||||
|
||||
# Route vdd and gnd
|
||||
for pin_name in [vdd_name, gnd_name]:
|
||||
pins = self.pins[pin_name]
|
||||
# Route closest pins according to the minimum spanning tree
|
||||
for source, target in self.get_mst_pairs(list(pins)):
|
||||
# This is the routing region scale
|
||||
scale = 1
|
||||
while True:
|
||||
# Create the graph
|
||||
g = graph(self)
|
||||
region = g.create_graph(source, target, scale)
|
||||
# Find the shortest path from source to target
|
||||
path = g.find_shortest_path()
|
||||
# If there is no path found, exponentially try again with a
|
||||
# larger routing region
|
||||
if path is None:
|
||||
rll, rur = region
|
||||
bll, bur = self.ring_bbox
|
||||
# Stop scaling the region and throw an error
|
||||
if rll.x < bll.x and rll.y < bll.y and \
|
||||
rur.x > bur.x and rur.y > bur.y:
|
||||
self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target)
|
||||
debug.error("Couldn't route from {} to {}.".format(source, target), -1)
|
||||
# Exponentially scale the region
|
||||
scale *= 2
|
||||
debug.info(0, "Retry routing in larger routing region with scale {}".format(scale))
|
||||
continue
|
||||
# Create the path shapes on layout
|
||||
self.add_path(path)
|
||||
# Find the recently added shapes
|
||||
self.prepare_gds_reader()
|
||||
self.find_blockages(pin_name)
|
||||
self.find_vias()
|
||||
break
|
||||
|
||||
|
||||
def calculate_ring_bbox(self, num_vias=3):
|
||||
""" Calculate the ring-safe bounding box of the layout. """
|
||||
|
||||
ll, ur = self.design.get_bbox()
|
||||
# Calculate the "wideness" of a side supply pin
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
# Total wideness is used to find it any pin overlaps in this region. If
|
||||
# so, the bbox is shifted to prevent this overlap.
|
||||
total_wideness = wideness * 4
|
||||
for blockage in self.blockages:
|
||||
bll, bur = blockage.rect
|
||||
if self.get_zindex(blockage.lpp) == 1: # Vertical
|
||||
diff = ll.x + total_wideness - bll.x
|
||||
if diff > 0:
|
||||
ll = vector(ll.x - diff, ll.y)
|
||||
diff = ur.x - total_wideness - bur.x
|
||||
if diff < 0:
|
||||
ur = vector(ur.x - diff, ur.y)
|
||||
else: # Horizontal
|
||||
diff = ll.y + total_wideness - bll.y
|
||||
if diff > 0:
|
||||
ll = vector(ll.x, ll.y - diff)
|
||||
diff = ur.y - total_wideness - bur.y
|
||||
if diff < 0:
|
||||
ur = vector(ur.x, ur.y - diff)
|
||||
self.ring_bbox = [ll, ur]
|
||||
|
||||
|
||||
def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):
|
||||
""" Add supply pin to one side of the layout. """
|
||||
|
||||
ll, ur = self.ring_bbox
|
||||
vertical = side in ["left", "right"]
|
||||
inner = pin_name == self.gnd_name
|
||||
|
||||
# Calculate wires' wideness
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
|
||||
# Calculate the offset for the inner ring
|
||||
if inner:
|
||||
margin = wideness * 2
|
||||
else:
|
||||
margin = 0
|
||||
|
||||
# Calculate the lower left coordinate
|
||||
if side == "top":
|
||||
offset = vector(ll.x + margin, ur.y - wideness - margin)
|
||||
elif side == "bottom":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "left":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "right":
|
||||
offset = vector(ur.x - wideness - margin, ll.y + margin)
|
||||
|
||||
# Calculate width and height
|
||||
shape = ur - ll
|
||||
if vertical:
|
||||
shape_width = wideness
|
||||
shape_height = shape.y
|
||||
else:
|
||||
shape_width = shape.x
|
||||
shape_height = wideness
|
||||
if inner:
|
||||
if vertical:
|
||||
shape_height -= margin * 2
|
||||
else:
|
||||
shape_width -= margin * 2
|
||||
|
||||
# Add this new pin
|
||||
layer = self.get_layer(int(vertical))
|
||||
pin = self.design.add_layout_pin(text=pin_name,
|
||||
layer=layer,
|
||||
offset=offset,
|
||||
width=shape_width,
|
||||
height=shape_height)
|
||||
|
||||
# Add fake pins on this new pin evenly
|
||||
fake_pins = []
|
||||
if vertical:
|
||||
space = (shape_height - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x, offset.y + wideness)
|
||||
else:
|
||||
space = (shape_width - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x + wideness, offset.y)
|
||||
for i in range(1, num_fake_pins + 1):
|
||||
if vertical:
|
||||
offset = vector(start_offset.x, start_offset.y + i * (space + self.track_wire))
|
||||
ll = vector(offset.x, offset.y - self.track_wire)
|
||||
ur = vector(offset.x + wideness, offset.y)
|
||||
else:
|
||||
offset = vector(start_offset.x + i * (space + self.track_wire), start_offset.y)
|
||||
ll = vector(offset.x - self.track_wire, offset.y)
|
||||
ur = vector(offset.x, offset.y + wideness)
|
||||
rect = [ll, ur]
|
||||
fake_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
fake_pins.append(fake_pin)
|
||||
return pin, fake_pins
|
||||
|
||||
|
||||
def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4):
|
||||
""" Add the supply ring to the layout. """
|
||||
|
||||
# Add side pins
|
||||
new_pins = []
|
||||
for side in ["top", "bottom", "right", "left"]:
|
||||
new_shape, fake_pins = self.add_side_pin(pin_name, side, num_vias, num_fake_pins)
|
||||
ll, ur = new_shape.rect
|
||||
rect = [ll, ur]
|
||||
layer = self.get_layer(side in ["left", "right"])
|
||||
new_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
new_pins.append(new_pin)
|
||||
self.pins[pin_name].update(fake_pins)
|
||||
self.fake_pins.extend(fake_pins)
|
||||
|
||||
# Add vias to the corners
|
||||
shift = self.track_wire + self.track_space
|
||||
half_wide = self.track_wire / 2
|
||||
for i in range(4):
|
||||
ll, ur = new_pins[i].rect
|
||||
if i % 2:
|
||||
top_left = vector(ur.x - (num_vias - 1) * shift - half_wide, ll.y + (num_vias - 1) * shift + half_wide)
|
||||
else:
|
||||
top_left = vector(ll.x + half_wide, ur.y - half_wide)
|
||||
for j in range(num_vias):
|
||||
for k in range(num_vias):
|
||||
offset = vector(top_left.x + j * shift, top_left.y - k * shift)
|
||||
self.design.add_via_center(layers=self.layers,
|
||||
offset=offset)
|
||||
|
||||
# Save side pins for routing
|
||||
self.new_pins[pin_name] = new_pins
|
||||
for pin in new_pins:
|
||||
self.blockages.append(self.inflate_shape(pin, is_pin=True))
|
||||
|
||||
|
||||
def get_mst_pairs(self, pins):
|
||||
"""
|
||||
Return the pin pairs from the minimum spanning tree in a graph that
|
||||
connects all pins together.
|
||||
"""
|
||||
|
||||
pin_count = len(pins)
|
||||
|
||||
# Create an adjacency matrix that connects all pins
|
||||
edges = [[0] * pin_count for i in range(pin_count)]
|
||||
for i in range(pin_count):
|
||||
for j in range(pin_count):
|
||||
# Skip if they're the same pin
|
||||
if i == j:
|
||||
continue
|
||||
# Skip if both pins are fake
|
||||
if pins[i] in self.fake_pins and pins[j] in self.fake_pins:
|
||||
continue
|
||||
edges[i][j] = pins[i].distance(pins[j])
|
||||
|
||||
pin_connected = [False] * pin_count
|
||||
pin_connected[0] = True
|
||||
|
||||
# Add the minimum cost edge in each iteration (Prim's)
|
||||
mst_pairs = []
|
||||
for i in range(pin_count - 1):
|
||||
min_cost = float("inf")
|
||||
s = 0
|
||||
t = 0
|
||||
# Iterate over already connected pins
|
||||
for m in range(pin_count):
|
||||
# Skip if not connected
|
||||
if not pin_connected[m]:
|
||||
continue
|
||||
# Iterate over this pin's neighbors
|
||||
for n in range(pin_count):
|
||||
# Skip if already connected or isn't a neighbor
|
||||
if pin_connected[n] or edges[m][n] == 0:
|
||||
continue
|
||||
# Choose this edge if it's better the the current one
|
||||
if edges[m][n] < min_cost:
|
||||
min_cost = edges[m][n]
|
||||
s = m
|
||||
t = n
|
||||
pin_connected[t] = True
|
||||
mst_pairs.append((pins[s], pins[t]))
|
||||
|
||||
return mst_pairs
|
||||
|
||||
|
||||
def get_new_pins(self, name):
|
||||
""" Return the new supply pins added by this router. """
|
||||
|
||||
return self.new_pins[name]
|
||||
Loading…
Reference in New Issue