first commt

This commit is contained in:
FriedrichWu 2024-11-17 10:35:01 +01:00
parent f56460bb94
commit 86588619fd
7 changed files with 522 additions and 39 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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:

View File

@ -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...")

View File

@ -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]

View File

@ -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)

View File

@ -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()