mirror of https://github.com/VLSIDA/OpenRAM.git
Use bbox trees to iterate over shapes in routing region efficiently
This commit is contained in:
parent
d9004f6de6
commit
775922774a
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue