Use Hanan points to generate the routing graph

This commit is contained in:
Eren Dogan 2023-05-22 13:08:21 -07:00
parent cd339ebbd0
commit 648a631a28
5 changed files with 132 additions and 199 deletions

View File

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

View File

@ -5,41 +5,21 @@
#
import heapq
from openram import debug
from openram.base.vector3d import vector3d
from .direction import direction
from .navigation_node import navigation_node
from .navigation_blockage import navigation_blockage
from .navigation_utils import *
class navigation_graph:
""" This is the navigation graph created from the blockages. """
def __init__(self, track_width):
def __init__(self, router):
self.track_width = track_width
self.router = router
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):
def create_graph(self, layout_source, layout_target):
""" """
debug.info(0, "Creating the navigation graph for source '{0}' and target'{1}'.".format(layout_source, layout_target))
@ -49,71 +29,78 @@ class navigation_graph:
region = (s_ll.min(t_ll), s_ur.min(t_ur))
debug.info(0, "Routing region is ll: '{0}' ur: '{1}'".format(region[0], region[1]))
# 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))
# Find the blockages that are in the routing area
self.graph_blockages = []
for blockage in self.router.blockages:
ll, ur = blockage.rect
if is_in_region(ll, region) or is_in_region(ur, region):
self.graph_blockages.append(blockage)
debug.info(0, "Number of blockages detected in the routing region: {}".format(len(self.graph_blockages)))
# Obtain the x and y points for Hanan grid
x_values = []
y_values = []
offset = max(self.router.horiz_track_width, self.router.vert_track_width) / 2
for shape in [layout_source, layout_target]:
center = shape.center()
x_values.append(center.x)
y_values.append(center.y)
for blockage in self.graph_blockages:
ll, ur = blockage.rect
x_values.extend([ll.x - offset, ur.x + offset])
y_values.extend([ll.y - offset, ur.y + offset])
# Generate Hanan points here (cartesian product of all x and y values)
hanan_points = []
for x in x_values:
for y in y_values:
hanan_points.append(vector3d(x, y, 0))
hanan_points.append(vector3d(x, y, 1))
# Remove blocked points
for point in hanan_points.copy():
for blockage in self.graph_blockages:
ll, ur = blockage.rect
if self.router.get_zindex(blockage.lpp) == point.z and is_in_region(point, blockage.rect):
hanan_points.remove(point)
break
# Create graph nodes from Hanan points
self.nodes = []
for point in hanan_points:
self.nodes.append(navigation_node(point))
# 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:
blockage.create_corner_nodes(self.track_width / 2)
# These nodes will be connected to create the final graph
connect_objs = []
connect_objs.extend(self.nodes)
connect_objs.extend(self.nav_blockages)
# Create intersection nodes
# NOTE: Intersection nodes are used to connect boundaries of blockages
# perpendicularly.
debug.info(0, "Number of objects: {}".format(len(connect_objs)))
for i in range(len(connect_objs)):
obj1 = connect_objs[i]
for j in range(i + 1, len(connect_objs)):
obj2 = connect_objs[j]
node1, node2 = get_closest_nodes(obj1, obj2)
# Try two different corners
for k in [0, 1]:
# Create a node at the perpendicular corner of these two nodes
x_node = node1 if k else node2
y_node = node2 if k else node1
corner = navigation_node([x_node.center[0], y_node.center[1], 0])
# Skip this corner if the perpendicular connection is blocked
if self.is_probe_blocked(corner.center, node1.center) or self.is_probe_blocked(corner.center, node2.center):
# Connect closest points avoiding blockages
for i in range(len(self.nodes)):
node = self.nodes[i]
for d in direction.cardinal_offsets():
min_dist = float("inf")
min_neighbor = None
for j in range(i + 1, len(self.nodes)):
neighbor = self.nodes[j]
if node.center.z != neighbor.center.z:
continue
# Check if this new node stands on an existing connection
self.remove_intersected_neighbors(node1, corner, k)
self.remove_intersected_neighbors(node2, corner, not(k))
# Add this new node to the graph
corner.add_neighbor(node1)
corner.add_neighbor(node2)
self.nodes.append(corner)
distance_vector = neighbor.center - node.center
distance = node.center.distance(neighbor.center)
if (distance_vector.x or (distance_vector.y * d.y <= 0)) and \
(distance_vector.y or (distance_vector.x * d.x <= 0)):
continue
if distance < min_dist:
min_dist = distance
min_neighbor = neighbor
if min_neighbor:
node.add_neighbor(min_neighbor)
# Add corner nodes from blockages after intersections
for blockage in self.nav_blockages:
self.nodes.extend(blockage.corners)
debug.info(0, "Number of nodes after corners: {}".format(len(self.nodes)))
def remove_intersected_neighbors(self, node, corner, axis):
""" """
a = node.center
mid = corner.center
for neighbor in node.neighbors:
b = neighbor.center
if a[not(axis)] == b[not(axis)] and is_between(a[axis], b[axis], mid[axis]):
neighbor.remove_neighbor(node)
neighbor.add_neighbor(corner)
# Connect nodes that are on top of each other
for i in range(len(self.nodes)):
node = self.nodes[i]
for j in range(i + 1, len(self.nodes)):
neighbor = self.nodes[j]
if node.center.x == neighbor.center.x and \
node.center.y == neighbor.center.y and \
node.center.z != neighbor.center.z:
node.add_neighbor(neighbor)
debug.info(0, "Number of nodes in the routing graph: {}".format(len(self.nodes)))
def find_shortest_path(self, source, target):
@ -122,11 +109,19 @@ class navigation_graph:
A* algorithm.
"""
source = self.nodes[0]
target = self.nodes[1]
# Find source and target nodes
source_center = source.center()
source_center = vector3d(source_center.x, source_center.y, self.router.get_zindex(source.lpp))
target_center = target.center()
target_center = vector3d(target_center.x, target_center.y, self.router.get_zindex(target.lpp))
for node in self.nodes:
if node.center == source_center:
source = node
if node.center == target_center:
target = node
# Heuristic function to calculate the scores
h = lambda node: target.center.distance(node.center)
h = lambda node: target.center.distance(node.center) + abs(target.center.z - node.center.z)
queue = []
close_set = set()
@ -161,7 +156,7 @@ class navigation_graph:
# Update neighbor scores
for node in current.neighbors:
tentative_score = current.get_edge_cost(node) + g_scores[current.id]
tentative_score = self.get_edge_cost(current, node) + g_scores[current.id]
if node.id not in g_scores or tentative_score < g_scores[node.id]:
came_from[node.id] = current
g_scores[node.id] = tentative_score
@ -172,48 +167,14 @@ class navigation_graph:
return None
def is_between(a, b, mid):
""" Return if 'mid' is between 'a' and 'b'. """
def get_edge_cost(self, source, target):
""" """
return (a < mid and mid < b) or (b < mid and mid < a)
def get_closest_nodes(a, b):
""" """
if isinstance(a, navigation_node) and isinstance(b, navigation_node):
return a, b
if isinstance(a, navigation_blockage) and isinstance(b, navigation_blockage):
min_dist = float("inf")
min_a = None
min_b = None
for node_a in a.corners:
for node_b in b.corners:
dist = node_a.center.distance(node_b.center)
if dist < min_dist:
min_dist = dist
min_a = node_a
min_b = node_b
return min_a, min_b
if isinstance(a, navigation_node):
min_dist = float("inf")
min_a = None
min_b = None
for node_b in b.corners:
dist = a.center.distance(node_b.center)
if dist < min_dist:
min_dist = dist
min_a = a
min_b = node_b
return min_a, min_b
if isinstance(b, navigation_node):
min_dist = float("inf")
min_a = None
min_b = None
for node_a in a.corners:
dist = b.center.distance(node_a.center)
if dist < min_dist:
min_dist = dist
min_a = node_a
min_b = b
return min_a, min_b
if target in source.neighbors:
is_vertical = source.center.x == target.center.x
layer_dist = source.center.distance(target.center)
if is_vertical != bool(source.center.z):
layer_dist *= 2
via_dist = abs(source.center.z - target.center.z) * 2
return layer_dist + via_dist
return float("inf")

View File

@ -13,26 +13,23 @@ class navigation_node:
# This is used to assign unique ids to nodes
next_id = 0
def __init__(self, center, offset=None, horizontal=1, vertical=1):
def __init__(self, center):
self.id = navigation_node.next_id
navigation_node.next_id += 1
if isinstance(center, vector):
self.center = vector3d(center[0], center[1], 0)
elif isinstance(center, vector3d):
if isinstance(center, vector3d):
self.center = center
else:
self.center = vector3d(center)
if offset:
self.center += vector3d(offset * horizontal, offset * vertical, 0)
self.neighbors = []
def add_neighbor(self, node):
""" Connect two nodes. """
self.neighbors.append(node)
node.neighbors.append(self)
if node not in self.neighbors:
self.neighbors.append(node)
node.neighbors.append(self)
def remove_neighbor(self, node):
@ -47,6 +44,5 @@ class navigation_node:
""" Return the cost of going to node. """
if node in self.neighbors:
return self.center.distance(node.center)
else:
return float("inf")
return self.center.distance(node.center) + abs(self.center.z - node.center.z)
return float("inf")

View File

@ -35,7 +35,7 @@ class navigation_router(router_tech):
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))
debug.info(1, "Running router for {}...".format(pins))
# Prepare gdsMill to find pins and blockages
self.design.gds_write(self.gds_filename)
@ -54,8 +54,8 @@ class navigation_router(router_tech):
pin_iter = iter(self.pins["vdd"])
vdd_0 = next(pin_iter)
vdd_1 = next(pin_iter)
self.nav = navigation_graph(self.track_width)
self.nav.create_graph(vdd_0, vdd_1, self.blockages)
self.nav = navigation_graph(self)
self.nav.create_graph(vdd_0, vdd_1)
# Find the shortest path from source to target
path = self.nav.find_shortest_path(vdd_0, vdd_1)
@ -100,7 +100,7 @@ class navigation_router(router_tech):
rect = [ll, ur]
new_shape = pin_layout("blockage{}".format(len(self.blockages)),
rect,
lpp).inflated_pin()
lpp).inflated_pin(multiple=1)
# 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):
@ -124,18 +124,10 @@ class navigation_router(router_tech):
def connect_nodes(self, a, b):
""" Connect nodes 'a' and 'b' with a wire. """
# Calculate the shape of the wire
track_offset = vector3d(self.track_width / 2, self.track_width / 2, 0)
ll = a.center.min(b.center) - track_offset
ur = a.center.max(b.center) + track_offset
debug.info(0, "Adding wire: ({}, {})".format(ll, ur))
# Add the shape to the layout
self.design.add_rect(layer="text",
offset=(ll[0], ll[1]),
width=ur.x - ll.x,
height=ur.y - ll.y)
if a.center.x == b.center.x and a.center.y == b.center.y:
self.design.add_via_center(self.layers, vector(a.center.x, b.center.y))
else:
self.design.add_path(self.get_layer(a.center.z), [a.center, b.center])
def write_debug_gds(self, gds_name="debug_route.gds", source=None, target=None):
@ -150,7 +142,7 @@ class navigation_router(router_tech):
""" """
# Display the inflated blockage
for blockage in self.nav.nav_blockages:
for blockage in self.nav.graph_blockages:
self.add_object_info(blockage, "blockage")
for node in self.nav.nodes:
offset = (node.center.x, node.center.y)

View File

@ -0,0 +1,21 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
# All rights reserved.
#
"""
Utility functions for navigation router.
"""
def is_in_region(point, region):
""""""
if is_between(region[0].x, region[1].x, point.x) and is_between(region[0].y, region[1].y, point.y):
return True
return False
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)