From 909ac6ce687c0333814c189b4008e061ac86f5c4 Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Thu, 4 May 2023 20:51:30 -0700 Subject: [PATCH] Add initial files for navigation router --- compiler/modules/sram_1bank.py | 2 + compiler/router/__init__.py | 1 + compiler/router/navigation_blockage.py | 31 +++++ compiler/router/navigation_graph.py | 101 ++++++++++++++++ compiler/router/navigation_node.py | 28 +++++ compiler/router/navigation_router.py | 153 +++++++++++++++++++++++++ 6 files changed, 316 insertions(+) create mode 100644 compiler/router/navigation_blockage.py create mode 100644 compiler/router/navigation_graph.py create mode 100644 compiler/router/navigation_node.py create mode 100644 compiler/router/navigation_router.py diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 8cc7cd8c..c5049b40 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -255,6 +255,8 @@ class sram_1bank(design, verilog, lef): return elif OPTS.route_supplies == "grid": from openram.router import supply_grid_router as router + elif OPTS.route_supplies == "navigation": + from openram.router import navigation_router as router else: from openram.router import supply_tree_router as router rtr=router(layers=self.supply_stack, diff --git a/compiler/router/__init__.py b/compiler/router/__init__.py index e428adda..e8da5264 100644 --- a/compiler/router/__init__.py +++ b/compiler/router/__init__.py @@ -8,3 +8,4 @@ from .signal_escape_router import * from .signal_router import * from .supply_grid_router import * from .supply_tree_router import * +from .navigation_router import * diff --git a/compiler/router/navigation_blockage.py b/compiler/router/navigation_blockage.py new file mode 100644 index 00000000..cd9cd554 --- /dev/null +++ b/compiler/router/navigation_blockage.py @@ -0,0 +1,31 @@ +# 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 .navigation_node import navigation_node + + +class navigation_blockage: + """ This class represents a blockage on the navigation graph. """ + + def __init__(self, ll, ur): + + self.ll = ll + self.ur = ur + + + def create_corner_nodes(self): + """ 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 diff --git a/compiler/router/navigation_graph.py b/compiler/router/navigation_graph.py new file mode 100644 index 00000000..793c9b1c --- /dev/null +++ b/compiler/router/navigation_graph.py @@ -0,0 +1,101 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram import debug +from openram.base.vector import vector +from .navigation_node import navigation_node +from .navigation_blockage import navigation_blockage + + +class navigation_graph: + """ This is the navigation graph created from the blockages. """ + + def __init__(self): + pass + + + 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): + """ """ + 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) + + # 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)) + + self.nodes = [] + + # 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: + self.nodes.extend(blockage.create_corner_nodes()) + + # 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 + # 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)])) + # 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): + continue + 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))) + + +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) diff --git a/compiler/router/navigation_node.py b/compiler/router/navigation_node.py new file mode 100644 index 00000000..73d16267 --- /dev/null +++ b/compiler/router/navigation_node.py @@ -0,0 +1,28 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# + +class navigation_node: + """ This class represents a node on the navigation graph. """ + + def __init__(self, position): + + self.position = position + self.neighbors = [] + + + def add_neighbor(self, node): + """ Connect two nodes. """ + + self.neighbors.append(node) + node.neighbors.append(self) + + + def remove_neighbor(self, node): + """ Disconnect two nodes. """ + + if node in self.neighbors: + self.neighbors.remove(node) + node.neighbors.remove(self) diff --git a/compiler/router/navigation_router.py b/compiler/router/navigation_router.py new file mode 100644 index 00000000..201b5b29 --- /dev/null +++ b/compiler/router/navigation_router.py @@ -0,0 +1,153 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz +# All rights reserved. +# +from openram import debug +from openram.base.pin_layout import pin_layout +from openram.base.vector import vector +from openram.gdsMill import gdsMill +from openram.tech import GDS +from openram.tech import layer as tech_layer +from openram import OPTS +from .router_tech import router_tech +from .navigation_graph import navigation_graph + + +class navigation_router(router_tech): + """ + This is the router class that implements navigation graph routing algorithm. + """ + + def __init__(self, layers, design, bbox=None, pin_type=None): + + router_tech.__init__(self, layers, route_track_width=1) + + self.layers = layers + self.design = design + self.gds_filename = OPTS.openram_temp + "temp.gds" + self.pins = {} + self.all_pins = set() + self.blockages = [] + + + 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)) + + # Prepare gdsMill to find pins and blockages + self.design.gds_write(self.gds_filename) + self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) + self.reader = gdsMill.Gds2reader(self.layout) + self.reader.loadFromFile(self.gds_filename) + + # Find pins to be routed + self.find_pins(vdd_name) + self.find_pins(gnd_name) + + # Find blockages + 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.create_graph(vdd_0, vdd_1, self.blockages) + + self.write_debug_gds(source=vdd_0, target=vdd_1) + + + def find_pins(self, pin_name): + """ """ + debug.info(1, "Finding all pins for {}".format(pin_name)) + + shape_list = self.layout.getAllPinShapes(str(pin_name)) + pin_set = set() + for shape in shape_list: + 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() + rect = [ll, ur] + pin = pin_layout(pin_name, rect, layer) + pin_set.add(pin) + # Add these pins to the 'pins' dict + self.pins[pin_name] = pin_set + self.all_pins.update(pin_set) + + + def find_blockages(self): + """ """ + debug.info(1, "Finding all blockages") + + for lpp in [self.vert_lpp, self.horiz_lpp]: + shapes = self.layout.getAllShapes(lpp) + for boundary in shapes: + # gdsMill boundaries are in (left, bottom, right, top) order + # so repack and snap to the grid + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) + rect = [ll, ur] + new_shape = pin_layout("blockage{}".format(len(self.blockages)), + rect, + lpp) + + # 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): + self.blockages.append(new_shape) + + + def pin_contains(self, shape): + for pin in self.all_pins: + if pin.contains(shape): + return True + return False + + + def write_debug_gds(self, gds_name="debug_route.gds", source=None, target=None): + """ """ + + self.add_router_info(source, target) + self.design.gds_write(gds_name) + self.del_router_info() + + + def add_router_info(self, source=None, target=None): + """ """ + + # 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) + 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-", + layer="text", + offset=node.position) + if source: + self.design.add_label(text="source", + layer="text", + offset=source.rect[0]) + if target: + self.design.add_label(text="target", + layer="text", + offset=target.rect[0]) + + + def del_router_info(self): + """ """ + + lpp = tech_layer["text"] + self.design.objs = [x for x in self.design.objs if x.lpp != lpp]