From 887a66553b737c4e82007ff12914311e7ec0c95f Mon Sep 17 00:00:00 2001 From: Eren Dogan Date: Tue, 1 Aug 2023 12:46:02 -0700 Subject: [PATCH] Implement signal escape router using the new gridless router --- compiler/router/router.py | 4 + compiler/router/signal_escape_router.py | 146 ++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 compiler/router/signal_escape_router.py diff --git a/compiler/router/router.py b/compiler/router/router.py index 64f63d69..a6c02620 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -31,6 +31,10 @@ class router(router_tech): self.design = design # Temporary GDSII file name to find pins and blockages self.gds_filename = OPTS.openram_temp + "temp.gds" + # Bounding box can be given with margin, or created by default + if bbox is None: + bbox = self.design.get_bbox() + self.bbox = bbox # Dictionary for vdd and gnd pins self.pins = {} # Set of all the pins diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py new file mode 100644 index 00000000..570b3cf6 --- /dev/null +++ b/compiler/router/signal_escape_router.py @@ -0,0 +1,146 @@ +# 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 openram import OPTS +from .graph import graph +from .router import router +from .graph_shape import graph_shape + + +class signal_escape_router(router): + """ + This is the signal escape router that uses the Hanan grid graph method. + """ + + def __init__(self, layers, design, bbox=None, pin_type=None): + + # `router_tech` contains tech constants for the router + router.__init__(self, layers, design, bbox) + + # New pins are the side supply pins + self.new_pins = {} + + + def route(self, pin_names): + """ Route the given pins to the perimeter. """ + debug.info(1, "Running signal escape router...") + + # Prepare gdsMill to find pins and blockages + self.prepare_gds_reader() + + # Find pins to be routed + for name in pin_names: + self.find_pins(name) + + # Find blockages and vias + self.find_blockages() + self.find_vias() + + # Convert blockages and vias if they overlap a pin + self.convert_vias() + self.convert_blockages() + + # Add fake pins on the perimeter to do the escape routing on + self.add_perimeter_fake_pins() + + # Add vdd and gnd pins as blockages as well + # NOTE: This is done to make vdd and gnd pins DRC-safe + for pin in self.all_pins: + self.blockages.append(self.inflate_shape(pin, is_pin=True)) + + # Route vdd and gnd + for name in pin_names: + # Route each pin to the perimeter + source = next(iter(self.pins[name])) + target = self.get_closest_perimeter_fake_pin(source) + # Change fake pin's name so the graph will treat it as routable + target.name = source.name + # This is the routing region scale + scale = 1 + while True: + # Create the graph + g = graph(self) + region = g.create_graph(source, target, scale) + # Find the shortest path from source to target + path = g.find_shortest_path() + # If there is no path found, exponentially try again with a + # larger routing region + if path is None: + rll, rur = region + bll, bur = self.bbox + # Stop scaling the region and throw an error + if rll.x < bll.x and rll.y < bll.y and \ + rur.x > bur.x and rur.y > bur.y: + self.write_debug_gds(gds_name="{}error.gds".format(OPTS.openram_temp), g=g, source=source, target=target) + debug.error("Couldn't route from {} to {}.".format(source, target), -1) + # Exponentially scale the region + scale *= 2 + debug.info(0, "Retry routing in larger routing region with scale {}".format(scale)) + continue + # Create the path shapes on layout + self.add_path(path) + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(name) + self.find_vias() + break + + + def add_perimeter_fake_pins(self): + """ + Add the fake pins on the perimeter to where the signals will be routed. + """ + + ll, ur = self.bbox + wide = self.track_wire + + for side in ["top", "bottom", "left", "right"]: + vertical = side in ["left", "right"] + + # Calculate the lower left coordinate + if side == "top": + offset = vector(ll.x, ur.y - wide) + elif side == "bottom": + offset = vector(ll.x, ll.y) + elif side == "left": + offset = vector(ll.x, ll.y) + elif side == "right": + offset = vector(ur.x - wide, ll.y) + + # Calculate width and height + shape = ur - ll + if vertical: + shape_width = wide + shape_height = shape.y + else: + shape_width = shape.x + shape_height = wide + + # Add this new pin + # They must lie on the non-preferred direction since the side supply + # pins will lie on the preferred direction + layer = self.get_layer(int(not vertical)) + nll = vector(offset.x, offset.y) + nur = vector(offset.x + shape_width, offset.y + shape_height) + rect = [nll, nur] + pin = graph_shape(name="fake", + rect=rect, + layer_name_pp=layer) + self.fake_pins.append(pin) + + + def get_closest_perimeter_fake_pin(self, pin): + """ Return the closest fake pin for the given pin. """ + + min_dist = float("inf") + close_fake = None + for fake in self.fake_pins: + dist = pin.distance(fake) + if dist < min_dist: + min_dist = dist + close_fake = fake + return close_fake