Improve supply routing for ring and side pins

This commit is contained in:
mrg 2021-05-28 10:58:30 -07:00
parent da67edbde8
commit f6587badad
9 changed files with 134 additions and 67 deletions

View File

@ -41,7 +41,8 @@ class layout():
self.width = None
self.height = None
self.bounding_box = None
self.bounding_box = None # The rectangle shape
self.bbox = None # The ll, ur coords
# Holds module/cell layout instances
self.insts = []
# Set of names to check for duplicates
@ -1163,6 +1164,57 @@ class layout():
self.bbox = [self.bounding_box.ll(), self.bounding_box.ur()]
def get_bbox(self, side="all", big_margin=0, little_margin=0):
"""
Get the bounding box from the GDS
"""
gds_filename = OPTS.openram_temp + "temp.gds"
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.gds_write(gds_filename)
layout = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(layout)
reader.loadFromFile(gds_filename)
top_name = layout.rootStructureName
if not self.bbox:
# The boundary will determine the limits to the size
# of the routing grid
boundary = layout.measureBoundary(top_name)
# These must be un-indexed to get rid of the matrix type
ll = vector(boundary[0][0], boundary[0][1])
ur = vector(boundary[1][0], boundary[1][1])
else:
ll, ur = self.bbox
ll_offset = vector(0, 0)
ur_offset = vector(0, 0)
if side in ["ring", "top"]:
ur_offset += vector(0, big_margin)
else:
ur_offset += vector(0, little_margin)
if side in ["ring", "bottom"]:
ll_offset += vector(0, big_margin)
else:
ll_offset += vector(0, little_margin)
if side in ["ring", "left"]:
ll_offset += vector(big_margin, 0)
else:
ll_offset += vector(little_margin, 0)
if side in ["ring", "right"]:
ur_offset += vector(big_margin, 0)
else:
ur_offset += vector(little_margin, 0)
bbox = (ll - ll_offset, ur + ur_offset)
size = ur - ll
debug.info(1, "Size: {0} x {1} with perimeter big margin {2} little margin {3}".format(size.x,
size.y,
big_margin,
little_margin))
return bbox
def add_enclosure(self, insts, layer="nwell", extend=0, leftx=None, rightx=None, topy=None, boty=None):
"""
Add a layer that surrounds the given instances. Useful

View File

@ -112,23 +112,25 @@ class lef:
for pin_name in self.pins:
pins = self.get_pins(pin_name)
for pin in pins:
inflated_pin = pin.inflated_pin(multiple=1)
another_iteration_needed = True
while another_iteration_needed:
another_iteration_needed = False
inflated_pin = pin.inflated_pin(multiple=2)
continue_fragmenting = True
while continue_fragmenting:
continue_fragmenting = False
old_blockages = list(self.blockages[pin.layer])
for blockage in old_blockages:
if blockage.overlaps(inflated_pin):
intersection_shape = blockage.intersection(inflated_pin)
# If it is zero area, don't add the pin
# If it is zero area, don't split the blockage
if intersection_shape[0][0]==intersection_shape[1][0] or intersection_shape[0][1]==intersection_shape[1][1]:
continue
another_iteration_needed = True
# Remove the old blockage and add the new ones
self.blockages[pin.layer].remove(blockage)
intersection_pin = pin_layout("", intersection_shape, inflated_pin.layer)
new_blockages = blockage.cut(intersection_pin)
self.blockages[pin.layer].extend(new_blockages)
# We split something so make another pass
continue_fragmenting = True
def lef_write_header(self):
""" Header of LEF file """

View File

@ -606,7 +606,9 @@ class pin_layout:
# Don't add the existing shape in if it overlaps the pin shape
if new_shape.contains(shape):
continue
new_shapes.append(new_shape)
# Only add non-zero shapes
if new_shape.area() > 0:
new_shapes.append(new_shape)
return new_shapes

View File

@ -9,8 +9,8 @@ nominal_corner_only = True
# Local wordlines have issues with met3 power routing for now
#local_array_size = 16
#route_supplies = "ring"
route_supplies = "left"
route_supplies = "ring"
#route_supplies = "left"
check_lvsdrc = True
#perimeter_pins = False
#netlist_only = True

View File

@ -37,6 +37,8 @@ class grid:
# This is really lower left bottom layer and upper right top layer in 3D.
self.ll = vector3d(ll.x, ll.y, 0).scale(self.track_factor).round()
self.ur = vector3d(ur.x, ur.y, 0).scale(self.track_factor).round()
debug.info(1, "BBOX coords: ll=" + str(ll) + " ur=" + str(ur))
debug.info(1, "BBOX grids: ll=" + str(self.ll) + " ur=" + str(self.ur))
# let's leave the map sparse, cells are created on demand to reduce memory
self.map={}
@ -127,33 +129,47 @@ class grid:
Side specifies which side.
Layer specifies horizontal (0) or vertical (1)
Width specifies how wide the perimter "stripe" should be.
Works from the inside out from the bbox (ll, ur)
"""
if "ring" in side:
ring_width = width
else:
ring_width = 0
if "ring" in side:
ring_offset = offset
else:
ring_offset = 0
perimeter_list = []
# Add the left/right columns
if side=="all" or side=="left":
for x in range(self.ll.x + offset, self.ll.x + width + offset, 1):
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1):
if side=="all" or "left" in side:
for x in range(self.ll.x - offset, self.ll.x - width - offset, -1):
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
for layer in layers:
perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="right":
for x in range(self.ur.x - width - offset, self.ur.x - offset, 1):
for y in range(self.ll.y + offset + margin, self.ur.y - offset - margin, 1):
if side=="all" or "right" in side:
for x in range(self.ur.x + offset, self.ur.x + width + offset, 1):
for y in range(self.ll.y - ring_offset - margin - ring_width + 1, self.ur.y + ring_offset + margin + ring_width, 1):
for layer in layers:
perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="bottom":
for y in range(self.ll.y + offset, self.ll.y + width + offset, 1):
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1):
if side=="all" or "bottom" in side:
for y in range(self.ll.y - offset, self.ll.y - width - offset, -1):
for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
for layer in layers:
perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="top":
for y in range(self.ur.y - width - offset, self.ur.y - offset, 1):
for x in range(self.ll.x + offset + margin, self.ur.x - offset - margin, 1):
if side=="all" or "top" in side:
for y in range(self.ur.y + offset, self.ur.y + width + offset, 1):
for x in range(self.ll.x - ring_offset - margin - ring_width + 1, self.ur.x + ring_offset + margin + ring_width, 1):
for layer in layers:
perimeter_list.append(vector3d(x, y, layer))
# Add them all to the map
self.add_map(perimeter_list)
return perimeter_list
def add_perimeter_target(self, side="all", layers=[0, 1]):

View File

@ -82,31 +82,13 @@ class router(router_tech):
"""
Initialize the ll,ur values with the paramter or using the layout boundary.
"""
# If didn't specify a gds blockage file, write it out to read the gds
# This isn't efficient, but easy for now
# Load the gds file and read in all the shapes
self.cell.gds_write(self.gds_filename)
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(self.gds_filename)
self.top_name = self.layout.rootStructureName
if not bbox:
# The boundary will determine the limits to the size
# of the routing grid
self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
self.bbox = self.cell.get_bbox(margin)
else:
self.ll, self.ur = bbox
self.bbox = bbox
(self.ll, self.ur) = self.bbox
margin_offset = vector(margin, margin)
self.bbox = (self.ll - margin_offset, self.ur + margin_offset)
size = self.ur - self.ll
debug.info(1, "Size: {0} x {1} with perimeter margin {2}".format(size.x, size.y, margin))
def get_bbox(self):
return self.bbox
@ -893,19 +875,21 @@ class router(router_tech):
# Clearing the blockage of this pin requires the inflated pins
self.clear_blockages(pin_name)
def add_side_supply_pin(self, name, side="left", width=2):
def add_side_supply_pin(self, name, side="left", width=3, space=2):
"""
Adds a supply pin to the perimeter and resizes the bounding box.
"""
pg = pin_group(name, [], self)
if name == "gnd":
offset = width + 1
# Offset two spaces inside and one between the rings
if name == "vdd":
offset = width + 2 * space
else:
offset = 1
offset = space
if side in ["left", "right"]:
layers = [1]
else:
layers = [0]
pg.grids = set(self.rg.get_perimeter_list(side=side,
width=width,
margin=self.margin,
@ -920,39 +904,39 @@ class router(router_tech):
self.new_pins[name] = pg.pins
def add_ring_supply_pin(self, name, width=2):
def add_ring_supply_pin(self, name, width=3, space=2):
"""
Adds a ring supply pin that goes inside the given bbox.
"""
pg = pin_group(name, [], self)
# Offset the vdd inside one ring width
# Offset two spaces inside and one between the rings
# Units are in routing grids
if name == "gnd":
offset = width + 1
if name == "vdd":
offset = width + 2 * space
else:
offset = 1
offset = space
# LEFT
left_grids = set(self.rg.get_perimeter_list(side="left",
left_grids = set(self.rg.get_perimeter_list(side="left_ring",
width=width,
margin=self.margin,
offset=offset,
layers=[1]))
# RIGHT
right_grids = set(self.rg.get_perimeter_list(side="right",
right_grids = set(self.rg.get_perimeter_list(side="right_ring",
width=width,
margin=self.margin,
offset=offset,
layers=[1]))
# TOP
top_grids = set(self.rg.get_perimeter_list(side="top",
top_grids = set(self.rg.get_perimeter_list(side="top_ring",
width=width,
margin=self.margin,
offset=offset,
layers=[0]))
# BOTTOM
bottom_grids = set(self.rg.get_perimeter_list(side="bottom",
bottom_grids = set(self.rg.get_perimeter_list(side="bottom_ring",
width=width,
margin=self.margin,
offset=offset,

View File

@ -34,7 +34,7 @@ class supply_tree_router(router):
# The pin escape router already made the bounding box big enough,
# so we can use the regular bbox here.
if pin_type:
debug.check(pin_type in ["left", "right", "top", "bottom", "tree", "ring"],
debug.check(pin_type in ["left", "right", "top", "bottom", "single", "ring"],
"Invalid pin type {}".format(pin_type))
self.pin_type = pin_type
router.__init__(self,

View File

@ -329,13 +329,21 @@ class sram_1bank(sram_base):
# Some technologies have an isolation
self.add_dnwell(inflate=2)
# We need the initial bbox for the supply rings later
# because the perimeter pins will change the bbox
# Route the pins to the perimeter
pre_bbox = None
if OPTS.perimeter_pins:
self.route_escape_pins()
pre_bbox = self.get_bbox(side="ring",
big_margin=self.m3_pitch)
bbox = self.get_bbox(side=OPTS.route_supplies,
big_margin=14 * self.m3_pitch,
little_margin=4 * self.m3_pitch)
self.route_escape_pins(bbox)
# Route the supplies first since the MST is not blockage aware
# and signals can route to anywhere on sides (it is flexible)
self.route_supplies()
self.route_supplies(pre_bbox)
def route_dffs(self, add_routes=True):

View File

@ -230,7 +230,7 @@ class sram_base(design, verilog, lef):
def create_modules(self):
debug.error("Must override pure virtual function.", -1)
def route_supplies(self):
def route_supplies(self, bbox=None):
""" Route the supply grid and connect the pins to them. """
# Copy the pins to the top level
@ -252,11 +252,14 @@ class sram_base(design, verilog, lef):
return
elif OPTS.route_supplies == "grid":
from supply_grid_router import supply_grid_router as router
rtr=router(grid_stack, self)
rtr=router(layers=grid_stack,
design=self,
bbox=bbox)
else:
from supply_tree_router import supply_tree_router as router
rtr=router(grid_stack,
self,
rtr=router(layers=grid_stack,
design=self,
bbox=bbox,
pin_type=OPTS.route_supplies)
rtr.route()
@ -283,7 +286,7 @@ class sram_base(design, verilog, lef):
pin.width(),
pin.height())
elif OPTS.route_supplies:
elif OPTS.route_supplies or OPTS.route_supplies == "single":
# Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords()
@ -321,7 +324,7 @@ class sram_base(design, verilog, lef):
# Grid is left with many top level pins
pass
def route_escape_pins(self):
def route_escape_pins(self, bbox):
"""
Add the top-level pins for a single bank SRAM with control.
"""
@ -364,7 +367,7 @@ class sram_base(design, verilog, lef):
from signal_escape_router import signal_escape_router as router
rtr=router(layers=self.m3_stack,
design=self,
margin=8 * self.m3_pitch)
bbox=bbox)
rtr.escape_route(pins_to_route)
def compute_bus_sizes(self):