mirror of https://github.com/VLSIDA/OpenRAM.git
155 lines
5.3 KiB
Python
155 lines
5.3 KiB
Python
# 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 .router import router
|
|
from .graph_shape import graph_shape
|
|
|
|
|
|
class signal_escape_router(router):
|
|
"""
|
|
This is the signal escape 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
|
|
router.__init__(self, layers, design, bbox)
|
|
|
|
# New pins are the side supply pins
|
|
self.new_pins = {}
|
|
|
|
|
|
def route(self, pin_names):
|
|
""" Route the given pins to the perimeter. """
|
|
debug.info(1, "Running signal escape router...")
|
|
|
|
# Prepare gdsMill to find pins and blockages
|
|
self.prepare_gds_reader()
|
|
|
|
# Find pins to be routed
|
|
for name in pin_names:
|
|
self.find_pins(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 fake pins on the perimeter to do the escape routing on
|
|
self.add_perimeter_fake_pins()
|
|
|
|
# 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 source, target, _ in self.get_route_pairs(pin_names):
|
|
# Change fake pin's name so the graph will treat it as routable
|
|
target.name = source.name
|
|
# 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.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(name)
|
|
self.find_vias()
|
|
break
|
|
|
|
|
|
def add_perimeter_fake_pins(self):
|
|
"""
|
|
Add the fake pins on the perimeter to where the signals will be routed.
|
|
"""
|
|
|
|
ll, ur = self.bbox
|
|
wide = self.track_wire
|
|
|
|
for side in ["top", "bottom", "left", "right"]:
|
|
vertical = side in ["left", "right"]
|
|
|
|
# Calculate the lower left coordinate
|
|
if side == "top":
|
|
offset = vector(ll.x, ur.y - wide)
|
|
elif side == "bottom":
|
|
offset = vector(ll.x, ll.y)
|
|
elif side == "left":
|
|
offset = vector(ll.x, ll.y)
|
|
elif side == "right":
|
|
offset = vector(ur.x - wide, ll.y)
|
|
|
|
# Calculate width and height
|
|
shape = ur - ll
|
|
if vertical:
|
|
shape_width = wide
|
|
shape_height = shape.y
|
|
else:
|
|
shape_width = shape.x
|
|
shape_height = wide
|
|
|
|
# Add this new pin
|
|
# They must lie on the non-preferred direction since the side supply
|
|
# pins will lie on the preferred direction
|
|
layer = self.get_layer(int(not vertical))
|
|
nll = vector(offset.x, offset.y)
|
|
nur = vector(offset.x + shape_width, offset.y + shape_height)
|
|
rect = [nll, nur]
|
|
pin = graph_shape(name="fake",
|
|
rect=rect,
|
|
layer_name_pp=layer)
|
|
self.fake_pins.append(pin)
|
|
|
|
|
|
def get_closest_perimeter_fake_pin(self, pin):
|
|
""" Return the closest fake pin for the given pin. """
|
|
|
|
min_dist = float("inf")
|
|
close_fake = None
|
|
for fake in self.fake_pins:
|
|
dist = pin.distance(fake)
|
|
if dist < min_dist:
|
|
min_dist = dist
|
|
close_fake = fake
|
|
return close_fake
|
|
|
|
|
|
def get_route_pairs(self, pin_names):
|
|
""" """
|
|
|
|
to_route = []
|
|
for name in pin_names:
|
|
pin = next(iter(self.pins[name]))
|
|
fake = self.get_closest_perimeter_fake_pin(pin)
|
|
to_route.append((pin, fake, pin.distance(fake)))
|
|
return sorted(to_route, key=lambda x: x[2])
|