Add initial files for navigation router

This commit is contained in:
Eren Dogan 2023-05-04 20:51:30 -07:00
parent 20744cdbcd
commit 909ac6ce68
6 changed files with 316 additions and 0 deletions

View File

@ -255,6 +255,8 @@ class sram_1bank(design, verilog, lef):
return
elif OPTS.route_supplies == "grid":
from openram.router import supply_grid_router as router
elif OPTS.route_supplies == "navigation":
from openram.router import navigation_router as router
else:
from openram.router import supply_tree_router as router
rtr=router(layers=self.supply_stack,

View File

@ -8,3 +8,4 @@ from .signal_escape_router import *
from .signal_router import *
from .supply_grid_router import *
from .supply_tree_router import *
from .navigation_router import *

View File

@ -0,0 +1,31 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
from openram.base.vector import vector
from .navigation_node import navigation_node
class navigation_blockage:
""" This class represents a blockage on the navigation graph. """
def __init__(self, ll, ur):
self.ll = ll
self.ur = ur
def create_corner_nodes(self):
""" Create nodes on all 4 corners of this blockage. """
corners = []
corners.append(navigation_node(vector(self.ll[0], self.ll[1])))
corners.append(navigation_node(vector(self.ll[0], self.ur[1])))
corners.append(navigation_node(vector(self.ur[0], self.ll[1])))
corners.append(navigation_node(vector(self.ur[0], self.ur[1])))
corners[0].add_neighbor(corners[1])
corners[0].add_neighbor(corners[2])
corners[3].add_neighbor(corners[1])
corners[3].add_neighbor(corners[2])
return corners

View File

@ -0,0 +1,101 @@
# 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 .navigation_node import navigation_node
from .navigation_blockage import navigation_blockage
class navigation_graph:
""" This is the navigation graph created from the blockages. """
def __init__(self):
pass
def is_probe_blocked(self, p1, p2):
"""
Return if a probe sent from p1 to p2 encounters a blockage.
The probe must be sent vertically or horizontally.
This method assumes that blockages are rectangular.
"""
# Check if any blockage blocks this probe
for blockage in self.nav_blockages:
right_x = blockage.ur[0]
upper_y = blockage.ur[1]
left_x = blockage.ll[0]
lower_y = blockage.ll[1]
# Check if blocked vertically
if is_between(left_x, right_x, p1.x) and (is_between(p1.y, p2.y, upper_y) or is_between(p1.y, p2.y, lower_y)):
return True
# Check if blocked horizontally
if is_between(upper_y, lower_y, p1.y) and (is_between(p1.x, p2.x, left_x) or is_between(p1.x, p2.x, right_x)):
return True
return False
def create_graph(self, layout_source, layout_target, layout_blockages):
""" """
debug.info(0, "Creating the navigation graph for source '{0}' and target'{1}'.".format(layout_source, layout_target))
# Find the region to be routed and only include objects inside that region
s_ll, sou_ur = layout_source.rect
t_ll, tar_ur = layout_target.rect
ll = vector(min(s_ll.x, t_ll.x), max(s_ll.y, t_ll.y))
ur = vector(max(sou_ur.x, tar_ur.x), min(sou_ur.y, tar_ur.y))
region = (ll, ur)
# Instantiate "navigation blockage" objects from layout blockages
self.nav_blockages = []
for layout_blockage in layout_blockages:
ll, ur = layout_blockage.rect
if (is_between(region[0].x, region[1].x, ll.x) and is_between(region[0].y, region[1].y, ll.y)) or \
(is_between(region[0].x, region[1].x, ur.x) and is_between(region[0].y, region[1].y, ur.y)):
self.nav_blockages.append(navigation_blockage(ll, ur))
self.nodes = []
# Add source and target for this graph
self.nodes.append(navigation_node(layout_source.center()))
self.nodes.append(navigation_node(layout_target.center()))
# Create the corner nodes
for blockage in self.nav_blockages:
self.nodes.extend(blockage.create_corner_nodes())
# Create intersection nodes
# NOTE: Intersection nodes are used to connect boundaries of blockages
# perpendicularly.
new_nodes = []
debug.info(0, "Number of blockages: {}".format(len(self.nav_blockages)))
debug.info(0, "Number of nodes: {}".format(len(self.nodes)))
for i in range(len(self.nodes)):
debug.info(3, "Creating intersections for node #{}".format(i))
node1 = self.nodes[i]
for j in range(i + 1, len(self.nodes)):
node2 = self.nodes[j]
# Skip if the nodes are already connected
if node1 in node2.neighbors:
continue
# Try two different corners
for k in [0, 1]:
# Create a node at the perpendicular corner of these two nodes
corner = navigation_node(vector(node1.position[k], node2.position[int(not k)]))
# Skip this corner if the perpendicular connection is blocked
if self.is_probe_blocked(corner.position, node1.position) or self.is_probe_blocked(corner.position, node2.position):
continue
corner.add_neighbor(node1)
corner.add_neighbor(node2)
new_nodes.append(corner)
self.nodes.extend(new_nodes)
debug.info(0, "Number of nodes after intersections: {}".format(len(self.nodes)))
def is_between(a, b, mid):
""" Return if 'mid' is between 'a' and 'b'. """
return (a < mid and mid < b) or (b < mid and mid < a)

View File

@ -0,0 +1,28 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
class navigation_node:
""" This class represents a node on the navigation graph. """
def __init__(self, position):
self.position = position
self.neighbors = []
def add_neighbor(self, node):
""" Connect two nodes. """
self.neighbors.append(node)
node.neighbors.append(self)
def remove_neighbor(self, node):
""" Disconnect two nodes. """
if node in self.neighbors:
self.neighbors.remove(node)
node.neighbors.remove(self)

View File

@ -0,0 +1,153 @@
# 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.pin_layout import pin_layout
from openram.base.vector import vector
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
from .navigation_graph import navigation_graph
class navigation_router(router_tech):
"""
This is the router class that implements navigation 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(0, "Running router for {}...".format(pins))
# 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()
# Create the navigation graph
self.nav = navigation_graph()
pin_iter = iter(self.pins["vdd"])
vdd_0 = next(pin_iter)
vdd_1 = next(pin_iter)
self.nav.create_graph(vdd_0, vdd_1, self.blockages)
self.write_debug_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
ll = vector(boundary[0], boundary[1]).snap_to_grid()
ur = vector(boundary[2], boundary[3]).snap_to_grid()
rect = [ll, ur]
pin = pin_layout(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):
""" """
debug.info(1, "Finding all 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]
new_shape = pin_layout("blockage{}".format(len(self.blockages)),
rect,
lpp)
# If there is a rectangle that is the same in the pins,
# it isn't a blockage
if new_shape not in self.all_pins and not self.pin_contains(new_shape):
self.blockages.append(new_shape)
def pin_contains(self, shape):
for pin in self.all_pins:
if pin.contains(shape):
return True
return False
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
for blockage in self.nav.nav_blockages:
ll, ur = blockage.ll, blockage.ur
self.design.add_rect(layer="text",
offset=ll,
width=ur.x - ll.x,
height=ur.y - ll.y)
self.design.add_label(text="blockage",
layer="text",
offset=ll)
for node in self.nav.nodes:
self.design.add_rect_center(layer="text",
offset=node.position,
width=1,
height=1)
self.design.add_label(text="-0-",
layer="text",
offset=node.position)
if source:
self.design.add_label(text="source",
layer="text",
offset=source.rect[0])
if target:
self.design.add_label(text="target",
layer="text",
offset=target.rect[0])
def del_router_info(self):
""" """
lpp = tech_layer["text"]
self.design.objs = [x for x in self.design.objs if x.lpp != lpp]