mirror of https://github.com/VLSIDA/OpenRAM.git
Improve supply routing for ring and side pins
This commit is contained in:
parent
da67edbde8
commit
f6587badad
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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]):
|
||||
|
|
|
|||
|
|
@ -82,30 +82,12 @@ 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
|
||||
|
||||
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))
|
||||
(self.ll, self.ur) = self.bbox
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
Loading…
Reference in New Issue