diff --git a/Makefile b/Makefile index 42e84f41..12c9eaa7 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,6 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git # Use this for development #SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git #SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git - SRAM_LIB_GIT_COMMIT ?= baa2b14282ee6c8498a9e480c88a5096fdce2b06 # Open PDKs diff --git a/VERSION b/VERSION index 37258517..550c9e96 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.2.32 +1.2.29 diff --git a/compiler/router/graph.py b/compiler/router/graph.py index dc9f5c40..132681c9 100644 --- a/compiler/router/graph.py +++ b/compiler/router/graph.py @@ -184,7 +184,7 @@ class graph: return False - def create_graph(self, source, target): + def create_graph(self, source, target, scale=1): """ Create the graph to run routing on later. """ debug.info(2, "Creating the graph for source '{}' and target'{}'.".format(source, target)) @@ -195,6 +195,7 @@ class graph: # Find the region to be routed and only include objects inside that region region = deepcopy(source) region.bbox([target]) + region.multiply(scale) region = region.inflated_pin(spacing=self.router.track_space) debug.info(3, "Routing region is {}".format(region.rect)) @@ -220,6 +221,9 @@ class graph: 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))) + # Return the region to scale later if no path is found + return region.rect + def find_graph_blockages(self, region): """ Find blockages that overlap the routing region. """ @@ -418,7 +422,6 @@ class graph: path.append(current) current = came_from[current.id] path.append(current) - path.reverse() return path # Get the previous node to better calculate the next costs diff --git a/compiler/router/graph_shape.py b/compiler/router/graph_shape.py index 0f950ae4..1221dabe 100644 --- a/compiler/router/graph_shape.py +++ b/compiler/router/graph_shape.py @@ -72,6 +72,17 @@ class graph_shape(pin_layout): return graph_shape(self.name, inflated_area, self.layer, self) + def multiply(self, scale): + """ Multiply the width and height with the scale value. """ + + width = (self.width() * (scale - 1)) / 2 + height = (self.height() * (scale - 1)) / 2 + ll, ur = self.rect + newll = vector(ll.x - width, ll.y - height) + newur = vector(ur.x + width, ur.y + height) + self.rect = [snap(newll), snap(newur)] + + def core_contained_by_any(self, shape_list): """ Return if the core of this shape is contained by any shape's core in the diff --git a/compiler/router/router.py b/compiler/router/router.py index c498a815..fe445ae1 100644 --- a/compiler/router/router.py +++ b/compiler/router/router.py @@ -59,8 +59,6 @@ class router(router_tech): def prepare_gds_reader(self): """ Write the current layout to a temporary file to read the layout. """ - # NOTE: Avoid using this function if possible since it is too slow to - # write/read these files self.design.gds_write(self.gds_filename) self.layout = gdsMill.VlsiLayout(units=GDS["unit"]) self.reader = gdsMill.Gds2reader(self.layout) @@ -111,26 +109,16 @@ class router(router_tech): self.all_pins.update(pin_set) - def find_blockages(self, name="blockage", shape_list=None): + def find_blockages(self, name="blockage"): """ Find all blockages in the routing layers. """ debug.info(2, "Finding blockages...") for lpp in [self.vert_lpp, self.horiz_lpp]: - # If the list of shapes is given, don't get them from gdsMill - if shape_list is None: - shapes = self.layout.getAllShapes(lpp) - else: - shapes = shape_list + shapes = self.layout.getAllShapes(lpp) for boundary in shapes: - if shape_list is not None: - if boundary.lpp != lpp: - continue - ll = boundary.ll() - ur = boundary.ur() - else: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape(name, rect, lpp) new_shape = self.inflate_shape(new_shape) @@ -144,28 +132,20 @@ class router(router_tech): self.blockages.append(new_shape) - def find_vias(self, shape_list=None): + def find_vias(self): """ Find all vias in the routing layers. """ debug.info(2, "Finding vias...") # Prepare lpp values here from openram.tech import layer via_lpp = layer[self.via_layer_name] - valid_lpp = self.horiz_lpp # Just a temporary lpp to prevent errors + valid_lpp = self.horiz_lpp - # If the list of shapes is given, don't get them from gdsMill - if shape_list is None: - shapes = self.layout.getAllShapes(via_lpp) - else: - shapes = shape_list + shapes = self.layout.getAllShapes(via_lpp) for boundary in shapes: - if shape_list is not None: - ll = boundary.ll() - ur = boundary.ur() - else: - # gdsMill boundaries are in (left, bottom, right, top) order - ll = vector(boundary[0], boundary[1]) - ur = vector(boundary[2], boundary[3]) + # gdsMill boundaries are in (left, bottom, right, top) order + ll = vector(boundary[0], boundary[1]) + ur = vector(boundary[2], boundary[3]) rect = [ll, ur] new_shape = graph_shape("via", rect, valid_lpp) # Skip this via if it's contained by an existing via blockage @@ -284,8 +264,7 @@ class router(router_tech): working for this router. """ - new_wires = [] - new_vias = [] + new_shapes = [] for i in range(0, len(nodes) - 1): start = nodes[i].center end = nodes[i + 1].center @@ -296,16 +275,15 @@ class router(router_tech): offset.y - self.half_wire) if direction == (1, 1): # Via offset = vector(start.x, start.y) - via = self.design.add_via_center(layers=self.layers, - offset=offset) - new_vias.append(via) + self.design.add_via_center(layers=self.layers, + offset=offset) else: # Wire - wire = self.design.add_rect(layer=self.get_layer(start.z), - offset=offset, - width=abs(diff.x) + self.track_wire, - height=abs(diff.y) + self.track_wire) - new_wires.append(wire) - return new_wires, new_vias + shape = self.design.add_rect(layer=self.get_layer(start.z), + offset=offset, + width=abs(diff.x) + self.track_wire, + height=abs(diff.y) + self.track_wire) + new_shapes.append(shape) + return new_shapes def write_debug_gds(self, gds_name, g=None, source=None, target=None): diff --git a/compiler/router/signal_escape_router.py b/compiler/router/signal_escape_router.py index 21cae924..5e4ae66e 100644 --- a/compiler/router/signal_escape_router.py +++ b/compiler/router/signal_escape_router.py @@ -5,7 +5,6 @@ # from openram import debug from openram.base.vector import vector -from openram.base.vector3d import vector3d from openram import OPTS from .graph import graph from .graph_shape import graph_shape @@ -57,82 +56,42 @@ class signal_escape_router(router): 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 - # Create the graph - g = graph(self) - g.create_graph(source, target) - # Find the shortest path from source to target - path = g.find_shortest_path() - # If no path is found, throw an error - if path is None: - 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) - # Create the path shapes on layout - new_wires, new_vias = self.add_path(path) - self.new_pins[source.name] = new_wires[-1] - # Find the recently added shapes - self.find_blockages(name, new_wires) - self.find_vias(new_vias) + # 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 + new_shapes = self.add_path(path) + self.new_pins[source.name] = new_shapes[0] + # Find the recently added shapes + self.prepare_gds_reader() + self.find_blockages(name) + self.find_vias() + break self.replace_layout_pins() - def get_closest_edge(self, point): - """ Return a point's the closest edge and the edge's axis direction. """ - - ll, ur = self.bbox - - # Snap the pin to the perimeter and break the iteration - ll_diff_x = abs(point.x - ll.x) - ll_diff_y = abs(point.y - ll.y) - ur_diff_x = abs(point.x - ur.x) - ur_diff_y = abs(point.y - ur.y) - min_diff = min(ll_diff_x, ll_diff_y, ur_diff_x, ur_diff_y) - - if min_diff == ll_diff_x: - return "left", True - if min_diff == ll_diff_y: - return "bottom", False - if min_diff == ur_diff_x: - return "right", True - return "top", False - - - def prepare_path(self, path): - """ - Override the `prepare_path` method from the `router` class to prevent - overflows from the SRAM layout area. - """ - - ll, ur = self.bbox - nodes = super().prepare_path(path) - new_nodes = [] - for i in range(len(nodes)): - node = nodes[i] - c = node.center - # Haven't overflown yet - if ll.x < c.x and c.x < ur.x and ll.y < c.y and c.y < ur.y: - new_nodes.append(node) - continue - # Snap the pin to the perimeter and break the iteration - edge, _ = self.get_closest_edge(c) - if edge == "left": - fake_center = vector3d(ll.x + self.half_wire, c.y, c.z) - if edge == "bottom": - fake_center = vector3d(c.x, ll.y + self.half_wire, c.z) - if edge == "right": - fake_center = vector3d(ur.x - self.half_wire, c.y, c.z) - if edge == "top": - fake_center = vector3d(c.x, ur.y - self.half_wire, c.z) - node.center = fake_center - new_nodes.append(node) - break - return new_nodes - - def add_perimeter_fake_pins(self): """ Add the fake pins on the perimeter to where the signals will be routed. - These perimeter fake pins are only used to replace layout pins at the - end of routing. """ ll, ur = self.bbox @@ -173,36 +132,17 @@ class signal_escape_router(router): self.fake_pins.append(pin) - def create_fake_pin(self, pin): - """ Create a fake pin on the perimeter orthogonal to the given pin. """ + def get_closest_perimeter_fake_pin(self, pin): + """ Return the closest fake pin for the given pin. """ - ll, ur = self.bbox - c = pin.center() - - # Find the closest edge - edge, vertical = self.get_closest_edge(c) - - # Keep the fake pin out of the SRAM layout are so that they won't be - # blocked by previous signals if they're on the same orthogonal line - if edge == "left": - fake_center = vector(ll.x - self.track_wire * 2, c.y) - if edge == "bottom": - fake_center = vector(c.x, ll.y - self.track_wire * 2) - if edge == "right": - fake_center = vector(ur.x + self.track_wire * 2, c.y) - if edge == "top": - fake_center = vector(c.x, ur.y + self.track_wire * 2) - - # Create the fake pin shape - layer = self.get_layer(int(not vertical)) - half_wire_vector = vector([self.half_wire] * 2) - nll = fake_center - half_wire_vector - nur = fake_center + half_wire_vector - rect = [nll, nur] - pin = graph_shape(name="fake", - rect=rect, - layer_name_pp=layer) - return 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 def get_route_pairs(self, pin_names): @@ -211,7 +151,7 @@ class signal_escape_router(router): to_route = [] for name in pin_names: pin = next(iter(self.pins[name])) - fake = self.create_fake_pin(pin) + fake = self.get_closest_perimeter_fake_pin(pin) to_route.append((pin, fake, pin.distance(fake))) return sorted(to_route, key=lambda x: x[2]) diff --git a/compiler/router/supply_router.py b/compiler/router/supply_router.py index 3cdcc4e6..f6acd24c 100644 --- a/compiler/router/supply_router.py +++ b/compiler/router/supply_router.py @@ -71,20 +71,35 @@ class supply_router(router): pins = self.pins[pin_name] # Route closest pins according to the minimum spanning tree for source, target in self.get_mst_pairs(list(pins)): - # Create the graph - g = graph(self) - g.create_graph(source, target) - # Find the shortest path from source to target - path = g.find_shortest_path() - # If no path is found, throw an error - if path is None: - 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) - # Create the path shapes on layout - new_wires, new_vias = self.add_path(path) - # Find the recently added shapes - self.find_blockages(pin_name, new_wires) - self.find_vias(new_vias) + # 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(pin_name) + self.find_vias() + break def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):