merge dev

This commit is contained in:
Jesse Cirimelli-Low 2023-08-30 22:04:42 -07:00
commit daec840888
7 changed files with 161 additions and 107 deletions

View File

@ -13,6 +13,7 @@ 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

View File

@ -1 +1 @@
1.2.29
1.2.32

View File

@ -184,7 +184,7 @@ class graph:
return False
def create_graph(self, source, target, scale=1):
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))
@ -195,7 +195,6 @@ 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))
@ -221,9 +220,6 @@ 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. """
@ -422,6 +418,7 @@ 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

View File

@ -72,17 +72,6 @@ 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

View File

@ -59,6 +59,8 @@ 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)
@ -109,16 +111,26 @@ class router(router_tech):
self.all_pins.update(pin_set)
def find_blockages(self, name="blockage"):
def find_blockages(self, name="blockage", shape_list=None):
""" Find all blockages in the routing layers. """
debug.info(2, "Finding blockages...")
for lpp in [self.vert_lpp, self.horiz_lpp]:
shapes = self.layout.getAllShapes(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
for boundary in shapes:
# gdsMill boundaries are in (left, bottom, right, top) order
ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3])
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])
rect = [ll, ur]
new_shape = graph_shape(name, rect, lpp)
new_shape = self.inflate_shape(new_shape)
@ -132,20 +144,28 @@ class router(router_tech):
self.blockages.append(new_shape)
def find_vias(self):
def find_vias(self, shape_list=None):
""" 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
valid_lpp = self.horiz_lpp # Just a temporary lpp to prevent errors
shapes = self.layout.getAllShapes(via_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
for boundary in shapes:
# gdsMill boundaries are in (left, bottom, right, top) order
ll = vector(boundary[0], boundary[1])
ur = vector(boundary[2], boundary[3])
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])
rect = [ll, ur]
new_shape = graph_shape("via", rect, valid_lpp)
# Skip this via if it's contained by an existing via blockage
@ -264,7 +284,8 @@ class router(router_tech):
working for this router.
"""
new_shapes = []
new_wires = []
new_vias = []
for i in range(0, len(nodes) - 1):
start = nodes[i].center
end = nodes[i + 1].center
@ -275,15 +296,16 @@ class router(router_tech):
offset.y - self.half_wire)
if direction == (1, 1): # Via
offset = vector(start.x, start.y)
self.design.add_via_center(layers=self.layers,
offset=offset)
via = self.design.add_via_center(layers=self.layers,
offset=offset)
new_vias.append(via)
else: # Wire
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
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
def write_debug_gds(self, gds_name, g=None, source=None, target=None):

View File

@ -5,6 +5,7 @@
#
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
@ -56,42 +57,82 @@ 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
# 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
# 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)
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
@ -132,17 +173,36 @@ class signal_escape_router(router):
self.fake_pins.append(pin)
def get_closest_perimeter_fake_pin(self, pin):
""" Return the closest fake pin for the given pin. """
def create_fake_pin(self, pin):
""" Create a fake pin on the perimeter orthogonal to 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
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
def get_route_pairs(self, pin_names):
@ -151,7 +211,7 @@ class signal_escape_router(router):
to_route = []
for name in pin_names:
pin = next(iter(self.pins[name]))
fake = self.get_closest_perimeter_fake_pin(pin)
fake = self.create_fake_pin(pin)
to_route.append((pin, fake, pin.distance(fake)))
return sorted(to_route, key=lambda x: x[2])

View File

@ -71,35 +71,20 @@ 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)):
# 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
# 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)
def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):