mirror of https://github.com/VLSIDA/OpenRAM.git
Add A* algorithm for navigation router
This commit is contained in:
parent
909ac6ce68
commit
cd339ebbd0
|
|
@ -3,29 +3,35 @@
|
|||
# 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):
|
||||
def __init__(self, ll, ur, layer=0):
|
||||
|
||||
self.ll = ll
|
||||
self.ur = ur
|
||||
self.layer = layer
|
||||
|
||||
|
||||
def create_corner_nodes(self):
|
||||
@property
|
||||
def rect(self):
|
||||
""" """
|
||||
|
||||
return self.ll, self.ur
|
||||
|
||||
|
||||
def create_corner_nodes(self, offset):
|
||||
""" 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
|
||||
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])
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
import heapq
|
||||
from openram import debug
|
||||
from openram.base.vector import vector
|
||||
from .navigation_node import navigation_node
|
||||
from .navigation_blockage import navigation_blockage
|
||||
|
||||
|
|
@ -12,8 +12,9 @@ from .navigation_blockage import navigation_blockage
|
|||
class navigation_graph:
|
||||
""" This is the navigation graph created from the blockages. """
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
def __init__(self, track_width):
|
||||
|
||||
self.track_width = track_width
|
||||
|
||||
|
||||
def is_probe_blocked(self, p1, p2):
|
||||
|
|
@ -43,11 +44,10 @@ class navigation_graph:
|
|||
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)
|
||||
s_ll, s_ur = layout_source.rect
|
||||
t_ll, t_ur = layout_target.rect
|
||||
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 = []
|
||||
|
|
@ -65,37 +65,155 @@ class navigation_graph:
|
|||
|
||||
# Create the corner nodes
|
||||
for blockage in self.nav_blockages:
|
||||
self.nodes.extend(blockage.create_corner_nodes())
|
||||
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.
|
||||
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
|
||||
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
|
||||
corner = navigation_node(vector(node1.position[k], node2.position[int(not k)]))
|
||||
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.position, node1.position) or self.is_probe_blocked(corner.position, node2.position):
|
||||
if self.is_probe_blocked(corner.center, node1.center) or self.is_probe_blocked(corner.center, node2.center):
|
||||
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)
|
||||
new_nodes.append(corner)
|
||||
self.nodes.extend(new_nodes)
|
||||
debug.info(0, "Number of nodes after intersections: {}".format(len(self.nodes)))
|
||||
self.nodes.append(corner)
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
def find_shortest_path(self, source, target):
|
||||
"""
|
||||
Find the shortest path from the source node to target node using the
|
||||
A* algorithm.
|
||||
"""
|
||||
|
||||
source = self.nodes[0]
|
||||
target = self.nodes[1]
|
||||
|
||||
# Heuristic function to calculate the scores
|
||||
h = lambda node: target.center.distance(node.center)
|
||||
|
||||
queue = []
|
||||
close_set = set()
|
||||
came_from = {}
|
||||
g_scores = {}
|
||||
f_scores = {}
|
||||
|
||||
# Initialize score values for the source node
|
||||
g_scores[source.id] = 0
|
||||
f_scores[source.id] = h(source)
|
||||
|
||||
heapq.heappush(queue, (f_scores[source.id], source.id, source))
|
||||
|
||||
# Run the A* algorithm
|
||||
while len(queue) > 0:
|
||||
# Get the closest node from the queue
|
||||
current = heapq.heappop(queue)[2]
|
||||
|
||||
# Return if already discovered
|
||||
if current in close_set:
|
||||
continue
|
||||
close_set.add(current)
|
||||
|
||||
# Check if we've reached the target
|
||||
if current == target:
|
||||
path = []
|
||||
while current.id in came_from:
|
||||
path.append(current)
|
||||
current = came_from[current.id]
|
||||
path.append(current)
|
||||
return path
|
||||
|
||||
# Update neighbor scores
|
||||
for node in current.neighbors:
|
||||
tentative_score = current.get_edge_cost(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
|
||||
f_scores[node.id] = tentative_score + h(node)
|
||||
heapq.heappush(queue, (f_scores[node.id], node.id, node))
|
||||
|
||||
# Return None if not connected
|
||||
return None
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,13 +3,28 @@
|
|||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram.base.vector import vector
|
||||
from openram.base.vector3d import vector3d
|
||||
|
||||
|
||||
class navigation_node:
|
||||
""" This class represents a node on the navigation graph. """
|
||||
|
||||
def __init__(self, position):
|
||||
# This is used to assign unique ids to nodes
|
||||
next_id = 0
|
||||
|
||||
self.position = position
|
||||
def __init__(self, center, offset=None, horizontal=1, vertical=1):
|
||||
|
||||
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):
|
||||
self.center = center
|
||||
else:
|
||||
self.center = vector3d(center)
|
||||
if offset:
|
||||
self.center += vector3d(offset * horizontal, offset * vertical, 0)
|
||||
self.neighbors = []
|
||||
|
||||
|
||||
|
|
@ -26,3 +41,12 @@ class navigation_node:
|
|||
if node in self.neighbors:
|
||||
self.neighbors.remove(node)
|
||||
node.neighbors.remove(self)
|
||||
|
||||
|
||||
def get_edge_cost(self, node):
|
||||
""" Return the cost of going to node. """
|
||||
|
||||
if node in self.neighbors:
|
||||
return self.center.distance(node.center)
|
||||
else:
|
||||
return float("inf")
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
from openram import debug
|
||||
from openram.base.pin_layout import pin_layout
|
||||
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 layer as tech_layer
|
||||
|
|
@ -26,6 +27,7 @@ class navigation_router(router_tech):
|
|||
self.layers = layers
|
||||
self.design = design
|
||||
self.gds_filename = OPTS.openram_temp + "temp.gds"
|
||||
self.track_width = 1
|
||||
self.pins = {}
|
||||
self.all_pins = set()
|
||||
self.blockages = []
|
||||
|
|
@ -49,12 +51,18 @@ class navigation_router(router_tech):
|
|||
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 = navigation_graph(self.track_width)
|
||||
self.nav.create_graph(vdd_0, vdd_1, self.blockages)
|
||||
|
||||
# Find the shortest path from source to target
|
||||
path = self.nav.find_shortest_path(vdd_0, vdd_1)
|
||||
|
||||
# Create the path shapes on layout
|
||||
self.add_path(path)
|
||||
|
||||
self.write_debug_gds(source=vdd_0, target=vdd_1)
|
||||
|
||||
|
||||
|
|
@ -68,8 +76,8 @@ class navigation_router(router_tech):
|
|||
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()
|
||||
ll = vector(boundary[0], boundary[1])
|
||||
ur = vector(boundary[2], boundary[3])
|
||||
rect = [ll, ur]
|
||||
pin = pin_layout(pin_name, rect, layer)
|
||||
pin_set.add(pin)
|
||||
|
|
@ -80,7 +88,7 @@ class navigation_router(router_tech):
|
|||
|
||||
def find_blockages(self):
|
||||
""" """
|
||||
debug.info(1, "Finding all blockages")
|
||||
debug.info(1, "Finding all blockages...")
|
||||
|
||||
for lpp in [self.vert_lpp, self.horiz_lpp]:
|
||||
shapes = self.layout.getAllShapes(lpp)
|
||||
|
|
@ -92,8 +100,7 @@ class navigation_router(router_tech):
|
|||
rect = [ll, ur]
|
||||
new_shape = pin_layout("blockage{}".format(len(self.blockages)),
|
||||
rect,
|
||||
lpp)
|
||||
|
||||
lpp).inflated_pin()
|
||||
# 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):
|
||||
|
|
@ -107,6 +114,30 @@ class navigation_router(router_tech):
|
|||
return False
|
||||
|
||||
|
||||
def add_path(self, path):
|
||||
""" """
|
||||
|
||||
for i in range(len(path) - 1):
|
||||
self.connect_nodes(path[i], path[i + 1])
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def write_debug_gds(self, gds_name="debug_route.gds", source=None, target=None):
|
||||
""" """
|
||||
|
||||
|
|
@ -120,30 +151,16 @@ class navigation_router(router_tech):
|
|||
|
||||
# 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)
|
||||
self.add_object_info(blockage, "blockage")
|
||||
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-",
|
||||
offset = (node.center.x, node.center.y)
|
||||
self.design.add_label(text="O",
|
||||
layer="text",
|
||||
offset=node.position)
|
||||
offset=offset)
|
||||
if source:
|
||||
self.design.add_label(text="source",
|
||||
layer="text",
|
||||
offset=source.rect[0])
|
||||
self.add_object_info(source, "source")
|
||||
if target:
|
||||
self.design.add_label(text="target",
|
||||
layer="text",
|
||||
offset=target.rect[0])
|
||||
self.add_object_info(target, "target")
|
||||
|
||||
|
||||
def del_router_info(self):
|
||||
|
|
@ -151,3 +168,16 @@ class navigation_router(router_tech):
|
|||
|
||||
lpp = tech_layer["text"]
|
||||
self.design.objs = [x for x in self.design.objs if x.lpp != lpp]
|
||||
|
||||
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue