From a9e63efad79cd3004cc96a6fd70eed5b795f87d7 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Thu, 31 Aug 2023 18:26:04 -0700 Subject: [PATCH 1/6] Increase via cost in router --- compiler/router/graph_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/graph_node.py b/compiler/router/graph_node.py index d849673b..eacfddd3 100644 --- a/compiler/router/graph_node.py +++ b/compiler/router/graph_node.py @@ -69,6 +69,6 @@ class graph_node: # Add a constant wire cost to prevent dog-legs if prev_node and self.get_direction(prev_node) != self.get_direction(other): layer_dist += drc["grid"] - via_dist = abs(self.center.z - other.center.z) + via_dist = abs(self.center.z - other.center.z) * 2 return layer_dist + via_dist return float("inf") From 8a60684e51b958c1ef0aadb9a236aea1d8c508a4 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Thu, 31 Aug 2023 18:26:45 -0700 Subject: [PATCH 2/6] Increase the routing region inflation to be safer --- compiler/router/graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/graph.py b/compiler/router/graph.py index dc9f5c40..1db403af 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -195,7 +195,7 @@ class graph: # Find the region to be routed and only include objects inside that region region = deepcopy(source) region.bbox([target]) - region = region.inflated_pin(spacing=self.router.track_space) + region = region.inflated_pin(spacing=self.router.track_width + self.router.track_space) debug.info(3, "Routing region is {}".format(region.rect)) # Find the blockages that are in the routing area From d9004f6de66fd06d9023a10b0ef7ab21b5f829f3 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Thu, 31 Aug 2023 19:03:31 -0700 Subject: [PATCH 3/6] Print more info for the routing processes --- compiler/router/graph.py | 10 +++++----- compiler/router/router.py | 6 +++--- compiler/router/signal_escape_router.py | 4 ++++ compiler/router/supply_router.py | 5 +++++ 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/compiler/router/graph.py b/compiler/router/graph.py index 1db403af..85158ef7 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -186,7 +186,7 @@ class graph: def create_graph(self, source, target): """ Create the graph to run routing on later. """ - debug.info(2, "Creating the graph for source '{}' and target'{}'.".format(source, target)) + debug.info(3, "Creating the graph for source '{}' and target'{}'.".format(source, target)) # Save source and target information self.source = source @@ -196,7 +196,7 @@ class graph: region = deepcopy(source) region.bbox([target]) region = region.inflated_pin(spacing=self.router.track_width + self.router.track_space) - debug.info(3, "Routing region is {}".format(region.rect)) + debug.info(4, "Routing region is {}".format(region.rect)) # Find the blockages that are in the routing area self.graph_blockages = [] @@ -216,9 +216,9 @@ class graph: self.generate_graph_nodes(x_values, y_values) # Save the graph nodes that lie in source and target shapes self.save_end_nodes() - debug.info(3, "Number of blockages detected in the routing region: {}".format(len(self.graph_blockages))) - debug.info(3, "Number of vias detected in the routing region: {}".format(len(self.graph_vias))) - debug.info(3, "Number of nodes in the routing graph: {}".format(len(self.nodes))) + debug.info(4, "Number of blockages detected in the routing region: {}".format(len(self.graph_blockages))) + debug.info(4, "Number of vias detected in the routing region: {}".format(len(self.graph_vias))) + debug.info(4, "Number of nodes in the routing graph: {}".format(len(self.nodes))) def find_graph_blockages(self, region): diff --git a/compiler/router/router.py b/compiler/router/router.py index c498a815..61395f1f 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -89,7 +89,7 @@ class router(router_tech): def find_pins(self, pin_name): """ Find the pins with the given name. """ - debug.info(2, "Finding all pins for {}".format(pin_name)) + debug.info(4, "Finding all pins for {}".format(pin_name)) shape_list = self.layout.getAllPinShapes(str(pin_name)) pin_set = set() @@ -113,7 +113,7 @@ class router(router_tech): def find_blockages(self, name="blockage", shape_list=None): """ Find all blockages in the routing layers. """ - debug.info(2, "Finding blockages...") + debug.info(4, "Finding blockages...") for lpp in [self.vert_lpp, self.horiz_lpp]: # If the list of shapes is given, don't get them from gdsMill @@ -146,7 +146,7 @@ class router(router_tech): def find_vias(self, shape_list=None): """ Find all vias in the routing layers. """ - debug.info(2, "Finding vias...") + debug.info(4, "Finding vias...") # Prepare lpp values here from openram.tech import layer diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 21cae924..f4d5c3b1 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -54,6 +54,8 @@ class signal_escape_router(router): self.blockages.append(self.inflate_shape(pin)) # Route vdd and gnd + routed_count = 0 + routed_max = len(pin_names) 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 @@ -72,6 +74,8 @@ class signal_escape_router(router): # Find the recently added shapes self.find_blockages(name, new_wires) self.find_vias(new_vias) + routed_count += 1 + debug.info(2, "Routed {} of {} signal pins".format(routed_count, routed_max)) self.replace_layout_pins() diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 3cdcc4e6..a7614b22 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -67,6 +67,8 @@ class supply_router(router): self.blockages.append(self.inflate_shape(pin)) # Route vdd and gnd + routed_count = 0 + routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name]) for pin_name in [vdd_name, gnd_name]: pins = self.pins[pin_name] # Route closest pins according to the minimum spanning tree @@ -85,6 +87,9 @@ class supply_router(router): # Find the recently added shapes self.find_blockages(pin_name, new_wires) self.find_vias(new_vias) + # Report routed count + routed_count += 1 + debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max)) def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4): From 775922774ac5f731a3ea65ae620c0513b00f5553 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Fri, 1 Sep 2023 20:37:07 -0700 Subject: [PATCH 4/6] Use bbox trees to iterate over shapes in routing region efficiently --- compiler/router/bbox.py | 63 +++++++++++++++++++ compiler/router/bbox_node.py | 117 +++++++++++++++++++++++++++++++++++ compiler/router/graph.py | 36 ++++++++--- 3 files changed, 207 insertions(+), 9 deletions(-) create mode 100644 compiler/router/bbox.py create mode 100644 compiler/router/bbox_node.py diff --git a/compiler/router/bbox.py b/compiler/router/bbox.py new file mode 100644 index 00000000..56f53be0 --- /dev/null +++ b/compiler/router/bbox.py @@ -0,0 +1,63 @@ +# 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 .graph_utils import snap + + +class bbox: + """ + This class represents a bounding box object that is used in `bbox_node` + class. We are using bbox objects to group shapes in the router graphs. + """ + + def __init__(self, shape=None): + + self.shape = shape + self.rect = None + if self.shape: + self.rect = self.shape.rect + + + def area(self): + """ Return the area of this bbox. """ + + ll, ur = self.rect + width = ur.x - ll.x + height = ur.y - ll.y + return snap(width * height) + + + def merge(self, other): + """ Return the bbox created by merging two bbox objects. """ + + ll, ur = self.rect + oll, our = other.rect + min_x = min(ll.x, oll.x) + max_x = max(ur.x, our.x) + min_y = min(ll.y, oll.y) + max_y = max(ur.y, our.y) + rect = [vector(min_x, min_y), vector(max_x, max_y)] + merged = bbox() + merged.rect = rect + return merged + + + def overlap(self, other): + """ Return the bbox created by overlapping two bbox objects. """ + + ll, ur = self.rect + oll, our = other.rect + min_x = max(ll.x, oll.x) + max_x = min(ur.x, our.x) + min_y = max(ll.y, oll.y) + max_y = min(ur.y, our.y) + if max_x >= min_x and max_y >= min_y: + rect = [vector(min_x, min_y), vector(max_x, max_y)] + else: + return None + overlapped = bbox() + overlapped.rect = rect + return overlapped diff --git a/compiler/router/bbox_node.py b/compiler/router/bbox_node.py new file mode 100644 index 00000000..e5c1aadd --- /dev/null +++ b/compiler/router/bbox_node.py @@ -0,0 +1,117 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from .bbox import bbox + + +class bbox_node: + """ + This class represents a node in the bbox tree structure. Bbox trees are + binary trees we use to partition the shapes in the routing region so that + we can detect overlaps faster in a binary search-like manner. + """ + + def __init__(self, bbox, left=None, right=None): + + self.bbox = bbox + self.is_leaf = not left and not right + self.left = left + self.right = right + + + def iterate_point(self, point, check_done=False): + """ Iterate over shapes in the tree that overlap the given point. """ + + px, py = point.x, point.y + # Return this shape if it's a leaf + if self.is_leaf: + ll, ur = self.bbox.rect + if check_done or (ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y): + yield self.bbox.shape + else: + # Check the left child + if self.left: + ll, ur = self.left.bbox.rect + if ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y: + yield from self.left.iterate_point(point, True) + # Check the right child + if self.right: + ll, ur = self.right.bbox.rect + if ll.x <= px and px <= ur.x and ll.y <= py and py <= ur.y: + yield from self.right.iterate_point(point, True) + + + def iterate_shape(self, shape, check_done=False): + """ Iterate over shapes in the tree that overlap the given shape. """ + + sll, sur = shape.rect + # Return this shape if it's a leaf + if self.is_leaf: + ll, ur = self.bbox.rect + if check_done or (ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y): + yield self.bbox.shape + else: + # Check the left child + if self.left: + ll, ur = self.left.bbox.rect + if ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y: + yield from self.left.iterate_shape(shape, True) + # Check the right child + if self.right: + ll, ur = self.right.bbox.rect + if ll.x <= sur.x and sll.x <= ur.x and ll.y <= sur.y and sll.y <= ur.y: + yield from self.right.iterate_shape(shape, True) + + + def get_costs(self, bbox): + """ Return the costs of bbox nodes after merging the given bbox. """ + + # Find the new areas for all possible cases + self_merge = bbox.merge(self.bbox) + left_merge = bbox.merge(self.left.bbox) + right_merge = bbox.merge(self.right.bbox) + + # Add the change in areas as cost + self_cost = self_merge.area() + left_cost = self_merge.area() - self.bbox.area() + left_cost += left_merge.area() - self.left.bbox.area() + right_cost = self_merge.area() - self.bbox.area() + right_cost += right_merge.area() - self.right.bbox.area() + + # Add the overlaps in areas as cost + self_overlap = self.bbox.overlap(bbox) + left_overlap = left_merge.overlap(self.right.bbox) + right_overlap = right_merge.overlap(self.left.bbox) + if self_overlap: + self_cost += self_overlap.area() + if left_overlap: + left_cost += left_overlap.area() + if right_overlap: + right_cost += right_overlap.area() + + return self_cost, left_cost, right_cost + + + def insert(self, bbox): + """ Insert a bbox to the bbox tree. """ + + if self.is_leaf: + # Put the current bbox to the left child + self.left = bbox_node(self.bbox) + # Put the new bbox to the right child + self.right = bbox_node(bbox) + else: + # Calculate the costs of adding the new bbox + self_cost, left_cost, right_cost = self.get_costs(bbox) + if self_cost < left_cost and self_cost < right_cost: # Add here + self.left = bbox_node(self.bbox, left=self.left, right=self.right) + self.right = bbox_node(bbox) + elif left_cost < right_cost: # Add to the left + self.left.insert(bbox) + else: # Add to the right + self.right.insert(bbox) + # Update the current bbox + self.bbox = self.left.bbox.merge(self.right.bbox) + self.is_leaf = False diff --git a/compiler/router/graph.py b/compiler/router/graph.py index 85158ef7..d8017999 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -9,6 +9,8 @@ from openram import debug from openram.base.vector import vector from openram.base.vector3d import vector3d from openram.tech import drc +from .bbox import bbox +from .bbox_node import bbox_node from .graph_node import graph_node from .graph_probe import graph_probe from .graph_utils import snap @@ -80,11 +82,8 @@ class graph: probe_shape = graph_probe(p1, p2, self.router.get_lpp(p1.z)) pll, pur = probe_shape.rect # Check if any blockage blocks this probe - for blockage in self.graph_blockages: + for blockage in self.blockage_bbox_tree.iterate_shape(probe_shape): bll, bur = blockage.rect - # Not overlapping - if bll.x > pur.x or pll.x > bur.x or bll.y > pur.y or pll.y > bur.y: - continue # Not on the same layer if not blockage.same_lpp(blockage.lpp, probe_shape.lpp): continue @@ -116,11 +115,8 @@ class graph: half_wide = self.router.half_wire spacing = snap(self.router.track_space + half_wide + drc["grid"]) blocked = False - for blockage in self.graph_blockages: + for blockage in self.blockage_bbox_tree.iterate_point(p): ll, ur = blockage.rect - # Not overlapping - if ll.x > x or x > ur.x or ll.y > y or y > ur.y: - continue # Not on the same layer if self.router.get_zindex(blockage.lpp) != z: continue @@ -168,11 +164,16 @@ class graph: for node in nodes: if self.is_node_blocked(node, pin_safe=False): return True + + # Skip if no via is present + if len(self.graph_vias) == 0: + return False + # If the nodes are blocked by a via x = node.center.x y = node.center.y z = node.center.z - for via in self.graph_vias: + for via in self.via_bbox_tree.iterate_point(node.center): ll, ur = via.rect # Not overlapping if ll.x > x or x > ur.x or ll.y > y or y > ur.y: @@ -212,6 +213,8 @@ class graph: region.bbox(self.graph_blockages) # Find and include edge shapes to prevent DRC errors self.find_graph_blockages(region) + # Build the bbox tree + self.build_bbox_trees() # Generate the graph nodes from cartesian values self.generate_graph_nodes(x_values, y_values) # Save the graph nodes that lie in source and target shapes @@ -257,6 +260,21 @@ class graph: self.graph_vias.append(via) + def build_bbox_trees(self): + """ Build bbox trees for blockages and vias in the routing region. """ + + # Bbox tree for blockages + self.blockage_bbox_tree = bbox_node(bbox(self.graph_blockages[0])) + for i in range(1, len(self.graph_blockages)): + self.blockage_bbox_tree.insert(bbox(self.graph_blockages[i])) + # Bbox tree for vias + if len(self.graph_vias) == 0: + return + self.via_bbox_tree = bbox_node(bbox(self.graph_vias[0])) + for i in range(1, len(self.graph_vias)): + self.via_bbox_tree.insert(bbox(self.graph_vias[i])) + + def generate_cartesian_values(self): """ Generate x and y values from all the corners of the shapes in the From abb12bd785eb6bdd077aecca4c26215bc6b51251 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Sat, 2 Sep 2023 08:00:58 -0700 Subject: [PATCH 5/6] Increase non-preferred direction cost in router --- compiler/router/graph_node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/router/graph_node.py b/compiler/router/graph_node.py index eacfddd3..9ea30a80 100644 --- a/compiler/router/graph_node.py +++ b/compiler/router/graph_node.py @@ -65,7 +65,7 @@ class graph_node: layer_dist = self.center.distance(other.center) # Double the cost if the edge is in non-preferred direction if is_vertical != bool(self.center.z): - layer_dist *= 2 + layer_dist *= 4 # Add a constant wire cost to prevent dog-legs if prev_node and self.get_direction(prev_node) != self.get_direction(other): layer_dist += drc["grid"] From 1a25fcf9a5af0234dd7afb3f4f6fa901ce118c34 Mon Sep 17 00:00:00 2001 From: vlsida-bot Date: Sat, 2 Sep 2023 18:43:35 +0000 Subject: [PATCH 6/6] Bump version: 1.2.32 -> 1.2.33 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 37258517..47f5bfd9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.32 +1.2.33