diff --git a/compiler/router/navigation_blockage.py b/compiler/router/navigation_blockage.py deleted file mode 100644 index 8cf5ac0a..00000000 --- a/compiler/router/navigation_blockage.py +++ /dev/null @@ -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]) diff --git a/compiler/router/navigation_graph.py b/compiler/router/navigation_graph.py index eeaffce2..b9864067 100644 --- a/compiler/router/navigation_graph.py +++ b/compiler/router/navigation_graph.py @@ -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") diff --git a/compiler/router/navigation_node.py b/compiler/router/navigation_node.py index 7808cc1b..87a5935d 100644 --- a/compiler/router/navigation_node.py +++ b/compiler/router/navigation_node.py @@ -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") diff --git a/compiler/router/navigation_router.py b/compiler/router/navigation_router.py index ea9b43bd..8827ba3c 100644 --- a/compiler/router/navigation_router.py +++ b/compiler/router/navigation_router.py @@ -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) diff --git a/compiler/router/navigation_utils.py b/compiler/router/navigation_utils.py new file mode 100644 index 00000000..cf4e93c3 --- /dev/null +++ b/compiler/router/navigation_utils.py @@ -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)