OpenRAM/compiler/router/hanan_router.py

228 lines
7.8 KiB
Python
Raw Normal View History

# 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
2023-05-09 22:23:01 +02:00
from openram.base.vector3d import vector3d
from openram.gdsMill import gdsMill
from openram.tech import GDS
from openram.tech import layer as tech_layer
from openram import OPTS
from .router_tech import router_tech
2023-05-29 18:18:55 +02:00
from .hanan_graph import hanan_graph
from .hanan_shape import hanan_shape
2023-05-29 18:18:55 +02:00
class hanan_router(router_tech):
"""
2023-05-29 18:18:55 +02:00
This is the router class that implements Hanan graph routing algorithm.
"""
def __init__(self, layers, design, bbox=None, pin_type=None):
router_tech.__init__(self, layers, route_track_width=1)
self.layers = layers
self.design = design
self.gds_filename = OPTS.openram_temp + "temp.gds"
self.pins = {}
self.all_pins = set()
self.blockages = []
def route(self, vdd_name="vdd", gnd_name="gnd"):
""" Route the given pins in the given order. """
#debug.info(1, "Running router for {}...".format(pins))
self.write_debug_gds(gds_name="before.gds")
# Prepare gdsMill to find pins and blockages
self.design.gds_write(self.gds_filename)
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(self.gds_filename)
# Find pins to be routed
self.find_pins(vdd_name)
self.find_pins(gnd_name)
# Find blockages
self.find_blockages()
2023-05-29 18:18:55 +02:00
# Create the hanan graph
# TODO: Remove this part later and route all pins
vdds = list(self.pins["vdd"])
for pin in vdds:
ll, ur = pin.rect
if ll.x == -11 and ll.y == -8.055:
vdd_0 = pin
if ll.x == 10.557500000000001 and ll.y == 11.22:
vdd_1 = pin
#vdds.sort()
#pin_iter = iter(vdds)
#vdd_0 = next(pin_iter)
#next(pin_iter)
#next(pin_iter)
#next(pin_iter)
#next(pin_iter)
#next(pin_iter)
#next(pin_iter)
#vdd_1 = next(pin_iter)
2023-05-29 18:18:55 +02:00
self.hg = hanan_graph(self)
self.hg.create_graph(vdd_0, vdd_1)
2023-05-09 22:23:01 +02:00
# Find the shortest path from source to target
path = self.hg.find_shortest_path()
2023-05-09 22:23:01 +02:00
# Create the path shapes on layout
if path:
self.add_path(path)
debug.info(0, "Successfully routed")
else:
debug.info(0, "No path was found!")
2023-05-09 22:23:01 +02:00
self.write_debug_gds(gds_name="after.gds", source=vdd_0, target=vdd_1)
def find_pins(self, pin_name):
""" """
debug.info(1, "Finding all pins for {}".format(pin_name))
shape_list = self.layout.getAllPinShapes(str(pin_name))
pin_set = set()
for shape in shape_list:
layer, boundary = shape
# gdsMill boundaries are in (left, bottom, right, top) order
# so repack and snap to the grid
2023-05-09 22:23:01 +02:00
ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3])
rect = [ll, ur]
pin = hanan_shape(pin_name, rect, layer)
pin_set.add(pin)
# Add these pins to the 'pins' dict
self.pins[pin_name] = pin_set
self.all_pins.update(pin_set)
def find_blockages(self):
""" """
2023-05-09 22:23:01 +02:00
debug.info(1, "Finding all blockages...")
2023-06-06 04:33:45 +02:00
blockages = []
for lpp in [self.vert_lpp, self.horiz_lpp]:
shapes = self.layout.getAllShapes(lpp)
for boundary in shapes:
# gdsMill boundaries are in (left, bottom, right, top) order
# so repack and snap to the grid
ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3])
rect = [ll, ur]
2023-06-06 04:33:45 +02:00
new_shape = hanan_shape("blockage{}".format(len(blockages)),
rect,
lpp)
# If there is a rectangle that is the same in the pins,
# it isn't a blockage
2023-06-06 04:33:45 +02:00
if new_shape.contained_by_any(self.all_pins) or new_shape.contained_by_any(blockages):
continue
# Remove blockages contained by this new blockage
for i in range(len(blockages) - 1, -1, -1):
blockage = blockages[i]
# Remove the previous blockage contained by this new
# blockage
if new_shape.contains(blockage):
blockages.remove(blockage)
# Merge the previous blockage into this new blockage if
# they are aligning
elif new_shape.aligns(blockage):
new_shape.bbox([blockage])
blockages.remove(blockage)
blockages.append(new_shape)
# Inflate the shapes to prevent DRC errors
offset = self.layer_widths[0] / 2
for blockage in blockages:
self.blockages.append(blockage.inflated_pin(multiple=1, extra_spacing=offset))
2023-05-09 22:23:01 +02:00
def add_path(self, path):
2023-05-29 21:43:43 +02:00
""" Add the route path to the layout. """
2023-05-09 22:23:01 +02:00
coordinates = self.prepare_path(path)
self.design.add_route(layers=self.layers,
coordinates=coordinates,
layer_widths=self.layer_widths)
2023-05-09 22:23:01 +02:00
def prepare_path(self, path):
"""
Remove unnecessary nodes on the path to reduce the number of shapes in
the layout.
"""
last_added = path[0]
coordinates = [path[0].center]
direction = path[0].get_direction(path[1])
candidate = path[1]
for i in range(2, len(path)):
node = path[i]
current_direction = node.get_direction(candidate)
# Skip the previous candidate since the current node follows the
# same direction
if direction == current_direction:
candidate = node
else:
last_added = candidate
coordinates.append(candidate.center)
direction = current_direction
candidate = node
if candidate.center not in coordinates:
coordinates.append(candidate.center)
return coordinates
def write_debug_gds(self, gds_name="debug_route.gds", source=None, target=None):
""" """
self.add_router_info(source, target)
self.design.gds_write(gds_name)
self.del_router_info()
def add_router_info(self, source=None, target=None):
""" """
# Display the inflated blockage
2023-05-31 05:09:10 +02:00
if "hg" in self.__dict__:
2023-05-29 18:18:55 +02:00
for blockage in self.hg.graph_blockages:
2023-05-31 05:09:10 +02:00
self.add_object_info(blockage, "blockage{}".format(self.get_zindex(blockage.lpp)))
2023-05-29 18:18:55 +02:00
for node in self.hg.nodes:
offset = (node.center.x, node.center.y)
2023-05-31 05:09:10 +02:00
self.design.add_label(text="n{}".format(node.center.z),
layer="text",
offset=offset)
if source:
2023-05-09 22:23:01 +02:00
self.add_object_info(source, "source")
if target:
2023-05-09 22:23:01 +02:00
self.add_object_info(target, "target")
def del_router_info(self):
""" """
lpp = tech_layer["text"]
self.design.objs = [x for x in self.design.objs if x.lpp != lpp]
2023-05-09 22:23:01 +02:00
def add_object_info(self, obj, label):
""" """
ll, ur = obj.rect
self.design.add_rect(layer="text",
offset=ll,
width=ur.x - ll.x,
height=ur.y - ll.y)
self.design.add_label(text=label,
layer="text",
offset=ll)