mirror of https://github.com/VLSIDA/OpenRAM.git
first commt
This commit is contained in:
parent
f56460bb94
commit
86588619fd
|
|
@ -1913,6 +1913,42 @@ class layout():
|
|||
# Just use the power pin function for now to save code
|
||||
self.add_power_pin(new_name, pin.center(), start_layer=start_layer, directions=directions)
|
||||
|
||||
def add_power_pin_no_via(self, name, loc, directions=None, start_layer="m1"):
|
||||
# same function like normal one, but do not add via to the gird layer
|
||||
# Hack for min area
|
||||
if OPTS.tech_name == "sky130":
|
||||
min_area = drc["minarea_{}".format(self.pwr_grid_layers[1])]
|
||||
width = round_to_grid(sqrt(min_area))
|
||||
height = round_to_grid(min_area / width)
|
||||
else:
|
||||
width = None
|
||||
height = None
|
||||
|
||||
pin = None
|
||||
if start_layer in self.pwr_grid_layers:
|
||||
pin = self.add_layout_pin_rect_center(text=name,
|
||||
layer=start_layer,
|
||||
offset=loc,
|
||||
width=width,
|
||||
height=height)
|
||||
else:
|
||||
via = self.add_via_stack_center(from_layer=start_layer,
|
||||
to_layer=start_layer,# so only enclosure shape will be added
|
||||
offset=loc,
|
||||
directions=directions)
|
||||
|
||||
if not width:
|
||||
width = via.width
|
||||
if not height:
|
||||
height = via.height
|
||||
pin = self.add_layout_pin_rect_center(text=name,
|
||||
layer=start_layer,
|
||||
offset=loc,
|
||||
width=width,
|
||||
height=height)
|
||||
|
||||
return pin
|
||||
|
||||
def add_power_pin(self, name, loc, directions=None, start_layer="m1"):
|
||||
# Hack for min area
|
||||
if OPTS.tech_name == "sky130":
|
||||
|
|
@ -2027,7 +2063,7 @@ class layout():
|
|||
layer=layer,
|
||||
offset=peri_pin_loc)
|
||||
|
||||
def add_dnwell(self, bbox=None, inflate=1):
|
||||
def add_dnwell(self, bbox=None, inflate=1, add_vias=True):
|
||||
""" Create a dnwell, along with nwell moat at border. """
|
||||
|
||||
if "dnwell" not in tech_layer:
|
||||
|
|
@ -2049,11 +2085,20 @@ class layout():
|
|||
ul = vector(ll.x, ur.y)
|
||||
lr = vector(ur.x, ll.y)
|
||||
|
||||
# Add the dnwell
|
||||
self.add_rect("dnwell",
|
||||
offset=ll,
|
||||
height=ur.y - ll.y,
|
||||
width=ur.x - ll.x)
|
||||
# Hack for sky130 klayout drc rule nwell.6
|
||||
if OPTS.tech_name == "sky130":
|
||||
# Apply the drc rule
|
||||
# Add the dnwell
|
||||
self.add_rect("dnwell",
|
||||
offset=ll - vector(0.5 * self.nwell_width, 0.5 * self.nwell_width) - vector(drc["minclosure_nwell_by_dnwell"], drc["minclosure_nwell_by_dnwell"]),
|
||||
height=ur.y - ll.y + self.nwell_width + 2 * drc["minclosure_nwell_by_dnwell"],
|
||||
width=ur.x - ll.x + self.nwell_width + 2 * drc["minclosure_nwell_by_dnwell"])
|
||||
else: # other tech
|
||||
# Add the dnwell
|
||||
self.add_rect("dnwell",
|
||||
offset=ll,
|
||||
height=ur.y - ll.y,
|
||||
width=ur.x - ll.x)
|
||||
|
||||
# Add the moat
|
||||
self.add_path("nwell", [ll, lr, ur, ul, ll - vector(0, 0.5 * self.nwell_width)])
|
||||
|
|
@ -2080,9 +2125,14 @@ class layout():
|
|||
to_layer="m1",
|
||||
offset=loc)
|
||||
else:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
if add_vias:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
else:
|
||||
self.add_power_pin_no_via(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
count += 1
|
||||
loc += nwell_offset.scale(tap_spacing, 0)
|
||||
|
||||
|
|
@ -2100,9 +2150,14 @@ class layout():
|
|||
to_layer="m1",
|
||||
offset=loc)
|
||||
else:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
if add_vias:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
else:
|
||||
self.add_power_pin_no_via(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
count += 1
|
||||
loc += nwell_offset.scale(tap_spacing, 0)
|
||||
|
||||
|
|
@ -2120,9 +2175,14 @@ class layout():
|
|||
to_layer="m2",
|
||||
offset=loc)
|
||||
else:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
if add_vias:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
else:
|
||||
self.add_power_pin_no_via(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
count += 1
|
||||
loc += nwell_offset.scale(0, tap_spacing)
|
||||
|
||||
|
|
@ -2140,9 +2200,14 @@ class layout():
|
|||
to_layer="m2",
|
||||
offset=loc)
|
||||
else:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
if add_vias:
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
else:
|
||||
self.add_power_pin_no_via(name="vdd",
|
||||
loc=loc,
|
||||
start_layer="li")
|
||||
count += 1
|
||||
loc += nwell_offset.scale(0, tap_spacing)
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ class sram_1bank(design, verilog, lef):
|
|||
# delay control logic does not have RBLs
|
||||
self.has_rbl = OPTS.control_logic != "control_logic_delay"
|
||||
|
||||
# IO pins, except power, list of pin names
|
||||
self.pins_to_route = []
|
||||
|
||||
def add_pins(self):
|
||||
""" Add pins for entire SRAM. """
|
||||
|
||||
|
|
@ -244,6 +247,31 @@ class sram_1bank(design, verilog, lef):
|
|||
def create_modules(self):
|
||||
debug.error("Must override pure virtual function.", -1)
|
||||
|
||||
def route_supplies_constructive(self, bbox=None):
|
||||
# prepare the "router"
|
||||
from openram.router.supply_placer import supply_placer as router
|
||||
rtr = router(layers=self.supply_stack,
|
||||
design=self,
|
||||
bbox=bbox,
|
||||
pin_type=OPTS.supply_pin_type,
|
||||
ext_vdd_name=self.vdd_name,
|
||||
ext_gnd_name=self.gnd_name)
|
||||
# add power rings / side pins
|
||||
if OPTS.supply_pin_type in ["top", "bottom", "right", "left"]:
|
||||
rtr.add_side_pin(self.vdd_name)
|
||||
rtr.add_side_pin(self.gnd_name)
|
||||
elif OPTS.supply_pin_type == "ring":
|
||||
rtr.add_ring_pin(self.vdd_name)# ring vdd name
|
||||
rtr.add_ring_pin(self.gnd_name)
|
||||
else:
|
||||
debug.warning("Side supply pins aren't created.")
|
||||
|
||||
# maze router the bank power pins
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for inst in self.bank_insts:
|
||||
self.copy_power_pins(inst, pin_name)
|
||||
rtr.route_bank()
|
||||
|
||||
def route_supplies(self, bbox=None):
|
||||
""" Route the supply grid and connect the pins to them. """
|
||||
|
||||
|
|
@ -324,44 +352,41 @@ class sram_1bank(design, verilog, lef):
|
|||
"""
|
||||
Add the top-level pins for a single bank SRAM with control.
|
||||
"""
|
||||
|
||||
# List of pin to new pin name
|
||||
pins_to_route = []
|
||||
for port in self.all_ports:
|
||||
# Connect the control pins as inputs
|
||||
for signal in self.control_logic_inputs[port]:
|
||||
if signal.startswith("rbl"):
|
||||
continue
|
||||
if signal=="clk":
|
||||
pins_to_route.append("{0}{1}".format(signal, port))
|
||||
self.pins_to_route.append("{0}{1}".format(signal, port))
|
||||
else:
|
||||
pins_to_route.append("{0}{1}".format(signal, port))
|
||||
self.pins_to_route.append("{0}{1}".format(signal, port))
|
||||
|
||||
if port in self.write_ports:
|
||||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
pins_to_route.append("din{0}[{1}]".format(port, bit))
|
||||
self.pins_to_route.append("din{0}[{1}]".format(port, bit))
|
||||
|
||||
if port in self.readwrite_ports or port in self.read_ports:
|
||||
for bit in range(self.word_size + self.num_spare_cols):
|
||||
pins_to_route.append("dout{0}[{1}]".format(port, bit))
|
||||
self.pins_to_route.append("dout{0}[{1}]".format(port, bit))
|
||||
|
||||
for bit in range(self.col_addr_size):
|
||||
pins_to_route.append("addr{0}[{1}]".format(port, bit))
|
||||
self.pins_to_route.append("addr{0}[{1}]".format(port, bit))
|
||||
|
||||
for bit in range(self.row_addr_size):
|
||||
pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size))
|
||||
self.pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size))
|
||||
|
||||
if port in self.write_ports:
|
||||
if self.write_size != self.word_size:
|
||||
for bit in range(self.num_wmasks):
|
||||
pins_to_route.append("wmask{0}[{1}]".format(port, bit))
|
||||
self.pins_to_route.append("wmask{0}[{1}]".format(port, bit))
|
||||
|
||||
if port in self.write_ports:
|
||||
if self.num_spare_cols == 1:
|
||||
pins_to_route.append("spare_wen{0}".format(port))
|
||||
self.pins_to_route.append("spare_wen{0}".format(port))
|
||||
else:
|
||||
for bit in range(self.num_spare_cols):
|
||||
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
|
||||
self.pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
|
||||
|
||||
if route_option == "classic":
|
||||
from openram.router import signal_escape_router as router
|
||||
|
|
@ -373,7 +398,7 @@ class sram_1bank(design, verilog, lef):
|
|||
bbox=bbox,
|
||||
design=self,
|
||||
mod=mod)
|
||||
rtr.route(pins_to_route)
|
||||
rtr.route(self.pins_to_route)
|
||||
elif route_option == "fast":
|
||||
# use io_pin_placer
|
||||
# put the IO pins at the edge
|
||||
|
|
@ -381,10 +406,10 @@ class sram_1bank(design, verilog, lef):
|
|||
pl = placer(layers=self.m3_stack,
|
||||
bbox=bbox,
|
||||
design=self)
|
||||
for name in pins_to_route:
|
||||
for name in self.pins_to_route:
|
||||
debug.warning("pins_to_route pins -> {0}".format(name))
|
||||
pl.add_io_pins_connected(pins_to_route)
|
||||
#pl.add_io_pins(pins_to_route)
|
||||
pl.add_io_pins_connected(self.pins_to_route)
|
||||
#pl.add_io_pins(self.pins_to_route)
|
||||
|
||||
def compute_bus_sizes(self):
|
||||
""" Compute the independent bus widths shared between two and four bank SRAMs """
|
||||
|
|
@ -1089,7 +1114,7 @@ class sram_1bank(design, verilog, lef):
|
|||
self.add_layout_pins()
|
||||
|
||||
# Some technologies have an isolation
|
||||
self.add_dnwell(inflate=2.5)
|
||||
self.add_dnwell(inflate=2.5, add_vias=False)
|
||||
|
||||
init_bbox = self.get_bbox()
|
||||
# Route the supplies together and/or to the ring/stripes.
|
||||
|
|
@ -1100,7 +1125,8 @@ class sram_1bank(design, verilog, lef):
|
|||
self.route_escape_pins(bbox=init_bbox, mod=mod, route_option=route_option)
|
||||
|
||||
if OPTS.route_supplies:
|
||||
self.route_supplies(init_bbox)
|
||||
#self.route_supplies(init_bbox)
|
||||
self.route_supplies_constructive(init_bbox)
|
||||
|
||||
def route_dffs(self, add_routes=True):
|
||||
|
||||
|
|
|
|||
|
|
@ -412,7 +412,7 @@ class io_pin_placer(router):
|
|||
return [source_pin.uc(), target_pin.bc()]
|
||||
else:
|
||||
# need intermediate point
|
||||
via_basic_y = ll.y + 21 # 21 is magic number, make sure out of dff area
|
||||
via_basic_y = ll.y + 22 # 22 is magic number, make sure out of dff area
|
||||
if is_up:
|
||||
via_basic_y = via_basic_y + 0.5
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -111,6 +111,54 @@ class router(router_tech):
|
|||
self.all_pins.update(pin_set)
|
||||
|
||||
|
||||
def find_pins_inside(self, pin_name):
|
||||
# find pins except moat, power ring, the moat pins will be store as list and return
|
||||
""" Find the pins with the given name. """
|
||||
debug.info(4, "Finding all pins for {}".format(pin_name))
|
||||
moat_pins = []
|
||||
shape_list = self.layout.getAllPinShapes(str(pin_name))
|
||||
pin_set = set()
|
||||
for shape in shape_list:
|
||||
layer, boundary = shape
|
||||
# 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_pin = graph_shape(pin_name, rect, layer)
|
||||
# Skip this pin if it's contained by another pin of the same type
|
||||
if new_pin.core_contained_by_any(pin_set):
|
||||
continue
|
||||
# skip the moat pin
|
||||
if self.check_pin_on_moat(new_pin):
|
||||
moat_pins.append(new_pin)
|
||||
continue
|
||||
# Merge previous pins into this one if possible
|
||||
self.merge_shapes(new_pin, pin_set)
|
||||
pin_set.add(new_pin)
|
||||
# Add these pins to the 'pins' dict
|
||||
self.pins[pin_name] = pin_set
|
||||
self.all_pins.update(pin_set)
|
||||
return moat_pins
|
||||
|
||||
|
||||
def check_pin_on_moat(self, pin_shape):
|
||||
""" Check if a given pin is on the moat. """
|
||||
ll, ur = self.bbox
|
||||
left_x = ll.x
|
||||
right_x = ur.x
|
||||
bottom_y = ll.y
|
||||
top_y = ur.y
|
||||
|
||||
threshold = 10 # inside this distance, could be considered as on the moat
|
||||
|
||||
is_on_left = abs(pin_shape.center().x - left_x) < threshold
|
||||
is_on_right = abs(pin_shape.center().x - right_x) < threshold
|
||||
is_on_bottom = abs(pin_shape.center().y - bottom_y) < threshold
|
||||
is_on_top = abs(pin_shape.center().y - top_y) < threshold
|
||||
|
||||
return is_on_left or is_on_right or is_on_bottom or is_on_top
|
||||
|
||||
|
||||
def find_blockages(self, name="blockage", shape_list=None):
|
||||
""" Find all blockages in the routing layers. """
|
||||
debug.info(4, "Finding blockages...")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2024 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import debug
|
||||
from openram.base.vector import vector
|
||||
from openram import OPTS
|
||||
from .graph import graph
|
||||
from .graph_shape import graph_shape
|
||||
from .router import router
|
||||
|
||||
class supply_placer(router):
|
||||
|
||||
def __init__(self, layers, design, bbox=None, pin_type=None, ext_vdd_name="vccd1", ext_gnd_name="vssd1"):
|
||||
|
||||
# `router` is the base router class
|
||||
router.__init__(self, layers, design, bbox)
|
||||
|
||||
# Side supply pin type
|
||||
# (can be "top", "bottom", "right", "left", and "ring")
|
||||
self.pin_type = pin_type
|
||||
# New pins are the side supply pins
|
||||
self.new_pins = {}
|
||||
# external power name of the whole macro
|
||||
self.ext_vdd_name = ext_vdd_name
|
||||
self.ext_gnd_name = ext_gnd_name
|
||||
# instances
|
||||
self.insts = self.design.insts
|
||||
# moat pins
|
||||
self.moat_pins = []
|
||||
# io pins
|
||||
self.io_pins_left = []
|
||||
self.io_pins_right = []
|
||||
self.io_pins_top = []
|
||||
self.io_pins_bottom = []
|
||||
|
||||
def route_bank(self, vdd_name="vdd", gnd_name="gnd"):
|
||||
debug.info(1, "Running router for {} and {}...".format(vdd_name, gnd_name))
|
||||
|
||||
# Save pin names
|
||||
self.vdd_name = vdd_name
|
||||
self.gnd_name = gnd_name
|
||||
|
||||
# Prepare gdsMill to find pins and blockages
|
||||
self.prepare_gds_reader()
|
||||
|
||||
# Find vdd/gnd pins of bank, to be routed
|
||||
self.moat_pins = self.find_pins_inside(vdd_name)
|
||||
self.find_pins_inside(gnd_name)
|
||||
|
||||
# Find blockages and vias
|
||||
self.find_blockages()
|
||||
self.find_vias()
|
||||
|
||||
# Convert blockages and vias if they overlap a pin
|
||||
self.convert_vias()
|
||||
self.convert_blockages()
|
||||
|
||||
# Add vdd and gnd pins as blockages as well
|
||||
# NOTE: This is done to make vdd and gnd pins DRC-safe
|
||||
for pin in self.all_pins:
|
||||
self.blockages.append(self.inflate_shape(pin))
|
||||
|
||||
# Route vdd and gnd
|
||||
routed_count = 0
|
||||
routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name])
|
||||
for pin_name in [vdd_name, gnd_name]:
|
||||
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)
|
||||
# Report routed count
|
||||
routed_count += 1
|
||||
debug.info(2, "Routed {} of {} supply pins".format(routed_count, routed_max))
|
||||
|
||||
|
||||
#def route_moat(self):
|
||||
|
||||
#def route_other(self):
|
||||
|
||||
def check_overlap(self, moat_pin, io_pin_names):
|
||||
# use all the IO pins(at correspoding edge) to check overlap, check 1 moat vdd pin, give the corresponding target/source position as list, and pull the source up to m3
|
||||
add_distance = 0
|
||||
direction = 1
|
||||
self.prepare_io_pins(io_pin_names)
|
||||
# judge the edge of moat vdd
|
||||
edge = self.get_closest_edge(moat_pin)
|
||||
source_center = moat_pin.center()
|
||||
if edge == "bottom":
|
||||
pin_too_close = any(abs(io_pin.center().x - source_center.x) < self.track_width for io_pin in self.io_pins_bottom)
|
||||
tmp_center = source_center
|
||||
while pin_too_close:
|
||||
tmp_center = source_center
|
||||
add_distance = add_distance + 0.1
|
||||
if direction == 1: # right shift
|
||||
tmp_center = tmp_center + add_distance
|
||||
else: # left shift
|
||||
tmp_center = tmp_center - add_distance
|
||||
pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < self.track_width for io_pin in self.io_pins_bottom)
|
||||
if tmp_center == source_center: # no overlap
|
||||
# no jog, direct pull to m3
|
||||
self.design.copy_power_pin(moat_pin, loc=None, directions=None, new_name="")
|
||||
else: # need jog
|
||||
# shift the center
|
||||
# add rectangle at same layer (original)
|
||||
self.design.add
|
||||
elif edge == "top":
|
||||
pass
|
||||
elif edge == "left":
|
||||
pass
|
||||
else: #right
|
||||
pass
|
||||
|
||||
def prepare_io_pins(self, io_pin_names):
|
||||
# io_pin_names is a list
|
||||
# find all the io pins
|
||||
for pin_name in io_pin_names:
|
||||
self.find_pins(pin_name)# pin now in self.pins
|
||||
io_pin = self.pins[pin_name]
|
||||
self.find_closest_edge(io_pin)
|
||||
|
||||
|
||||
def get_closest_edge(self, pin):
|
||||
""" Return a point's the closest edge and the edge's axis direction. Here we use to find the edge of moat vdd """
|
||||
|
||||
ll, ur = self.bbox
|
||||
point = pin.center()
|
||||
# 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"
|
||||
if min_diff == ll_diff_y:
|
||||
return "bottom"
|
||||
if min_diff == ur_diff_x:
|
||||
return "right"
|
||||
return "top"
|
||||
|
||||
|
||||
def find_closest_edge(self, pin):
|
||||
""" Use to find the edge, where the io pin locats """
|
||||
|
||||
ll, ur = self.bbox
|
||||
point = pin.center()
|
||||
# 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:
|
||||
self.io_pins_left.append(pin)
|
||||
elif min_diff == ll_diff_y:
|
||||
self.io_pins_bottom.append(pin)
|
||||
elif min_diff == ur_diff_x:
|
||||
self.io_pins_right.append(pin)
|
||||
else:
|
||||
self.io_pins_top.append(pin)
|
||||
|
||||
|
||||
def add_side_pin(self, pin_name, side, num_vias=3, num_fake_pins=4):
|
||||
""" Add supply pin to one side of the layout. """
|
||||
|
||||
ll, ur = self.bbox
|
||||
vertical = side in ["left", "right"]
|
||||
inner = pin_name == self.ext_vdd_name
|
||||
|
||||
# Calculate wires' wideness
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
|
||||
# Calculate the offset for the inner ring
|
||||
if inner:
|
||||
margin = wideness * 2
|
||||
else:
|
||||
margin = 0
|
||||
|
||||
# Calculate the lower left coordinate
|
||||
if side == "top":
|
||||
offset = vector(ll.x + margin, ur.y - wideness - margin)
|
||||
elif side == "bottom":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "left":
|
||||
offset = vector(ll.x + margin, ll.y + margin)
|
||||
elif side == "right":
|
||||
offset = vector(ur.x - wideness - margin, ll.y + margin)
|
||||
|
||||
# Calculate width and height
|
||||
shape = ur - ll
|
||||
if vertical:
|
||||
shape_width = wideness
|
||||
shape_height = shape.y
|
||||
else:
|
||||
shape_width = shape.x
|
||||
shape_height = wideness
|
||||
if inner:
|
||||
if vertical:
|
||||
shape_height -= margin * 2
|
||||
else:
|
||||
shape_width -= margin * 2
|
||||
|
||||
# Add this new pin
|
||||
layer = self.get_layer(int(vertical))
|
||||
pin = self.design.add_layout_pin(text=pin_name,
|
||||
layer=layer,
|
||||
offset=offset,
|
||||
width=shape_width,
|
||||
height=shape_height)
|
||||
|
||||
# Add fake pins on this new pin evenly
|
||||
fake_pins = []
|
||||
if vertical:
|
||||
space = (shape_height - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x, offset.y + wideness)
|
||||
else:
|
||||
space = (shape_width - (2 * wideness) - num_fake_pins * self.track_wire) / (num_fake_pins + 1)
|
||||
start_offset = vector(offset.x + wideness, offset.y)
|
||||
for i in range(1, num_fake_pins + 1):
|
||||
if vertical:
|
||||
offset = vector(start_offset.x, start_offset.y + i * (space + self.track_wire))
|
||||
ll = vector(offset.x, offset.y - self.track_wire)
|
||||
ur = vector(offset.x + wideness, offset.y)
|
||||
else:
|
||||
offset = vector(start_offset.x + i * (space + self.track_wire), start_offset.y)
|
||||
ll = vector(offset.x - self.track_wire, offset.y)
|
||||
ur = vector(offset.x, offset.y + wideness)
|
||||
rect = [ll, ur]
|
||||
fake_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
fake_pins.append(fake_pin)
|
||||
return pin, fake_pins
|
||||
|
||||
|
||||
def add_ring_pin(self, pin_name, num_vias=3, num_fake_pins=4):
|
||||
""" Add the supply ring to the layout. """
|
||||
|
||||
# Add side pins
|
||||
new_pins = []
|
||||
for side in ["top", "bottom", "right", "left"]:
|
||||
new_shape, fake_pins = self.add_side_pin(pin_name, side, num_vias, num_fake_pins)
|
||||
ll, ur = new_shape.rect
|
||||
rect = [ll, ur]
|
||||
layer = self.get_layer(side in ["left", "right"])
|
||||
new_pin = graph_shape(name=pin_name,
|
||||
rect=rect,
|
||||
layer_name_pp=layer)
|
||||
new_pins.append(new_pin)
|
||||
#self.pins[pin_name].update(fake_pins)
|
||||
self.fake_pins.extend(fake_pins)
|
||||
|
||||
# Add vias to the corners
|
||||
shift = self.track_wire + self.track_space
|
||||
half_wide = self.track_wire / 2
|
||||
for i in range(4):
|
||||
ll, ur = new_pins[i].rect
|
||||
if i % 2:
|
||||
top_left = vector(ur.x - (num_vias - 1) * shift - half_wide, ll.y + (num_vias - 1) * shift + half_wide)
|
||||
else:
|
||||
top_left = vector(ll.x + half_wide, ur.y - half_wide)
|
||||
for j in range(num_vias):
|
||||
for k in range(num_vias):
|
||||
offset = vector(top_left.x + j * shift, top_left.y - k * shift)
|
||||
self.design.add_via_center(layers=self.layers,
|
||||
offset=offset)
|
||||
|
||||
# Save side pins for routing
|
||||
self.new_pins[pin_name] = new_pins
|
||||
for pin in new_pins:
|
||||
self.blockages.append(self.inflate_shape(pin))
|
||||
|
||||
|
||||
def get_mst_pairs(self, pins):
|
||||
"""
|
||||
Return the pin pairs from the minimum spanning tree in a graph that
|
||||
connects all pins together.
|
||||
"""
|
||||
|
||||
pin_count = len(pins)
|
||||
|
||||
# Create an adjacency matrix that connects all pins
|
||||
edges = [[0] * pin_count for i in range(pin_count)]
|
||||
for i in range(pin_count):
|
||||
for j in range(pin_count):
|
||||
# Skip if they're the same pin
|
||||
if i == j:
|
||||
continue
|
||||
# Skip if both pins are fake
|
||||
if pins[i] in self.fake_pins and pins[j] in self.fake_pins:
|
||||
continue
|
||||
edges[i][j] = pins[i].distance(pins[j])
|
||||
|
||||
pin_connected = [False] * pin_count
|
||||
pin_connected[0] = True
|
||||
|
||||
# Add the minimum cost edge in each iteration (Prim's)
|
||||
mst_pairs = []
|
||||
for i in range(pin_count - 1):
|
||||
min_cost = float("inf")
|
||||
s = 0
|
||||
t = 0
|
||||
# Iterate over already connected pins
|
||||
for m in range(pin_count):
|
||||
# Skip if not connected
|
||||
if not pin_connected[m]:
|
||||
continue
|
||||
# Iterate over this pin's neighbors
|
||||
for n in range(pin_count):
|
||||
# Skip if already connected or isn't a neighbor
|
||||
if pin_connected[n] or edges[m][n] == 0:
|
||||
continue
|
||||
# Choose this edge if it's better the the current one
|
||||
if edges[m][n] < min_cost:
|
||||
min_cost = edges[m][n]
|
||||
s = m
|
||||
t = n
|
||||
pin_connected[t] = True
|
||||
mst_pairs.append((pins[s], pins[t]))
|
||||
|
||||
return mst_pairs
|
||||
|
||||
|
||||
def get_new_pins(self, name):
|
||||
""" Return the new supply pins added by this router. """
|
||||
|
||||
return self.new_pins[name]
|
||||
|
|
@ -97,7 +97,7 @@ class supply_router(router):
|
|||
|
||||
ll, ur = self.bbox
|
||||
vertical = side in ["left", "right"]
|
||||
inner = pin_name == self.gnd_name
|
||||
inner = pin_name == self.vdd_name
|
||||
|
||||
# Calculate wires' wideness
|
||||
wideness = self.track_wire * num_vias + self.track_space * (num_vias - 1)
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ for path in output_files:
|
|||
|
||||
# Create an SRAM (we can also pass sram_config, see documentation/tutorials for details)
|
||||
from openram import sram
|
||||
s = sram(route_option="classic")# "classic" or "fast"
|
||||
s = sram(route_option="fast")# "classic" or "fast"
|
||||
# Output the files for the resulting SRAM
|
||||
s.save()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue