Add left stripe power routes to tree router as option.

This commit is contained in:
mrg 2021-05-05 13:45:12 -07:00
parent d3f4810d1b
commit f48b0b8f41
9 changed files with 185 additions and 129 deletions

View File

@ -122,35 +122,45 @@ class grid:
self.set_target(n) self.set_target(n)
# self.set_blocked(n, False) # self.set_blocked(n, False)
def add_perimeter_target(self, side="all"): def get_perimeter_list(self, side="left", layers=[0, 1], width=1, margin=0, offset=0):
debug.info(3, "Adding perimeter target") """
Side specifies which side.
Layer specifies horizontal (0) or vertical (1)
Width specifies how wide the perimter "stripe" should be.
"""
perimeter_list = [] perimeter_list = []
# Add the left/right columns # Add the left/right columns
if side=="all" or side=="left": if side=="all" or side=="left":
x = self.ll.x for x in range(self.ll.x + offset, self.ll.x + width + offset, 1):
for y in range(self.ll.y, self.ur.y, 1): for y in range(self.ll.y + margin, self.ur.y - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="right": if side=="all" or side=="right":
x = self.ur.x for x in range(self.ur.x - width - offset, self.ur.x - offset, 1):
for y in range(self.ll.y, self.ur.y, 1): for y in range(self.ll.y + margin, self.ur.y - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="bottom": if side=="all" or side=="bottom":
y = self.ll.y for y in range(self.ll.y + offset, self.ll.y + width + offset, 1):
for x in range(self.ll.x, self.ur.x, 1): for x in range(self.ll.x + margin, self.ur.x - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
if side=="all" or side=="top": if side=="all" or side=="top":
y = self.ur.y for y in range(self.ur.y - width - offset, self.ur.y - offset, 1):
for x in range(self.ll.x, self.ur.x, 1): for x in range(self.ll.x + margin, self.ur.x - margin, 1):
perimeter_list.append(vector3d(x, y, 0)) for layer in layers:
perimeter_list.append(vector3d(x, y, 1)) perimeter_list.append(vector3d(x, y, layer))
return perimeter_list
def add_perimeter_target(self, side="all", layers=[0, 1]):
debug.info(3, "Adding perimeter target")
perimeter_list = self.get_perimeter_list(side, layers)
self.set_target(perimeter_list) self.set_target(perimeter_list)
def is_target(self, point): def is_target(self, point):

View File

@ -155,6 +155,10 @@ class pin_group:
# Now simplify the enclosure list # Now simplify the enclosure list
new_pin_list = self.remove_redundant_shapes(pin_list) new_pin_list = self.remove_redundant_shapes(pin_list)
# Now add the right name
for pin in new_pin_list:
pin.name = self.name
debug.check(len(new_pin_list) > 0, debug.check(len(new_pin_list) > 0,
"Did not find any enclosures.") "Did not find any enclosures.")

View File

@ -28,7 +28,7 @@ class router(router_tech):
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
It populates blockages on a grid class. It populates blockages on a grid class.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None, margin=0, route_track_width=1): def __init__(self, layers, design, bbox=None, margin=0, route_track_width=1):
""" """
This will instantiate a copy of the gds file or the module at (0,0) and This will instantiate a copy of the gds file or the module at (0,0) and
route on top of this. The blockages from the gds/module will be route on top of this. The blockages from the gds/module will be
@ -39,19 +39,7 @@ class router(router_tech):
self.cell = design self.cell = design
# If didn't specify a gds blockage file, write it out to read the gds self.gds_filename = OPTS.openram_temp + "temp.gds"
# This isn't efficient, but easy for now
# start_time = datetime.now()
if not gds_filename:
gds_filename = OPTS.openram_temp+"temp.gds"
self.cell.gds_write(gds_filename)
# Load the gds file and read in all the shapes
self.layout = gdsMill.VlsiLayout(units=GDS["unit"])
self.reader = gdsMill.Gds2reader(self.layout)
self.reader.loadFromFile(gds_filename)
self.top_name = self.layout.rootStructureName
# print_time("GDS read",datetime.now(), start_time)
# The pin data structures # The pin data structures
# A map of pin names to a set of pin_layout structures # A map of pin names to a set of pin_layout structures
@ -91,6 +79,16 @@ class router(router_tech):
""" """
Initialize the ll,ur values with the paramter or using the layout boundary. 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: if not bbox:
# The boundary will determine the limits to the size # The boundary will determine the limits to the size
# of the routing grid # of the routing grid
@ -178,6 +176,17 @@ class router(router_tech):
""" """
Find the pins and blockages in the design Find the pins and blockages in the design
""" """
# 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
# print_time("GDS read",datetime.now(), start_time)
# This finds the pin shapes and sorts them into "groups" that # This finds the pin shapes and sorts them into "groups" that
# are connected. This must come before the blockages, so we # are connected. This must come before the blockages, so we
# can not count the pins themselves # can not count the pins themselves
@ -881,12 +890,32 @@ class router(router_tech):
# Clearing the blockage of this pin requires the inflated pins # Clearing the blockage of this pin requires the inflated pins
self.clear_blockages(pin_name) self.clear_blockages(pin_name)
def add_side_supply_pin(self, name, side="left", width=2):
"""
Adds a supply pin to the perimeter and resizes the bounding box.
"""
pg = pin_group(name, [], self)
if name == "vdd":
offset = width
else:
offset = 0
pg.grids = set(self.rg.get_perimeter_list(side=side,
width=width,
margin=self.margin,
offset=offset,
layers=[1]))
pg.enclosures = pg.compute_enclosures()
pg.pins = set(pg.enclosures)
self.cell.pin_map[name].update(pg.pins)
self.pin_groups[name].append(pg)
def add_perimeter_target(self, side="all"): def add_perimeter_target(self, side="all"):
""" """
This will mark all the cells on the perimeter of the original layout as a target. This will mark all the cells on the perimeter of the original layout as a target.
""" """
self.rg.add_perimeter_target(side=side) self.rg.add_perimeter_target(side=side)
def num_pin_components(self, pin_name): def num_pin_components(self, pin_name):
""" """
This returns how many disconnected pin components there are. This returns how many disconnected pin components there are.
@ -1219,7 +1248,7 @@ class router(router_tech):
""" Return the lowest, leftest pin group """ """ Return the lowest, leftest pin group """
keep_pin = None keep_pin = None
for index,pg in enumerate(self.pin_groups[pin_name]): for index, pg in enumerate(self.pin_groups[pin_name]):
for pin in pg.enclosures: for pin in pg.enclosures:
if not keep_pin: if not keep_pin:
keep_pin = pin keep_pin = pin
@ -1229,23 +1258,6 @@ class router(router_tech):
return keep_pin return keep_pin
def get_left_pins(self, pin_name):
""" Return the leftest pin(s) group """
keep_pins = []
for index, pg in enumerate(self.pin_groups[pin_name]):
for pin in pg.enclosures:
if not keep_pins:
keep_pins = [pin]
else:
# Only need to check first since they are all the same left value
if pin.lx() == keep_pins[0].lx():
keep_pins.append(pin)
elif pin.lx() < keep_pins[0].lx():
keep_pins = [pin]
return keep_pins
def check_all_routed(self, pin_name): def check_all_routed(self, pin_name):
""" """
Check that all pin groups are routed. Check that all pin groups are routed.

View File

@ -123,7 +123,7 @@ class router_tech:
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf) min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
min_width = drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf) min_width = self.route_track_width * drc("minwidth_{0}".format(layer_name), self.route_track_width * min_wire_width, math.inf)
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf) min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.route_track_width * min_wire_width, math.inf)
return (min_width, min_spacing) return (min_width, min_spacing)

View File

@ -17,7 +17,7 @@ class signal_escape_router(router):
A router that routes signals to perimeter and makes pins. A router that routes signals to perimeter and makes pins.
""" """
def __init__(self, layers, design, bbox=None, margin=0, gds_filename=None): def __init__(self, layers, design, bbox=None, margin=0):
""" """
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).
@ -25,7 +25,6 @@ class signal_escape_router(router):
router.__init__(self, router.__init__(self,
layers=layers, layers=layers,
design=design, design=design,
gds_filename=gds_filename,
bbox=bbox, bbox=bbox,
margin=margin) margin=margin)

View File

@ -15,12 +15,12 @@ class signal_router(router):
route on a given layer. This is limited to two layer routes. route on a given layer. This is limited to two layer routes.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, bbox=None):
""" """
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).
""" """
router.__init__(self, layers, design, gds_filename, bbox) router.__init__(self, layers, design, bbox)
def route(self, src, dest, detour_scale=5): def route(self, src, dest, detour_scale=5):
""" """

View File

@ -21,7 +21,7 @@ class supply_grid_router(router):
routes a grid to connect the supply on the two layers. routes a grid to connect the supply on the two layers.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, margin=0, bbox=None):
""" """
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).
@ -29,9 +29,9 @@ class supply_grid_router(router):
start_time = datetime.now() start_time = datetime.now()
# Power rail width in minimum wire widths # Power rail width in minimum wire widths
self.route_track_width = 2 self.route_track_width = 1
router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) router.__init__(self, layers, design, bbox=bbox, margin=margin, route_track_width=self.route_track_width)
# The list of supply rails (grid sets) that may be routed # The list of supply rails (grid sets) that may be routed
self.supply_rails = {} self.supply_rails = {}
@ -357,8 +357,9 @@ class supply_grid_router(router):
# This is inefficient since it is non-incremental, but it was # This is inefficient since it is non-incremental, but it was
# easier to debug. # easier to debug.
self.prepare_blockages(pin_name) self.prepare_blockages()
self.clear_blockages(self.vdd_name)
# Add the single component of the pin as the source # Add the single component of the pin as the source
# which unmarks it as a blockage too # which unmarks it as a blockage too
self.add_pin_component_source(pin_name, index) self.add_pin_component_source(pin_name, index)
@ -369,7 +370,7 @@ class supply_grid_router(router):
# Actually run the A* router # Actually run the A* router
if not self.run_router(detour_scale=5): if not self.run_router(detour_scale=5):
self.write_debug_gds("debug_route.gds", False) self.write_debug_gds("debug_route.gds")
# if index==3 and pin_name=="vdd": # if index==3 and pin_name=="vdd":
# self.write_debug_gds("route.gds",False) # self.write_debug_gds("route.gds",False)

View File

@ -21,7 +21,7 @@ class supply_tree_router(router):
routes a grid to connect the supply on the two layers. routes a grid to connect the supply on the two layers.
""" """
def __init__(self, layers, design, gds_filename=None, bbox=None): def __init__(self, layers, design, bbox=None, side_pin=None):
""" """
This will route on layers in design. It will get the blockages from This will route on layers in design. It will get the blockages from
either the gds file name or the design itself (by saving to a gds file). either the gds file name or the design itself (by saving to a gds file).
@ -31,11 +31,19 @@ class supply_tree_router(router):
# for prettier routes. # for prettier routes.
self.route_track_width = 1 self.route_track_width = 1
router.__init__(self, layers, design, gds_filename, bbox, self.route_track_width) # The pin escape router already made the bounding box big enough,
# so we can use the regular bbox here.
self.side_pin = side_pin
router.__init__(self,
layers,
design,
bbox=bbox,
route_track_width=self.route_track_width)
def route(self, vdd_name="vdd", gnd_name="gnd"): def route(self, vdd_name="vdd", gnd_name="gnd"):
""" """
Route the two nets in a single layer) Route the two nets in a single layer.
Setting pin stripe will make a power rail on the left side.
""" """
debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
self.vdd_name = vdd_name self.vdd_name = vdd_name
@ -50,11 +58,17 @@ class supply_tree_router(router):
# but this is simplest for now. # but this is simplest for now.
self.create_routing_grid(signal_grid) self.create_routing_grid(signal_grid)
# Get the pin shapes
start_time = datetime.now() start_time = datetime.now()
# Get the pin shapes
self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
print_time("Finding pins and blockages", datetime.now(), start_time, 3) print_time("Finding pins and blockages", datetime.now(), start_time, 3)
# Add side pins if enabled
if self.side_pin:
self.add_side_supply_pin(self.vdd_name)
self.add_side_supply_pin(self.gnd_name)
# Route the supply pins to the supply rails # Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter # Route vdd first since we want it to be shorter
start_time = datetime.now() start_time = datetime.now()
@ -87,15 +101,15 @@ class supply_tree_router(router):
pin_size = len(self.pin_groups[pin_name]) pin_size = len(self.pin_groups[pin_name])
adj_matrix = [[0] * pin_size for i in range(pin_size)] adj_matrix = [[0] * pin_size for i in range(pin_size)]
for index1,pg1 in enumerate(self.pin_groups[pin_name]): for index1, pg1 in enumerate(self.pin_groups[pin_name]):
for index2,pg2 in enumerate(self.pin_groups[pin_name]): for index2, pg2 in enumerate(self.pin_groups[pin_name]):
if index1>=index2: if index1>=index2:
continue continue
dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids)) dist = int(grid_utils.distance_set(list(pg1.grids)[0], pg2.grids))
adj_matrix[index1][index2] = dist adj_matrix[index1][index2] = dist
# Find MST # Find MST
debug.info(2, "Finding MinimumSpanning Tree") debug.info(2, "Finding Minimum Spanning Tree")
X = csr_matrix(adj_matrix) X = csr_matrix(adj_matrix)
Tcsr = minimum_spanning_tree(X) Tcsr = minimum_spanning_tree(X)
mst = Tcsr.toarray().astype(int) mst = Tcsr.toarray().astype(int)

View File

@ -225,6 +225,38 @@ class sram_base(design, verilog, lef):
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst, pin_name) self.copy_power_pins(inst, pin_name)
try:
from tech import power_grid
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
grid_stack = self.m3_stack
# lowest_coord = self.find_lowest_coords()
# highest_coord = self.find_highest_coords()
# # Add two rails to the side
# if OPTS.route_supplies == "side":
# supply_pins = {}
# # Find the lowest leftest pin for vdd and gnd
# for (pin_name, pin_index) in [("vdd", 0), ("gnd", 1)]:
# pin_width = 8 * getattr(self, "{}_width".format(grid_stack[2]))
# pin_space = 2 * getattr(self, "{}_space".format(grid_stack[2]))
# supply_pitch = pin_width + pin_space
# # Add side power rails on left from bottom to top
# # These have a temporary name and will be connected later.
# # They are here to reserve space now and ensure other pins go beyond
# # their perimeter.
# supply_height = highest_coord.y - lowest_coord.y
# supply_pins[pin_name] = self.add_layout_pin(text=pin_name,
# layer=grid_stack[2],
# offset=lowest_coord + vector(pin_index * supply_pitch, 0),
# width=pin_width,
# height=supply_height)
if not OPTS.route_supplies: if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins) # Do not route the power supply (leave as must-connect pins)
@ -233,71 +265,52 @@ class sram_base(design, verilog, lef):
from supply_grid_router import supply_grid_router as router from supply_grid_router import supply_grid_router as router
else: else:
from supply_tree_router import supply_tree_router as router from supply_tree_router import supply_tree_router as router
try:
from tech import power_grid
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
grid_stack = self.m3_stack
rtr=router(grid_stack, self) rtr=router(grid_stack, self, side_pin=(OPTS.route_supplies == "side"))
rtr.route() rtr.route()
lowest_coord = self.find_lowest_coords() if OPTS.route_supplies == "side":
highest_coord = self.find_highest_coords() # Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Find the lowest leftest pin for vdd and gnd # Copy the pin shape(s) to rectangles
for (pin_name, pin_index) in [("vdd", 0), ("gnd", 1)]: for pin in self.get_pins(pin_name):
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Either add two long rails or a simple pin
if OPTS.route_supplies == "side":
# Get the leftest pins
pins = rtr.get_left_pins(pin_name)
pin_width = 2 * getattr(self, "{}_width".format(pins[0].layer))
pin_space = 2 * getattr(self, "{}_space".format(pins[0].layer))
supply_pitch = pin_width + pin_space
# Add side power rails on left from bottom to top
# These have a temporary name and will be connected later.
# They are here to reserve space now and ensure other pins go beyond
# their perimeter.
supply_height = highest_coord.y - lowest_coord.y
supply_pin = self.add_layout_pin(text=pin_name,
layer="m4",
offset=lowest_coord + vector(pin_index * supply_pitch, 0),
width=pin_width,
height=supply_height)
route_width = pins[0].rx() - lowest_coord.x
for pin in pins:
pin_offset = vector(lowest_coord.x, pin.by())
self.add_rect(pin.layer, self.add_rect(pin.layer,
pin_offset, pin.ll(),
route_width, pin.width(),
pin.height()) pin.height())
center_offset = vector(supply_pin.cx(),
pin.cy()) # Remove the pin shape(s)
self.add_via_center(layers=self.m3_stack, self.remove_layout_pin(pin_name)
offset=center_offset)
else:
# Get the lowest, leftest pin # Get the lowest, leftest pin
pin = rtr.get_ll_pins(pin_name) pin = rtr.get_ll_pin(pin_name)
self.add_layout_pin(pin_name,
pin_width = 2 * getattr(self, "{}_width".format(pin.layer)) pin.layer,
pin_space = 2 * getattr(self, "{}_space".format(pin.layer)) pin.ll(),
pin.width(),
pin.height())
elif OPTS.route_supplies == "tree":
# Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords()
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get the lowest, leftest pin
pin = rtr.get_ll_pin(pin_name)
pin_width = 2 * getattr(self, "{}_width".format(pin.layer))
# Add it as an IO pin to the perimeter # Add it as an IO pin to the perimeter
route_width = pin.rx() - lowest_coord.x route_width = pin.rx() - lowest_coord.x
pin_offset = vector(lowest_coord.x, pin.by()) pin_offset = vector(lowest_coord.x, pin.by())
@ -311,6 +324,9 @@ class sram_base(design, verilog, lef):
pin_offset, pin_offset,
pin_width, pin_width,
pin.height()) pin.height())
else:
# Grid is left with many top level pins
pass
def route_escape_pins(self): def route_escape_pins(self):
""" """
@ -355,7 +371,7 @@ class sram_base(design, verilog, lef):
from signal_escape_router import signal_escape_router as router from signal_escape_router import signal_escape_router as router
rtr=router(layers=self.m3_stack, rtr=router(layers=self.m3_stack,
design=self, design=self,
margin=4 * self.m3_pitch) margin=8 * self.m3_pitch)
rtr.escape_route(pins_to_route) rtr.escape_route(pins_to_route)
def compute_bus_sizes(self): def compute_bus_sizes(self):